NER任务的CRF-layer的原理 |
文章目录
- 一. 预备工作
- 二. BILSTM-CRF模型
- 2.1. BiLSTM层输出
- 2.2. 如果没有CRF层会怎么样
- 2.3. CRF层可以从训练数据中学到约束
- 三. CRF层
- 3.1. 发射(Emission)分数
- 3.2. 转移(Transition)分数
- 3.3. CRF损失函数
- 3.4. 实际路径得分
- 3.5. 所有可能的路径的得分
- 3.5.1. 步骤1: 回想一下CRF损失函数
- 3.5.2. 步骤2: 回忆一下Emission和Transition得分
- 3.5.3. 步骤3: 开始战斗(准备好纸笔)
- 3.6. 为新的句子推理标签
- 四. 参考文献
- 用命名实体识别任务来解释CRF:https://arxiv.org/abs/1603.01360,该文提出了一个使用词和字嵌入的 BiLSTM-CRF 命名实体识别模型。下将以本文中的模型为例来解释CRF层是如何工作的。
- 更详细的文章请参考该系列:https://createmomo.github.io/2017/09/12/CRF_Layer_on_the_Top_of_BiLSTM_1/
一. 预备工作
- 假设有一个数据集,有两个实体类型,
Person和Organization
。但是,事实上,在我们的数据集中,我们有5个实体标签:
- B-Person
- I- Person
- B-Organization
- I-Organization
- O
- 此外,x\mathbf xx 是一个包含5个单词的句子,w0,w1,w2,w3,w4w_0,w_1,w_2,w_3,w_4w0,w1,w2,w3,w4。更重要的是,在句子 x\mathbf xx 中,[w0,w1][w_0,w_1][w0,w1] 是一个
Person
实体,[w3][w_3][w3] 是一个Organization
实体,其他都是“O”
。
二. BILSTM-CRF模型
- 如下图所示:
- 首先,将句子 x\mathbf xx 中的每个单词表示为一个向量,其中包括 单词的嵌入和字符的嵌入。字符嵌入是随机初始化的。词嵌入通常是从一个预先训练的词嵌入文件导入的。所有的嵌入将在训练过程中进行微调。
- 第二,BiLSTM-CRF模型的输入是这些嵌入,输出是句子 x\mathbf xx 中的单词的预测标签。
2.1. BiLSTM层输出
- 虽然不需要知道BiLSTM层的细节,但是为了更容易的理解CRF层,我们需要知道BiLSTM层输出的意义是什么。
- 上图说明BiLSTM层的输出是每个标签的分数。例如,对于 w0w_0w0, BiLSTM节点的输出为1.5 (B-Person)、0.9 (I-Person)、0.1 (B-Organization)、0.08 (I-Organization)和0.05 (O),这些分数将作为 CRF层的输入。
- 然后,将BiLSTM层预测的所有分数输入CRF层。在CRF层中,选择预测得分最高的标签序列作为最佳答案。
2.2. 如果没有CRF层会怎么样
- 你可能已经发现,即使没有CRF层,也就是说,我们可以训练一个BiLSTM命名实体识别模型,如下图所示。
- 因为每个单词的BiLSTM的输出是标签分数。我们可以选择每个单词得分最高的标签。
- 例如,对于w0w_0w0,“B-Person”得分最高(1.5),因此我们可以选择“B-Person”作为其最佳预测标签。同样,我们可以为w0w_0w0选择“I-Person”,为w2w_2w2选择“O”,为w3w_3w3选择“B-Organization”,为w4w_4w4选择“O”。
- 虽然在这个例子中我们可以得到正确的句子 x\mathbf xx的标签,但是并不总是这样。再试一下下面图片中的例子。
- 显然,这次的输出是无效的,“I-Organization I-Person”和“B-Organization I-Person”。
2.3. CRF层可以从训练数据中学到约束
- CRF层可以向最终的预测标签添加一些约束,以确保它们是有效的。这些约束可以由CRF层在训练过程中从训练数据集自动学习。
- 约束条件可以是:
- 句子中第一个单词的标签应该以“B-”或“O”开头,而不是“I-”
- “B-label1 I-label2 I-label3 I-…”,在这个模式中,label1、label2、label3…应该是相同的命名实体标签。例如,“B-Person I-Person”是有效的,但是“B-Person I-Organization”是无效的。
- “O I-label”无效。一个命名实体的第一个标签应该以“B-”而不是“I-”开头,换句话说,有效的模式应该是“O B-label”
- 有了这些有用的约束,无效预测标签序列的数量将显著减少。
三. CRF层
- 下面了解为什么CRF层可以学习这些约束。 在CRF层的损失函数中,我们有两种类型的分数。这两个分数是CRF层的关键概念。
3.1. 发射(Emission)分数
- 第一个是emission分数。这些emission分数来自BiLSTM层。例如,如下图所示,标记为B-Person的 w0w_0w0 的分数为1.5。
- 为了方便起见,我们将 给每个标签一个索引号,如下表所示。
Label | index |
---|---|
B-Person | 0 |
I-Person | 1 |
Organization | 2 |
I-Organization | 3 |
O | 4 |
- 我们用 xiyjx_{iy_j}xiyj来表示emission分数。iii 是word的索引,yjy_jyj 是label的索引。如上一节图中所示,xi=1,yj=2=xw1,B−Organization=0.1x_{i=1, y_j=2}=x_{w_1, B-Organization}=0.1xi=1,yj=2=xw1,B−Organization=0.1,即w1w_1w1作为B-Organization的得分为0.1。
3.2. 转移(Transition)分数
- 转移概率矩阵(Transition Probability Matrix):矩阵各元素都是非负的,并且各行元素之和等于 111,各元素用概率表示,在一定条件下是互相转移的,故称为转移概率矩阵。P(k)P^{(k)}P(k) 表示 kkk 步转移概率矩阵。转移概率矩阵有如下特征:
- 0≤Pij≤10 \leq P_{ij} \leq 10≤Pij≤1,各元素值都是处于0到1之前。
- ∑j=1nPij=1\displaystyle\sum^{n}_{j=1}P_{ij}=1j=1∑nPij=1,即矩阵中每一行转移概率之和等于1。
- 我们使用 tyiyjt_{y_iy_j}tyiyj 来表示transition分数。例如,tB−Person,I−Person=0.9t_{B-Person, I-Person}=0.9tB−Person,I−Person=0.9 表示标签的transition,B−Person→I−PersonB-Person \rightarrow I-PersonB−Person→I−Person 得分为0.9。因此,我们有一个transition得分矩阵,它存储了所有标签之间的所有得分。
- 为了使transition评分矩阵更健壮,我们将添加另外两个标签,START和END。START是指一个句子的开头,而不是第一个单词。END表示句子的结尾。
- 下面是一个transition得分矩阵的例子,包括额外添加的START和END标签。
- 如上表所示,我们可以发现transition矩阵已经学习了一些有用的约束。
- 句子中第一个单词的标签应该以“B-”或“O”开头,而不是“I-”开头 (从“START”到“I- person或I- organization”的transition分数非常低)
- “B-label1 I-label2 I-label3 I-…”,在这个模式中,label1、label2、label3…应该是相同的命名实体标签。例如,“B-Person I-Person”是有效的,但是“B-Person I-Organization”是无效的。(例如,从“B-Organization”到“I-Person”的分数只有0.0003,比其他分数低很多)
- “O I-label”无效。一个被命名实体的第一个标签应该以“B-”而不是“I-”开头,换句话说,有效的模式应该是“O B-label”(同样,tO,I−Persont_{O, I-P e r s o n}tO,I−Person的分数非常小)
- 你可能想问一个关于矩阵的问题。在哪里或如何得到transition矩阵?
- 实际上,该矩阵是BiLSTM-CRF 模型的一个参数。在训练模型之前,可以随机初始化矩阵中的所有transition分数。所有的随机分数将在你的训练过程中自动更新。换句话说,CRF层可以自己学习这些约束。我们不需要手动构建矩阵。随着训练迭代次数的增加,分数会逐渐趋于合理。
3.3. CRF损失函数
- CRF损失函数由 真实路径得分 和 所有可能路径的总得分 组成。在所有可能的路径中,真实路径的得分应该是最高的。 例如,如果我们的数据集中有如下表所示的这些标签:
- 例如,如果我们的数据集中有如下表所示的这些标签:
- 我们还是有一个5个单词的句子。可能的路径是:
- ①START B-Person B-Person B-Person B-Person B-Person END
- ②START B-Person I-Person B-Person B-Person B-Person END
- …
- 101010 START B-Person I-Person O B-Organization O END
- NNN …
- 假设每条可能的路径都有一个分数 PiP_iPi,并且总共有NNN条可能的路径,所有路径的总分数是 Ptotal =P1+P2+…+PN=eS1+eS2+…+eSNP_{\text {total }}=P_1+P_2+\ldots+P_N=e^{S_1}+e^{S_2}+\ldots+e^{S_N}Ptotal =P1+P2+…+PN=eS1+eS2+…+eSN。(在第3.4节中,我们将解释如何计算,你也可以把它当作这条路径的分数)
- 如果我们说第10条路径是真正的路径,换句话说,第10条路径是我们的训练数据集提供的黄金标准标签。在所有可能的路径中,得分 P10P_{10}P10 应该是百分比最大的。
- 在训练过程中,我们的BiLSTM-CRF模型的参数值将会一次又一次的更新,以保持增加真实路径的分数百分比。
LossFunction=PRealPathP1+P2+…+PN(1)LossFunction=\frac{P_{RealPath}}{P_1+P_2+\ldots+P_N} \tag{1} LossFunction=P1+P2+…+PNPRealPath(1)
- 现在的问题是:①如何定义一个路径的分数?②如何计算所有可能路径的总分?③当我们计算总分时,我们需要列出所有可能的路径吗?(这个问题的答案是否定的)
3.4. 实际路径得分
- 在3.3节中,我们假设每条可能的路径都有一个得分,并且有N条可能的路径,所有路径的总得分为 Ptotal=P1+P2+…+PN=eS1+eS2+…+eSNP_{total}=P_1+P_2+\ldots+P_N=e^{S_1}+e^{S_2}+\ldots+e^{S_N}Ptotal=P1+P2+…+PN=eS1+eS2+…+eSN。
- 显然,在所有可能的路径中,一定有一条是真实路径。对于这个例子来说,前面中句子的实际路径是 “START B-Person I-Person O B-Organization O END”。其他的是不正确的,如“START B-Person B-Organization O I-Person I-Person B-Person”。eSie^{S_i}eSi是第iii条路径的得分。
- 在训练过程中,CRF损失函数只需要两个分数: 真实路径的分数和所有可能路径的总分数。所有可能路径的分数中,真实路径分数所占的比例会逐渐增加。
- 计算实际路径分数eSie^{S_i}eSi非常简单。这里我们主要关注的是Si{S_i}Si的计算。
- 选取真实路径,“START B-Person I-Person O B-Organization O END”,我们以前用过,例如:
- ①我们有一个5个单词的句子,w1,w2,w3,w4,w5w_1,w_2,w_3,w_4, w_5w1,w2,w3,w4,w5
- ②我们增加了两个额外的单词来表示一个句子的开始和结束,w0,w6w_0,w_6w0,w6
- ③Si{S_i}Si 由两部分组成:Si=S_i=Si= EmissionScore +++ TransitionScore
- Emission得分:
EmissionScore =x0,START+x1,B−Person+x2,I−Person+x3,O+x4,B−Organization+x5,O+x6,END(2)\text { EmissionScore }=\\ x_{0, START}+x_{1, B-Person}+x_{2, I-Person}+x_{3, O}+x_{4, B-Organization }+x_{5, O}+x_{6, E N D}\tag{2} EmissionScore =x0,START+x1,B−Person+x2,I−Person+x3,O+x4,B−Organization+x5,O+x6,END(2)- xindex,labelx_{index, label}xindex,label 是第index个单词被label标记的分数
- 这些得分x1,B−Persan;x2,I−Person;x3,O;x4,B−Organization;x5,Ox_{1, B-P e r s a n};x_{2, I-P e r \operatorname{son}}; x_{3, O} ;x_{4, B-Organization}; x_{5, O}x1,B−Persan;x2,I−Person;x3,O;x4,B−Organization;x5,O 来自之前的BiLSTM输出。
- 对于x0,STARTx_{0, START}x0,START 和 x6,ENDx_{6, E N D}x6,END,我们可以把它们设为0。
- Transition得分:
TransitionScore =tSTART−>B−Person+tB−Person−>I−Person+tI−Person−>O+tO−>B−Orgnization+tB−Orgnization−>O+tO−>END(3)\text { TransitionScore }= \\t_{S T A R T->B- Person}+t_{B-Person->I-Person} +t_{I-Person-> O}+\\ t_{O->B-Orgnization}+t_{B-Orgnization->O}+t_{O-> END}\tag{3} TransitionScore =tSTART−>B−Person+tB−Person−>I−Person+tI−Person−>O+tO−>B−Orgnization+tB−Orgnization−>O+tO−>END(3)
- 综上所述,现在我们可以计算出Si{S_i}Si以及路径得分eSie^{S_i}eSi。
3.5. 所有可能的路径的得分
- 如何逐步计算一个toy例子一个句子的所有可能的路径的总分。
- 在上一节中,我们学习了如何计算一个路径(即)的标签路径得分。到目前为止,我们还有一个需要解决的问题,就是如何得到所有路径的总分(Ptotal=P1+P2+…+PN=eS1+eS2+…+eSNP_{total}=P_1+P_2+\ldots+P_N=e^{S_1}+e^{S_2}+\ldots+e^{S_N}Ptotal=P1+P2+…+PN=eS1+eS2+…+eSN)。
- 衡量总分最简单的方法是:列举所有可能的路径并将它们的分数相加。是的,你可以用这种方法计算总分。然而,这是非常低效的。训练的时间将是难以忍受的。
- 在探索以下内容之前,我建议你先准备一张白纸和一支笔,并按照示例中列出的步骤进行操作。我相信这将有助于你更好地理解算法的细节。此外,你应该知道如何用你喜欢的编程语言实现它。
3.5.1. 步骤1: 回想一下CRF损失函数
- 在3.3中,我们将CRF损失函数定义为:
LossFunction=PRealPathP1+P2+…+PNLossFunction=\frac{P_{RealPath}}{P_1+P_2+\ldots+P_N} LossFunction=P1+P2+…+PNPRealPath- 现在我们 把loss函数变成log loss函数:
LogLossFunction=logPRealPathP1+P2+…+PN(4)LogLossFunction=\log\frac{P_{RealPath}}{P_1+P_2+\ldots+P_N}\tag{4} LogLossFunction=logP1+P2+…+PNPRealPath(4)- 因为当我们训练一个模型时,通常我们的目标是 最小化 我们的损失函数,我们加上一个负号:
LogLossFunction=−logPRealPathP1+P2+…+PN=−logeSRealPatheS1+eS2+…+eSN这里根据前面的定义=−[log(eSRealPath)−log(eS1+eS2+…+eSN)]=−[SRealPath−log(eS1+eS2+…+eSN)]=−[∑i=1Nxiyi+∑i=1N−1tyiyi+1−log(eS1+eS2+…+eSN)]前面两项就是真实(5)\begin{aligned}LogLossFunction & =-\log \frac{P_{RealPath}}{P_1+P_2+\ldots+P_N} \\ & =-\log \frac{e^{S_{RealPath}}}{e^{S_1}+e^{S_2}+\ldots+e^{S_N}} \text{这里根据前面的定义}\\ & =-\left[\log \left(e^{S_{RealPath}}\right)-\log \left(e^{S_1}+e^{S_2}+\ldots+e^{S_N}\right)\right] \\ & =-\left[S_{RealPath}-\log \left(e^{S_1}+e^{S_2}+\ldots+e^{S_N}\right)\right] \\ & =-\left[\sum_{i=1}^N x_{i y i}+\sum_{i=1}^{N-1} t_{y_i y_{i+1}}-\log \left(e^{S_1}+e^{S_2}+\ldots+e^{S_N}\right)\right] \text{前面两项就是真实} \end{aligned}\tag{5} LogLossFunction=−logP1+P2+…+PNPRealPath=−logeS1+eS2+…+eSNeSRealPath这里根据前面的定义=−[log(eSRealPath)−log(eS1+eS2+…+eSN)]=−[SRealPath−log(eS1+eS2+…+eSN)]=−[i=1∑Nxiyi+i=1∑N−1tyiyi+1−log(eS1+eS2+…+eSN)]前面两项就是真实(5)- 在上一节中,我们已经知道如何计算实际路径得分,现在我们需要找到一个有效的解决方案来计算log(eS1+eS2+…+eSN)\log \left(e^{S_1}+e^{S_2}+\ldots+e^{S_N}\right)log(eS1+eS2+…+eSN)
3.5.2. 步骤2: 回忆一下Emission和Transition得分
- 为了简化,我们假设我们从这个句子中训练我们的模型,它的 长度只有3:
x=[w0,w1,w2]\mathbf x=[w_0, w_1, w_2]x=[w0,w1,w2]- 此外,在我们的数据集中,我们有 两个标签:
LabelSet={l1,l2}LabelSet=\{l_1, l_2\}LabelSet={l1,l2}- 我们还有Bi-LSTM层 输出的Emission分数: xijx_{ij}xij表示 wiw_iwi 被标记为 ljl_jlj 的得分。参考3.1
l1l_1l1 | l2l_2l2 | |
---|---|---|
w0w_0w0 | x01x_{01}x01 | x02x_{02}x02 |
w1w_1w1 | x11x_{11}x11 | x12x_{12}x12 |
w2w_2w2 | x21x_{21}x21 | x22x_{22}x22 |
- 此外,Transition分数来自CRF层:tijt_{ij}tij表示标签 iii 到标签 jjj 的Transition得分。参考3.2
l1l_1l1 | l2l_2l2 | |
---|---|---|
l1l_1l1 | t11t_{11}t11 | t12t_{12}t12 |
l2l_2l2 | t21t_{21}t21 | t22t_{22}t22 |
3.5.3. 步骤3: 开始战斗(准备好纸笔)
- 记住:我们的目标是: log(eS1+eS2+…+eSN)\log \left(e^{S_1}+e^{S_2}+\ldots+e^{S_N}\right)log(eS1+eS2+…+eSN)
- 这个过程就是分数的累加。其思想与动态规划相似(如果你不知道什么是动态编程,也可以继续阅读本文)。我将逐步解释这个例子。但我强烈建议你学习动态规划算法。
- 简而言之,计算 w0w_0w0 的所有可能路径的总分。然后,我们用总分来计算 w0→w1w_0→w_1w0→w1。最后,我们使用最新的总分来计算w0→w1→w2w_0→w_1→w_2w0→w1→w2。我们需要的是最后的总分。
- 在接下来的步骤中,你将看到两个变量:obs和previous。previous存储前面步骤的最终结果。obs表示当前单词的信息。
- 实际上,第二次迭代已经完成。如果有人想知道如何计算所有可能路径的总分 (label1→label1, label1→label2, label2→label1, label2→label2),从 w0到w1w_0到w_1w0到w1,可以做如下计算。
- 我们使用新的previous中的元素:TotalScore(w0→w1)=log(eprevious [0]+eprevious [1])=log(elog(ex01+x11+t11+ex02+x11+t21)+elog(ex01+x12+t12+ex02+x12+t22))=log(ex01+x11+t11+ex02+x11+t21+ex01+x12+t12+ex02+x12+t22)\begin{aligned} & \operatorname{TotalScore}\left(w_0 \rightarrow w_1\right) \\ & =\log \left(e^{\text {previous }[0]}+e^{\text {previous }[1]}\right) \\ & =\log \left(e^{\log \left(e^{x_{01}+x_{11}+t_{11}}+e^{x_{02}+x_{11}+t_{21}}\right)}+e^{\log \left(e^{x 01+x 12+t 12}+e^{x 02+x 12+t_{22}}\right)}\right) \\ & =\log \left(e^{x_{01}+x_{11}+t_{11}}+e^{x_{02}+x_{11}+t_{21}}+e^{x_{01}+x_{12}+t_{12}}+e^{x_{02}+x_{12}+t_{22}}\right) &\end{aligned}TotalScore(w0→w1)=log(eprevious [0]+eprevious [1])=log(elog(ex01+x11+t11+ex02+x11+t21)+elog(ex01+x12+t12+ex02+x12+t22))=log(ex01+x11+t11+ex02+x11+t21+ex01+x12+t12+ex02+x12+t22)
- 你发现了吗?这正是我们的目标:log(eS1+eS2+…+eSN)\log \left(e^{S_1}+e^{S_2}+\ldots+e^{S_N}\right)log(eS1+eS2+…+eSN)
- 在这个等式中,我们可以看到:
S1=x01+x11+t11(label1→label1)S2=x02+x11+t21(label2→label1)S3=x01+x12+t12(label1→label2)S4=x02+x12+t22(label2→label2)\begin{aligned} & S_1=x_{01}+x_{11}+t_{11}\left(\text {label}_1 \rightarrow \text {label}_1\right) \\ & S_2=x_{02}+x_{11}+t_{21}\left(\text {label}_2 \rightarrow \text {label}_1\right) \\ & S_3=x_{01}+x_{12}+t_{12}\left(\text {label}_1 \rightarrow \text {label}_2\right) \\ & S_4=x_{02}+x_{12}+t_{22}\left(\text {label}_2 \rightarrow \text {label}_2\right) \end{aligned} S1=x01+x11+t11(label1→label1)S2=x02+x11+t21(label2→label1)S3=x01+x12+t12(label1→label2)S4=x02+x12+t22(label2→label2)
- 恭喜!
- 我们达到了目标,log(eS1+eS2+…+eSN)\log \left(e^{S_1}+e^{S_2}+\ldots+e^{S_N}\right)log(eS1+eS2+…+eSN),我们的toy句子有三个单词,label set有两个label,所以一共应该有8种可能的label path。 虽然你发现这个过程相当复杂,但是实现这个算法要容易得多。使用计算机的优点之一是可以完成一些重复性的工作。现在你可以自己实现CRF损失函数,并开始训练自己的模型。
- 详细可以参考该链接:BiLSTM上的CRF,用命名实体识别任务来解释CRF(2)损失函数
3.6. 为新的句子推理标签
- 在前面的章节中,我们学习了BiLSTM-CRF模型的结构和CRF损失函数的细节。你可以通过各种开源框架(Keras、Chainer、TensorFlow等)实现自己的BiLSTM-CRF模型。最重要的事情之一是模型的反向传播是在这些框架上自动计算的,因此你不需要自己实现反向传播来训练你的模型(即计算梯度和更新参数)。此外,一些框架已经实现了CRF层,因此将CRF层与你自己的模型结合起来非常容易,只需添加一行代码即可。
- 这部分直接参考文章:BiLSTM上的CRF,用命名实体识别任务来解释CRF(3)推理
四. 参考文献
- https://createmomo.github.io/2017/09/12/CRF_Layer_on_the_Top_of_BiLSTM_1/
- https://createmomo.github.io/2017/10/08/CRF-Layer-on-the-Top-of-BiLSTM-3/
- 详细可以参考该链接:BiLSTM上的CRF,用命名实体识别任务来解释CRF(2)损失函数
- https://github.com/createmomo/CRF-Layer-on-the-Top-of-BiLSTM