HMM原理及其实现

基本假设

齐次马尔科夫性假设:即假设隐藏的马尔科夫链在任意时刻t只依赖于前一时刻的状态,与其他时刻的状态及观测无关,与时间t也无关。

观测独立性假设:即假设任何时刻的观测只依赖于该时刻的马尔科夫状态,与其他观测及状态无关。

三大基本问题

概率计算问题,计算给定模型的情况下某观测序列出现的概率

学习问题,已知观测序列,使用极大似然估计的方法估计模型的参数,使得在该模型参数下,该观测序列生成的概率最大

预测问题,给定观测序列和模型参数,求概率最大的状态序列 

import numpy as np
class HMM():

    def __init__(self, A, B, pi):
        self.A = A
        self.B = B
        self.pi = pi
    def simulate(self, T):
        # draw_from接受一个概率分布,然后生成该分布下的一个样本。    
        def draw_from(probs):
            return np.where(np.random.multinomial(1, probs) == 1)[0][0]
        observations = np.zeros(T, dtype=int)
        states = np.zeros(T, dtype=int)    
        states[0] = draw_from(self.pi)
        observations[0] = draw_from(self.B[states[0], :])
        for t in range(1, T):
            states[t] = draw_from(self.A[states[t-1], :])
            observations[t] = draw_from(self.B[states[t], :])
        return states, observations
    def forward(self, obs_seq):
        N = self.A.shape[0]
        T = len(obs_seq)
        F = np.zeros((N, T))
        F[:, 0] = self.pi * self.B[:, obs_seq[0]]
        for t in range(1, T):
            for n in range(N):
                F[n, t] = np.dot(F[:,t-1], (self.A[:,n])) * self.B[n, obs_seq[t]]
        return F

    def backward(self,obs_seq):
        '''后向算法'''
        N = self.A.shape[0]
        T = len(obs_seq)
        M = np.zeros((N,T))
        M[:,T-1] = 1
        for t in reversed(range(T-1)):
            for n in range(N):
                M[n,t] = np.dot(self.A[n,:],M[:,t+1])*self.B[n,obs_seq[t+1]]
        return M
    def EM(self, observation, criterion=0.05):
        n_state = self.A.shape[0]
        n_sample = len(observation)
        done = 1
        while done:
            Alpha = self.forward(observation)
            Beta = self.backward(observation)
            xi = np.zeros((n_state, n_state, n_sample-1))
            gamma = np.zeros((n_state, n_sample))
            for t in range(n_sample-1):
                denom = np.sum(np.dot(Alpha[:, t].T, self.A) * self.B[:, observation[t+1]].T * Beta[:, t+1].T)
                sum_gamma1 = np.sum(Alpha[:, t] * Beta[:, t])
                for i in range(n_state):
                    numer = Alpha[i, t] * self.A[i, :] * self.B[:, observation[t+1]].T * Beta[:, t+1].T
                    xi[i, :, t] = numer/denom
                    gamma[i, t] = Alpha[i, t] * Beta[i, t] / sum_gamma1
        last_col = Alpha[:, n_sample-1].T * Beta[:, n_sample-1]
        gamma[:, n_sample-1] = last_col / np.sum(last_col)
        # 更新参数
        n_pi = gamma[:, 0]
        n_A = np.sum(xi, 2) / np.sum(gamma[:, :-1], 1)
        n_B = np.copy(self.B)
        num_level = self.B.shape[1]
        sum_gamma = 0
        a = 0
        for lev in range(num_level):
            for h in range(n_state):
                for t in range(n_sample):
                    sum_gamma = sum_gamma + gamma[h, t]
                    if observation[t] == lev:
                        a = a + gamma[h, t]
                n_B[h, lev] = a / sum_gamma
                a = 0
        # 检查阈值
        if np.max(np.abs(self.pi-n_pi)) < criterion and np.max(np.abs(self.B-n_B)) < criterion and np.max(np.abs(self.A-n_A)) < criterion:
            done = 0
        self.A, self.B, self.pi = n_A, n_B, n_pi
        return self.A, self.B, self.pi
   
    def viterbi(self, obs_seq):
        N = self.A.shape[0]
        T = len(obs_seq)
        P = np.zeros((N, T))
        prev_point = np.zeros((N, T))
        prev_point[:, 0] = 0
        P[:, 0] = self.pi * self.B[:, obs_seq[0]]
        for t in range(1, T):
            for n in range(N):
                P[n, t] = np.max(P[:, t - 1] * self.A[:, n]) * self.B[n, obs_seq[t]]
                prev_point[n, t] = np.argmax(P[:, t - 1] * self.A[:, n] * self.B[n, obs_seq[t]])
        return P, prev_point

    def build_path(self, obs_seq):
        P, prev_point = self.viterbi(obs_seq)
        T = len(obs_seq)
        opt_path = np.zeros(T)
        last_state = np.argmax(P[:, T-1])
        opt_path[T-1] = last_state
        for t in reversed(range(T-1)):
            opt_path[t] = prev_point[int(opt_path[t+1]), t+1]
            last_path = reversed(opt_path)
        return last_path



    
  

主函数实现如下:

if __name__ == '__main__':
    A = np.array([[0.5, 0.2, 0.3], [0.3, 0.5, 0.2], [0.2, 0.3, 0.5]])
    B = np.array([[0.5, 0.5], [0.4, 0.6], [0.7, 0.3]])
    pi = np.array([0.2, 0.4, 0.4])
    test1 = HMM(A, B, pi)
    obs_seq = [0, 1, 0]
    # 前向算法
    print(test1.forward(obs_seq))
    # 后向算法
    print(test1.backward(obs_seq))
    # 维特比算法
    print(test1.viterbi(obs_seq))
    # 最优路径
    print(test1.build_path(obs_seq))
    # EM算法
    print(test1.EM(obs_seq))  
发布了101 篇原创文章 · 获赞 46 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/weixin_40539952/article/details/103676818