基于中文简历的命名实体识别
文章目录
基于中文简历的命名实体识别项目(持续更新中)
命名实体识别是一个极具挑战性的任务。首先,在大多数语言和领域中,训练数据非常少;其次,对可以成为实体的单词类型的限制很小,因此很难从小样本数据集训练出一个泛化性强的模型。本项目尝试了多种模型算法(如HMM, CRF,Bi-LSTM + CRF)来处理中文命名实体识别的问题。数据集来自 Chinese NER using Lattice LSTM
(ACL 2018)中收集到的简历数据集。
数据
总共的数据集有 4775(大概5000条数据),按照以下的 8:1:1 分成训练集、验证集和测试集。
数据集分类 | 数量 |
---|---|
train set | 3821 |
dev set | 477 |
test set | 477 |
数据的格式如下,它的每一行由一个字及其对应的标注组成,标注集采用BIOES,句子之间用一个空行隔开。
|
|
训练数据 词典大小总数 1792,tag 总数28
一份简历信息可以划分成借个中长句子。每一条就是一个训练数据集。
|
|
特征向量(对于一条训练数据集而言)
|
|
一元语法(unigram)、二元语法(bigram)、三元语法(trigram)和多元词法(n-gram),指的是文本中连续出现的n 个词语。对于英文来说 gram 越大,那么上下文信息越是明显,但是训练的时间成本相对变大;对于中文来说,使用unigram 就可以(左右各一个)
BFGS 算法是一种拟牛顿算法。
为什么使用BiLSTM,有人认为这种倒叙的是没有意义的。但是在实际工程中,BilSTM 的效果十有八九是好于LSTM,所以一般时候都是这样使用的。双向LSTM 的结构也很简单,就是两个单向LSTM 分别沿着正序和反序进行传播,然后将最后输出拼接在一起。注意该层输出的size 是 2 * hidden_size
。
代码
(1)数据预处理阶段:
输入是一个list,返回一个dictionary,其中key 是list 内容,val 是相应的index,所以完成了 char2index 的功能。实现比较巧妙 maps[e] =len(maps)
|
|
只有在训练数据集上需要建立词典索引和tag 索引,在验证集和测试集上是不需要建立索引的。
(2)HMM 模型
train 阶段
HMM 的训练就是根据训练语料对模型参数进行估计:我们有观测序列和对应的状态序列,然后使用极大似然的方法估计hmm 模型的参数。无论是转移概率矩阵还是观测概率矩阵,都是做的一个统计的工作。以观测矩阵为例
|
|
问题:当某个元素没有出现过的时候,该位置为0。解决方案是,将0替换成很小的数字,比如说 $e ^{-10}$。
decoding阶段
使用维特比算法,给定观测序列求解状态序列,这个是就是生成标注的过程。维特比算法实际上使用动态规划解hmm模型预测问题,基于dp 求解概率最大路径。
维特比算法(viterbi algorithm)使用的是动态规划的思想解决HMM 和CRF 中的预测问题。即在HMM模型中寻找最有可能的隐状态的路径。使用一句话概括为:在每一时刻,计算当前时刻是由上一个时刻中的每种隐状态导致的最大概率,并且记录这个最大概率是从前一时刻哪个隐状态转移过来的,最后回溯最大概率,即为路径。
DP 递推方程
\begin{equation} \delta_{t}(j)=\max \left[\delta_{t-1}(i) \times a_{i j} \times b_{j}\left(o_{t}\right)\right] \end{equation}
- $\delta_{t}(j)$ : $t$ 时刻沿着一条状态路径 $q_1$, $q_2$, $q_t$, 当 $t$ 时刻处于$j$状态,产生$o_1$, $o_2$,… $o_t$的最大的概率
- $a_{ij}$ :从状态$i$ 到状态 $j$ 的转移概率
- $b_j(o_t)$ :从状态 $j$ 到输出$o_t$ 的概率
时间复杂度 $O(TN^2)$,其中$T$ 表示时刻, $N$表示有多少种隐状态, $N^2$表示隐状态的组合。
问题1: 当$T$很长的时候,连乘容易下溢。解决方案:使用对数概率,这样相乘变成了相加。 问题2:如果某个词不再字典中,那么假设其为均匀分布。 问题3:最优路径的计算。正向传播的时候,记录当前时刻最大概率是由上一时刻哪个隐状态转换过来的,然后回溯求解最大值。
(3)CRF 模型
对于中文的特征函数写的比较简单。
|
|
(4)Bi-LSTM
|
|
文本的数据预处理无非是将文本转换成模型可以理解的数字,在训练lstm 的时候需要加上四个特殊的token:
- < PAD>: 补全字符。
- < EOS>: 解码器端的句子结束标识符。
- < UNK>: 低频词或者一些未遇到过的词等。
- < GO>: 解码器端的句子起始标识符。
这种基于 len()
的实现实在是太骚了
|
|
如果一个类中只有函数的(如 utils.py
),那么使用 from utils import fun1
就可以;如果某个类中包含类crf.py
, 在引用的时候,使用 from crf import CRF
的语句。
(5)Bi-LSTM +CRF模型
为了使转移分数矩阵更具鲁棒性,我们加上START 和 END两类标签。START代表一个句子的开始(不是句子的第一个单词),END代表一个句子的结束。对于这种写法是非常有条理的:train,evaluation 和test 数据集是需要经过不同的处理,然后可以写一个函数,引入不同的参数,表示不同的处理。
|
|
如果是 bi-lstm
模型,因为相当于是一个语言模型,
通过softmax 可以获得输出序列的概率 $p(y| X)$,然后取对数,这个时候的损失函数就定义为 $- \log p(y|X)$,使用最大似然估计求解。到了 bi-lstm + CRF
中是可以使用各种损失函数的。
LSTM without CRF 的损失函数
|
|
LSTM with CRF的损失函数。相比于上一个损失函数,该损失函数增加了对于生成label之间的约束。具体如下:
输入数据 $X$ 表示如下:
\begin{equation} \mathbf{X}=\left(\mathbf{x} _{1}, \mathbf{x} _{2}, \dots, \mathbf{x} _{n}\right) \end{equation}
\begin{equation} \mathbf{y}=\left(y_{1}, y_{2}, \ldots, y_{n}\right) \end{equation}
we consider $P$ to be the matrix of scores output by the bidirectional LSTM network. P is of size $n × k$, where k is the number of distinct tags, and $P_{i,j} corresponds to the score of the $j$ th tag of the $i$th word in a sentence. For a sequence of predictions
其中分数 score 定义为: \begin{equation} s(\mathbf{X}, \mathbf{y})=\sum_{i=0}^{n} A_{y_{i}, y_{i+1}}+\sum_{i=1}^{n} P_{i, y_{i}} \end{equation}
$A$ 是转移矩阵,$A_{i, j}$表示从tags $i$ 到 tags$j$的转移。
对结果使用softmax 可以得到:
\begin{equation} p(\mathbf{y} | \mathbf{X})=\frac{e^{s(\mathbf{X}, \mathbf{y})}}{\sum_{\widetilde{\mathbf{y}} \in \mathbf{Y}_{\mathbf{X}}} e^{s(\mathbf{X}, \widetilde{\mathbf{y}})}} \end{equation}
在训练过程中,经常将结果取对数。最终的tag 用 $y^*$表示为:
\begin{equation} \mathbf{y}^{*}=\underset{\tilde{\mathbf{y}} \in \mathbf{Y}_{\mathbf{X}}}{\operatorname{argmax}} s(\mathbf{X}, \widetilde{\mathbf{y}}) \end{equation}
{% fold 开/合 %}
|
|
{% endfold %}
如果数据集比较小,那么是可以通过切分的方式获得一个 batch size
的数据的。其中的预先排序感觉有点意思,这样保证一个batch 中长度基本上是保持一致的。
|
|
所以LSTM-CRF最大的特点就是:由LSTM提供特征,而且特征是有参数的,是可以学习的!因此它可能根据不同问题学到各种合适的底层特征;而CRF的特征是人工定义出来的,不可变的,我们最多改改这个特征的参数。
(6) ensemble多个模型
这个ensemble机制也是比较简单,就是众人投票机制。
|
|
其他函数。
下面有两个函数,第一个函数是能够递归的处理多层(三层及以上)的嵌套,第二个函数只能处理两层的嵌套。本质是需要采取递归的思路。
|
|
(7)评估模型
主要是针对precision, recall 和F1 的实现问题。按照行输出混淆矩阵。
{% fold 开/合 %}
|
|
{% endfold %}
(8)常见的参数
|
|
使用DropOut 提高了最后的结果
Initial experiments showed that character-level embeddings did not improve our overall performance when used in conjunction with pre-trained word representations. To encourage the model to depend on both representations, we use dropout training, applying a dropout mask to the final embedding layer just before the input to the bi-directional LSTM. We observe a significant improvement in our model’s performance after using dropout.
(8)手写viterbi 算法
{% fold 开/合 %}
|
|
{% endfold %}
实验结果
下面结果是不同类别加权平均和得来的。
hmm | crf | bi-lstm | bilstm+crf | ensemble | |
---|---|---|---|---|---|
precision | 91.49% | 95.43% | 95.37% | 95.74% | 95.69% |
recall | 91.22% | 95.43% | 95.32% | 95.72% | 95.65% |
F1 | 91.30% | 95.42% | 95.32% | 95.70% | 95.64% |
可以看出bilstm+crf
得到了最好的结果 $F1=95.70%$,当使用 ensemble时候,并不见得能够得到更好的结果,尤其是当子模型有较大的重合度且模型效果不是很好的时候。
数据特点:
(1)IOBES标注方式更加细化,相应的label 预测要求更加高。 作者提到了两种tagging scheme,一种是IOB标注形式的,这种形式标注集为{B、I、O},B表示命名实体的开头词,I表示命名实体非开始的词,O表示非命名实体词。另一个中是IOBES标注形式,标注集合为{B、I、E、O、S},添加的E表示命名实体结尾词,S表示单个词的命名实体。
优化点:
(1)CRF 模型中自定义更加丰富的特征函数,相对于HMM而言,能够表示上下文。 (2)损失函数是交叉熵。ilstm+crf 中参考的是16年一篇论文中计算$y_{pred}$,但是那篇论文中的数据集是针对外文的(英文,德文)等,主要思想是加上了 $A_{i,j}$ 转换约束 CRF: 通过上述的双向LSTM获得整个句子的表示后,一个简单有效的标记方法就是独立地为每个单词输出标签。尽管这种方法在简单任务如POS上很成功,但是它在输出标签有强相关性时有很大的局限性。NER就是这样,输出标签是有强相关性的,比如I-PER后不能接B-LOC。因此,使用条件随机场CRF来解决这个问题。
展望:
(1)基于少量的标注数据进行NER 也是研究的热点。一种常见的思路是使用海量无标注数据训练一个语言模型,然后使用这个训练好的语言模型获取当前要标注词的语言模型向量,然后将这些向量加入到原始的双向LSTM-RNN 的模型中。实验结果表明,加入语言模型向量可以提高NER 的效果。
文章作者 jijeng
上次更新 2019-12-21