HMM
隐马尔可夫模型
HMM模型是**概率图模型的一种统计模型
HMM模型描述的就是由这些 隐状态序列(实体标记) **生成 可观测状态(可读文本)的过程。从可观察的参数中确定该过程的隐含参数
两个基本假设
-
马尔可夫假设
当前的隐藏状态只和前一隐藏状态有关
-
观测独立性假设
某个观测状态只和生成他的隐藏状态有关
HMM 模型的参数
-
转移概率矩阵 A A A
描述系统从一个状态转移到另一个状态的概率。这些概率构成了状态之间的转移矩阵。
例如:
-
发射概率矩阵 B B B (由隐藏状态生成观测状态的矩阵)
描述系统从特定状态生成观察符号的概率。这些概率构成了状态到观察的发射矩阵。
例如:
-
初始隐藏状态概率 π \pi π
描述系统在时间序列开始时处于各个状态的概率。
一个 HMM模型可以表示为 λ = ( π , A , B ) \lambda = (\pi,A,B) λ=(π,A,B)
-
状态集(隐藏的,不可观测)
这是模型中的一组离散的隐藏状态,代表系统在不同时刻的内部状态。在自然语言处理任务中,状态集可能表示词性。
-
观测集(可观测的)
这是在每个时刻模型可以观察到的符号,也就是可见的输出。在自然语言处理中,观测符号可能是单词。
关于观测状态和隐藏状态的举例:
海藻的状态有四种,分别是Dry(干燥的)、Dryish(稍干的)、Damp(潮湿的)、Soggy(湿漉漉的)。海藻的状态是可观测的,那它就是 观测状态,天气信息看不到就是 隐藏状态。
HMM 的三类问题
-
概率计算问题:给定模型 λ = ( π , A , B ) \lambda=(\pi,A,B) λ=(π,A,B) 和观测序列 O O O 的情况下,求在模型 λ \lambda λ 下观测序列 O = { o 1 , o 2 , … , o T } O = \{o_1,o_2,\dots,o_T\} O={o1,o2,…,oT} 出现的概率 P ( O ∣ λ ) P(O|λ) P(O∣λ)。(Forward-Backward算法、直接计算法)
例如:计算𝑃(𝑥 =< You hate green bananas > | 𝐴, 𝐵),即给定 HMM 的参数,计算可观测序列 的概率
-
解码问题:给定模型 λ = ( π , A , B ) \lambda=(\pi,A,B) λ=(π,A,B) 和观测序列 O = { o 1 , o 2 , … , o T } O = \{o_1,o_2,\dots,o_T\} O={o1,o2,…,oT} 的情况下,求对给定观测序列 P ( I ∣ O ) P(I | O) P(I∣O) 最大的状态序列 I I I 。(Viterbi算法)
例如:输出句子𝑥 =< You hate green bananas >的解码结果,即给定 HMM 的参数和可观测序列,计算 tag 序列。
-
学习问题:观测序列 O = { o 1 , o 2 , … , o T } O = \{o_1,o_2,\dots,o_T\} O={o1,o2,…,oT} 已知的情况下,求解模型 λ = ( π , A , B ) \lambda=(\pi,A,B) λ=(π,A,B) 参数,使得在该模型下观测序列概率 P ( O ∣ λ ) P(O|λ) P(O∣λ) 最大。(极大似然估计算法)
概率计算问题 - 直接计算法
P ( I ∣ λ ) = π i 1 a i 1 i 2 a i 2 i 3 ⋯ a i T − 1 i T P(I \mid \lambda) =\pi_{i_1} a_{i_1 i_2} a_{i_2 i_3} \cdots a_{i_{T-1} i_T} P(I∣λ)=πi1ai1i2ai2i3⋯aiT−1iT
P ( O ∣ I , λ ) = b i 1 ( o 1 ) b i 2 ( o 2 ) ⋯ b i T ( o T ) P(O \mid I, \lambda) =b_{i_1}\left(o_1\right) b_{i_2}\left(o_2\right) \cdots b_{i_T}\left(o_T\right) P(O∣I,λ)=bi1(o1)bi2(o2)⋯biT(oT)
P ( O , I ∣ λ ) = P ( O ∣ I , λ ) P ( I ∣ λ ) = π i 1 b i 1 ( o 1 ) a i 1 i 2 b i 2 ( o 2 ) ⋯ a i T − 1 i T b i T ( o T ) P(O, I \mid \lambda) =P(O \mid I, \lambda) P(I \mid \lambda) =\pi_{i_1} b_{i_1}\left(o_1\right) a_{i_1 i_2} b_{i_2}\left(o_2\right) \cdots a_{i_{T-1} i_T} b_{i_T}\left(o_T\right) P(O,I∣λ)=P(O∣I,λ)P(I∣λ)=πi1bi1(o1)ai1i2bi2(o2)⋯aiT−1iTbiT(oT)
P ( O ∣ λ ) = ∑ I P ( O ∣ I , λ ) P ( I ∣ λ ) = ∑ i 1 , i 2 , ⋯ , i T π i 1 b i 1 ( o 1 ) a i 1 i 2 b i 2 ( o 2 ) ⋯ a i T − 1 i T b i T ( o T ) P(O \mid \lambda) =\sum_I P(O \mid I, \lambda) P(I \mid \lambda) =\sum_{i_1, i_2, \cdots, i_T} \pi_{i_1} b_{i_1}\left(o_1\right) a_{i_1 i_2} b_{i_2}\left(o_2\right) \cdots a_{i_{T-1} i_T} b_{i_T}\left(o_T\right) P(O∣λ)=I∑P(O∣I,λ)P(I∣λ)=i1,i2,⋯,iT∑πi1bi1(o1)ai1i2bi2(o2)⋯aiT−1iTbiT(oT)
概率计算问题 - 前向算法
前向算法:
假设我们有一个HMM模型,包括初始状态概率 $ \pi $,状态转移概率矩阵 $ A $,观测概率矩阵 $ B $。观测序列为 $ O = O_1, O_2, \ldots, O_T $。
-
初始化:
- 计算初始时刻的前向概率: α 1 ( j ) = π j ⋅ B j ( O 1 ) \alpha_1(j) = \pi_j \cdot B_j(O_1) α1(j)=πj⋅Bj(O1) for each state j j j.
-
递推计算:
- 对于每个时刻
t
=
2
t = 2
t=2 到
T
T
T:
$ \alpha_t(j) = \left( \sum_{i} \alpha_{t-1}(i) \cdot A_{ij} \right) \cdot B_j(O_t) $
这里, α t ( j ) \alpha_t(j) αt(j) 表示在时刻 t t t 处于状态 j j j 且观测到序列 O t O_t Ot 的前向概率。
- 对于每个时刻
t
=
2
t = 2
t=2 到
T
T
T:
-
观测序列的联合概率:
- 最终的观测序列的联合概率是在时刻
T
T
T 处于所有可能状态的前向概率之和:
$ P(O) = \sum_{i} \alpha_T(i) $
- 最终的观测序列的联合概率是在时刻
T
T
T 处于所有可能状态的前向概率之和:
-
代码实现:
double forwardAlgorithm() { int T = num_observations; // 初始化前向概率矩阵 double alpha[T + 1][num_states]; // 初始化步骤 for (int j = 0; j < num_states; ++j) { alpha[1][j] = initial_prob[j] * emission_prob[j][observations[0]]; } // 递推步骤 for (int t = 2; t <= T; ++t) { for (int j = 0; j < num_states; ++j) { double sum = 0.0; for (int i = 0; i < num_states; ++i) { sum += alpha[t - 1][i] * transition_prob[i][j]; } alpha[t][j] = sum * emission_prob[j][observations[t - 1]]; } } // 终止步骤 double joint_probability = 0.0; for (int i = 0; i < num_states; ++i) { joint_probability += alpha[T][i]; } return joint_probability; }
解码问题 - Viterbi 算法
Viterbi 算法解决一个什么问题?
寻找有向无环图(篱笆网络Lattice)中两个点之间的最短路径。
在 HMM 中解决解码问题: 给定一个观测序列 O O O,求一个状态序列 I I I 使得 P ( O , I ) P(O,I) P(O,I) 最大.
代码实现:
// Viterbi 算法
void viterbi_algorithm()
{
int T = sizeof(observations) / sizeof(observations[0]);
double dp[T][num_states];
int backpointer[T][num_states];
// 初始化
for (int i = 0; i < num_states; ++i)
{
dp[0][i] = initial_prob[i] * emission_prob[i][observations[0]];
}
// 递推
for (int t = 1; t < T; ++t)
{
for (int j = 0; j < num_states; ++j)
{
double max_prob = -1.0;
int argmax = -1;
for (int i = 0; i < num_states; ++i)
{
double prob = dp[t - 1][i] * transition_prob[i][j] * emission_prob[j][observations[t]];
if (prob > max_prob)
{
max_prob = prob;
argmax = i;
}
}
dp[t][j] = max_prob;
backpointer[t][j] = argmax;
}
}
// 回溯
double max_prob = -1.0;
int argmax = -1;
for (int i = 0; i < num_states; ++i)
{
if (dp[T - 1][i] > max_prob)
{
max_prob = dp[T - 1][i];
argmax = i;
}
}
// 构造最优路径
int best_path[T];
best_path[T - 1] = argmax;
for (int t = T - 2; t >= 0; --t)
{
best_path[t] = backpointer[t + 1][best_path[t + 1]];
}