用 HMM 维特比平滑纠正睡眠分期预测:从原理到代码实现
1. 背景:生理规律造成模型预测的噪声
在最近的睡眠分期研究中,我基于单通道 Fpz-Cz 脑电信号构建了分类模型。为了让模型尽可能抓取到丰富的特征,我引入了多尺度并行架构——有的分支看宏观波形,有的分支抓微观节律。
模型训练完后,准确率表现尚可。但是根据预测结果的睡眠hypnogram(睡眠图)与真实标签对比后,我发现模型在预测时容易造成不合理的高频跳变。
这就引出了纯深度学习模型在序列预测中的两个致命弱点:
- 抖动效应:缺乏时间维度的平滑,分类结果在相邻帧之间剧烈跳变。
- 违背生理常识:在睡眠医学中,状态转换是有严格顺序的(比如从清醒W到深度N3,必须经过N1、N2,不可能“瞬间平移”)。但深度学习的全连接层或Softmax是不懂医学常识的。
因此,模型只管“当前这30秒像什么”,不管“前后30秒合不合理”
2. 理论思路:隐马尔可夫模型(HMM)与维特比算法
为了解决上述问题,我引入了 HMM(隐马尔可夫模型)维特比平滑器。
我们把睡眠分期看作两个过程:
- 观测过程:模型输出的每个epoch属于各个阶段的概率(比如模型判断这一帧有80%概率是N3)。这称为发射概率。
- 隐状态过程(真实的):人体真实的睡眠阶段转换规律。这称为转移概率。
我们要做的,就是用转移概率去约束发射概率,寻找一条全局最合理的路径。
理论简述:维特比算法
维特比算法是一种动态规划,用于在 HMM 中求解给定观测序列下的最大概率状态序列。
设共有 个状态(睡眠阶段数,通常 5),序列长度 。
- 定义 为到时刻 且状态为 的所有路径中的最大概率(对数域使用加法)。
- 递推公式(对数域):
- 同时记录回溯指针 。
最终从 开始回溯,得到最优状态序列。
为什么不用简单的“中值滤波”?
很多人平滑序列喜欢用中值滤波(比如连续3帧取众数)。但中值滤波是“暴力抹平”,它无法利用睡眠特定的生理先验知识(比如 N2 到 REM 的概率远大于 W 到 N3 的概率)。
3. 工程实现:两步走的代码逻辑
HMM平滑的实现分为绝对清晰的两步:先“学规矩”,再“纠错误”。
阶段一:从真实标签中学习人类睡眠规律
这一步在训练集的真实标签上运行,目的是构建一个 5x5 的转移矩阵(根据美国睡眠医学会提出的 睡眠及相关事件判读手册:规则、术语与技术规范 ,定义了 W, N1, N2, N3, REM 共5个阶段的标准)。
核心逻辑伪代码:
def fit(self, labels_sequence_list):
# 1. 初始化计数器
trans_counts = np.zeros((5, 5)) # 5个阶段互相转移的次数
initial_counts = np.zeros(n_classes)
# 2. 遍历所有受试者的真实标签序列
for seq in labels_sequence_list:
initial_counts[seq[e]]+= 1
for i in range(len(seq)-1):
trans_counts[seq[i],seq[i+1]]+= 1
# 3. 计算概率(加上拉普拉斯平滑,防止某些未出现的转移变成0)
# 比如:从N2转移到N3的概率 = (N2->N3的次数 + 1) / (N2总共转移出去的次数 + 5)
self.initial_dist = (initial_counts + 1.e) / (total + n_classes)
self.trans_mat = (trans_counts + 1.e) / (row_sums + n_classes)
# 存储对数概率
self.log_initial_dist = np.log(self.initial_dist + 1e-9)
self.log_trans_mat = np.log(self.trans_mat + 1e-9)
经过这一步,模型就知道了一些人类睡眠规律:比如对角线上的概率肯定最大(睡眠具有惯性),而 W 直接到 N3 的概率极小。
阶段二:维特比算法的全局动态规划纠错
当深度学习模型输出了一串概率序列,维特比算法纠错的方法是:看走到当前帧的某状态时,哪一条历史路径的总概率最大”。
核心逻辑伪代码:
# log_probs:模型输出的对数概率序列
def viterbi_predict(self, log_probs):
T1 = log_probs # 记录到达每个状态的最大路径概率
T2 = log_probs # 记录到达每个状态时,上一个状态是什么(回溯用)
# 初始化第0帧:初始概率 + 模型预测概率
T1[0, :] = self.log_initial_dist + log_probs[0, :]
# 递推:从第1帧遍历到最后一帧
for t in range(1, T):
# 当前候选状态 j
for j in range(K):
# 关键点:不只看模型当前预测,还要加上“从上一帧某个状态转移到当前状态的概率”
probs = T1[t-1, :] + self.log_trans_mat[:, j] + log_probs[t, j]
# 找出历史路径中,到达当前状态概率最大的那一条
T2[t, j] = np.argmax(probs)
T1[t, j] = np.max(probs)
# 终局回溯:从最后一帧往回找,抽出那条全局最优路径 best_path
best_path = [0] * np.zeros(T, dtype=int) # 总帧数
best_path[-1] = argmax(T1[-1, :]) # 最后一帧选概率最大的
for t in range(T-2, -1, -1):
最优路径[t] = T2[t+1, best_path[t+1]] # 回找
return best_path
这段代码的解决方案在于:
假设在某一帧,深度学习模型非常笃定地预测为“清醒(W)”,但是如果通过 T2 回溯发现,要走到这个“W”,它的前几帧全是“N3”,而在 对数转移概率矩阵 中,N3 -> W 的对数概率是一个极其可怕的负数(比如 -10)。那么在 候选路径概率 相加时,这个负数会直接拉低总分,导致维特比算法放弃这条路径,强制将这一帧“纠错”为符合生理常识的 N2 或 N3。
4. 实验效果
在我的单通道 Fpz-Cz 实验中,加入 HMM 平滑后,带来了几个显著的收益:
- 指标层面的提高:在完全不改动深度学习主干网络的情况下,整体 Accuracy 提升了约 2%,而 Macro-F1 提升更为明显。因为 HMM 极大地修复了少数类(如 N1)容易被误判为相邻类的边界错误。
Test Accuracy_raw: 0.7825
Test Cohen's Kappa_raw: 0.7059
precision recall f1-score support
W 0.9266 0.8979 0.9120 13886
N1 0.3749 0.5418 0.4432 4644
N2 0.8219 0.8019 0.8118 13543
N3 0.8745 0.7806 0.8249 3036
REM 0.7883 0.6489 0.7118 5548
accuracy 0.7825 40657
macro avg 0.7572 0.7342 0.7407 40657
weighted avg 0.8059 0.7825 0.7912 40657
Test Accuracy_smoothed: 0.8159
Test Cohen's Kappa_smoothed: 0.7483
precision recall f1-score support
W 0.9326 0.9121 0.9222 13886
N1 0.4347 0.4731 0.4531 4644
N2 0.8095 0.8660 0.8368 13543
N3 0.8902 0.7263 0.7999 3036
REM 0.8653 0.7886 0.8252 5548
accuracy 0.8159 40657
macro avg 0.7865 0.7532 0.7674 40657
weighted avg 0.8224 0.8159 0.8178 40657
- Cohen's Kappa 系数显著增加:Kappa 系数不仅看分类对不对,还看重一致性。平滑后的序列与专家标注的序列在“走势”上高度吻合,Kappa 值自然有明显改善。
- 临床可用性:画出来的 Hypnogram 抖动大幅减少了,呈现出人类真实睡眠那种阶梯状、块状分布的特征。
5. 基于HMM后处理的轻量级睡眠分期时序纠错机制解析
架构选型:为何摒弃深度 LSTM/Transformer 转向 HMM?
在引入时序约束时,一种直觉方案是采用端到端的深度时序架构(如 LSTM/Transformer 或深层 GRU 网络)替代独立分类头。然而,这种范式在本研究的特定约束下并非最优解。
计算开销与特征焦点冲突:全局深度时序网络会引入呈指数级增长的参数量与计算复杂度(尤其是 Self-Attention 的 复杂度),破坏模型在边缘设备上部署的潜力。更重要的是,本研究的核心创新点在于多尺度卷积核对频段生理特征(如睡眠纺锤波、K复合波、δ波)的精准解耦。若强行堆叠深层时序模块,模型极易发生特征偏移,将拟合重心转向序列记忆,反而削弱了对微弱生理波形的提取能力。
内生时序建模的平衡:为了在不显著增加计算代价的前提下捕获时序依赖,本研究仅在特征提取层后轻量化地接入了双向门控循环单元与注意力机制,实现内生时序建模,结合自适应特征重校准(AFR)模块,该设计以极低的参数增量完成了对阶段转换概率的初步建模。在包含预处理后约 153 名受试者整晚数据的 SleepEDF-CASTLE 数据集上,该架构仅需 10 轮左右即可达到拟合状态,在消费级单张 RTX4060 显卡上 ,单 epoch 训练时间被压缩在 3 分钟左右。
HMM 的定位:正是由于主干网络已经具备了轻量级的时序感知能力,残留的错误多为零星的“感知盲区”。此时,引入隐马尔可夫模型(HMM)作为后验概率层面的二次解码器,成为最具性价比的方案。HMM 不引入任何可学习参数到反向传播链路中,纯粹利用统计先验进行路径纠偏,实现了计算开销与分类性能的最佳平衡,经过证明,我们的模型相对深度时序建模模型(比如 DeepSleepNet 的 CNN+Bi-LSTM)处于同一先进水平。
缺点剖析:正确预测的误判”
由于 HMM 强依赖于一阶转移概率,它会倾向于惩罚所有小概率的跳变。在真实睡眠中,存在短暂的微觉醒或瞬间状态切换(例如持续的 N2 中突然出现 1-2 个 epoch 的 W)。如果深度学习模型凭借强大的特征提取能力正确识别出了这个短暂的 W,但 HMM 发现 N2 -> W -> N2 的转移概率极低,它就会将原本预测正确的短暂 W 强制抹平为 N2。这种为了局部平滑而牺牲单帧精度的现象是 HMM 的固有缺陷。
6. 临床视角的权衡:为何这种代价是值得的?
在算法评估中,单帧的绝对精准往往不等于临床应用的最优解。HMM 的平滑换来的是两个极具临床价值的收益:
- 难分类别的边界修正(N1 提升显著):N1 期脑电特征微弱,极易与 N2 或 REM 混淆。原始模型往往在 N1/N2 边界产生剧烈抖动,HMM 通过状态转移约束,将部分被误判为 N2 的 N1 序列在时序上拉平回来,提升了 N1 的召回率与 Macro-F1 指标。
- 宏观一致性与 Cohen's Kappa 的跃升:临床医生评估睡眠结构,关注的是整晚睡眠周期的完整性,而非单一 30 秒的判定。HMM 消除伪跳变后,预测出的 Hypnogram 走势与真实标签的宏观拓扑结构高度一致,这直接反映在重度依赖序列一致性权重的 Cohen's Kappa 系数的大幅提升上。
结论
在基于 Fpz-Cz 单通道的轻量级睡眠分期架构中,HMM 维特比平滑提供了一种极低计算成本(零可训练参数)的时序先验注入方案。尽管其存在过度平滑单帧正确预测的理论缺陷,但在全局一致性、难分阶段召回率及临床核心评价指标上,证明了该后处理机制的有效性与必要性。
PS:本篇内容基于本人论文 MSAFR-Net: A Light-weight Multi-scale Adaptive ResNet for Robust Raw Single-channel EEG Sleep Staging 由于论文还在审核阶段,如果您希望获取原码,请等待后续开源或者与我联系