4. 数据清洗与质量检查

在医学影像AI训练流水线中,数据清洗与质量检查是决定模型上限的关键环节。原始数据往往来自多家医院、多台设备、多个扫描协议,甚至包含人工整理时的误操作。如果直接把这些数据送入训练,模型学到的可能是伪影、重复样本或损坏文件带来的噪声,而不是真正的解剖与病理特征。本节围绕四类核心任务展开:异常值检测、重复数据剔除、图像完整性校验,以及自动化QC报告的生成。

一、异常值检测:从元数据到像素分布

异常值检测需要同时关注“元数据异常”和“图像内容异常”。元数据层面,常见异常包括:层厚为0或负值、像素间距缺失、患者年龄超出合理范围(如0~120岁)、扫描时间与检查类型不匹配等。图像内容层面,则包括灰度分布异常、信噪比过低、存在大面积金属伪影或截断等。

实际操作中,可以先对DICOM标签做规则过滤。例如,对CT数据检查 SliceThickness 是否在0.5~10 mm之间,对MRI检查 RepetitionTime 是否为正数。对于像素级异常,常用Z-score或IQR方法。以某批胸部CT为例,假设统计得到全数据集像素均值为-200 HU、标准差为300 HU,则可将像素均值超过+3σ(即700 HU)的切片标记为疑似异常,因为正常胸部CT中极少出现如此高的整体均值。更稳健的做法是计算每个体积的灰度直方图,提取均值、方差、偏度、峰度四个特征,再用孤立森林(Isolation Forest)做无监督检测。示例参数可设为:n_estimators=100, contamination=0.02,即假设约2%的样本为异常。被标记的样本不应直接删除,而应进入人工复核队列。

二、重复数据剔除:精确匹配与近似匹配

重复数据在医学影像中非常普遍,常见来源包括:同一患者多次导出、不同标注版本重复入库、数据增强后误存回原始目录。重复样本会导致训练集与验证集泄漏,使评估指标虚高。

精确重复可以通过文件哈希(如SHA-256)快速识别。对DICOM文件,还可以结合 PatientID + StudyInstanceUID + SeriesInstanceUID + SOPInstanceUID 四元组判断。近似重复则更复杂,因为同一扫描可能因重新保存而改变文件头,但像素内容几乎一致。此时可对图像降采样到64×64,计算感知哈希(pHash),再设定汉明距离阈值。示例中,若两个体积的pHash汉明距离≤5,则判定为高度相似,进入人工确认。对于分割掩膜,还可以计算Dice系数,若Dice>0.98且图像pHash距离≤5,则几乎可以确定为重复。

建议在流水线中维护一张“已入库指纹表”,记录每个样本的哈希、pHash和来源路径。新样本入库前先查表,命中则直接跳过或转入复核,避免重复计算。

三、图像完整性校验:文件、几何与内容三层检查

图像完整性校验应覆盖三个层次。第一层是文件层:检查DICOM文件能否被pydicom正常读取,PixelData是否存在,传输语法是否支持。第二层是几何层:检查层间距是否均匀、方向矩阵是否正交、图像尺寸是否与元数据一致。第三层是内容层:检查是否存在全黑切片、全白切片、严重截断或明显错层。

一个实用的完整性校验流程如下:

  1. 读取DICOM,捕获异常并记录文件路径与错误类型。
  2. 检查 Rows、Columns、PixelSpacing、SliceThickness 是否齐全且为正数。
  3. 将像素值转换为HU(CT)或归一化强度(MRI),统计非零像素占比。若非零像素占比低于5%,标记为“疑似空片”。
  4. 计算相邻切片间的互相关,若某切片与上下层相关性均低于0.3,标记为“错层或伪影”。
  5. 输出校验结果表,包含文件路径、检查项、状态和备注。

以下是一个可直接参考的校验结果示例表:

文件路径检查项状态备注
/data/ct/patient_001/ser_01/IMG_0032.dcmPixelData存在通过—
/data/ct/patient_001/ser_01/IMG_0033.dcm非零像素占比失败非零像素占比2.1%,疑似空片
/data/mri/patient_014/ser_02/IMG_0010.dcm层间距均匀性警告相邻层间距偏差>15%
/data/ct/patient_022/ser_03/IMG_0007.dcm方向矩阵正交性失败方向余弦不满足正交,需重新导出

四、自动化QC报告生成

当数据量达到数千甚至数万例时,人工逐例检查不现实。自动化QC报告的目标是:把上述检测结果汇总为可读、可筛选、可追溯的文档。报告通常包含总体统计、异常分布、失败样本清单和可视化图表。

实现上,可以用Python脚本串联各检查模块,将结果写入Pandas DataFrame,再导出为HTML或PDF。报告首页给出关键指标:总样本数、通过数、警告数、失败数、重复样本数、异常值比例。第二页按检查项分组,列出失败样本路径和原因。第三页可嵌入直方图,展示像素均值分布、层厚分布和信噪比分布,帮助快速定位系统性问题。

以下是一个QC报告生成的核心代码片段,展示如何组织检查结果:

import pandas as pd
from qc_checks import check_dicom_integrity, detect_outliers, find_duplicates

records = []
for file_path in dicom_files:
    integrity = check_dicom_integrity(file_path)
    outlier = detect_outliers(file_path)
    records.append({
        "file_path": file_path,
        "integrity_status": integrity["status"],
        "integrity_note": integrity["note"],
        "outlier_flag": outlier["flag"],
        "outlier_score": outlier["score"]
    })

df = pd.DataFrame(records)
dup_groups = find_duplicates(dicom_files)
summary = {
    "total": len(df),
    "pass": (df["integrity_status"] == "pass").sum(),
    "fail": (df["integrity_status"] == "fail").sum(),
    "outlier_count": df["outlier_flag"].sum(),
    "duplicate_groups": len(dup_groups)
}
df.to_csv("qc_detail.csv", index=False)
print(summary)

运行后,qc_detail.csv 可作为逐例追溯的依据,而 summary 字典则用于生成报告首页的统计卡片。若某检查项失败率超过预设阈值(例如空片比例>1%),报告应自动标红并提示“建议检查扫描协议或导出流程”。

五、把清洗与QC嵌入流水线

数据清洗不是一次性任务,而应嵌入每次数据入库的入口。推荐的做法是:在数据注册阶段就执行完整性校验和哈希去重,在数据预处理阶段执行异常值检测,在训练前生成最终QC报告并冻结数据版本。这样,任何一次训练都能追溯到具体的数据快照和QC结论。

注意:异常值检测和重复剔除都会改变数据分布。每次清洗后应重新统计类别比例、病灶大小分布和强度分布,避免因清洗引入新的选择偏差。对于被剔除的样本,应保留清单和原因,便于后续审计和复现。
数据清洗与质量检查流水线 原始DICOM数据 多中心 / 多设备 / 多协议 图像完整性校验 文件可读 / 几何一致 非零像素 / 层间相关 重复数据剔除 SHA-256 / pHash Dice系数辅助判断 异常值检测 Z-score / IQR 孤立森林 是否通过 QC阈值? 进入训练集 记录指纹与QC状态 人工复核 / 修复 重新导出或剔除 自动化QC报告 总体统计 总数 / 通过 / 警告 / 失败 重复组数 / 异常比例 失败样本清单 文件路径 / 检查项 失败原因 / 建议操作 分布可视化 像素均值直方图 层厚 / 信噪比分布 版本与追溯 数据快照ID 清洗规则版本号

把上述四个环节串联起来,数据清洗与QC就不再是训练前的“杂活”,而是可度量、可追溯、可自动化的工程步骤。下一阶段的数据增强与划分,将直接建立在本节产出的干净数据快照之上。