rostcm6情感分析案例分析_卷积情感分析
这是⼀个⾯向⼩⽩(⽐如,本⼈)的关于情感分析的系列教程 [1]。⽼鸽⼦整理了“4 - Convolutional Sentiment Analysis.ipynb”中的内容。
本⽂任务:使⽤卷积神经⽹络(CNN)来实现句⼦分类。
简介
CNN⽤于分析图像,包含⼀个或多个卷积层,紧跟⼀个或多个线性层。在卷积层中,使⽤滤波器扫描图像。扫描后的图像送⼊另⼀个卷积层或线性层。每个滤波器都有⼤⼩,如:每次使⽤⼤⼩的滤波器来扫描⼤⼩的图像区域,滤波器有9个对应的权重。
类似于使⽤滤波器查看图像区域,使⽤⼤⼩的滤波器来查看⽂本中两个连续的词(bi-gram)。
准备数据
不同于FastText模型,不⽤明确创建bi-grams并添加到句尾。
因为卷积层的第⼀个维度是batch,所以可以设置field中的batch_first为True。
划分训练集和验证集;构建词汇表;创建迭代器。
import torch
from torchtext import data
from torchtext import datats
import random
import numpy as np
SEED = 1234
random.ed(SEED)
np.random.ed(SEED)
torch.manual_ed(SEED)
torch.backends.cudnn.deterministic = True
TEXT = data.Field(tokenize='spacy', batch_first=True)
LABEL = data.LabelField(dtype=torch.float)
train_data, test_data = datats.IMDB.splits(TEXT, LABEL)
train_data, valid_data = train_data.split(random_state = random.ed(SEED))
#-----------------------------
MAX_VOCAB_SIZE = 25_000
TEXT.build_vocab(
train_data,
max_size = MAX_VOCAB_SIZE,
vectors = "glove.6B.100d",
unk_init = al_
英国路冠)
LABEL.build_vocab(train_data)
#-----------------------------
BATCH_SIZE = 64
device = torch.device('cuda' if torch.cuda.is_available() el 'cpu')
free是什么意思
train_iterator, valid_iterator, test_iterator = data.BucketIterator.splits(
(train_data, valid_data, test_data),
batch_size=BATCH_SIZE,
device=device
)
构建模型
在⼆维中可视化句中的词。每个词沿⼀个轴,嵌⼊沿另⼀个轴。
使⽤⼀个⼤⼩的滤波器,每次扫描n个词。
下图有4个词,每个词有5维嵌⼊,因此得到⼀个⼤⼩的“图像”张量。使⽤⼀个⼤⼩的滤波器,每次扫描两个词(黄⾊)。滤波器的每个元素包含⼀个权重,它的输出为10个嵌⼊元素的加权和。滤波器向下扫描(跨句)到下⼀个bi-gram,输出另⼀个加权和。滤波器再次向下扫描,计算最后的加权和。
滤波器的宽度等于“图像”的宽度,输出⼀个向量,它的长度等于“图像”的⾼度减滤波器的⾼度加1:。
本⽂所⽤的模型将有不同⼤⼩的滤波器,它们的⾼度分别为3、4和5,每个⼤⼩有100个,从⽽能够寻找与评论的情感相关的不同的tri-grams、4-grams和5-grams。
罗莎帕克斯为了确定与情绪相关的最重要的n-gram,最⼤池化卷积层的输出。
因为模型有300个不同且重要的n-grams,所以可以认为全连接层⽤于加权这些n-grams,从⽽给出最终的判断。
应⽤细节
使⽤nn.Conv2d来实现卷积层:in_channels为输⼊通道数;图像的通道数为3(红-绿-蓝),⽽⽂本的通道数为1;out_channels为输出通道数,kernel_size为滤波器⼤⼩:,其中的为n-grams的⼤⼩。
RNN的batch位于第⼆维,⽽CNN的batch位于第⼀维。CNN的第⼆维为通道数(为嵌⼊添加⼤⼩为1的新维度,与卷积层的
in_channels=1⼀致)。
卷积后的激活函数为ReLU。池化层可以处理不同长度的句⼦,卷积层的输出取决于它的输⼊,不同的batches包含了不同长度的句⼦。当没有最⼤池化层时,线性层的输⼊取决于输⼊句⼦的⼤⼩。为了
解决该问题,可以修剪或填充所有句⼦到等长。当有最⼤池化层时,线性层的输⼊数等于滤波器个数。
当句⼦⽐最⼤的滤波器还短时,必须填充句⼦到最⼤的滤波器长度。因为IMDb数据中的评论都⼤于5个字长,所以不⽤担⼼该情况。
对级联的滤波器输出使⽤Dropout,再经过线性层得到预测结果。
as nn
functional as F
class CNN(nn.Module):
def __init__(lf, vocab_size, embedding_dim, n_filters, filter_sizes, output_dim, dropout, pad_idx):
super().__init__()
vocab_size,
embedding_dim,
padding_idx=pad_idx
)
in_channels=1,
out_channels=n_filters,
kernel_size=(filter_sizes[0], embedding_dim)
)
in_channels=1,
out_channels=n_filters,
智慧消防工程师
kernel_size=(filter_sizes[1], embedding_dim)
)
in_channels=1,
out_channels=n_filters,
kernel_size=(filter_sizes[2], embedding_dim)
环球青少儿国际英语怎么样)
lf.fc = nn.Linear(len(filter_sizes) * n_filters, output_dim)
lf.dropout = nn.Dropout(dropout)
def forward(lf, text):
# text: [batch size, nt len]
# embedded: [batch size, nt len, emb dim]
embedded = lf.embedding(text)
# embedded: [batch size, 1, nt len, emb dim]
embedded = embedded.unsqueeze(1)
# conved_n: [batch size, n_filters, nt len - filter_sizes[n] + 1]
conved_0 = F.v_0(embedded).squeeze(3))
conved_1 = F.v_1(embedded).squeeze(3))
conved_2 = F.v_2(embedded).squeeze(3))
高中补习班# pooled_n: [batch size, n_filters]
gdjwgl
pooled_0 = F.max_pool1d(conved_0, conved_0.shape[2]).squeeze(2)
pooled_1 = F.max_pool1d(conved_1, conved_1.shape[2]).squeeze(2)
pooled_2 = F.max_pool1d(conved_2, conved_2.shape[2]).squeeze(2)
# cat: [batch size, n_filters * len(filter_sizes)]
cat = lf.dropout(torch.cat((pooled_0, pooled_1, pooled_2), dim=1))
return lf.fc(cat)
CNN模型仅⽤了3种不同⼤⼩的滤波器。为了使⽤任意数量的滤波器,将所有的卷积层放⼊nn.ModuleList。
vocab_size,
embedding_dim,
padding_idx=pad_idx
)
nn.Conv2d(
in_channels=1,
out_channels=n_filters,
kernel_size=(fs, embedding_dim)
)
for fs in filter_sizes
])
lf.fc = nn.Linear(len(filter_sizes) * n_filters, output_dim)
lf.dropout = nn.Dropout(dropout)
def forward(lf, text):
# text: [batch size, nt len]
# embedded: [batch size, nt len, emb dim]
embedded = lf.embedding(text)
# embedded: [batch size, 1, nt len, emb dim]
embedded = embedded.unsqueeze(1)
# conved_n: [batch size, n_filters, nt len - filter_sizes[n] + 1]
conved = [F.relu(conv(embedded)).squeeze(3) for conv vs]
# pooled_n: [batch size, n_filters]
pooled = [F.max_pool1d(conv, conv.shape[2]).squeeze(2) for conv in conved]
# cat: [batch size, n_filters * len(filter_sizes)]
cat = lf.dropout(torch.cat(pooled, dim=1))
return lf.fc(cat)
使⽤⼀维卷积层,滤波器的“深度”和宽分别为嵌⼊的维度和句中的词数。
vocab_size,
embedding_dim,
padding_idx=pad_idx
)
nn.Conv1d(
in_channels=embedding_dim,
nian
out_channels=n_filters,
kernel_size=fs
)
for fs in filter_sizes
])
lf.fc = nn.Linear(len(filter_sizes) * n_filters, output_dim)
lf.dropout = nn.Dropout(dropout)
def forward(lf, text):
# text: [batch size, nt len]
# embedded: [batch size, nt len, emb dim]
embedded = lf.embedding(text)
# embedded: [batch size, emb dim, nt len]
embedded = embedded.permute(0, 2, 1)
# conved_n: [batch size, n_filters, nt len - filter_sizes[n] + 1]
考研英语复习经验conved = [F.relu(conv(embedded)) for conv vs]
# pooled_n: [batch size, n_filters]
pooled = [F.max_pool1d(conv, conv.shape[2]).squeeze(2) for conv in conved]
# cat: [batch size, n_filters * len(filter_sizes)]
cat = lf.dropout(torch.cat(pooled, dim=1))
return lf.fc(cat)
创建CNN2d(或CNN、CNN1d)类的实例;打印模型中可训练的参数量;清0“”和“”的初始权重。
INPUT_DIM = len(TEXT.vocab)
EMBEDDING_DIM = 100
N_FILTERS = 100
梦想者FILTER_SIZES = [3, 4, 5]
OUTPUT_DIM = 1
DROPOUT = 0.5
PAD_IDX = TEXT.vocab.stoi[TEXT.pad_token]
model = CNN2d(INPUT_DIM, EMBEDDING_DIM, N_FILTERS, FILTER_SIZES, OUTPUT_DIM, DROPOUT, PAD_IDX) #-----------------------------
def count_parameters(model):
return sum(p.numel() for p in model.parameters() quires_grad)
print(f'The model has {count_parameters(model):,} trainable parameters')
#-----------------------------
pretrained_embeddings = TEXT.vocab.vectors
UNK_IDX = TEXT.vocab.stoi[TEXT.unk_token]