機器學習-- > 隱馬爾科夫模型(HMM)

本篇博文將詳細總結隱馬模型相關知識,理解該模型有一定的難度,在此淺薄的談下自己的理解。參考的實現代碼:HMM_EM(無監督式)HMM有監督式

概率計算問題

HMMHMM 是關於時序的概率模型,描述由一個隱藏的馬爾科夫鏈生成不可觀測的狀態隨機序列,再由各個狀態生成觀測隨機序列的過程。

隱馬爾科夫模型隨機生成的狀態隨機序列,稱爲狀態序列;每個狀態生成一個觀測,由此產生的觀測隨機序列,稱爲觀測序列。序列的每個位置可看做是一個時刻。

這裏寫圖片描述

上圖中的ZZ 表示狀態序列,XX 表示觀測序列。假設每個時刻的狀態可能有NN 種可能,每個時刻的觀測可能有MM 種可能。

HMMHMM 由初始概率分佈ππ、狀態轉移概率分佈AA 以及觀測概率分佈BB 確定。
λ=(A,B,π)\lambda =(A, B, \pi)

這個初始概率分佈ππ 是一個大小爲NN 的向量,AA 爲大小爲NNN*N 的矩陣,BB 爲大小爲NMN*M 的矩陣。

  • II 是長度爲TT 的狀態序列,OO 是對應的觀測序列,則有:
     I={i1,i2,...,iT}O={o1,o2,...,oT}\ I =\{i_1, i_2,...,i_T\};O=\{o_1,o_2,...,o_T\}
  • AA 是狀態轉移概率矩陣,則有:
    A=[aij]NNA=[a_{ij}]_{N*N}
    其中 aij=P(it+1=qjit=qi)a_{ij}=P(i_{t+1}=q_j|i_t=q_i) 表示在時刻tt 處於狀態qiq_i 的條件下時刻 t+1t+1 轉移到狀態qjq_j 的概率。

HMM的兩個基本性質

齊次假設:
P(itit1,ot1,it2,ot2...i1,o1)=P(itit1)P(i_t|i_{t-1},o_{t-1},i_{t-2},o_{t-2}...i_1,o_1)=P(i_t|i_{t-1})
也即是當前時刻的隱狀態只與前一時刻的隱狀態有關。

觀測獨立性假設:
P(otiT,oT,iT1,oT1,...,i1,o1)=P(otit)P(o_t|i_T,o_T,i_{T-1},o_{T-1},...,i_1,o_1) = P(o_t|i_t)
也即是當前時刻的觀測狀態只與當前時刻的隱狀態有關。

HMM的三個問題

問題一:給定模型λ=(A,B,π)\lambda = (A,B,\pi) 和觀測序列O=o1,o2,..,oTO={o_1,o_2,..,o_T},計算模型λ\lambda 下觀測序列O出現的概率P(Oλ)P(O|\lambda)

我們先看看代碼中,是如何做的,參考的代碼中,數據的格式爲 [900,2][900,2],假設了這是由三個高斯模型混合而成的樣本集,每個樣本有兩個特徵,共900個樣本。注意:樣本個數也即是上面所說的步長TT。形如:

   5.8045294e-01   9.6570931e-01
   1.0101383e+00   3.9152260e-01
  -4.1251308e-01   9.6435345e-01
  -2.0477262e+00   1.5029133e+00
  -1.3130763e+00   1.6049016e-01
  -6.2352642e-01   3.7779862e-01
   1.8841870e+00   1.1210070e+00
   2.5608726e+00  -1.4935448e+00
   3.2966895e-01  -1.1184212e+00
  -1.1982074e+00   7.4402510e-01
   7.1916729e-01   1.2625977e+00
  -3.6946331e-01   1.4573214e+00
  -7.3522039e-01   7.0942551e-02
  -4.8153116e-01   1.3661593e+00

然後需要初始化出上面所講的模型的三個要素πAB\pi、A、B

def initForwardBackward(X,K,d,N):##X爲數據集,K爲隱狀態數,在這裏爲3,d爲觀測狀態數,N爲時刻總數,共N步。
    # Initialize the state transition matrix, A. A is a KxK matrix where
    # element A_{jk} = p(Z_n = k | Z_{n-1} = j)
    # Therefore, the matrix will be row-wise normalized. IOW, Sum(Row) = 1  
    # State transition probability is time independent.
    A = np.ones((K,K))##隱狀態轉移矩陣初始爲1
    A = A/np.sum(A,1)[None].T ##需要保證每列之和爲1 
    
    # Initialize the marginal probability for the first hidden variable
    # It is a Kx1 vector
    PI = np.ones((K,1))/K## 初始pi,即爲第0時刻轉移到某個隱狀態的概率
    
    ## 這裏我們假設發射矩陣服從高斯分佈,所以我們只需要定義均值和方差即可。
    ## 顯然對於不同的隱狀態,會得到不同的觀測序列。即對於不同的隱狀態有不同的高斯分佈
    ## 並且這裏數據集每個樣本有d(d=2)個feature,相當於有多維度隨機變量,每個隨機變量的分佈都有不同的均值
    ## 所以MU的shape爲[d,k],即每個隱狀態對應d個均值
    ## 每個隱狀態對應有不同的協方差矩陣
    MU = np.random.rand(d,K)
    SIGMA = [np.eye(d) for i in xrange(K)]
    return A, PI, MU, SIGMA

這樣我們就得到初始的π\pi ,狀態轉移矩陣AA,發射矩陣MU,SIGMAMU, SIGMA

前向後向算法—動態規劃

給定模型λ=(A,B,π)\lambda =(A, B, \pi) 和觀測序列 O={o1,o2,...,oT}O=\{o_1,o_2,...,o_T\} ,計算模型λλ 下觀測序列OO 出現的概率P(Oλ)P(O| λ)

我們首先嚐試直接用暴力求解:

  • 狀態序列 I={i1,i2,...,iT}\ I=\{i_1,i_2,...,i_T\} 的概率是:
    P(Iλ)=πi1αi1i2αi2i3...αiT1iTP(I|\lambda)=\pi_{i_1}\alpha_{i_1i_2}\alpha_{i_2i_3}...\alpha_{i_{T-1}i_T}
  • 對固定的狀態序列 II,測序列OO 的概率是:
    P(OI.λ)=bi1o1bi2o2...biToTP(O|I. \lambda)=b_{i_1o_1}b_{i_2o_2}...b_{i_To_T}
  • OOII 同時出現的聯合概率是:
    P(o,Iλ)=P(oI,λ)P(Iλ)=πi1bi1o1ai1i2bi2o2...P(o,I|\lambda)=P(o|I,\lambda)P(I|\lambda)=\pi_{i_1}b_{i_1o_1}a_{i_1i_2}b_{i_2o_2}...
  • 對所有可能的狀態序列II 求和,得到觀測序列OO 的概率P(Oλ)P(O|λ)
    P(oλ)=IP(o,Iλ)=IP(oI,λ)P(Iλ)P(o|\lambda) =\sum_{I}P(o,I|\lambda)=\sum_{I}P(o|I,\lambda)P(I|\lambda)
    我們可以試想,在每一個時刻,隱狀態都有NN 個選擇,一共有TT 個時刻,故TNT^N ,而求和裏面共有2T2T 個因子,故時間複雜度爲O(TNT)O(TN^T)

顯然直接暴力計算P(o,Iλ)P(o,I|\lambda) 時間複雜度過高。

前向概率

定義:給定λλ,定義到時刻tt 部分觀測序列爲o1,o2...oto_1,o_2...o_t 且狀態爲qiq_i 的概率稱爲前向概率,記做:
αt(i)=P(o1,o2,..,ot,qt=iλ)\alpha_t(i)=P(o_1,o_2,..,o_t,q_t=i|\lambda)

這裏寫圖片描述

  • 初值:
    α1(i)=πibio1\alpha_1(i)=\pi_ib_{io_1}

  • 遞推:對於t=1,2...T1t=1,2...T-1(注意這是一個從前向後的遞推過程)


    這裏寫圖片描述

    需要注意上一步,在第tt 步時,位於隱狀態jj 的概率轉移到第t+1t+1 步的隱狀態 ii,這裏面的jjNN 種情況,ii 也有NN 種情況,在第t+1t+1 步的隱狀態ii 對應有前一步的NN 種情況求和(這也是爲什麼前向計算能得出qt=iq_t=i),然後再乘以發射概率。作爲第t+1t+1 步處於該種隱狀態的概率值。注意biot+1b_{io_{t+1}} 的意義,爲當前隱狀態 ii 生成特定Ot+1O_{t+1} 的概率。 **

  • 最終可得:


    這裏寫圖片描述

    ii 積分,其結果即爲生成指定的 o1,o2,o3,....,oto_1,o_2,o_3,....,o_t 的觀測序列的概率。

前向算法的時間複雜度是O(N2T)O(N^2T)
那麼在代碼中是如何實現前向計算的呢?

def buildAlpha(X,PI,A,MU,SIGMA):## X.shape[feature,N]
    # We build up Alpha here using dynamic programming. It is a KxN matrix
    # where the element ALPHA_{ij} represents the forward probability
    # for jth timestep (j = 1...N) and ith state. The columns of ALPHA are
    # normalized for preventing underflow problem as discussed in secion
    # 13.2.4 in Bishop's PRML book. So,sum(column) = 1
    # c_t is the normalizing costant
    N = np.size(X,1)
    K = np.size(PI,0)
    ## 這裏需要注意Alpha的shape爲[K,N],表示在某個時刻爲某個特定隱藏狀態,其生成從開始時刻到當前時刻觀測序列的概率
    Alpha = np.zeros((K,N))
    c = np.zeros(N)

    # Base case: build the first column of ALPHA
    for i in xrange(K):
	    ## PI[i]表示選擇該隱狀態的概率值。
	    ## normPDF(X[:,0],MU[:,i],SIGMA[i])表示該樣本在該隱狀態下的高斯分佈下對應的概率值
    ┆   Alpha[i,0] = PI[i]*normPDF(X[:,0],MU[:,i],SIGMA[i])##也就是當前時刻的發射概率
    c[0] = np.sum(Alpha[:,0])## 每列求和
    Alpha[:,0] = Alpha[:,0]/c[0]## 歸一

    # 以下就是上面所講的從前往後的遞推過程
    for t in xrange(1,N):
    ┆   for i in xrange(K):
    ┆   ┆   for j in xrange(K):
    ┆   ┆   ┆   Alpha[i,t] += Alpha[j,t-1]*A[j,i] # sum part of recursion
    ┆   ┆   Alpha[i,t] *= normPDF(X[:,t],MU[:,i],SIGMA[i]) # product with emission prob
    ┆   c[t] = np.sum(Alpha[:,t])
    ┆   Alpha[:,t] = Alpha[:,t]/c[t]   # for scaling factors
    return Alpha, c ##注意函數返回的Alpha的shape爲[K,N]

特別需要注意上面所求矩陣AlphaAlpha 的意義:其shapeshape[K,N][K,N],表示在某個時刻爲某個特定隱藏狀態,其生成從開始時刻到當前時刻觀測序列的概率。

後向計算(同前向計算同理,只不過是從後向前計算)

定義:給定λλ,定義到時刻tt 狀態爲qiq_i 的前提下,從t+1t+1TT 的部分觀測序列爲ot+1,ot+2...oTo_{t+1} ,o_{t+2} ...o_T 的概率爲後向概率,記做:
βt(i)=P(ot+1,ot+2,...,oTit=qi.λ)\beta_t(i)=P(o_{t+1},o_{t+2},...,o_T|i_t=q_i.\lambda)

  • 初值:
    βT(i)=1\beta_T(i)=1
    此時, 觀測值爲OT+1O_{T+1}。故爲11
  • 遞推:對於t=T1,T2...,1t=T-1,T-2...,1(注意:這是一個從後向前的遞推過程

    這裏寫圖片描述

    這裏需要注意βt+1(j)\beta_{t+1}(j) 是可以得到Ot+2,Ot+3,...O_{t+2},O_{t+3},... 而不知道Ot+1O_{t+1},所以對於不同的 jj ,都要乘以 bjot+1b_{jo_{t+1}}
  • 最終:

    這裏寫圖片描述

    看看代碼中是如何實現後向計算的:
def buildBeta(X,c,PI,A,MU,SIGMA):## X.shape[features, N]
    # Beta is KxN matrix where Beta_{ij} represents the backward probability
    # for jth timestamp and ith state. Columns of Beta are normalized using
    # the element of vector c.

    N = np.size(X,1)
    K = np.size(PI,0)
    Beta = np.zeros((K,N))## 同上,shape也是[K,N],表示某一時刻的隱狀態爲某一隱狀態從最後生成到當前時刻序列的概率。

    # Base case: build the last column of Beta
    for i in xrange(K):
    ┆   Beta[i,N-1]=1.## 此時,觀測值爲$O_{N}$。故爲1
    ┆
    # 按照上面所說的從後向前進行遞推。直到t==0時。
    for t in xrange(N-2,-1,-1):
    ┆   for i in xrange(K):
    ┆   ┆   for j in xrange(K):
    ┆   ┆   ┆   Beta[i,t] += Beta[j,t+1]*A[i,j]*normPDF(X[:,t+1],MU[:,j],SIGMA[j])
    ┆   Beta[:,t] /= c[t+1]
    return Beta

一定要注意上面代碼中,矩陣β\beta 的意義:shape也是[K,N],表示某一時刻的隱狀態爲某一隱狀態生成從最後時刻到當前時刻指定序列的概率。

前向後向概率的關係

這裏寫圖片描述

這裏寫圖片描述

這裏寫圖片描述

這種前向後向計算,每次都是在上一層的基礎上進行遞推,相對於暴力計算方法,避免了大量的重複計算,降低了複雜度,計算只存在於相鄰的時間點內。

單個狀態的概率

求給定模型λλ 和觀測OO,在時刻t處於狀態qiq_i 的概率。記:
γt(i)=P(it=qiO,λ)\gamma_t(i) = P(i_t=q_i|O,\lambda)
可計算得:


這裏寫圖片描述

Gamma = Alpha*Beta ## 矩陣點乘,shape爲[k,N],這裏沒有除以分母,分母即所求gamma矩陣所在列之和。
這個Gamma主要是在M步更新PI的,故在M步再除分母一樣

這就意味着只要我們知道TT 個觀測序列,和模型λ\lambda(初始狀態,狀態矩陣,狀態轉移矩陣),就可以計算每個時刻的隱狀態。即:在每個時刻tt 選擇在該時刻最有可能出現的狀態iti_t^* ,從而得到一個狀態序列I=i1,i2...iTI^* ={i_1^* , i_2^ *... i_T^* },將它作爲預測的結果。

兩個狀態的聯合概率

求給定模型λλ 和觀測OO,在時刻tt 處於狀態qiq_i 並且時刻t+1t+1 處於狀態qjq_j 的概率。
εt(i,j)=P(it=qi,it+1=qjO,λ)\varepsilon _t(i,j)=P(i_t=q_i,i_{t+1}=q_j|O,\lambda)


這裏寫圖片描述

那麼在代碼中如何實現 εt(i,j)\varepsilon _t(i,j) 呢?首先我們可以試想,ii 共有三種狀態,jj 也有三種狀態,故每相鄰的狀態轉移共有 99 種轉移方式。而代碼中共有N=900N=900 個時刻。故 εt(i,j)\varepsilon _t(i,j)shape[3,3,900]shape[3,3,900]

i = np.zeros((K,K,N))
for t in xrange(1,N):
	## 每個時刻都是3*3 的矩陣,\alpha_{T}(i)*a_{ij}*\Beta_{t+1}(j),i,j都有k種可能,故a_{ij}就是轉移矩陣A。
    Xi[:,:,t] = (1/c[t])*Alpha[:,t-1][None].T.dot(Beta[:,t][None])*A
    # Now columnwise multiply the emission prob
    for col in xrange(K):
	    ## 因爲還需要乘以b_{jO_{t+1}},而3*3矩陣第二維即表示轉移的j
    ┆   Xi[:,col,t] *= normPDF(trainSet[:,t],MU[:,col],SIGMA[col])

第二個問題:學習問題,給出觀測序列OO,估計模型參數λ=(π,A,B)\lambda =(\pi,A,B),使得P(Oλ)P(O|\lambda) 最大,顯然用MLE的方式來估計。

監督學習

若訓練數據包括觀測序列和狀態序列,則HMMHMM 的學習非常簡單,是監督學習。利用大數定理的結論 “頻率的極限是概率”,給出HMMHMM 的參數估計。

  1. 初始概率

    這裏寫圖片描述
  2. 轉移概率

    這裏寫圖片描述
  3. 觀測概率

    這裏寫圖片描述

有監督式的學習比較簡單,就是統計每個句子裏的每個詞的狀態而已,大概的講下思路:

  1. 獲取已經分詞好的語料庫,類似這樣
    1986年 , 
    十億 中華 兒女 踏上 新 的 徵 程 。 
    過去 的 一 年 , 
    是 全國 各族 人民 在 中國 共產黨 領導 下 , 
    在 建設 有 中國 特色 的 社會主義 道路 上 , 
    堅持 改革 、 開放 , 
    團結 奮鬥 、 勝利 前進 的 一 年 。 
    
  2. 每個詞即爲一個觀測狀態,再定義詞的隱狀態,例如參考代碼中的 B(開頭),M(中間), E(結尾), S(獨立成詞)作爲四種隱狀態,則可得到語料庫中每句話的隱狀態序列。由隱狀態序列求得 轉移概率。
  3. π\pi 爲初始狀態,可從語料庫中每句開頭第一個詞對應的隱狀態得出
  4. 由語料庫中每個隱狀態對應的詞得出 觀測概率
  5. 由此可以從語料庫中學習到參數矩陣 (π,A,B)(\pi,A,B),然後可以利用學習到的參數矩陣,對要預測的句子進行分詞(Viterbi算法),可根據預測得到的每個詞隱狀態,決定是否進行分詞。

循環遍歷語料庫中每個句子,統計句子中每個詞的隱狀態(已經定義好每個詞對應的隱狀態)。得到該句的line_stateline\_state 隱狀態列表。

for i in range(len(line_state)):## 不同的句子,其line_state不同,可以理解爲不同的時刻
    if i == 0:
    ┆   Pi_dic[line_state[0]] += 1## 該句第一個詞的隱狀態
    ┆   Count_dic[line_state[0]] += 1## 後面做歸一化用的
    else:
    ┆   A_dic[line_state[i-1]][line_state[i]] += 1## 統計轉移概率
    ┆   Count_dic[line_state[i]] += 1
	    ## 統計發射概率,第i個隱狀態對應第i個觀測狀態
    ┆   if not B_dic[line_state[i]].has_key(word_list[i]):
    ┆   ┆   B_dic[line_state[i]][word_list[i]] = 0.0 
    ┆   else:
    ┆   ┆   B_dic[line_state[i]][word_list[i]] += 1

這樣統計完語料庫中的每個句子後,做完歸一化後得到最終的 πAB\pi、A、B

無監督學習(Baum-Welch算法)

若訓練數據只有觀測序列,則HMMHMM 的學習,需要使用EMEM 算法,是非監督學習。
EMEM 算法整體框架:

這裏寫圖片描述

所有觀測數據寫成O=(o1,o2...oT)O=(o_1 ,o_ 2 ...o_T ),所有隱數據寫成I=(i1,i2...iT)I=(i_1 ,i_2 ...i_T ),完全數據是(O,I)=(o1,o2...oT,i1,i2...iT)(O,I)=(o_1 ,o_2 ...o_T ,i_1 ,i_2 ...i_T ),完全數據的對數似然函數是 lnP(O,Iλ)lnP(O,I|λ)

HMMHMM 中,上面公式中的xx 就是觀測OO,隱隨機變量就是隱狀態 II。則其EMEM 公式中的QQHMMHMM 中爲 p(Io)p(I|o),這其實就是EE 步。

假設λˉ\bar{\lambda}HMMHMM 參數的當前估計值(也就是上一輪中得出的最優的參數)

這個EE 步在代碼中如何實現呢?實際上由後面的MM 步中爲了得到λ(MU,SIGMAAπ)\lambda(MU, SIGMA、轉移矩陣A,\pi) 需要先知道γε\gamma、\varepsilon 的值,故在EE 步時先更新得到:

def Estep(trainSet, PI,A,MU,SIGMA):## PI,A,MU,SIGMA 爲上一輪M步迭代更新出的\lambda
	## 即在E步利用上一輪更新後的PI,A,MU,SIGMA來計算gamma等
    # The goal of E step is to evaluate Gamma(Z_{n}) and Xi(Z_{n-1},Z_{n})
    # First, create the forward and backward probability matrices
    Alpha, c = buildAlpha(trainSet, PI,A,MU,SIGMA)
    Beta = buildBeta(trainSet,c,PI,A,MU,SIGMA)
    
    # Dimension of Gamma is equal to Alpha and Beta where nth column represents
    # posterior density of nth latent variable. Each row represents a state
    # value of all the latent variables. IOW, (i,j)th element represents
    # p(Z_j = i | X,MU,SIGMA) 
    Gamma = Alpha*Beta
    #pdb.set_trace()
    
    # Xi is a KxKx(N-1) matrix (N is the length of data seq)
    # Xi(:,:,t) = Xi(Z_{t-1},Z_{t})
    N = np.size(trainSet,1)
    K = np.size(PI,0)    
    Xi = np.zeros((K,K,N))
    for t in xrange(1,N):
    ┆   Xi[:,:,t] = (1/c[t])*Alpha[:,t-1][None].T.dot(Beta[:,t][None])*A
    ┆   # Now columnwise multiply the emission prob
    ┆   for col in xrange(K):
    ┆   ┆   Xi[:,col,t] *= normPDF(trainSet[:,t],MU[:,col],SIGMA[col])
    return Gamma, Xi, c

λλ 爲待求 的參數。則有:


這裏寫圖片描述

我們就是要最求上面 Q(λ,λˉ)Q(\lambda,\bar{\lambda}) 取極值時對應的λ\lambda ,其實就是 πAB\pi、A、B,這就是MM 步。

根據上面的暴力計算的結論:


這裏寫圖片描述

函數可寫成:


這裏寫圖片描述

  • 極大化QQ,獲得π\pi 參數

    這裏寫圖片描述

    注意到πi\pi_i 加和爲1,利用拉格朗日乘子法得:

    這裏寫圖片描述

對上式中的πi\pi_i 求導,可得:


這裏寫圖片描述

ii 求和,得到:


這裏寫圖片描述

從而得到:


這裏寫圖片描述

PI = (Gamma[:,0]/np.sum(Gamma[:,0]))[None].T

同理可以用拉格朗日乘子法求得:


這裏寫圖片描述
這裏寫圖片描述

代碼中是如何實現MM 步呢?

def Mstep(X, Gamma, Xi):
    # Goal of M step is to calculate PI, A, MU, and SIGMA while treating
    # Gamma and Xi as constant
    K = np.size(Gamma,0)
    d = np.size(X,0)

    PI = (Gamma[:,0]/np.sum(Gamma[:,0]))[None].T
    tempSum = np.sum(Xi[:,:,1:],axis=2)
    A = tempSum/np.sum(tempSum,axis=1)[None].T ## 轉移矩陣A
    MU = np.zeros((d,K))
    GamSUM = np.sum(Gamma,axis=1)[None].T
    SIGMA = []
    for k in xrange(K):  
    ┆   MU[:,k] = np.sum(Gamma[k,:]*X,axis=1)/GamSUM[k]
    ┆   X_MU = X - MU[:,k][None].T
    ┆   SIGMA.append(X_MU.dot(((X_MU*(Gamma[k,:][None])).T))/GamSUM[k])
    return PI,A,MU,SIGMA

問題三:預測算法

在每個時刻 tt 選擇在該時刻最有可能出現的狀態 iti_t^* ,從而得到一個狀態序列I={i1,i2...iT}I^* =\{i_1^* , i_2^ *... i_T^* \},將它作爲預測的結果。

Viterbi算法

ViterbiViterbi 算法實際是用 動態規劃HMMHMM 預測問題,用DPDP 求概率最大的路徑(最優路徑),這是一條路徑對應一個狀態序列。其實就是我們知道了模型參數λ\lambda 後,從時刻11 遞推到時刻TT 的最大概率路徑。

定義變量δt(i)δ_t (i):在時刻tt 狀態爲ii 的所有路徑中,概率的最大值。

  • 遞推

    這裏寫圖片描述
  • 終止

    這裏寫圖片描述

那麼在代碼中時如何實現ViterbiViterbi 算法呢?

def viterbi(obs, states, start_p, trans_p, emit_p):
"""
obs: 需要切分的sentence
states: 狀態種類序列,例如每個詞可能有四個狀態[B, M, E, S]
start_p: 就是上面所講的\PI
trans_p: 狀態轉移矩陣
emit_p: 發射矩陣
"""
    V = [{}] #tabular V[t][state]:t表示時刻,state表示該時刻的隱狀態
    path = {}
    for y in states: #init
	    ## emit_p[y].get(obs[0],0)表示在y隱狀態下,觀測狀態爲obs[0] 的發射概率。
	    ## 在t=0 時刻時,觀測狀態即爲obs[0]
    ┆   V[0][y] = start_p[y] * emit_p[y].get(obs[0],0)
    ┆   path[y] = [y] ## 記錄當前的狀態路徑
    for t in range(1,len(obs)):
    ┆   V.append({})
    ┆   newpath = {}
    ┆   for y in states:
		    ## 在t時刻時,遍歷t-1時刻所有可能的隱狀態state與當前y隱狀態的連接概率,獲取最大時對於的state和對應的概率prob
    ┆   ┆   (prob,state ) = max([(V[t-1][y0] * trans_p[y0].get(y,0) * emit_p[y].get(obs[t],0) ,y0) for y0 in states if V[t-1][y0]>0])
    ┆   ┆   V[t][y] =prob## 將最大概率prob作爲V[t][y]
    ┆   ┆   newpath[y] = path[state] + [y]## path[state] 表示t-1時刻最大概率對應的隱狀態序列,再加上當前時刻的y
    ┆   path = newpath
    ## 取最後概率最大的對應的序列作爲最後結果。
    (prob, state) = max([(V[len(obs) - 1][y], y) for y in states])
    return (prob, path[state])

def cut(sentence):
    #pdb.set_trace()
    prob, pos_list =  viterbi(sentence,('B','M','E','S'), prob_start, prob_trans, prob_emit)
    return (prob,pos_list)

ViterbiViterbi 算法其實就是多步驟、每步多選擇模型的最優選擇問題,其在每一步的所有選擇都保存了從第一步到當前步的的最小或最大代價,以及當前情況下前進步驟的選擇。並且記下在每一步每一個隱狀態與上一步對應代價最小的隱狀態節點,如果有nn 個隱狀態,就形成nn 個不同的鏈路,並且保證了每個節點對應的鏈路都是該節點的最小代價。

個人總結

我個人覺得前向、後向計算、ViterbiViterbi 算法、BeamSearchBeamSearch 這幾個算法有幾分相似,又有幾分區別之處,值得思考一下:

  • 前向、後向計算是利用動態規劃的思想,每一步的計算都是在前一步計算結果的基礎上,大大降低了計算量。
  • ViterbiViterbi 算法 同樣也是利用了動態規劃的思想,能保證找到最優的路徑。
  • BeamSearchBeamSearch 利用的是貪心的思想,每一步只能找到當前時刻最優的BeamSizeBeamSize 個不同的tokentoken,而只是在當前時刻最優,卻不能保證整體最優,故beamSearchbeamSearch最後結果可能不是最優結果。那麼有人可能會問,爲啥 BeamSearchBeamSearch 不用ViterbiViterbi 的那種動態規劃的思想,而用貪心?這個問題其實很簡單,試想一下,在ViterbiViterbi 中,最後一個時刻每個狀態都有對應的最優鏈路,因此我們可以找到最優的,而在BeamSearchBeamSearch 中呢,你不可能在最後一步遍歷所有的隱狀態(詞表一般很大),你只能採取貪心的方式每一步選擇當前最優的 BeamSizeBeamSize 個狀態。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章