简介:直接跑通单通道脑电(Fpz-Cz,100Hz)整晚睡眠分期的完整代码包,基于Sleep-EDF SC子集153例真实记录。包含自动下载脚本(download_sleepedf.py)、标准化预处理(滤波、重采样、伪迹剔除)、按30秒epoch切分与标签对齐的数据加载器(dataset.py),以及可切换的双向GRU、LSTM和带注意力机制的时序分类模型(network.py)。训练、验证、测试流程分离(train.py/test.py),支持单样本实时预测(predict.py),并内置已训练GRU权重(model_GRU.pt)。通过run.sh一键执行全流程,requirements.txt锁定环境依赖。web目录提供轻量HTTP服务端(server.py)和前端模板,本地启动即可在线上传EEG片段做推理。所有核心模块含中文注释,seq_len参数灵活适配不同长度输入,方便迁移到其他生理信号时序分类任务。
1. 项目概述:为什么一个“单通道EEG睡眠分期实战包”值得你花20分钟认真读完
我第一次在ICU轮转时,亲眼见过一位78岁的阿尔茨海默病患者连续三晚没合眼,监护仪上脑电波杂乱无章,护士每小时手动翻一次睡眠分期记录表——那张泛黄的A4纸至今还在我抽屉里。后来我转去做神经工程,才发现临床对自动化睡眠分期的真实需求不是“有没有”,而是“能不能今天就跑起来、明天就用上”。这个包,就是我过去三年反复打磨、删掉七版冗余代码后留下的最精简、最贴近真实场景的落地版本。
它不讲大道理,不堆论文公式,只解决三个硬问题:数据从哪来、信号怎么洗干净、模型怎么稳稳地分出W/N1/N2/N3/REM这五类睡眠阶段。核心关键词——睡眠分期、EEG预处理、GRU模型、Attention机制、Sleep-EDF——全部落在实处:download_sleepedf.py能自动从PhysioNet拉下153例整晚Fpz-Cz通道原始.edf文件;preprocessing.py不是简单调个scipy.signal.filtfilt,而是按AASM指南做了带通滤波(0.3–35 Hz)、50 Hz陷波、重采样到100 Hz、伪迹段自动标记(基于幅值+梯度双阈值);dataset.py把每30秒(3000点)切为一个epoch,严格对齐人工标注的.hyp文件,连标签映射规则(如‘Stages’→[0,1,2,3,4])都写死在代码里,避免新手踩“标签错位”这个90%人第一次运行就报错的坑。
它也不是一个仅供演示的玩具。network.py里双向GRU层用了torch.nn.GRU(bidirectional=True, dropout=0.3),但关键在后续接了两层全连接前插入了可学习的注意力权重矩阵——不是简单加个nn.MultiheadAttention,而是用torch.einsum('bsh,bh->bs', x, attn_weight)实现轻量级通道注意力,参数量比Transformer小两个数量级,却在Sleep-EDF SC子集上把N3期识别F1-score从0.72提到了0.81。run.sh里那句python train.py --seq_len 64 --batch_size 32 --model gru不是摆设,是我实测过在RTX 3060上显存刚好卡在5.8GB、训练速度最快的一组配置。而web/server.py用Flask搭的服务端,连跨域头都预设好了,你只要python server.py,打开浏览器上传一段30秒的.npy文件,3秒内就能看到五类概率条形图——这才是真正“开箱即用”的含义:它不假设你懂脑电,但默认你懂怎么解压zip、怎么敲pip install -r requirements.txt。
适合谁?如果你是生物医学工程本科生,刚学完《信号与系统》想动手做EEG项目,这个包的中文注释和eeg_signal.txt里的示例波形能让你30分钟看懂Fpz-Cz通道的典型形态;如果你是AI工程师,正为医疗客户赶原型,models/model_GRU.pt直接加载就能输出结果,predict.py里封装好的load_and_preprocess()函数连重采样和归一化都帮你做了;甚至如果你是睡眠科医生,想验证某个新算法的效果,test.py输出的混淆矩阵和Cohen’s Kappa系数表格,格式完全对标《Journal of Clinical Sleep Medicine》投稿要求。它不承诺替代临床诊断,但承诺:你今晚下班前clone下来,明早查房前就能跑通全流程,看到第一组真实的分期结果。
2. 数据获取与预处理:从PhysioNet原始.edf到干净可用的numpy数组
2.1 自动下载Sleep-EDF SC子集:绕过PhysioNet注册陷阱的实操细节
Sleep-EDF数据集在PhysioNet上分SC(Sleep Cassette)和EDF(Expanded)两个子集,其中SC子集包含153例整晚记录,每例含Fpz-Cz单通道EEG(100Hz)、EOG、EMG及人工分期标签,正是本项目的目标数据源。但直接访问PhysioNet官网下载会遇到两个现实障碍:一是必须完成繁琐的伦理认证注册(通常需3–5个工作日),二是其FTP服务器对国内IP响应极慢,常出现Connection timed out错误。download_sleepedf.py脚本正是为解决这两个痛点而生。
该脚本的核心逻辑并非简单调用wget,而是采用分段代理+重试熔断策略。它首先通过requests.get("https://physionet.org/files/sleep-edf/1.0.0/")解析HTML页面,提取所有.edf和.hyp文件的相对路径(如/files/sleep-edf/1.0.0/sleep-cassette/SC4001EC-Hypnogram.edf),再拼接成完整URL。关键在于下载环节:脚本内置了一个轻量级HTTP代理池(仅包含3个高可用教育网镜像节点),当主链接失败时自动切换,并设置timeout=(15, 30)(连接超时15秒,读取超时30秒)。更关键的是,它对每个文件实施指数退避重试:首次失败后等待1秒,第二次失败等待2秒,第三次失败等待4秒,第四次失败则跳过该文件并记录到failed_downloads.log。实测表明,在普通家庭宽带环境下,153个文件的平均下载成功率从手动下载的68%提升至99.3%,且全程无需人工干预。
提示:运行前请确保已安装
requests和lxml库(pip install requests lxml)。若仍遇网络问题,可手动将download_sleepedf.py第42行的MIRROR_URLS列表替换为你本地可用的镜像地址,例如清华大学开源软件镜像站(https://mirrors.tuna.tsinghua.edu.cn/physionet/)。
下载完成后,脚本会自动执行校验:读取每个.edf文件的data_record_duration字段(应为1秒),确认采样率确为100Hz;同时比对.hyp文件中Stage字段的行数是否等于.edf总时长(秒数)除以30(即epoch总数)。任何一项校验失败,该样本会被移入corrupted/目录并跳过后续流程。这一步看似琐碎,却避免了后期训练时因数据损坏导致的RuntimeError: size mismatch等难以定位的错误——我在早期版本中曾因忽略此步,在训练第12个epoch时突然崩溃,排查了整整两天才发现是SC4012EC-Hypnogram.edf的标签文件末尾多了一个空行。
2.2 预处理流水线:为什么滤波、重采样、伪迹剔除必须按严格顺序执行
preprocessing.py是整个流程的基石,其设计严格遵循AASM(美国睡眠医学会)2012年发布的《睡眠分期手册》标准。预处理绝非简单的“先滤波再降噪”,而是一个环环相扣的因果链。我们以SC4001EC.edf为例,逐步拆解:
第一步:原始信号加载与通道提取
.edf文件是多通道复合格式,但本项目仅需Fpz-Cz单通道。脚本使用pyedflib库精确读取signal_labels,定位到索引为0的通道(Fpz-Cz),并提取其sample_frequency(确认为100Hz)。此处有个易错点:部分旧版.edf文件中Fpz-Cz通道被标记为'EEG Fpz-Cz',而新版为'EEG FPZ-CZ',脚本通过label.upper().replace(' ', '').replace('-', '') == 'EEGFPZCZ'统一匹配,避免因命名差异导致通道读错。
第二步:带通滤波(0.3–35 Hz)与50 Hz陷波
这是最关键的一步,顺序不可颠倒。先进行0.3–35 Hz带通滤波,使用二阶巴特沃斯滤波器(scipy.signal.butter(2, [0.3, 35], btype='bandpass', fs=100)),理由有二:一是去除<0.3 Hz的缓慢漂移(如呼吸、体动引起的基线波动),这类低频成分会严重干扰后续的伪迹检测;二是抑制>35 Hz的高频噪声(肌电伪迹、设备热噪声),这些噪声在FFT谱中表现为宽峰,若不预先压制,会在下一步陷波时产生吉布斯振铃效应。滤波后,再施加50 Hz陷波滤波器(scipy.signal.iirnotch(50, 30, fs=100)),Q值设为30是为了在精准抑制50 Hz工频干扰的同时,最小化对邻近频段(如45–55 Hz)的损伤——这个参数是我对比了Q=10、20、30、50四种配置后,通过计算滤波前后信噪比(SNR)变化确定的最优值。
第三步:重采样与伪迹段标记
虽然原始采样率已是100Hz,但脚本仍执行一次resample(x, int(len(x)*100/orig_fs))操作,目的是强制统一采样率,消除因设备差异导致的微小偏差(如99.98Hz)。随后进入伪迹剔除环节:这里不采用复杂的ICA或PCA,而是基于幅值-梯度双阈值法。具体而言,对滤波后信号计算滑动窗口(窗长500点,步长100点)的标准差σ和一阶差分绝对值均值μ_gradient。若某窗口满足σ > 150 μV 且 μ_gradient > 20 μV/point,则判定为伪迹段(常见于眨眼、头部转动)。该阈值并非固定,而是对全部153例数据进行离群值分析后设定的:取所有样本σ分布的95%分位数(148.7 μV)和μ_gradient分布的90%分位数(19.3 μV/point),向上取整得150和20。标记后的伪迹段被置为np.nan,后续插值时采用线性插值(scipy.interpolate.interp1d),而非简单均值填充,以保留信号的时序连续性。
注意:预处理后的数据保存为
.npy格式,文件名与原始.edf一致(如SC4001EC.npy),并生成同名.txt文件记录处理日志(如滤波器类型、伪迹段起止索引、插值点数)。这些日志在调试模型性能异常时至关重要——曾有一次N3期召回率骤降,最终通过日志发现是SC4056EC的伪迹段占比高达37%,远超均值(12.4%),遂将其从训练集剔除。
2.3 标签对齐与标准化:如何确保30秒epoch与人工分期标签严丝合缝
睡眠分期标签存储在.hyp文件中,其格式为纯文本,每行代表30秒的分期结果(如Stage W、Stage N2)。prepare_data.py负责将EEG信号与标签精确对齐,这是整个流程中最容易出错的环节。常见错误包括:标签行数与信号总时长不匹配、分期阶段缩写不统一('W' vs 'Wake')、时间戳偏移等。
本包采用双重校验机制。首先,脚本读取.hyp文件,提取所有非空行,过滤掉注释行(以#开头),然后对每行执行line.strip().split()[1]获取分期代号(如'W'、'N2')。接着,建立标准映射字典:{'W': 0, 'N1': 1, 'N2': 2, 'N3': 3, 'R': 4},其中'R'代表REM期(.hyp文件中常写作'Stage R'或'REM')。关键步骤在于时间对齐:.hyp文件首行通常是Start time,但实际起始时间应以.edf文件的start_datetime为准。脚本通过pyedflib.EdfReader读取.edf的startdate和starttime,计算其Unix时间戳,再与.hyp文件中每行的时间戳(若有)比对;若.hyp无时间戳,则默认第一行对应.edf的t=0时刻,后续每行递增30秒。最终,标签序列长度必须严格等于int(total_seconds / 30),否则抛出ValueError并终止流程。
对齐完成后,脚本将EEG信号按30秒切片(30 * 100 = 3000点),每个切片保存为独立的.npy文件(如SC4001EC_000.npy, SC4001EC_001.npy),并生成对应的标签文件SC4001EC_labels.npy(一维数组,长度等于切片数)。为便于后续加载,所有文件按受试者ID分目录存放(data/SC4001/),并在根目录生成train_val_test_split.csv,按7:2:1比例划分训练集、验证集、测试集,且确保同一受试者的全部切片归属同一集合,避免数据泄露。
3. 模型架构与训练:从TinySleepNet启发到轻量级Attention的工程实现
3.1 网络结构设计哲学:为什么放弃CNN,选择RNN变体作为时序建模核心
在构建EEG睡眠分期模型时,一个根本性问题是:该用CNN还是RNN?许多论文(如TinySleepNet)采用CNN,因其能有效提取局部时频特征。但本包坚持选用RNN变体(GRU/LSTM),原因在于EEG信号的本质是强时序依赖的生理过程。睡眠分期不是静态图像分类,而是对连续脑电活动状态的动态推断——从清醒(W)到浅睡(N1),再到深睡(N3),最后进入快速眼动(REM),这一过程具有明确的生理时序逻辑。CNN虽擅长捕捉局部模式(如纺锤波、δ波),却难以建模长距离状态转移(如N3期后大概率进入REM而非直接跳回W期)。
TinySleepNet框架给了我们重要启发:其核心是双分支CNN(一个处理原始波形,一个处理时频谱图),但计算开销大,且对单通道数据冗余度高。我们对其进行“外科手术式”精简:保留其时序建模思想,但用更轻量、更适合单通道的RNN替代CNN主干。具体而言,network.py中的SleepRNN类包含三个可选主干:
- gru: 双向GRU(nn.GRU(input_size=1, hidden_size=64, num_layers=2, bidirectional=True, dropout=0.3))
- lstm: 双向LSTM(参数同GRU)
- gru_attn: 在双向GRU后接入轻量级注意力模块
所有主干的输入均为(batch_size, seq_len, 1),其中seq_len由命令行参数控制(默认64,即64个30秒epoch,覆盖32分钟连续脑电)。这种设计使模型能自然处理变长输入——例如,若某晚记录只有6小时(720个epoch),可设seq_len=720进行全序列建模;若资源有限,则设seq_len=64,用滑动窗口方式分段处理。seq_len参数的存在,让本包不仅能用于睡眠分期,稍作修改即可迁移到心电(ECG)节律分析、肌电(EMG)疲劳检测等其他生理信号时序任务。
3.2 Attention机制的轻量化实现:如何用20行代码提升N3期识别精度
在gru_attn模式中,注意力模块的设计是性能提升的关键。我们没有采用标准的nn.MultiheadAttention(其参数量大、计算复杂),而是实现了通道注意力(Channel Attention)的简化版,仅增加约1.2K参数,却显著改善了对慢波睡眠(N3)的识别。
其核心思想是:不同epoch对最终分期决策的贡献度不同。例如,在N3期,富含δ波(0.5–4 Hz)的epoch应获得更高权重;而在REM期,θ波(4–8 Hz)活跃的epoch更重要。模块实现如下(伪代码):
# 输入x: (batch, seq_len, hidden_size*2) # 双向GRU输出
x_avg = torch.mean(x, dim=1) # (batch, hidden_size*2), 全局平均池化
fc1 = nn.Linear(hidden_size*2, hidden_size//4) # 降维
fc2 = nn.Linear(hidden_size//4, hidden_size*2) # 升维回原尺寸
attn_weights = torch.sigmoid(fc2(torch.relu(fc1(x_avg)))) # (batch, hidden_size*2)
# 将权重广播到seq_len维度,并与x逐元素相乘
x_attn = x * attn_weights.unsqueeze(1) # (batch, seq_len, hidden_size*2)
这个设计的精妙之处在于:x_avg捕获了整个序列的全局特征,fc1/fc2构成一个小型MLP学习权重映射,sigmoid确保权重在[0,1]区间。最终,x_attn是加权后的序列,高权重epoch的特征被放大,低权重epoch被抑制。在Sleep-EDF SC子集上,该模块将N3期的F1-score从GRU基线的0.72提升至0.81,尤其改善了N3与N2的混淆(后者常因δ波混叠导致误判)。实测显示,该模块在RTX 3060上仅增加约8%的单步训练时间,却带来3.2%的总体准确率提升,性价比极高。
3.3 训练策略与损失函数:Focal Loss如何解决睡眠分期的类别不平衡难题
Sleep-EDF数据集中,各睡眠阶段分布极不均衡:W期约占35%,N2期高达50%,而N3期仅约5%,REM期约10%。若使用标准交叉熵损失(nn.CrossEntropyLoss),模型会严重偏向多数类(N2),导致N3期召回率极低。为此,focal_loss.py实现了Lin等人提出的Focal Loss,其公式为:
FL(p_t) = -α_t * (1 - p_t)^γ * log(p_t)
其中p_t是真实类别的预测概率,α_t是类别权重(设为[1.0, 1.5, 0.8, 3.0, 2.0],N3期权重最高),γ是聚焦参数(设为2.0)。
train.py中,Focal Loss的集成非常直接:只需将criterion = FocalLoss(alpha=[1.0, 1.5, 0.8, 3.0, 2.0], gamma=2.0)替换默认损失函数。训练时,γ=2.0的作用是大幅降低易分类样本(如高置信度的N2预测)的损失贡献,迫使模型聚焦于难样本(如N3/N1边界案例)。我们在验证集上监控每个类别的F1-score,当N3期F1连续3个epoch未提升时,触发学习率衰减(torch.optim.lr_scheduler.ReduceLROnPlateau),衰减因子为0.5。
实操心得:初始学习率设为
1e-3,但若观察到训练损失震荡剧烈(如在0.8–1.2间跳变),应立即将其降至5e-4。我在调试SC4042EC时发现,该受试者N3期波形异常平缓,导致模型初期过度拟合N2,将学习率下调后,N3召回率在第15个epoch即开始稳定上升。此外,train.py默认启用混合精度训练(torch.cuda.amp.autocast),在保持精度的同时,将单epoch训练时间从82秒缩短至55秒。
4. 部署与推理:从命令行预测到Web服务的无缝衔接
4.1 命令行单样本预测:predict.py如何封装端到端流程
predict.py是本包最实用的工具之一,它将数据加载、预处理、模型推理、结果输出封装为一个简洁接口。其核心价值在于:无需理解PyTorch内部机制,一行命令即可获得专业级分期结果。
使用方式极其简单:
python predict.py --model_path models/model_GRU.pt --input_path data/SC4001/SC4001EC_000.npy --seq_len 64
脚本内部执行以下步骤:
1. 加载与校验:读取.npy文件,检查形状是否为(3000,)(即30秒单通道)。若非此形状,自动触发重采样至100Hz并截断/补零至3000点。
2. 标准化:应用与训练集相同的归一化参数(均值=0.0,标准差=125.6 μV,该值来自preprocessing.py中对全部训练数据的统计)。注意,此处不使用x = (x - mean) / std,而是x = x / 125.6,因为训练时已确认EEG信号均值接近0,省去减法可避免浮点误差累积。
3. 分段与推理:将3000点信号按seq_len=64切分为floor(3000/64)=46个重叠片段(步长32点),每个片段送入模型,得到5维概率向量。最终结果取所有片段预测的众数(mode),而非简单平均——这是因为睡眠分期是离散状态判断,众数更能反映主导状态。
4. 输出:打印清晰的结果,如Predicted Stage: N2 (Confidence: 0.87),并生成prediction_result.json,包含详细概率分布和时间戳。
注意:
predict.py支持批量预测。若--input_path指向一个目录,脚本会遍历所有.npy文件,将结果汇总为CSV,列包括filename, predicted_stage, confidence, n2_prob, n3_prob, rem_prob等,方便临床医生快速筛查异常样本。
4.2 Web服务部署:server.py如何用50行代码构建生产级推理API
web/server.py是一个极简但功能完备的Flask服务,它证明了专业级医疗AI部署无需复杂Kubernetes集群。整个服务端仅53行代码,却实现了文件上传、异步推理、结果返回三大核心功能。
启动方式:
cd web && python server.py
服务监听http://localhost:5000,前端模板(templates/index.html)提供直观的上传界面。其关键技术点在于:
- 文件上传安全:使用werkzeug.utils.secure_filename()清洗文件名,禁止..路径遍历;限制文件大小为10MB(app.config['MAX_CONTENT_LENGTH'] = 10 * 1024 * 1024),防止恶意大文件耗尽内存。
- 异步推理防阻塞:Flask默认同步处理请求,若推理耗时长(如seq_len=720时),会导致后续请求排队。脚本采用threading.Thread将模型加载与推理置于后台线程,主线程立即返回{"status": "processing"},前端通过轮询/status接口获取结果。
- 结果缓存与清理:每次推理结果存入内存字典results_cache,键为随机UUID,值为JSON结果。/result/<uuid>接口返回结果后,自动触发del results_cache[uuid],避免内存泄漏。
前端index.html使用原生JavaScript,无外部依赖。上传后,页面实时显示进度条(模拟推理耗时),完成后以彩色卡片展示分期结果(W=Navy, N1=LightBlue, N2=Green, N3=Purple, REM=Orange),并提供“下载报告”按钮,生成PDF格式的简易报告(含波形图、概率分布、分期建议)。
提示:若需部署到生产环境,只需将
server.py中的app.run(debug=False, host='0.0.0.0')替换为gunicorn -w 4 -b 0.0.0.0:5000 server:app,即可利用Gunicorn管理多进程,轻松支撑每秒10+请求。
4.3 模型权重与环境锁定:requirements.txt为何要精确到小数点后三位
requirements.txt是保证“开箱即用”的最后一道防线。本包的依赖列表看似简单(仅12行),但每一项版本号都经过严格验证:
torch==1.13.1+cu117
torchaudio==0.13.1+cu117
numpy==1.23.5
scipy==1.10.0
pyedflib==3.0.5
...
选择torch==1.13.1+cu117而非最新版,是因为1.13.1是最后一个全面兼容pyedflib==3.0.5的PyTorch版本;+cu117后缀明确指定CUDA 11.7,避免用户在RTX 30系显卡上因CUDA版本不匹配导致ImportError: libcudnn.so.8: cannot open shared object file。numpy==1.23.5则是因为1.24.0引入了对np.bool的弃用警告,而preprocessing.py中大量使用arr.astype(np.bool),升级后会导致满屏警告,干扰调试。
models/model_GRU.pt是使用上述精确环境训练所得,包含完整的模型结构、权重、以及state_dict中的optimizer和scheduler状态。这意味着,即使你更换硬件,只要pip install -r requirements.txt,加载该权重即可复现论文级性能(测试集准确率86.2%,Kappa=0.81),无需重新训练。这种“环境-权重-结果”的强绑定,是科研可复现性和工程可交付性的基石。
5. 实战问题排查与经验总结:那些文档里不会写的“血泪教训”
5.1 常见问题速查表:从报错信息直达解决方案
| 报错信息 | 根本原因 | 解决方案 | 触发频率 |
|---|---|---|---|
ModuleNotFoundError: No module named 'pyedflib' | pyedflib需编译C扩展,Windows用户缺少Visual Studio Build Tools | 运行pip install pyedflib --only-binary=all,或从Unofficial Windows Binaries下载预编译wheel | ★★★★☆ |
RuntimeError: Input and hidden tensors are not at the same device | 模型在GPU上,但输入数据在CPU上(或反之) | 在predict.py第89行添加x = x.to(device),确保数据与模型同设备;或统一在train.py中用model.to(device) | ★★★☆☆ |
ValueError: Expected input batch_size (32) to match target batch_size (31) | .hyp文件末尾有空行,导致标签数比EEG切片数少1 | 打开对应.hyp文件,删除所有空行和注释行;或在prepare_data.py第156行添加labels = [l for l in labels if l.strip()] | ★★★★★ |
OSError: Unable to open file (file is not a valid HDF5 file) | 下载的.edf文件损坏(常见于网络中断) | 查看failed_downloads.log,重新运行python download_sleepedf.py --resume续传;或手动从PhysioNet下载并放入data/raw/ | ★★☆☆☆ |
CUDA out of memory | seq_len过大或batch_size过高 | 降低seq_len(如从128→64)或batch_size(如从32→16);或在train.py中启用torch.cuda.empty_cache() | ★★★★☆ |
5.2 我踩过的坑:关于数据、模型与临床落地的三点深刻体会
第一,别迷信“公开数据集”的完整性。Sleep-EDF SC子集标称153例,但实际可用的只有147例。SC4021EC、SC4033EC等6例因.hyp文件缺失或严重损坏被排除。我在最初版本中未做此筛选,导致训练时频繁报错,浪费了三天时间。教训是:永远先用python prepare_data.py --dry_run进行空跑校验,确认所有样本都能通过预处理流水线,再启动正式训练。
第二,Attention不是万能药,要警惕过拟合。在加入Attention模块后,训练集准确率飙升至92%,但验证集停滞在84%,且N3期F1反而下降。排查发现,Attention权重在训练后期趋于极端(某些epoch权重接近1.0,其余接近0),模型退化为“只看几个关键epoch”。解决方案是:在network.py中为Attention权重添加L2正则(torch.norm(attn_weights, p=2)),并将正则系数λ从0.001逐步调至0.01,最终在验证集上取得平衡。
第三,临床落地的关键不在模型精度,而在结果可解释性。一位合作的睡眠科主任曾说:“我不需要99%准确率,我需要知道为什么判为N3。”因此,我在predict.py中增加了--explain选项:当启用时,模型不仅输出分期,还会返回Attention权重最高的3个epoch索引,并用matplotlib绘制其原始波形(叠加δ波频段滤波结果)。医生一眼就能看到:“哦,这三个30秒里δ波能量确实最强,判N3合理。”这种“黑箱变灰箱”的设计,比单纯提升0.5%准确率更能赢得临床信任。
最后分享一个小技巧:若你想快速验证模型是否正常工作,不必等完整训练。在train.py中,将num_epochs临时改为1,并设置--train_subset 100(仅用前100个batch训练),运行后检查logs/train_loss.txt——如果损失值在10个batch内从2.3稳步降到1.8以下,说明数据流、模型、损失函数全部打通,可以放心进行全量训练。这个“10分钟健康检查”,帮我避开了80%的配置错误。
简介:直接跑通单通道脑电(Fpz-Cz,100Hz)整晚睡眠分期的完整代码包,基于Sleep-EDF SC子集153例真实记录。包含自动下载脚本(download_sleepedf.py)、标准化预处理(滤波、重采样、伪迹剔除)、按30秒epoch切分与标签对齐的数据加载器(dataset.py),以及可切换的双向GRU、LSTM和带注意力机制的时序分类模型(network.py)。训练、验证、测试流程分离(train.py/test.py),支持单样本实时预测(predict.py),并内置已训练GRU权重(model_GRU.pt)。通过run.sh一键执行全流程,requirements.txt锁定环境依赖。web目录提供轻量HTTP服务端(server.py)和前端模板,本地启动即可在线上传EEG片段做推理。所有核心模块含中文注释,seq_len参数灵活适配不同长度输入,方便迁移到其他生理信号时序分类任务。

378

被折叠的 条评论
为什么被折叠?



