隱馬爾可夫模型的學習,根據訓練數據是包括觀測序列和狀態序列還是隻有觀測序列,可以分別由監督學習與非監督學習實現。由於監督學習需要使用訓練數據,而人工標註訓練數據往往代價很高,有時就會利用非監督學習的方法,即Baum-Welch算法(也就是EM算法)。在介紹學習算法之前,先介紹一些概率和期望值的計算。這些計算會成爲Baum-Welch算法公式的基礎。
一些概率和期望值的計算
利用前向概率和後向概率,可以得到關於單個狀態和兩個狀態概率的計算公式。
1. 給定模型 和觀測 ,在時刻 處於狀態 的概率。記爲
先分解爲分數形式
根據前向概率的定義可以做以下變換
後向概率的定義如下
將這兩者相乘得到
以上結果從兩者的定義上也很好理解。
對變量 在範圍 上求和
將式 代入 可以得到
2. 給定模型 和觀測 ,在時刻 處於狀態 且在時刻 處於狀態 的概率。記爲
通過前向後向概率計算:
分子可以用前向後向概率表示
則 可以表示爲
3. 將 和 對各個時刻求和,可以得到一些有用的期望值。
(1) 觀測 下,狀態 出現的期望值
將每一個時刻下,出現狀態 的概率相加
(2) 觀測 下,由狀態 轉移的期望值
能夠從狀態 轉移的時刻是 ,比上一個求和公式少了時刻
(3) 觀測 下,由狀態 轉移到狀態 的期望值
Baum-Welch模型
參數估計公式
·推導的過程,尤其是拉格朗日對偶,我暫時還不十分理解,先直接給出訓練方法,公式和代碼。Baum-Welch算法(Baum-Welch algorithm),它是EM算法在隱馬爾可夫模型學習過程中的具體實現,由Baum和Welch提出。
(1)初始化
對n=0,選取 ,得到模型
(2)遞推。對
公式右端按照觀測 和模型 代入計算
(3)終止,得到模型
Baum-Welch算法的Python實現
def baum_welch_train(self, observations, criterion=0.05):
n_states = self.A.shape[0]
n_samples = len(observations)
done = False
while not done:
# alpha_t(i) = P(O_1 O_2 ... O_t, q_t = S_i | hmm)
# Initialize alpha
alpha = self._forward(observations)
# beta_t(i) = P(O_t+1 O_t+2 ... O_T | q_t = S_i , hmm)
# Initialize beta
beta = self._backward(observations)
xi = np.zeros((n_states,n_states,n_samples-1))
for t in range(n_samples-1):
denom = np.dot(np.dot(alpha[:,t].T, self.A) * self.B[:,observations[t+1]].T, beta[:,t+1])
for i in range(n_states):
numer = alpha[i,t] * self.A[i,:] * self.B[:,observations[t+1]].T * beta[:,t+1].T
xi[i,:,t] = numer / denom
# gamma_t(i) = P(q_t = S_i | O, hmm)
gamma = np.sum(xi,axis=1)
# Need final gamma element for new B
prod = (alpha[:,n_samples-1] * beta[:,n_samples-1]).reshape((-1,1))
gamma = np.hstack((gamma, prod / np.sum(prod))) #append one more to gamma!!!
newpi = gamma[:,0]
newA = np.sum(xi,2) / np.sum(gamma[:,:-1],axis=1).reshape((-1,1))
newB = np.copy(self.B)
num_levels = self.B.shape[1]
sumgamma = np.sum(gamma,axis=1)
for lev in range(num_levels):
mask = observations == lev
newB[:,lev] = np.sum(gamma[:,mask],axis=1) / sumgamma
if np.max(abs(self.pi - newpi)) < criterion and \
np.max(abs(self.A - newA)) < criterion and \
np.max(abs(self.B - newB)) < criterion:
done = 1
self.A[:],self.B[:],self.pi[:] = newA,newB,newpi