机器学习小结:条件随机场

机器学算法基础:条件随机场

CONTENT

1. 概率无向图的马尔科夫性

2. 概率无向图的因子分解

3. 条件随机场的定义与形式

4. 条件随机场的基本问题

  • 概率计算问题
  • 学习问题
  • 预测问题

5. 代码实践

1. 概率无向图的马尔科夫性

  • 成对马尔科夫性:在无向图中有两个没有链接的结点,结点对应两个随机变量。在其余点给定的条件下,这两个点表示的变量条件独立。

  • 局部马尔科夫性:在无向图中有一个点v,与之连接的结点的集合称为w,其余点的集合为o,在w给定的条件下,v和o条件独立。

  • 全局马尔科夫性:在无向图中两个结点集合A,B被结点集合C分开,聪明的你应该猜出来了,当C给定的条件下,A,B表示的变量条件独立。

2. 概率无向图的因子分解

团和最大团

团:无向图中任意两点都有边相连。

最大团:团中无法再添加任意节点使得任意两点都有边相连的团。

3. 条件随机场的定义与形式

3.1 定义

给定 \(X=(x_1,x_2,...,x_n)\)\(Y=(y_1,y_2,...,y_n)\) 均为线性链表示的随机变量序列,若在给随机变量序列 X 的条件下,随机变量序列 Y 的条件概率分布 \(P(Y|X)\) 构成条件随机场,即满足马尔可夫性:

\[ P(y_i|x_1,x_2,...,x_{i-1},y_1,y_2,...,y_{i-1},y_{i+1}) = P(y_i|x,y_{i-1},y_{i+1}) \]

则称为 P(Y|X) 为线性链条件随机场。

通过去除了隐马尔科夫算法中的观测状态相互独立假设,使算法在计算当前隐状态\(x_i\)时,会考虑整个观测序列,从而获得更高的表达能力,并进行全局归一化解决标注偏置问题。

条件随机场

3.2 参数化形式

\[p\left(y | x\right)=\frac{1}{Z\left(x\right)} \prod_{i=1}^{n} \exp \left(\sum_{i, k} \lambda_{k} t_{k}\left(y_{i-1}, y_{i}, x, i\right)+\sum_{i, l} \mu_{l} s_{l}\left(y_{i}, x, i\right)\right) \]

其中:

\(Z(x)\) 为归一化因子,是在全局范围进行归一化,枚举了整个隐状态序列\(x_{1…n}\)的全部可能,从而解决了局部归一化带来的标注偏置问题。

\[Z(x)=\sum_{y} \exp \left(\sum_{i, k} \lambda_{x} t_{k}\left(y_{i-1}, y_{i}, x, i\right)+\sum_{i, l} \mu_{l} s_{l}\left(y_{i}, x, i\right)\right) \]

\(t_k\) 为定义在边上的特征函数,转移特征,依赖于前一个和当前位置

\(s_1\) 为定义在节点上的特征函数,状态特征,依赖于当前位置。

3.3 简化形式

step 1

将转移特征和状态特征及其权值用统一的符号表示,设有\(k_1\)个转移特征,\(k_2\)个状态特征,\(K=k_1+k_2\),记

\[f_k(y_{i-1},y_i,x,i) = \begin{cases}t_k(y_{i-1},y_i,x,i),\quad k = 1,2,3, ..., K_1 \\s_l(y_i,x,i), \quad k = k_1+l; l = 1, 2, ... , K_2\end{cases} \]

step 2

对转移与状态特征在各个位置i求和,记作

\[f_k(y,x) = \sum_{i=1}^{n}f_k(y_{i-1},y_i,x,i), k = 1, 2, ..., K \]

step 3

\(\lambda_{x}\)\(\mu_{l}\) 用统一的权重表示,记作

\[w_k = \begin{cases}\lambda_k, \quad k = 1, 2, ..., K_1 \\\mu_l, \quad k = K_1+l; l = 1, 2, ..., K_2\end{cases} \]

step 4

转化后的条件随机场可表示为:

\[\begin{split}P(y\mid x) &= \frac{1}{Z(x)}exp\sum_{k=1}^{K}w_kf_k(y,x) \\Z(x) &= \sum_{y}exp\sum_{k=1}^{K}w_kf_k(y,x)\end{split} \]

step 5

\(w\) 表示权重向量:

\[w = (w_1,w_2,...,w_K)^T \]

\(F(y,x)\) 表示特征向量,即

\[F(y,x) = (f_1(y,x), f_2(y,x), ..., f_K(y,x))^T \]

则,条件随机场写成内积形式为:

\[\begin{split}P_w(y\mid x) &= \frac{exp(w.F(y,x))}{Z_w(x)} \\Z_w(x) &= \sum_{y}exp(w.F(y,x))\end{split} \]

3.4 矩阵形式

\[P_w(y\mid x) = \frac{1}{Z_w(x)}\prod_{i=1}^{n+1}M_i(y_{i-1}, y_i \mid x) \]

4. 条件随机场的基本问题

条件随机场包含概率计算问题、学习问题和预测问题三个问题:

  • 概率计算问题:已知模型的所有参数,计算观测序列 𝑌 出现的概率,常用方法:前向和后向算法;
  • 学习问题:已知观测序列 𝑌,求解使得该观测序列概率最大的模型参数,包括隐状态序列、隐状态间的转移概率分布和从隐状态到观测状态的概率分布,常用方法:Baum-Wehch 算法;
  • 预测问题:一直模型所有参数和观测序列 𝑌 ,计算最可能的隐状态序列 𝑋,常用算法:维特比算法。

5. 代码实践

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\n'%(i, y, self.D[i,y]))
        print('self.Delta:\n', self.D)
        print('self.Psi:\n', 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()
posted @ 2020-04-27 19:21  hou永胜  阅读(230)  评论(0编辑  收藏  举报