tutorial教程(详解Tutorial代码的学习过程与准备)
简介:本文主要分析Pytorch教程中的BiLSTM_CRF代码,几乎每一行代码都有注释。希望这篇文章能帮助你理解这个教程。此外,借助代码和图表来理解条件随机场(CRF)将会有所帮助,因为本教程代码主要实现CRF。
1知识准备
在阅读教程之前,你需要有一些理论或知识基础,包括LSTM单位,BiLSTM-CRF模型,CRF原理以及代码中一些函数的使用。参考资料涵盖了所涉及的主要知识,可以用教程学习。
理解通用报告格式中归一化因子Z(x)的计算
条件随机字段中的Z(x)表示归一化因子,它是一个句子中所有可能的标签序列路径的得分之和。一般我们有一个直接的思路,就是列出所有可能的路径,计算每条路径的得分然后相加。如上图的例子所示,有5个字符和5个标签。如果按照上述暴力穷举法计算,就会有路径组合。但是在我们的实际工作中,可能会有更长的序列,更多的标签。这时候暴力穷举法就有些低效了。于是我们考虑用分数累加的方法来计算所有路径的总分数,即先计算所有到达路径的总分数,再计算所有路径在-中的分数,再计算-。-”,最后我们得到我们分数的总和。这个想法来自下面的等价等式:
上式表明,直接计算整个句子序列的全局得分相当于计算每一步的路径得分之和,可以大大减少计算时间,因此Pytorch教程中的_forward_alg()函数就是据此实现的。这种计算每一步的路径分值并相加的方法也可以在下图中计算出来。
3了解CRF中的序列解码过程,即维特比算法。
如上图所示,在每个时间步,比如' word==go to '列,在每个标签处(0~6竖框是标签的id),注意两个值:上一个时间步所有标签到当前标签的最大总分,以及这个最大值对应的上一个时间步标签的id。这样每个标签在前一个时间步记录自己的最优路径,最后通过标签的id回溯,就可以得到最终的最优标签序列。这部分对应Pytorch教程中_viterbi_decode()函数的实现。
4理解log_sum_exp()函数
Pytorch教程中log_sum_exp()函数最终返回的计算方法数学推导如下:
5 Pytorch教程代码部分注释有助于理解
进口火炬
将torch.nn作为nn导入
将torch.optim作为optim导入
#手动设置随机种子,保证初始化参数相同,这样模型就可以重现了。
火炬.手册_种子(1)
#获取每行的最大索引idx
def argmax(vec):
#获取每行的最大索引idx
_,idx=torch.max(vec,1)
#返回每行中最大值的位置索引
返回idx.item()
#将序列中的单词转换成数字(int)表示
定义准备序列(seq,to_ix):
#将序列中的单词转换成数字(int)表示
idx=[to_ix[c] for c in seq]
返回torch.tensor(idx,dtype=torch.long)
#正向算法是不断积累的结果,所以会有劣势。
#指数和累加到一定程度后,会超过计算机浮点值的最大值。
#变成了inf,所以取log后也是inf。
#为了避免这种情况,使用合适的值clip=max来提取指数和的公因数。
#这不会使项目变得太大而无法计算
Log _ sum _ exp (vec): # vec:类似于[[标记元素]]
#取vec中的最大值
max_score=vec[0,argmax(vec)]
# vec.size () [1]:标签的数量
max _ score _ broadcast=max _ score . view(1,-1)。expand(1,vec.size()[1])
#先减。减去最大值,避免E的指数级和计算机溢出。
#相当于torch . log(torch . sum(torch . exp(vec)),防止E的索引造成计算机溢出。
返回max _ score torch . log(torch . sum(torch . exp(vec-max _ score _ broadcast)))
BiLSTM_CRF类(nn。模块):
#初始化参数
def __init__(self,vocab_size,tag_to_ix,embedding_dim,hidden_dim):
超级(BiLSTM_CRF,self)。__init__()
#单词嵌入维度
self . embedding _ dim=embedding _ dim
# BiLSTM隐藏层尺寸
self.hidden_dim=hidden_dim
#字典的大小
self.vocab_size=vocab_size
#标签到号码的映射
self.tag_to_ix=tag_to_ix
#标签号
self.tagset_size=len(tag_to_ix)
# num _ embeddings (int):词汇大小字典的大小
# embedding _ dim(int):embedding _ dim嵌入一个向量的维数,即用多少维来表示一个符号?
self.word_embeds=nn。嵌入(vocab_size,embedding_dim)
# input_size: embedding_dim _ dim输入数据的特征维数通常是embedding_dim(词向量的维数)。
# hidden _ size:hidden _ dimlstm中隐藏层的尺寸
# num_layers:递归神经网络的层数
默认使用# Bias,默认不使用dropout
# bidirectional=True使用双向LSTM
#设置为单层双向
#隐藏层设置为指定尺寸的一半,方便后期拼接。
#//表示整数除法,返回不大于结果的最大整数。
self.lstm=nn。LSTM(嵌入_维度,隐藏_维度//2,
num_layers=1,双向=真)
#将BiLSTM提取的特征向量映射到特征空间,即通过全连接得到发射得分。
# in _ features:hidden _ dim _ dim每个输入样本的大小
# out_features:tagset_size每个输出样本的大小
self.hidden2tag=nn。线性(hidden_dim,self.tagset_size)
#转移矩阵的参数初始化,并且transition [i,j]表示从第j个标签到第I个标签的转移分数。
self.transitions=nn。参数(torch.randn(self.tagset_size,self.tagset_size))
#初始化所有其他标签转移到START_TAG的分数很小,就是无法从其他标签转移到START_TAG。
#初始化STOP_TAG转移到所有其他标签的分值很小,也就是不能有STOP_TAG转移到其他标签。
# CRF,T [I,J]的转移矩阵表示从J标签到I标签的转移,
self . transitions . data[TAG _ to _ IX[START _ TAG],]=-10000
self.transitions.data[:tag_to_ix[STOP_TAG]]=-10000
#初始化LSTM的参数
self.hidden=self.init_hidden()
#使用随机正态分布来初始化LSTM的h0和c0
#否则,模型会自动初始化为零,尺寸为[层数*方向数,批量大小,隐藏尺寸]]
def init_hidden(自身):
return (torch.randn(2,1,self.hidden_dim //2),
torch.randn(2,1,self.hidden_dim //2))
#计算归一化因子Z(x)
def _forward_alg(自己,专长):
'''
输入:排放分数实际上是LSTM的输出。
句子的每个单词都经过BiLSTM,对应每个标签的分数。
输出:所有可能路径分数的总和/归一化因子/分区函数/Z(x)
'''
#通过正向算法进行递归计算
#初始化单行tagset_size列的嵌套列表
init_alphas=torch.full((1,self.tagset_size),-10000。)
#初始化step 0,即起始位置的启动分数,START_TAG取0,其他位置取-10000。
init _ alpha s[0][self . TAG _ to _ IX[START _ TAG]]=0。
#包装在变量中用于自动反向传播
forward_var=init_alphas
#重复整个句子
#专长:形状像[[。],映射到标签的每个词的传输概率,
# [。],
# [。]]
因功绩而获奖:
#存储当前时间步长中每个标签的分数
alphas_t=[]
对于范围内的下一个标记(self.tagset_size):
#取出当前标签的发射分数(与上一时间步的标签无关),展开到标签维度。
emit_score=feat[next_tag]。视图(1,-1)。expand(1,self.tagset_size)
#取出从先前标签转移的当前标签的转移分数
trans _ score=self . transitions[next _ tag]。视图(1,-1)
#当前路径的分数:上一时间步分数转移分数传输分数。
next _ tag _ var=forward _ var trans _ score emit _ score
#取当前分数的对数-总和-指数
alphas _ t . append(log _ sum _ exp(next _ tag _ var))。视图(1))
# Update forward_var递归计算下一个时间步
# torch.cat默认是逐行添加的。
forward_var=torch.cat(alphas_t)。视图(1,-1)
#考虑最终转向STOP_TAG
terminal _ var=forward _ var self . transitions[self . TAG _ to _ IX[STOP _ TAG]]
#取当前分数的对数-总和-指数
分数=log_sum_exp(terminal_var)
返回分数
#通过BiLSTM提取特征
def _get_lstm_features(self,句子):
#初始化LSTM的h0和c0
self.hidden=self.init_hidden()
#使用前面构建的单词嵌入为语句中的每个单词(word_id)生成一个矢量表示
#并将形状更改为[[seq_len,1(batch_size),embedding_dim]]
embeds=self.word_embeds(句子)。view(len(句子),1,-1)
# LSTM网络根据输入字向量和初始状态h0和c0
#计算输出结果lstm_out和最后状态hn和cn
lstm_out,self.hidden=self.lstm(embeds,self.hidden)
lstm _ out=lstm _ out . view(len(sentence),self.hidden_dim)
#转换为单词-标记([[seq_len,tagset_size]])表格
#可以看作是每个词被标注为对应标签的得分,也就是维特比算法中的传输矩阵。
lstm _ feats=self . hidden 2 tag(lstm _ out)
返回lstm_feats
#计算标签序列路径的分数
def _score_sentence(自我、专长、标签):
# feats排放分数矩阵
#计算给定标签序列的分数,即路径的分数
score=torch.zeros(1)
# tags前面是句头标记,便于计算迁移分数。
tags=torch . cat([torch . tensor([self . TAG _ to _ IX[START _ TAG]],dtype=torch.long),tags])
# Loop用于计算给定标签序列的分数
对我来说,功绩在列举(feats):
#递归计算路径分数:转移分数和排放分数
# T [I,J]表示J转移到I。
score=score self . transitions[tags[I 1],tags[i]] feat[tags[i 1]]
#加上转移到句尾的分数,就得到gold_score
score=score self . transitions[self . TAG _ to _ IX[STOP _ TAG],tags[-1]]
返回分数
# veterbi解码以获得最佳标签序列
def _viterbi_decode(自我,专长):
'''
:参数技能:传输分数矩阵
'''
#以后很容易追踪到最佳路径。
反向指针=[]
#初始化维特比的forward_var变量
init_vvars=torch.full((1,self.tagset_size),-10000。)
init _ vvars[0][self . TAG _ to _ IX[START _ TAG]]=0
# forward_var表示每个标签的转发状态分值,即前一个词作为每个标签对应的分值输入。
forward_var=init_vvars
#遍历每个时间步时的排放分数
因功绩而获奖:
#记录当前单词对应的每个标签的最优转移节点。
#保存当前时间步的回溯指针
bptrs_t=[]
#对应bptrs_t,记录对应的最优值
#保存当前时间步长的维特比变量
viterbivars_t=[]
#遍历每个标签,并获得当前单词被键入的每个标签的分数。
#并将其添加到当前单词的发射矩阵feat中,得到当前状态,即下一个单词的转发状态。
对于范围内的下一个标记(self.tagset_size):
# transitions [next_tag]表示每个标签到next_tag的转换分数。
# forward_var表示每个标签的转发状态分值,即前一个词作为每个标签对应的分值输入。
#将它们加在一起,以获得作为next_tag键入的当前单词的所有可能的分数。
# Viterbi算法在记录最优路径时只考虑上一步的得分和前一标签到当前标签的转移得分。
#不依赖于当前标签发射分数
next _ tag _ var=forward _ var self . transitions[next _ tag]
#获取从最后一个可能的标签到当前标签的具有最大分数的标签位置索引id
best _ tag _ id=arg max(next _ tag _ var)
#在BPTRS _ T中存储最佳标签的位置索引。
bptrs_t.append(best_tag_id)
#添加对应于最佳标签位置索引的值
viterbivars _ t . append(next _ tag _ var[0][best _ tag _ id])。视图(1))
# update forward_var=当前单词的发射分数feat当前标签状态下的先前最佳标签的分数。
forward _ var=(torch . cat(viterbivars _ t)feat)。视图(1,-1)
#回溯指针记录当前时间步中每个标签源上一步的最优标签。
backpointers.append(bptrs_t)
# forward_var表示每个标签的转发状态分数。
#加上转移到结束标记STOP_TAG的转移分数。
terminal _ var=forward _ var self . transitions[self . TAG _ to _ IX[STOP _ TAG]]
#获取标签STOP_TAG的前一时间步的最优标签位置索引。
best _ tag _ id=arg max(terminal _ var)
#获取标签STOP_TAG的当前最优标签对应的分值。
path _ score=terminal _ var[0][最佳标签标识]
#根据流程中存储的换乘路径节点,反演最优换乘路径。
#通过回溯指针解码最优路径。
最佳路径=[最佳标签标识]
# best_tag_id作为线程头,反向遍历后指针,寻找最佳路径。
对于反向bptrs _ t(反向指针):
best _ tag _ id=BP TRS _ t[best _ tag _ id]
最佳路径.追加(最佳标签标识)
#删除开始标记
start=best_path.pop()
#初始转移节点必须是人工构造的START_TAG,删除,并据此确认路径的正确性。
assert START==self . TAG _ to _ IX[START _ TAG]
#最后,逆向路径,从头得到最佳转移路径best_path。
best_path.reverse()
返回路径分数,最佳路径
#损失函数损失
def neg_log_likelihood(自我,句子,标签):
#获得与句子相对应的排放得分矩阵
功勋=自我。_get_lstm_features(句子)
#通过正向算法获得归一化因子Z(x)
forward_score=self。_forward_alg(专长)
#获取标签序列的路径分数
gold_score=self。_score_sentence(专长、标签)
返回forward_score - gold_score
#输入语句序列以获得最佳标记路径及其分数
def forward(自我,句子):#不要把这个和上面的_forward_alg混淆。
#从BiLSTM获取排放分数矩阵
lstm_feats=self。_get_lstm_features(句子)
#使用维特比算法解码并计算最佳标签路径及其分数。
score,tag_seq=self。_viterbi_decode(lstm_feats)
返回分数,标记序列
START_TAG="《START》"
STOP_TAG="《STOP》"
#单词嵌入维度
EMBEDDING_DIM=5
# LSTM隐藏层尺寸
HIDDEN_DIM=4
#培训数据
训练_数据=[(
“《华尔街日报》今天报道苹果公司赚钱了”。拆分(),
“B I,我爱你,B I,我爱你”。拆分()
), (
“佐治亚理工学院是佐治亚州的一所大学”。拆分(),
“B I O O O B”。拆分()
)]
word_to_ix={}
#建立一个单词索引表,并将其数字化以供计算机处理
对于句子,training_data中的标签:
对于句子中的单词:
如果单词不在word_to_ix中:
word _ to _ IX[word]=len(word _ to _ IX)
#建立标签索引表并将其数字化以便计算机处理
tag_to_ix={"B": 0,"I": 1,"O": 2,START_TAG: 3,STOP_TAG: 4}
#初始化模型参数
model=BiLSTM _ CRF(len(word _ to _ IX),tag_to_ix,EMBEDDING_DIM,HIDDEN_DIM)
#使用随机梯度下降法(SGD)优化参数。
# model.parameters()在这个实例中是一个可优化的参数,
# lr:学习率,weight _ decrease:防止模型过拟合的正则化系数。
optimizer=optim。SGD(模型参数(),lr=0.01,权重衰减=1e-4)
#在no_grad模式下,检测正向推理。功能是暂时不计算导数,以减少计算量和内存消耗。
#训练前检查模型预测结果。
with torch.no_grad():
#获取训练数据中的第一个句子序列,并将其转换为数字
pre check _ sent=prepare _ sequence(training _ data[0][0],word_to_ix)
#数字化对应于训练数据中第一个句子序列的标签序列。
pre check _ tags=torch . tensor([tag _ to _ IX[t]for t in training _ data[0][1]],dtype=torch.long)
打印(型号(预检查_发送))
# 300轮迭代训练
对于范围内的历元(300):
对于句子,training_data中的标签:
#第一步。每次开始前清除最后一轮的参数梯度,以防止累积影响。
零grad()
#第二步。seq和tags分别数字化为sentence_in和targets。
句子_输入=准备_序列(句子,单词_至_九)
目标=火炬。张量([tag _ to _ IX[t]for t in tags],dtype=torch.long)
#第三步。损失函数失败
损失=model.neg_log_likelihood(句子_输入,目标)
#第四步。通过调用optimizer.step()计算损失、梯度、更新参数
loss.backward()
optimizer.step()
# torch.no_grad()是一个上下文管理器,被该语句包起来的部分将不会轨道梯度
# 训练结束查看模型预测结果,对比观察模型是否学到
with torch.no_grad():
pre check _ sent=prepare _ sequence(training _ data[0][0],word_to_ix)
打印(型号(预检查_发送))
#我们成功了!
欢迎交流指正
参考资料:
[1]torch.max()使用讲解
https://www.jianshu.com/p/3ed11362b54f
[2]火炬。手动_种子()用法
https://www.cnblogs.com/dychen/p/13920000.html
[3]比尔斯特姆-通用报告格式原理介绍Pytorch _教程代码解析
https://blog.csdn.net/misite_j/article/details/109036725
[4]关于nn。嵌入函数的理解
https://blog.csdn.net/a845717607/article/details/104752736
[5]火炬。nn。LSTM()详解
https://blog.csdn.net/m0_45478865/article/details/104455978
[6]pytorch函数之nn .线性的
https://www.cnblogs.com/Archer-Fang/p/10645473.html
[7]pytorch之torch.randn()
https://blog.csdn.net/zouxiaolv/article/details/99568414
[8]torch.full()
https://blog.csdn.net/Fluid_ray/article/details/109855155
[9]PyTorch中视角的用法
https://blog.csdn.net/york1996/article/details/81949843
https://blog.csdn.net/zkq_1986/article/details/100319146
[10]torch.cat()函数
https://blog.csdn.net/xinjieyuan/article/details/105208352
[11]高级:动态决策和双LSTM通用报告格式
https://py火炬。组织/教程/初学者/NLP/advanced _教程. html
[12]条件随机场理论理解
https://blog.csdn.net/qq_27009517/article/details/107154441
13 py torch教程- BiLSTM CRF代码解析
https://blog.csdn.net/ono_online/article/details/105089750lyn
推荐阅读
- 打造“易派客”这个“互联网+供应链”的新型工业品电商平台
- 最有效的臭虫药,什么臭虫药最好、可以根治的臭虫药
- 律师24小时免费咨询?(免费律师咨询在线)
- 2021年山东高考准考证打印入口(山东省高考打印准考证时间)
- 如何将多个文件压缩打包,怎样将多个文件压缩打包成一个文件
- 佳得乐携手足球巨星为年轻女运动员注入自信力量
- 海信电视是2024年欧洲杯赛事的VAR提供商
- X Games和SONIC DriveIn首次合作举办活动创造历史
- TicketRev与迈阿密马林鱼队宣布多年合作伙伴关系
- 海信U7N Mini LED ULED电视为您带来非凡足球视野
- 职业排球联合会首个赛季表现强劲
- InventHelp发明家为旅行者开发新型睡眠面罩
- 苏福尔斯机场Fairfield Inn&Suites酒店进行翻新
- 圣地亚哥芝麻街乐园宣布Sunny Day Café全新与艾摩和朋友们一起用餐体验开业日期
- Gravity Haus欢迎行业领袖Mike DeFrino加入董事会
- 乔什奥康纳和卡莉史派妮出演利刃出鞘3
- 安德鲁斯科特加入利刃出鞘3
- 达文乔伊兰道夫加入永恒剧组
- 凯莉华盛顿出演第三部利刃出鞘电影
- 长虹手机好用吗(长虹手机怎么样)