(三)使⽤FastText模型进⾏⽂本情感分析(Pytorch)
在上⼀篇笔记中,我们使⽤了所有常⽤的情感分析技术,成功地达到了⼤约84%的测试精度。在本笔记本中,我们将实现⼀个模型,得到可⽐的结果,同时训练效果明显更快,使⽤⼤约⼀半的参数。更具体地说,我们将实现来⾃论⽂的“FastText”模型。
准备数据
FastText论⽂的⼀个关键概念是,它们计算输⼊句⼦的n-gram,并将它们附加到句⼦的末尾。这⾥,我们⽤bi-grams。简单地说,bi-gram是在⼀个句⼦中连续出现的⼀对单词/标记
例如,在“how are you ?”句⼦中,bi-grams是:“how are”,“are you”和“you ?”
generate_bigrams函数接受⼀个已经被标记化的句⼦,计算bi-grams并将它们附加到标记化的列表的末尾。
def generate_bigrams(x):
n_grams =set(zip(*[x[i:]for i in range(2)]))
for n_gram in n_grams:
x.append(' '.join(n_gram))
return x
例如:
generate_bigrams(['This','film','is','terrible'])
['This','film','is','terrible','This film','film is','is terrible']
TorchText字段有⼀个预处理参数。这⾥传递的函数将在句⼦被标记化之后(从字符串转换为标记列表时),但在句⼦被数字化之前(从标记列表转换为索引列表时)应⽤于句⼦。这就是我们传递generate_bigrams函数的地⽅。因为我们没有使⽤RNN,所以我们不能使⽤打包填充序列(packed padded sequences),因此我们不需要设置include_length = True。
import torch
from torchtext import data
from torchtext import datasets
SEED =1234
torch.manual_seed(SEED)
torch.backends.cudnn.deterministic =True
TEXT = data.Field(tokenize='spacy', tokenizer_language='en_core_web_sm', preprocessing=generate_bigrams)
LABLE = data.LabelField(dtype=torch.float)
与之前⼀样,我们加载IMDb数据集并创建分割。
import random
train_data, test_data = datasets.IMDB.splits(TEXT, LABEL)
train_data, valid_data = train_data.split(random_state = random.seed(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()else'cpu')
train_iterator, valid_iterator, test_iterator = data.BucketIterator.splits(
(train_data, valid_data, test_data),
batch_size = BATCH_SIZE,
device = device)
搭建模型
这个模型的参数⽐之前的模型少得多,因为它只有两个层,即嵌⼊层和线性层。在视线中没有RNN组件!
比亚迪s6动力怎么样相反,它⾸先使⽤嵌⼊层(蓝⾊)计算每个单词的词嵌⼊,然后计算所有词嵌⼊的平均值(粉⾊),并通过线性层(银⾊)提供预测结果,就是这样!
我们使⽤avg_pool2d(average pool 2-dimensions 2维平均池)函数实现平均计算。最初,你可能会认为使⽤⼆维池似乎很奇怪,我们的句⼦肯定是⼀维的,⽽不是⼆维的?然⽽,您可以将单词嵌⼊看作是⼀个⼆维⽹格,其中单词沿着⼀个轴,单词嵌⼊的维度沿着另⼀个轴。下图是转换为5维词嵌⼊后的⽰例句⼦,词沿纵轴,词嵌⼊沿横轴。这个[4x5]张量中的每个元素都⽤⼀个绿⾊块表⽰。
avg_pool2d使⽤embedded.shape乘以1的形状的过滤器。下图中粉红⾊的部分显⽰了这⼀点。
我们计算过滤器覆盖的所有元素的平均值,然后过滤器向右滑动,计算句⼦中每个单词的下⼀列嵌⼊值的和的平均值。
过滤器每滑过⼀个位置为我们提供⼀个值,即所有覆盖元素的平均值。当过滤器覆盖了所有的嵌⼊维度后,我们得到⼀个[1x5]的张量。然后这个张量通过线性层来产⽣我们的预测。
as nn
functional as F
class FastText(nn.Module):
def__init__(self, vocab_size, embedding_dim, output_dim, pad_idx):
super().__init__()
self.fc = nn.Linear(embedding_dim, output_dim)
def forward(self, text):
#text = [sent len, batch size]
embedded = bedding(text)
#embedded = [sent len, batch size, emb dim]
embedded = embedded.permute(1,0,2)
#embedded = [batch size, sent len, emb dim]
pooled = F.avg_pool2d(embedded,(embedded.shape[1],1)).squeeze(1)
#pooled = [batch size, embedding_dim]
return self.fc(pooled)
与以前⼀样,我们将创建FastText类的⼀个实例。
INPUT_DIM =len(TEXT.vocab)
EMBEDDING_DIM =100
OUTPUT_DIM =1
PAD_IDX = TEXT.vocab.stoi[TEXT.pad_token]
model = FastText(INPUT_DIM, EMBEDDING_DIM, OUTPUT_DIM, PAD_IDX)
看看我们模型中的参数数量,我们看到我们与第⼀个笔记中的标准RNN⼏乎相同,⽽只有前⼀个模型的⼀半参数。
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')
The model has 2,500,301 trainable parameters
把预先训练好的向量复制到嵌⼊层。
pretrained_embeddings = TEXT.vocab.vectors
tensor([[-0.1117,-0.4966,0.1631,...,1.2647,-0.2753,-0.1325],
[-0.8555,-0.7208,1.3755,...,0.0825,-1.1314,0.3997],
[-0.0382,-0.2449,0.7281,...,-0.1459,0.8278,0.2706],
...,
极狐新能源车是哪个公司的[0.3199,0.0746,0.0231,...,-0.3609,1.1303,0.5668],
[-1.0530,-1.0757,0.3903,...,0.0792,-0.3059,1.9734],
[-0.1734,-0.3195,0.3694,...,-0.2435,0.4767,0.1151]])
不要忘记将未知《unk》和填充《pad》标记的初始权重归零。
UNK_IDX = TEXT.vocab.stoi[TEXT.unk_token]
训练模型
训练模型和上次完全⼀样。
我们初始化优化器…
import torch.optim as optim
optimizer = optim.Adam(model.parameters())
我们定义标准(criterion),并将模型和标准(criterion)放在GPU上(如果有的话)…
criterion = nn.BCEWithLogitsLoss()
model = (device)
criterion = (device)
我们实现了计算准确率的函数…
新途安
def binary_accuracy(preds, y):
"""
Returns accuracy per batch, i.e. if you get 8/10 right, this returns 0.8, NOT 8
"""
#round predictions to the closest integer
rounded_preds = und(torch.sigmoid(preds))
correct =(rounded_preds == y).float()#convert into float for division
acc = correct.sum()/len(correct)
return acc
我们定义⼀个函数来训练我们的模型…
注意:我们不再使⽤dropout,所以我们不需要使⽤ain(),但正如第1本笔记本中提到的,使⽤它是⼀个很好的实践。def train(model, iterator, optimizer, criterion):
东南菱悦v3怎么样
小货车价格
epoch_loss =0
epoch_acc =0
for batch in iterator:
<_grad()
predictions = ).squeeze(1)
loss = criterion(predictions, batch.label)
acc = binary_accuracy(predictions, batch.label)
loss.backward()
qq汽车报价及图片optimizer.step()
epoch_loss += loss.item()
epoch_acc += acc.item()
return epoch_loss /len(iterator), epoch_acc /len(iterator)