⚠️ Alpha内测版本警告:此为早期内部构建版本,尚不完整且可能存在错误,欢迎大家提Issue反馈问题或建议。
Skip to content

第10章 隐马尔可夫模型

习题10.1

  给定盒子和球组成的隐马尔可夫模型λ=(A,B,π),其中,

A=[0.50.20.30.30.50.20.20.30.5],B=[0.50.50.40.60.70.3],π=(0.2,0.4,0.4)T

T=4O=(,,,),试用后向算法计算P(O|λ)

解答:

解答思路:

  1. 列出隐马尔可夫模型的定义
  2. 列出后向算法
  3. 自编码实现隐马尔可夫的后向算法

解答步骤:

第1步:隐马尔可夫模型

  根据书中第10.1.1节隐马尔可夫模型的定义:

  1. 条件假设:
      设Q是所有可能的状态的集合,V是所有可能的观测的集合:
Q={q1,q2,,qN},V={v1,v2,,vM}

其中,N是可能的状态数,M是可能的观测数。
  I是长度为T的状态序列,O是对应的观测序列:

I=(i1,i2,,iT),O=(o1,o2,,oT)
  1. 状态转移概率矩阵A
      A是状态转移概率矩阵:
A=[aij]N×N

其中,

aij=P(it+1=qj|it=qi),i=1,2,,N;j=1,2,N

是在时刻t处于状态qi的条件下,在时刻t+1转移到状态qj的概率。

  1. 观测概率矩阵B
      B是观测概率矩阵:
B=[bj(k)]N×M

其中,

bj(k)=P(ot=vk|it=qj),k=1,2,,M;j=1,2,N

是在时刻t处于状态qj的条件下,生成观测vk的概率。

  1. 初始状态概率向量π
      π是初始状态概率向量:
π=(πi)

其中,

π=P(i1=qi),i=1,2,N

是时刻t=1处于状态qi的概率。

  1. 隐马尔可夫模型的表示:
      隐马尔可夫模型由初始状态概率向量π、状态转移概率矩阵A和观测概率矩阵B决定。πA决定状态序列,B决定观测序列。因此隐马尔可夫模型λ可以用三元符号表示,即
λ=(A,B,π)

A,B,π称为隐马尔可夫模型的三要素。

第2步:后向算法

  根据书中第10章的算法10.3的观测序列概率的后向算法:

算法10.3(观测序列概率的后向算法)
输入:隐马尔可夫模型λ,观测序列O
输出:观测序列概率P(O|λ)
(1)

βT(i)=1,i=1,2,,N

(2)对t=T1,T2,,1

βt(i)=j=1Naijbj(ot+1)βt+1(j),i=1,2,,N

(3)

P(O|λ)=i=1Nπibi(o1)β1(i)

第3步:自编码实现隐马尔可夫的后向算法

python
import numpy as np


class HiddenMarkovBackward:
    def __init__(self):
        self.betas = None
        self.backward_P = None

    def backward(self, Q, V, A, B, O, PI):
        """
        后向算法
        :param Q: 所有可能的状态集合
        :param V: 所有可能的观测集合
        :param A: 状态转移概率矩阵
        :param B: 观测概率矩阵
        :param O: 观测序列
        :param PI: 初始状态概率向量
        """
        # 状态序列的大小
        N = len(Q)
        # 观测序列的大小
        M = len(O)
        # (1)初始化后向概率beta值,书中第201页公式(10.19)
        betas = np.ones((N, M))
        self.print_betas_T(N, M)

        # (2)对观测序列逆向遍历,M-2即为T-1
        print("\n从时刻T-1到1观测序列的后向概率:")
        for t in range(M - 2, -1, -1):
            # 得到序列对应的索引
            index_of_o = V.index(O[t + 1])
            # 遍历状态序列
            for i in range(N):
                # 书中第201页公式(10.20)
                betas[i][t] = np.dot(np.multiply(A[i], [b[index_of_o] for b in B]),
                                     [beta[t + 1] for beta in betas])
                real_t = t + 1
                real_i = i + 1
                self.print_betas_t(A, B, N, betas, i, index_of_o, real_i, real_t, t)

        # 取出第一个值索引,用于得到o1
        index_of_o = V.index(O[0])
        self.betas = betas
        # 书中第201页公式(10.21)
        P = np.dot(np.multiply(PI, [b[index_of_o] for b in B]),
                   [beta[0] for beta in betas])
        self.backward_P = P
        self.print_P(B, N, P, PI, betas, index_of_o)

    @staticmethod
    def print_P(B, N, P, PI, betas, index_of_o):
        print("\n观测序列概率:")
        print("P(O|lambda) = ", end="")
        for i in range(N):
            print("%.1f * %.1f * %.5f + "
                  % (PI[0][i], B[i][index_of_o], betas[i][0]), end="")
        print("0 = %f" % P)

    @staticmethod
    def print_betas_t(A, B, N, betas, i, index_of_o, real_i, real_t, t):
        print("beta%d(%d) = sum[a%dj * bj(o%d) * beta%d(j)] = ("
              % (real_t, real_i, real_i, real_t + 1, real_t + 1), end='')
        for j in range(N):
            print("%.2f * %.2f * %.2f + "
                  % (A[i][j], B[j][index_of_o], betas[j][t + 1]), end='')
        print("0) = %.3f" % betas[i][t])

    @staticmethod
    def print_betas_T(N, M):
        print("初始化后向概率:")
        for i in range(N):
            print('beta%d(%d) = 1' % (M, i + 1))
python
Q = [1, 2, 3]
V = ['红', '白']
A = [[0.5, 0.2, 0.3], [0.3, 0.5, 0.2], [0.2, 0.3, 0.5]]
B = [[0.5, 0.5], [0.4, 0.6], [0.7, 0.3]]
O = ['红', '白', '红', '白']
PI = [[0.2, 0.4, 0.4]]

hmm_backward = HiddenMarkovBackward()
hmm_backward.backward(Q, V, A, B, O, PI)
初始化后向概率:
beta4(1) = 1
beta4(2) = 1
beta4(3) = 1

从时刻T-1到1观测序列的后向概率:
beta3(1) = sum[a1j * bj(o4) * beta4(j)] = (0.50 * 0.50 * 1.00 + 0.20 * 0.60 * 1.00 + 0.30 * 0.30 * 1.00 + 0) = 0.460
beta3(2) = sum[a2j * bj(o4) * beta4(j)] = (0.30 * 0.50 * 1.00 + 0.50 * 0.60 * 1.00 + 0.20 * 0.30 * 1.00 + 0) = 0.510
beta3(3) = sum[a3j * bj(o4) * beta4(j)] = (0.20 * 0.50 * 1.00 + 0.30 * 0.60 * 1.00 + 0.50 * 0.30 * 1.00 + 0) = 0.430
beta2(1) = sum[a1j * bj(o3) * beta3(j)] = (0.50 * 0.50 * 0.46 + 0.20 * 0.40 * 0.51 + 0.30 * 0.70 * 0.43 + 0) = 0.246
beta2(2) = sum[a2j * bj(o3) * beta3(j)] = (0.30 * 0.50 * 0.46 + 0.50 * 0.40 * 0.51 + 0.20 * 0.70 * 0.43 + 0) = 0.231
beta2(3) = sum[a3j * bj(o3) * beta3(j)] = (0.20 * 0.50 * 0.46 + 0.30 * 0.40 * 0.51 + 0.50 * 0.70 * 0.43 + 0) = 0.258
beta1(1) = sum[a1j * bj(o2) * beta2(j)] = (0.50 * 0.50 * 0.25 + 0.20 * 0.60 * 0.23 + 0.30 * 0.30 * 0.26 + 0) = 0.112
beta1(2) = sum[a2j * bj(o2) * beta2(j)] = (0.30 * 0.50 * 0.25 + 0.50 * 0.60 * 0.23 + 0.20 * 0.30 * 0.26 + 0) = 0.122
beta1(3) = sum[a3j * bj(o2) * beta2(j)] = (0.20 * 0.50 * 0.25 + 0.30 * 0.60 * 0.23 + 0.50 * 0.30 * 0.26 + 0) = 0.105

观测序列概率:
P(O|lambda) = 0.2 * 0.5 * 0.11246 + 0.4 * 0.4 * 0.12174 + 0.4 * 0.7 * 0.10488 + 0 = 0.060091

可得P(O|λ)=0.060091

习题10.2

  给定盒子和球组成的隐马尔可夫模型λ=(A,B,π),其中,

A=[0.50.10.40.30.50.20.20.20.6],B=[0.50.50.40.60.70.3],π=(0.2,0.3,0.5)T

T=8O=(,,,,,,,),试用前向后向概率计算P(i4=q3|O,λ)

解答:

解答思路:

  1. 列出前向算法
  2. 根据前向概率和后向概率,列出单个状态概率的计算公式
  3. 自编程实现用前向后向概率计算P(i4=q3|O,λ)

解答步骤:

第1步:前向算法

  根据书中第10章的算法10.2的观测序列概率的前向算法:

算法10.2(观测序列概率的前向算法)
输入:隐马尔可夫模型λ,观测序列O
输出:观测序列概率P(O|λ)
(1)初值

α1(i)=πibi(o1),i=1,2,,N

(2)递推,对t=1,2,,T1

αt+1(i)=[j=1Nαt(j)aji]bi(ot+1)i=1,2,,N

(3)终止

P(O|λ)=i=1NαT(i)

第2步:单个状态概率的计算公式

  根据书中第10.2.4节单个状态概率的计算公式:

  利用前向概率和后向概率,给定模型λ和观测O,在时刻t处于状态qi的概率

γt(i)=P(it=qi|O,λ)=P(it=qi,O|λ)P(O|λ)=αt(i)βt(i)P(O|λ)

第3步:自编程实现前向后向算法

python
import numpy as np


class HiddenMarkovForwardBackward(HiddenMarkovBackward):
    def __init__(self, verbose=False):
        super(HiddenMarkovBackward, self).__init__()
        self.alphas = None
        self.forward_P = None
        self.verbose = verbose

    def forward(self, Q, V, A, B, O, PI):
        """
        前向算法
        :param Q: 所有可能的状态集合
        :param V: 所有可能的观测集合
        :param A: 状态转移概率矩阵
        :param B: 观测概率矩阵
        :param O: 观测序列
        :param PI: 初始状态概率向量
        """
        # 状态序列的大小
        N = len(Q)
        # 观测序列的大小
        M = len(O)
        # 初始化前向概率alpha值
        alphas = np.zeros((N, M))
        # 时刻数=观测序列数
        T = M
        # (2)对观测序列遍历,遍历每一个时刻,计算前向概率alpha值

        for t in range(T):
            if self.verbose:
                if t == 0:
                    print("前向概率初值:")
                elif t == 1:
                    print("\n从时刻1到T-1观测序列的前向概率:")
            # 得到序列对应的索引
            index_of_o = V.index(O[t])
            # 遍历状态序列
            for i in range(N):
                if t == 0:
                    # (1)初始化alpha初值,书中第198页公式(10.15)
                    alphas[i][t] = PI[t][i] * B[i][index_of_o]
                    if self.verbose:
                        self.print_alpha_t1(alphas, i, t)
                else:
                    # (2)递推,书中第198页公式(10.16)
                    alphas[i][t] = np.dot([alpha[t - 1] for alpha in alphas],
                                          [a[i] for a in A]) * B[i][index_of_o]
                    if self.verbose:
                        self.print_alpha_t(alphas, i, t)
        # (3)终止,书中第198页公式(10.17)
        self.forward_P = np.sum([alpha[M - 1] for alpha in alphas])
        self.alphas = alphas

    @staticmethod
    def print_alpha_t(alphas, i, t):
        print("alpha%d(%d) = [sum alpha%d(i) * ai%d] * b%d(o%d) = %f"
              % (t + 1, i + 1, t - 1, i, i, t, alphas[i][t]))

    @staticmethod
    def print_alpha_t1(alphas, i, t):
        print('alpha1(%d) = pi%d * b%d * b(o1) = %f'
              % (i + 1, i, i, alphas[i][t]))

    def calc_t_qi_prob(self, t, qi):
        result = (self.alphas[qi - 1][t - 1] * self.betas[qi - 1][t - 1]) / self.backward_P[0]
        if self.verbose:
            print("计算P(i%d=q%d|O,lambda):" % (t, qi))
            print("P(i%d=q%d|O,lambda) = alpha%d(%d) * beta%d(%d) / P(O|lambda) = %f"
                  % (t, qi, t, qi, t, qi, result))

        return result
python
Q = [1, 2, 3]
V = ['红', '白']
A = [[0.5, 0.1, 0.4], [0.3, 0.5, 0.2], [0.2, 0.2, 0.6]]
B = [[0.5, 0.5], [0.4, 0.6], [0.7, 0.3]]
O = ['红', '白', '红', '红', '白', '红', '白', '白']
PI = [[0.2, 0.3, 0.5]]

hmm_forward_backward = HiddenMarkovForwardBackward(verbose=True)
hmm_forward_backward.forward(Q, V, A, B, O, PI)
print()
hmm_forward_backward.backward(Q, V, A, B, O, PI)
print()
hmm_forward_backward.calc_t_qi_prob(t=4, qi=3)
print()
前向概率初值:
alpha1(1) = pi0 * b0 * b(o1) = 0.100000
alpha1(2) = pi1 * b1 * b(o1) = 0.120000
alpha1(3) = pi2 * b2 * b(o1) = 0.350000

从时刻1到T-1观测序列的前向概率:
alpha2(1) = [sum alpha0(i) * ai0] * b0(o1) = 0.078000
alpha2(2) = [sum alpha0(i) * ai1] * b1(o1) = 0.084000
alpha2(3) = [sum alpha0(i) * ai2] * b2(o1) = 0.082200
alpha3(1) = [sum alpha1(i) * ai0] * b0(o2) = 0.040320
alpha3(2) = [sum alpha1(i) * ai1] * b1(o2) = 0.026496
alpha3(3) = [sum alpha1(i) * ai2] * b2(o2) = 0.068124
alpha4(1) = [sum alpha2(i) * ai0] * b0(o3) = 0.020867
alpha4(2) = [sum alpha2(i) * ai1] * b1(o3) = 0.012362
alpha4(3) = [sum alpha2(i) * ai2] * b2(o3) = 0.043611
alpha5(1) = [sum alpha3(i) * ai0] * b0(o4) = 0.011432
alpha5(2) = [sum alpha3(i) * ai1] * b1(o4) = 0.010194
alpha5(3) = [sum alpha3(i) * ai2] * b2(o4) = 0.011096
alpha6(1) = [sum alpha4(i) * ai0] * b0(o5) = 0.005497
alpha6(2) = [sum alpha4(i) * ai1] * b1(o5) = 0.003384
alpha6(3) = [sum alpha4(i) * ai2] * b2(o5) = 0.009288
alpha7(1) = [sum alpha5(i) * ai0] * b0(o6) = 0.002811
alpha7(2) = [sum alpha5(i) * ai1] * b1(o6) = 0.002460
alpha7(3) = [sum alpha5(i) * ai2] * b2(o6) = 0.002535
alpha8(1) = [sum alpha6(i) * ai0] * b0(o7) = 0.001325
alpha8(2) = [sum alpha6(i) * ai1] * b1(o7) = 0.001211
alpha8(3) = [sum alpha6(i) * ai2] * b2(o7) = 0.000941

初始化后向概率:
beta8(1) = 1
beta8(2) = 1
beta8(3) = 1

从时刻T-1到1观测序列的后向概率:
beta7(1) = sum[a1j * bj(o8) * beta8(j)] = (0.50 * 0.50 * 1.00 + 0.10 * 0.60 * 1.00 + 0.40 * 0.30 * 1.00 + 0) = 0.430
beta7(2) = sum[a2j * bj(o8) * beta8(j)] = (0.30 * 0.50 * 1.00 + 0.50 * 0.60 * 1.00 + 0.20 * 0.30 * 1.00 + 0) = 0.510
beta7(3) = sum[a3j * bj(o8) * beta8(j)] = (0.20 * 0.50 * 1.00 + 0.20 * 0.60 * 1.00 + 0.60 * 0.30 * 1.00 + 0) = 0.400
beta6(1) = sum[a1j * bj(o7) * beta7(j)] = (0.50 * 0.50 * 0.43 + 0.10 * 0.60 * 0.51 + 0.40 * 0.30 * 0.40 + 0) = 0.186
beta6(2) = sum[a2j * bj(o7) * beta7(j)] = (0.30 * 0.50 * 0.43 + 0.50 * 0.60 * 0.51 + 0.20 * 0.30 * 0.40 + 0) = 0.241
beta6(3) = sum[a3j * bj(o7) * beta7(j)] = (0.20 * 0.50 * 0.43 + 0.20 * 0.60 * 0.51 + 0.60 * 0.30 * 0.40 + 0) = 0.176
beta5(1) = sum[a1j * bj(o6) * beta6(j)] = (0.50 * 0.50 * 0.19 + 0.10 * 0.40 * 0.24 + 0.40 * 0.70 * 0.18 + 0) = 0.106
beta5(2) = sum[a2j * bj(o6) * beta6(j)] = (0.30 * 0.50 * 0.19 + 0.50 * 0.40 * 0.24 + 0.20 * 0.70 * 0.18 + 0) = 0.101
beta5(3) = sum[a3j * bj(o6) * beta6(j)] = (0.20 * 0.50 * 0.19 + 0.20 * 0.40 * 0.24 + 0.60 * 0.70 * 0.18 + 0) = 0.112
beta4(1) = sum[a1j * bj(o5) * beta5(j)] = (0.50 * 0.50 * 0.11 + 0.10 * 0.60 * 0.10 + 0.40 * 0.30 * 0.11 + 0) = 0.046
beta4(2) = sum[a2j * bj(o5) * beta5(j)] = (0.30 * 0.50 * 0.11 + 0.50 * 0.60 * 0.10 + 0.20 * 0.30 * 0.11 + 0) = 0.053
beta4(3) = sum[a3j * bj(o5) * beta5(j)] = (0.20 * 0.50 * 0.11 + 0.20 * 0.60 * 0.10 + 0.60 * 0.30 * 0.11 + 0) = 0.043
beta3(1) = sum[a1j * bj(o4) * beta4(j)] = (0.50 * 0.50 * 0.05 + 0.10 * 0.40 * 0.05 + 0.40 * 0.70 * 0.04 + 0) = 0.026
beta3(2) = sum[a2j * bj(o4) * beta4(j)] = (0.30 * 0.50 * 0.05 + 0.50 * 0.40 * 0.05 + 0.20 * 0.70 * 0.04 + 0) = 0.023
beta3(3) = sum[a3j * bj(o4) * beta4(j)] = (0.20 * 0.50 * 0.05 + 0.20 * 0.40 * 0.05 + 0.60 * 0.70 * 0.04 + 0) = 0.027
beta2(1) = sum[a1j * bj(o3) * beta3(j)] = (0.50 * 0.50 * 0.03 + 0.10 * 0.40 * 0.02 + 0.40 * 0.70 * 0.03 + 0) = 0.015
beta2(2) = sum[a2j * bj(o3) * beta3(j)] = (0.30 * 0.50 * 0.03 + 0.50 * 0.40 * 0.02 + 0.20 * 0.70 * 0.03 + 0) = 0.012
beta2(3) = sum[a3j * bj(o3) * beta3(j)] = (0.20 * 0.50 * 0.03 + 0.20 * 0.40 * 0.02 + 0.60 * 0.70 * 0.03 + 0) = 0.016
beta1(1) = sum[a1j * bj(o2) * beta2(j)] = (0.50 * 0.50 * 0.01 + 0.10 * 0.60 * 0.01 + 0.40 * 0.30 * 0.02 + 0) = 0.006
beta1(2) = sum[a2j * bj(o2) * beta2(j)] = (0.30 * 0.50 * 0.01 + 0.50 * 0.60 * 0.01 + 0.20 * 0.30 * 0.02 + 0) = 0.007
beta1(3) = sum[a3j * bj(o2) * beta2(j)] = (0.20 * 0.50 * 0.01 + 0.20 * 0.60 * 0.01 + 0.60 * 0.30 * 0.02 + 0) = 0.006

观测序列概率:
P(O|lambda) = 0.2 * 0.5 * 0.00633 + 0.3 * 0.4 * 0.00685 + 0.5 * 0.7 * 0.00578 + 0 = 0.003477

计算P(i4=q3|O,lambda):
P(i4=q3|O,lambda) = alpha4(3) * beta4(3) / P(O|lambda) = 0.536952

可知,P(i4=q3|O,λ)=P(i4=q3,O|λ)P(O|λ)=α4(3)β4(3)P(O|λ)=0.536952

习题10.3

  在习题10.1中,试用维特比算法求最优路径I=(i1,i2,i3,i4)

解答:

解答思路:

  1. 列出维特比算法
  2. 自编程实现维特比算法,并求最优路径

解答步骤:

第1步:维特比算法

  根据书中第10章的算法10.5的维特比算法:

算法10.5(维特比算法)
输入:模型λ=(A,B,π)和观测O=(o1,o2,,oT)
输出:最优路径I=(i1,i2,,iT)
(1)初始化

δ1(i)=πibi(o1),i=1,2,,NΨ1(i)=0,i=1,2,,N

(2)递推。对t=2,3,,T

δt(i)=max1jN[δt1(j)aji]bi(ot),i=1,2,,NΨt(i)=argmax1jN[δt1(j)aji],i=1,2,,N

(3)终止

P=max1iNδT(i)iT=argmax1iN[δT(i)]

(4)最优路径回溯。对t=T1,T2,,1

it=Ψt+1(it+1)

求得最优路径I=(i1,i2,,iT)

第2步:自编程实现维特比算法

python
import numpy as np


class HiddenMarkovViterbi:
    def __init__(self, verbose=False):
        self.verbose = verbose

    def viterbi(self, Q, V, A, B, O, PI):
        """
        维特比算法
        :param Q: 所有可能的状态集合
        :param V: 所有可能的观测集合
        :param A: 状态转移概率矩阵
        :param B: 观测概率矩阵
        :param O: 观测序列
        :param PI: 初始状态概率向量
        """
        # 状态序列的大小
        N = len(Q)
        # 观测序列的大小
        M = len(O)
        # 初始化deltas
        deltas = np.zeros((N, M))
        # 初始化psis
        psis = np.zeros((N, M))

        # 初始化最优路径矩阵,该矩阵维度与观测序列维度相同
        I = np.zeros((1, M))
        # (2)递推,遍历观测序列
        for t in range(M):
            if self.verbose:
                if t == 0:
                    print("初始化Psi1和delta1:")
                elif t == 1:
                    print("\n从时刻2到T的所有单个路径中概率"
                          "最大值delta和概率最大的路径的第t-1个结点Psi:")

            # (2)递推从t=2开始
            real_t = t + 1
            # 得到序列对应的索引
            index_of_o = V.index(O[t])
            for i in range(N):
                real_i = i + 1
                if t == 0:
                    # (1)初始化
                    deltas[i][t] = PI[0][i] * B[i][index_of_o]
                    psis[i][t] = 0

                    self.print_delta_t1(
                        B, PI, deltas, i, index_of_o, real_i, t)
                    self.print_psi_t1(real_i)
                else:
                    # (2)递推,对t=2,3,...,T
                    deltas[i][t] = np.max(np.multiply([delta[t - 1] for delta in deltas],
                                                      [a[i] for a in A])) * B[i][index_of_o]
                    self.print_delta_t(
                        A, B, deltas, i, index_of_o, real_i, real_t, t)

                    psis[i][t] = np.argmax(np.multiply([delta[t - 1] for delta in deltas],
                                                       [a[i] for a in A]))
                    self.print_psi_t(i, psis, real_i, real_t, t)

        last_deltas = [delta[M - 1] for delta in deltas]
        # (3)终止,得到所有路径的终结点最大的概率值
        P = np.max(last_deltas)
        # (3)得到最优路径的终结点
        I[0][M - 1] = np.argmax(last_deltas)
        if self.verbose:
            print("\n所有路径的终结点最大的概率值:")
            print("P = %f" % P)
        if self.verbose:
            print("\n最优路径的终结点:")
            print("i%d = argmax[deltaT(i)] = %d" % (M, I[0][M - 1] + 1))
            print("\n最优路径的其他结点:")

        # (4)递归由后向前得到其他结点
        for t in range(M - 2, -1, -1):
            I[0][t] = psis[int(I[0][t + 1])][t + 1]
            if self.verbose:
                print("i%d = Psi%d(i%d) = %d" %
                      (t + 1, t + 2, t + 2, I[0][t] + 1))

        # 输出最优路径
        print("\n最优路径是:", "->".join([str(int(i + 1)) for i in I[0]]))

    def print_psi_t(self, i, psis, real_i, real_t, t):
        if self.verbose:
            print("Psi%d(%d) = argmax[delta%d(j) * aj%d] = %d"
                  % (real_t, real_i, real_t - 1, real_i, psis[i][t]))

    def print_delta_t(self, A, B, deltas, i, index_of_o, real_i, real_t, t):
        if self.verbose:
            print("delta%d(%d) = max[delta%d(j) * aj%d] * b%d(o%d) = %.2f * %.2f = %.5f"
                  % (real_t, real_i, real_t - 1, real_i, real_i, real_t,
                     np.max(np.multiply([delta[t - 1] for delta in deltas],
                                        [a[i] for a in A])),
                     B[i][index_of_o], deltas[i][t]))

    def print_psi_t1(self, real_i):
        if self.verbose:
            print("Psi1(%d) = 0" % real_i)

    def print_delta_t1(self, B, PI, deltas, i, index_of_o, real_i, t):
        if self.verbose:
            print("delta1(%d) = pi%d * b%d(o1) = %.2f * %.2f = %.2f"
                  % (real_i, real_i, real_i, PI[0][i], B[i][index_of_o], deltas[i][t]))
python
Q = [1, 2, 3]
V = ['红', '白']
A = [[0.5, 0.2, 0.3], [0.3, 0.5, 0.2], [0.2, 0.3, 0.5]]
B = [[0.5, 0.5], [0.4, 0.6], [0.7, 0.3]]
O = ['红', '白', '红', '白']
PI = [[0.2, 0.4, 0.4]]

HMM = HiddenMarkovViterbi(verbose=True)
HMM.viterbi(Q, V, A, B, O, PI)
初始化Psi1和delta1:
delta1(1) = pi1 * b1(o1) = 0.20 * 0.50 = 0.10
Psi1(1) = 0
delta1(2) = pi2 * b2(o1) = 0.40 * 0.40 = 0.16
Psi1(2) = 0
delta1(3) = pi3 * b3(o1) = 0.40 * 0.70 = 0.28
Psi1(3) = 0

从时刻2到T的所有单个路径中概率最大值delta和概率最大的路径的第t-1个结点Psi:
delta2(1) = max[delta1(j) * aj1] * b1(o2) = 0.06 * 0.50 = 0.02800
Psi2(1) = argmax[delta1(j) * aj1] = 2
delta2(2) = max[delta1(j) * aj2] * b2(o2) = 0.08 * 0.60 = 0.05040
Psi2(2) = argmax[delta1(j) * aj2] = 2
delta2(3) = max[delta1(j) * aj3] * b3(o2) = 0.14 * 0.30 = 0.04200
Psi2(3) = argmax[delta1(j) * aj3] = 2
delta3(1) = max[delta2(j) * aj1] * b1(o3) = 0.02 * 0.50 = 0.00756
Psi3(1) = argmax[delta2(j) * aj1] = 1
delta3(2) = max[delta2(j) * aj2] * b2(o3) = 0.03 * 0.40 = 0.01008
Psi3(2) = argmax[delta2(j) * aj2] = 1
delta3(3) = max[delta2(j) * aj3] * b3(o3) = 0.02 * 0.70 = 0.01470
Psi3(3) = argmax[delta2(j) * aj3] = 2
delta4(1) = max[delta3(j) * aj1] * b1(o4) = 0.00 * 0.50 = 0.00189
Psi4(1) = argmax[delta3(j) * aj1] = 0
delta4(2) = max[delta3(j) * aj2] * b2(o4) = 0.01 * 0.60 = 0.00302
Psi4(2) = argmax[delta3(j) * aj2] = 1
delta4(3) = max[delta3(j) * aj3] * b3(o4) = 0.01 * 0.30 = 0.00220
Psi4(3) = argmax[delta3(j) * aj3] = 2

所有路径的终结点最大的概率值:
P = 0.003024

最优路径的终结点:
i4 = argmax[deltaT(i)] = 2

最优路径的其他结点:
i3 = Psi4(i4) = 2
i2 = Psi3(i3) = 2
i1 = Psi2(i2) = 3

最优路径是: 3->2->2->2

所以,最优路径I=(i1,i2,i3,i4)=(3,2,2,2)

习题10.4

  试用前向概率和后向概率推导$$P(O|\lambda)=\sum_{i=1}^N\sum_{j=1}^N\alpha_t(i)a_{ij}b_j(o_{t+1})\beta_{t+1}(j),\quad t=1,2,\cdots,T-1$$

解答:

解答思路:

  1. P(O|λ)按照定义展开,即P(O|λ)=P(o1,o2,...,oT|λ)
  2. 假设在时刻t状态为qi的条件下,将概率拆分为两个条件概率(前向概率、后向概率)
  3. 在后向概率中,假设在时刻t+1状态为qj,继续拆分为两个条件概率(t+1时刻的概率和t+2T的概率)
  4. t+1时刻的概率拆分为t+1时刻的观测概率和状态转移概率
  5. 按照前向概率和后向概率定义,使用α,β,a,b来表示

解答步骤:

第1步:P(O|λ)展开推导

P(O|λ)=P(o1,o2,...,oT|λ)=i=1NP(o1,..,ot,it=qi|λ)P(ot+1,..,oT|it=qi,λ)=i=1Nj=1NP(o1,..,ot,it=qi|λ)P(ot+1,it+1=qj|it=qi,λ)P(ot+2,..,oT|it+1=qj,λ)=i=1Nj=1N[P(o1,..,ot,it=qi|λ)P(ot+1|it+1=qj,λ)P(it+1=qj|it=qi,λ)P(ot+2,..,oT|it+1=qj,λ)]

第2步:前向概率和后向概率

根据书中第10.2.2节的前向概率:

定义10.2(前向概率) 给定隐马尔可夫模型λ,定义到时刻t部分观测序列为o1,o2,,ot且状态为qi的概率为前向概率,记作

αt(i)=P(o1,o2,,ot,it=qi|λ)

可以递推地求得前向概率αt(i)及观测序列概率P(O|λ)

根据书中第10.2.3节的后向概率:

定义10.3(后向概率) 给定隐马尔可夫模型λ,定义在时刻t状态为qi的条件下,从t+1T的部分观测序列为ot+1,ot+2,,oT的后向概率,记作

βt(i)=P(ot+1,ot+2,,oT||it=qi,λ)

可以用递推的方法求得后向概率βt(i)及观测序列概率P(O|λ)

第3步:用α,β,a,b表示

根据书中第10章的状态转移概率矩阵公式(10.2)和观测概率矩阵公式(10.4)的定义:

aij=P(it+1=qj|it=qi),i=1,2,,N;j=1,2,,Nbj(k)=P(ot=vk|it=qj),k=1,2,,M;j=1,2,,N

P(O|λ)=i=1Nj=1N[P(o1,..,ot,it=qi|λ)P(ot+1|it+1=qj,λ)P(it+1=qj|it=qi,λ)P(ot+2,..,oT|it+1=qj,λ)]=i=1Nj=1Nαt(i)aijbj(ot+1)βt+1(j),t=1,2,...,T1

命题得证。

习题10.5

  比较维特比算法中变量δ的计算和前向算法中变量α的计算的主要区别。

解答:

解答思路:

  1. 列出维特比算法中变量δ的计算;
  2. 列出前向算法中变量α的计算;
  3. 比较两个变量计算的主要区别。

解答步骤:

第1步:维特比算法中变量δ的计算

  根据书中第10.4.2节的算法10.5的维特比算法:

(1)初始化

δ1(i)=πibi(o1),i=1,2,,N

(2)递推,对t=2,3,,T

δt(i)=max1jN[δt1(j)aji]bi(ot),i=1,2,,N

第2步:前向算法中变量α的计算

  根据书中第10.2.2节的算法10.2的观测序列概率的前向算法:

(1)初值

α1(i)=πibi(oi),i=1,2,,N

(2)递推,对t=1,2,,T1

αt+1(i)=[j=1Nαt(j)aji]bi(ot+1)i=1,2,,N

第3步:比较两个变量计算的主要区别

通过比较两个变量的计算,主要区别包括计算目标不同和关注对象不同两个方面:

  1. 计算目标不同
  • 前向算法:计算长度为t的观测序列概率,以某一时刻某一状态为最终状态的概率;
  • 维特比算法:计算长度为t的观测序列概率,选择当前观测序列的最大可能概率;
  1. 关注对象不同
  • 前向算法:包含到t1时刻所有可能的观测序列概率和,其中不变的是观测序列上的最终状态;
  • 维特比算法:包含出现观测序列中所有t个时刻相应观测的最大概率,其中不变的是整条观测序列。