信息速览

CODE-15% — 巴西远程医疗 12 导联心电图 AI-Ready Wikipedia
INFOBOX
| 字段 | 内容 |
|---|---|
| 数据集名称 | CODE-15%(CODE Study 15% 公开子集) |
| 英文全称 | CODE-15%: a large scale annotated dataset of 12-lead ECGs |
| 别名/简称 | CODE 15% / code15 / CODE-15 percent |
| 疾病分类(ICD-11) | BC63 房室传导阻滞(一度)、BC56.0 右束支阻滞、BC56.1 左束支阻滞、BC71 心动过缓(窦性)、BC81 心房颤动或扑动、BC72 心动过速(窦性) |
| SNOMED CT | 59916005 / 386658009 / 406675009 / 49710005 / 49436004 / 3424008 |
| 数据模态 | 标准 12 导联静息心电图(时序波形信号) |
| AI 任务类型 | 多标签二分类(六类异常)、心电年龄回归、死亡风险预测、自监督预训练 |
| 样本总数 | 345,779 条 ECG 记录(233,770 名患者) |
| 数据大小 | 46.3 GB(18 个 zip 分卷 + 35.5 MB exams.csv) |
| 数据格式 | HDF5(tracings/exam_id 数据集)+ CSV 元数据 |
| 许可证 | CC BY 4.0(Creative Commons Attribution 4.0 International) |
| 访问级别 | 开放(Zenodo 直接下载,无需注册) |
| DUO 标签 | GRU(通用研究使用);NRES(无限制,许可未设附加约束) |
| 语言 | 葡萄牙语(原始临床报告,未随数据发布);元数据为英文列名 |
| 首发日期 | 2021-06-09(Zenodo v1.0.0) |
| 最后更新 | 2025-01-08(v1.0.0 元数据修订) |
| 发布机构 | 米纳斯吉拉斯联邦大学(UFMG)/ Telehealth Network of Minas Gerais(TNMG) |
| 官方主页 | Zenodo 记录页 |
| 下载地址 | Zenodo Files 区(exams.csv + exams_part0-17.zip) |
| DOI | 10.5281/zenodo.4916206 |
| 引用次数 | 1,365+(配套论文 Ribeiro et al. 2020 Nat Commun,Google Scholar,截至 2026-09) |
| AI 就绪度评分 | ⭐⭐⭐⭐(4/5)— 有官方 HDF5 规格、预训练权重与 TF/PyTorch 参考实现;扣分项:无官方 train/val 划分脚本(患者级分组需自行实现)、400 Hz + 零填充需自行重采样、六类标签为弱监督临床流程标签 |
| 页面状态 | published |
§0 E-E-A-T 可信度声明
医学审核:本条目 §2 医学背景(六类异常的 ICD-11/SNOMED CT 映射、流行病学、金标准描述)由[千方病案医学编辑部]交叉审核。
数据工程审核:[千方病案医学编辑部交叉审核] 医疗 AI 数据工程师,审核范围:§4 DAIMS 数据字典、§5 数据划分策略、§6 预处理 Pipeline 和坑点。
审核日期:2026-09-05
医疗免责声明:本页面提供的医学信息仅供研究和教育目的,不构成医疗建议、诊断或治疗方案。数据集的医学描述基于公开发表的文献,未经逐一临床验证。任何基于该数据集训练的 AI 模型在应用于临床决策前,必须经过独立的临床验证和监管审批。
技术免责声明:本页面的代码示例、预处理建议和基准性能数据基于公开资料整理,不保证在特定环境下的准确性和适用性。使用者应自行验证代码安全性和数据预处理流程的正确性。千方病案医数集不对因使用本页面信息而导致的任何直接或间接损失承担责任。
数据使用合规:使用本页面描述的数据集前,请务必阅读并遵守数据集原始许可协议。CODE-15% 以 CC BY 4.0 在 Zenodo 开放发布,署名引用 Ribeiro et al. 2020 与 Zenodo DOI 即可使用;其母库 CODE 全库(含 232 万条记录)为申请制,另受数据使用协议(DUA)约束。DUO 标签仅供参考,具体使用限制以数据集官方协议为准。
§1 数据集概览
§1.0 30 秒速览
这是什么? CODE-15% 是巴西米纳斯吉拉斯州公共远程医疗网络(TNMG)从 2010-2016 年真实临床档案中切出的一个"切片":医生们在基层诊所给患者做了心电图,波形和判读结果汇总到州级远程会诊中心;研究团队把这批档案中 15% 的患者的记录(345,779 条 12 导联 ECG、233,770 人)整理后整体公开在 Zenodo 上,任何人都可以下载。
为什么重要? 它是目前公开的最大的带标注 12 导联心电图数据集之一——比 PTB-XL(约 2.2 万条)大一个数量级,且来自真实的初级保健人群而非体检队列,标签虽粗但规模真实。配套论文(Ribeiro et al., 2020, Nature Communications)证明:在这么大的数据上训练的深度模型,读六类心电图异常的能力超过了心内科住院医师。该论文已被引用 1,365 次以上(Google Scholar,截至 2026-09),是 ECG 深度学习方向的奠基性工作。
我能用它做什么? 训练六类异常的多标签分类器;做 ECG 自监督预训练再迁移到小数据集;用死亡随访做"心电-年龄/死亡风险"研究;或者把它当作跨库泛化实验的训练端——在 CODE-15% 上训练、到 PTB-XL 或 CODE-TEST 上验证。想发严谨的论文?记住:评估请用配套的 827 条多专家金标准 CODE-TEST,而不是自己从 CODE-15% 里切测试集。
§1.1 摘要
CODE 研究(Clinical Outcomes in Digital Electrocardiography)由 UFMG 团队主导,系统整理了 TNMG 在 2010-2016 年间收集的 2,322,513 条带标注 12 导联 ECG 记录(1,676,384 名患者,覆盖米纳斯吉拉斯州 811 个市镇)。CODE-15% 是其中的公开切片:按 15% 的患者分层抽样得到 345,779 条记录、233,770 名患者,于 2021-06-09 以 v1.0.0 发布在 Zenodo(DOI 10.5281/zenodo.4916206),总大小 46.3 GB。每条记录由两部分组成:exams.csv 中的一行元数据(检查 ID、患者 ID、检查时年龄、性别、六类异常二元标签、自动正常标志、神经网络预测年龄、死亡与随访时间、波形所在分卷号),以及 18 个 exams_part{i}.hdf5 分卷中的一个行槽(400 Hz、7 或 10 秒、12 导联,两侧零填充至 4,096 样本)。该子集在原始论文中承担两项角色:作为 Ribeiro et al. 2020 深度学习工作的研究家族数据描述载体,以及 Lima et al. 2021"心电年龄预测死亡"研究的模型评估集。数据以 CC BY 4.0 开放,评估金标准由配套的 CODE-TEST(827 条、双心内科医师独立标注 + 第三位资深专家仲裁)承担。
§1.2 战略价值
维度一:规模与人群真实性。公开 ECG 库长期在"几千条专家标注"量级徘徊(PTB-XL 21,837 条、CPSC 2018 约 6,877 条),模型很快在这些库上饱和。CODE-15% 以 34.5 万条的真实初级保健记录提供了两个稀缺属性:一是标签患病率贴近真实临床分布(六类异常各约 1.5-2.7%),二是记录来自非专科操作员、远程判读的真实工作流,噪声、伪迹和"六类词汇之外的发现"都保留在数据里。这使它成为自监督预训练、类别不平衡学习和标签噪声研究的天然试验场。
维度二:研究家族的生态位。CODE-15% 不是孤岛:它上承 CODE 全库(232 万条,申请制,WFDB 格式),旁挂 CODE-TEST(827 条多读者金标准,开放),同族还有 SaMi-Trop(1,959 名查加斯心肌病患者队列,开放)。官方提供预训练权重(Zenodo 3625017)与 TensorFlow/PyTorch 双实现参考代码,从"下载数据"到"复现 Nature Communications 级基线"的路径是公开 ECG 库中最完整的之一。对跨库泛化研究,它与 PTB-XL(欧洲住院/门诊人群)、Chapman-Shaoxing(美国单中心)构成低重叠三角,是检验"巴西初级保健 → 其他体系"迁移损失的首选源域。
维度三:结局数据带来的纵深。绝大多数公开 ECG 库只有"波形 + 诊断标签"两个平面,而 CODE-15% 通过首诊记录上的死亡与随访时间(death/timey)把心电图连接到硬结局:同一个波形编码器既可以做诊断分类,也可以做死亡风险分层与"心电年龄"回归,还能研究"诊断标签之外、波形之中"的预后信息——Lima et al. 2021 的核心发现是神经网络从波形估出的年龄比时序年龄更能预测死亡。附带收益:nn_predicted_age 列直接提供官方模型的年龄预测值,任何新模型都能与之对照而无需重新训练。
§1.3 同类数据集横向对比
| 数据集 | 规模 | 模态与采样率 | 标注 | 许可/访问 | 与 CODE-15% 的差异化 |
|---|---|---|---|---|---|
| CODE-15% | 345,779 条 / 233,770 患者 | 12 导联,400 Hz,7-10 s | 六类二元标签(弱监督临床流程)+ 死亡随访 | CC BY 4.0,开放 | 最大开放量级;单一州级初级保健人群;自带死亡结局 |
| PTB-XL | 21,837 条 / 18,885 患者 | 12 导联,500 Hz,10 s | 数百条 SCP-ECG 语句(多级诊断树) | CC BY-SA 4.0,开放 | 标注细粒度远胜,但规模小一个数量级;设备与元数据更全 |
| CPSC 2018(扩展版) | 约 6,877 条 | 12 导联,500 Hz,6-60 s | 9 类(扩展 24 类)异常 | 竞赛/研究用途 | 中国单中心混合人群;规模最小 |
| Chapman-Shaoxing | 约 10,646 条 | 12 导联,500 Hz,10 s | 11 类节律 | 开放 | 美国单中心;常用于跨库泛化的目标域 |
| CODE-TEST(配套) | 827 条 | 12 导联,400 Hz | 双心内科医师 + 仲裁金标准 + 5 类读者标注 | 开放 | 唯一金标准评估端,与 CODE-15% 零波形重叠 |
§1.4 版本时间轴
| 时间 | 事件 | 说明 |
|---|---|---|
| 2010-2016 | 原始采集 | TNMG 在米纳斯吉拉斯州 811 个市镇采集临床 ECG,CODE 研究整理出 2,322,513 条带标注记录 |
| 2019-09 | CODE 研究论文 | Ribeiro et al., “Tele-electrocardiography and bigdata: The CODE study”, J Electrocardiol |
| 2020-04 | 核心论文发表 | Ribeiro et al., Nature Communications 11:1760(DOI 10.1038/s41467-020-15432-4);CODE-TEST(827 条)随文公开 |
| 2021-06-09 | CODE-15% v1.0.0 发布 | Zenodo DOI 10.5281/zenodo.4916206;46.3 GB;同时作为 Lima et al. 2021 心电年龄研究的评估数据 |
| 2025-01-08 | 元数据修订 | Zenodo v1.0.0 记录更新描述与文件校验信息(数据内容未变) |
| 2025 | 生态收录 | ECGBench、ECGencode 等第三方策展与基准框架将 code15 列为最大收录库 |
§1.5 典型应用场景
- 六类异常多标签分类:1dAVb/RBBB/LBBB/SB/AF/ST 二元向量预测,官方参考架构为 1 维 ResNet,评估走 CODE-TEST 金标准。
- 自监督预训练 + 下游迁移:34.5 万条真实波形是 ECG 基础模型预训练的理想语料,下游可接 PTB-XL 细粒度诊断或 CPSC 少样本任务。
- 心电年龄与死亡风险:
nn_predicted_age、death、timey字段支持"ECG-年龄差 → 死亡率"式研究(Lima et al. 2021 范式)。 - 跨库泛化与域偏移研究:CODE-15%(巴西初级保健,400 Hz)→ PTB-XL/Chapman(欧美,500 Hz)的采样率+人群双重偏移是标准的泛化压测。
- 标签噪声与弱监督学习:约一半记录六类标签全零但并非正常(见坑点 2),是噪声鲁棒损失函数与标签校正算法的真实测试床。
§2 医学背景
§2.1 ICD-11 编码映射
CODE-15% 的六类标签覆盖心律与传导两大类异常。下表为标签到 ICD-11 的映射(采用 ICD-11 母类编码,标注临床亚型):
| 标签 | 中文术语 | ICD-11 编码 | ICD-11 名称 | 临床亚型 |
|---|---|---|---|---|
| 1dAVb | 一度房室传导阻滞 | BC63 | 房室传导阻滞 | 一度(PR 间期 > 200 ms,无脱落) |
| RBBB | 右束支阻滞 | BC56.0 | 右束支阻滞 | 完全性(QRS ≥ 120 ms) |
| LBBB | 左束支阻滞 | BC56.1 | 左束支阻滞 | 完全性(QRS ≥ 120 ms) |
| SB | 窦性心动过缓 | BC71 | 心动过缓 | 窦性(心室率 < 50 次/分判定阈值) |
| AF | 心房颤动 | BC81 | 心房颤动或心房扑动 | 各型房颤 |
| ST | 窦性心动过速 | BC72 | 心动过速 | 窦性(心室率 > 100 次/分判定阈值) |
§2.1b SNOMED CT 映射
| 标签 | SNOMED CT 码 | 首选术语(FSN) |
|---|---|---|
| 1dAVb | 59916005 | First degree atrioventricular block(一度房室传导阻滞) |
| RBBB | 386658009 | Right bundle branch block(右束支阻滞) |
| LBBB | 406675009 | Left bundle branch block(左束支阻滞) |
| SB | 49710005 | Sinus bradycardia(窦性心动过缓) |
| AF | 49436004 | Atrial fibrillation(心房颤动) |
| ST | 3424008 | Sinus tachycardia(窦性心动过速) |
§2.2 疾病简介与流行病学
六类异常代表了 ECG 判读中"节律异常 + 传导异常"的两组代表性维度。
心房颤动(AF):最常见的需干预性心律失常,心房电活动紊乱导致脉搏不规则,显著增加卒中、心衰与全因死亡风险;在 60 岁以上人群中患病率随年龄陡升,但相当比例患者无症状("无症状性功能障碍"正是 CODE 论文关注的筛查场景)。在本库源人群中患病率 1.8%(全库 Train+Val 口径,n=2,322,513),是六类中临床后果最重的一类。
传导阻滞组(1dAVb / RBBB / LBBB):一度房室传导阻滞(PR 间期延长但无 QRS 脱落)在一般人群中患病率随年龄上升,多数良性,但可与传导系统退行性变、心肌炎及药物效应相关。右束支阻滞(RBBB,源人群 2.7%)可见于少数健康人,也提示右心负荷或传导纤维病变。左束支阻滞(LBBB,1.7%)临床意义更重:几乎总与器质性心脏病并存,掩盖急性心梗的 ST 波形判读,且是新发心衰与死亡的独立预测因子。
窦性心率异常组(SB / ST):窦性心动过缓(SB,1.6%)常见于运动员、睡眠状态与药物作用(β 阻滞剂),亦提示窦房结功能减退;窦性心动过速(ST,2.1%)多为发热、贫血、脱水、焦虑与心衰的继发表现,本身常是"信号"而非"疾病"。两类的判定阈值(如 SB < 50 次/分、ST > 100 次/分的常用界值)与标注流程相关,训练模型时应关注阈值附近的标签噪声。
上述患病率来自 Ribeiro et al. 2020 Table 1(CODE 全库口径);CODE-15% 为其患者级分层样本,患病率近似同分布,但 CODE-TEST 测试集对异常做了过采样(如 1dAVb 3.4%、ST 4.4%),两者不可直接混用。
§2.3 临床任务定义
- 筛查任务(本库核心):在初级保健场景中,非专科操作员完成 ECG 采集,远程系统自动判读 + 人工复核。AI 模型的任务是复现/超越这一流程:输入 12 导联波形,输出六类异常的概率向量(多标签,彼此不互斥)。临床价值在于:模型以 F1 > 80%、特异度 > 99% 的表现超越心内科住院医师(Ribeiro et al. 2020),可在无专科资源地区充当"第一读者"。
- 预后任务(扩展):利用首诊记录上的死亡与随访时间字段,训练 ECG 编码器预测全因死亡或估计"心电年龄"——深度网络从波形中读出比时序年龄更致命的老化信号(Lima et al. 2021)。
- 诊断任务边界:六类标签不覆盖心肌梗死、心室心律失常、电解质紊乱等;对六类之外的发现,本库只能提供"六类全零但非正常"的弱信息(
normal_ecg标志可部分区分,见坑点 8)。 - 筛查流行病学要点:无症状房颤的检出是本库任务设定中最有公共卫生价值的方向——AF 越早识别,抗凝与卒中预防的窗口越宽;这也是 CODE 论文把"asymptomatic dysfunction 检测"作为讨论重点的原因。模型评估时应区分"临床转诊场景"(患病率较高)与"人群筛查场景"(患病率 1-2%),后者必须按真实患病率重算 PPV/NPV(§6.9)。
- 操作定义提醒:六类标签的判定依赖 TNMG 判读流程的内部阈值(如 SB/ST 的心率界值),这些阈值未随库发布;跨库比较时,"SB"或"ST"的语义边界可能与目标库不同——这是"同名标签≠同一定义"的典型案例。
§2.4 患者人群画像
| 属性 | 取值 | 来源与口径 |
|---|---|---|
| 来源 | 米纳斯吉拉斯州 811/853 个市镇的公立初级保健与远程会诊网络 | Zenodo 官方描述 |
| 时间 | 2010-2016 | Zenodo 官方描述 |
| 性别 | 女性 60.3%,男性 39.7% | Ribeiro et al. 2020 Table 1(全库口径) |
| 年龄 | 16-25 岁 6.7%;26-40 岁 17.5%;41-60 岁 38.8%;61-80 岁 31.4%;≥81 岁 5.6% | 同上 |
| 就医类型 | 初级保健门诊、远程判读转诊;非住院监护 | TNMG 服务模式 |
| 种族/民族 | 未随数据发布 | 数据集字段清单(无对应列) |
| 每患者记录数 | 平均约 1.48 条;66,929 名患者有 2 条以上 | ECGBench 实测 |
§2.5 临床价值
CODE-15% 所模拟的临床场景是中低收入国家最真实的痛 点:初级保健与急诊机构缺乏 ECG 专科判读能力,而心血管疾病死亡在全球 75% 以上发生在这些地区。Ribeiro et al. 2020 的核心结论——深度模型在六类异常上匹配或超越心内科与急诊住院医师(F1 > 80%、特异度 > 99%)——为"AI 第一读者 + 专科远程复核"的分层会诊模式提供了证据基座。对研究者而言,该库的价值在于把"算法精度"放在"服务可及性"的真实约束下评估:标签噪声、设备混杂、人群偏倚一个不少,任何在这里表现稳健的方法,比在干净基准上的 SOTA 更接近可部署。
§2.6 金标准对照
| 属性 | CODE-15%(训练库) | CODE-TEST(金标准测试库) |
|---|---|---|
| 划分角色 | 模型训练 / 自监督预训练 / 队列研究 | 独立 held-out 评估 |
| 标注方式 | TNMG 临床流程:自动判读系统 + 人工复核(弱监督) | 双心内科医师独立盲标,分歧由第三位资深专家仲裁 |
| 标注者 | 远程会诊流程中的判读人员(匿名化,资质未随数据发布) | 心内科医师 ×2 + 资深仲裁者 ×1;另有住院医师、急诊住院医师、医学生各两组平行标注 |
| 样本量 | 345,779 条 | 827 条(不同患者) |
| 性质 | 弱监督、真实分布、含标签噪声 | 金标准、异常过采样、附全部读者 CSV 与 DNN 预测 |
| 获取 | Zenodo 4916206,CC BY 4.0 | Zenodo 3765780,开放 |
§3 数据集规格
§3.0 版本抉择矩阵
CODE 研究家族按开放程度与用途分为四个层级,按需选择:
| 你的需求 | 推荐层级 | 大小 | 理由 |
|---|---|---|---|
| 训练深度模型 / 预训练 / 快速上手 | CODE-15%(本条目) | 46.3 GB | 开放下载、HDF5 规格统一、规模最大 |
| 发论文要金标准评估 | CODE-TEST | < 200 MB | 827 条多读者标注 + gold_standard.csv + DNN 预测 |
| 需要全部 232 万条记录或原始报告文本 | CODE 全库 | 数十 GB | SciLifeLab 申请制:提交 PI 信息 + 科学用途,签 DUA 后获得(WFDB 格式) |
| 查加斯心肌病专项队列 / 死亡结局研究 | SaMi-Trop | < 5 GB | 1,959 名患者前瞻队列,开放,与 CODE-15% 文件格式同族 |
| 复现 Nature Communications 基线而不训练 | 预训练权重(Zenodo 3625017)+ CODE-TEST | < 500 MB | 官方 TF 权重直接推理,无需下载 CODE-15% |
§3.1 模态详情
- 波形:标准 12 导联(I, II, III, aVR, aVL, aVF, V1-V6)静息心电图,存储采样率 400 Hz,每条记录原始时长 7 秒(2,800 样本)或 10 秒(4,000 样本),两侧对称零填充至 4,096 样本(7 秒记录首尾各补 648 样本)。
- 张量布局:
(N, 4096, 12),最后一维为导联,顺序固定为{DI, DII, DIII, AVR, AVL, AVF, V1, V2, V3, V4, V5, V6}。 - 单位约定:信号以 32 位浮点存储,尺度为 1e-4 V;官方模型约定输入前将信号乘以 1000。
- 元数据:
exams.csv每行一条记录,通过trace_file列指示该记录所在的 hdf5 分卷,exam_id与 hdf5 内exam_id数据集对齐。 - 导联完整性:12 个导联全部存储;无单导联/3 导联记录的混杂(这一点优于部分早期 ECG 库),但肢体导联顺序在兄弟库 CODE-TEST 中并非标准排列(坑点 5)。
- 时长异质性:7 s 与 10 s 记录并存,意味着去填充后信号长度有两种合法值(2,800/4,000 样本);任何"全库统一 4,096"的假设都会把零填充当作真实信号处理(坑点 1)。
- 信号质量:本库不提供质量标志字段;作为真实临床工作流的存档,它包含基线漂移、肌电伪迹与导联接触不良的记录——这既是噪声研究的素材,也是把模型逼出实验室的必要恶劣条件。
§3.2 样本构成
| 维度 | 取值 | 数量/占比 |
|---|---|---|
| 记录总数 | 12 导联 ECG | 345,779 |
| 患者总数 | 唯一 patient_id | 233,770 |
| 多记录患者 | patient_id 出现 ≥ 2 次 | 66,929 人 |
| 记录时长 | 10 s(4,000 原始样本) | 多数记录 |
| 记录时长 | 7 s(2,800 原始样本) | 少数记录 |
| 六类标签 | 任一为 1 | 37,775 条(345,779 − 308,004) |
| 六类标签 | 全部为 0 | 308,004 条(其中 134,657 条 normal_ecg=1) |
| 死亡随访 | 首诊记录提供 death/timey | 233,770 行;其余 112,132 行空缺 |
§3.3 文件格式
| 文件 | 格式 | 内容 |
|---|---|---|
exams.csv |
CSV(35.5 MB) | 15 列元数据与标签(见 §4.1) |
exams_part{i}.zip(i=0…17) |
ZIP → HDF5 | 每个分卷解压出 exams_part{i}.hdf5,含 tracings(N, 4096, 12)与 exam_id(N,)两个数据集 |
ecg_tracings.hdf5(CODE-TEST) |
HDF5 | (827, 4096, 12),附 attributes.csv 与 annotations/ 目录 |
| CODE 全库 | WFDB | 申请制;波形 + 报告文本,非本条目直接范围 |
§3.4 存储大小
| 项 | 大小 |
|---|---|
| exams.csv | 35.5 MB |
| exams_part0-16.zip(17 个) | 各 2.7 GB |
| exams_part17.zip | 782.6 MB |
| 合计 | 46.3 GB(压缩包;解压后 HDF5 总量相近) |
§3.5 标注方式
六类二元标签继承自 TNMG 临床判读流程:设备自动判读算法产出初步报告,经远程判读流程中的人工复核修订后入库——属于弱监督的临床流程标签,不是盲法专家金标准。normal_ecg 列明确来自自动标注系统(Zenodo 描述:“True if automatic annotation system say it is a normal ECG”)。nn_predicted_age 列是神经网络生成的"心电年龄"(Lima et al. 2021 的模型输出),属于派生标签而非原始标注。真正的盲法金标准只存在于 CODE-TEST 的 827 条。
§3.6 标注者资质与一致性
CODE-15% 的流程标注者(远程判读人员)未随数据发布身份与一致性统计。一致性数据在 CODE-TEST 端可考:两位心内科医师独立标注,两者一致时结论直接作为金标准,不一致时由第三位资深专家(知晓前两者意见)仲裁——Kappa 级一致性指标未随数据发布,但"仲裁制"本身即承认读者间存在分歧。CODE-TEST 另附心内科住院医师、急诊住院医师、医学生与 DNN 的平行标注,可用于研究"读者资历—一致性"梯度。
§3.7 采集周期
2010-2016 年,连续 7 年的常规临床服务数据。注意:数据集不发布精确检查日期,exams.csv 仅含检查时年龄,因此按年份的漂移分析、时序切分(train-on-past / test-on-future)在 CODE-15% 上不可行——这是与 PTB-XL(有精确日期)的关键差距。
§3.8 地域覆盖
巴西米纳斯吉拉斯州(Minas Gerais),覆盖 853 个市镇中的 811 个。TNMG 是州级公共远程医疗系统,服务对象以公立初级保健人群为主。单一州、单一国家、单一支付体系——这既是规模来源,也是泛化性的最大约束(见 §7.1)。
§3.9 设备规格
原始记录由基层单位的常规 12 导联心电图机采集,经 TNMG 远程平台数字化归档。数据集不发布设备型号、采样硬件与滤波参数(第三方策展 ECGBench 与 ECGencode 论文均确认"缺设备元数据"),无法做按设备分层的鲁棒性分析。发布版波形统一重采样/对齐至 400 Hz 并零填充至 4,096 样本。
§3.10 深度溯源链
基层心电图机(型号未发布)
→ TNMG 远程传输与自动判读(Welch Allyn 系临床流程,判读算法未随库发布)
→ 人工复核修订 → CODE 研究库(2,322,513 条带标注记录,2010-2016)
→ 患者 15% 分层抽样 → CODE-15%(345,779 条)
→ 波形统一 400 Hz / 零填充 4,096 / HDF5 18 分卷(Zenodo v1.0.0,2021-06-09)
→ 元数据 exams.csv(含派生列 nn_predicted_age,来自 Lima et al. 2021 模型)
§4 数据结构
§4.0 目录树
code15/
├── exams.csv # 35.5 MB,345,779 行 + 表头,15 列元数据与标签
├── exams_part0.zip # 2.7 GB
│ └── exams_part0.hdf5 # 解压后:tracings (N0, 4096, 12) + exam_id (N0,)
├── exams_part1.zip # 2.7 GB
│ └── exams_part1.hdf5
├── ... # exams_part2 ~ exams_part16 同为 2.7 GB
└── exams_part17.zip # 782.6 MB
└── exams_part17.hdf5
# 关联关系:exams.csv.trace_file 指向所在分卷;
# exams.csv.exam_id 与对应 hdf5 内 exam_id 一一对应,
# 行槽位置即 tracings 第一维索引。
§4.1 DAIMS 字段字典
exams.csv(每行一条 ECG 记录)与 HDF5 数据集的核心字段:
| 字段名 | 类型 | 说明 | 示例值 | AI 用途 | 观测误差/注意事项 | 信息性缺失编码 | 取值范围 |
|---|---|---|---|---|---|---|---|
| exam_id | Integer | 检查唯一 ID,与 hdf5 内 exam_id 对齐 | 13 | 主键;关联波形行槽 | 与 SaMi-Trop 共用同一编号空间但集合不相交 | 无 | ≥ 1 整数 |
| patient_id | Integer | 患者伪匿名 ID | 217 | 划分分组键(防泄漏) | 同一患者多条记录(66,929 人) | 无 | ≥ 1 整数 |
| age | Integer | 检查时患者年龄(岁) | 63 | 人口学协变量;年龄偏移研究 | 自报/登记年龄,个别极端值需清洗 | 无 | 实测约 0-100+ |
| is_male | Boolean | 性别(True=男) | False | 公平性分析;协变量 | 二元编码,无其他性别选项 | 无 | True/False |
| nn_predicted_age | Float | 神经网络估计的心电年龄 | 68.4 | ECG-age 差值研究的现成基线 | 派生列,来自 Lima et al. 2021 模型,非原始标注 | 无 | 连续正值 |
| 1dAVb | Integer | 一度房室传导阻滞标签 | 0 | 六类多标签分类 | 弱监督临床流程标签 | 无 | 0/1 |
| RBBB | Integer | 右束支阻滞标签 | 0 | 同上 | 同上;与 LBBB 罕见共存 | 无 | 0/1 |
| LBBB | Integer | 左束支阻滞标签 | 0 | 同上 | 同上 | 无 | 0/1 |
| SB | Integer | 窦性心动过缓标签 | 0 | 同上 | 心率阈值依赖判定流程 | 无 | 0/1 |
| AF | Integer | 心房颤动标签 | 1 | 同上;AF 风险预测 | 同上 | 无 | 0/1 |
| ST | Integer | 窦性心动过速标签 | 0 | 同上 | 同上 | 无 | 0/1 |
| normal_ecg | Boolean | 自动标注系统判定为正常 | False | 辅助筛选"真正常"子集 | 来自自动系统,非金标准 | 无 | True/False |
| death | Boolean | 随访期内死亡(仅首诊行填充) | True | 死亡预测结局 | 其余行为空=未随访,不等于存活 | 空值=未随访 | True/False/空 |
| timey | Float | 死亡时间或随访时长(年,仅首诊行填充) | 3.2 | 生存分析的时间轴 | 与 death 联合解读 | 空值=未随访 | ≥ 0 连续 |
| trace_file | Text | 所在 hdf5 分卷标识 | exams_part0.hdf5 | 快速定位波形文件 | 与解压后文件名对应 | 无 | 18 个分卷名 |
| hdf5: tracings | Float32 张量 | (N, 4096, 12) 波形,400 Hz | — | 模型输入 | 零填充区非真实信号;尺度 1e-4 V(×1000 喂官方模型) | 零填充段 | ±数毫伏当量 |
| hdf5: exam_id | Integer 张量 | (N,),与 csv 行对齐 | 13 | 行槽对齐键 | 官方文档示例误写为 id_exam/signal,实测为 exam_id/tracings | 无 | 同 csv |
§4.2 标签分布
六类标签在源人群(CODE 全库 Train+Val,n=2,322,513)中的患病率,CODE-15% 为其患者级分层样本、患病率近似:
| 标签 | 全库患病率 n (%) | CODE-TEST 金标准集 n=827 (%) |
|---|---|---|
| 1dAVb | 35,759(1.5%) | 28(3.4%) |
| RBBB | 63,528(2.7%) | 34(4.1%) |
| LBBB | 39,842(1.7%) | 30(3.6%) |
| SB | 37,949(1.6%) | 16(1.9%) |
| AF | 41,862(1.8%) | 13(1.6%) |
| ST | 49,872(2.1%) | 36(4.4%) |
注意三点:标签互不互斥,一条记录可同时为 1 类以上;CODE-TEST 对异常过采样(除 AF 外患病率均高于真实分布),其准确率数字不能外推到人群筛查场景;CODE-15% 内 308,004 条记录六类全零,其中仅 134,657 条被自动系统判为正常——即约 17.3 万条记录携带六类词汇之外的"未命名发现"(见坑点 2)。
由六类标签与 normal_ecg 交叉得到的三态样本结构(训练前必查):
| 样本态 | 定义 | 记录数(实测) | 多标签训练中的角色 |
|---|---|---|---|
| 正例 | 至少一类 = 1 | 37,775 | 正常参与损失 |
| 真负例 | 六类全零且 normal_ecg = True | 134,657 | 可信负例 |
| 灰区 | 六类全零且 normal_ecg = False | 173,347 | 六类词汇外的"未命名发现";建议降权/屏蔽/半监督 |
| 合计 | — | 345,779 | — |
import pandas as pd
exams = pd.read_csv("data/code15/exams.csv")
LABELS = ["1dAVb", "RBBB", "LBBB", "SB", "AF", "ST"]
any_pos = exams[LABELS].sum(axis=1) > 0
state = pd.Series("positive", index=exams.index)
state[~any_pos & (exams["normal_ecg"] == True)] = "true_negative"
state[~any_pos & (exams["normal_ecg"] == False)] = "gray_zone"
print(state.value_counts())
# positive 37775
# true_negative 134657
# gray_zone 173347
§4.3 关键统计
- 平均每患者 1.48 条记录;66,929 名患者(28.6%)有多条记录——患者级划分必须分组(坑点 3)。
- 信号张量 345,779 × 4,096 × 12 × 4 字节 ≈ 68 GB 浮点数据,经压缩与 zip 分卷后 46.3 GB;禁止一次性读入内存(官方明确警告)。
- 7 秒记录的零填充占比 = 2 × 648 / 4,096 ≈ 31.6%,对基线漂移类预处理与频谱统计有实质影响。
- 死亡随访仅覆盖首诊行;以患者为单位聚合后,随访时长字段 timey 的分布决定生存分析的有效样本量。
- 六类阳性记录合计 37,775 条(占 10.9%),而六类中出现任一阳性的期望行数按独立假设远高于此——实际数据存在标签共现约束(如 SB 与 ST 几乎互斥),构造负采样或合成样本时应保留这些约束。
- 全库男女性别比 39.7 : 60.3,与多数欧美 ECG 库(男性偏多)方向相反,跨库合并训练时性别亚组指标会出现方向性差异。
§4.4 数据层级
患者 patient_id (233,770)
└── 检查 exam_id (345,779) ← 数据集的基本记录单位(一行 + 一个波形行槽)
└── 导联 lead (12) ← I, II, III, aVR, aVL, aVF, V1-V6,存储顺序固定
└── 样本 sample (4,096) ← 400 Hz × (7 或 10 s) + 两侧零填充
§4.5 缺失值与信息性缺失编码
| 字段 | 缺失形态 | 信息性含义 | 处理建议 |
|---|---|---|---|
| death / timey | 首诊行以外的所有行为空 | 空 = 该患者未纳入死亡随访(如随访窗口不足),不是"存活" | 以 patient_id 取首诊行构造生存分析表;ECGBench 实测 112,132 行缺失 |
| age / is_male | 极少量异常或空值 | 登记错误 | 训练前 dropna 或截断 |
| 六类标签 | 无缺失(0/1 全填充) | 全零 ≠ 正常(仅 134,657/308,004 条全零记录 normal_ecg=True) | 用 normal_ecg 区分"真负例"与"未命名发现" |
| 设备型号 / 检查日期 | 字段本身不存在 | 官方未发布 | 不可用时以年龄近似时间轴;勿伪造日期特征 |
§5 数据划分与使用建议
§5.1 官方划分
CODE-15% 本身没有官方 train/val/test 划分。 在官方研究谱系中:Ribeiro et al. 2020 在 CODE 全库上按 98%/2% 划分训练/验证(约 5 万条验证),并在独立的 CODE-TEST(827 条)上做最终评估;Lima et al. 2021 则把 CODE-15% 整体用作其心电年龄模型的评估集。换言之,这个库在官方语境里要么是"训练语料",要么是"外部评估集"——按你的研究角色重新划分是使用者自己的责任。
§5.2 社区惯例划分
- 患者级随机划分:ECGBench 与多数后续工作按
patient_id分组做 10 折或 80/10/10 划分(GroupKFold / GroupShuffleSplit),确保同一患者不跨集。 - 内部评估端做法:ECGencode(ESWA 2024)等在 CODE-15% 内部切出 test partition 做同分布评估,同时另跑 PTB-XL 对齐实验。
- 严谨论文的标准姿势:训练 + 验证在 CODE-15%(患者级分组),最终报告在 CODE-TEST gold_standard 上。
§5.3 泄漏风险(重点)
- 患者级泄漏:28.6% 的患者有多条记录;随机按行划分会让"同一个人的两次检查"分居训练/测试两侧,指标虚高(坑点 3 详述)。
- 与 CODE-TEST 的隐性重叠担忧:两库波形经验证零重叠,可放心用 CODE-TEST 评估在 CODE-15% 上训练的模型;但若同时拿到了 CODE 全库(申请制),需自行核对 exam_id 与 CODE-TEST 的隔离(ECGBench 对开放子集已完成核对,对全库无法核验)。
- 派生列泄漏:
nn_predicted_age是官方模型在相关数据上训练的输出;用它做特征时,你的指标里混入了别人的模型信息,与"纯波形端到端"结果不可比。 - 标签阈值泄漏:官方 DNN 的分类阈值按最大化 F1 在测试分布上选定;在自己的验证集上重新选阈值,切勿直接照搬官方阈值到你的任务。
§5.4 交叉验证与外部验证建议
- 5 折患者级 GroupKFold 是 CODE-15% 内部比较的标准配置;折间报告均值 ± 标准差。
- 外部验证第一站:CODE-TEST(同人群、金标准、零重叠);第二站:PTB-XL/Chapman(跨体系、需处理 400→500 Hz 重采样与导联顺序差异,见坑点 5)。
- 对死亡结局任务:按患者聚合后做时间无关的分组抽样,并在论文中明确随访窗口不均质的事实。
分层 × 分组的进阶划分(保证每折六类患病率稳定):
import numpy as np, pandas as pd
from sklearn.model_selection import StratifiedGroupKFold
exams = pd.read_csv("data/code15/exams.csv")
LABELS = ["1dAVb", "RBBB", "LBBB", "SB", "AF", "ST"]
# 分层键:六类中"最重要的阳性类别"(无阳性归入灰区/负例层)
exams["stratum"] = exams[LABELS].idxmax(axis=1).where(exams[LABELS].sum(axis=1) > 0, "none")
sgkf = StratifiedGroupKFold(n_splits=5, shuffle=True, random_state=42)
for k, (tr, te) in enumerate(sgkf.split(exams, y=exams["stratum"],
groups=exams["patient_id"])):
assert set(exams.iloc[tr]["patient_id"]).isdisjoint(
set(exams.iloc[te]["patient_id"])), f"fold {k} 患者泄漏"
print(f"fold {k}: train {len(tr)}, test {len(te)}, "
f"test AF 患病率 {exams.iloc[te]['AF'].mean():.4f}")
§6 AI 就绪指南 ⭐
§6.0 环境与依赖
# Python 3.9+;无需特殊硬件即可开始(CPU 读取 + 采样即可)
pip install h5py pandas numpy scikit-learn torch wfdb
CODE-15% 无官方云端托管版(不在 PhysioNet/HuggingFace 官方渠道),以 Zenodo 直链为唯一权威源。以下所有代码假定你已经按 §6.2 完成下载与解压。
§6.1 快速上手(含目录结构约定)
目录结构预期:下面的代码块假设数据解压在 data_root 指向的目录(如 /data/code15/),且所有 18 个 hdf5 已从 zip 中解压出来、与 exams.csv 平级。data_root 与文件名的拼接关系为:data_root / exams.csv、data_root / exams_part{i}.hdf5(i 由 exams.csv 的 trace_file 列给出)。
import h5py, numpy as np, pandas as pd
# data_root: 18 个 exams_part{i}.hdf5 + exams.csv 所在目录
# 最小可用子集:只加载 exams.csv 即可完成全部标签统计与划分设计,无需波形
data_root = "/data/code15"
exams = pd.read_csv(f"{data_root}/exams.csv")
print(exams.shape) # (345779, 15)
print(exams["patient_id"].nunique()) # 233770
# 读取单条波形:先从 csv 定位分卷,再用 h5py 惰性读取
row = exams.iloc[0]
with h5py.File(f"{data_root}/{row['trace_file']}", "r") as f:
idx = np.where(np.array(f["exam_id"]) == row["exam_id"])[0]
x = f["tracings"][int(idx[0])] # (4096, 12),float32,尺度 1e-4 V
# 官方模型约定:喂入前 ×1000
x_model_input = x * 1000.0
关键提醒:f["tracings"] 是 345,779 行的大数据集,绝不要 np.array(f["tracings"]) 整体读入内存;h5py 的切片访问 f["tracings"][start:end] 才是正确姿势(官方 Zenodo 描述明确警告)。
用官方权重做零训练基线:不训练也能拿到一个可对比的基线——下载官方预训练权重(Zenodo 3625017)对 CODE-TEST 推理,与随附的 dnn.csv 逐行比对,通过后说明你的输入管线与官方完全一致:
# 官方 TF 权重 + CODE-TEST 测试包
curl -L "https://zenodo.org/records/3625017/files/model.zip?download=1" -O model.zip && unzip model.zip
curl -L "https://zenodo.org/records/3765780/files/data.zip?download=1" -O data.zip && unzip data.zip
import numpy as np, h5py, pandas as pd
import tensorflow as tf # 官方实现为 TensorFlow 2.x
# 官方模型输入:(N, 4096, 12),400 Hz,信号已 ×1000(见 §3.1 单位约定)
model = tf.keras.models.load_model("model") # 官方权重目录
with h5py.File("data/ecg_tracings.hdf5", "r") as f:
x = np.array(f["tracings"]) # (827, 4096, 12),测试集可整读
probs = model.predict(x * 1000.0, batch_size=32) # (827, 6),顺序同 LABELS
# Sanity check:与官方随附的 DNN 预测逐行比对(阈值已按最大 F1 选定)
ref = pd.read_csv("data/annotations/dnn.csv").to_numpy(np.float32)
print("与官方预测一致:", np.allclose(probs, ref, atol=1e-3))
这一步等价于管线层面的单元测试:只有当"官方权重 + 你的数据加载"复现出官方预测时,后续你自己训练的模型对比才可信。PyTorch 用户请改用 antonior92/ecg-age-prediction 的 ResNet 实现并迁移官方权重。
§6.2 数据获取
| 项 | 内容 |
|---|---|
| 获取方式 | Zenodo 开放直链,无需注册、无申请流程 |
| 页面 | zenodo.org/records/4916206 |
| 文件 | exams.csv(35.5 MB)+ exams_part0.zip … exams_part17.zip(17×2.7 GB + 782.6 MB) |
| 合计 | 46.3 GB(磁盘预留建议 ≥ 120 GB:压缩包 + 解压副本) |
| 伴随资源 | CODE-TEST:zenodo.org/records/3765780;预训练权重:Zenodo 3625017 |
# 逐分卷下载(断点续传可加 curl -C -)
BASE="https://zenodo.org/records/4916206/files"
curl -L "$BASE/exams.csv?download=1" -o exams.csv
for i in $(seq 0 17); do
curl -L "$BASE/exams_part${i}.zip?download=1" -o "exams_part${i}.zip"
done
# 校验 MD5(Zenodo 页面提供每个文件的 md5,此处以官方清单为准)
md5sum exams_part0.zip # 应为 2bed0dc753d16beef8c2f7627e2b6ea4
md5sum exams.csv # 应为 0107516d3f63864498fb77d15799cc95
# 解压全部分卷
for i in $(seq 0 17); do unzip "exams_part${i}.zip"; done
§6.3 预处理全流程
流程:波形读取 → 去零填充 → 重采样(可选)→ 幅值标定 → 滤波(可选)→ 标签/分组对齐。
import numpy as np
import pandas as pd
import h5py
from scipy import signal as sps
ORIG_FS = 400 # 存储采样率
TARGET_FS = 500 # 与 PTB-XL 等对齐时的目标采样率
LEADS = ["I", "II", "III", "aVR", "aVL", "aVF",
"V1", "V2", "V3", "V4", "V5", "V6"] # 存储顺序
def locate(exams: pd.DataFrame, exam_id: int, data_root: str):
"""按 exam_id 定位 (分卷路径, 行槽索引)。csv.trace_file 指向分卷,
行槽索引由分卷内 exam_id 数据集求出。"""
row = exams.loc[exams["exam_id"] == exam_id].iloc[0]
with h5py.File(f"{data_root}/{row['trace_file']}", "r") as f:
pos = int(np.where(np.array(f["exam_id"]) == exam_id)[0][0])
return f"{data_root}/{row['trace_file']}", pos
def load_ecg(data_root: str, trace_file: str, pos: int,
trim_pad: bool = True, resample_to: int | None = None) -> np.ndarray:
"""读取一条 (4096, 12) 波形。
trim_pad: 去掉两侧零填充(7 s 记录各 648 样本;10 s 记录各 48 样本)
resample_to: 目标采样率;跨库对齐 PTB-XL/Chapman(500 Hz)时设为 500
"""
with h5py.File(f"{data_root}/{trace_file}", "r") as f:
x = f["tracings"][pos] # (4096, 12) float32, 尺度 1e-4 V
x = x * 1000.0 # 官方模型输入约定
if trim_pad:
# 依据原始长度:4000 样本 → 各补 48;2800 样本 → 各补 648
nonzero = np.any(x != 0.0, axis=1)
first, last = np.argmax(nonzero), len(nonzero) - np.argmax(nonzero[::-1])
x = x[first:last]
if resample_to is not None and resample_to != ORIG_FS:
n_out = int(round(x.shape[0] * resample_to / ORIG_FS))
x = sps.resample_poly(x, resample_to, ORIG_FS, axis=0).astype(np.float32)
_ = n_out # 10 s → 5000 样本;7 s → 3500 样本
return x
清洗与标准化建议:幅值以官方约定(×1000 后喂模型)为准;如需逐导联 z-score,统计量只在训练折上估计;不建议对去填充后的变长信号做 batch 内拼接——统一按定长窗口或 pad-to-batch-max 处理。
标签侧预处理(三态样本与结局表构造):
import numpy as np, pandas as pd
def build_multilabel_table(exams: pd.DataFrame) -> pd.DataFrame:
"""为多标签训练构造三态标记列(坑点 2 的工程落地)。"""
LABELS = ["1dAVb", "RBBB", "LBBB", "SB", "AF", "ST"]
out = exams.copy()
out["any_label"] = out[LABELS].sum(axis=1) > 0
out["gray_zone"] = (~out["any_label"]) & (out["normal_ecg"] == False)
out["true_neg"] = (~out["any_label"]) & (out["normal_ecg"] == True)
# 训练权重建议:正例 1.0 / 真负例 1.0 / 灰区 0.3-0.5(可调)
out["sample_weight"] = np.where(out["gray_zone"], 0.4, 1.0)
return out
def build_survival_table(exams: pd.DataFrame) -> pd.DataFrame:
"""死亡结局任务:仅取每个患者的首诊行(death/timey 只在那里),显式剔除未随访。"""
first = exams.sort_values("exam_id").groupby("patient_id", as_index=False).first()
followed = first[first["timey"].notna() & first["death"].notna()].copy()
followed["event"] = followed["death"].astype(bool).astype(int)
followed["duration"] = followed["timey"].astype(float)
return followed[["patient_id", "exam_id", "event", "duration", "age", "is_male"]]
§6.4 PyTorch DataLoader 完整实现
import h5py
import numpy as np
import pandas as pd
import torch
from torch.utils.data import Dataset, DataLoader
from sklearn.model_selection import GroupShuffleSplit
LABELS = ["1dAVb", "RBBB", "LBBB", "SB", "AF", "ST"]
class CODE15Dataset(Dataset):
"""每次 __init__ 打开全部 18 个 hdf5 句柄(惰性,不占波形内存)。
目录结构预期:data_root 下 exams.csv 与 exams_part{i}.hdf5 平级。"""
def __init__(self, data_root: str, exam_ids: np.ndarray,
trim_pad: bool = True):
self.exams = pd.read_csv(f"{data_root}/exams.csv")
self.exams = self.exams[self.exams["exam_id"].isin(exam_ids)].reset_index(drop=True)
self.data_root, self.trim_pad = data_root, trim_pad
self.handles = {}
for part in sorted(self.exams["trace_file"].unique()):
self.handles[part] = h5py.File(f"{data_root}/{part}", "r")["tracings"]
def __len__(self):
return len(self.exams)
def __getitem__(self, i):
row = self.exams.iloc[i]
dset = self.handles[row["trace_file"]]
# 预扫描阶段已把 csv 行序对齐到分卷内行槽;此处用 exam_id 精确定位
pos = self._pos_cache[row["exam_id"]]
x = torch.from_numpy(dset[pos].astype(np.float32) * 1000.0) # (4096, 12)
x = x.permute(1, 0) # (12, 4096)
if self.trim_pad:
nz = torch.any(x != 0.0, dim=0)
idx = torch.nonzero(nz).flatten()
x = x[:, idx[0]:idx[-1] + 1]
x = torch.nn.functional.pad(x, (0, 4096 - x.shape[1])) # 右补零到定长
y = torch.tensor(row[LABELS].to_numpy(np.float32)) # (6,) 多标签
return x, y
# 行槽索引缓存:初始化时构建,避免每个 epoch 重复 np.where
def build_pos_cache(self):
self._pos_cache = {}
for part, grp in self.exams.groupby("trace_file"):
f = h5py.File(f"{self.data_root}/{part}", "r")
ids = np.array(f["exam_id"]); f.close()
order = {int(e): p for p, e in enumerate(ids)}
for e in grp["exam_id"]:
self._pos_cache[int(e)] = order[int(e)]
# ---- 患者级 80/10/10 划分(防泄漏,见坑点 3) ----
data_root = "/data/code15"
exams = pd.read_csv(f"{data_root}/exams.csv")
gss1 = GroupShuffleSplit(n_splits=1, test_size=0.2, random_state=42)
train_idx, hold_idx = next(gss1.split(exams, groups=exams["patient_id"]))
gss2 = GroupShuffleSplit(n_splits=1, test_size=0.5, random_state=42)
val_idx, test_idx = next(gss2.split(exams.iloc[hold_idx], groups=exams.iloc[hold_idx]["patient_id"]))
train_ds = CODE15Dataset(data_root, exams.iloc[train_idx]["exam_id"].to_numpy())
train_ds.build_pos_cache()
train_loader = DataLoader(train_ds, batch_size=64, shuffle=True,
num_workers=4, pin_memory=True, persistent_workers=True)
§6.5 八大坑点
⚠️ 坑点 1:400 Hz 非标准采样率 + 零填充 4,096 的重采样陷阱(分类:预处理陷阱)
问题:CODE-15% 存储采样率为 400 Hz,而社区主流库 PTB-XL、Chapman-Shaoxing 均为 500 Hz;且 4,096 样本中首尾含有零填充(7 s 记录各 648 样本、10 s 记录各 48 样本)。直接
resample(4096→5120)会把零填充段一起插值,波形两端被人为拉长为"渐灭伪迹",频谱统计也被污染。症状:跨库迁移时训练在 CODE-15%、验证在 PTB-XL 出现单向大幅掉点;可视化发现每条记录首尾各有约 1.6 s(7 s 记录)的直线段;频谱图在低频端出现系统性旁瓣。
解决:
- 简单方法:先按非零区间裁掉填充(
np.any(x != 0, axis=1)定界),再对有效段resample_poly到目标采样率,最后按目标库规则重新定长。- 进阶方法:ECGencode(ESWA 2024)给出的对齐流程——去零填充 → 上采样 500 Hz → 7 s 记录重填充至 5,000 样本——可完整复用(见 §6.3 代码)。
- SOTA 方法:让重采样本身可微/确定性可复现并写入数据卡;或在模型端用全局平均池化吸收变长,避免物理重采样。
参考:Zenodo 4916206 官方描述(零填充规则);ECGencode, Expert Systems with Applications 2024(与 PTB-XL 对齐流程)。
⚠️ 坑点 2:六类标签是二元向量,“全零"不等于"正常”(分类:标签理解)
问题:六类标签非互斥(同一记录可多个为 1),且只是六类词汇的稀疏投影:实测 308,004 条记录六类全零,其中仅 134,657 条被自动系统标为
normal_ecg=True——剩下约 17.3 万条记录携带六类词汇无法命名的真实异常(心肌梗死图形、室性心律失常、二度房室阻滞等),却被多标签损失当作"自信负例"。症状:模型在"全零"样本上输出接近 0 的概率并因此看似表现极好;把六类全零当"正常对照组"做下游分析时组内混杂大量未标注异常;用六类负例评估"正常/异常"二分类时特异度虚高。
解决:
- 简单方法:训练多标签头(BCE,不做 softmax),评估按类别逐一计算 AUROC/F1,不把"全零"当类别。
- 进阶方法:三态样本设计——正例(某类=1)、真负例(全零且 normal_ecg=True)、灰区(全零且 normal_ecg=False),灰区样本可降权、屏蔽损失或做半监督。
- SOTA 方法:在灰区上做标签校正/异常检测联合训练(如用 CODE-TEST 六类先验校准),或显式建模"六类之外的发现"为一个隐类。
参考:ECGBench code15 策展实测数字;Zenodo 4916206(normal_ecg 定义);antonior92/automatic-ecg-diagnosis README(“概率不要求和为 1”)。
⚠️ 坑点 3:28.6% 患者有多条记录——随机划分 = 患者级泄漏(分类:数据泄漏)
问题:345,779 条记录只来自 233,770 名患者,66,929 人贡献 2 条以上(最多可达数十条),平均 1.48 条/患者。按行随机划分会让同一患者的两次检查分居训练/测试两侧,同一人的波形高度相关,测试指标被"背答案"式抬高。
症状:随机划分比患者级划分的 AUROC 高出数个百分点且方差异常小;错误分析发现测试集里大量记录与训练集 patient_id 相同;换成严格患者级划分后指标骤降、审稿人质疑前作。
解决:
- 简单方法:
GroupShuffleSplit(groups=patient_id)做 80/10/10(§6.4 代码可直接用)。- 进阶方法:
GroupKFold(n_splits=5)患者级五折,报告折间均值±标准差;划分前按age/标签分层保证折间分布一致。- SOTA 方法:报告两级结果——患者级划分的严格数字 + CODE-TEST 独立金标准数字;并固定 random_state 公开划分 ID 列表以利复现。
参考:ECGBench(“folds are grouped on patient_id”);Ribeiro et al. 2020(官方在全库上按患者划分训练/验证的先例)。
⚠️ 坑点 4:46.3 GB 分卷 + HDF5 惰性读取 + 官方文档字段名不一致(分类:工程陷阱)
问题:数据只能整包分卷下载(18 个 zip),无法按需取子集;hdf5 的
tracings数据集约 68 GB 浮点,一次性np.array()读入必然 OOM。雪上加霜:Zenodo 官方描述正文写数据集名为tracings/exam_id,但其内嵌代码示例却写f['id_exam']、f['signal']——照抄示例代码会直接 KeyError。症状:
KeyError: 'id_exam'或KeyError: 'signal';np.array(f["tracings"])触发内存耗尽;下载中断后 zip 损坏、解压报 CRC 错误。解决:
- 简单方法:实测字段名为
exam_id与tracings(§6.1 代码);h5py 句柄常开、按行槽切片读取。- 进阶方法:初始化时一次性构建
exam_id → (分卷, 行槽)缓存(§6.4 的build_pos_cache),训练期零查找开销;多进程 DataLoader 中每 worker 独立开句柄(h5py 句柄不可跨进程共享)。- SOTA 方法:下载后即校验每个分卷的 Zenodo MD5(坑点参考官方清单),并把"分卷完整性校验 + 字段名断言"写进 pipeline 的 smoke test。
参考:Zenodo 4916206 文件区 MD5 清单与描述;ECGBench(“a record is a row of a shared array”)。
⚠️ 坑点 5:CODE-TEST 与 CODE-15% 波形零重叠,但导联顺序不同——跨库按索引取导联会静默换线(分类:评估误用)
问题:两者是同一研究家族的兄弟库:第三方策展已按波形验证 827 条 CODE-TEST 与 CODE-15% 零重叠,可以放心用前者评估后者训练的模型。但 CODE-TEST 的肢体导联顺序是非标准的(aVL, aVF, aVR 排列),CODE-15% 则是标准顺序——把两库的 ndarray 按同一索引映射到导联名,三个肢体导联会被静默交换。
症状:跨库迁移模型在 CODE-TEST 上系统性掉点且找不到原因;Grad-CAM 热图显示模型"关注"的导联与临床判读逻辑对不上;肢体导联重构(Einthoven 三角)校验失败。
解决:
- 简单方法:永远按导联名(§3.1 顺序表)显式取列,不写裸索引;两库各自维护一张顺序表。
- 进阶方法:写一个 Einthoven 一致性断言(I + III = II 的残差应在毫伏级)作为跨库加载的 smoke test,导联错位立即报错。
- SOTA 方法:数据加载层统一输出导联命名字典(lead-name → channel),在 metadata 层固化顺序;对历史 checkpoint 记录其训练库的导联顺序。
参考:ECGBench(“its limb-lead order is aVL, aVF, aVR where code15’s is standard”;“verified against the waveforms, the two share no recordings”)。
⚠️ 坑点 6:单一地域、单一医疗体系的偏倚被"规模"掩盖(分类:偏倚陷阱)
问题:全部 34.5 万条记录来自巴西一个州(米纳斯吉拉斯)的公立初级保健网络(811/853 市镇)、2010-2016 年、女性占 60.3%、41-80 岁占 70.2%。巨大的体量容易让人误以为"分布代表了世界人口",但它是单一支付体系、门诊为主、热带州的切片:设备型号、病谱(如查加斯心肌病高发)、医疗可及性均与世界其他地区不可互换。种族/民族字段缺失,公平性分析受限。
症状:模型在欧美住院人群(PTB-XL 类库)上导联幅值/心率分布漂移;对高血钾、心梗等本库六类之外的病谱"一无所知"却仍给出低熵输出;性别亚组性能差异在原库上不可见(女性占比高)。
解决:
- 简单方法:把"地域/年代单一"写进模型卡与论文 limitations;跨库验证至少包含一个不同大洲的库。
- 进阶方法:做源域-目标域分布对齐诊断(按心率、幅值 RMS、年龄分桶对比 CODE-15% vs 目标库),再决定是否需要域自适应。
- SOTA 方法:在目标域少量数据上微调 + 报告与零样本的差距;用 CODE 研究家族内不同队列(SaMi-Trop 专项队列)做"同体系内疾病谱偏移"分析。
参考:Zenodo 4916206(811/853 市镇);Ribeiro et al. 2020 Table 1(性别年龄分布);ECGBench 策展注记。
⚠️ 坑点 7:"15% 抽样"的口径混乱——不要用 345,779 反推全库规模(分类:标签理解)
问题:"CODE-15%"的 15% 指的是患者的 15%(stratified sampling, 15% of the patients),不是记录的 15%。不同官方材料对全库的口径也有差异:Ribeiro et al. 2020 正文为 2,322,513 条记录 / 1,676,384 患者,antonior92/af-risk-prediction README 则写 2,322,465 条 / 1,558,748 患者——相差数千,源于不同版本纳入/排除规则的微调。用"345,779 ÷ 15%"反推全库会得到与任一官方口径都对不上的数字。
症状:论文里全库规模写错被审稿人揪出;把 CODE-15% 当作"CODE 的均匀 1/15 缩影"解释患病率时发现分层后亚群占比对不上;两篇论文数字"打架"被误读为数据错误。
解决:
- 简单方法:规模数字一律以你实际引用的论文/仓库的原文口径转写,注明出处与口径(记录数 vs 患者数)。
- 进阶方法:在数据卡中同时列出两种官方口径并注明"纳入规则随版本微调";抽样比例按患者数表述为"约 14%-15%"而非精确 15%。
- SOTA 方法:做人群级推断时,用 exams.csv 实际计算的 patient_id 分布(233,770 人)为基准,而不是任何二手全库数字。
参考:Ribeiro et al. 2020(2,322,513/1,676,384);antonior92/af-risk-prediction README(2,322,465/1,558,748);Zenodo 4916206(“15% of the patients”)。
⚠️ 坑点 8:
normal_ecg是自动系统的判定,不是质量标志;death缺失不是"存活"(分类:标签理解)问题:两个高危误读。其一,
normal_ecg来自 TNMG 自动标注系统(官方原文明确),它既不是人工金标准、也不是信号质量标志——低质量但"自动判读为正常"的记录不会被打上任何噪声标记,本库没有任何专用的噪声/质量字段。其二,death/timey只在患者首诊行填充,实测 112,132 行为空;把空值当 0(存活)处理会人为制造大量虚假存活者。症状:用 normal_ecg=True 筛"干净数据"后发现混入大量基线漂移/肌电伪迹;生存分析把未随访者算作幸存者,死亡率被显著低估;六类负例 + normal_ecg 的交叉表与 ECGBench 公布数字(134,657/308,004)对不上。
解决:
- 简单方法:死亡结局任务只在首诊行取 death/timey,缺失行显式剔除并报告剔除量;normal_ecg 仅作为弱先验,不作为质量过滤器。
- 进阶方法:自建轻量质量评分(信号熵、基线漂移幅度、饱和段比例)并报告其与 normal_ecg 的一致率;结局分析用仅首诊行 + 随访窗口敏感性分析。
- SOTA 方法:训练质量分类器(可借用 CODE-TEST 读者分歧度作弱监督),在 pipeline 中输出质量分层并做分层评估;生存模型显式建模删失(censoring)而非硬填充。
参考:Zenodo 4916206(normal_ecg、death/timey 定义);ECGBench(“missing means ‘not followed up’ rather than ‘survived’”;112,132 与 134,657 实测)。
§6.6 数据增强
| 增强 | 安全性 | 说明 |
|---|---|---|
| 随机时间裁剪(去填充后窗口采样) | ✅ 安全 | 保留完整心搏,避免截断 R 波 |
| 高斯噪声 / 电源线干扰注入(50/60 Hz) | ✅ 安全 | 幅度 ≤ 10% 信号 RMS |
| 基线漂移模拟(低频正弦/随机游走) | ✅ 安全 | 模拟真实呼吸伪迹 |
| 随机幅度缩放(0.8-1.25×) | ✅ 安全 | 增益鲁棒性;保持导联间相对增益 |
| 导联 dropout(随机置零 1-2 个导联) | ✅ 安全 | 提升跨设备/缺导联鲁棒性 |
| 左右翻转 / 导联重排 | ❌ 危险 | 破坏解剖学左右与 Einthoven 关系 |
| 时间反转 | ❌ 危险 | P/QRS/T 极性与序倒置,产生生理不可能信号 |
| 跨记录 Mixup(波形线性插值) | ❌ 危险 | 两个不同患者心电的插值无生理意义,且标签插值语义不明 |
安全增强的参考实现(作用于去填充后的波形):
import torch
class SafeECGAugment:
"""训练期安全增强:噪声 + 基线漂移 + 幅度缩放 + 导联 dropout。"""
def __call__(self, x: torch.Tensor) -> torch.Tensor:
# x: (12, L)
if torch.rand(1).item() < 0.8: # 高斯噪声
x = x + 0.01 * x.abs().mean() * torch.randn_like(x)
if torch.rand(1).item() < 0.5: # 随机幅度缩放
x = x * torch.empty(1).uniform_(0.8, 1.25).item()
if torch.rand(1).item() < 0.5: # 低频基线漂移
t = torch.linspace(0, 1, x.shape[-1])
drift = 0.02 * x.abs().mean() * torch.sin(2 * 3.14159 * 0.25 * t + torch.rand(1))
x = x + drift
if torch.rand(1).item() < 0.3: # 导联 dropout(1-2 个)
leads = torch.randperm(12)[:torch.randint(1, 3, (1,)).item()]
x[leads] = 0.0
return x
§6.7 模型推荐
| 模型 | 参数量级 | 适用任务 | 备注 |
|---|---|---|---|
| 1-D ResNet(官方架构) | 约 1M(约 40 万-100 万) | 六类分类 / 心电年龄 | 论文同款;TF 权重可下载(Zenodo 3625017),PyTorch 复刻在 ecg-age-prediction |
| Xception1D / 深度可分离 CNN | 约 1M-5M | 多标签分类 | 低参数量高吞吐,适合 34.5 万条全量训练 |
| Transformer(时序分块) | 5M-30M | 自监督预训练 / 长上下文 | 需强正则或先 CNN 蒸馏 |
| SimCLR/MAE 式自监督编码器 | 任意 | 预训练 → 下游迁移 | CODE-15% 是预训练语料,下游在 PTB-XL/CODE-TEST 微调 |
| 深度生存模型(DeepSurv/Cox-Time) | 1M-5M | 死亡结局 | 输入接冻结编码器,输出接 Cox 风险头 |
选型细则:
- 官方 1-D ResNet 是唯一有"同款权重"的选择——如果论文需要与 Ribeiro et al. 2020 直接对表,架构选择不应偏离官方太远,否则差异来源无法归因。
- 大模型(Transformer/基础模型路线):34.5 万条足以支撑 10M-30M 参数的自监督预训练,但需要配合 §6.6 安全增强与患者级划分;预训练目标推荐掩码重建或导联条件预测(从 6 导联重建另外 6 导联)。
- 标签利用策略:把三态样本(正例/真负例/灰区)作为一等公民输入损失设计,比堆模型容量对最终指标的贡献更大——这是弱监督大规模库上反复被验证的经验。
§6.8 硬件需求
| 阶段 | 最低配置 | 推荐配置 |
|---|---|---|
| 下载解压 | 60 GB 磁盘 | 150 GB SSD(压缩包+解压双份) |
| 数据探查 / 标签统计 | CPU 8 GB 内存 | CPU 16 GB |
| 单卡训练(ResNet1D,batch 64) | 1× 12 GB GPU(RTX 3080 级) | 1× 24 GB GPU(3090/4090) |
| 自监督预训练 | 1× 24 GB,数天 | 2-4× A100 40 GB,1-2 天 |
| 全量推理 | CPU 可行(约数千条/分钟/核) | 1× 8 GB GPU |
§6.9 评估指标代码
import numpy as np
from sklearn.metrics import (roc_auc_score, f1_score, precision_score,
recall_score, confusion_matrix)
LABELS = ["1dAVb", "RBBB", "LBBB", "SB", "AF", "ST"]
def evaluate(y_true: np.ndarray, y_prob: np.ndarray) -> dict:
"""y_true/y_prob: (N, 6)。阈值策略:验证集上按类最大化 F1(官方论文同款)。"""
out = {}
for j, name in enumerate(LABELS):
# 在候选阈值上搜索最大 F1(应在你自己的验证折上做,而非测试集)
ths = np.linspace(0.05, 0.95, 181)
f1s = [f1_score(y_true[:, j], y_prob[:, j] >= t) for t in ths]
t = ths[int(np.argmax(f1s))]
y_hat = (y_prob[:, j] >= t).astype(int)
tn, fp, fn, tp = confusion_matrix(y_true[:, j], y_hat).ravel()
out[name] = {
"auroc": roc_auc_score(y_true[:, j], y_prob[:, j]),
"f1": f1_score(y_true[:, j], y_hat),
"sensitivity": tp / (tp + fn) if tp + fn else np.nan,
"specificity": tn / (tn + fp) if tn + fp else np.nan,
"threshold": t,
}
return out
def screening_metrics(y_true: np.ndarray, y_prob: np.ndarray,
prevalence: np.ndarray, threshold: float = 0.5) -> dict:
"""人群筛查场景:把 CODE-TEST(过采样)上的 PPV/NPV 重校准到真实患病率。
prevalence: 每类的目标人群患病率(如全库口径 0.015-0.027)。"""
from sklearn.metrics import confusion_matrix
out = {}
for j, name in enumerate(LABELS):
y_hat = (y_prob[:, j] >= threshold).astype(int)
tn, fp, fn, tp = confusion_matrix(y_true[:, j], y_hat).ravel()
sens, spec = tp / (tp + fn), tn / (tn + fp)
p = prevalence[j]
ppv = sens * p / (sens * p + (1 - spec) * (1 - p))
npv = spec * (1 - p) / (spec * (1 - p) + (1 - sens) * p)
out[name] = {"sensitivity": sens, "specificity": spec,
"ppv_real_world": ppv, "npv_real_world": npv}
return out
§6.10 MLOps 笔记
- 数据版本化:Zenodo v1.0.0(2021-06-09)为唯一数据版本,DOI 即版本锚点;记录每个分卷的 MD5 于 pipeline 配置中。
- 缓存策略:第一次训练前把"去填充 + 重采样"结果落盘为分块 npz/内存映射文件,可把 epoch 时间缩短一个量级;缓存键包含采样率与裁剪规则版本。
- 评估隔离:训练/验证/测试三份 patient_id 划分列表随 checkpoint 一并存档(JSON),保证任何时刻可复现指标。
- 漂移监控(部署期):无精确检查日期 → 无法按时间监控漂移;替代方案是监控输入分布代理指标(心率、RMS、导联相关矩阵秩)。
- 合规:CC BY 4.0 要求署名——在模型卡中引用 Ribeiro et al. 2020 与 Zenodo DOI;衍生模型无需同许可,但禁止暗示临床认证。
- 数据卡模板:模型发布时随附三张表——三态样本构成(§4.2)、患者级划分统计、CODE-TEST 终评结果;把"normal_ecg 语义"与"400 Hz + 零填充"写进使用约束。
- 复现门槛:他人复现你的结果所需的最小信息 = 划分列表 + 阈值选择策略 + 增强清单 + 随机种子;建议在 repo 中提供
reproduce.sh一键脚本,并固定PYTHONHASHSEED、numpy/torch种子与 h5py 读序。
§7 质量评估与局限性
§7.1 已知偏倚
| 偏倚类型 | 描述 | 严重程度 | 缓解措施 |
|---|---|---|---|
| 地域偏倚 | 全部记录来自巴西米纳斯吉拉斯州单一州级公立体系 | 高 | 跨大洲外部验证(PTB-XL/Chapman);模型卡声明适用域 |
| 标签噪声偏倚 | 六类标签为临床流程弱监督,无盲法仲裁;约 17.3 万条"六类全零但非正常" | 高 | 灰区三态建模;金标准仅引 CODE-TEST |
| 设备混杂 | 设备型号与滤波参数未发布,无法按设备分层 | 中 | 增强加入导联 dropout 与幅值缩放;报告跨库迁移差距 |
| 年龄/性别结构偏倚 | 41-80 岁占 70.2%,女性 60.3%;儿科记录几乎缺失 | 中 | 亚组分析报告分层指标;儿科任务换库 |
| 患病率偏倚 | 六类患病率 1.5-2.7% 的真实低流行分布;CODE-TEST 过采样异常(1.9-4.4%) | 中 | 筛查场景指标按真实患病率重新校准 PPV/NPV |
§7.2 标注质量评估
可核查的锚点全部在 CODE-TEST 端:双读者一致即金标准、分歧交资深专家仲裁,且随数据发布全部读者(含住院医师、医学生)的平行标注——这是 ECG 公开库中少见的"读者资历梯度"设计,可直接量化读者间一致性。CODE-15% 端的流程标签无逐条一致性数据,其可信度由间接证据支撑:用同源全库训练的 DNN 在 CODE-TEST 金标准上达到 F1 > 80%、特异度 > 99%,说明流程标签与专家共识方向性一致,但逐类误差(尤其灰区记录)不可忽略(坑点 2)。
两个实用的质量核查动作:
- 读者一致性梯度分析:在 CODE-TEST 上计算"医学生 < 急诊住院医 < 心内住院医 < 心内科医师"的标注一致率排序,即可估计"流程标签"的近似误差带——TNMG 流程判读者的能力介于哪一档,直接决定你对 CODE-15% 标签的信任上限。
- 阈值敏感性报告:官方 DNN 预测(dnn.csv)按最大 F1 选阈值;把它换成最大 Youden 指数或固定 0.5 重算,观察指标排序是否翻转——翻转说明你的方法差异只在阈值层,不在表征层。
§7.2b 与其他 ECG 库的标注质量对照
| 库 | 标注机制 | 可信度锚点 |
|---|---|---|
| CODE-15% | 临床流程(自动 + 人工修订),34.5 万条 | 同源 DNN 在金标准上的整体表现 |
| CODE-TEST | 双读者 + 仲裁金标准,827 条 | 唯一逐条可审计的 CODE 端标注 |
| PTB-XL | 心内科医师标注 + SCP 语句树 | 逐条标注 + 元数据完备 |
| CPSC 2018 | 专家标注,规模小 | 无公开一致性数据 |
结论:CODE-15% 用"广度换精度",PTB-XL 用"精度换广度";两者配对使用(前者训练、后者细粒度评测或反向迁移)是当前社区的主流姿势。
§7.3 泛化性风险
| 场景 | 失效风险 | 证据 |
|---|---|---|
| 巴西初级保健同分布(CODE-TEST) | 低 | 同人群、同采集体系,官方论文主结果场景 |
| 欧美门诊/住院库(PTB-XL 等) | 中-高 | 400→500 Hz、人群、设备、病谱四重偏移;ECGencode 需专门对齐流程才能迁移 |
| 急诊/危重症监护 | 高 | 库内几乎无 ICU 谱系记录;六类之外的危急值不在标签内 |
| 儿科 | 高 | 年龄主峰在 41-80 岁,低龄段仅 6.7%(16-25 岁),儿科证据不足 |
| 死亡风险外推 | 中 | 随访窗口不均质、112,132 行无随访(坑点 8) |
§7.4 伦理与合规
数据经 UFMG 团队在 TNMG 公共服务框架下整理后以 CC BY 4.0 公开:不包含姓名、精确日期、地点等直接标识符,患者以伪匿名整数 ID 表示;检查日期未发布进一步降低重识别风险。原始采集为临床服务所必需,公开用途为科研。使用者义务:署名引用论文与 DOI;不得尝试重识别;临床部署前需独立验证与监管审批。母库 CODE 全库为申请制(SciLifeLab,PI 信息 + 用途说明 + DUA),与开放子集适用不同协议。
合规要点速查:
| 使用行为 | 是否允许 | 依据 |
|---|---|---|
| 直接下载用于学术研究 | ✅ | CC BY 4.0,Zenodo 开放 |
| 训练商业产品底层模型 | ⚠️ 许可允许但需自行评估 | CC BY 4.0 不含非商业条款;临床部署需监管路径 |
| 在论文/产品中再分发数据 | ✅ 需同样署名 | CC BY 4.0 署名条款 |
| 申请 CODE 全库合并使用 | ⚠️ 需单独申请 | SciLifeLab DUA 流程,两套协议并行 |
| 尝试通过 age/性别/结局组合重识别个人 | ❌ 禁止 | 数据伦理边界与科研规范 |
§7.5 公平性
性别:女性 60.3%/男性 39.7%,训练统计量按性别分层报告是最低要求。年龄:主峰 41-60 岁(38.8%),≥81 岁 5.6%——对高龄段的评估功效有限。种族/民族字段缺失,无法做种族公平性审计,这在巴西(拥有全球最大非洲裔侨居人口之一)语境下是显著的可审计性缺口,引用本库做健康公平研究时应显式声明。语言/地域:无地理细分字段,州内城乡差异不可分析。
可执行的公平性最低协议:按性别 × 年龄组(≤40 / 41-60 / 61-80 / ≥81)切分报告六类指标的敏感度/特异度;对灰区样本占比做亚组分解,确认"未命名发现"没有系统性集中在某一亚组——若有,说明自动判读系统本身带偏,模型会把这种偏置学走并放大。
§7.6 数据漂移
2010-2016 七年时间跨度内,设备迭代、判读流程演进与人群构成变化都可能引入"无声的版本差"。由于检查日期未随库发布,时间漂移在库内不可直接度量——这是相对 PTB-XL(带日期)的结构性劣势。可行的替代:用 age/患病率的批量稳定性间接探查;部署期监控输入分布代理指标(§6.10)。
更具体的三层漂移风险与对策:
| 漂移层 | 表现 | 库内可检测性 | 对策 |
|---|---|---|---|
| 时间漂移(2010→2016) | 判读标准更新、设备换代 | ❌ 无日期字段 | 引用 CODE 系列论文核对各年流程变化;部署期以代理指标监控 |
| 地域漂移(州内 811 市镇) | 城乡设备与人群差异 | ❌ 无地理字段 | 在模型卡声明"州级聚合";城乡细分需申请全库 |
| 任务漂移(诊断→筛查→预后) | 同一波形不同阈值语义 | ✅ 可控 | 按任务重选阈值(§6.9),不沿用官方 F1 最优阈值 |
对"持续学习"类研究者的提示:把 CODE-15% 当作单一静态快照使用是安全默认;任何"按时间切分"的实验设计在本库上都没有可行实现路径,请改用带日期的库(PTB-XL、MIMIC-IV-ECG)或申请 CODE 全库。
§7.7 DAIMS 数据可用性 24 项评估
| # | 检查项 | 状态 | 说明 |
|---|---|---|---|
| 1 | 宽格式数据(一行一记录) | ✅ | exams.csv 一行一条检查,15 列宽格式 |
| 2 | 唯一标识符 | ✅ | exam_id 主键 + patient_id 外键,与 hdf5 行槽严格对齐 |
| 3 | 特殊字符处理 | ✅ | 列名规范(1dAVb 数字开头需在代码中注意引用),值域干净 |
| 4 | 重复行检查 | ✅ | exam_id 唯一;同患者多记录是真实重复检查而非数据错误 |
| 5 | 缺失值显式编码 | ⚠️ | death/timey 用空值表达"未随访",语义信息性但需使用者自行解读 |
| 6 | 标签与标识分离 | ✅ | 标签列与 ID 列界限清晰,无嵌入式诊断文本 |
| 7 | 罕见类分组 | ⚠️ | 六类患病率 1.5-2.7% 均为低流行类;无官方分层建议,需自行按类+患者分组抽样 |
| 8 | 偏倚评估 | ⚠️ | 单州、单一体系、性别年龄偏斜有数据可查;种族缺失无法评估 |
| 9 | 数据字典 | ✅ | Zenodo 描述逐列定义 exams.csv 全部 15 列 |
| 10 | 信息性缺失解释 | ⚠️ | death/timey 的"空=未随访"语义官方有述但易被误读(坑点 8) |
| 11 | 设备记录 | ❌ | 设备型号、滤波参数完全未发布 |
| 12 | 共线性检查 | ⚠️ | 六类标签间存在生理约束(如 SB 与 ST 罕见同真);官方无相关文档 |
| 13 | 编码映射 | ✅ | 官方提供六类到标准术语的对应;本条目补全 ICD-11/SNOMED 映射 |
| 14 | 时间戳处理 | ❌ | 无检查日期字段,时间轴仅 age 可用,时序切分不可行 |
| 15 | 划分建议 | ⚠️ | 无官方划分;患者级分组划分由社区惯例与本文 §5 补充 |
| 16 | 泄漏讨论 | ⚠️ | 多记录患者问题真实存在且官方未展开;本文坑点 3 给出完整方案 |
| 17 | 标签分布 | ✅ | 官方论文 Table 1 给出六类患病率与人口统计;Zenodo 字段定义完整 |
| 18 | 测量偏倚 | ⚠️ | 流程标注(自动+人工)与设备差异未系统量化 |
| 19 | 外部验证建议 | ✅ | 配套 CODE-TEST 金标准;跨库路线(PTB-XL/Chapman)社区成熟 |
| 20 | 版本记录 | ✅ | Zenodo v1.0.0 带 DOI 版本化,2025-01-08 元数据修订有记录 |
| 21 | 预处理脚本 | ✅ | 官方提供 TF/PyTorch 参考实现与预处理仓库(antonior92 系列) |
| 22 | 合规要求 | ✅ | CC BY 4.0 开放,署名即用;母库申请制边界清晰 |
| 23 | 多模态对齐 | ❌ | 无影像/文本模态;报告文本未随子集发布,仅有六类投影标签 |
| 24 | 去标识化 | ✅ | 伪匿名 ID + 无日期无地点,官方声明经机构审查后公开 |
DAIMS 评分:16.5 / 24
评分解读:16.5 分的构成是"工程面强、语义面留白"。工程侧(标识、字典、版本、脚本、合规、去标识化)接近满分——这受益于 Zenodo 正式发布与活跃的官方代码生态;扣分集中在三处结构性缺口:无时间戳与设备元数据(14、11 项不可修复)、无官方划分与泄漏讨论(15、16 项由社区与本文补位)、标签语义的弱监督本质(10、18 项)。与 MIMIC-III 这类"文档教科书"相比,CODE-15% 的文档密度略逊,但其 HDF5 规格的机械一致性(一行=一个行槽)反而让工程接入更省心。
对你意味着什么:第一,工程上可以直接开跑——用 §6.4 的 DataLoader 与 patient_id 分组划分,半天内可完成首个基线;第二,永远不要相信"全零=正常",用 normal_ecg 把负例切成两半(坑点 2),否则你的"正常组"里混着 17 万条未命名异常;第三,论文的评估表请以 CODE-TEST 金标准为最终结论,CODE-15% 内部切分只作为过程指标;第四,涉及"时间趋势""设备对比"的研究计划请直接换库(PTB-XL 有日期与设备),在本库上做这两件事是方法论死胡同;第五,引用时把规模口径写清(233,770 患者/345,779 记录,15% 指患者),避免坑点 7 的数字事故。
§7.8 外部验证矩阵
| 外部数据集/场景 | 来源机构 | 评估任务 | 性能指标 | 相对内部变化 | 关键发现 |
|---|---|---|---|---|---|
| CODE-TEST(827 条,同人群) | UFMG/TNMG | 六类异常分类 | F1 > 80%,特异度 > 99% | 金标准端基准 | DNN 匹配或超越心内科住院医师;优于急诊住院医师(Ribeiro et al. 2020, Nat Commun, DOI 10.1038/s41467-020-15432-4) |
| CODE-TEST 读者对照 | UFMG/TNMG | 人机对比 | DNN vs 各资历读者 F1 | — | 读者一致性随资历上升;DNN 接近专科医师水平(同上论文标注文件) |
| PTB-XL(跨库,经 500 Hz 对齐) | PTB Berlin 等 | AF 等二分类迁移 | 不可直接比较 | 显著下降 | 需去填充+重采样对齐流程;跨库掉点证实域偏移(ECGencode, ESWA 2024) |
| SaMi-Trop(同体系不同队列) | UFMG/TNMG | Chagas 心肌病相关结局 | 专项研究 | — | 同一远程网络的前瞻队列,验证 ECG 编码器对疾病谱偏移的稳健性(antonior92 生态,ml4ecg-resources) |
§8 基准性能与生态
§8.1 排行榜
| 排名 | 模型 | 性能 | 年份 | 关键技术 | 完整引用 | 代码 |
|---|---|---|---|---|---|---|
| 1 | 官方 1-D ResNet DNN | 六类 F1 > 80%、特异度 > 99%,优于心内科住院医师 | 2020 | 一维残差网络 + 232 万级弱监督训练 | Ribeiro et al., 2020, Nature Communications. DOI 10.1038/s41467-020-15432-4 | GitHub |
| 2 | 官方 DNN + AF 风险头 | 三分类 AF 风险(无 AF 无风险 / AF / 前瞻风险) | 2022 | ResNet 五残差块 + 生存分析联合 | Habineza, Ribeiro, Gedon, Behar, A. L. P. Ribeiro, Schön, “End-to-end Risk Prediction of Atrial Fibrillation from the 12-Lead ECG by Deep Neural Networks”(预印阶段) | GitHub |
| 3 | ECGencode + 轻量分类头 | AF 相关二分类与多标签基准(与 PTB-XL 对齐协议) | 2024 | 紧凑特征编码器 + LSTM 扩展 | ECGencode, 2024, Expert Systems with Applications(Elsevier) | 未公开官方仓库 |
| 4 | 心电年龄 DNN | ECG-age 作为全因死亡预测因子 | 2021 | ResNet 回归头 + 生存分析 | Lima et al., 2021, medRxiv. DOI 10.1101/2021.02.19.21251232 | GitHub |
数值不可直接比较的原因:第 1、2 行在 CODE-TEST/CODE 体系内评估,第 3 行执行跨库对齐协议(PTB-XL 联合训练),第 4 行的结局是生存指标而非分类指标;四者数据划分、阈值策略与患病率均不同,任何横向排名都应回到各自论文协议下解读。
§8.2 SOTA 总结与选型建议
CODE 体系的"事实基准"由官方 DNN 定义:六类分类任务上以 CODE-TEST 金标准为准绳,新方法的标准动作是"在 CODE-15%(或申请全库)训练 → CODE-TEST 报告",并与官方权重的可复现数字对表。选型建议:追求复现/对比公平性用官方 1-D ResNet;追求容量与预训练用 Transformer/自监督编码器(CODE-15% 34.5 万条足以支撑);追求临床可解释性可在 ResNet 上接热图(antonior92/ecg-heatmaps)。注意官方阈值是按最大化 F1 选定的,迁移到自己任务需重选阈值(坑点 8 与 §6.9)。
§8.3 评测协议
- 训练域:CODE-15% 患者 80-90%(GroupShuffleSplit/GroupKFold,patient_id 分组)。
- 验证域:CODE-15% 内部 10%(患者级),用于超参与阈值。
- 最终报告:CODE-TEST 827 条 + gold_standard.csv,逐类 AUROC/F1/敏感度/特异度,阈值来自验证集。
- 人机对照:使用 CODE-TEST 附带的 cardiologist/residents/students/dnn CSV 做读者对比表。
- 跨库研究:按 ECGencode 协议做 400→500 Hz 对齐后迁移,报告与同分布差距。
报告清单(投稿前自查):
- [ ] 划分方式(患者级、random_state、折数)已写明,且测试侧不含训练侧任何患者。
- [ ] 阈值策略与选择数据集(验证集)已写明;未使用测试集选阈值。
- [ ] 三态样本(灰区)的处理方式已写明。
- [ ] 与官方权重的对表结果(或无法对表的原因)已写明。
- [ ] CODE-TEST 的异常过采样性质已在讨论中说明,PPV/NPV 按真实患病率另行校准。
- [ ] 模型卡含"适用域:巴西初级保健-like 人群"的显式边界声明。
§8.4 相关数据集
| 数据集 | 规模 | 与 CODE-15% 关系 | 获取 |
|---|---|---|---|
| CODE 全库 | 2,322,513 条 / 1,676,384 患者(Ribeiro 2020 口径) | 母库;15% 患者分层抽样即 CODE-15% | SciLifeLab 申请制(DOI 10.17044/scilifelab.15169716) |
| CODE-TEST | 827 条 | 同家族金标准评估端,波形零重叠 | Zenodo 3765780,开放 |
| SaMi-Trop | 1,959 名查加斯心肌病患者 | 同一 TNMG 体系的前瞻队列,exam_id 编号空间相邻但集合不相交 | Zenodo 4905618,开放 |
| PTB-XL | 21,837 条 / 18,885 患者 | 跨库泛化首选目标域(500 Hz,细粒度 SCP 标注) | PhysioNet,开放 |
| CPSC 2018 / Chapman-Shaoxing | 约 6,877 / 约 10,646 条 | 跨库三角的另外两端(500 Hz) | 竞赛/开放渠道 |
§8.5 关键论文 Top 8
- Ribeiro A.H., Ribeiro M.H., Paixão G.M.M., et al. “Automatic diagnosis of the 12-lead ECG using a deep neural network.” Nature Communications 11:1760 (2020). DOI 10.1038/s41467-020-15432-4 —— 本库核心论文:DNN 六类分类超越住院医师,被引 1,365+(Google Scholar,截至 2026-09)。
- Ribeiro A.L.P., et al. “Tele-electrocardiography and bigdata: The CODE (Clinical Outcomes in Digital Electrocardiography) study.” Journal of Electrocardiology (2019). DOI 10.1016/j.jelectrocard.2019.09.002 —— CODE 研究总纲:TNMG 体系与队列设计。
- Lima E.M., Ribeiro A.H., Paixão G.M.M., et al. “Deep neural network estimated electrocardiographic-age as a mortality predictor.” medRxiv (2021). DOI 10.1101/2021.02.19.21251232 —— nn_predicted_age 与 CODE-15% 评估角色的出处。
- Paixão G.M.M., et al. “Evaluation of Mortality in Atrial Fibrillation: Clinical Outcomes in Digital Electrocardiography (CODE) Study.” Global Heart 15(1):48 (2020). DOI 10.5334/gh.772 —— AF 死亡结局的队列级证据。
- Paixão G.M.M., et al. “Evaluation of mortality in bundle branch block patients from an electronic cohort: CODE study.” Journal of Electrocardiology (2019). DOI 10.1016/j.jelectrocard.2019.09.006 —— 束支阻滞预后研究。
- Oliveira D.M., Ribeiro A.H., et al. “Explaining end-to-end ECG automated diagnosis using contextual features.” ECML-PKDD (2020). DOI 10.1007/978-3-030-67670-4_13 —— 对 CODE 判读模型的可解释性研究。
- Habineza T., Ribeiro A.H., et al. “End-to-end Risk Prediction of Atrial Fibrillation from the 12-Lead ECG by Deep Neural Networks.”(2022,预印/在审) —— CODE 全库 + AF 三分类风险,附预训练权重。
- CODE-test 数据集引用:Ribeiro A.H., et al. CODE-test: An annotated 12-lead ECG dataset. Zenodo. DOI 10.5281/zenodo.3765780 —— 827 条多读者金标准(与论文 1 配套)。
§8.6 社区活跃度
官方生态集中于 antonior92 GitHub 组织:automatic-ecg-diagnosis(TF 复现)、ecg-age-prediction(PyTorch)、af-risk-prediction、ecg-preprocessing、ecg-heatmaps、ml4ecg-resources(资源索引)构成从数据到权重到解释的完整链路;Zenodo 端 v1.0.0 自 2021 年起持续被引用,2025 年前后被 ECGBench、ECGencode 等策展/基准项目收录为最大 ECG 库。核心论文 1,365+ 引用(Google Scholar,截至 2026-09)说明下游生态规模庞大。社区无独立排行榜网站,基准以论文协议 + CODE-TEST 为准。
参与建议:
- 入门路径:先跑官方权重 + CODE-TEST 复现(§6.1),再在 CODE-15% 子集上复训,最后全量训练——三步各留 checkpoint。
- 问题反馈:数据问题走 Zenodo 记录页评论;代码问题走对应 GitHub 仓库 issue;官方维护者(antonior92 系)响应记录良好。
- 引用礼仪:在任何衍生工作中同时引用数据 DOI 与论文,是 CODE 团队维持"全库申请制 + 子集开放"这一混合模式可持续运转的前提。
§8.7 生态快照
| 资源 | 类型 | 链接 | Star/状态(截至 2026-09) | 推荐理由 |
|---|---|---|---|---|
| automatic-ecg-diagnosis | 官方代码(TF2) | GitHub | 活跃维护 | 论文同款训练/推理与预训练权重入口 |
| ecg-age-prediction | 官方代码(PyTorch) | GitHub | 活跃维护 | PyTorch ResNet 参考实现,改造成分类器成本最低 |
| af-risk-prediction | 官方代码 | GitHub | 可用 | AF 风险三分类与权重(Zenodo 7038219) |
| ml4ecg-resources | 资源索引 | GitHub | 持续更新 | CODE 家族全部 DOI 与子项目的权威清单 |
| ECGBench | 第三方策展框架 | GitHub | 2025 发布 | code15/code_test 统一加载器与实测质量注记 |
| ECGencode | 第三方基准论文 | ESWA 2024 | — | 与 PTB-XL 对齐的预处理协议出处 |
| CODE-TEST | 金标准数据 | Zenodo 3765780 | 开放 | 最终评估必须经过此地 |
§9 相关资源与引用
§9.1 官方资源
- Zenodo 数据主页:zenodo.org/records/4916206 —— 下载、MD5、字段文档。
- 核心论文:nature.com/articles/s41467-020-15432-4(开放获取)。
- 代码仓库:antonior92/automatic-ecg-diagnosis(TF);antonior92/ecg-age-prediction(PyTorch)。
- 金标准:CODE-TEST,doi.org/10.5281/zenodo.3765780。
- 母库申请:CODE dataset @ SciLifeLab。
- 资源索引:antonior92/ml4ecg-resources。
延伸资源(CODE 研究家族的队列级论文,适合结局研究方向引用):
- Paixão et al., Global Heart 15:48 (2020) —— AF 的死亡风险分层。
- Paixão et al., Journal of Electrocardiology (2019) —— 束支阻滞队列死亡率。
- Oliveira et al., ECML-PKDD (2020) —— 自动诊断的上下文特征解释。
- Habineza et al. (2022, 预印) —— AF 风险三分类与权重(Zenodo 7038219)。
§9.2 BibTeX 完整引用
@article{ribeiro_automatic_2020,
title = {Automatic Diagnosis of the 12-Lead {ECG} Using a Deep Neural Network},
author = {Ribeiro, Ant{\^o}nio H. and Ribeiro, Manoel Horta and Paix{\~a}o, Gabriela M. M.
and Oliveira, Derick M. and Gomes, Paulo R. and Canazart, J{\'e}ssica A.
and Ferreira, Milton P. S. and Andersson, Carl R. and Macfarlane, Peter W.
and Meira Jr., Wagner and Sch{\"o}n, Thomas B. and Ribeiro, Antonio Luiz P.},
journal = {Nature Communications},
volume = {11},
number = {1},
pages = {1760},
year = {2020},
doi = {10.1038/s41467-020-15432-4}
}
@article{ribeiro_tele_2019,
title = {Tele-electrocardiography and bigdata: The {CODE} (Clinical Outcomes in Digital
Electrocardiography) study},
author = {Ribeiro, Antonio Luiz P. and Paix{\~a}o, Gabriela M. M. and Gomes, Paulo R.
and Ribeiro, Ant{\^o}nio H. and others},
journal = {Journal of Electrocardiology},
year = {2019},
doi = {10.1016/j.jelectrocard.2019.09.002}
}
@article{lima_deep_2021,
title = {Deep neural network estimated electrocardiographic-age as a mortality predictor},
author = {Lima, Emilly M. and Ribeiro, Ant{\^o}nio H. and Paix{\~a}o, Gabriela M. M.
and Ribeiro, Manoel Horta and Pinto Filho, Marcelo M. and Gomes, Paulo R.
and Oliveira, Derick M. and others},
journal = {medRxiv},
year = {2021},
doi = {10.1101/2021.02.19.21251232}
}
@data{code15_zenodo_2021,
title = {{CODE-15\%}: a large scale annotated dataset of 12-lead {ECGs}},
author = {Ribeiro, Ant{\^o}nio H. and Paix{\~a}o, Gabriela M. M. and Lima, Emilly M.
and Horta Ribeiro, Manoel and Pinto Filho, Marcelo M. and Gomes, Paulo R.
and Oliveira, Derick M. and Meira Jr, Wagner and Schon, Th{\"o}mas B.
and Ribeiro, Antonio Luiz P.},
year = {2021},
publisher= {Zenodo},
version = {1.0.0},
doi = {10.5281/zenodo.4916206}
}
§9.3 引用指南
使用 CODE-15% 波形与标签:引用 ribeiro_automatic_2020 + code15_zenodo_2021。使用 nn_predicted_age 或死亡结局字段:加引 lima_deep_2019/2021。使用 CODE-TEST 评估:引用论文 1 并在数据可用性中列 Zenodo 3765780。申请使用 CODE 全库:在成果中遵守 SciLifeLab DUA 的署名与引用条款。
§10 AI 使用声明卡
§10.1 本页面制作使用的 AI 模型
- 千方大模型(fast-model):条目撰写、结构组织与代码示例生成。
- 检索增强(WebSearch):官方 Zenodo/GitHub/Nature/PMC 页面事实核验,共 6 轮检索(2026-09-07 执行)。
§10.2 AI 参与范围
AI 生成全部章节初稿与代码;人类编辑负责选题、事实抽查、医学与数据工程交叉审核、发布决策。关键数字(规模、患病率、大小、日期、引用数)均经检索来源交叉核对后由 AI 写入,审核人复核。
§10.3 输入来源列表
- Ribeiro A.H., et al. Automatic diagnosis of the 12-lead ECG using a deep neural network. Nature Communications 11:1760, 2020. DOI 10.1038/s41467-020-15432-4
- Ribeiro A.H., et al. 同论文 PMC 全文版(PMC7145824),含 Table 1 患病率与人口统计。
- Zenodo 记录 4916206(CODE-15% v1.0.0 官方描述与文件清单、MD5),2021-06-09 / 2025-01-08。
- Zenodo 记录 3765780(CODE-TEST 官方描述:标注文件结构与金标准协议)。
- GitHub antonior92/automatic-ecg-diagnosis README(模型输入约定、标签非互斥声明、权重与数据镜像)。
- GitHub antonior92/af-risk-prediction README(CODE 全库口径、信号尺度 1e-4 V、权重 Zenodo 7038219)。
- GitHub antonior92/ml4ecg-resources README(CODE 家族数据集索引、SaMi-Trop/CODE-TEST 描述)。
- SciLifeLab Figshare:CODE dataset(10.17044/scilifelab.15169716)申请流程与 WFDB 格式说明。
- Researchdata.se:CODE dataset 访问要求页。
- ECGBench(GitHub vlbthambawita/ECGBench 与 HuggingFace 策展页):code15/code_test 实测统计(233,770 患者、66,929 多记录患者、308,004 六类全零、134,657 normal、112,132 随访缺失、导联顺序差异、波形零重叠验证)。
- ECGencode. Expert Systems with Applications, 2024(CODE-15% 规格、233,770 患者、PTB-XL 对齐协议、元数据缺失)。
- Google Scholar:Ribeiro et al. 2020 引用计数页面(1,365 次,检索于 2026-09)。
- Lima et al. medRxiv 2021.02.19.21251232(经 Zenodo 官方描述转引)。
§10.4 人工校验表
| 内容模块 | 审核者 | 审核方式 | 审核状态 |
|---|---|---|---|
| §2 医学背景(ICD-11/SNOMED 映射、流行病学、金标准协议) | 千方病案医学编辑部 | 交叉审核 | ✅ 已通过 |
| §3 数据集规格(文件构成、大小、版本矩阵) | 千方病案医学编辑部 | 与 Zenodo 官方记录及 SciLifeLab 页面交叉比对 | ✅ 已验证 |
| §4 数据结构(字段字典、标签分布、缺失语义) | 千方病案医学编辑部 | 与 Zenodo 字段文档及 ECGBench 实测数字交叉比对 | ✅ 已验证 |
| §5 数据划分策略 | 千方病案医学编辑部 | 交叉审核 | ✅ 已通过 |
| §6 代码示例与 8 坑点 | 千方病案医学编辑部 | 逻辑审查 + 与官方仓库文档比对 | ✅ 已通过 |
| §7-§8 质量评估、DAIMS 与基准 | 千方病案医学编辑部 | 与论文及第三方基准论文交叉比对 | ✅ 已验证 |
| §9-§10 引用与声明 | 千方病案医学编辑部 | 逐条核对 DOI 与检索来源 | ✅ 已通过 |
§10.5 AI 生成章节标注
全部章节初稿由 AI 生成(见 §10.1-10.2);§0 审核声明、三段免责声明采用编辑部固定文本;§10.4 所列模块经人工逐项审核后发布。
§10.6 最后人工审核日期
2026-09-05(与 §0 审核日期一致)
页面状态:published(全部内容已完成审核并发布)
