本文希望用尽可能简短的语言把CRF(条件随机场,Conditional Random Field)的原理讲清楚,这里In A Nutshell在英文中其实有“导论”、“科普”等意思(霍金写过一本《果壳中的宇宙》,这里东施效颦一下)。

网上介绍CRF的文章,不管中文英文的,基本上都是先说一些概率图的概念,然后引入特征的指数公式,然后就说这是CRF。所谓“概率图”,只是一个形象理解的说法,然而如果原理上说不到点上,你说太多形象的比喻,反而让人糊里糊涂,以为你只是在装逼。(说到这里我又想怼一下了,求解神经网络,明明就是求一下梯度,然后迭代一下,这多好理解,偏偏还弄个装逼的名字叫“反向传播”,如果不说清楚它的本质是求导和迭代求解,一下子就说反向传播,有多少读者会懂?)

好了,废话说完了,来进入正题。

逐标签Softmax #

CRF常见于序列标注相关的任务中。假如我们的模型输入为Q,输出目标是一个序列a1,a2,,an,那么按照我们通常的建模逻辑,我们当然是希望目标序列的概率最大
P(a1,a2,,an|Q)


不管用传统方法还是用深度学习方法,直接对完整的序列建模是比较艰难的,因此我们通常会使用一些假设来简化它,比如直接使用朴素假设,就得到
P(a1,a2,,an|Q)=P(a1|Q)P(a2|Q)P(an|Q)

注意这里的Q不一定是原始输入,比如它可能是经过多层LSTM之后的隐藏输出q1,q2,,qn,并且我们认为全局的关联已经由前面的模型捕捉完成了,因此在最后一步,我们可以认为特征之间互不相关,那么
P(a1|Q)=P(a1|q1,q2,,qn)=P(a1|q1)P(a2|Q)=P(a2|q1,q2,,qn)=P(a2|q2)P(an|Q)=P(an|q1,q2,,qn)=P(an|qn)

从而
P(a1,a2,,an|Q)=P(a1|q1)P(a2|q2)P(an|qn)

这就得到了我们最常用的方案:直接逐标签输出最大概率的那个标签。而前面的模型通常是多层的双向LSTM。

条件随机场 #

逐标签softmax是一种简单有效的方法,但有时候会出现不合理的结果。比如我们用sbme来做4标签分词时,逐标签softmax无法排除出现bbbb这样的序列的可能性,但这个序列是违反了我们的解码规则(b后面只能接m或e)。因此,有人说逐标签softmax不需要动态规划,那是不对的,这种情况下,我们至少需要一个“非0即1”的转移矩阵,直接把不合理的转移概率设为0(如P(b|b)=0),然后通过动态规划保证得到合理的序列。

上述方案会出问题,归根结底就是我们在建模的时候,使用了输出完全独立的朴素假设(一元模型),但我们真实的输出序列又是上下文有关的,因此造成了优化目标与模型假设不吻合。能不能直接把上下文考虑进去呢?很简单,使用二元模型即可。
PQ(a1,a2,,an)=PQ(a1)PQ(a2|a1)PQ(a3|a1,a2)PQ(an|a1,,an1)=PQ(a1)PQ(a2|a1)PQ(a3|a2)PQ(an|an1)


这里为了使得表达式更好看一些,我把输入Q放到了下标中。这已经很接近CRF了!

继续观察上式,上面是一个转移概率的乘积,然而我们为什么非要定义每一项都是转移概率呢?于是,CRF的做法非常一般,首先它定义了一个函数f(x,y;Q)(它可能是一些简单的特征函数之和,但具体的形式其实不重要),然后直接令
PQ(a1,a2,,an)=1Zexp(kf(ak1,ak;Q))


其中Z是归一化因子。跟前一式相比,两者的区别在于,PQ(ak|ak1)是有概率意义的(条件概率),而单项的ef(ak1,ak;Q)/Z是没有概率意义的,所以CRF是更一般的形式。

这就是CRF的全部了。

一个更完整的参考链接:https://zhuanlan.zhihu.com/p/28465510

线性链CRF #

What?你在逗我?这就是CRF?这一个个PQ(ak|ak1)是什么鬼?还有f(x,y;Q)呢?

这位读者,你可问倒我了,我也不知道是什么鬼呀。不信您到再看看网上的一些教程,它们给出的公式大概是这样的(直接抄自这里):
p(l|s)=exp[score(l|s)]lexp[score(l|s)]=exp[mj=1ni=1λjfj(s,i,li,li1)]lexp[mj=1ni=1λjfj(s,i,li,li1)]


这里的f都是未知的“特征函数”,需要根据具体问题具体设计,那还不是等价于说是未知的f(ak1,ak;Q)。所以,我确实不知道是什么鬼呀。

好吧,就算你是对的,你好歹也教教我怎么用吧?

这里介绍一个经常用的版本——线性链CRF,它就是tensorflow自带的版本。我们先写出
PQ(a1,a2,,an)=PQ(a1)PQ(a2|a1)PQ(a3|a2)PQ(an|an1)=PQ(a1)PQ(a1,a2)PQ(a1)PQ(a2)PQ(a2)PQ(a2,a3)PQ(a2)PQ(a3)PQ(a3)PQ(an1,an)PQ(an1)PQ(an)PQ(an)


是不是感觉还挺好看的?根据CRF的一般思路,我们放弃每一项的概率意义,直接写出
PQ(a1,a2,,an)=1Zexp[f(a1;Q)+g(a1,a2;Q)+f(a2;Q)++g(an1,an;Q)+f(an;Q)]

所谓线性链,就是直接认为函数g实际上跟Q没关系,任何情况都共用一个g(ak1,ak),这样它不过是个待确定的矩阵而已。剩下的则跟逐标签softmax的情形差不多了,认为f(ak;Q)f(ak;qk)。按照极大似然的思想,loss应该取为:
logPQ(a1,a2,,an)=nk=1f(ak;qk)nk=2g(ak1,ak)+logZ

如果前面模型用双向LSTM来得到特征qk,那么就得到了序列标注任务中最经典的BiLSTM-CRF了。

所以,现在也就不难理解tensorflow中自带的CRF的函数了:
https://github.com/tensorflow/tensorflow/tree/master/tensorflow/contrib/crf

相对于逐标签softmax,CRF不过是换了一个loss罢了,当然,还多了一个互信息矩阵,并且解码的时候需要用到viterbi算法。但这都不重要,因为tensorflow都帮我们写好了。

再设计? #

线性链CRF可以说是一个化简的模版,我们是不是可能参照这个模版,设计一个改进的CRF?比如,用模型生成一个跟Q有关的互信息矩阵?也许是可能的。

剥开nutshell,才能更好地品尝nut,这就是果壳中的味道了。知其然,知其所以然,一切就没那么神秘了。

(新介绍:https://kexue.fm/archives/5542

转载到请包括本文地址:https://spaces.ac.cn/archives/4695

更详细的转载事宜请参考:《科学空间FAQ》

如果您还有什么疑惑或建议,欢迎在下方评论区继续讨论。

如果您觉得本文还不错,欢迎分享/打赏本文。打赏并非要从中获得收益,而是希望知道科学空间获得了多少读者的真心关注。当然,如果你无视它,也不会影响你的阅读。再次表示欢迎和感谢!

如果您需要引用本文,请参考:

苏剑林. (Nov. 25, 2017). 《果壳中的条件随机场(CRF In A Nutshell) 》[Blog post]. Retrieved from https://spaces.ac.cn/archives/4695

@online{kexuefm-4695,
        title={果壳中的条件随机场(CRF In A Nutshell)},
        author={苏剑林},
        year={2017},
        month={Nov},
        url={\url{https://spaces.ac.cn/archives/4695}},
}