【NLP9-Transformer经典案例】
Transformer经典案例
1、语言模型
以一个符合语言规律的序列为输入,模型将利用序列间关系等特征,输出在一个在所有词汇上的概率分布,这样的模型称为语言模型。
2、语言模型能解决的问题
根据语言模型定义,可以在它的基础上完成机器翻译,文本生成等任务,因为我们通过最后输出的概率分布来预测下一个词汇是什么
语言模型可以判断输入的序列是否为一句完整的话,因为我们可以根据输出的概率分布查看最大概率是否落在句子结束符上,来判断完整性
语言模型本身的训练目标是预测下一个词,因为它的特征提取部分会抽象很多语言序列之间的关系,这些关系可能同样对其它语言类任务有效果。因此可以作为预训练模型进行迁移学习
3、模型实现步骤
1、导包
2、导入wikiText-2数据集并作基本处理
3、构建用于模型输入的批次化数据
4、构建训练和评估函数
5、进行训练和评估(包括验证以及测试)
4、数据准备
wikiText-2数据集体量中等,训练集共有600篇文章,共208万左右词汇,在33278个不重复词汇,OVV(有多少正常英文词汇不在该数据集中的占比)为2.6%。验证集和测试集均为60篇。
torchtext重要功能
对文本数据进行处理,比如文本语料加载,文本迭代器构建等。
包含很多经典文本语料的预加载方法。其中包括的语料有:用于情感分析的SST和IMDB,用于问题分类TREC,用于及其翻译的WMT14,IWSLT,以及用于语言模型任务wikiText-2
# 1、导包
import math
import torch
import torch.nn as nn
import torch.nn.functional as F
#英文文本数据集工具包
import torchtext
#导入英文分词工具
#from torchtext.legacy.data import Field
from torchtext.data.utils import get_tokenizer
#from torchtext.legacy.data import *
#导入已经构建完成的Transformer包
from pyitcast.transformer import TransformerModel
import torch
import torchtext
from torchtext.legacy.data import Field,TabularDataset,Iterator,BucketIterator
TEXT=torchtext.data.Field(tokenize=get_tokenizer("basic_english"),
init_token ='<sos>',
eos_token ='<eos>',
lower = True)
print(TEXT)
# 2、导入wikiText-2数据集并作基本处理
train_txt,val_txt,test_txt = torchtext.datasets.WikiText2.splits(TEXT)
print(test_txt.examples[0].text[:10])
#将训练集文本数据构建一个vocab对象,可以使用vocab对象的stoi方法统计文本共包含的不重复的词汇总数
TEXT.build_vocab(train_txt)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
# 3、构建用于模型输入的批次化数据
def batchify(data,batch_size):
#data,之前得到的文本数据
# bsz,批次的样本量
#第一步使用TEXT的numericalize方法将单词映射成对应的联系数字
data = TEXT.numericalize([data.examples[0].text])
#取得需要经过多少次的batch_size后能够遍历完所有的数据
nbatch = data.size(0) // batch_size
#利用narrow方法对数据进行切割
#第一参数代表横轴切割还是纵轴切割,0 代表横轴,1代表纵轴
#第二个参数,第三个参数分别代表切割的起始位置和终止位置
data=data.narrow(0,0,nbatch * batch_size)
#对data的形状进行转变
data = data.view(batch_size,-1).t().contiguous()
return data.to(device)
# x=torch.tensor([[1,2,3],[4,5,6],[7,8,9]])
# print(x.narrow(0,0,2))
# print(x.narrow(1,1,2))
#设置训练数据、验证数据、测试数据的批次大小
batch_size =20
eval_batch_size =10
train_data = batchify(train_txt,batch_size)
val_data = batchify(val_txt,eval_batch_size)
test_data = batchify(test_txt,eval_batch_size)
#设定句子的最大长度
bptt =35
def get_batch(source,i):
seq_len = min(bptt,len(source) - 1 - i)
data = source[i:i+seq_len]
target = source[i+1:i+1+seq_len].view(-1)
return data,target
# source = test_data
# i =1
# x,y = get_batch(source,i)
# print(x)
# print(y)
# 4、构建训练和评估函数
#通过TEXT.vocab.stoi方法获取不重复的词汇总数
ntokens = len(TEXT.vocab.stoi)
#设置词嵌入维度的值等于200
emsize =200
#设置前馈全连接层的节点数等于200
nhid =200
#设置编码层层数等于2
nlayers=2
#设置多头注意力中的头数等于2
nhead =2
#设置置零比率
dropout =0.2
#将参数传入TransformerModel 中实例化模型
model = TransformerModel(ntokens,emsize,nhead,nhid,nlayers,dropout).to(device)
#设定损失函数,采用交叉熵损失函数
criterion= nn.CrossEntropyLoss()
#设置学习率
lr =5.0
#设置优化器
optimizer = torch.optim.SGD(model.parameters(),lr=lr)
criterion = nn.CrossEntropyLoss()
lr =5.0
optimizer = torch.optim.SGD(model.parameters(),lr=lr)
#定义学习率调整器,使用torch自带的lr_scheduler,将优化器传入
scheduler = torch.optim.lr_scheduler.StepLR(optimizer,1.0,gamma=0.95)
# 5、进行训练和评估(包括验证以及测试)
#训练函数
import time
def train():
#首先开启训练模式
model.train()
#定义初始损失值
total_loss =0
start_time = time.time()
#遍历训练数据进行模型的训练
for batch,i in enumerate(range(0,train_data.size(0) -1 ,bptt)):
#通过前面的get_batch函数获取源数据和目标数据
data,targets = get_batch(train_data,i)
#设置梯度归零
optimizer.zero_grad()
#通过模型预测输出
output = model(data)
#计算损失值
loss = criterion(output.view(-1,ntokens),targets)
#进行反向传播
loss.backward()
#进行梯度规范化,防止出现梯度爆炸或者梯度消失
torch.nn.utils.clip_grad_norm(model.parameters(),0.5)
#进行参数更新
optimizer.step()
#将损失值进行累加
total_loss +=loss.item()
# 获取当前开始时间
log_interval =200
#打印日志信息
if batch % log_interval ==0 and batch>0:
#计算平均损失
cur_loss = total_loss/log_interval
#计算训练到目前的耗时
elapsed = time.time() - start_time
#打印日志信息
print('| epoch {:3d} | {:5d}/{:5d} batches | '
'lr {:02.2f} | ms/batch {:5.2f} | '
'loss {:5.2f} | ppl {:8.2f}'.format(
epoch, batch, len(train_data) // bptt, scheduler.get_lr()[0],
elapsed * 1000 / log_interval,
cur_loss, math.exp(cur_loss)))
#每个打印批次借结束后,将总损失值清零
total_loss =0
start_time= time.time()
#评估函数
def evaluate(eval_model,data_source):
#eval_model,代表每轮训练后产生的模型
# data_source,代表验证集数据或者测试集数据
#首先开启评估模式
eval_model.eval()
#初始化总损失值
total_loss =0
#模型开启评估模式,不进行反向传播求梯度
with torch.no_grad():
#遍历验证数据
for i in range(0,data_source.size(0)-1,bptt):
#首先通过get_batch函数获取源数据和目标数据
data,targets = get_batch(data_source,i)
#将源数据放入评估模型中,进行预测
output = eval_model(data)
#对输出张量进行变形
output_flat = output.view(-1,ntokens)
#累加损失值
total_loss +=criterion(output_flat,targets).item()
#返回评估的总损失值
return total_loss
best_val_loss = float("inf")
epochs =3
best_model =None
for epoch in range(1,epochs +1):
epoch_start_time = time.time()
train()
val_loss = evaluate(model,val_data)
print('-'*50)
print('| end of epoch {:3d} | time: {:5.2f}s | valid loss {:5.2f} | '
'valid ppl {:8.2f}'.format(epoch, (time.time() - epoch_start_time),
val_loss, math.exp(val_loss)))
print('-'*50)
#通过比较当前轮次的损失值,获取最佳模型
if val_loss<best_val_loss:
best_val_loss=val_loss
best_model=model
#每个轮次后调整优化器的学习率
scheduler.step()
#添加测试的流程代码
test_loss = evaluate(best_model,test_data)
print('-'*90)
print('|End of training |test loss {:5,2f}'.format(test_loss))
print('-'*50)
没有运行成功