对于条件随机场的学习,我以为应该结合HMM模型一块儿进行对比学习。首先浏览HMM模型:http://www.javashuo.com/article/p-cqbbyrxs-gz.htmlhtml
1、定义node
条件随机场(crf):是给定一组输入随机变量条件下,另外一组输出随机变量的条件几率的分布模型,其特色是假设输出随机变量构成马尔科夫随机场。本文所指线性链条件随机场。python
隐马尔科夫模型(HMM):描述由隐藏的马尔科夫链随机生成观测序列的过程,属于生成模型。git
固然,做为初学者,从概念上直观感觉不到二者的区别与联系,甚至感受两个概念都理解不了了,不过这没啥问题,继续学下去吧。算法
2、学习CRF包含的知识点dom
参考李航的统计学习方法,将该部份内容的主要知识点梳理如图,能够看到,CRF和HMM由不少共同点,譬如,都和马尔科夫有关、都有三个问题要解决,解决的方法也有相同的地方。ide
3、几率无向图函数
几率无向图,又称马尔科夫随机场,也就是定义中假设输出随机变量构成的。关于模型的构建,其实就是一个由节点(node,记做V)和节点连接关系的边(edge,记做E)组成的图G = (V,E),所谓无向图,就是边没有方向。学习
随机变量存在的关系包括:成对马尔科夫性、局部马尔科夫性和全局马尔科夫性。假设随机变量的联合几率分布P(Y)和表示它的无向图G,若P(Y)知足上述三种关系,则此联合几率分布为几率无向图或称为马尔科夫随机场。优化
提出该定义事实上是为了求联合几率分布作铺垫,为了求联合几率,给出无向图中的团与最大团的定义。
团:{Y1,Y2},{Y1,Y3},{Y2,Y3},{Y2,Y4},{Y3,Y4}
最大团:{Y1,Y2,Y3},{Y4,Y2,Y3}
几率无向图模型的联合几率分布表示为其最大团上的随机变量的函数的乘积的形式的操做。几率无向图模型的链和几率分布P(Y)能够表示为以下形式:
C为无向图的最大团,Yc是C的结点对应的随机变量,Ψ就是一个函数,暂且不用管是什么,就是一个转换关系。
看到这里,其实对于CRF的结果的图的理解已经有了铺垫,实际上,CRF就是将输入X,通过变换,得到输出Y的过程,固然这个Y就知足了上面所画的无向图。可是这个几率是干什么的呢?
继续看CRF的内容,等看完再第四章返回来看该处内容,我相信会有进一步了解,知道几率是怎么求的了吧。
4、条件随机场的定义
忽然来的一点感悟,和内容无关:虽然这一章和第一章内容有点重合,可是我以为做为初学者,最应该的是一个按部就班的过程,有不少书都是为了内容的连续性,而忽略初学者的接受能力,事实上不少东西须要不断学习,不断深刻的过程,这个在不少教程并不能体现出来,并且有时候,网上查问题找资料,老是一搜一大堆,一打开都是同样的,多是不少人看到别人的博客,学习完了,理解了而后就复制粘贴上了,也懒得再改改或者加点本身的东西。我以为是能够理解的,最好百度能作一个机制,相同的东西别都索引上了。
条件几率模型:P(Y|X),Y为输出变量,表示标记的序列,X为输入变量,表示须要标注的观测序列(再HMM中也称为状态序列)。
通常的线性链条件随机场表示如图,一般假设X和Y有相同的结构,那么表示图就以下所示:
而此时,最大团,就是相邻两个结点的集合。能够引出公式:
固然,后续会有CRF的参数化形式、简化形式等表示形式,但实际上都是第三章求几率的表达。
5、CRF的三个须要解决的问题
5.1 几率计算问题
条件随机场的几率计算问题,就是给定x,y,求它的P(Yi = yi | x),P(Yi-1 = yi-1 ,Yi = yi | x)以及相应的数学指望的问题。其解决手段用的是HMM那样的前向-后向算法。
前向-后向算法:
定义前向向量:ai(x):
递推公式表示为:
ai(yi|x)表示在位置i的标记为yi而且到位置i的前部分标记序列的非规范化几率,yi可取m个,因此ai(x)是m维列向量。同理也能够定义后向算法。其中M的定义是在上述条件随机场的矩阵形式中定义的,本文中未介绍,直接给出定义:
Mi表示的是随机变量Y取值为yi的非规范化的条件几率,这个几率依赖于当前和前一个位置。
对于此处的理解,我以为若是非要和HMM中类比的化,a相似于前向几率,只不过此处叫作:前向向量,二者的不一样就是在CRF中,a的求法没有状态转移矩阵;而M相似于HMM中状态转移几率位置。而且,CRF的这个式子中,没有观测矩阵。总之虽然都叫前向求法,可是里面参数意义是不同的,很差对比,CRF中,并非依赖状态转移矩阵和观测矩阵的过程,而是一个依赖于前一时刻生成结果的几率的预测几率。由于咱们要计算的是一个已知yi排列的几率嘛,因此是一个连乘的关系,按序列顺序将几率相乘(i时刻的几率依赖于i-1时刻的几率)。
后向向量,表示在位置i的标记为yi而且从i+1到n的后部分标记序列的非规范化几率。
固然,前向向量是从前日后扫描,扫到头的化,就和后向向量第一个值相同了。。。
几率计算:
按照前向-后向向量的定义,可知,αi表示位置i处标记为yi,从1到i-1处为某一排列的几率,βi表示位置i处标记为yi,从i+1到n处为某一排列的几率。所以,获得条件几率:
对于下面一个式子的理解:事实上,这两个几率都是根据定义直接列出来的,αi-1表示i-1标记为yi-1时,以及以前排序为某一序列的几率,由于yi-1与yi并非独立的,因此联合几率就表示成上面的式子了。
指望值的计算:
利用前向-后向算法,能够求出特征函数fk关于P(X,Y)和P(Y|X)的数学指望。不过要求P(X , Y)的话,须要假设经验分布P^(X)。该指望值的计算公式此处略过,就是一个求指望的公式嘛。
5.2 条件随机场的学习算法
该节研究的是给定训练数据集估计CRF模型参数的问题。参数估计一般用极大似然估计,HMM中,若是隐层状态未知的话,也是用极大似然估计。
具体的优化实现算法有改进的迭代尺度法IIS、梯度降低法以及拟牛顿法。直接给出优化函数吧:
其实就是一个EM算法,道理和HMM中的同样,先对权值w进行优化,优化完求状态特征和转移特征的指望,而后再根据指望再迭代优化w,最后知足几率最大就能够了。
5.3 条件随机场的预测算法
条件随机场的预测问题,是给定CRF和输入x,求输出y的问题。这个求法就是使用viterbi算法。求法同HMM同样,只不过HMM中反推的时候,利用的是上一时刻某一状态转移到当前时刻状态几率最大的那个上一时刻的状态。
此处,结合序列标记问题,定义为δi(l),表示在位置i标记l各个可能取值(1,2...m),递推公式:
其中,就是非规范化的P(yi|x),所以,这里采用了相加的方法。反推的时候,选择上一时刻某一Ψ,在李航书中那个例子的观察方法以下,其中画黄框的是取值大的那一项。
6、CRF与HMM的区别联系
来自:Sutton, Charles, and Andrew McCallum. "An introduction to conditional random fields." Machine Learning 4.4 (2011): 267-373.
虽然网上这个图都在抄,不过我感受都解释的不够详细啊。首先从HMM看,白色圆表明状态,黑色表明观测值,能够看到,他们之间有一个顺序的依赖关系;而采用CRF的话,状态和观测值(或者说用词性和词语表示),能够看到没有这种顺序的依赖关系。HMM中的状态只与前一个有关,而CRF综合考虑了先后的依赖关系。
判别式模型和生成式模型
HMM是生成式模型,CRF是判别式模型,首先介绍两种模型。判别模型是给定输入序列 X,直接评估对应的输出Y ;生成模型是评估给定输出 Y,如何从几率分布上生成输入序列 X。其实二者的评估目标都是要获得最终的类别标签Y, 即Y=argmax p(y|x)。判别式模型直接经过解在知足训练样本分布下的最优化问题获得模型参数,主要用到拉格朗日乘算法、梯度降低法,常见的判别式模型如最大熵模型、CRF、LR、SVM等;而生成式模型先通过贝叶斯转换成Y = argmax p(y|x) = argmax p(x|y)*p(y),而后分别学习p(y)和p(x|y)的几率分布,常见的如n-Gram、HMM、Naive Bayes。
判别模型和生成模型只是描述一种问题的两种方式,在理论上,它们是能够互相转换的。 对于上述HMM和CRF的区别,最主要的就是对于HMM,须要加入状态几率分布的先验知识,即:
在HMM中有这样一个过程,可是CRF就不须要了。
最大后验估计 vs最大似然估计
能够看到,HMM和CRF是两种思路,CRF是频率学派,而HMM是贝叶斯学派的。
7、CRF与Softmax
对于序列标注问题,能够简单的理解为分类问题,既然是分类,为何NLP中一般不直接用softmax等分类器,而使用CRF\HMM呢?这是由于目标输出序列自己会带有一些上下文关联,而softmax等不能体现出这种联系。固然,CRF体现的不只仅是上下文的联系,更重要的是利用viterbi算法,体现的是一种路径规划的几率。
另外,一般在NLP中,输入每一个batch的语句长度是不同的(单个batch语句长度能够经过padding补齐),若是用CNN作特征提取的话,batch之间的结果的维度是不一样的。而采用CRF的话,就不用考虑这个维度不一样的问题了。
softmax在tf中的接口:
@tf_export("nn.sampled_softmax_loss") def sampled_softmax_loss(weights, biases, labels, inputs, num_sampled, num_classes, num_true=1, sampled_values=None, remove_accidental_hits=True, partition_strategy="mod", name="sampled_softmax_loss", seed=None): #num_sampled则是Sample Softmax时候用到的一个超参数,肯定选几个词来对比优化 ''' weights: A `Tensor` of shape `[num_classes, dim]`, or a list of `Tensor` objects whose concatenation along dimension 0 has shape [num_classes, dim]. The (possibly-sharded) class embeddings. biases: A `Tensor` of shape `[num_classes]`. The class biases. labels: A `Tensor` of type `int64` and shape `[batch_size, num_true]`. The target classes. Note that this format differs from the `labels` argument of `nn.softmax_cross_entropy_with_logits`. inputs: A `Tensor` of shape `[batch_size, dim]`. The forward activations of the input network. num_sampled: An `int`. The number of classes to randomly sample per batch. num_classes: An `int`. The number of possible classes. '''
而crf的接口:
def crf_log_likelihood(inputs, tag_indices, sequence_lengths, transition_params=None): """Computes the log-likelihood of tag sequences in a CRF. Args: inputs: A [batch_size, max_seq_len, num_tags] tensor of unary potentials to use as input to the CRF layer. tag_indices: A [batch_size, max_seq_len] matrix of tag indices for which we compute the log-likelihood. sequence_lengths: A [batch_size] vector of true sequence lengths. transition_params: A [num_tags, num_tags] transition matrix, if available. """
能够看到,虽然二者都是实现分类的功能,可是其实是不同的,基于seq2seq的时候用softmax判断字典中是哪个字,其softmax的输入是一个固定维度的向量,由于整个seq2seq是基于序列的。而CRF输入就须要句子的长度作内部处理,它自己是基于序列的。
8、条件随机场的tensorflow代码实现
参考:https://mp.weixin.qq.com/s/1KAbFAWC3jgJTE-zp5Qu6g
做者以骰子为例,假设了每次掷骰子之间会有一个相互依赖的关系,参考代码:http://www.javashuo.com/article/p-hfuqwseq-gv.html 的基础上进行修改便可:
import tensorflow as tf import numpy as np import matplotlib.pyplot as plt TIME_STEPS = 15#20 # backpropagation through time 的time_steps BATCH_SIZE = 1#50 INPUT_SIZE = 1 # x数据输入size LR = 0.05 # learning rate num_tags = 2 # 定义一个生成数据的 get_batch function: def get_batch(): xs = np.array([[2, 3, 4, 5, 5, 5, 1, 5, 3, 2, 5, 5, 5, 3, 5]]) res = np.array([[0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1]]) return [xs[:, :, np.newaxis], res] # 定义 CRF 的主体结构 class CRF(object): def __init__(self, n_steps, input_size, num_tags, batch_size): self.n_steps = n_steps self.input_size = input_size self.num_tags = num_tags self.batch_size = batch_size self.xs = tf.placeholder(tf.float32, [None, self.n_steps, self.input_size], name='xs') self.ys = tf.placeholder(tf.int32, [self.batch_size, self.n_steps], name='ys') #将输入 batch_size x seq_length x input_size 映射到 batch_size x seq_length x num_tags weights = tf.get_variable("weights", [self.input_size, self.num_tags]) matricized_x_t = tf.reshape(self.xs, [-1, self.input_size]) matricized_unary_scores = tf.matmul(matricized_x_t, weights) unary_scores = tf.reshape(matricized_unary_scores, [self.batch_size, self.n_steps, self.num_tags]) sequence_lengths = np.full(self.batch_size,self.n_steps,dtype=np.int32) log_likelihood,transition_params = tf.contrib.crf.crf_log_likelihood(unary_scores,self.ys,sequence_lengths) self.pred, viterbi_score = tf.contrib.crf.crf_decode(unary_scores, transition_params, sequence_lengths) # add a training op to tune the parameters. self.cost = tf.reduce_mean(-log_likelihood) self.train_op = tf.train.AdamOptimizer(LR).minimize(self.cost) # 训练 CRF if __name__ == '__main__': # 搭建 CRF 模型 model = CRF(TIME_STEPS, INPUT_SIZE, num_tags, BATCH_SIZE) sess = tf.Session() sess.run(tf.global_variables_initializer()) # matplotlib可视化 plt.ion() # 设置连续 plot plt.show() # 训练屡次 for i in range(150): xs, res = get_batch() # 提取 batch data #print(res.shape) # 初始化 data feed_dict = { model.xs: xs, model.ys: res, } # 训练 _, cost,pred = sess.run( [model.train_op, model.cost, model.pred], feed_dict=feed_dict) # plotting x = xs.reshape(-1,1) r = res.reshape(-1, 1) p = pred.reshape(-1, 1) x = range(len(x)) plt.clf() plt.plot(x, r, 'r', x, p, 'b--') plt.ylim((-1.2, 1.2)) plt.draw() plt.pause(0.3) # 每 0.3 s 刷新一次 # 打印 cost 结果 if i % 20 == 0: print('cost: ', round(cost, 4))
获得的结果: