隐马尔可夫模型
观测是看得见的,状态是看不见的。天气看不见但雨伞看得见,情绪看不见但表情看得见,基因的功能区看不见但碱基序列看得见。隐马尔可夫模型把这种"透过表象猜内里"的问题写成了一个可以计算的形式,并且给了三个配套的算法:算似然、找路径、学参数。
本页实验台
LAB 01
两个骰子的故事
庄家偷偷换骰子:只看点数序列,你能看出换在哪儿吗
问题
语音识别里你听到声音、想知道说的是哪个字;基因序列里你看到碱基、想知道哪段是基因;风控里你看到交易、想知道账号有没有被盗。共同的形状是:真正关心的状态看不见,只能看见它产生的观测。
模型
隐马尔可夫模型由三样东西定义:初始分布 π、状态转移 A、发射概率 B。
zt ~ A(zt−1, ·), xt ~ B(zt, ·), P(x,z) = π(z₁) Πt Azt−1zt Bztxt
z 是看不见的状态(用的是公平骰子还是灌铅骰子),x 是看得见的观测(点数)。实际换了多少次骰子—
用灌铅骰子的轮数占比—
公平段里 6 的比例—
灌铅段里 6 的比例—
先把中间那条状态带当作不存在,只看最上面和最下面两条。最下面那条标出了所有的 6。你能看出哪几段"6 特别密"吗?灌铅骰子出 6 的概率是 0.5,公平骰子是 1/6——差了三倍,但在一小段里,随机涨落很容易骗过眼睛。这就是隐马尔可夫模型要解决的全部问题:单看一次观测几乎没有信息,但把连续一段观测放在一起,再加上"状态不会频繁乱跳"这个先验,信息量就足够了。把换骰概率拖大到 30%:状态带变成细碎的花纹,这时候连算法也认不出来了——状态换得越快,每一段能积累的证据越少,可识别性越差。反过来把它拖到 1%,状态带变成几个大块,识别几乎不会出错。再调灌铅程度:从 0.5 降到 0.25 时,两种骰子的差别只剩一点点,再长的序列也很难分开。这两个滑杆分别对应现实里两个关键量:状态的持续性,和观测的区分度。任何一个太差,模型就没得救。
LAB 02
前向算法
动态规划把指数条路径压成一张表
问题
要算"这串点数出现的概率",得把所有可能的状态路径加起来——长度 n 的序列有 2ⁿ 条路径,n=50 就已经超过一千万亿条。这一台展示动态规划怎么把这个指数变成线性。
模型
前向变量 αt(i) 把"前 t 个观测且此刻在状态 i"的概率一次记下来,递推一步一步往前推。
αt(j) = [ Σi αt−1(i) Aij ] · Bj xt, P(x) = Σi αn(i)
复杂度从 O(Kn) 降到 O(nK²),K 是状态数。这是动态规划最干净的一个例子。前向算法给出的对数似然—
暴力枚举所有路径(长度 ≤ 12 才算)—
一共有多少条状态路径—
前向表格有多少个格子—
把序列长度拖到 10 以下,比较前两行读数:它们完全相同。上面那行是前向算法算出来的,下面那行是把 2¹⁰ = 1024 条路径一条条枚举、加起来算出来的。一样的答案,工作量差了两个数量级。把长度拖到 120,路径数是 2¹²⁰ ≈ 1.3×10³⁶——宇宙年龄的秒数也就 10¹⁷ 量级,枚举彻底没戏;而前向表格只有 240 个格子,瞬间算完。动态规划的威力就写在这两个数字的对比里:O(2ⁿ) 变成 O(K²n)。机制其实很简单。要算"前 t 步的所有路径的总概率",你不需要知道每条路径具体怎么走,只需要知道"走到 t 时停在各个状态上的总概率"——因为马尔可夫性保证了:未来只依赖当前状态,不依赖历史。所以指数条路径可以在每一步被压缩成 K 个数字。上面那张 α 热力图就是这 K 个数字随时间的变化:颜色深的那一行,就是模型当前认为更可能的状态。把它和下面的真实状态带对照着看,你会发现 α 的切换总是比真实状态"慢半拍"——因为前向变量只用了到当前为止的信息,还没看到后面的证据。
LAB 03
维特比解码
最可能的那条状态路径,和逐点最优并不是一回事
问题
知道每一时刻"最可能是哪个状态"还不够——把每个时刻各自最优的状态串起来,得到的那条路径可能整体上根本不可能发生(比如要求一步之内换两次状态,而转移矩阵里这一步概率是 0)。
模型
维特比算法把前向递推里的求和换成取最大,并记住来路。
δt(j) = maxi [ δt−1(i) Aij ] · Bj xt, z* = argmaxz P(z | x)
求和给出"每点的边缘最优",取最大给出"整条路径最优"。两者不是一回事,这一台把差别直接摆出来。维特比路径的正确率—
逐点后验的正确率—
两者不一致的位置数—
维特比路径的对数概率—
这两个算法回答的是两个不同的问题,很多人会混。维特比问的是:"哪一整条状态路径的联合概率最大?"逐点后验问的是:"在第 t 个位置上,哪个状态的边缘概率最大?"把每个位置上最可能的状态串起来,得到的并不是最可能的路径。读数里逐点后验的正确率总是略高一点(84.5% 对 81.8%),这不奇怪——它本来就是在逐点意义上最优的。但它有一个致命缺点:拼出来的这条"路径"可能根本走不通,比如中间出现一个孤零零的单点切换,而真实的转移矩阵根本不允许这么跳。维特比给出的一定是一条合法的、连贯的路径。所以选哪个取决于你要的是什么:如果下游要用整条路径(比如语音识别里要解码出一整句话、基因注释里要标出一整段编码区),必须用维特比;如果只关心"这一刻处于哪个状态的概率有多大"(比如风控实时打分),后验概率更合适,而且它还额外给了你一条置信度曲线。下面那条曲线就是它:在状态切换的地方,曲线会从 0 爬到 1,爬坡的宽度就是"模型需要多少个观测才敢确认换了状态"。
LAB 04
Baum–Welch 学参数
只给观测序列,EM 迭代把转移矩阵一点点找回来
问题
前面三台都假设转移矩阵和发射概率是已知的。现实里它们恰恰是未知的——你只有一串观测。能不能只凭观测把参数找回来?能,而且用的是同一套前向-后向递推。
模型
Baum–Welch 是 EM 算法在 HMM 上的特例:用当前参数算状态的后验(E 步),再用后验重新估参数(M 步)。
γt(i) = P(zt=i | x), ξt(i,j) = P(zt=i, zt+1=j | x), Âij = Σt ξt(i,j) ⁄ Σt γt(i)
每一轮似然单调不降,但只保证收敛到局部最优——换个初值可能落到另一个解上。学出来的参数的对数似然—
用真参数算的对数似然—
学到的 P(6) / 真值—
学到的维持概率 / 真值—
前面三个实验台都假装知道转移矩阵和发射概率。现实里你通常什么都不知道,只有一串观测。Baum–Welch(也就是 HMM 上的 EM)解决的正是这件事:先随便猜一组参数,用它算出"每个位置处于各状态的概率"(E 步),再拿这些概率当作软标签重新估参数(M 步),来回迭代。把迭代次数一格一格往上推,看那条似然曲线:它只会上升,从不下降。这是 EM 的基本性质,也是它可靠的原因。n=600 时,似然从 −1048 爬到 −1003,二十次迭代后基本就平了。下面的柱状图显示学到的灌铅骰子:P(6) 学成了 0.496(真值 0.50),维持概率 0.903(真值 0.920)。但请注意那条蓝虚线:学出来的似然 −1002.9 竟然高于用真参数算的 −1010.7。这不是 bug。EM 最大化的是"这组参数解释这一份数据的能力",而不是"找回生成数据的那组参数"。在有限样本下,前者的最优解本来就不等于后者——这正是过拟合最干净的一个例子。把序列长度从 200 拖到 1500,两个数会越靠越近。还有一个坑要知道:EM 只保证收敛到局部最优。换一组初值,可能收敛到一个"两个状态都是公平骰子"的平庸解。实践中的对策是多次随机初始化,取似然最高的那次。
这一页想说的
隐马尔可夫模型的三个算法各解决一个问题:前向算法算"这串观测有多可能"(评估),维特比算"背后最可能是哪条状态路径"(解码),Baum–Welch 算"参数应该是多少"(学习)。三者共用同一个动态规划骨架,把指数级的路径压成 O(K²n)。用得好的前提有两条:状态要足够持续,观测要足够有区分度。这套东西在语音识别、基因注释、用户状态识别和金融状态切换模型里至今都在用。