介绍
在之前的实验中我们介绍和使用了 BERT 预训练模型和 GPT-2 预训练模型,分别进行了文本分类和文本生成实验。在本次实验中,我们将介绍 XLNet 预训练模型,并使用其进行命名实体识别实验。
知识点 XLNet
XLNet 在 BERT 和 GPT-2 上的改进
XLNet 模型结构
使用 XLNet 进行命名实体识别实验
GLUE
XLNet 在 BERT 和 GPT-2 上的改进
BERT 的缺点
可以说 XLNet 是 BERT 的增强版,但它与 BERT 又有许多不同之处。下面,我们将详细介绍一下。
在第一次实验中提到的,BERT 是自编码模型(Autoencoding),换一种说法来说就是,BERT 以遮蔽语言模型(Masked Language Model)为训练目标。训练自回归模型时,输入语句的一些词会被随机替换成 [MASK] 标签,然后训练模型预测被标签掩盖的词。
我们可以从这个过程中看到两个缺点:
错误地假设了被覆盖词与被覆盖词之间是独立的。
使预训练和微调时的输入不统一。
缺点 1 是指,在进行预测时,由于部分词被 [MASK] 覆盖,所以当 BERT 模型在预测一个被覆盖词时,忽略了其他的覆盖词对他的影响。也就是假设了所有被覆盖词是不相关的,很明显可以知道这个假设是错误的。
缺点 2 是指,在预训练是我们使用了 [MASK] 标签把部分词覆盖,而在微调预训练好的模型时,我们并不会使用到这个标签,这就导致了预训练过程和微调过程不符。
from torch.autograd import Variable
import time
pre = time.time()
epoch = 3
for i in range(epoch):
for batch_idx, (data, target) in enumerate(train_loader):
data, target = Variable(data).to(device), Variable(target).to(device)
optimizer.zero_grad()
# 生成掩膜
mask = []
for sample in data:
mask.append([1 if i != 0 else 0 for i in sample])
mask = torch.FloatTensor(mask).to(device)
output = model(data, attention_mask=mask)
# 得到模型预测结果
pred = torch.argmax(output, dim=2)
loss = loss_function(output, target, mask)
loss.backward()
optimizer.step()
if ((batch_idx+1) % 10) == 1:
print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss:{:.6f}'.format(
i+1, batch_idx, len(train_loader), 100. *
batch_idx/len(train_loader), loss.item()
))
if batch_idx == len(train_loader)-1:
# 在每个 Epoch 的最后输出一下结果
print('labels:', target)
print('pred:', pred)
print('训练时间:', time.time()-pre)
# 这里使用和训练集同样的方式修改标签,不再赘述
input_ids = []
input_labels = []
for text, ori_labels in zip(eval_samples, eval_labels):
l = text.split(' ')
labels = []
text_ids = []
for word, label in zip(l, ori_labels):
if word == '':
continue
tokens = tokenizer.tokenize(word)
for i, j in enumerate(tokens):
if i == 0:
labels.append(int(label))
text_ids.append(tokenizer.convert_tokens_to_ids(j))
else:
labels.append(9)
text_ids.append(tokenizer.convert_tokens_to_ids(j))
input_ids.append(text_ids)
input_labels.append(labels)
del eval_samples
del eval_labels
for j in range(len(input_ids)):
# 将样本数据填充至长度为 128
i = input_ids[j]
if len(i) != 128:
input_ids[j].extend([0]*(128 - len(i)))
for j in range(len(input_labels)):
# 将样本数据填充至长度为 128
i = input_labels[j]
if len(i) != 128:
input_labels[j].extend([10]*(128 - len(i)))
# 构建数据集和数据迭代器,设定 batch_size 大小为 1
eval_set = TensorDataset(torch.LongTensor(input_ids),
torch.LongTensor(input_labels))
eval_loader = DataLoader(dataset=eval_set,
batch_size=1,
shuffle=False)
eval_loader
将模型设置为验证模式,输入验证集数据。
from tqdm.notebook import tqdm
model.eval()
correct = 0
total = 0
for batch_idx, (data, target) in enumerate(tqdm(eval_loader)):
data = data.to(device)
target = target.float().to(device)
# 生成掩膜
mask = []
for sample in data:
mask.append([1 if i != 0 else 0 for i in sample])
mask = torch.Tensor(mask).to(device)
output = model(data, attention_mask=mask)
# 得到模型预测结果
pred = torch.argmax(output, dim=2)
# 将掩膜添加到预测结果上,便于计算准确率
pred = pred.float()
pred = pred * mask
target = target * mask
pred = pred[:, 0:mask.sum().int().item()]
target = target[:, 0:mask.sum().int().item()]
correct += (pred == target).sum().item()
total += mask.sum().item()
print('正确分类的标签数:{},标签总数:{},准确率:{:.2f}%'.format(
correct, total, 100.*correct/total))
XLNet: Generalized Autoregressive Pretraining for Language Understanding
XLNet 模型实现
来源:已购买的-蓝桥课程