记录从零到实现CRF+LSTM的整个过程
- 查找概述,了解实现的过程【1h30min】
- 建立模型【3h30min】
- 阅读代码,对于实现细节有进一步的了解【4h】
- 自己手动实现【】
背景知识
NER
神经网络成为可以有效处理许多NLP任务的模型。这类方法对于序列标注任务(如CWS、POS、NER)的处理方式是类似的,将token从离散one-hot表示映射到低维空间中成为稠密的embedding,随后将句子的embedding序列输入到RNN中,用神经网络自动提取特征,Softmax来预测每个token的标签。
缺点在于对于token打标签的时候是独立的分类,不能够直接利用上文已经预测的标签。
为了解决这个问题,提出LSTM+CRF模型做序列标注,在LSTM层后接入CRF层来做句子级别的标签预测,使得标注过程不再是对各个token独立分类。
LSTM
信息来源:《Neural Network Methods for NLP》
LSTM,即为Long Short-Term Memory,是目前最成功的RNN架构类型之一。
从名字可以看出,其设计的主要目的是解决RNN的梯度消失问题和梯度爆炸问题。门机制能够使得与记忆部分相关的梯度保留很长时间。因此,RNN能够在更长的序列中有更好的表现。
主要手段是首次引入门机制。所谓的门机制,简单来说,就是利用一个0-1列向量,通过hadamard乘积来控制对于历史/记忆的获取。
具体来说,LSTM架构将状态向量
s
i
s_i
si分成两部分,一部分称之为memory cell
c
i
c_i
ci,被设计用来保存记忆,每次计算的时候通过忘记门向量f控制记忆的获取、通过输入门向量i控制更新的获取;另一部分称之为working memory
h
i
h_i
hi,隐藏状态组件。
信息来源:人人都能看懂的LSTM
图一:LSTM和普通RNN结构对比图
左边为普通的RNN结构,只有一个传递状态。
右边为LSTM结构,有两个传递状态c和h。前者改变的较慢,主要由历史经验决定;后者改变的较快,在不同节点下往往差别较大。
图二:LSTM内部结构解析
LSTM内部有三个阶段:
阶段一:忘记阶段。
简言之,从历史数据中选择最重要的。
用数学公式表示即为用
z
f
z^f
zf作为忘记门控,来控制上一个状态哪一部分需要保留。
z
f
.
∗
c
t
−
1
z^f.*c^{t-1}
zf.∗ct−1
阶段二:选择记忆阶段。
简言之,从输入数据中选择最重要的。
用数学公式表示即为用
z
i
z^i
zi作为输入门控,来控制输入哪一部分需要重点保留。
z
i
.
∗
z
z^i.*z
zi.∗z
c
t
=
z
f
.
∗
c
t
−
1
+
z
i
.
∗
z
c^t=z^f.*c^{t-1}+z^i.*z
ct=zf.∗ct−1+zi.∗z
阶段三:输出阶段。
简言之,控制输出。
用数学公式表示即为用
z
o
z^o
zo作为输出门控,来控制输出。
h
t
=
z
o
.
∗
t
a
n
h
(
c
t
)
h^t=z^o.*tanh(c^t)
ht=zo.∗tanh(ct)
其中,门控向量的计算方法:
z
g
a
t
e
=
s
i
g
m
o
i
d
(
W
g
a
t
e
[
x
t
;
h
t
−
1
]
)
z^{gate}=sigmoid(W^{gate}[x^t ;h^{t-1}])
zgate=sigmoid(Wgate[xt;ht−1])
sigmoid函数的目的是将值映射为0-1之间,从而可以作为门向量。
z
=
t
a
n
h
(
W
[
x
t
;
h
t
−
1
]
)
z=tanh(W[x^t;h^{t-1}])
z=tanh(W[xt;ht−1])
模型建立
主要思路:
- 将token从离散one-hot表示映射到低维空间中成为稠密的embedding
- 将句子的embedding序列输入到LSTM中,用神经网络自动提取特征
- 用CRF做句子级别的标签预测
模型示意图:
大致可分为五层:
- Embedding层
输入:字对应的id列表以及分词信息特征
目标:将字转化为低维稠密向量
操作:预先训练好n维词向量模型,通过查询得到每一个字的n维向量。n维向量与分词信息特征向量一起输出到dropout层。
这里预训练的embedding可以捕捉全局上单词的相似度。 - dropout层
输入:字对应的n维向量以及分词信息特征向量
目标:缓解过拟合
操作: - bi-lstm层
输入:字对应的n维向量
目标:自动提取句子特征
操作:将每个字的char embedding序列作为bi-lstm的各个时间步的输入,再将正向LSTM输出的隐状态序列与反向LSTM的在各个位置输出的隐状态按照位置进行拼接,得到完整的隐状态序列。
这里可以捕捉字内部的一些规律。与预训练的embedding结合,可以得到更好的词嵌入。 - project层
线性层。
输入:bi-lstm输出的隐状态序列
目标:得到自动提取的句子特征
操作:将隐状态序列映射到标签数的维数,从而可以将每一维视作字到标签的打分值。此时如果使用softmax可以直接得到分类结果,但是没办法应用上其他位置上已经标注过的信息,所以接下来再接入一个CRF层。 - loss层
内嵌CRF层。
输入:字对于标签序列的打分
目标:句子级的序列标注
操作:重新打分;打分分成两部分,LSTM输出的打分加上CRF的转移矩阵(即由一个标签转移到另一个标签的转移得分)。得到打分之后,进而利用Softmax得到归一化之后的概率。
参考链接:
代码实现
整体
Bi-LSTM Conditional Random Field Discussion(官方文档居然有!整体的实现!)
Bi-LSTM层
以下链接展示了如何使用Pytorch写LSTM,这里就不再过多重复。
- PyTorch 中的 LSTM模型参数解释
- nn.LSTM 代码文档(代码文档是真的清楚!)
- PyTorch 实现序列模型和基于LSTM的循环神经网络(主要参考)