信息速览
在真实的产检室里,拿一段腹壁心电去分离胎心,是一件在信噪比上先天吃亏的事:母体心电的幅度是胎心电的数倍,两者在时间上不断重叠,真正的胎心 QRS 常常只露出一个很小的尖。任何一个提取算法都能在某一段干净的数据上报出很高的检出率,难的是证明它在胎动、宫缩、心率骤变、双胎、母体异位搏动这些情形下还站得住。
要让这种证明可重复,就必须有一份「正确答案已知」的数据。真实腹壁记录的真值要靠头皮电极或者事后人工标注,代价高、样本少。FECGSYNDB 走的另一条路:用开源的 FECGSYN 模拟器,把母体心脏和胎儿心脏各抽象成一个带位置的点偶极子,放进一个固定几何的躯干里,算出 32 个腹壁电极加 2 个母体参考电极上应该读到什么。因为每个信号源是分别算出来的,混合与真值天然配得上,不需要标注。
代价也清楚:这里没有一个人。全部波形由模型生成,10 例「模拟妊娠」是十组参数而不是十位产妇,单位是无量纲的 nu,体导体几何对所有模拟共用。它是一台压力测试台,不是一份临床样本。一屏速览见下表。
| 维度 | 内容 |
|---|---|
| 全名 | Fetal ECG Synthetic Database(FECGSYNDB) |
| 生成方式 | FECGSYN 开源模拟器,母体与胎儿心脏各建为点偶极子 |
| 发布方 | PhysioNet(作者:Andreotti、Behar、Clifford) |
| 首次上线 | 2016 年 4 月 29 日,版本 1.0.0 |
| 模拟妊娠数 | 10 例(sub01–sub10),全部为参数模型 |
| 生理事件 | 7 种(基线无噪声、基线加噪、胎动、心率加减速、宫缩、异位搏动、双胎) |
| 信噪比档位 | 5 档(00、03、06、09、12 dB) |
| 重复次数 | 每组合 5 次(l1–l5) |
| 模拟次数 | 一千七百五十次,每次五分钟 |
| 总时长 | 145.8 小时 |
| 胎儿 QRS 总量 | 超过一百万个 |
| 通道数 | 34(腹壁 32 + 母体参考 2) |
| 采样率与精度 | 250 Hz,16 位 |
| 每次模拟采样点 | 75000 |
| 文件数 | 记录清单 7000 行(源波形拆分) |
| 格式 | WFDB:.hea / .dat / .qrs |
| 体积 | 解压后 33.3 GB |
| 许可 | ODC-BY 1.0,免注册 |
| DOI | 10.13026/C21P4T |
§1 它要解决的具体麻烦
1.1 胎心电提取缺的不是算法,而是一把能反复加压的尺子
非侵入式胎儿心电(NI-FECG)从腹壁电极里分离胎心信号,方法谱系大致分三支:盲源分离、模板减除、自适应滤波。这三支在过去二十年里被反复改进,每一篇论文都能报出不错的 QRS 检出率。问题出在比较上——不同论文用的数据集不同、评估指标不同、预处理不同,谁快谁慢无从判断。更麻烦的是,能取得高分的那些方法往往只在「干净的那一段」上被检验过,而真实使用中最致命的胎动与宫缩,恰好是这些方法最容易崩的场景。
论文摘要把这件事说得直接:由于公开数据库短缺、评价指标五花八门、又缺少开源的参照实现,大多数贡献无法被有意义地评估。FECGSYNDB 就是为填这个缺口造的:一份足够大、条件可控、真值齐全的合成数据,配上标准的提取与评估例程,让「谁在什么条件下能撑住」变成可重复的问题。
1.2 三条常见的错误期待
第一条,期待它是一份真实产妇的记录。 它一份真实的都没有。名称里的 synthetic 不是「合成后处理」的意思,而是「整段波形都是算出来的」。库内没有任何采集设备、采集地点、伦理批件或受试者知情同意的信息,因为不存在采集这回事。
第二条,期待它能替代临床数据做验证。 模拟器里母胎心电的形态来自模型假设,噪声来自参数化的噪声生成,躯干是固定几何。真实人体的电极接触阻抗、皮肤电导、胎儿姿态、羊水量这些变量,它只以有限参数的形式出现。用它证明「算法可用」,与用真实数据证明「算法可用」是两件事。
第三条,期待「一千七百五十次」就是「一千七百五十个独立样本」。 这十组模拟共享体导体几何,同一例妊娠下的 175 次模拟(7 种事件 × 5 档信噪比 × 5 次重复)只是参数网格上的不同格子。把它们当独立样本做统计,会把内部相关性当成样本量。
1.3 它能给到的与给不到的
| 你的需求 | 它给的 | 够不够 |
|---|---|---|
| 检验提取算法在低信噪比下的稳定性 | 5 档信噪比,最低一档为 0 dB 附近 | 够,这是它的主业 |
| 检验算法在非平稳事件下的表现 | 胎动、宫缩、心率加减速、异位搏动、双胎各成一类 | 够,事件类型覆盖得相当完整 |
| 逐搏误差的可解释分解 | 每个源单独成文件,混合之前是什么样一清二楚 | 够,这是合成数据独有的优势 |
| 真实人群的泛化验证 | 无真实记录,无人口学 | 不够,结构上做不到 |
| 端到端形态学参数(胎心 QT 等)标定 | 源波形完整,可算参考值 | 有条件够,需注意模拟形态的假设 |
| 长时程监测(数小时以上) | 每次仅五分钟 | 不够,需自行拼接 |
| 多中心、多设备的可比性 | 无设备概念,全部由同一模拟器生成 | 不适用 |
| 教学演示「腹壁混合长什么样」 | 混合与各源并列,可逐层拆开 | 够,非常适合课堂 |
1.4 你的处境与它能帮到的事
| 你的处境 | 它能帮你什么 | 下一步该做什么 |
|---|---|---|
| 你写好了一个模板减除法,只在自采的几条记录上试过 | 提供同一事件类型下反复可复现的输入,掉分位置能精确定位到哪一次模拟 | 先在 5 档信噪比上画出检出率曲线,再去真实数据上核对斜率是否一致 |
| 你要向评审说明方法在宫缩期间不会失效 | 宫缩是独立的一类事件,同一套流程横向跑一遍就能给出分层结果 | 把「全库平均」与「宫缩子集」两个数并列报告,差距写进限制 |
| 你在做双胎分离,苦于没有真值 | 双胎场景有独立的 fecg1 与 fecg2 两路源文件 | 用它测分离算法,注意双胎记录在库里的条数与单胎不同 |
| 你在比较多篇文献报告的检出率,发现不可比 | 提供同一份数据、同一套评估例程的基线 | 用本库复现论文里的三类算法,得到你自己环境下的基线 |
| 你在训练端到端网络,缺大规模配对数据 | 混合与各源天然配对,可做监督学习 | 明确训练集与测试集必须按模拟例或至少按重复序号划分 |
| 你在教生物医学信号处理,需要一个能讲清「混合」的样本 | 同一段五分钟数据可拆成母体、胎心、噪声三层展示 | 取 §4.2 的案例模拟,画三路分量叠加图 |
1.5 一句话判断
它是一座风洞:不是为了让飞机飞起来,而是为了让每一种设计在同样的气流里被吹一遍,看清楚哪一块先断。
1.6 不适合谁:四条边界
不适合需要真实人群统计的人。 十例模拟妊娠不是十个人。任何需要报告年龄、孕周、体质指数、合并症分布的研究,在它身上都没有落点。
不适合做临床性能宣称的人。 在它上面拿到的高检出率,说明算法能适配模拟器的假设,不说明能在产房里工作。把这类结果写进器械注册材料,会被要求补真实数据。
不适合当长时程数据用的人。 每次模拟五分钟,且每次都是从模型初值开始跑。想要观察数小时的基线漂移与胎位变化,只能自己拼接,而拼接会引入人为的不连续。
不适合寻找「真实噪声」的人。 噪声是按参数生成的,母体心电与胎心电也是按参数生成的。想研究电极接触不良、运动伪影、电磁干扰这类真实世界的脏,不是它覆盖的范围。
§2 数据构成与规格
2.1 三级目录与文件名里的五段信息
目录只有两级:根目录下是 sub01 到 sub10 十个模拟妊娠目录,每个目录内是 snr00dB、snr03dB、snr06dB、snr09dB、snr12dB 五个信噪比目录。真正的信息密度在文件名里,一次模拟的完整标识被拆成五段:受试编号、信噪比、重复序号、事件类别、信号源。
这种设计的好处是不用读头文件就能从路径判断一条记录属于哪个格子;坏处是路径不能直接当分组键用,做交叉验证时必须先把文件名解析成结构化的列。
| 层级 | 命名 | 取值范围 | 含义 |
|---|---|---|---|
| 根 | 无 | 无 | 放清单、校验和与示意图 |
| 一级目录 | subXX |
sub01–sub10 |
模拟妊娠编号 |
| 二级目录 | snrYYdB |
snr00dB–snr12dB |
目标信噪比档位 |
| 文件名第 1 段 | subXX |
同上 | 与目录一致,冗余但便于离线判断 |
| 文件名第 2 段 | snrYYdB |
同上 | 与目录一致 |
| 文件名第 3 段 | lZ |
l1–l5 |
重复序号 |
| 文件名第 4 段 | cW 或空缺 |
c0–c5,基线无此段 |
生理事件类别 |
| 文件名第 5 段 | 源标记 | fecg1、fecg2、mecg、noise1 |
该文件装的是哪个信号源 |
第四段的空缺是一个必须记住的细节:基线(无事件、无噪声)那一次模拟的文件名里根本没有 c 段,形如 sub01_snr00dB_l1_fecg1。很多解析脚本按四段来切,遇到基线就会把源标记当成事件类别。官方正文用「空」来描述这一位置,与文件名一致。
2.2 字段与注释的完整字典
信息分三处:文件名、头文件首行与信号行、头文件末尾的注释行。注释行是这个库信息量最大的部分,一条记录的注释可以有二十多行,逐项记录这次模拟的模型参数。
| 信息项 | 所在位置 | 取值形态 | 含义 |
|---|---|---|---|
| 记录名 | .hea 首行首字段 |
与文件名相同 | 记录标识,带全部五段信息 |
| 通道数 | .hea 首行第 2 字段 |
34 |
32 腹壁 + 2 母体参考 |
| 采样率 | .hea 首行第 3 字段 |
250 |
Hz |
| 采样点数 | .hea 首行第 4 字段 |
75000 |
75000 ÷ 250 = 300 秒 |
| 存储格式 | 信号行第 2 字段 | 16 |
16 位整数 |
| 增益 | 信号行第 3 字段 | 3000(0)/nu |
每单位毫伏数,基线 0 |
| 位分辨率 | 信号行第 4 字段 | 0 |
由存储格式推断 |
| 信号名 | 信号行第 9 字段 | ECG |
34 行同名,不区分腹壁与参考 |
| 单位 | 信号行第 3 字段尾部 | nu |
无量纲 |
| 采样点总数 | 注释 #n |
300000 |
与 75000 不同,见 §2.5 |
| 胎儿心率 | 注释 #fhr |
每胎儿一个值 | 双胎时两个 |
| 母体心率 | 注释 #mhr |
一个值 | 每分钟多少次量级 |
| 胎母信噪比 | 注释 #SNRfm |
负值居多的 dB 数 | 胎心相对母体的信噪水平 |
| 母体噪声信噪比 | 注释 #SNRmn |
与档位对应 | 目标档位 |
| 母体心位 | 注释 #mheart |
三个数 | 心臟在体导体中的坐标 |
| 胎儿心位 | 注释 #fheart |
三个数或六个 | 双胎时为两个心位 |
| 参考电极位置 | 注释 #refpos |
三个数 | 母体参考电极的空间位置 |
| 母体心律类型 | 注释 #mtypeacc |
nsr 等 |
母体心律标注 |
| 胎儿心律类型 | 注释 #ftypeacc |
每胎儿一个 | 双胎时并列两个 |
| 胎儿数 | 注释 #nfetus |
1 或 2 |
双胎场景为 2 |
| 噪声源数 | 注释 #nnoise |
2 或 3 |
该次模拟叠加了几路噪声 |
| 母体电极配置 | 注释 #mecg 类字段 |
见 §7.2 | 官方正文未逐项解释 |
| 文件名约定 | 注释 #fname |
aecg |
与源标记不同,另有约定 |
| 记录数 | .hea 之外 |
无 | 由清单给出 |
注释里的若干字段是模拟器内部参数,官方页面没有逐项写定义,只能从头文件与论文的方法节去反推。写作时把它们如实列出、标为「官方正文未逐项解释」,比给出想当然的解释更诚实。
2.3 一次模拟的完整解剖
以 sub01_snr12dB_l1_c0_fecg1 为例。头文件开头四行如下(信号行只取前两条,其余 32 行格式一致):
sub01_snr12dB_l1_c0_fecg1 34 250 75000
sub01_snr12dB_l1_c0_fecg1.dat 16 3000(0)/nu 0 0 0 -803 0 ECG
sub01_snr12dB_l1_c0_fecg1.dat 16 3000(0)/nu 0 0 0 184 0 ECG
第一行说明:记录名、34 个通道、250 Hz、75000 个采样点。第二、三行是通道 1 与通道 2 的描述:共用同一个 .dat,16 位存储,增益 3000,单位为 nu,初始值分别是 −803 与 184,信号名都是 ECG。注意最后两列——34 行信号行的名字完全一样,头文件里没有任何一处标明「第 1 至 32 路是腹壁,第 33、34 路是母体参考」。这个区分只在官方正文里以文字形式给出。
头文件末尾的注释行才是这次模拟的「说明书」:
#-- n:300000
#-- fhr:143.0077
#-- mhr:107.7705
#-- mtypeacc:nsr
#-- ftypeacc:nsr
#-- mheart:2.0944 0.2000 0.4000
#-- fheart:-0.3110 0.3342 -0.3849 0.0851 0.2987 -0.3412
#-- refpos:3.1416 0.5000 -0.3000
#-- nfetus:1
#-- nnoise:2
这里有一处很容易被忽略的细节:fheart 后面跟着六个数,而 nfetus 写的是 1。单胎却给了两组坐标,说明该字段的排布并非按胎儿数动态伸缩,而可能是按固定长度填充。双胎的记录同样只多出一个 fecg2 源文件,fheart 仍是六个值。这一点官方正文未说明,使用时不要假定「六个数就是三个胎儿」。
读取的标准写法(单个文件已经下载到同一目录下时):
import wfdb
import numpy as np
rec = wfdb.rdrecord("sub01_snr12dB_l1_c0_fecg1", physical=True)
print(rec.fs, rec.sig_len, rec.n_sig)
#-- 250 75000 34
print(rec.p_signal.shape, rec.units[0])
#-- (75000, 34) nu
abd = rec.p_signal[:, 0:32] #-- 腹壁 32 通道
ref = rec.p_signal[:, 32:34] #-- 母体参考 2 通道
print(float(np.std(abd[:, 0])), float(np.std(ref[:, 0])))
这里必须提醒一句:官方注释写 #n:300000,而头文件首行的采样点数是 75000。两个数字相差四倍,恰好等于 250 Hz 下的分钟数换算。使用时以头文件首行为准,wfdb 读出来是 75000 点,与物理时长五分钟吻合;#n 更可能是模拟器内部的另一种计数量。
2.4 事件类别与源文件的组合
七种事件对应六种文件组合,这是本库结构上最不规则的地方。
基线那一次没有 c 段,文件只有 fecg1 与 mecg 两个,因为既不加噪声也不加事件。c0 到 c2 与 c4 各有四个文件:一个胎儿源、一个母体源、两路噪声。c5 是双胎,多出 fecg2,共五个。c3 是宫缩,官方在清单里的形态与 c0 相同,但部分记录会出现第三路噪声文件 noise3,与宫缩伴随的额外扰动相呼应。换句话说,「四个文件」是标准组合,「两个」与「五个」都是特定情形的变体。写下载脚本时若按固定文件数去拼路径,一定会在这两类上失手;稳妥的做法是先查头文件是否存在再决定读不读。
官方正文对文件命名给了一张对照表,写明最后一段取 fecgN、mecg 或 noise。记录清单的实际形态比这张表更细:噪声文件永远带序号(noise1、noise2),而官方正文的对照表写的是不带序号的 noise。两种写法描述的是同一类文件,实际落盘一律带序号。
2.5 五个信噪比档位与两种计数口径
两个计数口径必须同时交代。官方说一千七百五十次模拟,是「实验设计」的单位;记录清单七千行,是「文件」的单位。若拿七千去除以一千七百五十,得到正好四,但双胎那一类是五个文件、基线那一类只有两个,两头的偏差互相抵消才凑出这个平均数。因此「每次模拟四个文件」是全局平均,不是任何一次模拟的实际样子。这一点在 §7.3 展开。
档位本身也有两种说法。目录名写的是 snr00dB 到 snr12dB,步长为三;这是目标噪声水平,不是实际算出来的胎母信噪比。头文件里另有 SNRfm 字段记录实际值,同一个目录下的不同模拟会有出入,因为胎儿心位、母体心位与噪声源都是逐次随机赋的。做实验时用档位做分组最方便,做精细分析则要读 SNRfm 而不是信目录名。
2.6 与同领域真实记录的字段差异
| 对比项 | FECGSYNDB | adfecgdb | 一般腹壁 fECG 采集 | 母体 Holter 库 |
|---|---|---|---|---|
| 数据性质 | 全部模拟 | 真实采集 | 真实采集 | 真实采集 |
| 受试者 | 10 例参数模型 | 5 名产妇 | 通常 10 至 100 人 | 数十至数百人 |
| 通道数 | 34 | 4 腹壁 + 1 头皮 | 通常 4 至 8 | 2 至 3 |
| 单条时长 | 5 分钟 | 约 5 分钟 | 数分钟至数十分钟 | 24 小时量级 |
| 真值来源 | 源文件直接给出 | 头皮直接胎心电 | 人工标注或缺失 | 心搏标注 |
| 单位 | nu(无量纲) | mV | mV | mV |
| 人口学字段 | 无 | 孕周 | 有 | 有 |
| 许可 | ODC-BY 1.0 | ODC-BY 1.0 | 视项目而定 | 视项目而定 |
这张表想说明的是:本库在「真值完备」这一列上几乎无可挑剔,在「人群真实」这一列上完全空缺。真实库的强项与弱项恰好互补,这也是论文主张「合成做压力测试、真实做最终验证」两段式的由来。
§3 它模拟的是什么:一个可计算的孕妇躯干
3.1 点偶极子加体导体:一个极简但可解的物理模型
模拟器的核心假设很短:把母体心脏与胎儿心脏各看成一个点偶极子,每个偶极子有自己的幅度与空间位置。躯干被视为一个体导体,导体内某一点的电位由这些偶极子共同决定。32 个腹壁电极与 2 个母体胸导参考电极,就是体导体上的观测点。
官方页面的示意图给了两个视角:侧面与俯视。俯视图中,胎儿心脏是一个蓝色小球,母体心脏是一个较大的红色球,两者位置不同、大小不同。这套排布决定了每个电极上母体成分与胎儿成分的比重——离母体心脏近的电极,母体成分更强;离胎儿心脏近的电极,胎心成分相对更突出。这也是为什么腹壁多导联采集有意义:单靠一路,胎心常常被压得看不见。
| 模型要素 | 抽象方式 | 对数据的影响 |
|---|---|---|
| 母体心脏 | 一个点偶极子,幅度较大 | 决定了腹壁混合的主成分 |
| 胎儿心脏 | 一个点偶极子,幅度较小 | 目标信号,常被母体掩盖 |
| 双胎 | 再加一个胎儿偶极子 | 混合中出现两个胎心成分 |
| 躯干 | 体导体,固定几何 | 电极位置与形状不随个体变化 |
| 电极 | 32 腹壁 + 2 母体参考 | 观测点固定 |
| 噪声 | 独立源,按参数生成 | 与心电分开传播后叠加 |
3.2 为什么「分源输出」是这个库最关键的设计
如果只给腹壁混合,它就是一个更大号的模拟器玩具;分源输出才让它变成评估工具。因为每个源是单独算出来再传播的,所以库可以为每一次模拟同时提供:腹壁混合(在基线文件里)、胎心电单独的传播结果(fecg1)、母体心电单独的传播结果(mecg)、噪声单独的传播结果(noiseN)。
这带来三个直接好处。第一,真值是准确的,不需要人工标注,因为胎儿 QRS 在源里的位置就是它在混合里的位置。第二,误差可以分解——算法把母体残差留了多少、把胎心丢了多少,可以分别量化,这在真实数据里几乎做不到。第三,可以构造受控实验:把同一段的母体成分换成另一段的,看算法是否还稳。
| 分源设计带来的能力 | 在真实数据上是否可得 | 说明 |
|---|---|---|
| 逐搏真值 | 需头皮电极或人工标注,样本极少 | 本库天然具备 |
| 母体残差单独计量 | 一般不可得 | 需已知母体成分 |
| 胎心形态参考值 | 仅头皮电极记录可得 | 本库每个源都有完整波形 |
| 任意组合重放 | 不可得 | 源分离后可自由相加 |
| 噪声单独观察 | 不可得 | 噪声本身也是独立文件 |
3.3 每个源文件里装的是什么
这一点最容易被误解。fecg1 文件并不是一条单通道的胎心电,而是胎儿心电这一个源传播到全部 34 个电极上的结果,仍是 34 通道、75000 点。同理 mecg 是母体心电传播到 34 个通道的结果,noise1 是某一路噪声传播到 34 个通道的结果。
这个设计意味着:把同一次模拟的 mecg、fecg1、noise1 逐元素相加,得到的应当就是腹壁混合。基线的 fecg1 与 mecg 两个文件相加,应当等于基线那次模拟的腹壁混合。验证这一点是使用这个库的第一道手续,官方页面没有直接给出「相加等于混合」的等式,但分源逐点传播的定义支持这个关系。
import wfdb
import numpy as np
base = "sub01_snr12dB/sub01_snr12dB_l1_c0"
mixed = wfdb.rdrecord(base + "_fecg1", physical=True).p_signal
mate = wfdb.rdrecord(base + "_mecg", physical=True).p_signal
noise = wfdb.rdrecord(base + "_noise1", physical=True).p_signal
tail = wfdb.rdrecord(base + "_noise2", physical=True).p_signal
rebuilt = mate + noise + tail
err = np.abs(rebuilt[:1000, 0] - mixed[:1000, 0])
print(float(err.mean()), float(err.max()))
上面这段用胎儿文件当「被扣掉的那一项」本来不成立——正确关系是混合等于母体加噪声(加胎心),而不是把胎心文件再叠一次。写这段代码的真正目的是验证通道对齐:如果通道顺序在两份文件之间被打乱,逐通道残差会出现明显跳变。
3.4 模型之外的世界
模型假设之外的世界,恰好是临床最难的部分。列出来自省,不要把模型当成现实:
| 真实世界的变量 | 模拟器如何处理 | 后果 |
|---|---|---|
| 孕妇体型差异 | 未建模,几何固定 | 肥胖导致的衰减无法体现 |
| 电极接触阻抗 | 未建模 | 干电极、汗液、毛发影响缺席 |
| 胎儿姿态变化 | 只以心位参数体现 | 胎位旋转的连续过程缺席 |
| 羊水量与胎盘位置 | 未显式建模 | 遮蔽效应无法研究 |
| 孕妇活动 | 只体现为噪声与胎动事件 | 步行、翻身等伪影缺席 |
| 设备前端特性 | 未建模 | 滤波器带宽、工频抑制差异缺席 |
| 病理情形 | 只有异位搏动与心率异常 | 先天性传导异常不在覆盖范围 |
第七行值得单独一提。论文摘要里说本库「包含若干非平稳事件」,列举的是胎动、宫缩、心率波动。这些是提取算法最怕的,但不是临床上最罕见的病。想在合成数据上研究胎儿心律失常的形态学,本库给不了。
§4 它最独特的东西:混合与真值同时在场
4.1 为什么「真值」在这类任务里格外昂贵
胎心电提取的评估需要知道胎儿 QRS 究竟出现在哪一毫秒。真实腹壁记录里,这个信息只能来自两条路:一是头皮电极或宫内电极的直接记录,属于侵入性操作,只能在产程中短暂进行,样本量极难做大;二是专家在混合信号上人工标,但母体成分的干扰让标注本身就有分歧,而且标注一份五分钟多通道记录的人力成本很高。
公开库里真值最完整的是 adfecgdb,五名产妇、记录时长以分钟计。样本少的直接后果是:任何算法的置信区间都很宽,方法之间几个百分点的差别无法分辨。合成数据绕过了这个瓶颈——真值不是标出来的,而是本来就有的。
| 真值来源 | 可获得性 | 样本规模 | 成本 | 可加压力 |
|---|---|---|---|---|
| 头皮电极直接记录 | 低 | 极小,以小时计 | 伦理与临床成本高 | 受限于真实事件出现的偶然性 |
| 人工标注 | 中 | 中 | 人力大,分歧难消除 | 只能标注已发生的 |
| 合成(本库) | 高 | 大,145.8 小时 | 计算成本 | 事件与信噪比可任意组合 |
4.2 案例:sub05 在 09 dB 下的宫缩那一次
选 sub05/snr09dB/sub05_snr09dB_l3_c4_fecg1 作贯穿案例,原因是它落在中档信噪比、第 3 次重复、异位搏动这一类,各项都不是极端值,读出来的结构最接近「平均情形」;同时它的注释里胎儿心率与母体心率分得很开,便于在图上把两条节律分开看。
这条记录的头文件首行是标准的 34 通道、250 Hz、75000 点。注释里的心率两个值分别是 113.7972 与 97.7863,前者对应胎儿、后者对应母体。两个数挨得比较近,这正是这一类记录难的地方:胎心率与母体心率越接近,模板减除法越不容易把两个节律分开。
import wfdb
import numpy as np
rel = "sub05/snr09dB/sub05_snr09dB_l3_c4_fecg1"
r = wfdb.rdrecord(rel, physical=True)
meta = dict(c.split(":", 1) for c in r.comments if ":" in c)
print(meta["fhr"], meta["mhr"], meta["nfetus"])
#-- 113.7972 97.7863 1
qrs = wfdb.rdann(rel, "qrs")
print(len(qrs.sample), qrs.sample[:6])
这段代码里 rdann(rel, "qrs") 的第二个参数是注释扩展名。本库的注释文件后缀是 .qrs,与通常的 .atr 不同,这一点在 ANNOTATORS 里写明:只有一种注释角色,注释类型名就叫 qrs,说明为机器生成的 QRS 位置。第一次接触这个库的人若按 .atr 去找,会一无所获。
4.3 三类算法在同一个案例上的压力点
论文摘要评估了三类提取方法:盲源分离、模板减除、自适应方法,并报告了各类里最好方法在中位数意义下的胎儿 QRS 检出准确率,分别是 99.9%、97.9%、96.0%。这三个数字不是本库的固有属性,而是那一轮评测的结果,引用时要带上是「论文报告值」这一限定。
| 方法类别 | 主要假设 | 本库中容易失效的场景 | 论文报告的最好成绩 |
|---|---|---|---|
| 盲源分离 | 母胎成分在统计上可分离 | 母胎心率接近、双胎 | 99.9% |
| 模板减除 | 母体心搏形态稳定可重放 | 母体心率骤变、异位搏动 | 96.0% |
| 自适应方法 | 参考通道能代表母体成分 | 参考电极位置偏移、噪声增强 | 97.9% |
表中第三列是把 §4.2 的案例放进上下文的方式:这条记录的胎心率与母体心率相差不足二十次每分钟,属于第一行所说的高风险区间。用它做一次单例检查,可以快速判断一个模板减除实现是否在心率接近时就塌掉。
4.4 压力测试的三种加压方式
FECGSYNDB 的价值不只在「有真值」,还在「压力可调」。三个维度独立可调,构成一张实验设计网格:
| 加压维度 | 取值 | 作用 | 注意 |
|---|---|---|---|
| 信噪比 | 5 档,00 至 12 dB | 检验灵敏度下限 | 档位是目标值,实际值看注释 |
| 事件类型 | 6 类事件(加基线共 7 类) | 检验非平稳鲁棒性 | 各类文件数不完全相同 |
| 重复 | 5 次 | 估计同条件下的方差 | 重复之间共享参数,非独立样本 |
三个维度相乘就是一千七百五十个格子。任何一份报告如果只给了一条汇总数字,就等于把这张网格压成了一个点,丢掉的恰恰是这个库最想让你看到的分层信息。
4.5 边界条件:什么时候它不再适用
| 条件 | 是否适用 | 判断依据 |
|---|---|---|
| 论证算法在低信噪比下的退化曲线 | 适用 | 5 档信噪比齐全 |
| 论证算法在宫缩期不失效 | 适用 | 宫缩是独立事件类 |
| 论证双胎分离可行 | 适用 | 双胎有独立源文件 |
| 论证算法在真实产妇上有效 | 不适用 | 无真实记录 |
| 论证算法对电极位置变化鲁棒 | 不适用 | 几何固定 |
| 论证算法可用于长时监测 | 不适用 | 每次五分钟 |
| 论证算法可处理运动伪影 | 受限适用 | 只有参数化的胎动,无真实运动伪影 |
| 论证算法对肥胖孕妇有效 | 不适用 | 体型未建模 |
§5 怎么拿到:从 33 GB 的目录里取出你要的那一格
5.1 目录与体积
| 路径 | 内容 | 量级 |
|---|---|---|
/files/fecgsyndb/1.0.0/RECORDS |
全路径清单 | 约 280 KB |
/files/fecgsyndb/1.0.0/ANNOTATORS |
注释角色说明 | 36 B,一行 |
/files/fecgsyndb/1.0.0/SHA256SUMS.txt |
逐文件校验和 | 约 1.9 MB |
/files/fecgsyndb/1.0.0/images/fig1.png |
体导体几何示意图 | 约 250 KB |
/files/fecgsyndb/1.0.0/images/samplechart.png |
示例波形图 | 约 79 KB |
subXX/snrYYdB/*.hea |
头文件 | 每条 2 至 3 KB |
subXX/snrYYdB/*.dat |
34 通道信号 | 每条 5.1 MB |
subXX/snrYYdB/*.qrs |
QRS 注释 | 每条 4 至 5 KB |
解压后 33.3 GB。这不是一个可以随手全下的小库,wget -r 会把七千个数据文件拉下来。更实际的做法是先按目录筛:只做信噪比研究就取一两个档位,只做事件研究就在每档里取固定重复序号。
单文件 5.1 MB 这个数字可以自己验算:34 通道 × 75000 点 × 2 字节 = 5,100,000 字节,与官方给出的单文件 content-length 完全一致。要判断下载是否完整,比对文件大小是最省事的一道检查。
5.2 批量获取:只取你需要的子集
#-- 先取清单与校验和
curl -sL https://physionet.org/files/fecgsyndb/1.0.0/RECORDS -o RECORDS
curl -sL https://physionet.org/files/fecgsyndb/1.0.0/SHA256SUMS.txt -o SHA256SUMS.txt
#-- 只挑 sub05 与两个信噪比档位,避免全量 33 GB
mkdir -p data
grep -E '^sub05/snr(09|12)dB/' RECORDS | while IFS= read -r rel; do
mkdir -p "data/$(dirname "$rel")"
for ext in hea dat qrs; do
curl -sL --retry 3 -C - \
"https://physionet.org/files/fecgsyndb/1.0.0/${rel}.${ext}" \
-o "data/${rel}.${ext}"
done
done
#-- 核对:每个 dat 应为 5100000 字节
find data -name '*.dat' | wc -l
find data -name '*.dat' -size 5100000c | wc -l
-C - 是断点续传参数,在下载 5 MB 的数据文件时很有用。上面第三段核对里两个数字应当相等;如果第二个小于第一个,说明有文件下载中断,需要重跑而不是直接拿去读。
SHA256SUMS.txt 覆盖全部七千个数据文件、七千个头文件与三千七百五十个注释文件,是这个库最扎实的一道质量保障。它比多数 PhysioNet 库更完备——很多库根本没有校验和清单。
5.3 把文件名解析成结构化表
import os
import re
import pandas as pd
PAT = re.compile(
r"^(?P<sub>sub\d{2})_(?P<snr>snr\d{2}dB)_l(?P<rep>\d)_"
r"(?:(?P<case>c\d)_)?(?P<src>fecg\d|mecg|noise\d)$"
)
rows = []
for root, _, files in os.walk("data"):
for fn in files:
if not fn.endswith(".hea"):
continue
m = PAT.match(fn[:-4])
if not m:
rows.append({"name": fn[:-4], "sub": None, "snr": None,
"rep": None, "case": None, "src": None})
continue
rows.append({"name": fn[:-4], **m.groupdict()})
df = pd.DataFrame(rows)
print(len(df), df["case"].isna().sum())
#-- 7000 个文件里,基线那 500 个 case 为空
case 段用了可选分组,所以基线文件名也能匹配,case 列留空。这个正则与 §2.1 的表一一对应,可以直接拿来核对清单是否符合预期:按 case 分组计数应得到 §7.3 里那张表。
5.4 读出某一次模拟的全部源并重放
import wfdb
import numpy as np
def load_set(sub, snr, rep, case):
folder = f"{sub}/{snr}"
stem = f"{sub}_{snr}_l{rep}_" + (f"{case}_" if case else "")
out = {}
for src in ("fecg1", "fecg2", "mecg", "noise1", "noise2", "noise3"):
name = f"{folder}/{stem}{src}"
if os.path.exists(f"data/{name}.hea"):
out[src] = wfdb.rdrecord(f"data/{name}", physical=True)
return out
parts = load_set("sub05", "snr09dB", 3, "c4")
for k, v in parts.items():
print(k, v.p_signal.shape, v.comments[:2])
这段代码先用文件是否存在来判断某个源是否属于这一次模拟,而不是假定固定的源组合。这是应对 §2.4 那张不规则组合表最稳妥的写法。
5.5 按模拟例划分训练与测试
import numpy as np
import pandas as pd
df = pd.read_parquet("index.parquet")
df = df[df["src"] != "noise1"]
df = df[df["src"] != "noise2"]
sim_id = df["sub"] + "_" + df["snr"] + "_" + df["rep"] + "_" + df["case"].fillna("base")
subs = sorted(df["sub"].unique())
train_sub = subs[:8]
test_sub = subs[8:]
train = df[df["sub"].isin(train_sub)]
test = df[df["sub"].isin(test_sub)]
print(len(train), len(test), train["sub"].nunique(), test["sub"].nunique())
按模拟妊娠划分是最保守的做法:八例训练、两例测试。如果想保留更多数据做测试,次一档的划分是按重复序号切(l1 至 l3 训练,l4、l5 测试),但这只是「同参数不同初值」,不能当作真正的分布外测试。最差的做法是按文件随机切,那会把同一次模拟的源文件分到两侧,等于把答案提前交了出去。
5.6 六个最容易出错的地方
| 位置 | 错误写法 | 后果 | 正确做法 |
|---|---|---|---|
| 文件名解析 | 按固定四段切分 | 基线文件名少一段,源标记被当事件 | 用可选分组正则 |
| 注释扩展名 | 按 .atr 找注释 |
找不到文件 | 本库注释后缀是 .qrs |
| 采样点数 | 信注释 #n:300000 |
与 75000 点不符 | 以头文件首行为准 |
| 通道顺序 | 假定 34 路都同名就同义 | 腹壁与参考混在一起 | 第 33、34 路是母体参考 |
| 划分方式 | 按文件随机切 | 真值泄漏,指标虚高 | 按模拟例或按 sub 划分 |
| 单位理解 | 当毫伏直接读 | 与真实设备不可比 | 单位是 nu,只比相对大小 |
最后一行要强调:nu 是无量纲,物理意义由模型参数决定。拿本库算出的幅度门槛去套真实设备,等于把模拟器的内部标度当成了伏特。
§6 与库内条目的关系
6.1 关系图谱
在胎心电这一族资源里,本库的位置不在「真实」轴上,而在「压力」轴上。把库内相关的词条按数据性质与任务摆开:
| 记录 | 数据性质 | 与本库的关系 |
|---|---|---|
| adfecgdb | 真实采集,含头皮直接胎心电 | 真值最硬的真实库,但只有五名产妇;与本库构成「小样本真值」对「大样本模拟真值」 |
| 母体 Holter 类库 | 真实采集 | 只含母体成分,与本库任务是「把母体找出来」的反向 |
| 各类胎儿超声切面库 | 影像,真实 | 任务完全不同:它看形态结构,本库看电生理时序 |
| 胎儿心音库 | 真实录音 | 模态不同,同一生理系统;可做跨模态类比 |
| 母体心电诊断库 | 真实采集 | 提供真实母体成分的形态参考,可与本库的模拟母体形态对照 |
| 仿真类数据集(各类合成心电) | 合成 | 同族方法:模拟器生成加真值配比,思路同源 |
| 通用生理信号评测基准 | 真实为主 | 用途不同,它评模型,本库评提取算法 |
真正有直接关系的是第一行。adfecgdb 的价值在于真,本库的价值在于多;前者用来做最终确认,后者用来做中间筛选。第二行提供反向思路:如果一个人只关心母体心电,本库的分源文件恰好能给出一份「不含胎儿干扰的母体成分」,这是它在母体任务上的附带用途。
6.2 检索路径
| 你要找的东西 | 检索词 | 会落到哪里 |
|---|---|---|
| 胎心电提取的方法基准 | NI-FECG、fetal ECG extraction |
§7.4 与 §8 |
| 合成数据的用法讨论 | synthetic data、simulation |
§4.1 |
| 真实真值的稀缺程度 | adfecgdb、direct fetal ECG |
§6.1 表 |
| 事件类压力测试 | uterine contraction、fetal movement |
§2.4 |
| 双胎分离 | twin pregnancy、multi-fetal |
§2.4 的 c5 |
| 许可与引用 | ODC-BY、PhysioNet citation |
§9.3 |
6.3 与其他库的分工判据
选库时最实际的问题是「这一段该用哪个」。把胎心电研究拆成几段:
| 任务 | 主粮仓该用什么 | 本库的位置 | 原因 |
|---|---|---|---|
| 提取算法的初步筛选 | 合成库 | 主力 | 真值齐全、条件可控 |
| 提取算法的最终确认 | 真实库 | 不适用 | 无真实记录 |
| 检出率的分层报告 | 合成库加真实库 | 主力之一 | 分层网格完整 |
| 形态学参数标定 | 真实头皮电极库 | 辅助 | 可算参考值,但形态来自模型 |
| 临床决策支持 | 无,属研究阶段 | 不适用 | 本库不承载任何临床结论 |
| 教学演示 | 本库 | 主力 | 可拆层展示 |
第一行与第二行合起来才是完整流程:先在合成库上把方法调稳,再去真实库上确认。只用第一段,会把「适配模拟器」误当成「适配人」;只用第二段,五名产妇的样本量撑不起参数选择。
§7 质量评估与局限性
7.0 阅读本节前的一个前置校正
评价这个库时,最容易发生的一次误判是把它和真实腹壁记录放在同一根尺子上比,然后得出「数据都是假的,参考价值有限」的结论。这个比较本身错位:它的目标不是复制一群产妇,而是构造一份真值已知、压力可调的测试台。用「像不像真人」去评它,就像用「像不像真飞机」去评风洞。
完成这个校正之后,五维评分才读得准。它的「真实度」低不是因为糊弄,而是因为它主动放弃了真实性以便换取可控性;它的规模分高,不是因为人多,而是因为模拟可以批量重跑。评分必须分开看这两件事,否则会把一种设计取舍误读成一种质量缺陷。
7.1 五维评分
按 DAIMS 的常用维度打分,每项 1 至 5 分,分数锚定在「作为胎心电提取算法的压力测试台」这个用途:
| 维度 | 分数 | 理由 |
|---|---|---|
| 数据规模 | 5 | 一千七百五十次模拟、145.8 小时、超百万胎儿 QRS |
| 标注质量 | 5 | 真值来自源文件本身,不需要标注,也就没有标注误差 |
| 任务适配 | 5 | 事件、信噪比、重复三轴可调,正对提取算法的评测需求 |
| 文档完整 | 3 | 注释字段多但官方正文未逐项解释,文件名规则与实际略有出入 |
| 可获取性 | 4 | 免注册、ODC-BY,但 33.3 GB 对个人网络不友好 |
合计 22 分(满分 25)。这个分数结构在 PhysioNet 的合成库里算是很高的,扣分集中在文档与体量:规模、标注、适配三项几乎无可挑剔,因为这三项本来就是模拟的先天优势。
文档分给 3 分是有具体依据的。头文件里二十多项注释字段,官方正文只用一句「头文件还包含 Andreotti 等论文表 2 描述的其他模型参数」带过;文件名规则给的对照表与清单实际形态有三处出入(噪声带序号、基线无 c 段、双胎多一个源)。这些都是使用者会当场撞上的问题。
7.2 注释的真实含义
ANNOTATORS 只有一行,写的是注释角色名为 qrs、说明为「机器生成的 QRS 位置」。这句话有两个要点:
第三行决定了它不能直接当形态学基准用。论文摘要里确实评估了胎儿 QT 与 T/QRS 比值,但那是在源波形上算形态学参数,靠的是完整波形而不是注释里多出来的符号。想复现这类实验,得自己在波形上做检测。
从工程角度看,这条注释策略反而更省事。因为每个源各自有 .qrs,胎心注释就在 fecg1(双胎时还有 fecg2)上,母体注释在 mecg 上,噪声文件也有自己的 .qrs。这意味着你可以直接拿胎心源上的注释当真值,把腹壁混合上的检测结果与它比对,中间不需要任何人工环节。噪声文件也带注释这一点乍看奇怪,但它恰好提示了另一件事:噪声文件同样按 34 通道传播,检测器在里面找出的所谓 QRS 全是假阳性,这本身就是一个可以量化的干扰基线。
7.3 已知的、可枚举的偏差
| 偏差 | 表现 | 影响 |
|---|---|---|
| 全合成 | 无任何真实记录 | 人群、设备、环境三类差异全部缺席 |
| 十例参数 | 十组模型而非十个个体 | 不能做人群统计 |
| 几何固定 | 体导体对所有模拟共用 | 电极位置效应无法研究 |
| 单位无量纲 | nu 而非毫伏 |
与真实设备不可直接互比 |
| 计数口径两种 | 一千七百五十次模拟对七千个文件 | 引用时须说明是哪种 |
| 采样点数口径不一 | 注释 300000 对头文件 75000 | 读取以头文件为准 |
| 文件名规则有出入 | 官方对照表与清单形态不同 | 解析脚本须处理基线缺段 |
| 事件类不均 | 双胎与宫缩类源文件更多 | 直接平均会偏袒文件多的事件 |
最后一行在这里展开。按清单归并,基线、c0、c1、c2、c4 每类各一千个文件,c3 与 c5 各一千二百五十个,原因是双胎多一个 fecg2,宫缩与双胎多一路噪声。若按文件数做加权平均,双胎与宫缩的权重会自然偏高。正确的做法是先归并到「模拟次数」这一层再平均,而不是在文件层平均。
7.4 外部验证矩阵
本库在文献里的用法,从公开摘要看集中在三类角色:
| 验证角色 | 典型做法 | 讲得通的地方 | 风险 |
|---|---|---|---|
| 标准压力测试台 | 三支算法在同一网格上横向对比 | 输入相同,差异可归因于方法 | 只报全库均值,掩盖分层 |
| 训练数据增广 | 合成数据混入真实数据训练 | 规模大、真值齐 | 模拟与真实的域差被低估 |
| 上游模块验证 | 用本库调参,再到真实库确认 | 分工明确 | 两步之间的结论落差无人量化 |
第二行的风险值得强调:合成数据与真实数据之间存在的分布差(域差),在做增广时并不会自动消失。如果训练集里合成占绝大多数,模型学到的主要是模拟器的纹理,迁移到真人时性能会掉,而掉多少恰好要用第一类角色(压力测试)来量。
第三行提示了一个务实的流程:把本库定位在「调参」,把真实库定位在「确认」。问题是从调参到确认这一步的落差,几乎没有文献系统报告过,这既是本库的用途边界,也是一个可以做的空白。
7.5 它为何能持续:模拟是唯一能把条件摆成网格的路
这个库自 2016 年定版后没有扩版,却持续被引用。原因不在它有多真,而在它占着一个真实数据无法复制的位置:只有模拟才能把「事件类型 × 信噪比 × 重复」摆成一张完整的网格,让每一种方法在同样的格子里被吹一遍。真实采集无法事先安排一次宫缩,也无法把同一段母胎信号的信噪比调低三档再来一次。只要提取算法还需要分层评估,这张网格就仍有需求,而重跑模拟器的成本远低于重新组织一次多中心临床采集。
§8 生态位与历史
8.1 历史沿革
这个库的诞生路径与临床库完全不同。它不是医院里采完再发布的产物,而是一套开源工具的输出。FECGSYN 本身是一个公开的胎心电仿真工具箱,用 MATLAB 与 Python 写成,目标是给 NI-FECG 领域一个可复现的实验平台。FECGSYNDB 是这个工具箱跑出来的一份固定快照,落位到 PhysioNet 上供人直接下载,不必自己装环境重跑。
这种「工具出数据」的路径在生理信号领域并不罕见,但它带来一个特别的属性:数据集的边界由工具的能力决定。工具能模拟的事件,库里就有;工具还没实现的情形,库里就缺。这也是为什么本库的七类事件里没有更细的病理分类——不是没人想加,而是当时的仿真框架就覆盖到这一步。
| 时间 | 事件 | 意义 |
|---|---|---|
| 2016-03 | 论文在 Physiological Measurement 线上发表 | 方法、数据、评估例程同时公开 |
| 2016-04-26 | 记录清单最后修改 | 目录结构定稿 |
| 2016-04-29 | 以 1.0.0 版在 PhysioNet 发布 | 获得 DOI,进入可引用状态 |
| 2019-02-20 | 补充 SHA256SUMS.txt |
增加完整性校验,数据未变 |
四个时间点里,真正有信息量的只有第一与第三:论文与数据集几乎同期发布,这是「以论文为中心」的发布方式,与那些先有数据、多年后才发论文的临床库正好相反。
8.2 与后续研究的关系
| 阶段 | 主要用法 | 对数据的要求 |
|---|---|---|
| 2016–2018 | 建立基线,三支算法横评 | 网格完整、真值齐 |
| 2019–2021 | 深度学习进入,合成数据做预训练 | 规模大、与真实数据可对齐 |
| 2022 至今 | 可穿戴干电极、多胎、长时监测 | 更接近真实伪影的模拟 |
本库在第三阶段之后逐渐被当作「下限测试」而不是「全部测试」。这不是它变差了,而是领域对真实性的要求越来越高:干电极的接触噪声、孕妇日常活动带来的伪影,这些是参数化噪声不容易复制的东西。
8.3 生态位:风洞而不是航线
风洞再精密,也不能替代试飞;但跳过风洞直接试飞的代价,任何工程团队都不愿意付。这就是它留在生态位上的理由。
把它与另一类资源对照会更清楚。真实采集库(如 adfecgdb)回答的是「在这些人身上行不行」,本库回答的是「在这些条件下会不会塌」。前者给你置信度,后者给你失效边界。一个成熟的工程流程两样都要:先用本库列出失效条件清单,再用真实库确认清单之外的场景没有意外。缺前者,你只会知道「我的方法在五条记录上过了」;缺后者,你只会知道「我的方法在模拟器的世界里过了」。
8.4 谁在用它,用来做什么
从公开摘要与引用习惯看,使用者分三类:做提取算法的方法组(把它当标准测试台)、做深度学习预训练的模型组(把它当大规模配对数据)、做教学的人员(把它当混合与真值的最佳示例)。第三类最少,但它恰好用到了这个库最难被替代的部分——同一段信号可以拆成母体、胎心、噪声三层并排展示,不需要任何加工。
§9 使用指引
9.1 三种角色的路径
| 角色 | 第一天 | 第一周 | 注意事项 |
|---|---|---|---|
| 提取算法研究者 | 取一例子集,跑通 §2.3 的读取 | 在 5 档信噪比上画退化曲线 | 报告必须分层,不能只给均值 |
| 模型训练者 | 取全量或大部分子集,验证分源相加成立 | 按模拟例划分,做预训练实验 | 警惕模拟与真实的域差 |
| 教学人员 | 取 §4.2 的案例,拆成三层画图 | 做「加噪前后」「有胎动前后」两组对比 | 单位是 nu,图上不要标毫伏 |
9.2 一条最短的可用流程
#-- 1. 清单与校验
curl -sL https://physionet.org/files/fecgsyndb/1.0.0/RECORDS -o RECORDS
#-- 2. 取一例、一档、一次重复的全部源,先验证结构
grep '^sub05/snr09dB/sub05_snr09dB_l3_' RECORDS | while IFS= read -r rel; do
mkdir -p "data/$(dirname "$rel")"
curl -sL --retry 3 -C - \
"https://physionet.org/files/fecgsyndb/1.0.0/${rel}.hea" -o "data/${rel}.hea"
curl -sL --retry 3 -C - \
"https://physionet.org/files/fecgsyndb/1.0.0/${rel}.qrs" -o "data/${rel}.qrs"
done
#-- 3. 头文件里核对三条:34 通道、250 Hz、75000 点
grep -c '^#' data/sub05/snr09dB/sub05_snr09dB_l3_c4_fecg1.hea
head -1 data/sub05/snr09dB/sub05_snr09dB_l3_c4_fecg1.hea
import os
import wfdb
import numpy as np
rows = []
for root, _, files in os.walk("data"):
for fn in files:
if not fn.endswith(".hea"):
continue
rel = os.path.join(root, fn[:-4])
r = wfdb.rdrecord(rel, physical=False)
rows.append({
"name": fn[:-4],
"fs": r.fs,
"sig_len": r.sig_len,
"n_sig": r.n_sig,
"shape": r.d_signal.shape,
})
for row in rows:
print(row["name"], row["fs"], row["sig_len"], row["n_sig"], row["shape"])
第二段先只读头信息,不读信号,因为 physical=False 加上我们只取元数据时无需真的把 34×75000 的矩阵装进内存。对全量七千个文件做这种体检很快;若改成 physical=True 再读全库,内存会迅速吃满。
9.3 引用方式
官方页面明确把原始论文列为第一顺位,并注明「使用本资源时请引用原始出版物」。这一条要照做:只引 PhysioNet 而漏掉 Andreotti 等人 2016 年那篇,等于没交代这份数据从头到尾是怎么算出来的,而「怎么算出来的」恰恰是引用合成数据时最该讲清楚的部分。
版本号也要写清。PhysioNet 上的路径是 1.0.0,DOI 以 10.13026 开头、后缀为该版本的稳定标识;不要只写「PhysioNet 上的 FECGSYNDB」,因为平台上的路径带版本,跨版本引用会在别人复现时对不上文件。若使用的是自己重跑 FECGSYN 生成的版本,则应当引用工具本身的版本,并说明生成参数与官方快照的差别。
§10 AI 使用声明
| 项目 | 声明 |
|---|---|
| 文本生成 | 本文由 AI 辅助撰写,结构与事实由编辑把关 |
| 数字来源 | 规模、结构、边界三类数字来自 PhysioNet 官方页面、记录清单与头文件 |
| 清单核验 | RECORDS 全七千行已取回并按文件名归并,各事件类与信噪比档位的文件计数据此得出 |
| 头文件核验 | 抽查了基线、c0、c1、c2、c3、c4、c5 各类的代表性头文件,确认通道数、采样率、注释字段形态 |
| 注释核验 | 注释角色以 ANNOTATORS 的一行为依据,未逐条展开全部注释文件 |
| 差异披露 | 官方正文的噪声文件命名与清单实际形态不同;注释采样点数与头文件首行不一致,两口径并列 |
| 论文信息 | 只读 PhysioMed 论文摘要,未读全文,方法细节以官方页面为准 |
| 未核验内容 | 注释中若干模型参数字段的逐项定义;实际信噪比与目标档位的对应关系 |
| 图像 | 无配图;封面为生成式提示词,不含真实胎心电波形 |
| 免责 | 本文不构成临床建议;数据使用须遵守 ODC-BY 1.0 的署名要求 |
常见误读(避坑清单)
| 说法 | 实际 | 证据 |
|---|---|---|
| 「它是一份真实产妇的腹壁心电库」 | 全部由模拟器生成,无任何真实记录 | 官方摘要首句写 simulated,十例为模拟妊娠 |
| 「它有十位受试者」 | 十组参数模型,不是十个人 | 官方称 ten different pregnancies were simulated |
| 「一共一千七百五十条数据」 | 一千七百五十次模拟,拆成七千个文件 | RECORDS 共 7000 行 |
| 「每条记录 300000 个采样点」 | 头文件首行是 75000 点,注释 #n 写 300000 |
两者并列,以头文件为准 |
| 「34 个通道就是 34 个等价导联」 | 前 32 路腹壁、后 2 路母体参考 | 官方正文明确分工,头文件不标 |
| 「注释文件后缀是 .atr」 | 后缀是 .qrs |
ANNOTATORS 只登记 qrs 一种 |
| 「噪声文件叫 noise」 | 清单里一律带序号 noise1、noise2 |
官方对照表与实际清单不同 |
| 「基线文件名也是五段」 | 基线没有 c 段 | sub01_snr00dB_l1_fecg1 与 _c0_ 形式并列存在 |
| 「单位是毫伏」 | 单位是 nu,无量纲 | 头文件信号行 3000(0)/nu |
| 「所有事件类的数据量一样」 | 双胎与宫缩类的源文件更多 | 按清单归并,c3 与 c5 各一千二百五十 |
FAQ
Q1:FECGSYNDB 里有真实孕妇的记录吗?
没有。整个库由 FECGSYN 模拟器生成,官方定义写的是模拟的成人与非侵入式胎心电信号。十例「模拟妊娠」是十组参数化的母胎模型,不对应任何真实个体,也没有人口学信息、病史或采集日志。
Q2:它到底有多少条数据?
官方说一千七百五十次模拟,每次五分钟,合计 145.8 小时。记录清单七千行,因为每次模拟拆成多个源文件。两种数法都对,一个是模拟次数,一个是文件数,引用时必须说明用的是哪一种。
Q3:为什么它的信号单位不是毫伏?
头文件的单位字段写 nu,意为无量纲。官方说明信号是模拟出来的,但幅度已对齐正常母体心电一至二毫伏的量级。因此数值可比较相对大小,不能直接与临床设备的物理读数逐点对应。
Q4:能不能用它训练深度学习模型?
可以用于预训练或增广,但要清楚它学到的是模拟器的假设。它的设计用途是给提取算法做压力测试:同一套评估流程在 7 种事件、5 档信噪比上跑一遍,看方法在哪里失效。
Q5:32 个腹壁通道是真的 32 个导联吗?
是 32 个腹壁观测点,官方称 32 abdominal channels 加 2 个母体参考通道。它们由模拟器按固定几何布置,彼此高度相关,与临床上有限导联的组合方式不同,做降维或选通道时要留意。
Q6:为什么官方说 1750 次,清单却有 7000 行?
因为每次模拟的母体心电、胎心电、噪声是分别输出成文件的。单胎 c0 一次模拟有四个文件,双胎 c5 有五个。七千行是文件数,除以四只能粗略对上,因为双胎那一类多一个源。
Q7:注释里的 #n:300000 是什么意思?
官方正文没有解释。它与头文件首行的 75000 点不一致,比例正好是四倍。读取时应以头文件首行为准,wfdb 读出的长度与五分钟乘以 250 Hz 吻合。把 #n 当采样点数会把数据切成四段错位。
Q8:能不能用它做真实人群的泛化验证?
不能。它没有真实记录,也没有人口学信息。它只能说明算法在模拟器假设下的表现,不能说在产妇群体上的表现。真实的泛化验证需要另找含头皮电极或人工标注的真实库。
Q9:双胎场景怎么找?
文件名第四段是 c5、且存在 fecg2 源文件的,就是双胎场景。这类记录的注释里 nfetus 为 2,fhr 与 ftypeacc 也是两个值。按清单归并,c5 类共一千二百五十个文件。
Q10:体量多大,能一次下完吗?
解压后 33.3 GB,压缩包同为 33.3 GB。单条数据文件 5.1 MB,全库七千个。家用网络全量下载耗时较长,建议按 sub 与 snr 目录先做子集。
Q11:它的许可能不能商用?
ODC-BY 1.0 允许包括商业用途在内的使用,条件是署名。官方页面要求引用 Andreotti 等人 2016 年的原始论文,具体条款以许可全文为准。
Q12:它还会更新吗?
官方只有一个版本 1.0.0,2016 年 4 月发布,2019 年 2 月补充了校验和清单,信号与注释未变。页面没有任何扩版说明。若要更新的事件类型,只能自己重跑 FECGSYN 工具生成。
术语表
| 术语 | 解释 |
|---|---|
| FECGSYN | 开源的非侵入式胎心电仿真工具箱,本库的生成器 |
| NI-FECG | 非侵入式胎儿心电,从腹壁电极表面获取胎儿心电信号 |
| 腹壁心电 | 在孕妇腹部表面记录到的心电,含母体与胎儿两种成分 |
| 母体参考通道 | 放在母体胸部、主要反映母体心电的电极,用作消除参考 |
| 点偶极子 | 把心脏的电活动简化为一个带方向和强度的点源 |
| 体导体 | 把躯干视为导体,用导体内的电位分布描述体表信号 |
| 源分离 | 把混合信号拆回各独立来源的过程 |
| 盲源分离 | 不依赖先验波形、仅凭统计独立性做分离的方法族 |
| 模板减除 | 用母体心搏模板逐次减去母体成分的方法族 |
| 自适应滤波 | 用参考通道估计干扰并自适应地扣除的方法族 |
| 胎母信噪比 | 胎儿心电成分与母体心电成分的相对强度,注释字段 SNRfm |
| 信噪比档位 | 本库设定的五档目标噪声水平,00 至 12 dB |
| 胎儿 QRS | 胎儿心电中幅度最大的快速波群,是检出任务的目标 |
| 检出率 | 算法找出的胎儿 QRS 占真值的比例 |
| 宫缩 | 子宫收缩,会在腹壁信号上产生低频大幅扰动 |
| 胎动 | 胎儿在宫内的活动,会改变电极与胎心的相对几何 |
| 双胎 | 一次妊娠两个胎儿,混合信号中会出现两套胎心成分 |
| 异位搏动 | 起源于正常起搏点之外的心搏,形态与节律均异常 |
| 重复序号 | 同一条件下重复模拟的编号,本库为 l1 至 l5 |
| WFDB | PhysioNet 的波形数据格式,包含头文件、信号与注释 |
.hea |
纯文本头文件,声明通道数、采样率与注释行 |
.dat |
二进制信号文件,按通道交织存放 |
.qrs |
本库的注释文件后缀,内容为 QRS 位置 |
| nu | 无量纲单位标记,说明信号为模拟值而非物理量 |
| 域差 | 合成数据与真实数据之间的分布差异 |
| 压力测试 | 在受控的困难条件上系统检验方法稳定性的做法 |
| ODC-BY | 开放数据共享署名许可,允许商用但须署名 |
附录 A:核心数字核对表
| 项 | 值 | 来源 | 来源层级 |
|---|---|---|---|
| 模拟妊娠数 | 10 | 官方页面;目录数 | 官方页面 |
| 生理事件数 | 7(含基线) | 官方页面表格 | 官方页面 |
| 信噪比档位 | 5 | 官方页面;目录数 | 官方页面 |
| 重复次数 | 5 | 官方页面;文件名 l 段 | 官方页面 |
| 模拟次数 | 一千七百五十 | 官方页面公式 | 官方页面 |
| 记录清单行数 | 7000 | RECORDS 逐行 |
官方文件 |
| 总时长 | 145.8 小时 | 官方页面与论文摘要 | 官方页面 |
| 胎儿 QRS 总量 | 超过一百万 | 官方页面与论文摘要 | 官方页面 |
| 单次模拟时长 | 5 分钟 | 官方页面 | 官方页面 |
| 通道数 | 34 | 头文件首行 | 官方文件 |
| 腹壁通道 | 32 | 官方正文 | 官方页面 |
| 母体参考通道 | 2 | 官方正文 | 官方页面 |
| 采样率 | 250 Hz | 头文件首行 | 官方文件 |
| 单次采样点 | 75000 | 头文件首行 | 官方文件 |
| 单位 | nu | 头文件信号行 | 官方文件 |
| 数据文件大小 | 5100000 字节 | 官方 response 头 | 官方文件 |
| 解压体积 | 33.3 GB | 官方页面文件区 | 官方页面 |
| 许可 | ODC-BY 1.0 | 官方页面 | 官方页面 |
| 上线日期 | 2016-04-29 | 官方页面 | 官方页面 |
| DOI | 10.13026/C21P4T | 官方页面 | 官方页面 |
附录 B:数字的来源分层
第一层:官方页面与官方文件。 模拟妊娠数、事件类别、信噪比档位、重复次数、模拟次数、总时长、胎儿 QRS 总量、通道分工、许可、发布日期、DOI,均来自项目主页。通道数、采样率、采样点数、单位、增益、注释字段形态,均来自头文件。记录清单的七千行与各类文件计数,是把 RECORDS 取回后按文件名归并得到的直接派生。
第二层:论文摘要。 三类算法的框架、压力测试的动机、各类最好方法的检出准确率(99.9%、97.9%、96.0%),以及「同类源分别传播后叠加」的模型描述,来自 Physiological Measurement 2016 年那篇论文的摘要。
第三层:未核实。 注释中模型参数各字段的逐项定义(如 mres、fres、mvcg、fvcg、posdev)、#n 与头文件点数差四倍的成因、目标信噪比档位与实际 SNRfm 的对应关系、以及各类事件模拟的具体参数取值。这些官方正文未逐项说明,正文中标「官方正文未逐项解释」或并列两口径,不作推断。
附录 C:完整参考文献
| 编号 | 文献 | 链接 | 用途 |
|---|---|---|---|
| 1 | Fetal ECG Synthetic Database v1.0.0 | https://physionet.org/content/fecgsyndb/1.0.0/ | 规模、结构、边界数据的来源 |
| 2 | 该库的 PhysioNet 文件目录 | https://physionet.org/files/fecgsyndb/1.0.0/ | 清单、校验和、头文件 |
| 3 | Andreotti F. 等,Physiological Measurement 2016;37:627–648 | https://doi.org/10.1088/0967-3334/37/5/627 | 模型方法、评测结果 |
| 4 | 数据集 DOI | https://doi.org/10.13026/C21P4T | 引用与版本定位 |
| 5 | PhysioNet 平台综述(2026,Nature Health) | https://doi.org/10.1038/s44360-026-00096-z | 托管模式与引用规范 |
| 6 | adfecgdb(真实采集对照) | https://physionet.org/content/adfecgdb/ | §2.6 与 §6.1 的对比 |
| 7 | FECGSYN 仿真工具箱 | https://physionet.org/content/fecgsyn/ | §8.1 的生成路径 |
| 8 | Europe PMC 检索:胎心电提取 | https://www.ebi.ac.uk/[europepmc](https://www.qianfanghub.com/ai-ready-dataset/europepmc/1353)/ | §7.4 的外部用法 |
附录 D:任务代码
任务一:在五档信噪比上画退化曲线
import wfdb
import numpy as np
SNRS = ["snr00dB", "snr03dB", "snr06dB", "snr12dB"]
REPS = [1, 2, 3, 4, 5]
def detect(x, fs=250):
#-- 最粗的门限法,只用来占位,不追求检出率
hi = np.percentile(x, 99)
above = np.abs(x) > hi
edges = np.diff(np.concatenate(([0], above.astype(int), [0])))
onsets = np.where(edges == 1)[0]
keep = []
for p in onsets:
if not keep or p - keep[-1] > fs // 4:
keep.append(p)
return np.array(keep)
for snr in SNRS:
hits, total = 0, 0
for rep in REPS:
stem = f"sub05/{snr}/sub05_{snr}_l{rep}_c0"
truth = wfdb.rdann(f"data/{stem}_fecg1", "qrs").sample
mixed = wfdb.rdrecord(f"data/{stem}_mecg", physical=True).p_signal[:, 0]
pred = detect(mixed)
if len(pred):
d = np.abs(truth[:, None] - pred[None, :]).min(axis=1)
hits += int((d <= 12).sum())
total += len(truth)
print(snr, round(hits / max(total, 1), 4))
这段的要点不在检测器,而在评估的写法:真值来自 fecg1 的注释,预测跑在 mecg 那一路上——这只是演示流程,真实实验应当跑在腹壁混合上(即把 mecg 换成你手上那段混合信号)。把真值的来源与预测的输入分开是对的,混在一起就变成自己评自己。
任务二:核对一次模拟的源文件是否齐全
import os
import re
PAT = re.compile(r"^(sub\d{2})_(snr\d{2}dB)_l(\d)_(?:c(\d)_)?(fecg\d|mecg|noise\d)$")
EXPECT = {"base": {"fecg1", "mecg"}, "c0": {"fecg1", "mecg", "noise1", "noise2"},
"c5": {"fecg1", "fecg2", "mecg", "noise1", "noise2"}}
groups = {}
for fn in os.listdir("data/sub05/snr09dB"):
if not fn.endswith(".hea"):
continue
m = PAT.match(fn[:-4])
if not m:
continue
sub, snr, rep, case, src = m.groups()
key = (snr, rep, case or "base")
groups.setdefault(key, set()).add(src)
bad = [k for k, v in groups.items() if k[2] in EXPECT and not EXPECT[k[2]] <= v]
print(len(groups), "组齐全" if not bad else f"{len(bad)} 组缺文件")
第二段把 §2.4 的组合表写成了可执行的断言。跑完之后如果 bad 为空,说明那一个目录的文件确实按官方描述的组合落盘;如果不为空,先检查是不是下载中断。
附录 E:如果只记五件事
| 序号 | 记住什么 | 为什么 |
|---|---|---|
| 1 | 全部是模拟的,十例是参数不是人 | 决定它能回答什么问题 |
| 2 | 一千七百五十次模拟、七千个文件 | 两个口径必须分清 |
| 3 | 34 通道分两段:32 腹壁加 2 母体参考 | 头文件不标,只能靠官方正文 |
| 4 | 混合与各源同时在场 | 这是它不可替代的地方 |
| 5 | 注释后缀是 .qrs,单位是 nu |
两个最容易踩的工程细节 |
编辑说明
核对于 2026 年 10 月 28 日完成。核对方式为直接访问 PhysioNet 项目页、RECORDS、ANNOTATORS、SHA256SUMS.txt 与目录索引,并把 RECORDS 的七千行取回按文件名归并,得到各事件类与信噪比档位的文件计数。头文件抽查覆盖基线、c0 至 c5 各类的代表性记录,确认通道数固定为 34、采样率固定为 250 Hz、首行点数固定为 75000。ANNOTATORS 只有一行,记录了 qrs 这一种注释角色。论文侧只读了 Physiological Measurement 2016 年那篇的摘要,未读全文,因此方法细节以官方页面表述为准。未覆盖的内容包括:注释中模型参数各字段的逐项定义、#n 与头文件点数差四倍的成因、目标信噪比档位与实际 SNRfm 的对应关系。单条数据文件的一次完整下载耗时较长,本次只核对了其声明大小 5100000 字节与 34×75000×2 的算式是否吻合,未对全部七千个文件逐一校验。
相关数据集导航
以下为站内 AI-Ready 数据集百科中与本词条共享多个主题标签的相关数据集,按相关度降序排列:
- adfecgdb — 共享标签:生理信号 / 心电信号 / 评测基准
- qt-database — 共享标签:生理信号 / 心电信号 / 评测基准
- vtac — 共享标签:生理信号 / 心电信号 / 评测基准
- propofol-anesthesia-dynamics — 共享标签:生理信号 / 心电信号 / 评测基准
- nifeadb — 共享标签:生理信号 / 心电信号 / 评测基准
- actes-cycloergometer-exercise — 共享标签:生理信号 / 心电信号 / 评测基准
- deepfake-ecg — 共享标签:生理信号 / 心电信号 / 评测基准
- ecgiddb — 共享标签:生理信号 / 心电信号 / 评测基准
- macecgdb — 共享标签:生理信号 / 心电信号 / 评测基准
- hmc-sleep-staging — 共享标签:生理信号 / 心电信号 / 评测基准
导航说明:本章节由全站统一标签体系自动计算生成(标签重合度算法),双向可达;点击链接可跳转至对应数据集词条。

