医学影像公平性评估:FRAME框架分离采样变异与表征原因

医学影像公平性评估:FRAME框架分离采样变异与表征原因 医学影像公平性研究里FRAME 这一类分析思路要解决的核心问题是把不同人群子组上的性能差异拆成两个来源采样变异sampling variation和表征原因representational cause。现实中的医学影像数据集通常包含大量相关性特征某些检查设备、医院来源、年龄组别在样本量、病灶分布、图像噪声上都存在差异。模型性能在这些子组上的差异可能是模型表征里真的编码了敏感信息也可能只是数据采样波动带来的短时假象。如果只把一次测试集上的点估计差异当作结论很容易把随机噪声当成立场证据或者反过来把真实缺陷当成偶然波动。FRAME 的思路是通过多次随机重采样、Bootstrap 置信区间、中间特征探针和对比干预把偶然波动和系统性原因分开。下面的内容按照“核心概念 - 可复现实验 - 采样变异量化 - 表征原因检测 - 排查清单 - 生产实践”的顺序展开侧重医学影像场景下如何落地这套分析流程。1. 医学影像公平性评估为什么不能只看一张测试集1.1 公平性差异是“信号”还是“噪声”医学影像模型上线前最常见的步骤是划分训练集、验证集、测试集然后在测试集上报告整体 AUC、敏感度、特异度。这种做法在模型能力评估时够用但在公平性评估里容易出问题。公平性评估要回答的不是“模型整体好不好”而是“模型在哪些子组上更差差异是否稳定”。假设某模型在 A 组上的 AUC 是 0.87在 B 组上是 0.82。表面看存在 0.05 的差异。但这个差异到底意味着什么如果测试集中 B 组只有 80 例阳性、150 例阴性AUC 的置信区间可能非常宽。用不同随机种子重新划分一次测试集差异可能变成 0.02甚至反向变成 -0.01。此时0.05 并不是一个稳定结论而是采样噪声的体现。统计上任何性能指标都是随机变量。AUC、准确率、敏感度、特异度都受样本量和样本分布影响。对于医学影像数据阳性病例往往偏少病变类型多样图像质量参差采样波动会被放大。公平性评估比整体性能评估更依赖不确定性量化因为子组的样本量通常远小于全量样本。1.2 两种偏差来源采样变异与表征原因FRAME 最核心的区分是两类来源。采样变异sampling variation指由于测试集样本量有限、随机划分不稳定、各组样本构成发生变化导致观测到的公平性差异在不同评测批次之间波动。这种波动不是模型自身的属性而是评估过程带来的噪声。表征原因representational cause指模型在特征表示层面学习到了与敏感属性相关、但与真实病变无关的模式从而在不同子组之间产生系统性性能差异。比如模型依靠设备厂商的噪声纹理做判断而不是依靠病灶本身那么不同设备来源的影像就会表现出稳定的性能差异。两者的关系可以用一个简化公式理解观测到的组间差异 系统性的表征原因 采样变异 评测噪声FRAME 的目标不是只报告左侧的观测差异而是尽量分离右侧的两个主要成分。只有把采样变异控制住才能判断是否存在需要干预的表征原因。这也是整篇文章的主线。2. FRAME 的核心思想把偶然性和系统性原因拆开2.1 FRAME 要回答的三个问题FRAME 不是单一算法而是一套分析流程。针对医学影像公平性它可以拆成三个问题观测到的组间指标差异是否稳定这个问题用重复子采样和 Bootstrap 置信区间回答。模型中间特征里是否编码了敏感属性信息这个问题用探针分类器、特征统计和表征对比回答。移除或抑制这些敏感信息后公平性差异是否显著缓解这个问题用干预实验回答。三个问题对应三种不同的操作。回答不了第一个问题就直接跳到第三个问题很容易对噪声做过度解释。反过来只回答第一个问题不进入表征层面就无法定位根因。2.2 一个适合医学影像场景的分析链路实际项目中FRAME 分析链路可以按以下顺序执行定义敏感属性和子组。比较常见的是年龄组、性别、检查设备厂商、医院中心。训练或加载一个医学影像模型得到预测概率和中间层特征。固定评估规则使用多次随机子采样计算各组性能指标和组间差异。使用 Bootstrap 计算组间差异的置信区间判断差异是否跨零。对中间层特征训练探针分类器量化敏感属性的可预测程度。设计对比实验例如对抗去偏、表征重加权、后处理阈值调整观察公平性指标变化。输出结论时同时报告点估计、置信区间、样本量和可复现的随机种子。这套链路在单中心实验中就能执行。如果条件允许应该放到多中心外部验证中重复一次。多中心数据能暴露采样变异之外的环境偏移也能验证表征原因是否具有泛化性。3. 搭建一个可复现的最小实验环境3.1 实验目标与数据假设为了让流程可复现可以用一个最小实验来演示。实际医学影像项目通常使用 PyTorch 或 TensorFlow 训练模型但公平性分析本身不依赖具体模型框架。假设我们已经有一个影像模型的推理结果保存为 CSV 文件每行代表一个测试样本包含以下字段字段含义patient_id患者标识group子组属性例如 center_a 或 center_by_true真实标签1 为阳性0 为阴性y_pred模型输出的阳性概率feat_dim1 ... feat_dim64模型倒数第二层或某个中间层的特征向量在真实项目中y_pred和特征列不是手工生成的而是通过训练好的影像模型对测试集推理得到。将推理结果保存为表格是为了让公平性分析阶段不依赖 GPU 和大模型加载分析速度快也便于复现。3.2 项目结构和依赖建议按以下结构组织实验fairness_analysis/ ├── data/ │ └── image_preds.csv ├── models/ │ └── probe.py ├── analysis/ │ ├── bootstrap_diff.py │ └── fairness_table.py ├── figures/ └── results/依赖建议使用 Python 3.9 或更高版本核心包为 pandas、numpy、scikit-learn、matplotlib。安装命令如下pip install pandas numpy scikit-learn matplotlib如果后续要从影像模型里提取特征还需要根据模型框架安装 torch 或 tensorflow。这里先不引入以免干扰公平性分析的主线。3.3 训练一个基线模型官方场景中“训练模型”不是 FRAME 分析的重点但为了让实验闭环可以先用一个简单的结构化特征模型来演示。取已保存的影像特征作为输入训练分类器输出阳性概率import pandas as pd from sklearn.model_selection import train_test_split from sklearn.ensemble import GradientBoostingClassifier df pd.read_csv(data/image_preds.csv) feature_cols [col for col in df.columns if col.startswith(feat_dim)] X df[feature_cols] y df[y_true] X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.3, random_state42, stratifyy ) model GradientBoostingClassifier(random_state42) model.fit(X_train, y_train) df.loc[X_test.index, y_pred] model.predict_proba(X_test)[:, 1] df.to_csv(data/image_preds_with_pred.csv, indexFalse)这段代码的意义是生成y_pred真实项目中可以直接替换为影像模型的推理结果。不要混淆这里的model和医学影像模型它只是演示流程。3.4 按敏感属性分组计算公平性指标有了预测概率后先按 group 计算各组 AUC、敏感度和特异度。AUC 适合作为整体判别能力指标敏感度和特异度则与临床评估更直接相关。import pandas as pd from sklearn.metrics import roc_auc_score, confusion_matrix df pd.read_csv(data/image_preds_with_pred.csv) for grp in sorted(df[group].unique()): sub df[df[group] grp] auc roc_auc_score(sub[y_true], sub[y_pred]) threshold 0.5 pred_label (sub[y_pred] threshold).astype(int) tn, fp, fn, tp confusion_matrix( sub[y_true], pred_label, labels[0, 1] ).ravel() sensitivity tp / (tp fn) if (tp fn) 0 else float(nan) specificity tn / (tn fp) if (tn fp) 0 else float(nan) print(f{grp}: AUC{auc:.3f}, Sensitivity{sensitivity:.3f}, Specificity{specificity:.3f})这个输出就是初始公平性表现。注意这里只报告了点估计还不适合下结论。4. 量化采样变异重复子采样、Bootstrap 与置信区间4.1 为什么单独一次划分不可靠在公平性分析中仅用一次随机划分得到的测试集存在两个问题。第一样本量小的子组在每次划分中可能只包含几十例阳性样本AUC 波动范围超过 0.1 并不罕见。第二不同随机种子会改变测试集构成导致组间差异符号变化。要评估差异的稳定性必须重复多次子采样并观察差异分布。一种简单有效的方案是重复 Bootstrap从原始测试集中有放回地抽取与原始样本量相同的样本每次计算组间指标差异重复 1000 次得到差异的经验分布。4.2 Bootstrap 计算组间指标差异下面的代码以 A 组和 B 组为例计算两组 AUC 差异的 Bootstrap 置信区间。实际项目中可以把“AUC 差异”替换成“敏感度差异”“特异度差异”或“校准误差差异”。import numpy as np import pandas as pd from sklearn.metrics import roc_auc_score df pd.read_csv(data/image_preds_with_pred.csv) rng np.random.default_rng(42) n_boot 1000 diff_auc_boot [] for _ in range(n_boot): idx rng.integers(0, len(df), len(df)) boot df.iloc[idx] sub_a boot[boot[group] center_a] sub_b boot[boot[group] center_b] if len(sub_a) 10 or len(sub_b) 10: continue auc_a roc_auc_score(sub_a[y_true], sub_a[y_pred]) auc_b roc_auc_score(sub_b[y_true], sub_b[y_pred]) diff_auc_boot.append(auc_a - auc_b) diff_auc_boot np.array(diff_auc_boot) ci_low, ci_high np.percentile(diff_auc_boot, [2.5, 97.5]) print(fAUC diff (A-B): {np.mean(diff_auc_boot):.3f}) print(f95% CI: [{ci_low:.3f}, {ci_high:.3f}])这里的rng.integers(0, len(df), len(df))是有放回抽样对应 Bootstrap 的核心逻辑。重复 1000 次后置信区间能反映采样波动范围。4.3 判定信号置信区间是否跨零Bootstrap 计算完成后可以按以下规则初步判断情况判断置信区间完全不包含 0组间差异在统计上较稳定值得进入表征原因分析置信区间包含 0当前样本量下无法区分真实差异与采样噪声区间宽度过大样本量不足或子组分布极不均衡需要增加数据或采用分层采样需要强调的是置信区间包含 0 并不是“指标上不存在差异”而是“当前数据不足以证明差异是稳定的”。如果后续要发布成果或推动模型上线这一点必须写清楚。这里有一个常见坑先看数据中 A 组 AUC 比 B 组低 0.03直接写结论“模型对 A 组不公平”。实际上 Bootstrap 置信区间可能是 [-0.02, 0.08]跨零。这种情况下第一步判断就不支持“存在系统性差异”。相反如果置信区间是 [0.01, 0.09]才能进入下一步。5. 检测表征原因从中间特征里寻找敏感信息5.1 什么是表征层面的“不公平”当组间差异稳定存在下一步要回答差异来自模型学到的表征还是来自标签分布本身的差异医学影像中不同设备厂商的图像往往存在纹理、对比度、噪声分布差异。模型如果只靠这些设备纹理来识别病灶在没有病灶但设备纹理不同的图像上可能输出完全不同的概率。这种模式不是数据随机波动而是表征层面系统性编码了设备信息。表征原因不一定意味着模型“故意”利用敏感属性更常见的是模型为了最小化损失学习了和设备信息强相关的捷径特征。在数学上可以观测到模型中间层特征能很好地区分不同设备来源这就是敏感属性信息被编码的证据。5.2 用探针分类器找到敏感属性线索探针分类器probing classifier是评估中间特征是否包含敏感信息的常用方法。步骤是取出模型某一层的特征用它们训练一个简单分类器目标变量是敏感属性例如 center_a / center_b。如果分类准确率显著高于随机水平说明该层特征中蕴含了可分离的敏感信息。import pandas as pd from sklearn.linear_model import LogisticRegression from sklearn.model_selection import cross_val_score df pd.read_csv(data/image_preds_with_pred.csv) feature_cols [col for col in df.columns if col.startswith(feat_dim)] X_feat df[feature_cols] y_group df[group].map({center_a: 0, center_b: 1}) probe LogisticRegression(max_iter1000) scores cross_val_score(probe, X_feat, y_group, cv5, scoringroc_auc) print(fProbe AUC: {scores.mean():.3f} /- {scores.std():.3f})如果输出结果接近 0.8 或 0.9说明中间层特征确实包含了敏感属性的强信号。但这还不能直接证明模型就是利用这些信号造成不公平需要继续做干预实验。5.3 对比实验去掉可学习敏感信息后指标是否稳定常见的干预方式是对抗去偏即在训练分类器的同时让敏感属性无法从中层特征中被准确预测。实现代码较长这里用逻辑回归演示一个简化版的“敏感信息抑制”from sklearn.linear_model import LogisticRegression from sklearn.metrics import roc_auc_score # 这里用去相关后的特征替换原特征说明“抑制敏感信息”的分析框架 # 具体实现需要结合模型结构不一定直接使用线性去相关 reduced_feat df[feature_cols] - df[feature_cols].mean(axis0) clf LogisticRegression(max_iter1000) clf.fit(reduced_feat, df[y_true]) df[y_pred_debiased] clf.predict_proba(reduced_feat)[:, 1] for grp in [center_a, center_b]: sub df[df[group] grp] auc roc_auc_score(sub[y_true], sub[y_pred_debiased]) print(grp, auc)这只是一个演示。实际对抗去偏需要修改模型训练过程例如加入梯度反转层、在损失函数中增加敏感属性预测的惩罚项或者对特征做正交化处理。对比干预前后的组间 AUC 差异如果干预后差异显著缩小说明原来的差异很大一部分来自表征层面的敏感信息利用。如果差异不变那么即使敏感属性可预测也不一定是公平性差异的主要驱动因素。6. 常见误判、错误做法与排查清单6.1 三个高频误判第一个误判是只在测试集上比较一次点估计。公平性指标本身波动大必须报告置信区间或多次重复采样的分布。第二个误判是把探针分类器的高准确率直接解读为模型不公平。敏感属性可预测只说明特征中存在相关信息。真正的公平性取决于这些信息如何影响模型决策。必须结合预测结果、组间误差和干预实验综合判断。第三个误判是反复在测试集上调整阈值或重新训练直到组间差异“变好看”。这本质上是过拟合测试集。在公平性评估中测试集应该只用于最终验证阈值调整和模型选择应放在验证集上完成。6.2 排查清单在实际项目中如果公平性分析结果混乱可以按下面的清单逐项排查检查项具体做法数据划分是否使用固定随机种子是否用分层采样保证每组正负样本比例稳定样本量最小子组是否有足够事件数至少应记录每个子组的阳性例数和阴性例数指标选择AUC 对类别不均衡相对稳健但敏感度、特异度受阈值影响大要说明阈值来源置信区间是否报告了组间差异的 Bootstrap 或解析置信区间重复次数Bootstrap 抽样次数是否大于 1000稳定结果建议 2000 次以上探针评估探针分类器是否使用交叉验证是否报告了多折的平均值和标准差阈值污染是否只在测试集上使用阈值有没有反复查看测试集结果后修改阈值多重比较如果同时评估多个敏感属性是否考虑了多重假设检验校正这些检查项可以做成代码里的断言或注释发布前逐项过一遍。7. 生产环境中的医学影像公平性实践7.1 学习环境、测试环境与生产环境的差别学习环境下只要跑通代码、看到置信区间和探针结果即可。测试环境中公平性分析需要与数据版本、模型版本、推理日志关联保证每次分析可追溯。生产环境则要额外考虑多中心部署、数据分布漂移和上线后的持续监控。建议在环境初始化时创建一份配置文件记录数据路径、模型版本、随机种子、敏感属性列名和相关阈值。data: path: data/image_preds_with_pred.csv patient_id_col: patient_id group_col: group label_col: y_true pred_col: y_pred feature_prefix: feat_dim analysis: random_seed: 42 n_bootstrap: 2000 threshold: 0.5 probe_cv: 5配置外置化后复现、审计和对比实验都会方便很多。7.2 发布前需要补强的检查项医学影像模型的公平性分析结果用于临床部署决策时至少需要补强以下内容。第一多中心外部验证。单中心数据只能说明模型在该中心内部的行为。不同医院的设备、操作流程和病例构成差异会显著影响公平性结论。第二群体校准误差。除了 AUC 和敏感度还应报告每个子组的预测概率校准情况。模型可以在 B 组 AUC 很高但预测概率整体偏高或偏低导致临床决策阈值在不同组之间不可比。第三数据质量审计。检查影像采集协议、标注方法、排除标准是否在不同子组之间存在系统性差异。比如 B 组阴性样本更容易被排除那么公平性差异就可能是数据生成过程造成的而不是模型表征问题。第四记录运行环境和随机种子。公平性分析结果要可复现模型推理、特征提取、重采样都必须记录版本。7.3 扩展方向FRAME 分析思路还可以扩展为持续监控系统。模型上线后真实数据分布会随时间变化原先稳定的公平性表现可能恶化。建议定期用新的院内数据重新计算组间指标和置信区间并把结果同步到监控面板。另一个扩展方向是把表征原因分析与因果推断结合。例如通过控制敏感属性和病灶特征估计“如果两组影像在设备纹理上完全一致模型性能差异是否会消失”。这需要更严谨的实验设计但比单点探针分析更接近因果答案。对于刚开始接触医学影像公平性的团队建议先建立固定分析模板数据表格、Bootstrap 代码、探针代码、结果报告。把模板问题自动化后再逐步加入多中心验证和在线监控。任何公平性结论都不要只说“有差异”或“没有差异”而要说清差异大小、置信区间、样本量、探针结果和可能的干预方式。