1. 为什么CHB-MIT至今仍是发作检测入门的首选数据集
做脑电数据处理的人,绕不开CHB-MIT这个经典数据集。它由波士顿儿童医院发布,收录了多位癫痫患儿的头皮EEG记录。我们课题组刚开始接触癫痫自动检测时,翻遍公开数据,最后也是从CHB-MIT开始跑通的第一个基线模型。原因很简单:它足够规范、标注相对清晰、社区讨论多,踩坑后能找到对照。
很多初学者一开始就奔着TUH EEG Seizure Corpus(tusz)去,反而被它复杂的目录层级和多样化的采样率折腾得够呛。TUH数据确实规模大、真实场景多,但预处理成本高,对刚入门的人来说并不友好。CHB-MIT之所以适合作为第一个抓手,在于以下几点。
首先是标注策略合理。每条记录文件里都有对应的专家标注,标注的是临床上确认的发作起止时间。相比某些只标“有无发作”的数据集,CHB-MIT的连续时间标签让你可以直接做基于时间窗口的逐段分类,也可以提取发作段和非发作段做二分类。这种标注结构对早期做特征工程和模型验证都特别顺手。
其次是文件格式统一。整个数据集几乎全是标准EDF格式(部分早期文件是EDF+),采样率统一为256Hz,通道布局稳定。你不需要为不同患者写不同的读取逻辑,一套代码可以跑完所有患者。这在实际开发中省掉了大量心智负担。
还有一个常被忽略的优势是数据量适中。24例患者的记录,总共接近一千小时的EEG信号,既不会大到让普通配置的机器在预处理时长时间空转,也足够支撑一个像样的训练集和测试集划分。我见过很多实验室用CHB-MIT做论文实验,理由就是单卡GPU也能在合理时间内完成特征提取和训练。
不过需要提醒的是,CHB-MIT也有它的局限性。它的采集环境相对“干净”,被试多为儿童,发作类型和导联设置与成人ICU场景差异不小。如果你的目标是开发一个在成人监护病房里鲁棒运行的检测系统,把它当作测试集验证外部泛化能力时,结果通常会打折扣。但作为技术路线验证,它依然是性价比最高的起点。
2. CHB-MIT数据目录结构解读:下载之后先别急着跑代码
第一次下载CHB-MIT时,我的第一反应是直接扔给MNE读取,结果发现物理通道和标注信息对不上,折腾了一个晚上。问题就出在目录结构和文件命名上。这个数据集的元信息藏得比较隐蔽,先用半小时把目录搞清楚,后面会省出几天的DEBUG时间。
标准的PhysioNet目录下,数据按患者编号存放,文件夹命名从chb01、chb02一直延续到chb24。注意,实际患者总数不止24,早期版本不含chb01的某些记录,后续版本又补充了,所以最终每个文件夹里文件的数目并不相同。比如chb01下通常有chb01_01.edf到chb01_26.edf共26条记录,有的患者会多出几段,有的会有合并文件或者缺失段。
更关键的是每个文件夹里那份患者专用的信息文件,比如chb01-summary.txt。这个文件记录了每条记录的发作起止时间、通道数量、采样频率,以及每个通道对应的电极名称列表。读取数据前一定要先仔细看这个文件,因为不同患者的通道顺序和名称存在差异。比如chb01的FP1-F7起源于双极导联,而有些患者的文件里会有“x”或者“AF3”这种非常规标记,如果你不做通道对齐,后续分析很容易出现串数据的问题。
从格式本身来看,虽然都是EDF扩展名,但内部结构有两种可能:一种是标准的EDF,另一种是EDF+。EDF+在文件头里通过“+”标记区分,而且允许不连续的记录,标注信息可能内嵌在annotations通道里。CHB-MIT的绝大多数文件在MNE读进来之后,annotations都是空的,发作时间段需要独立从summary文件解析。所以我的建议是:不要依赖MNE的annotations,统一用summary文件维护标注,这样逻辑更可控。
我可以分享一个快速检查目录结构的小脚本思路。遍历所有患者文件夹,读取每个EDF文件的时长和通道数,简单汇总成表格。这一招在接手任何脑电数据集时都适用,能让你在十分钟内掌握数据的整体面貌。CHB-MIT的时长差异很大,短的只有几分钟,长的接近一个小时,这种不均匀分布直接影响后续的数据划分策略。比如直接按比例切训练集和测试集时,得分段处理,避免把某个患者的连续几小时数据整个扔进训练集或验证集,否则会产生严重的个体泄露。
2.1 先从summary文件把患者信息结构化
summary文件不是固定模板,但内容很规律。以chb01-summary.txt为例,文件里列出每条记录的采样率、通道数、持续时间,以及发作时间段列表。例如“File Name: chb01_03.edf”之后,会跟着一行“Seizure Start Time: 2996 seconds”和“Seizure End Time: 3036 seconds”,这就对应一段40秒的发作期。
我在解析这个文件时踩过一个坑:有些患者文件里会有一行“Number of Seizures in File: 0”,有的却完全没有这句话,还有的记录里同一个文件包含两次以上的发作。如果只用正则去匹配“Seizure Start Time”这样的字符串,很容易漏掉那些没有发作的段。正确的做法是逐行解析,把每个EDF文件名映射到它对应的所有发作时间对,再统一转换为不受时区影响的绝对样本点索引。
通道列表的信息通常出现在summary文件末尾的“Channel Labels:”区域。每一行一个通道,顺序对应EDF文件内的实际序列。建议把这些通道名存成一个JSON文件,后续用这个JSON做通道对齐。我见过有人直接硬编码chb01的通道顺序,后来换到chb10时整个特征矩阵都乱了,最后花了一个下午排查才发现只是通道顺序问题。
2.2 物理通道与参考通道的区别
CHB-MIT数据集的导联设置是典型临床配置,不是标准的10-20系统,而是一套经过改造的双极导联组合。比如FP1-F7、F7-T7、T7-P7这些导联,表示的是相邻电极之间的差分信号,并非单一电极对地参考。这一点对做源定位研究的人来说尤其重要,如果你需要做脑电地形图,必须先进行参考转换,将双极导联恢复成单极空间信号。
从信号处理角度,双极导联的优势是天然抑制了共模噪声,尤其是工频干扰和运动伪迹。但坏处是通道之间的空间关系不再是一对一的电极位置映射,源定位和某些基于拓扑的深度学习模型会比较麻烦。CHB-MIT的大多数分析最后都退化到时序分类任务,所以双极设置并不会造成太大问题,只是要做好通道名称字符串的规范化处理。
3. 基于MNE读取EDF文件:把脑电信号变成可计算的张量
读取CHB-MIT的最佳工具是MNE-Python。它把EDF解析、通道类型识别、信号裁剪这些底层工作封装好了,而且在处理脑电数据时,它的事件结构、Raw对象和Epoch对象能很好地衔接后续建模。第一篇文章我尽量不涉及深度模型,重点讲清楚如何把EDF文件和发作标注加载成干净的数据张量,并保存成便于后续复用的格式。
先列出依赖环境,Python版本建议3.9以上,MNE至少1.4版本,最好再装上NumPy、pandas和SciPy。我的环境是Python 3.10,MNE最新稳定版,读取CHB-MIT没有问题。如果遇到依赖冲突,建议新建一个单独的虚拟环境,别跟其他项目混在一起。
下面是最基础的一段读取代码:
import mne import numpy as np edf_path = 'chb01/chb01_03.edf' raw = mne.io.read_raw_edf(edf_path, preload=True, verbose='ERROR') print(raw.info) print(raw.ch_names)运行后会输出文件的基本信息,采样频率、通道数量、时长等。CHB-MIT绝大多数文件的采样频率是256Hz,也就是说每秒钟每个通道有256个采样点。有些文件在summary里写的采样率是256,但EDF头部可能标记成250或者512,这种情况很少见,真遇到了以EDF头部为准,并额外确认是否有归一化系数需要处理。
接着要处理标注。从summary文件解析好的发作段落,可以构建一个Annotations对象:
annotations = mne.Annotations( onset=[start_sec, ...], duration=[dur_sec, ...], description=['seizure', ...] ) raw.set_annotations(annotations)这一步虽然MNE内部很标准,但我个人更推荐在后续清洗阶段保留独立的标签文件,而不是完全依赖Annotations。原因在于数据增强和切窗时,Annotations在裁剪过程中偶尔会因为边界和事件重叠问题给你埋坑,反而用单独的二元标签数组更透明。比如你针对1秒窗口做二分类,直接把每个窗口的起始时间与发作时间段做交集判断,看起来多写了点代码,但逻辑非常直观,不会出现MNE事件计数模糊导致标签错位。
3.1 通道类型统一设置
读取EDF后,MNE默认把所有通道都当作eeg类型的“生物电位”通道。如果后续要做带通滤波、ICA去噪或者计算功率谱密度,MNE会根据通道类型调用不同的处理流程。所以要把所有通道类型显式改为eeg:
raw.set_channel_types({ch: 'eeg' for ch in raw.ch_names}) raw.set_montage(None) # 或加载标准电极位置,如果可用需要注意,有些通道名可能是“LOC”或“ROC”这种眼电标记,它们在CHB-MIT里其实仍然对应头皮电极,而不是严格意义的眼电通道。如果项目研究的是眼球运动伪迹,你可以手动把它们标记为eog通道,否则全部留作eeg即可。不要因为名称里带个“O”就自动归为枕区,得看summary文本里对通道标签的注释。
3.2 预处理的第一步:滤波和工频干扰抑制
CHB-MIT的原始信号里有50Hz或60Hz的工频干扰,不同患者采集环境不同,噪声水平差异明显。我们一般先做0.5Hz到60Hz的带通滤波,如果目标是分析高频振荡(80Hz以上频段),就要把归档数据视为有损,因为原始采集系统并不保证存储了高频成分。
下面是一段常用的预处理代码:
raw.filter(0.5, 60, fir_design='firwin')如果工频是60Hz(美国标准),可以额外做50Hz或60Hz陷波:
raw.notch_filter(60, picks='eeg', verbose='ERROR')这里有个细节:双极导联的共模抑制已经能削弱不少工频干扰,但并非完全消除。陷波滤波会损失该频率附近的有用信息,如果后续要做频谱特征,而且某个频带恰好跟陷波频率重叠,建议不做陷波,而是用带阻或者把它当作鲁棒特征的一部分。我自己在提取功率谱密度时倾向于不陷波,直接用0.5-60Hz带通,因为对于癫痫检测任务,工频峰相对稳定,分类器可以自动学会忽略。
3.3 数据落盘格式:别再直接存EDF
预处理后的数据建议保存成numpy格式或者HDF5格式,方便多个脚本并行读取。我最早直接对EDF文件逐次读取和滤波,每次实验都要花十几分钟在IO上,后来把所有清洗后的数据保存成npy文件,实验启动时间缩到几秒。这是一笔非常值得花的预处理投资。
推荐用以下结构来存储每个患者的数据:
processed/ chb01/ signals.npy # 形状 (n_epochs, n_channels, n_times) labels.npy # 形状 (n_epochs,) metadata.json # 记录患者ID、窗口起止、原始文件名等关键信息这种结构让你后续做交叉验证时,可以按患者或按记录灵活分组,也可以直接用np.load一次性加载。CHB-MIT涉及的患者人数不多,数据总量不算恐怖,整个数据集预处理后的npy文件大概几十GB,大多数深度学习场景都能接受。
4. 数据质量的第一个拦路虎:EEG坏道检测与处理
很多人拿到数据集就直接滤波、切窗、提取特征,直到模型指标差得离谱才发现是坏道问题。CHB-MIT虽然整体信噪比不错,但坏道依然存在。所谓坏道,就是指某条通道因为电极脱落、接触不良或放大器饱和,导致信号呈现平直、超大噪声、剧烈跳动或50/60Hz强度异常等现象。这类通道如果混入后续计算,会污染所有涉及空间特征的任务。
手工查看每条通道的波形当然最准确,但患者多、记录多,逐一人工标记不现实。我们需要一套自动化的坏道检测方案。我在项目里用了三种互补策略,结合起来可靠度非常高。
4.1 基于方差的检测:最直接的手段
正常脑电信号的幅值方差在一个合理范围内,而坏道通常会有极端表现。如果电极完全脱落,信号会变成接近一条直线,方差趋近于零;如果受到接触不良干扰,方差可能比正常通道高出数倍甚至数十倍。
对每个通道计算一段时间内的信号方差,然后设定比例阈值。比如计算所有通道方差的中位数或中位数绝对偏差(MAD),偏离中位数3个标准差以上的通道标记为疑似坏道。这里要考虑数据非高斯,直接用均值和标准差容易被极端值带偏,稳健一点的做法是用MAD替代:
from scipy.stats import median_abs_deviation var = np.var(raw.get_data(), axis=1) med = np.median(var) mad = median_abs_deviation(var) threshold_high = med + 5 * mad threshold_low = med / 50 # 极低方差视为平道 bad_by_var = (var > threshold_high) | (var < threshold_low)这个阈值不是绝对真理,需要根据具体数据微调。CHB-MIT的导联设置导致通道间方差天然有区别,比如枕区通道的方差下限可能比额区高,所以如果有标准的脑功能分区模板,也可以分区检测。
4.2 基于功率谱和相关性检测:找出频谱异常的通道
方差不敏感的坏道,常见情况是某个通道出现强烈的周期性干扰,比如持续的工频噪声、高频振荡或电极导线颤振。这类通道的方差可能正常,但频谱分布与周围通道截然不同。
一个简单有效的方法是计算每个通道60Hz附近的功率占比(美国数据用60Hz,中国和欧洲数据用50Hz)。设置带通滤波器截取出55-65Hz信号,计算其能量占整个0.5-60Hz总能量的比例,如果比例超过假设阈值,比如20%,就标为疑似坏道。另外,计算通道间相关系数矩阵。正常状态下相邻通道之间存在一定相关性,而坏道往往与所有其他通道的相关性都异常低。如果某个通道与所有其他通道平均相关系数低于0.1甚至为负,那大概率是坏道。
这里有个常见的坑:参考电极通道和心电通道天然与脑电通道相关性低,所以不能只看相关性。CHB-MIT有少数通道名称是“ECG”或“V”之类,它们不是脑电,不应参与坏道判断。所以在做自动检测前,先用通道名把非脑电通道排除掉。
4.3 坏道的处理策略:插值还是直接剔除?
检测到坏道后,有的处理方式是用周围通道权重插值,MNE里可以调用raw.interpolate_bads()。但CHB-MIT的双极导联设置比较特殊,相邻通道不一定在空间上相邻,插值效果需要谨慎验证。我自己的经验是:如果坏道数少(比如一两条),直接删除该通道并记录删除索引,在建模时通过掩码处理,或者对特征矩阵做通道剔除。如果坏道数多(超过三条),插值时可能会引入虚假的空间相关性,还不如保留原始通道并在模型输入时告知该通道无效。
计算机视觉领域常把坏道视为数据缺失,在输入张量里加一个mask通道也是可行思路。实际做CHB-MIT的发作检测时,多数深度学习模型对单通道磨损并不敏感,直接把这一个通道的信号置零,并在损失函数里不加权重,训练后效果也比插值更稳。
4.4 坏道检测的完整流程示例
下面是我最常用的一段自动化坏道检测伪代码,结合了方差、频谱相关性以及手动标注结果:
def detect_bad_channels(raw, var_z_thresh=5.0): data = raw.get_data() n_ch = data.shape[0] # 1. 方差异常 var = np.var(data, axis=1) log_var = np.log1p(var) med = np.median(log_var) mad = median_abs_deviation(log_var) bad_var = np.abs(log_var - med) > var_z_thresh * 1.4826 * mad # 2. 高频功率占比异常 from scipy.signal import welch freqs, psd = welch(data, fs=raw.info['sfreq'], nperseg=fs*2) high_band = (freqs > 50) & (freqs<60) if not high_band.any(): high_band = (freqs > 55) & (freqs<65) ratio = psd[:, high_band].mean(axis=1) / psd.mean(axis=1) bad_ratio = ratio > 0.3 # 3. 相关系数异常 corr = np.corrcoef(data) avg_corr = (corr.sum(axis=1) - 1) / (n_ch - 1) bad_corr = avg_corr < 0.1 bad_flags = bad_var | bad_ratio | bad_corr return bad_flags这个脚本并不完美,但足够把明显的坏道找出来。找出来后我会把坏道列表存成JSON,并画出一张所有通道的时序缩略图,快速确认自动标记是否合理。整个流程跑完之后,再统一决定该患者哪些通道要置零、剔除或插值。
5. 发作事件标注读取与时间窗口切分准备
CHB-MIT最核心的价值就是那条经过专家确认的发作标注。如果你的目标只是做“是否发作”二分类,那么标注就是你的金标准。这一节重点讲如何把标注转成深度学习友好的标签,并设计出不泄漏的窗口划分方案。
5.1 从summary文件到统一的事件表
我把所有summary解析结果汇总成一个事件表,每行包含以下字段:患者ID、EDF文件名、通道数、采样率、信号时长、发作开始时间(秒)、发作结束时间(秒)。对于那些没有发作的记录,发作开始和结束时间填写NaN。这个事件表是后续所有实验的数据字典。
需要考虑的情况是:CHB-MIT有些发作记录紧邻另一段发作,中间可能只有几秒钟的间隔。如果采用固定窗口切分,比如2秒一个窗口,那么有些窗口可能同时包含发作结束和下一次发作开始。这种边界窗口的标签归属需要提前定义。我的做法是:只要窗口的中心点落在某个发作时间段内,就标记为发作;否则标记为非发作。这个规则简单且在不同实验间保持一致,减少了歧义。
5.2 窗口切分的边界条件
窗口长度选择直接影响模型输入尺寸。有的人喜欢选1秒窗口,因为可以避开“发作窗口太短导致标签不平衡”的问题;有的人喜欢选5秒甚至10秒窗口,因为特征估计更稳定。我在CHB-MIT上常用的基线是2秒窗口,重叠50%,这样既保留了时间连续性,又产生了足够多的样本。
窗口切分时要注意三个边界条件:
- 第一,包含发作边界的窗口,标签按照中心点归属于发作或非发作,避免模糊。
- 第二,如果窗口跨越记录文件末尾,要直接丢弃。有些EDF文件的总时长不是窗口长度的整数倍,最后一个窗口很容易越界。
- 第三,如果一段非发作数据离发作开始时间非常近,比如小于1秒,这段数据通常被认为处于发作前状态,在二分类任务里应该剔除,否则模型会学到一些“临近变化”的提示,导致测试时误报率高。
5.3 按患者划分训练集和测试集
做癫痫检测最忌讳的测试集泄漏,就是同一个患者的数据同时出现在训练集和测试集。人的脑电模式个体差异非常大,如果模型在训练阶段见过某个患者的部分发作,那么它很可能在测试时通过记忆来分类,而不是真正学习到泛化的发作模式。所以几乎统一的原则是:按患者独立划分数据集。
CHB-MIT通常把前20个患者作为训练集,后几个患者作为测试集,但这样划分会导致测试集患者数量过少,方差大。另一种常见做法是Leave-One-Subject-Out交叉验证,每个患者作为测试集时,其余患者作为训练集。这种方式更严格,但训练成本高。对于第一版基线,我建议至少保证训练集和测试集患者数量比例不低于4比1,并且报告每个患者单独的平均召回率、误报率,而不只是汇总的准确率。
5.4 建立数据集类
在实际编码中,我会把事件表和预处理好的npy信号封装成一个简单的Dataset类,供PyTorch或TensorFlow使用。这个类通常需要返回两个信息:窗口信号张量和标签。如果做了事件表,窗口索引可以直接由起始样本点计算得出,不需要额外存储整个窗口矩阵。
下面是PyTorch Dataset的核心逻辑:
class ChbMitDataset(Dataset): def __init__(self, events, signals_folder): self.events = events self.signals_folder = signals_folder self.windows = [] self.labels = [] self.window_len = 512 # 2秒 @ 256Hz self.stride = 256 # 50% overlap def __len__(self): return len(self.windows) def __getitem__(self, idx): win_start, win_end, label, signal_path = self.windows[idx] signal = np.load(signal_path) return signal[:, win_start:win_end], label真实项目里,windows列表会在init时一次性计算好,并保存为pkl文件,这样每次训练不用重新扫一遍事件表。
6. 运动伪迹和高密度运动EEG的注意事项
搜索热词里出现了“高密度运动eeg”,这个词本身不是标准术语,我理解是指运动状态下采集的脑电,比如跑步、骑行时记录EEG。这类数据在运动监护和脑机接口研究中越来越受关注,但和CHB-MIT这种静卧监测场景差别很大。CHB-MIT的儿童患者虽然也会有身体活动,但整体运动伪迹远低于运动场景。
即便如此,CHB-MIT里依然存在幅度较大的运动伪迹,尤其是儿童患者配合度不高,电极在头部晃动,信号会出现明显的突发放电样伪迹,从波形看像癫痫发作,其实是运动导致的。如果不对这类伪迹进行处理,模型很容易把“系统性伪迹”错当成“发作模式”,产生大量假阳性。
处理运动伪迹的常见思路包括:高通滤波把0.5Hz以下的漂移去掉;使用ICA或SSP找独立分量,识别出眨眼和肌电的固定模式并剔除。但ICA在发作检测任务中要慎用,因为某些发作节律成分在独立分量里可能和伪迹分量高度混合,粗暴剔除ICA分量可能把发作信号一并丢掉。
我的做法是:第一,只对非发作训练段做ICA去伪迹;第二,在保存特征之前使用相对稳健的带通滤波和不重叠的短窗口,把明显属于大异常幅值的样本剔除;第三,在评估阶段,允许误报但重点关注误报与伪迹的相关性,如果某个通道的伪迹被系统性触发,再回头调整该通道的权重或滤波参数。
7. 踩坑记录和给初学者的实践建议
这一部分分享我实际跑CHB-MIT时遇到过的几个典型坑,希望能让你少走弯路。
7.1 别被“儿科”标签误导,发作模式差异很大
CHB-MIT里的患者年龄跨度大,从几岁到十几岁都有,发作类型并不统一。有的患者是局灶性发作,放电集中在颞叶或额叶;有的患者是全面性发作,全脑同步放电。直接用同一个模型去拟合所有患者,往往平均指标看起来还不错,但个别患者的召回率极低。我第一次实验时在chb14上只拿到不到40%的召回率,后来才发现该患者的发作多为低幅高频起始段,跟训练集里的典型棘慢波模式差异太大。
建议在报告结果时,按患者拆分混淆矩阵,单独列出低召回率的患者。如果最终目标是临床应用,应该针对低召回率患者做模型微调或者数据增强,而不是对所有患者一视同仁。
7.2 EDF文件里的数值范围被压缩了
CHB-MIT的EDF文件在存储时对数字进行了缩放,有些文件的信号值不是物理单位微伏,而是经过ADC量化后的整数值。MNE读出后的数据单位是Volt,但底层原始值可能经过了校准系数。读取后最好乘以一个校准因子,否则信号幅值会偏差很大。MNE在read_raw_edf时会自动应用了EDF头部里的Calibration系数,所以使用MNE读出来的数据本身没问题。但如果你绕过MNE,自己去解析EDF,一定要手动处理calibration字段,这是我见过初学者踩坑最多的细节之一。
7.3 别用全局标准化覆盖所有通道
脑电信号的幅值在头皮不同区域有明显差异,额区表情肌伪迹大,枕区alpha节律强。如果你对整个数据集做全局均值方差标准化,模型可能过度关注幅值大的通道,弱化其他通道的信息。更好的做法是按通道单独标准化,或者在提取特征后做通道级归一化。我建议无论是原始信号还是频谱特征,都在每个通道内部计算均值方差,然后保存归一化参数。测试阶段用训练集的归一化参数,而不是在测试集上重新计算,这一点很重要,否则会引入数据泄漏。
7.4 数据增强不要破坏时间连续性
常见的数据增强方式包括时间偏移、通道交换、加高斯噪声等。但脑电信号具有极强的自相关结构,随机打乱通道顺序或随机大幅偏移时间轴,可能破坏发作节律的空间一致性。对于CHB-MIT,我更推荐在做频谱图输入时使用时间掩码和频率掩码,或者在同一患者不同发作段之间做混合增强,而不要简单地把整个信号矩阵的通道换掉。当然,具体效果需要实验验证。
7.5 评价指标不能只看准确率
发作检测是个严重类别不平衡问题。非发作样本通常占绝对多数,就算你全部预测为“无发作”,准确率也能达到95%以上。这时候准确率毫无意义。建议重点关注灵敏度,也就是真实发作样本中被正确识别的比例,以及每小时的假阳性次数。在CHB-MIT基准中,很多方法能达到90%以上灵敏度,但每小时的误报次数差别很大。临床上宁可灵敏度略低,也希望误报次数控制在一个较低水平,否则会大量干扰医护人员。
8. 后续系列内容预告:从预处理走向发作检测模型
作为系列的第一篇,我主要把数据获取、读取、预处理、坏道检测、标注对齐和窗口切分讲透了。这些步骤枯燥但至关重要。下一篇文章我会重点讲如何在CHB-MIT上做基础特征工程,包括频带能量、小波变换、非线性特征等,并给出一个基于随机森林的基线模型。再到后面,我们再逐步过渡到时序卷积网络和Transformer结构。
这里留一个关键问题供大家思考:按照窗口切出来的样本,训练集和测试集按患者划分后,训练集中存在同一患者多条连续记录,这些记录之间时间间隔较短,是否会出现相似样本跨记录泄漏?这种现象我们在实验里确实遇到过,比如chb01的连续几小时记录中,发作模式非常相似,如果粗暴地全部放进训练集,测试时万一该患者又被独立留出,实际评估结果会偏乐观。更严谨的做法是在记录级别或时间段级别做分组划分,而不是数据点级别随机打散。这个话题在下一篇做实验设计时会展开。
最后分享一个我个人的小技巧:每次处理一个新的CHB-MIT患者文件时,我先画出该患者一条包含发作的记录的完整持续时间压缩波形图,让发作段在图上直接可见。这样能快速判断该患者的发作形态、幅值特点,以及是否存在明显的周期性噪声。这个过程只需要三十秒,但对后续分析方向的影响非常大,强烈建议一试。
整个CHB-MIT处理流程跑通之后,你会发现它最大的价值不是数据本身,而是给了你一套可复现的脑电数据处理流程。换到其他数据集,比如TUH或高密度运动场景的EEG,核心逻辑依然成立,只是文件解析和坏道阈值需要重新适配。第一篇就到这里,有问题欢迎随时交流,下一篇我们继续从特征工程开始。