zoukankan      html  css  js  c++  java
  • 【机器学习】【条件随机场CRF-2】CRF的预测算法之维特比算法(viterbi alg) 详解 + 示例讲解 + Python实现

    1.CRF的预测算法

    条件随机场的预测算法是给定条件随机场P(Y|X)和输入序列(观测序列)x,求条件概率最大的输出序列(标记序列)y*,即对观测序列进行标注。条件随机场的预测算法是著名的维特比算法(Vitebi Algorthim)。

    维特比算法在隐马尔科夫模型的预测算法中已经详细介绍和Python实现过,详见以前的博客:

    【机器学习】【隐马尔可夫模型-4】维特比算法:算法详解+示例讲解+Python实现

    2.CRF的预测算法之维特比算法

    2.1维特比算法简介

    维特比算法实际使用动态规划解CRF条件随机场的预测问题,即用动态规划(Dynamic Programming)求概率最大状态路径(最优路径)。这时一条路径对应一个状态序列。

    根据动态规划原理,最优路径具有这样的特性:如果最优路径在时刻t通过节点i*t,那么这一路径从节点i*t到终点i*T的部分路径,对于从i*t到i*T的所有可能的部分来说,必须是最优的。

    我们只需要从时刻t=1开始,递推地计算在时刻t状态为i的各条部分路径的最大概率,直至得到时刻t=T状态为i的各条路径的最大概率。时刻t=T的最大概率即为最优路径的概率P*,最优路径的终结点i*T也同时得到。

    之后,为了找出最优路径的各个节点,从终结点i*T开始,由后向前逐步求得节点i*T-1,……,i*1,得到最优路径I*=(i*1, i*2, i*3,……,i*T),这就是维特比算法。

    2.2CRF预测问题的维特比算法

    在条件随机场预测问题中,CRF预测问题就成为了求非规范化概率最大的最优路径问题。

    提高效率思想:

        在CRF预测问题的维特比算法中,最终结果求得是最优路径,只需要计算非规范化概率,不必计算概率,可以大大提高效率。

    条件随机场预测的维特比算法步骤:

    输入:模型特征向量F(y, x)和权值向量w, 观测序列x=(x1, x2, ……,xn)

    输出:最优路径y*=(y*1, y*2, ……,y*n)

    (1)初始化位置1的各个标记j=1,2,……,m的非规范化概率

    (2)递推计算,对i=2,3,……,n

    (3)终止

    (4)返回路径

    注意理解:ψ保存每个节点的最大非规范化概率的前导节点,即当前节点的最优前节点,最优路径先已知最优终结点,然后根据最优终结点从ψ里面求出他的最优前前节点,依次求出y*n, y*n-1, ……,y*2, y*1

    则Y*=(y*1, y*2, ……,y*n-1, y*n)T就是条件随机场预测问题的最优路径。

    3.数学示例展示:CRF的预测算法之维特比算法

    设有一标注问题:输入观测序列为X=(X1,X2,X3),输出标记序列为Y=(Y1,Y2,Y3),Y1,Y2,Y3取值于{1,2}.

    假设特征t<k>,s<l>的对应的权值为λ<k>,μ<l>,公式如下所示:

    这里只注明特征取值为1的条件,取值为0的条件省略,如下:

    下同

    请用维特比算法求给定的输入序列(观测序列)x对应的最有输出序列(标记序列)y*=(y*1, y*2, y*3)。

    说明:t<k>是定义在边上的特征函数,称为转移特征,依赖于当前和前一个位置,s<l>是定义在节点上的特征函数,称为状态特征,依赖于当前位置。这部分内容属于:条件随机场的参数形式的知识。关于条件随机场的参数化形式,详见前面博客:点我。

    示例中给的是条件随机场的参数化形式,我给出了对应的状态路径图,画在了纸上,人肉出品,哈哈,详见下图:

    上图中给出了对t和s函数的理解,如果仔细看,应该很容易清楚其含义,然后就很容易地画出条件随机场的参数化形式对应的状态路径图~

    下面给出维特比算法的数学求解过程,如果不懂,可以板砖儿扔过来~:

    最后求得此示例的最优标记序列,即最优状态路径为Y*=(1,2,1)。

    4.python实现CRF的预测算法之维特比算法

    下面是展示数学过程的CRF预测问题的维特比算法的python实现,不是最终精简的代码,精简的代码,请见5节。

    完全人肉出品,请详见:

    4.1代码

    # -*- coding: utf-8 -*-
    """
    @author: 蔚蓝的天空tom
    Talk is cheap, show me code
    Aim:实现条件随机场预测问题的维特比算法
    """

    import numpy as np

    class CRF(object):
    '''实现条件随机场预测问题的维特比算法
    '''
    def __init__(self, V, VW, E, EW):
    '''
    :param V:是定义在节点上的特征函数,称为状态特征
    :param VW:是V对应的权值
    :param E:是定义在边上的特征函数,称为转移特征
    :param EW:是E对应的权值
    '''
    self.V = V #点分布表
    self.VW = VW #点权值表
    self.E = E #边分布表
    self.EW = EW #边权值表
    self.D = [] #Delta表,最大非规范化概率的局部状态路径概率
    self.P = [] #Psi表,当前状态和最优前导状态的索引表s
    self.BP = [] #BestPath,最优路径
    return

    def Viterbi(self):
    '''
    条件随机场预测问题的维特比算法,此算法一定要结合CRF参数化形式对应的状态路径图来理解,更容易理解.
    '''
    self.D = np.full(shape=(np.shape(self.V)), fill_value=.0)
    self.P = np.full(shape=(np.shape(self.V)), fill_value=.0)
    for i in range(np.shape(self.V)[0]):
    #初始化
    if 0 == i:
    self.D[i] = np.multiply(self.V[i], self.VW[i])
    self.P[i] = np.array([0, 0])
    print('self.V[%d]='%i, self.V[i], 'self.VW[%d]='%i, self.VW[i], 'self.D[%d]='%i, self.D[i])
    print('self.P:', self.P)
    pass
    #递推求解布局最优状态路径
    else:
    for y in range(np.shape(self.V)[1]): #delta[i][y=1,2...]
    for l in range(np.shape(self.V)[1]): #V[i-1][l=1,2...]
    delta = 0.0
    delta += self.D[i-1, l] #前导状态的最优状态路径的概率
    delta += self.E[i-1][l,y]*self.EW[i-1][l,y] #前导状态到当前状体的转移概率
    delta += self.V[i,y]*self.VW[i,y] #当前状态的概率
    print('(x%d,y=%d)-->(x%d,y=%d):%.2f + %.2f + %.2f='%(i-1, l, i, y,
    self.D[i-1, l],
    self.E[i-1][l,y]*self.EW[i-1][l,y],
    self.V[i,y]*self.VW[i,y]), delta)
    if 0 == l or delta > self.D[i, y]:
    self.D[i, y] = delta
    self.P[i, y] = l
    print('self.D[x%d,y=%d]=%.2f '%(i, y, self.D[i,y]))
    print('self.Delta: ', self.D)
    print('self.Psi: ', self.P)

    #返回,得到所有的最优前导状态
    N = np.shape(self.V)[0]
    self.BP = np.full(shape=(N,), fill_value=0.0)
    t_range = -1 * np.array(sorted(-1*np.arange(N)))
    for t in t_range:
    if N-1 == t:#得到最优状态
    self.BP[t] = np.argmax(self.D[-1])
    else: #得到最优前导状态
    self.BP[t] = self.P[t+1, int(self.BP[t+1])]

    #最优状态路径表现在存储的是状态的下标,我们执行存储值+1转换成示例中的状态值
    #也可以不用转换,只要你能理解,self.BP中存储的0是状态1就可以~~~~
    self.BP += 1

    print('最优状态路径为:', self.BP)
    return self.BP

    def CRF_manual():
    S = np.array([[1,1], #X1:S(Y1=1), S(Y1=2)
    [1,1], #X2:S(Y2=1), S(Y2=2)
    [1,1]]) #X3:S(Y3=1), S(Y3=1)
    SW = np.array([[1.0, 0.5], #X1:SW(Y1=1), SW(Y1=2)
    [0.8, 0.5], #X2:SW(Y2=1), SW(Y2=2)
    [0.8, 0.5]])#X3:SW(Y3=1), SW(Y3=1)
    E = np.array([[[1, 1], #Edge:Y1=1--->(Y2=1, Y2=2)
    [1, 0]], #Edge:Y1=2--->(Y2=1, Y2=2)
    [[0, 1], #Edge:Y2=1--->(Y3=1, Y3=2)
    [1, 1]]])#Edge:Y2=2--->(Y3=1, Y3=2)
    EW= np.array([[[0.6, 1], #EdgeW:Y1=1--->(Y2=1, Y2=2)
    [1, 0.0]], #EdgeW:Y1=2--->(Y2=1, Y2=2)
    [[0.0, 1], #EdgeW:Y2=1--->(Y3=1, Y3=2)
    [1, 0.2]]])#EdgeW:Y2=2--->(Y3=1, Y3=2)

    crf = CRF(S, SW, E, EW)
    ret = crf.Viterbi()
    print('最优状态路径为:', ret)
    return

    if __name__=='__main__':
    CRF_manual()
    4.2运行结果

    会发现,运行结果展示的数学计算中间值,和上面的数学求解步骤中间值一模一样~~~~~,可以说明代码is ok~~

    runfile('C:/Users/tom/CRF_vitebi.py', wdir='C:/Users/tom')
    self.V[0]= [1 1] self.VW[0]= [ 1. 0.5] self.D[0]= [ 1. 0.5]
    self.P: [[ 0. 0.]
    [ 0. 0.]
    [ 0. 0.]]
    (x0,y=0)-->(x1,y=0):1.00 + 0.60 + 0.80= 2.4
    (x0,y=1)-->(x1,y=0):0.50 + 1.00 + 0.80= 2.3
    self.D[x1,y=0]=2.40

    (x0,y=0)-->(x1,y=1):1.00 + 1.00 + 0.50= 2.5
    (x0,y=1)-->(x1,y=1):0.50 + 0.00 + 0.50= 1.0
    self.D[x1,y=1]=2.50

    (x1,y=0)-->(x2,y=0):2.40 + 0.00 + 0.80= 3.2
    (x1,y=1)-->(x2,y=0):2.50 + 1.00 + 0.80= 4.3
    self.D[x2,y=0]=4.30

    (x1,y=0)-->(x2,y=1):2.40 + 1.00 + 0.50= 3.9
    (x1,y=1)-->(x2,y=1):2.50 + 0.20 + 0.50= 3.2
    self.D[x2,y=1]=3.90

    self.Delta:
    [[ 1. 0.5]
    [ 2.4 2.5]
    [ 4.3 3.9]]
    self.Psi:
    [[ 0. 0.]
    [ 0. 0.]
    [ 1. 0.]]
    最优状态路径为: [ 1. 2. 1.]
    最优状态路径为: [ 1. 2. 1.]
    5.精简后的python代码

    5.1代码

    # -*- coding: utf-8 -*-
    """
    @author: 蔚蓝的天空tom
    Talk is cheap, show me code
    Aim:实现条件随机场预测问题的维特比算法
    """

    import numpy as np

    class CRF(object):
    '''实现条件随机场预测问题的维特比算法
    '''
    def __init__(self, V, VW, E, EW):
    '''
    :param V:是定义在节点上的特征函数,称为状态特征
    :param VW:是V对应的权值
    :param E:是定义在边上的特征函数,称为转移特征
    :param EW:是E对应的权值
    '''
    self.V = V #点分布表
    self.VW = VW #点权值表
    self.E = E #边分布表
    self.EW = EW #边权值表
    self.D = [] #Delta表,最大非规范化概率的局部状态路径概率
    self.P = [] #Psi表,当前状态和最优前导状态的索引表s
    self.BP = [] #BestPath,最优路径
    return

    def Viterbi(self):
    '''
    条件随机场预测问题的维特比算法,此算法一定要结合CRF参数化形式对应的状态路径图来理解,更容易理解.
    '''
    self.D = np.full(shape=(np.shape(self.V)), fill_value=.0)
    self.P = np.full(shape=(np.shape(self.V)), fill_value=.0)
    for i in range(np.shape(self.V)[0]):
    #初始化
    if 0 == i:
    self.D[i] = np.multiply(self.V[i], self.VW[i])
    self.P[i] = np.array([0, 0])
    #递推求解布局最优状态路径
    else:
    for y in range(np.shape(self.V)[1]): #delta[i][y=1,2...]
    for l in range(np.shape(self.V)[1]): #V[i-1][l=1,2...]
    #前导状态的最优状态路径的概率 + 前导状态到当前状体的转移概率 + 当前状态的概率
    delta = self.D[i-1, l] + self.E[i-1][l,y]*self.EW[i-1][l,y] + self.V[i,y]*self.VW[i,y] #
    if 0 == l or delta > self.D[i, y]:
    self.D[i, y], self.P[i, y] = delta, l
    #返回,得到所有的最优前导状态
    N = np.shape(self.V)[0]
    self.BP = np.full(shape=(N,), fill_value=0.0)
    t_range = -1 * np.array(sorted(-1*np.arange(N)))
    for t in t_range:
    if N-1 == t:#得到最优状态
    self.BP[t] = np.argmax(self.D[-1])
    else: #得到最优前导状态
    self.BP[t] = self.P[t+1, int(self.BP[t+1])]

    #最优状态路径表现在存储的是状态的下标,我们执行存储值+1转换成示例中的状态值
    #也可以不用转换,只要你能理解,self.BP中存储的0是状态1就可以~~~~
    self.BP += 1
    return self.BP

    def CRF_manual():
    S = np.array([[1,1], #X1:S(Y1=1), S(Y1=2)
    [1,1], #X2:S(Y2=1), S(Y2=2)
    [1,1]]) #X3:S(Y3=1), S(Y3=1)
    SW = np.array([[1.0, 0.5], #X1:SW(Y1=1), SW(Y1=2)
    [0.8, 0.5], #X2:SW(Y2=1), SW(Y2=2)
    [0.8, 0.5]])#X3:SW(Y3=1), SW(Y3=1)
    E = np.array([[[1, 1], #Edge:Y1=1--->(Y2=1, Y2=2)
    [1, 0]], #Edge:Y1=2--->(Y2=1, Y2=2)
    [[0, 1], #Edge:Y2=1--->(Y3=1, Y3=2)
    [1, 1]]])#Edge:Y2=2--->(Y3=1, Y3=2)
    EW= np.array([[[0.6, 1], #EdgeW:Y1=1--->(Y2=1, Y2=2)
    [1, 0.0]], #EdgeW:Y1=2--->(Y2=1, Y2=2)
    [[0.0, 1], #EdgeW:Y2=1--->(Y3=1, Y3=2)
    [1, 0.2]]])#EdgeW:Y2=2--->(Y3=1, Y3=2)

    crf = CRF(S, SW, E, EW)
    ret = crf.Viterbi()
    print('最优状态路径为:', ret)
    return

    if __name__=='__main__':
    CRF_manual()
    5.2运行结果

    runfile('C:/Users/tom/CRF_vitebi_imp.py', wdir='C:/Users/tom')
    最优状态路径为: [ 1. 2. 1.]
    写完了,条件随机场先到此结束,后续再更新。
    ---------------------
    作者:CV_ML_DP
    来源:CSDN
    原文:https://blog.csdn.net/u012421852/article/details/80287588
    版权声明:本文为博主原创文章,转载请附上博文链接!

  • 相关阅读:
    我为能准时下班而做的准备,以及由此的收获,同时总结下不足
    用象棋的思维趣说IT人的职业发展和钱途
    简历上如果出现过于高大上的项目,反而过犹不及:再论如何通过项目引出技术
    用python的matplotlib和numpy库绘制股票K线均线的整合效果(含从网络接口爬取数据和验证交易策略代码)
    如果当前没有拿得出手的简历,也别慌,努力的话最多两年情况就能改变
    分析若干没面试机会和没体现实力的简历
    IT人为了自己父母和家庭,更得注意自己的身体和心理健康
    Spring Cloud系列文,Feign整合Ribbon和Hysrix
    以互联网公司的经验告诉大家,架构师究竟比高级开发厉害在哪?
    博客园是个大金矿,管理员不挖掘有些可惜:给博客园提一些双赢的建议
  • 原文地址:https://www.cnblogs.com/jfdwd/p/11173442.html
Copyright © 2011-2022 走看看