跳至正文
仇天赐.研究与工程笔记
搜索标题、摘要和标签 Esc 关闭
返回技术档案

分割数据泄漏:切片分开了,患者可能还在两边

从患者与体数据的层级关系出发,建立组间隔离的数据划分,并检查预处理、重复样本和测试集选择中的泄漏。

回顾整理

本文归入 2025.04 主题档案,整理于 2026.11.05。月份用于组织阅读路径,不代表具体实验或项目的完成日期。

先确定要泛化到什么对象

这篇方法回顾按主题归档,不对应某次有明确日期的实验。医学体数据常被拆成切片或裁剪块,而这些文件不是天然独立样本。相邻切片共享解剖结构,同一次采集的多个重建版本也可能高度相似。

如果目标是泛化到新患者,划分单位就应至少是患者。先把切片随机打散,再按文件分训练集和验证集,可能让同一患者同时出现在两边。此时结果更多反映对已有对象的适配,不能直接解释为对新患者的能力。

用清单表达数据层级

建议为每个样本保留稳定的去标识化分组键、检查键、体数据键、派生关系和标签版本。分组键应来自经过核对的映射,不应仅根据文件名猜测身份,也不应在公开日志里写入可识别信息。

同一患者的多次检查应沿用相同分组;没有患者信息时,按体数据分组只能防住同一体内的切片泄漏,不能保证患者隔离。这个证据缺口应写在评估限制中。

划分发生在切片提取、裁剪和增强之前。所有派生样本继承源数据的集合身份,避免在后续处理时重新随机分配。

把组间隔离写进代码

下面函数接收每个样本对应的分组键,产生一次组间隔离的划分。示例比例是可配置参数,不代表任何真实数据集的规模或实验设置。

import numpy as np
from sklearn.model_selection import GroupShuffleSplit

def split_by_group(group_ids, held_out_fraction=0.2, seed=0):
    groups = np.asarray(group_ids)
    if groups.ndim != 1 or len(np.unique(groups)) < 2:
        raise ValueError("expected at least two verified groups")
    splitter = GroupShuffleSplit(
        n_splits=1, test_size=held_out_fraction, random_state=seed
    )
    indices = np.arange(len(groups))
    train_idx, held_out_idx = next(
        splitter.split(indices, groups=groups)
    )
    if set(groups[train_idx]) & set(groups[held_out_idx]):
        raise RuntimeError("group overlap")
    return train_idx, held_out_idx

GroupShuffleSplit 的比例针对组,而非样本数量;组大小不同,切片数量比例自然可能不同。官方文档

先锁定最终测试组,再在余下组中生成训练与验证划分,并把第二次产生的局部索引映射回原始清单。保存最终索引清单,比只记录随机种子更可靠。

组间隔离还不是全部

归一化统计、特征选择、类别权重和任何需要拟合的预处理,应只从训练部分获得。验证集可以用于选择阈值与配置;最终测试集用于一次冻结方案后的评估。

重复导出、不同窗宽截图、同一数据的增强版本,可能换了文件名却仍共享来源。应检查源数据标识、派生记录和内容摘要;像素完全相同的摘要检查只能发现精确重复,发现不了重采样或压缩后的近似重复。

预训练数据与目标测试集是否重叠,也属于数据来源审查的一部分。无法确认时,应记录信息边界,不能默认不存在。

清单还应接受独立核对:随机抽取源数据与派生样本,确认分组继承正确;检查缺失分组键、重复键和异常命名。未知身份不能简单统一填成某个普通患者编号,也不能给每张切片临时生成不同编号来绕过检查。

划分结果也需要人工审阅

患者隔离后,仍应检查类别、中心、采集协议和目标大小的分布。样本少时,随机划分可能把稀有情况全部放到一侧。可采用组级分层或交叉验证,但不能反复挑选分数最高的划分作为最终结果。

如果目标是跨中心或跨时间泛化,就要设计相应的外部留出,患者级随机划分不足以回答这个问题。发现泄漏后,应从分组与预处理拟合开始重建实验链路;只删掉几张重复图,却继续沿用受影响的配置选择,仍可能保留偏差。

参考

笔记至此,探索继续。交流与纠错