提示:最近在做文本情感分析,实现Electra预训练模型+BiLstm+attention。在github上找了一些代码,很多都是只有一部分,而且Electra预训练模型没有什么可以参考的代码。所以,记录一下学习过程,有错误的点,大家可以指出来,共同学习。
将一个词在整个语料库中的共现上下文信息聚合至该词的向量表示中,也就是说,对于任意一个词,其向量表示是恒定的,不随其上下文的变化而变化。
但是,在自然语言中,同一个词语在不同上下文或者语境中可能呈现出多种不同的词义、语法性质或者属性,也就是一词多义的情形。
具体算法:word2vec算法,glove算法
为了解决上述问题,提出了上下文相关的词向量,即随上下文而动态变化的,称为动态词向量。
与静态词向量类似, 动态词向量最简单、 直接的应用是作为输入特征供目标任务使用, 而无须改变目标任务已有的模型结构。
根据《自然语言处理:基于预训练的模型》这本书,其实现在很多预训练模型可以直接调用,相关源码已经开放,比如ELMO模型,利用AllenNLP ELMo接口进行调用,将词语进行向量化表示,然后输入自己的模型中。
这是上面那本书中的代码示例,可以看一下,直接调用elmo模型,然后得到embedding。
# ELMO+MLP的代码
import enum
import torch
from torch import nn, optim
from torch.nn import functional as F
from torch.utils.data import Dataset, DataLoader
from collections import defaultdict
from vocab import Vocab
from utils import load_sentence_polarity
from allennlp.modules.elmo import elmo,batch_to_ids
class BowDataset(Dataset):
#data为原始数据
def __init__(self, data):
self.data = data
def __len__(self):
#返回数据集中样例的数目
return len(self.data)
def __getitem__(self, i):
#返回下标为i的样例
return self.data[i]
def collate_fn(examples):
#从独立样本集合中构建各批次的输入输出
#BowDataset类定义了一个样本的数据结构,即输入标签和输出标签的元组
#因此,将输入inputs定义为一个张量的列表,其中每个张量为原始句子中标记序列
#对应的索引值序列 ex[0]
inputs = [torch.tensor(ex[0]) for ex in examples]
#输出的目标targets为该批次中全部样例输出结果构成的张量
targets = torch.tensor([ex[1] for ex in examples], dtype=torch.long)
#获取一个批次中每个样例的序列长度
offsets = [0] + [i.shape[0] for i in inputs]
#根据序列长度,转换为每个序列起始位置的偏移量
offsets = torch.tensor(offsets[:-1]).cumsum(dim=0)
#将inputs列表中的张量拼接成一个大的张量
inputs = torch.cat(inputs)
return inputs, offsets, targets
#感知器模型
class ElmoMLP(nn.Module):
def __init__(self, elmo, hidden_dim, num_class):
super(ElmoMLP, self).__init__()
#elmo预训练编码器,可使用allenNLP预训练Elmo模型
self.elmo=elmo
#线性变换:词向量层-》隐含层
self.linear1 = nn.Linear(self.elmo.get_output_dim(), hidden_dim)
self.activate = F.relu
#线性变换:隐含层-》输出层
self.linear2 = nn.Linear(hidden_dim, num_class)
def forward(self, inputs, lengths):
elmo_output=self.elmo(inputs)
embeds=elmo_output['elmo_representations'][0]
mask=elmo_output['mask']
#将每个序列中词的ELMO向量均值作为该序列的向量表示,作为MLP的输入
embeds=torch.sum(embeds*mask.unsqueeze(2),dim=1)/lengths.unsqueeze(1)
hidden = self.activate(self.linear1(embeds))
outputs = self.linear2(hidden)
log_probs = F.log_softmax(outputs, dim=1)
return log_probs
# tqdm是一个Python模块,能以进度条的方式显示迭代的进度
from tqdm.auto import tqdm
# 超参数设置
embedding_dim = 128
hidden_dim = 256
num_class = 2
batch_size = 32
num_epoch = 5
# 加载数据
train_data, test_data, vocab = load_sentence_polarity()
train_dataset = BowDataset(train_data)
test_dataset = BowDataset(test_data)
train_data_loader = DataLoader(train_dataset, batch_size=batch_size, collate_fn=collate_fn, shuffle=True)
test_data_loader = DataLoader(test_dataset, batch_size=1, collate_fn=collate_fn, shuffle=False)
# 加载模型
if torch.cuda.is_available():
print("cuda")
else:
print("cpu")
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model = MLP(len(vocab), embedding_dim, hidden_dim, num_class)
model.to(device) # 将模型加载到CPU或GPU设备
#训练过程
nll_loss = nn.NLLLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001) # 使用Adam优化器
model.train()
for epoch in range(num_epoch):
total_loss = 0
for batch in tqdm(train_data_loader, desc=f"Training Epoch {epoch}"):
inputs, offsets, targets = [x.to(device) for x in batch]
log_probs = model(inputs, offsets)
loss = nll_loss(log_probs, targets)
optimizer.zero_grad()
loss.backward()
optimizer.step()
total_loss += loss.item()
print(f"Loss: {total_loss:.2f}")
# 测试过程
acc = 0
for batch in tqdm(test_data_loader, desc=f"Testing"):
inputs, offsets, targets = [x.to(device) for x in batch]
with torch.no_grad():
output = model(inputs, offsets)
acc += (output.argmax(dim=1) == targets).sum().item()
# 输出在测试集上的准确率
print(f"Acc: {acc / len(test_data_loader):.2f}")