尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
单通道脑电睡眠分期:从EDF预处理到模型评估的Python实战
简介面向计算机、数学、电子信息类专业课程设计、期末大作业和毕业设计场景这套基于单通道脑电信号的自动睡眠分期研究源码包提供了可直接运行的完整项目。项目使用Sleep-EDF公开数据集SC数据含153条整晚睡眠记录采用Fpz-Cz通道与100Hz采样率从数据下载、预处理到模型训练、测试均有对应脚本。网络结构参考TinySleepNet并扩展了双向RNN、GRU、LSTM、Attention等常见时序分类模块通过参数即可灵活切换训练中定义focal loss测试阶段可输出accuracy、mf1、混淆矩阵等指标非常适合入门神经网络时序建模。资源共22个文件以12个Python源码、2个模型权重文件为主体另有依赖清单、数据下载脚本、网页可视化模板、示例图片和说明文档压缩包仅10.66MB。目前已有475人学习浏览包含预训练模型及完整复现流程便于在此基础上二次开发。1. 单通道脑电睡眠分期一个导联的精度边界在哪里如果只给你一条脑电通道能不能把清醒、浅睡、深睡和REM分清楚多数人直觉上觉得不行但实际在公开数据集上单通道Fpz-Cz已经能拿到80%以上的总体准确率N3和REM的召回率也能到85%左右。睡眠分期的临床金标准是多导睡眠监测但在消费级穿戴设备、睡眠监测头环和家庭远程医疗场景里单通道EEG是性价比最高的选择。用Python做自动睡眠分期意味着从信号读取、预处理、特征提取到模型推理都可以在一个脚本里闭环跑完。下面以这个常见压缩包为线索把数据切窗、特征设计、模型训练和分期结果的校正串起来适合想复现算法、或者打算把分期模型搬到自己的设备上的工程师。2. 数据准备与预处理把EDF原始信号切成30秒训练样本睡眠分期的基础是把连续的脑电信号切成固定长度的片段临床标准是30秒一个epoch。整个流程包含数据集选型、通道选择、滤波重采样、标签对齐和样本生成。这里以Sleep-EDF Expanded公开数据集为例它是睡眠分期研究最常用的benchmark包含197个整夜PSG记录采样率256Hz或100Hz标注采用AASM标准W/N1/N2/N3/REM也兼容旧的RK标准S1-S4。压缩包里的数据文件夹通常就是这个数据集的一个子集或者按同样的EDF格式存放。2.1 用mne读取EDF并只保留单通道EEGmne是Python处理脑电最常用的库底层用pyedflib解析EDF。读取时指定preloadTrue把数据加载到内存后面的resample和filter都必须在此基础上进行。下面是常见的读取和预处理函数import mne import numpy as np def load_eeg_channel(edf_path, ch_nameEEG Fpz-Cz, target_fs100): # 读取整个EDF文件preloadTrue后可以随意切片 raw mne.io.read_raw_edf(edf_path, preloadTrue, verboseERROR) # 只保留需要的单通道减少内存占用和后续计算量 raw.pick_channels([ch_name]) # 统一采样率到100Hz30秒正好3000个采样点 raw.resample(target_fs, npadauto) # 0.5Hz高通滤除基线漂移40Hz低通滤除肌电干扰 raw.filter(0.5, 40, fir_designfirwin, verboseERROR) return raw这段代码有两个值得注意的地方pick_channels必须保证ch_name在edf通道列表里存在可以用raw.ch_names先打印确认resample和filter的先后顺序一般先重采样再做滤波因为低通滤波可以提前去除重采样可能引入的混叠。0.5~40Hz是睡眠EEG分析的常用频段慢波主要集中在0.5~4Hz睡眠纺锤波在11~16Hz40Hz以上基本是肌电伪迹。2.2 把Annotation转成睡眠分期标签EDF文件里的注释记录了每个30秒epoch的分期结果但mne读取后是一个annotation列表起止时间和描述文本需要转换成分类标签。AASM标准下描述通常是Sleep stage W/N1/N2/N3/REM也可能带数字标识。整理成一个映射函数def make_labels(raw, stage_map): annots raw.annotations # 按采样点建立每个epoch的标签数组 total_seconds len(raw) // raw.info[sfreq] n_epochs total_seconds // 30 labels np.empty(n_epochs, dtypeint) for i in range(n_epochs): t0 i * 30 t1 t0 30 # 查注释中覆盖该时段的标签 desc for j, onset in enumerate(annots.onset): if onset annots.duration[j] t0 and onset t1: desc annots.description[j] break labels[i] stage_map.get(desc, -1) # -1表示未知或者伪迹 return labels这里采用一个非常朴素的匹配策略找到起始时间落在当前epoch范围内的第一个注释。实际项目中注释时间和epoch边界可能有几秒偏差更稳妥的做法是取覆盖时长最长的注释。如果出现-1多数公开数据集是movement或unscored习惯上直接丢弃这些样本。2.3 构建训练样本与数据划分样本构建就是把一个epoch的原始波形和对应标签组合起来。单一通道的输入可以有两种形态一种是给机器学习模型的特征向量另一种是给深度学习模型的原始信号或时频图。这里先给出保存numpy数组的常见做法def build_dataset(raw, labels, win_sec30, overlap0): sfreq int(raw.info[sfreq]) win_len sfreq * win_sec X [] y [] data raw.get_data()[0] # 单通道 step int(win_len * (1 - overlap)) for i in range(0, len(data) - win_len 1, step): epoch_index i // step if labels[epoch_index] 0: X.append(data[i:iwin_len]) y.append(labels[epoch_index]) return np.asarray(X), np.asarray(y)overlap参数表示相邻窗口重叠的比例睡眠分期通常不需要重叠因为30秒是临床定义的分期单位overlap反而会让相邻样本高度相关产生数据泄漏。但如果是做逐秒预测或者数据增强可以设置0.5或0.75。2.4 标准化与数据增强模型泛化的基础对原始的3000点波形直接做标准化是必须的否则不同设备、不同个体的幅值差异会让模型失效。标准做法是按全局统计做z-scoredef standardize(X, global_meanNone, global_stdNone): if global_mean is None: global_mean X.mean() global_std X.std() X (X - global_mean) / (global_std 1e-8) return X.astype(np.float32)注意这里是全局标准化即用整个训练集的均值和标准差而不是每个样本单独归一化。每个样本单独归一化会破坏EEG的绝对幅值信息而绝对幅值对分期是有用的比如N3期慢波幅值更大。数据增强方面单通道EEG常用的手段有添加高斯噪声、随机缩放、时间平移但幅度要控制在不破坏睡眠节律的范围内。我一般会用±10%的噪声和±2秒的随机平移能小幅提升模型的鲁棒性。预处理完成后还需要把数据按受试者划分不能直接随机打乱样本因为同一个人的相邻epoch高度相关随机划分会导致评估指标虚高。公开数据集有推荐的split比如Sleep-EDF有留出法协议建议按person id划分训练/测试。3. 特征工程与模型设计从手工特征到端到端模型单通道EEG信息量少所以特征提取对传统机器学习模型特别重要而深度学习模型可以直接从原始波形中学习。第二章已经得到了标准化后的numpy数组这一章先讲手工特征再讲一个可以直接跑的分类器和轻量CNN模型。3.1 单通道脑电的常用特征参数表下面的表是我在多人项目中会使用的组合覆盖时域、频域和非线性特征。频带划分主要参考AASM的睡眠脑电定义特征类别特征名计算方式在睡眠分期中的作用时域过零率信号穿过零点的次数/总点数清醒和REM较高深睡较低时域方差/标准差信号幅值的离散程度N3期慢波幅值大方差高频域δ波绝对功率0.5~4Hz带内功率N3期显著增强频域θ波绝对功率4~8Hz带内功率N1期占比更高频域α波绝对功率8~12Hz带内功率清醒闭眼时增强频域σ波纺锤波功率12~16Hz带内功率N2期出现纺锤波频域频带功率比(δθ)/(αβ)区分浅睡和深睡的关键指标非线性样本熵复杂度估计对N1/REM的区分有一定帮助每个特征并非在所有数据集上都有效比如部分公开数据集只有Fpz-Cz通道α波可能没有后脑区那么明显。因此我在项目里一般会先算全量特征再用随机森林的特征重要性或XGBoost的gain值筛选。3.2 用Scipy计算频带特征并构造特征矩阵基于Welch算法的功率谱密度估计是计算频带功率最常用的方法。下面代码对单个epoch数据计算多个频带的绝对功率from scipy import signal import numpy as np def band_power(data, fs, bands): # nperseg设置4秒窗口频率分辨率0.25Hz够区分θ和α freqs, psd signal.welch(data, fsfs, npersegfs*4) feats [] for name, low, high in bands: idx np.logical_and(freqs low, freqs high) # np.trapz是梯形积分结果等效于带内总功率 feats.append(np.trapz(psd[idx], freqs[idx])) return np.asarray(feats) bands [ (delta, 0.5, 4), (theta, 4, 8), (alpha, 8, 12), (sigma, 12, 16), (beta, 16, 30) ]需要注意welch的nperseg不要超过数据长度。对于100Hz采样率的3000点epochnperseg400即4秒频率分辨率是0.25Hz正好能区分4Hz以下的δ波和4~8Hz的θ波。如果fs是256Hz最好nperseg1024保证频率分辨率在0.25Hz附近。特征矩阵可以批量计算把所有epoch的时域特征和频域特征拼接起来。时域特征可以直接用np.mean、np.std和过零率过零率用np.diff(np.sign(data))计算。拼接后最好再做一次标准化因为不同特征尺度相差可能非常大比如δ功率和过零率。3.3 随机森林基线模型快速、可解释、能跑通睡眠分期用的模型从SVM到深度学习都有随机森林是快速建立基线的选择。它不需要归一化但特征标准化也不影响可以处理类别不平衡还能给出特征重要性。下面是一个完整训练片段from sklearn.ensemble import RandomForestClassifier from sklearn.model_selection import train_test_split # X_feats是(n_samples, n_features)的特征矩阵 X_train, X_test, y_train, y_test train_test_split( X_feats, y, test_size0.2, stratifyy, random_state42) clf RandomForestClassifier( n_estimators300, max_depth20, min_samples_leaf2, class_weightbalanced, n_jobs-1, random_state42 ) clf.fit(X_train, y_train)max_depth限制在20左右避免单个决策树过深记住噪声min_samples_leaf2保证叶节点包含至少2个样本对减少过拟合很有效。class_weightbalanced是处理N1样本不足的关键它自动按类别频率倒数加权让少数类不被多数类淹没。n_jobs-1让所有CPU参与训练300棵树在单通道特征集上通常几十秒内完成。训练完成后查看特征重要性clf.feature_importances_如果某几个频带特征的importance接近0可以移除减少模型体积。但注意随机森林的特征重要性有偏好连续或高基数特征可能被高估所以不要只依赖这一个指标筛选。3.4 轻量CNN用原始波形做端到端睡眠分期手工特征的局限性在于需要人工设计、调参而且可能丢失一些模型自己就能发现的模式。用卷积神经网络直接输入原始波形常见的做法是把每个epoch的3000点排成一维序列。一个结构简单、训练快的CNN如下import torch.nn as nn class SleepCNN(nn.Module): def __init__(self, n_classes5): super().__init__() self.features nn.Sequential( # 第一层卷积kernel_size25覆盖0.25秒内的短时波形 nn.Conv1d(1, 32, kernel_size25, stride2, padding12), nn.BatchNorm1d(32), nn.ReLU(), nn.MaxPool1d(4), # 第二层卷积捕捉纺锤波等节律 nn.Conv1d(32, 64, kernel_size15, stride2, padding7), nn.BatchNorm1d(64), nn.ReLU(), nn.MaxPool1d(4), ) self.classifier nn.Sequential( nn.AdaptiveAvgPool1d(1), nn.Flatten(), nn.Linear(64, n_classes) ) def forward(self, x): return self.classifier(self.features(x))输入形状是(batch, 1, 3000)经第一层池化后长度约375第二层后约93最后用全局平均池化将特征压缩成64维向量再过全连接层输出5类logits。kernel_size25对100Hz数据相当于250ms足以捕捉单个慢波或纺锤波的起始stride2降低分辨率减少计算量。如果数据采样率是256Hzkernel_size要相应调大比如64。训练时使用带权重的交叉熵损失。权重根据训练集各类样本数的倒数归一化import torch from sklearn.utils.class_weight import compute_class_weight class_weights compute_class_weight(balanced, classesnp.unique(y_train), yy_train) weights torch.tensor(class_weights, dtypetorch.float32) criterion nn.CrossEntropyLoss(weightweights)compute_class_weight计算出的权重已经比普通的1:1:1:1:1合理得多。有了这个权重即使N1样本很少模型也不会把所有输入都预测成N2。训练时的优化器我一般用Adam初始学习率1e-3batch size 64跑20~30个epoch配合早停。4. 模型评估与后处理用混淆矩阵和Kappa校准睡眠结构训练完模型后评估不能只看总体准确率。睡眠分期类别严重不均衡N1样本可能只有N2的十分之一一个把所有样本都预测成N2的模型准确率也能有60%以上。所以临床上更看重Cohens Kappa和逐类F1。4.1 评估指标与第一个混淆矩阵from sklearn.metrics import cohen_kappa_score, classification_report, confusion_matrix print(classification_report(y_test, y_pred, target_names[W, N1, N2, N3, REM])) print(Cohen Kappa:, cohen_kappa_score(y_test, y_pred))单通道模型的一个典型结果可能是总体准确率0.78~0.80Kappa 0.70~0.72其中W、N2、N3、REM的F1在0.75以上N1的F1只有0.35~0.45。不要被这个数字吓到即使多导联模型N1也是公认最难分期的阶段因为N1被定义成“从清醒到睡眠的过渡”很多专家打分的一致性本身就低。4.2 混淆矩阵解读哪些错误是可以修正的画混淆矩阵的代码import matplotlib.pyplot as plt from sklearn.metrics import ConfusionMatrixDisplay ConfusionMatrixDisplay.from_predictions( y_test, y_pred, display_labels[W, N1, N2, N3, REM], cmapBlues, values_formatd ) plt.savefig(confusion_matrix.png, dpi150)观察矩阵时最值得关注的几类错误N1被分成N2、W或REM这是信息量不足导致的N3被分成N2说明模型对慢波振幅不够敏感REM被分成W因为两者都是低幅混合频率脑电。如果N3大量被误分为N2可以先看看预处理阶段的滤波是否保留了0.5~2Hz的慢波很多EDF文件自带高通截断检查功率谱就能确认。4.3 序列后处理多数投票和时间上下文修正睡眠本身有很强的时序依赖性比如深睡N3通常持续数个epochREM至少持续几分钟。换句话说一个被孤立预测成N3的样本大概率是误分类。最简单的后处理是中值滤波from scipy.ndimage import median_filter # y_pred是模型在测试集上逐epoch的预测结果 y_pred_smooth median_filter(y_pred, size5, modenearest)size5表示取当前epoch前后各2个epoch的中值作为最终结果。modenearest保证序列开始和结束处不填充0。这个操作可以把Kappa提升0.02~0.03代价是可能抹掉一些短暂的N1片段。如果数据量够大更精细的做法是用隐马尔可夫模型HMM对预测序列解码把混淆矩阵作为发射概率从训练集统计的转换矩阵作为转移概率然后用Viterbi算法求最优状态序列。hmmlearn库可以直接完成这一步但对没有强先验的N1阶段帮助有限。4.4 睡眠结构指标验证逐epoch准确率不够还要看临床睡眠结构指标。常见的有总睡眠时间、睡眠效率、入睡后觉醒、N2/N3/REM占比。预测结果与专家标注在宏观指标上的偏差比逐epoch准确率更能说明模型有没有实际价值。我一般会在测试集上分别统计真实和预测的睡眠结构def sleep_structure(labels, epoch_sec30): total_min len(labels) * epoch_sec / 60 stages [W, N1, N2, N3, REM] counts [(labels i).sum() * epoch_sec / 60 for i in range(5)] return dict(zip(stages, counts)), total_min如果预测的N3比例比真实值高10%以上说明模型对慢波过于敏感可能需要提高分类阈值或增加正则化。如果REM比例偏低则要注意是否后处理窗口过大把短REM片段压成了N1。这类宏观检查不需要额外数据却是模型部署前最重要的校验。具体我比较常用的是把predicted和true各列一份按每30分钟一段对比N3占比的波动曲线偏差超过5%就回去查特征和预处理。5. 跑通整个项目目录认知、参数调整与验证技巧一个完整的单通道睡眠分期项目压缩包通常不会只有一个训练脚本而是包含几个核心模块。拿到压缩包先不要急着运行先确认目录结构、Python依赖和模型输入的预处理形式再动手修改。5.1 压缩包里的常见目录结构与文件以下是这类python源码项目的典型骨架sleep-staging/ ├── data/ # 原始EDF和中间numpy特征 │ ├── raw/ # 放置EDF文件 │ └── features/ # 缓存提取好的特征或epoch波形 ├── models/ # 保存训练好的权重和结构 ├── src/ │ ├── config.py # 全局参数配置 │ ├── preprocessing.py # 读取、滤波、切窗 │ ├── features.py # 特征提取 │ ├── train.py # 训练入口 │ ├── evaluate.py # 评估并输出混淆矩阵 │ └── predict.py # 单条样本推理 ├── requirements.txt ├── project_description.md # 项目说明 └── example_figures/ # 示例图片hypnogram和混淆矩阵先读project_description.md里面通常写了Python版本、依赖库版本和运行顺序。requirements.txt里的版本不要盲目升级mne、scipy、torch之间个别版本存在API差异。如果运行报错优先检查这些库的版本是否和项目说明一致。5.2 参数调整的优先级速查表面对一个已经跑通的项目调参顺序比参数本身更重要。下表是我常用的调整优先级参数常见设置调整优先级影响说明采样率100或128Hz低低于64Hz会丢失纺锤波信息高于256Hz徒增计算量滤波范围0.5~40Hz低慢波和高频伪迹处理的核心一般固定epoch长度30秒固定临床标准不能改特征组合频带功率时域熵中特征比模型重要先加特征再换模型类别权重balanced高不设平衡时少数类几乎学不到后处理窗口3~7中窗口太大会吞掉REM太小不起作用CNN卷积核大小25100Hz低与采样率强相关对准确率影响不大5.3 用单条epoch做推理并检查合理性把训练好的模型部署到自己的设备数据上时最容易出问题的是预处理参数不一致。下面给出一个单epoch推理片段def predict_from_waveform(wave, fs, model, mean, std, device): # 重采样到模型训练时的采样率 if fs ! target_fs: wave resample_eeg(wave, fs, target_fs) # 需要自己实现 # 滤波、标准化滤波器参数必须和训练时完全相同 wave bandpass_filter(wave, 0.5, 40, target_fs) wave (wave - mean) / (std 1e-8) # 形状(1,1,n_samples) x torch.tensor(wave, dtypetorch.float32).unsqueeze(0).unsqueeze(0).to(device) with torch.no_grad(): proba torch.softmax(model(x), dim1).squeeze(0).cpu().numpy() return proba检查预测概率的分布是否合理如果N2的概率始终在0.9以上说明输入的幅值可能比训练集大了好几倍标准化用的mean和std不对。睡眠分期的概率输出还应该有一定的表达性比如REM和N1的概率不会完全为0。另外注意验证阶段不要使用未来信息——如果后处理用到了前后epoch线上推理也需要缓存当前epoch前后若干个预测而不是只传一个epoch。最后一个实用技巧输出hypnogram画图直接观察分期结果是否存在“睡眠阶段频繁抖动”或“整夜没有N3”的异常。检查hypnogram时我习惯把预测标签和真实标签按整夜缩略图叠在一起打印重点看N3簇是否连续、REM簇是否集中在后半夜如果模型把REM零散地插到W中间通常不是后处理窗口问题而是特征没有学到REM特有的低肌肉张力特征。本文还有配套的精品资源点击获取
RELATED

相关推荐

AI 裕灌电动开罐器智能功率 MOSFET 完整选型方案

AI 裕灌电动开罐器智能功率 MOSFET 完整选型方案

2026 年随着 AI 技术在智能厨电中的深度渗透(如自适应力控、罐体识别、节能待机),电动开罐器对功率 MOSFET 提出更高要求:小型化、低损耗、高可靠性。微碧半导体(VBsemi)基于 Trench 工艺,为您提…

📅 2026/9/11 18:35:41
AI 浴室柜  电动升降台盆柜智能功率 MOSFET 完整选型方案

AI 浴室柜 电动升降台盆柜智能功率 MOSFET 完整选型方案

2026 年随着 AI 技术在智能家居中的深度渗透,浴室柜与电动升降台盆柜对功率器件提出更高要求:静音驱动、低功耗、高可靠性。微碧半导体(VBsemi)基于 Trench、SGT 及超结工艺,为您提供覆盖升降电机驱动、控制板电源、传…

📅 2026/9/11 18:35:41
WeChatMsg 使用教程:导出微信聊天记录

WeChatMsg 使用教程:导出微信聊天记录

WeChatMsg 使用教程:导出微信聊天记录 【免费下载链接】WeChatMsg 提取微信聊天记录,将其导出成HTML、Word、CSV文档永久保存,对聊天记录进行分析生成年度聊天报告 项目地址: https://gitcode.com/GitHub_Trending/we/WeChatMsg WeCha…

📅 2026/9/11 18:35:41
MORE NEWS

更多资讯

📰

CMSIS-FreeRTOS源码审计:从适配层到内核调度的嵌入式RTOS实践

做嵌入式开发这些年,我越来越觉得有一类源码值得每个 MCU 工程师静下心来从头到尾读一遍:CMSIS-FreeRTOS。尤其是用 STM32CubeMX、Keil RTE 或 ARM 官方仓库生成过工程的朋友,你们每天打开的就是这个东西,但它内部到底怎么跑起来的…

📰

运放同相与反相怎么选?从输入阻抗、偏置到噪声增益讲透

前阵子画一块光电检测板的信号调理电路,原理图出来后,同事指着一个运放级问我:这一级为什么用反相放大?信号相位在这里需要翻转吗?我当时愣了一下,然后诚实地说:其实不是因为相位,是…

📰

Duix.Avatar报SQLite3 can only bind错误?三步定位并解决

Duix.Avatar报SQLite3 can only bind错误?三步定位并解决 【免费下载链接】Duix-Avatar 🚀 Truly open-source AI avatar(digital human) toolkit for offline video generation and digital human cloning. 项目地址: https://gitcode.com/GitHub_Tre…

📰

OSI七层模型|各层级传输数据单位(详解)

很多人学不懂网络层级,核心问题就是:每层的“数据单位名词太多、容易混淆”。本文对照标准 OSI 七层表格,逐词拆解、通俗解释,不堆砌专业术语,零基础也能看懂。一、OSI七层模型标准对照表先把你看到的原版表格规整排版…

📰

Vben Admin 国际化(i18n)完全指南:从语言配置到远程语言包与自定义语言扩展

Vben Admin 国际化(i18n)完全指南:从语言配置到远程语言包与自定义语言扩展 【免费下载链接】vue-vben-admin A modern vue admin panel built with Vue3, Shadcn UI, Vite, TypeScript, and Monorepo. Its fast! 项目地址: https://gitcod…

📰

基于深度学习的人脸识别签到系统开发指南

简介:基于深度学习的人脸识别签到系统毕业设计源码包,主要面向计算机相关专业正在完成课程设计、毕业设计或需要项目实战练习的学生,可帮助解决人脸识别与自动签到相结合的系统实现问题。项目在导师指导下完成并获评审98分,源码经…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬