【深度剖析HMM(附Python代碼)】2.隱馬爾科夫鏈HMM的EM訓練過程

隱馬爾科夫鏈HMM的參數θ的EM訓練過程

現在回到前一節最後提出的參數θ的最大似然函數上來,先對其做個對數變換,做對數變換是考慮到序列X的概率計算公式中包含了連乘,爲了方便計算同時避免序列X的概率過小,因此對其做了對數變換。


的期望計算中,對於序列X是已知的,而的概率是由舊參數值 所估計的,因此上式可以表示爲:


爲了方便表示,以下定義:


可以表示爲:


根據HMM的結構定義,其參數θ主要分爲三部分:隱藏狀態的先驗分佈π(同相關),各隱藏狀態之間的轉移概率Λ(同 相關),即已知隱藏狀態確定觀測值的發射概率參數∅(同相關)。由此可以得出



此時綜合得出:


這裏定義:

1、E步驟


這裏定義:

求α的過程,也即所謂的前向算法,具體代碼如下(這裏增加了一個歸一化因子c,下面會具體講解):

    # 求向前傳遞因子
    def forward(self, X, Z):
        X_length = len(X)
        alpha = np.zeros((X_length, self.n_state))  # P(x,z)
        alpha[0] = self.emit_prob(X[0]) * self.start_prob * Z[0] # 初始值
        # 歸一化因子
        c = np.zeros(X_length)
        c[0] = np.sum(alpha[0])
        alpha[0] = alpha[0] / c[0]
        # 遞歸傳遞
        for i in range(X_length):
            if i == 0: continue
            alpha[i] = self.emit_prob(X[i]) * np.dot(alpha[i - 1], self.transmat_prob) * Z[i]
            c[i] = np.sum(alpha[i])
            if c[i]==0: continue
            alpha[i] = alpha[i] / c[i]

        return alpha, c

同理,我們也可以通過後向算法來遞歸求出β


Python代碼

    # 求向後傳遞因子
    def backward(self, X, Z, c):
        X_length = len(X)
        beta = np.zeros((X_length, self.n_state))  # P(x|z)
        beta[X_length - 1] = np.ones((self.n_state))
        # 遞歸傳遞
        for i in reversed(range(X_length)):
            if i == X_length - 1: continue
            beta[i] = np.dot(beta[i + 1] * self.emit_prob(X[i + 1]), self.transmat_prob.T) * Z[i]
            if c[i+1]==0: continue
            beta[i] = beta[i] / c[i + 1]

        return beta

另外還可以根據α和β值求出序列X的發生概率



α和β的歸一化問題


引入縮放因子:


歸一化的α的新求解公式表示爲


這裏的c非常好求:


因此可以求出


同理:


相關Python代碼:

# E步驟
# 向前向後傳遞因子
alpha, c = self.forward(X, Z)  # P(x,z)
beta = self.backward(X, Z, c)  # P(x|z)

post_state = alpha * beta
post_adj_state = np.zeros((self.n_state, self.n_state))  # 相鄰狀態的聯合後驗概率
      for i in range(X_length):
           if i == 0: continue
           if c[i]==0: continue
           post_adj_state += (1 / c[i])*np.outer(alpha[i - 1],beta[i]*self.emit_prob(X[i]))*self.transmat_prob

而此時序列X的發生概率可以計算爲:

 

相關Python代碼

    # 估計序列X出現的概率
    def X_prob(self, X, Z_seq=np.array([])):
        # 狀態序列預處理
        # 判斷是否已知隱藏狀態
        X_length = len(X)
        if Z_seq.any():
            Z = np.zeros((X_length, self.n_state))
            for i in range(X_length):
                Z[i][int(Z_seq[i])] = 1
        else:
            Z = np.ones((X_length, self.n_state))
        # 向前向後傳遞因子
        _, c = self.forward(X, Z)  # P(x,z)
        # 序列的出現概率估計
        prob_X = np.sum(np.log(c))  # P(X)
        return prob_X

2、M步驟

解最大似然方程,首先定義拉格朗日式:


求解初始狀態概率爲:


同理,求解狀態轉換概率爲:


這個過程用Python代碼表示:

# M步驟,估計參數
self.start_prob = post_state[0] / np.sum(post_state[0])
for k in range(self.n_state):
      self.transmat_prob[k] = post_adj_state[k] / np.sum(post_adj_state[k])
下面我們解決不同類型的發射概率計算。

均值求解:

同理協方差求解:

 

相關Python代碼:

    def emit_prob_updated(self, X, post_state): # 更新發射概率
        for k in range(self.n_state):
            for j in range(self.x_size):
                self.emit_means[k][j] = np.sum(post_state[:,k] *X[:,j]) / np.sum(post_state[:,k])

            X_cov = np.dot((X-self.emit_means[k]).T, (post_state[:,k]*(X-self.emit_means[k]).T).T)
            self.emit_covars[k] = X_cov / np.sum(post_state[:,k])
            if det(self.emit_covars[k]) == 0: # 對奇異矩陣的處理
                self.emit_covars[k] = self.emit_covars[k] + 0.01*np.eye(len(X[0]))


關於離散概率分佈函數的更新,離散概率分佈類似於一個表格,觀測值x只能包含有限的特定值,而離散概率分佈表示爲由某狀態得到某觀測值的概率。由此我們重新定義拉格朗日式,這裏增加的一項指某狀態生成所有觀測值的概率之和應該爲1。


然後我們求解離散概率分佈函數:


相關Python代碼爲:

    def emit_prob_updated(self, X, post_state): # 更新發射概率
        self.emission_prob = np.zeros((self.n_state, self.x_num))
        X_length = len(X)
        for n in range(X_length):
            self.emission_prob[:,int(X[n])] += post_state[n]

        self.emission_prob+= 0.1/self.x_num
        for k in range(self.n_state):
            if np.sum(post_state[:,k])==0: continue
            self.emission_prob[k] = self.emission_prob[k]/np.sum(post_state[:,k])


經過EM算法後,可以得到隱馬爾科夫的所有參數。


PS:

項目說明:http://blog.csdn.net/tostq/article/details/70846702

代碼下載:https://github.com/tostq/Easy_HMM (點星是對作者最好的支持!!!^_^)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章