ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

小病灶总被模型“忽略“?聊聊双向连通组件损失 BiCC 的设计巧思

小病灶总被模型“忽略“?聊聊双向连通组件损失 BiCC 的设计巧思 专注AI 大模型与前沿科技深度解析习惯从工程师视角拆解技术热点让我们一起在技术浪潮中保持清醒与好奇 小病灶总被模型忽略聊聊双向连通组件损失 BiCC 的设计巧思去年帮一个医学院朋友调肝脏病灶分割模型时遇到一个典型的指标幻觉验证集 Dice 干到 0.91大家都挺高兴结果医生拿去试了两例就退回来了——图像角落冒出三个绿豆大的假阳性灶而真正的早期小病灶反而漏了一个。Dice 明明很高为什么模型在临床上没法用这个问题拆开看其实是两层其一Dice、交叉熵这类损失是逐体素聚合的一个 50 体素的小病灶在百万体素的扫描里全部漏检对总损失的影响约等于零其二更隐蔽——现有的实例感知损失blob loss、CC-DiceCE只从标注里划分连通组件模型多预测出来的假阳性灶根本没有对应的实例级惩罚项。而在计算机辅助阅片场景里每一个假阳性灶都意味着医生要多花几分钟单独排查。BiCCBidirectional Connected-Component Loss就是冲着这两个坑来的。30 秒结论核心判断做多实例病灶分割肿瘤、出血灶、息肉等如果你的下游是人机协同阅片BiCC 值得替换 DiceCE 一试——它同时从标注侧和预测侧划分连通组件让假阳性灶第一次有了明确的实例级惩罚。适合谁用 nnU-Net 做医学影像分割的学生、转行者需要提升病灶级 F1、控制假阳性负担的项目。不适合谁单器官/单结构分割实例级损失无意义纯筛查场景只求极致召回、不在乎假阳性没有 GPU、只想跑通教程的入门者。关键证据体素级损失的结构性盲区体积越小的病灶对 Dice 的贡献越小临床上最危险的早期小病灶恰恰权重最低——这是数学决定的调参救不了。对照实验结果在 5 个数据集、五折交叉验证、统一使用 nnU-Net 的设定下BiCC 的病灶级 F1 在其中 4 个数据集上超过 CC-DiceCE5 个全部超过 blob loss对 DiceCE 则是 3 个数据集显著提升、2 个持平。单向实例损失的代价CC-DiceCE 为保召回会牺牲精确率最多掉了 0.363——换算到阅片场景就是大量需要人工排查的假警报。可调旋钮平衡参数 α 直接控制病灶级精确率与召回的取舍意味着同一个损失函数可以适配宁可多报和宁缺毋滥两种临床偏好。展开说明双向分区到底妙在哪先把实例感知说清楚。传统 Dice 把整张图当一个大集合算重叠实例感知损失则先用连通组件算法把标注切成一个个独立的病灶每个病灶单独算一项损失再聚合。这样小病灶和大病灶在目标函数里平权——50 体素的早期灶和 5000 体素的巨块灶漏掉任何一个都同样疼。问题在于blob loss 和 CC-DiceCE 的组件只从 GT 标注导出。想象老师批改作业只对着标准答案逐题检查你漏了哪题却从不检查你多写了什么。模型预测出的假阳性组件只能被体素级的背景项间接、微弱地惩罚。BiCC 的思路用一句话概括双向各划一次组件各管一件事。L_BiCC (1 − α) · L_gt分支 α · L_pred分支GT 分支从标注划分组件每个真病灶一项——漏检会被抓住管召回。预测分支从预测划分组件每个预测灶一项——假阳性灶无论多小都挨罚管精确率。α 就是那条精确率-召回率曲线的操纵杆。这个设计对在校生有个额外福利就算不换损失函数病灶级评估代码本身也是作品集里很加分的一段。下面是个最小可用版本importnumpyasnpfromscipyimportndimagedeflesion_wise_stats(pred:np.ndarray,gt:np.ndarray):病灶级 TP / FP / FN任一重叠即记为命中pred_lab,n_predndimage.label(pred)# 预测侧连通组件gt_lab,n_gtndimage.label(gt)# 标注侧连通组件matched_pred,tpset(),0forginrange(1,n_gt1):hitsnp.unique(pred_lab[gt_labg])hitshits[hits0]iflen(hits)0:tp1matched_pred.update(hits.tolist())fpn_pred-len(matched_pred)fnn_gt-tp f12*tp/(2*tpfpfn1e-8)return{TP:tp,FP:fp,FN:fn,lesion_F1:f1}十几行代码就能把评估从体素对不对升级到病灶抓没抓住、误报几个——这正是 BiCC 想优化的那个指标也是面试时讲清实例级 vs 体素级最直观的素材。落地建议今天就给项目加上病灶级评估。用上面这段代码scipy.ndimage.label即可无需额外依赖在报告里同时给出 Dice 和 lesion-wise F1、每例平均 FP 数。面试官追问你的指标有什么局限时这就是现成的答案。在 nnU-Net 里换损失跑一次对比。BiCC 已开源github.com/TIO-IKIM/BiCC-LossnnU-Net 换损失只需改 trainer 配置里的 loss 项。哪怕只跑一个 fold复现并对比三种实例级损失也足以写进简历。扫一遍 α 画权衡曲线。取 α ∈ {0.3, 0.5, 0.7} 各训一版画出病灶级 precision-recall 的变化。这条曲线本身就是你理解损失函数设计的证据比任何熟悉深度学习的自我评价都有说服力。风险与反例不是全面碾压BiCC 在 5 个数据集里有 2 个只是与 DiceCE 持平。如果你的数据集病灶大且少体素级损失可能就够用别为了用新方法而用。场景错配会帮倒忙筛查类任务如肺癌初筛往往宁可错杀不可放过此时用 α 压假阳性反而偏离临床目标——先和下游用户确认他们更怕漏还是更怕误。连通组件对噪声敏感预测里的散点噪声会产生大量碎小组件实际使用时通常需要最小体积过滤或后处理配合否则实例级指标会很难看。小样本指标抖动病灶级指标按个计数验证集只有几十例时一个病灶的归属变化就能让 F1 跳好几个点——务必做交叉验证再下结论。损失函数的本质是把业务在乎什么翻译成数学。BiCC 的启发不在于它多复杂而在于它指出了一个常被忽略的视角分区不仅可以从标注来也可以从预测来。下次你设计自己的目标函数时不妨也问一句——我的假阳性有人管吗
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进