條件隨機場(crf)及tensorflow代碼實例

對於條件隨機場的學習,我以爲應該結合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中也稱爲狀態序列)。

  • 學習問題中,利用極大似然估計,估計P^(Y|X)
  • 預測問題中,利用給定的序列x,求出條件機率P^(Y|X)最大的輸出序列y^

 

  通常的線性鏈條件隨機場表示如圖,一般假設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最大似然估計

  • 頻率學派:最大似然估計(MLE):在進行推論時,咱們只關心似然度,並選擇給出所最大化 p(data|hypo) 的假設做爲預測。
  • 貝葉斯學派:最大後驗估計(MAP):咱們也須要把先驗 p(hypo) 歸入計算,不只是似然度,還要選擇給出最大 p(data|hypo) * p(hypo) 的假設做爲預測。
  • 若是咱們認爲全部假設都服從均勻分佈,那麼MAP = MLE 。

  能夠看到,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))

  

   

  獲得的結果:

相關文章
相關標籤/搜索