语言模型
语言模型
1. 环境配置
Python .7.4 torch 1.5.1 torchtext 0.6.0 torchvision 0.6.1 numpy 1.16.5
2. 学习目标_语言模型
学习目标
学习语言模型,以及如何训练一个语言模型学习torchtext的基本使用方法 构建 vocabularyword to inde 和 index to word 学习tor
语言模型
1. 环境配置
Python .7.4
torch 1.5.1
torchtext 0.6.0
torchvision 0.6.1
numpy 1.16.5
2. 学习目标_语言模型
学习目标
- 学习语言模型,以及如何训练一个语言模型
- 学习torchtext的基本使用方法
- 构建 vocabulary
- word to inde 和 index to word
- 学习的一些基本模型
- Linear
- R
- LSTM
- GRU
- R的训练技巧
- Gradient Clipping
- 如何保存和读取模型
. 使用库的语法介绍
torchtext介绍和使用教程
4. 项目流程
- 我们使用 torchtext 来创建vocabulary, 然后把数据读成batch的格式。
- 定义模型R,LSTM模型介绍
- 继承nn.Module
- 初始化函数
- forward函数
- 其余可以根据模型需要定义相关的函数
- 初始化一个模型
- 定义评估模型
- 定义loss function和optimizer
- 定义训练模型:
- 模型一般需要训练若干个epoch
- 每个epoch我们都把所有的数据分成若干个batch
- 把每个batch的输入和输出都包装成cuda tensor
- forward pass,通过输入的句子预测每个单词的下一个单词
- 用模型的预测和正确的下一个单词计算cross entropy loss
- 清空模型当前gradient
- backward pass
- gradient clipping,防止梯度爆炸
- 更新模型参数
- 每隔一定的iteration输出模型在当前iteration的loss,以及在验证集上做模型的评估
- 计算perplexity
- 使用训练好的模型生成一些句子
5. 项目代码,部分运行结果与解析
import torch
import torchtext
from torchtext import data
from torchtext.vocab import Vectors
import numpy as np
import randomSEED = 511random.seed(SEED)
np.random.seed(SEED)
_seed(SEED)
_seed(SEED)
# torch.deterministic = TrueBATCH_SIZE = 2 #一个batch 有多少个句子
EMBEDDIG_SIZE = 500 #每个单词多少维
MAX_VOCAB_SIZE = 50000 # 单词总数
- 使用
torchtext
提供的LanguageModelingDataset
来处理语言数据
使用BPTTIterator
得到连续的句子
TEXT = torchtext.data.Field(lower = True)
# .Field这个对象包含了我们打算如何预处理文本数据的信息,这里定义单词全部小写train,val,test = \
torchtext.datasets.LanguageModelingDataset.splits(path = .,train = ,validation= text8. ,test = , text_field=TEXT)TEXT.build_vocab(train,max_size = MAX_VOCAB_SIZE)
print(vacabulary size:{}.format(len(TEXT.vocab)))
# #TEXT.vocab 就是定义好的词汇表# 查看生成的test
test
# 观察定义好的词汇表
print(TEXT.vocab.itos[0:50])
print(------*10)
print(list(TEXT.vocab.stoi.items())[0:50])
#生成连续的句子
VOCAB_SIZE = len(TEXT.vocab)
train_iter,val_iter,test_iter = \
torchtext.data.BPTTIterator.splits((train,val,test),batch_size = BATCH_SIZE,device = 0,bptt_len = 50,# 反向传播往回传的长度,这里我暂时理解为一个样本有多少个单词传入模型repeat = False,shuffle= True)
# BPTTIterator可以连续地得到连贯的句子,BPTT的全称是back propagation through time。
Iterator:标准迭代器BucketIerator:相比于标准迭代器,会将类似长度的样本当做一批来处理,
因为在文本处理中经常会需要将每一批样本长度补齐为当前批中最长序列的长度,
因此当样本长度差别较大时,使用BucketIerator可以带来填充效率的提高。
除此之外,我们还可以在Field中通过fix_length参数来对样本进行截断补齐操作。BPTTIterator: 基于BPTT(基于时间的反向传播算法)的迭代器,一般用于语言模型中。观察生成的batch
#观察生成的batch
print(next(iter(train_iter)))# 一个batch训练集维度
print(next(iter(val_iter)))
print(next(iter(test_iter)))#next(iterator[, default])
#返回iterator.__next__()的值,还可指定默认值,它指定在到达了迭代 器末尾时将返回的值
#iter(obj) 从可迭代对象创建一个迭代器 .简单地说,迭代器是包含方法__next__的对象,可用于迭代一组值。
#next(it) 让迭代器前进一步并返回下一个元素
#模型的输入是一串文字,模型的输出也是一串文字,他们之间相差一个位置,因为语言模型的目标是根据之前的单词预测下一个单词
it = iter(train_iter)
batch = next(it)
print( .join([TEXT.vocab.itos[i] for i in [:,1].data]))
print( .join([TEXT.vocab.itos[i] for i in [:,1].data]))
# string.join(sequence) 将string与sequence中的所有字符串元素合并,并返回结果
# X[:,1] 就是取所有行的第1个数据
#这里要取的数据是 next(iter(train_iter)).text[:,1].data
#torchtext.data: Generic data loaders, abstracti, and iterators for text (including vocabulary and word vectors)
#TEXT.vocab 就是定义好的词汇表
- 定义模型
.R
经典模板 背诵
经典模板_LSTM Word 语言模型上的(实验)动态量化
import torch
import as nnclass RModel(nn.Module): 一个简单的循环神经网络def __init__(self, rnn_type, ntoken, ninp, nhid, nlayers, dropout=0.5):# rnn_type;有两个层供选择 LSTM , GRU # ntoken:VOCAB_SIZE=50002# ninp:EMBEDDIG_SIZE = 650,输入层维度# nhid:EMBEDDIG_SIZE = 1000,隐藏层维度,这里是我自己设置的,用于区分ninp层。# nlayers:纵向有多少层神经网络 该模型包含以下几层:- 词嵌入层- 一个循环神经网络层(R, LSTM, GRU)- 一个线性层,从hidden state到输出单词表- 一个dropout层,用来做regularization super(RModel, self).__init__()self.drop = nn.Dropout(dropout) = nn.Embedding(ntoken, ninp)# 定义输入的Embedding层,用来把每个单词转化为词向量if rnn_type in [ LSTM , GRU ]: # 下面代码以LSTM举例self.rnn = getattr(nn, rnn_type)(ninp, nhid, nlayers, dropout=dropout)# getattr(nn, rnn_type) 相当于 nn.rnn_type# nlayers代表纵向有多少层。还有个参数是bidirectional: 是否是双向LSTM,默认falseelse:try:nonlinearity = { R_TAH : tanh , R_RELU : relu }[rnn_type]except KeyError:raise ValueError( An invalid option for `--model` was supplied,opti are [ LSTM , GRU , R_TAH or R_RELU ])self.rnn = nn.R(ninp, nhid, nlayers, nonlinearity=nonlinearity, dropout=dropout)self.decoder = nn.Linear(nhid, ntoken)# 最后线性全连接隐藏层的维度(1000,50002)self.init_weights()self.rnn_type = rnn_ = = nlayersdef init_weights(self):initrange = 0.1.weight.data.uniform_(-initrange, initrange)self.decoder.bias.data.zero_()self.decoder.weight.data.uniform_(-initrange, initrange)def forward(self, input, hidden): Forward pass:- word embedding- 输入循环神经网络- 一个线性层从hidden state转化为输出单词表 # input.shape = seg_length * batch = torch.Size([50, 2])# 如果觉得想变成2*50格式,可以在LSTM里定义batch_first = True# hidden = (nlayers * 2 * hidden_size, nlayers * 2 * hidden_size)# hidden是个元组,输入有两个参数,一个是刚开始的隐藏层h的维度,一个是刚开始的用于记忆的c的维度,# 这两个层的维度一样,并且需要先初始化,hidden_size的维度和上面nhid的维度一样 =1000,我理解这两个是同一个东西。emb = self.drop((input)) # # emb.shape=torch.Size([50, 2, 650]) # 输入数据的维度# 这里进行了运算(50,50002,650)*(50, 2,50002)output, hidden = self.rnn(emb, hidden)# output.shape = 50 * 2 * hidden_size # 最终输出数据的维度,# hidden是个元组,输出有两个参数,一个是最后的隐藏层h的维度,一个是最后的用于记忆的c的维度,这两个层维度相同 # hidden = (h层维度:nlayers * 2 * hidden_size, c层维度:nlayers * 2 * hidden_size)output = self.drop(output)decoded = self.decoder(output.view(output.size(0)*output.size(1), output.size(2)))# output最后的输出层一定要是二维的,只是为了能进行全连接层的运算,所以把前两个维度拼到一起,(50*2,hidden_size)# decoded.shape=(50*2,hidden_size)*(hidden_size,50002)=torch.Size([1600, 50002])return decoded.view(output.size(0), output.size(1), decoded.size(1)), hidden# 我们要知道每一个位置预测的是哪个单词,所以最终输出要恢复维度 = (50,2,50002)# hidden = (h层维度:2 * 2 * 1000, c层维度:2 * 2 * 1000)def init_hidden(self, bsz, requires_grad=True):# 这步我们初始化下隐藏层参数weight = next(self.parameters())# weight = torch.Size([50002, 650])是所有参数的第一个参数# 所有参数self.parameters(),是个生成器,LSTM所有参数维度种类如下:# print(list(iter(self.parameters())))# torch.Size([50002, 650])# torch.Size([4000, 650])# torch.Size([4000, 1000])# torch.Size([4000]) # 偏置项# torch.Size([4000])# torch.Size([4000, 1000])# torch.Size([4000, 1000])# torch.Size([4000])# torch.Size([4000])# torch.Size([50002, 1000])# torch.Size([50002])if self.rnn_type == LSTM :return (_zeros((, bsz, ), requires_grad=requires_grad),_zeros((, bsz, ), requires_grad=requires_grad))# return = (2 * 2 * 1000, 2 * 2 * 1000)# 这里不明白为什么需要_zeros,我估计是想整个计算图能链接起来# 这里特别注意hidden的输入不是model的参数,不参与更新,就跟输入数据x一样else:return _zeros((, bsz, ), requires_grad=requires_grad)# GRU神经网络把h层和c层合并了,所以这里只有一层。
初始化一个模型
nhid = 1000 # 我自己设置的维度,用于区分embeding_size=650
model = RModel(LSTM, VOCAB_SIZE, EMBEDDIG_SIZE, nhid, 2, dropout=0.5)
if USE_CUDA:model = ()model
定义评估模型的代码
- 模型的评估和模型的训练逻辑基本相同,唯一的区别是我们只需要forward pass,不需要backward pass
# 先从下面训练模式看起,在看evaluate
def evaluate(model, data):() # 预测模式total_loss = 0.it = iter(data)total_count = 0.with _grad():hidden = model.init_hidden(BATCH_SIZE, requires_grad=False)
# 这里不管是训练模式还是预测模式,h层的输入都是初始化为0,hidden的输入不是model的参数
# 这里model里的model.parameters()已经是训练过的参数。for i, batch in enumerate(it):data, target = , # # 取出验证集的输入的数据和输出的数据,相当于特征和标签if USE_CUDA:data, target = (), ()hidden = repackage_hidden(hidden) # 截断计算图with _grad(): # 验证阶段不需要更新梯度output, hidden = model(data, hidden)#调用model的forward方法进行一次前向传播,得到return输出值loss = loss_fn(output.view(-1, VOCAB_SIZE), target.view(-1))# 计算交叉熵损失total_count = (*data.size())
# 上面计算交叉熵的损失是平均过的,这里需要计算下总的损失
# total_count先计算验证集样本的单词总数,一个样本有50个单词,一个batch2个样本
# (*data.size()) =50*2=1600total_loss = loss.item()*(*data.size())
# 每次batch平均后的损失乘以每次batch的样本的总的单词数 = 一次batch总的损失loss = total_loss / total_count # 整个验证集总的损失除以总的单词数() # 训练模式return lossimport torch
import numpy as np
a = ((5,))
print(a.size())
(*a.size())
定义下面的一个function,帮助我们把一个hidden state和计算图之前的历史分离。
# Remove this part
def repackage_hidden(h):Wraps hidden states in new Tensors, to detach them from their history.if isinstance(h, torch.Tensor): # 这个是GRU的截断,因为只有一个隐藏层# 判断h是不是torch.Tensorreturn h.detach() # 截断计算图,h是全的计算图的开始,只是保留了h的值#参考[]else: # 这个是LSTM的截断,有两个隐藏层,格式是元组return tuple(repackage_hidden(v) for v in h)
定义loss function和optimizer
loss_fn = nn.CrossEntropyLoss() # 交叉熵损失
learning_rate = 0.001
optimizer = Adam(model.parameters(), lr=learning_rate)
scheduler = lr_scheduler.ExponentialLR(optimizer, 0.5)
# 每调用一次这个函数,lenrning_rate就降一半,0.5就是一半的意思
训练模型:
模型一般需要训练若干个epoch
每个epoch我们都把所有的数据分成若干个batch
把每个batch的输入和输出都包装成cuda tensor
forward pass,通过输入的句子预测每个单词的下一个单词
用模型的预测和正确的下一个单词计算cross entropy loss
清空模型当前gradient
backward pass
gradient clipping,防止梯度爆炸
更新模型参数
每隔一定的iteration输出模型在当前iteration的loss,以及在验证集上做模型的评估
import copy
GRAD_CLIP = 1.
UM_EPOCHS = 2val_losses = []
for epoch in range(UM_EPOCHS):() # 训练模式it = iter(train_iter) # iter,生成迭代器,这里train_iter也是迭代器,不用iter也可以hidden = model.init_hidden(BATCH_SIZE) # 得到hidden初始化后的维度for i, batch in enumerate(it):data, target = , # 取出训练集的输入的数据和输出的数据,相当于特征和标签if USE_CUDA:data, target = (), ()hidden = repackage_hidden(hidden)
# 语言模型每个batch的隐藏层的输出值是要继续作为下一个batch的隐藏层的输入的
# 因为batch数量很多,如果一直往后传,会造成整个计算图很庞大,反向传播会内存崩溃。
# 所有每次一个batch的计算图迭代完成后,需要把计算图截断,只保留隐藏层的输出值。
# 不过只有语言模型才这么干,其他比如翻译模型不需要这么做。
# repackage_hidden自定义函数用来截断计算图的。model.zero_grad() # 梯度归零,不然每次迭代梯度会累加output, hidden = model(data, hidden)# output = (50,2,50002)loss = loss_fn(output.view(-1, VOCAB_SIZE), target.view(-1))
# output.view(-1, VOCAB_SIZE) = (1600,50002)
# target.view(-1) =(1600),关于pytorch中交叉熵的计算公式请看下面链接。
# .backward()#反向传播,计算当前梯度._grad_norm_(model.parameters(), GRAD_CLIP)# 防止梯度爆炸,设定阈值,当梯度大于阈值时,更新的梯度为阈值optimizer.step()#根据梯度更新网络参数if i % 1000 == 0:print(epoch, epoch, iter, i, loss, loss.item())if i % 10000 == 0:val_loss = evaluate(model, val_iter)if len(val_losses) == 0 or val_loss < min(val_losses):# 如果比之前的loss要小,就保存模型print(best model, val loss: , val_loss)torch.save(model.state_dict(), )else: # 否则loss没有降下来,需要优化scheduler.step() # 自动调整学习率optimizer = Adam(model.parameters(), lr=learning_rate)# 学习率调整后需要更新optimizer,下次训练就用更新后的val_losses.append(val_loss) # 保存每10000次迭代后的验证集损失损失# 加载保存好的模型参数
best_model = RModel(LSTM, VOCAB_SIZE, EMBEDDIG_SIZE, nhid, 2, dropout=0.5)
if USE_CUDA:best_model = best_()
best_model.load_state_dict(torch.load())
# 把模型参数load到best_model里
使用最好的模型计算perplexity
val_loss = evaluate(best_model, val_iter)
print(perplexity: , (val_loss))
test_loss = evaluate(best_model, test_iter)
print(perplexity: , (test_loss))
Reference
邱锡鹏 深度学习
经典模板_LSTM Word 语言模型上的(实验)动态量化
5. LSTM Pytorch load
PyTorch中在反向传播前为什么要手动将梯度清零?@知乎 Pascal
七月在线面试题库
#感谢您对电脑配置推荐网 - 最新i3 i5 i7组装电脑配置单推荐报价格的认可,转载请说明来源于"电脑配置推荐网 - 最新i3 i5 i7组装电脑配置单推荐报价格
上传时间: 2024-01-25 09:36:45
上一篇:中小企业软文推广发布的小技巧
下一篇:适合小本创业的必做的赚钱项目
推荐阅读
留言与评论(共有 17 条评论) |
本站网友 血城 | 17分钟前 发表 |
nhid | |
本站网友 月经期不能吃的食物 | 1分钟前 发表 |
nonlinearity = { R_TAH | |
本站网友 电影团购网 | 5分钟前 发表 |
650])是所有参数的第一个参数# 所有参数self.parameters(),是个生成器,LSTM所有参数维度种类如下:# print(list(iter(self.parameters())))# torch.Size([50002 | |
本站网友 自虐方法 | 9分钟前 发表 |
val | |
本站网友 冠心病的治疗 | 19分钟前 发表 |
R_RELU | |
本站网友 如何瘦小腿 | 17分钟前 发表 |
1000])# torch.Size([50002])if self.rnn_type == LSTM | |
本站网友 来个身份证 | 5分钟前 发表 |
self).__init__()self.drop = nn.Dropout(dropout) = nn.Embedding(ntoken | |
本站网友 cd1钢丝绳电动葫芦 | 15分钟前 发表 |
test_iter = \ torchtext.data.BPTTIterator.splits((train | |
本站网友 生物术除皱 | 28分钟前 发表 |
abstracti | |
本站网友 cp126 | 24分钟前 发表 |
dropout=0.5) | |
本站网友 宇洋中央金座 | 23分钟前 发表 |
data | |
本站网友 admob | 13分钟前 发表 |
2 * 2 * 1000)# 这里不明白为什么需要_zeros,我估计是想整个计算图能链接起来# 这里特别注意hidden的输入不是model的参数,不参与更新,就跟输入数据x一样else | |
本站网友 fangdichan | 27分钟前 发表 |
还有个参数是bidirectional | |
本站网友 smartqq | 15分钟前 发表 |
nonlinearity=nonlinearity | |
本站网友 大水冲了龙王庙 | 11分钟前 发表 |
# 不过只有语言模型才这么干,其他比如翻译模型不需要这么做 | |
本站网友 卡客风暴 | 25分钟前 发表 |
input |