条件随机场(crf)及tensorflow代码实例

条件随机场(crf)及tensorflow代码实例

对于条件随机场的学习,我觉得应该结合HMM模型一起进行对比学习。首先浏览HMM模型:https://www.cnblogs.com/pinking/p/8531405.html

一、定义

  条件随机场(crf):是给定一组输入随机变量条件下,另一组输出随机变量的条件概率的分布模型,其特点是假设输出随机变量构成马尔科夫随机场。本文所指线性链条件随机场。

  隐马尔科夫模型(HMM):描述由隐藏的马尔科夫链随机生成观测序列的过程,属于生成模型。

 

  当然,作为初学者,从概念上直观感受不到两者的区别与联系,甚至感觉两个概念都理解不了了,不过这没啥问题,继续学下去吧。

 

二、学习CRF包含的知识点

<span>条件随机场(crf)及tensorflow代码实例</span>

  参考李航的统计学习方法,将该部分内容的主要知识点梳理如图,可以看到,CRF和HMM由很多共同点,譬如,都和马尔科夫有关、都有三个问题要解决,解决的方法也有相同的地方。

三、概率无向图

  概率无向图,又称马尔科夫随机场,也就是定义中假设输出随机变量构成的。关于模型的构建,其实就是一个由节点(node,记作V)和节点链接关系的边(edge,记作E)组成的图G = (V,E),所谓无向图,就是边没有方向。

  随机变量存在的关系包括:成对马尔科夫性、局部马尔科夫性和全局马尔科夫性。假设随机变量的联合概率分布P(Y)和表示它的无向图G,若P(Y)满足上述三种关系,则此联合概率分布为概率无向图或称为马尔科夫随机场。

  提出该定义事实上是为了求联合概率分布做铺垫,为了求联合概率,给出无向图中的团与最大团的定义。

<span>条件随机场(crf)及tensorflow代码实例</span>

  团:{Y1,Y2},{Y1,Y3},{Y2,Y3},{Y2,Y4},{Y3,Y4}

  最大团:{Y1,Y2,Y3},{Y4,Y2,Y3}

  概率无向图模型的联合概率分布表示为其最大团上的随机变量的函数的乘积的形式的操作。概率无向图模型的链和概率分布P(Y)可以表示为如下形式:

<span>条件随机场(crf)及tensorflow代码实例</span>

  C为无向图的最大团,Yc是C的结点对应的随机变量,Ψ就是一个函数,暂且不用管是什么,就是一个转换关系。

  看到这里,其实对于CRF的结果的图的理解已经有了铺垫,实际上,CRF就是将输入X,经过变换,获得输出Y的过程,当然这个Y就满足了上面所画的无向图。但是这个概率是干什么的呢?

  继续看CRF的内容,等看完再第四章返回来看该处内容,我相信会有进一步了解,知道概率是怎么求的了吧。

 

四、条件随机场的定义

突然来的一点感悟,和内容无关:虽然这一章和第一章内容有点重合,但是我觉得作为初学者,最应该的是一个循序渐进的过程,有很多书都是为了内容的连续性,而忽略初学者的接受能力,事实上很多东西需要不断学习,不断深入的过程,这个在很多教程并不能体现出来,而且有时候,网上查问题找资料,总是一搜一大堆,一打开都是一样的,可能是很多人看到别人的博客,学习完了,理解了然后就复制粘贴上了,也懒得再改改或者加点自己的东西。我觉得是可以理解的,最好百度能做一个机制,相同的东西别都索引上了。

 

   条件概率模型:P(Y|X),Y为输出变量,表示标记的序列,X为输入变量,表示需要标注的观测序列(再HMM中也称为状态序列)。

  • 学习问题中,利用极大似然估计,估计P^(Y|X)
  • 预测问题中,利用给定的序列x,求出条件概率P^(Y|X)最大的输出序列y^

 <span>条件随机场(crf)及tensorflow代码实例</span>

  一般的线性链条件随机场表示如图,通常假设X和Y有相同的结构,那么表示图就如下所示:

<span>条件随机场(crf)及tensorflow代码实例</span>

 

   而此时,最大团,就是相邻两个结点的集合。可以引出公式:

<span>条件随机场(crf)及tensorflow代码实例</span>

  当然,后续会有CRF的参数化形式、简化形式等表示形式,但实际上都是第三章求概率的表达。

 

 五、CRF的三个需要解决的问题

   5.1  概率计算问题

  条件随机场的概率计算问题,就是给定x,y,求它的P(Yi = yi | x),P(Yi-1 = yi-1 ,Yi = yi | x)以及相应的数学期望的问题。其解决手段用的是HMM那样的前向-后向算法。

  前向-后向算法:

  定义前向向量:ai(x):

<span>条件随机场(crf)及tensorflow代码实例</span>

  递推公式表示为:

<span>条件随机场(crf)及tensorflow代码实例</span>

  ai(yi|x)表示在位置i的标记为yi并且到位置i的前部分标记序列的非规范化概率,yi可取m个,所以ai(x)m维列向量。同理也可以定义后向算法。其中M的定义是在上述条件随机场的矩阵形式中定义的,本文中未介绍,直接给出定义:

<span>条件随机场(crf)及tensorflow代码实例</span>

  Mi表示的是随机变量Y取值为yi的非规范化的条件概率,这个概率依赖于当前和前一个位置

  对于此处的理解,我觉得如果非要和HMM中类比的化,a类似于前向概率,只不过此处叫做:前向向量,两者的不同就是在CRF中,a的求法没有状态转移矩阵;而M类似于HMM中状态转移概率位置。并且,CRF的这个式子中,没有观测矩阵。总之虽然都叫前向求法,但是里面参数意义是不一样的,不好对比,CRF中,并不是依赖状态转移矩阵和观测矩阵的过程,而是一个依赖于前一时刻生成结果的概率的预测概率。因为我们要计算的是一个已知yi排列的概率嘛,所以是一个连乘的关系,按序列顺序将概率相乘(i时刻的概率依赖于i-1时刻的概率)

<span>条件随机场(crf)及tensorflow代码实例</span>

 

  后向向量,表示在位置i的标记为yi并且从i+1到n的后部分标记序列的非规范化概率。

<span>条件随机场(crf)及tensorflow代码实例</span>

<span>条件随机场(crf)及tensorflow代码实例</span>

  当然,前向向量是从前往后扫描,扫到头的化,就和后向向量第一个值相同了。。。

<span>条件随机场(crf)及tensorflow代码实例</span>

 

   概率计算:

  按照前向-后向向量的定义,可知,αi表示位置i处标记为yi,从1到i-1处为某一排列的概率,βi表示位置i处标记为yi,从i+1到n处为某一排列的概率。因此,得到条件概率:

<span>条件随机场(crf)及tensorflow代码实例</span>

<span>条件随机场(crf)及tensorflow代码实例</span>

  对于下面一个式子的理解:事实上,这两个概率都是根据定义直接列出来的,α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、梯度下降法以及拟牛顿法。直接给出优化函数吧:

<span>条件随机场(crf)及tensorflow代码实例</span>

<span>条件随机场(crf)及tensorflow代码实例</span>

 

   其实就是一个EM算法,道理和HMM中的一样,先对权值w进行优化,优化完求状态特征和转移特征的期望,然后再根据期望再迭代优化w,最后满足概率最大就可以了。

   5.3  条件随机场的预测算法

   条件随机场的预测问题,是给定CRF和输入x,求输出y的问题。这个求法就是使用viterbi算法。求法同HMM一样,只不过HMM中反推的时候,利用的是上一时刻某一状态转移到当前时刻状态概率最大的那个上一时刻的状态

  此处,结合序列标记问题,定义为δi(l),表示在位置i标记l各个可能取值(1,2…m),递推公式:

<span>条件随机场(crf)及tensorflow代码实例</span>

<span>条件随机场(crf)及tensorflow代码实例</span>

  其中,<span>条件随机场(crf)及tensorflow代码实例</span>就是非规范化的P(yi|x),因此,这里采用了相加的方法。反推的时候,选择上一时刻某一Ψ,在李航书中那个例子的观察方法如下,其中画黄框的是取值大的那一项。

<span>条件随机场(crf)及tensorflow代码实例</span>

 六、CRF与HMM的区别联系

<span>条件随机场(crf)及tensorflow代码实例</span>

  来自: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,需要加入状态概率分布的先验知识,即:

<span>条件随机场(crf)及tensorflow代码实例</span>

  在HMM中有这样一个过程,但是CRF就不需要了。

  最大后验估计 vs最大似然估计

  • 频率学派:最大似然估计(MLE):在进行推论时,我们只关心似然度,并选择给出所最大化 p(data|hypo) 的假设作为预测。
  • 贝叶斯学派:最大后验估计(MAP):我们也需要把先验 p(hypo) 纳入计算,不仅是似然度,还要选择给出最大 p(data|hypo) * p(hypo) 的假设作为预测。
  • 如果我们认为所有假设都服从均匀分布,那么MAP = MLE 。

  可以看到,HMM和CRF是两种思路,CRF是频率学派,而HMM是贝叶斯学派的。

 七、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输入就需要句子的长度做内部处理,它本身是基于序列的。

 八、条件随机场的tensorflow代码实现

  参考:https://mp.weixin.qq.com/s/1KAbFAWC3jgJTE-zp5Qu6g

  作者以骰子为例,假设了每次掷骰子之间会有一个相互依赖的关系,参考代码:https://www.cnblogs.com/pinking/p/9362966.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))

  

   

  得到的结果:

<span>条件随机场(crf)及tensorflow代码实例</span>

 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/119453.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • 树莓派连接wifi教程[通俗易懂]

    树莓派连接wifi教程[通俗易懂]第一种方法:如果你已经连接了VNC图形界面,就像手机电脑一样点击wifi的图标找到你的wifi输入密码就行第二种方法:如果登录了putty1.输入sudonano/etc/wpa_supplicant/wpa_supplicant.conf2.在尾部添加network={ssid=""psk=""}引号内容SSID是你的无线名称PSK是你的无线密码无线名称不能是中…

  • 排序二叉树的建立注意重复元素

    排序二叉树的建立注意重复元素think:1建立排序二叉树时注意重复元素sdut原题链接树结构练习——排序二叉树的中序遍历TimeLimit:1000MSMemoryLimit:65536KBProblemDescription在树结构中,有一种特殊的二叉树叫做排序二叉树,直观的理解就是——(1).每个节点中包含有一个关键值(2).任意一个节点的左子树(如果存在的话)的关键值小于该节点的关键值

  • textview长按复制_android-TextView的长按复制[通俗易懂]

    textview长按复制_android-TextView的长按复制[通俗易懂]引子android很多app都支持文本的选中,复制。根据观察,主要分为两类:1)自由复制(弹出框里面支持全选)2)仅支持全选复制今天工作中遇到了全选的需求,现总结出来,方便以后查阅.自由复制这种方式很简单,只需要两行代码,(下面的红色)android:layout_height=”wrap_content”android:text=”长按自由复制”android:textColorHighligh…

  • 基于fpga的256M SDRAM控制器 【内含256m sdram仿真模型】

    基于fpga的256m的SDRAM控制器2018/7/26受教于邓堪文老师,开始真真学习控制sdram由于自己买的sdram模块是256的,原来老师的是128,所以边学边改,不知道最后好不好使,但是我有信心一.sdram的初始化sdram介绍啥的就不用了,上来就是干,简单粗暴。1.下面是引脚说明,看不懂自己可以用百度翻译,需要注意的是红框内的地址引脚和行列地址是复…

  • java是前端还是后端 对于java来讲那个以后发展的会更好

    java是前端还是后端 对于java来讲那个以后发展的会更好Java和前端很多的初学者都不知道该怎么去选择。本来对于java区分前端还是后端这个问题问的其实并没有什么技术含量,java本身来讲涉及的后端的知识要远远多于前端,当然java也有前端的知识javaweb就是啦,但是个人感觉如果你想学习java还是后端更好。第一后端就像一棵大树,你沿着一根树枝,可以慢慢地了解整个企业应用开发技术这个大树,而你的技术水平会越来越深入。第二前端一直以来就是界面,技术深度不够,随着你经验的丰富,你的技术水平会越来越熟练。所以前端和后端在技术上的区别就是一个趋向熟练,一个趋

  • [JAVA]定时任务之-Quartz使用篇

    [JAVA]定时任务之-Quartz使用篇定时任务之-Quartz使用篇        Quartz是OpenSymphony开源组织在Jobscheduling领域又一个开源项目,它可以与J2EE与J2SE应用程序相结合也可以单独使用。Quartz可以用来创建简单或为运行十个,百个,甚至是好几万个Jobs这样复杂的日程序表。Jobs可以做成标准的Java组件或EJBs。官方网站:http://www.opensymphon

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号