DL00338-使用序列到序列深度学习方法自动睡眠阶段评分 深度学习方法,用于使用单通道脑电图进行自动睡眠阶段评分。

睡眠阶段评分这事吧,传统方法费时费力还容易出错。睡眠技师盯着脑电图波形一个个30秒片段分类,跟玩大家来找茬似的。现在单通道EEG设备越来越普及,正好适合用序列到序列模型搞点事情——毕竟睡眠阶段本来就是时间序列问题。

咱们先看数据长啥样。一个睡眠epoch通常30秒长度,采样率128Hz的话就是3840个点。处理时建议切成小窗口:

def sliding_window(eeg, window_size=128, stride=64):
    return np.lib.stride_tricks.sliding_window_view(eeg, window_size)[::stride]

这操作相当于把长序列切成多个小时间窗,既能保留局部特征又减少计算量。注意窗口重叠别太大,否则容易过拟合。

模型结构上我试过Transformer效果不错,但LSTM方案更省资源。核心是个双向LSTM编码器:

class SleepLSTM(nn.Module):
    def __init__(self, input_dim=128, hidden_dim=64):
        super().__init__()
        self.encoder = nn.LSTM(input_dim, hidden_dim, bidirectional=True)
        self.decoder = nn.LSTM(hidden_dim*2, 5)  # 5个睡眠阶段
        
    def forward(self, x):
        enc_out, (h_n, c_n) = self.encoder(x)
        output, _ = self.decoder(enc_out)
        return output

这里有个小技巧:解码器直接复用编码器的全部输出,而不是传统seq2seq的逐步解码。毕竟睡眠阶段预测需要全局上下文,实时性要求不高。

DL00338-使用序列到序列深度学习方法自动睡眠阶段评分 深度学习方法,用于使用单通道脑电图进行自动睡眠阶段评分。

训练时别用常规交叉熵损失,试试带时序平滑的版本:

class SmoothLoss(nn.Module):
    def __init__(self, alpha=0.2):
        super().__init__()
        self.ce = nn.CrossEntropyLoss()
        self.alpha = alpha  # 相邻时段标签变化惩罚系数
        
    def forward(self, preds, labels):
        base_loss = self.ce(preds, labels)
        # 计算相邻预测差异
        shift_loss = torch.mean(torch.abs(preds[1:] - preds[:-1])) 
        return base_loss + self.alpha * shift_loss

这招有效减少阶段跳变,毕竟正常人不会1秒从深睡切到清醒。实测能使N1阶段的F1分数提升5%左右。

部署时要注意模型轻量化。用TorchScript导出后,树莓派4上能跑到实时处理的水平。不过遇到肌电干扰大的数据还是会翻车,这时候加个简单的噪声检测模块:

def is_noise(signal, threshold=50):
    diff = np.abs(np.diff(signal))
    return np.percentile(diff, 95) > threshold

虽然简单粗暴,但能过滤掉80%以上的体动伪迹。

最后说点坑:别迷信公开数据集的表现,实际应用时设备差异能让你掉10个点准确率。建议拿到真实数据后做一轮domain adaptation,哪怕只是调整BN层的running stats都好使。

更多推荐