这个系列咱们来聊聊序列标示中的中文实体辨认问题,第一章让咱们从当时比较通用的基准模型Bert+Bilstm+CRF说起,看看这个模型现已处理了哪些问题还有哪些问题待处理。以下模型完成和评价脚本,详见 Github-DSXiangLi/ChineseNER。Repo里上传了在MSRA上练习好的bert_bilstm_crf, bilstm_crf模型以及serving相关的代码, 能够开箱即用哟~

NER问题笼统

实体辨认需求从文本中抽取两类信息,不同类型的实体本身token组合的信息(实体长啥样),以及实体呈现的上下文信息(实体在哪里)一种解法便是经过序列标示把以上问题转化成每个字符的分类问题,label主要有两种其间BIO更常见些

  • BIO:B符号实体的开端,I符号其余部分,非实体是O
  • BMOES:B符号开端,E符号结束,中心是M,单字实体是S,非实体是O

干流中文实体辨认使命都是字符级输入,考虑后面会用到Bert-finetune统一用bert-tokenizer做字符到ID的映射。不以中文分词作为输入粒度的原因也很简略,其一分词本身的准确率约束了NER的天花板,其二不同范畴NER的词粒度和分词的粒度会存在差异进一步影响模型表现。怎么引进中文词粒度信息,之后会经过词汇增强的方式来完成。以下是练习数据的Demo

中文NER的那些事儿1. Bert-Bilstm-CRF基线模型详解&代码实现

NER评价

NER评价分为Tag等级(B-LOC,I-LOC)和Entity等级(LOC),一般以entity的micro F1-score为准。由于tag猜测准确率高但是抽取出的entity有误,例如鸿沟过错,在实践应用时仍旧抽取的是过错的实体。repo中的evalution.py会针对猜测成果别离核算Tag和Entity的目标,以下是Bert-bilstm-crf在MSRA数据集上的表现

中文NER的那些事儿1. Bert-Bilstm-CRF基线模型详解&代码实现

以上Entity等级的目标,针对猜测抽取的实体和实践标示的实体进行核算

\begin{align} precision &= \frac{猜测正的确体}{猜测得到实体}\\ recall &= \frac{猜测正的确体}{实践实体} \\ F1 &=\frac{2*precision*recall}{precision+recall}\\ \end{align}

有两种聚合方式,micro是直接全体求precision,recall和F1。macro是别离核算各个实体类型的precision, recall和F1然后简略均匀得到全体的成果。考虑到不同实体类型的占比往往存在差异(尤其是对细分类别的NER使命),所以全体评价一般以micro为准。在MSRA和people daily数据集上不同不大,由于只要3类实体且数据量差不太多。

基线模型 Bert-Bilstm-CRF

来看下基准模型的完成,输入是wordPiece tokenizer得到的tokenid,进入Bert预练习模型抽取丰富的文本特征得到batch_size * max_seq_len * emb_size的输出向量,输出向量过Bi-LSTM从中提取实体辨认所需的特征,得到batch_size * max_seq_len * (2*hidden_size)的向量,终究进入CRF层进行解码,核算最优的标示序列。

中文NER的那些事儿1. Bert-Bilstm-CRF基线模型详解&代码实现

def build_graph(features, labels, params, is_training):
    input_ids = features['token_ids']
    label_ids = features['label_ids']
    input_mask = features['mask']
    segment_ids = features['segment_ids']
    seq_len = features['seq_len']
    embedding = pretrain_bert_embedding(input_ids, input_mask, segment_ids, params['pretrain_dir'],
                                        params['embedding_dropout'], is_training)
    load_bert_checkpoint(params['pretrain_dir'])  # load pretrain bert weight from checkpoint
    lstm_output = bilstm(embedding, params['cell_type'], params['rnn_activation'],
                         params['hidden_units_list'], params['keep_prob_list'],
                         params['cell_size'], params['dtype'], is_training)
    logits = tf.layers.dense(lstm_output, units=params['label_size'], activation=None,
                             use_bias=True, name='logits')
    add_layer_summary(logits.name, logits)
    trans, log_likelihood = crf_layer(logits, label_ids, seq_len, params['label_size'], is_training)
    pred_ids = crf_decode(logits, trans, seq_len, params['idx2tag'], is_training)
    crf_loss = tf.reduce_mean(-log_likelihood)
    return crf_loss, pred_ids

下面让咱们从bottom到top,一层层来看Bert,bilstm和crf别离处理了哪些问题~

Layer1 – Bert:Contextual Embedding/Pretrain Language Model

处理问题:NER标示数据少,文本信息抽取作用不佳

paper:Semi-supervised sequence tagging with bidirectional language models

作者是18年提出ELMo的大神,他在16年就提出引进预练习言语模型能够提高序列标示作用。NER使命需求的文本信息能够大致分成词信息,考虑上下文的词信息,以及信息到实体类型的映射。paper指出预练习词向量(指Word2vec/glove这类静态不考虑上下文的词嵌入),只涵盖基于共现的独立词信息,而考虑上下文的词信息仍是要用有限的NER标示数据来练习,往往会导致信息抽取作用欠好,以及泛化作用欠好。

于是作者在大规模无标示数据集上练习了双向LM,由BiLSTM的forward和bachward层拼接得到文本表征,用LM模型来协助抽取更全面/通用的文本信息。在NER模型中第一层BiLSTM从NER标示数据中学习上下文信息,第二层BiLSTM的输入由第一层输出和LM模型的输出拼接得到,这样就能够结合小样本练习的文本表征和更加通用LM的文本表征。

中文NER的那些事儿1. Bert-Bilstm-CRF基线模型详解&代码实现

放在当下,预练习言语模型现已从Elmo一路到了各种bert,用法也和paper中略有差异。考虑到Bert强壮的信息记忆和抽取才能,咱们能够直接把Bert放在最底层用于抽取考虑上下文的文本信息。这儿我比照了用bert的输入token embedding练习的bilstm-crf和finetune bert+bilstm-crf的作用。能够看到bert在entity等级的F1提高是惊人的,当然这个提高关于大样本的标示数据或许不会这么显着。

中文NER的那些事儿1. Bert-Bilstm-CRF基线模型详解&代码实现

中文NER的那些事儿1. Bert-Bilstm-CRF基线模型详解&代码实现

顺便说一下在完成的时候由于用了Bert所以要参加[SEP],[CLS]和[PAD],其时纠结了下这是都映射到1个label?仍是各自映射到一个label?最后考虑到后面CRF搬运矩阵的核算仍是觉得各自映射到1个label会比较适宜,看了下练习时打出的summary会发现step=100(其实应该在10曾经)模型就现已完美的学到[SEP],[CLS],[PAD]了,所以对成果不会有任何影响滴

中文NER的那些事儿1. Bert-Bilstm-CRF基线模型详解&代码实现

Layer2 – BiLSTM层真的需求么?

处理问题:抽取用于实体分类的包括上下文的文本信息

paper:Bidirectional LSTM-CRF Models for Sequence Tagging

16年的paper算是首篇把BiLSTM-CRF用于NER使命的测验。Bilstm的存在是提取双向文本信息。和大都文本使命相同,假如想要speed up练习速度会考虑用CNN来代替RNN,想要捕捉kernel_size长度之外的信息,能够测验stack-CNN或许拼接不同长度kernel_size的CNN。其时这些都是SOTA等级的模型,不过放在BERT出生后的今日,bilstm/cnn作为文本上下文信息提取的作用究竟还有多大嘞?

我简略比较了Bert-bilstm-crf,Bert-cnn-crf和Bert-crf在msra和people_daily数据集上的作用。在msra上的确有提高,不过在people daily上Bert-crf作用最好。全体上感觉bert把需求的信息都做了提取,bilstm仅仅挑选性从中挑选有用的信息做整合,所以增益并不是很大。假如你的标示数据很少,或许对猜测latency有要求,Bert-crf或许更适宜些。

中文NER的那些事儿1. Bert-Bilstm-CRF基线模型详解&代码实现

中文NER的那些事儿1. Bert-Bilstm-CRF基线模型详解&代码实现

Layer3-Cross-entropy vs CRF

处理问题:实体内标签分类的一致性,T个N分类问题转化为NTN^T的分类问题

paper: Conditional Random Fields: Probabilistic Models for Segmenting and Labeling Sequence Data

把实体辨认笼统为序列标示问题后,其间一个问题便是label的猜测是独立的,但实体辨认的准确率是把实体作为全体来核算的,所以需求考虑到实体内label猜测的一致性,核算整个标示序列的大局最优,也便是把求解T个N分类问题转化为从NTN^T个序列中寻找概率最大的猜测序列。序列标示模型经历了从HMM到MEMM再到CRF的更迭,让咱们来简略比照下三个模型

1. HMM

中文NER的那些事儿1. Bert-Bilstm-CRF基线模型详解&代码实现

隐马尔可夫有向图模型,由观测序列和隐状况序列构成,在NER问题中观测序列便是输入句子,隐状况便是实体label,模型包括3个中心假定:

  • 齐次马尔可夫性假定:P(st∣s1,2,…t−1)=P(st∣st−1)P(s_{t}|s_{1,2,…t-1})=P(s_{t}|s_{t-1}),当时实体label只和前一方位的label有关
  • 不动性假定:搬运概率矩阵和方位无关P(si∣si−1)=P(sj∣sj−1)P(s_i|s_{i-1})=P(s_j|s_{j-1})
  • 观测独立假定:观测只和当时方位隐状况有关P(Ot∣s1,2,…t−1,O1,2,…t−1)=P(Ot∣st)P(O_t|s_{1,2,…t-1},O_{1,2,…t-1})=P(O_t|s_t)

用HMM来对NER问题建模需求把条件概首先转化为联合概率如下

argmaxP(s1,…,sT∣O1,…,OT)∝argmaxP(s1,…,sT,O1,…,OT)=argmaxP(O1,…,OT∣s1,…,sT)⋅P(s1,…,sT)=argmaxP(s0)⋅∏i=1TP(st∣st−1)⋅P(Ot∣st)argmax P(s_1,…,s_T|O_1,…,O_T) \propto argmax P(s_1,…,s_T,O_1,…,O_T)\\ = argmax P(O_1,…,O_T|s_1,…,s_T)\cdot P(s_1,…,s_T)\\ = argmax P(s_0)\cdot \prod_{i=1}^TP(s_t|s_{t-1})\cdot P(O_t|s_t)

以上也就得到了HMM需求求解的三个变量

  • 初始状况P(s0)P(s_0):句子第一个label是B-PER/I-PER/…/O的概率
  • 大局搬运矩阵P(st∣st−1)P(s_t|s_{t-1}):B-PER->I-PER, I-PER->B-LOC,实体label间的搬运概率
  • 输出概率P(Ot∣st)P(O_t|s_t):P(北|B-LOC)已知状况输出是某一token的概率

猜测进程,既对已知观测序列,求解最有或许的状况序列。直接求解长度为T,label_size=N的序列是O(NT)O(N^T)复杂度,通常选用动态规划的Viterbi算法来把复杂度下降到O(N2T)O(N^2T),具体的核算进程能够看文章最后的附录部分

看到这儿你觉着用HMM来求解NER有啥问题嘞?主要问题有2个

  • 观测独立假定,每个输出(character)当然不只依靠隐状况(label)还会依靠上下文信息
  • HMM作为生成模型要核算联合概率,假如要引进额外特征来描绘观测,例如大小写前缀后缀,需求核算每个特征的likelihood,所以很难引进额外特征。一起模型拟和的是联合概率,和猜测需求的P(S|O)条件概率存在不一致。

2.MEMM

中文NER的那些事儿1. Bert-Bilstm-CRF基线模型详解&代码实现

HMM在序列标示中的优势是隐状况间的跳转,会有用提高实体猜测label之间的一致性。让咱们保存优点,放松观测独立的假定,再把生成模型换成判别模型,直接对P(S|O)进行建模咱们就得到了MEMM最大熵马尔可夫模型。MEMM能够便利的引进恣意特征来对观测数据进行描绘,而且没有观测独立假定后实体标签能够依靠恣意上下文信息。

最大熵模型是对数线性模型,对滴便是了解的logistic regression,每个step都是多分类问题,输入F是基于t和t-1时间的状况以及观测序列X构建的特征函数,输出是t时间各个状况的概率(sum=1部分正则化),以下Z是正则因子

P(st∣st−1,O)=1Z(st−1,O)exp(WT⋅F(st,st−1,O))P(s_t|s_{t-1}, O) = \frac{1}{Z(s_{t-1},O)}exp(W^T \cdot F(s_t,s_{t-1},O))

用MEMM求解给定观测下最优的标示序列如下

argmaxP(s1,…,sT∣O1,…,OT)=∏i=1TP(st∣st−1,O1,…OT)=∏i=1T1Z(st−1,O)exp(WT⋅F(st,st−1,O))argmax P(s_1,…,s_T|O_1,…,O_T) \\ =\prod_{i=1}^TP(s_t|s_{t-1},O_1,…O_T) \\ =\prod_{i=1}^T\frac{1}{Z(s_{t-1},O)}exp(W^T \cdot F(s_t,s_{t-1},O)) \\

那用MEMM来求解NER还有啥问题勒?

  • LabelBias: MEMM的部分正则化会导致猜测时倾向于挑选可搬运状况更少的状况。许多博客都具体介绍了这个问题最大熵马尔可夫模型(MEMM)及其三个基本问题

3. CRF

中文NER的那些事儿1. Bert-Bilstm-CRF基线模型详解&代码实现

保存MEMM判别模型,马尔可夫状况搬运,以及每个状况都依靠完好上下文的优点,CRF直接在大局进行正则化,处理了label bias的问题。比照MEMM在每个step进行正则化得到概率,CRF是直接对T个step一切NTN^T个或许状况途径核算大局概率进行正则化,如下

argmaxP(s1,…,sT∣O1,…,OT)=1Z(x)∏i=1Texp(WT⋅F(si,si−1,O))=1∑NT个pathsexp(∑i=1TWT⋅F(si,si−1,O))exp(∑i=1TWT⋅F(si,si−1,O))argmax P(s_1,…,s_T|O_1,…,O_T) \\ =\frac{1}{Z(x)} \prod_{i=1}^Texp(W^T \cdot F(s_i,s_{i-1},O))\\ =\frac{1}{\sum_{N^T\text{个paths} }exp( \sum_{i=1}^T W^T \cdot F(s_i,s_{i-1},O))}exp( \sum_{i=1}^T W^T \cdot F(s_i,s_{i-1},O))

以上特征函数F能够进一步拆解成L个搬运类特征和K个状况类特征之和

WT⋅F(si−1,si,O)=∑k=1Kwkfk(si,O)+∑l=1Lwlfl(si,si−1,O)W^T \cdot F(s_{i-1},s_i,O) = \sum_{k=1}^{K} w_k f_k(s_i,O) + \sum_{l=1}^{L} w_l f_l(s_i,s_{i-1},O)

让咱们来看下tensorflow中tf.contrib.crf的相关完成。其间搬运特征函数是大局的搬运矩阵,状况特征是最后一个layer输出的logit。解码同样是viterbi就不多说了,练习时loglikelihood的核算分为以下几步

  1. 核算实在序列的特征得分
  • binary_score = ∑realpathtransitionprobabitliy\sum_{\text{real path}} \text{transition probabitliy}
  • unary_score = ∑realpathlogits\sum_{\text{real path}} \text{logits}
  • sequence_score = bianry_score + unary_score
  1. 核算用于正则化的全部途径得分log_norm

crf_log_norm巧妙利用了矩阵核算把遍历一切途径O(NT)O(N^T)的复杂度下降到了O(N2T)O(N^2T), 每一步都是N*N的矩阵乘积运算在CrfForwardRnnCell中完成,细节能够去看李航大大的统计学习方法,简略来说便是界说矩阵M

M(si−1,si∣O)=exp(WT⋅F(si,si−1,O))log(Z(x))=log(s1)+∑i=2T−1Mi(X)+log(sT)M(s_{i-1},s_i|O) = exp(W^T \cdot F(s_i,s_{i-1},O))\\ log(Z(x))= log(s_1) + \sum_{i=2}^{T-1} M_i(X) +log(s_T)\\
  1. 输出crf_log_likelihood = sequence_score – log_norm

现在NER使命基本以参加CRF为主,让咱们咱们比照下在Bert输出层后直接加cross-entropy和CRF的作用差异如下。在tag等级cross-entropy和CRF基本是相同的,但由于参加了对Label搬运概率的约束,CRF在entity等级的目标上有显着更高的召准。

中文NER的那些事儿1. Bert-Bilstm-CRF基线模型详解&代码实现

中文NER的那些事儿1. Bert-Bilstm-CRF基线模型详解&代码实现

还要注意一点便是和Bert一同练习的CRF,最好使用不同的learning rate,Bert层是微调lr不能太高不然会呈现信息遗忘,一般在e−5~e−6e^{-5}~e^{-6}。而这么小的lr会导致CRF层搬运概率收敛太慢,CRF的lr一般需求乘100~500倍。更多细节能够看下【REF6】。代码完成选用了分层lr,按variable_scope来区别lr,越靠近输出层的lr越高。

基准模型在两个数据集上看起来如同现已很不错,但是在实践应用中还有许多需求处理的问题。例如NER标示样本太少怎么处理,垂直范畴搬迁问题,中文词鸿沟问题和词汇信息增强,Label Imbalance问题等等,咱们在后面的章节再慢慢唠

==NER系列== 中文NER的那些事儿2. 多使命,对立搬迁学习详解&代码完成 中文NER的那些事儿3. SoftLexicon等词汇增强详解&代码完成


Ref-A

  1. Conditional Random Fields: Probabilistic Models for Segmenting and Labeling Sequence Data, 2001
  2. Bidirectional LSTM-CRF Models for Sequence Tagging, 2015
  3. Named Entity Recognition with Bidirectional LSTM-CNNs, 2016
  4. Neural Architectures for Named Entity Recognition, 2016
  5. Semi-supervised sequence tagging with bidirectional language model
  6. 你的CRF层的学习率或许不够大
  7. www.davidsbatista.net/blog/2018/0…
  8. zhuanlan.zhihu.com/p/103779616

Ref-B Viterbi

  1. 界说t时间抵达状况i的最大概率途径

    t(i)=maxs1,..st−1P(s1,..,st−1,st=i,O1,…,Ot∣,A,B)\delta_t(i)=max_{s_1,..s_{t-1}}P(s_1,..,s_{t-1},s_t=i,O_1,…,O_t|\pi,A,B)
  2. 综合搬运概率和输出概率,t(i)\delta_t(i)存在以下递归关系,因此每一步只需求做N->N个状况复杂度为O(N2)O(N^2)的递归核算

t+1(i)=maxj[t(j)⋅Aj,i]⋅Bi(Ot+1)\delta_{t+1}(i) = max_{j}[\delta_{t}(j) \cdot A_{j,i}] \cdot B_i(O_{t+1})
  1. 向前递归核算的一起记录每一步状况i前一刻的状况j,当递归到序列止境,得到T时间最大概率的状况再往前回溯,得到最大概率对应的状况序列