< Back to Blog
#Projects#Image Segmentation#Algorithm

从大创到毕设,我的“科研思想”进化之路

Projects3912 words

毕业设计结束之后,是时候记录下了,我想。

在做毕业设计的时候,我就一直想为这段时间发生的各种事,以及我的成长想个方式记录下来,正好自己的博客也完成的差不多了,或许是个记录的好方法。

整个毕业设计从最开始连方向都不确定,从壮着胆子去问本科的指导老师有没有“合适项目”的尝试;后来不断长成为我的大创项目、我与队友们一起完成了一个系统、最后还实现了算法的小创新。

现在回头看,整个过程最让人记忆深刻的不是学习到的知识,也不是读了多少论文和多少尝试,更不是最后的看起来蛮不错的成果,而是一个看起来很通俗但又明确的教训和经验:有时候遇到了失败,不要着急画句号,把他当作一个具体的指路牌,会更好的看清接下来的方向。


起点:一个挑战赛

大二下学期,我去找本科阶段的导师,想问问有没有适合参与的项目,去参加大学生创新创业项目,因为当时学长十分推荐我去试一试。那时我对“医学图像”还没有多清晰的概念,对“分割”这个词的理解也停留在把图像里的某个区域圈出来,正如计算机导论中那些经典的yolo算法一样。老师给我介绍了 PENGWIN 2024 骨盆骨折分割挑战赛,让我先围绕这个比赛自己试一试,往下发挥。

PENGWIN 的任务很直观:给定骨盆骨折患者的 CT 或 X-ray 数据,让算法自动分割骨折碎片。公开说明里提到,这类任务之所以困难,是因为骨盆结构复杂,骨折碎片形态和位置变化很大,骨折面又常常因为碰撞、压缩、粘连而变得模糊。

这是研究的行业前沿,而我一个纯纯小白,很难上手去做这种工作,但当时的我并不懂,觉得只是分离一个目标应该不难,所以当初我还有着创新的野心。

PENGWIN Task 1 的训练集在 Zenodo 上公开了 100 例 CT 及标签,完整数据来自 150 名患者,有50例作为官方的测试集,给出比赛最后的评分效果,这些数据清晰的标注了骶骨、左右髋骨及其碎片,并给出了详细的介绍说明,是理想的数据集。

当时的我对这些困难并没有真正的体感。我的第一反应更像是:既然有数据、有比赛、有任务,那就先把系统搭起来。

于是“折磨”我一整年的大创就这样开始了。

那一年,我和几位队友一起做了一个基本的医疗管理系统。系统本身要完成数据管理、模型调用、结果展示等功能,听起来像一个完整项目,做起来也确实花了很多时间。前端、后端、数据流、模型接口,很多东西都是边查边写,边写边推翻,常常熬到了凌晨四五点钟,才发觉必须要休息下了。

而其过程中最难的,在我看来或许是与队友的合作,需要解释清楚任务需求,预期效果等等,队友们要相互知道每个人的负责的部分以及对方的工作进度,而这一段团队合作的开发经历也让我深受其苦 (后面有机会再写一个文章记录下,不是这次的重点)。

到了中期,系统终于有了基础的样子,但老师指出了一个更关键的问题:系统可以展示结果,可我们的医学图像分割算法也需要有创新,直接拿来别人的工作不够达到大创的要求。

这句话其实挺扎心的。因为系统一点点磨出来以后,人很容易误以为自己已经完成了大部分工作,至少给人感觉的工作量很清晰。但在医学图像分割里,界面只是“把结果端出来”,真正难的是结果从哪里来,而这一点,却很难明确和别人介绍背后的工作量。

第一个 U-Net,以及第一次失败

我就是从那个时候正式开始补医学图像分割的知识。

最开始当然绕不开 U-Net。它是医学图像分割里很经典的编码器-解码器结构,一边下采样提取语义,一边上采样恢复空间细节,中间再用跳跃连接把浅层纹理信息带回来。听上去不复杂,所以我也很自然地产生了一种朴素自信:那我自己写一个试试。

于是我手撸了第一个 U-Net。

我的第一个U-net

现在想想,那大概是最有意义的一段代码。模型能跑,loss 会降,结果也能可视化,看到自己的显卡资源吃满,终端一点点的报出新一轮的信息,loss曲线一点点的下降,真是令人幸福的一件事。

但效果仍然不好。后来我又试着跟着AI的建议加深度可分离卷积、空间通道双注意力、SE block 之类的小模块。那个阶段的我读论文还很少,更多是在已有模块里挑一些看起来“可能有用”的东西往网络里胡乱放,再听取ai的奉承,觉得自己做的还算不错。

结果并没有因为模块变多就突然变好,虽然有些许提升,但仍然不够。

更残酷的是,它仍然打不过比赛的 baseline。

这件事给我的冲击不小。忙了一年,系统做出来了,模型也改了不少,熬了几个月的夜,健身也废弃了 (真的怕猝死了),但最后算法效果并不理想。那时候我第一次意识到,深度学习不是把几个“高级模块”拼起来就会自动变强。尤其在医学图像里,数据量、标注方式、预处理、损失函数、后处理、评价指标,每个环节都可能比“再加一个 attention”更重要。

也正是这次失败,把我推向了毕业设计真正的问题:不要急着发明一个复杂网络,先理解强 baseline 为什么强,再看它到底还有什么提升的空间。

从 nnU-Net 开始重新学

毕设阶段,在学长的帮助下,我把 nnU-Net 复现了出来。

nnU-Net 的厉害之处不只是网络结构,而是它把医学图像分割里大量“工程经验”系统化了。它会根据数据属性自动配置预处理、patch 大小、网络拓扑、训练策略和后处理,所以很多时候它不是一个普通 baseline,而是一条很扎实的分割流水线。nnU-Net 的论文也提到,它可以在无需大量人工调参的情况下,在多个公开医学分割任务上超过许多专门设计的方法。

复现 nnU-Net 之后,我终于有了一个更可靠的参照物。接着我读到了 FracSegNet 相关工作。它把任务拆成两步:第一步先定位左右髋骨和骶骨,第二步在目标骨区域内分割主要碎片和次要碎片。还引入了骨折距离图,也就是 FDM,让模型更关注骨折接触面附近的区域。

这个思路对我启发很大。

因为骨折分割最麻烦的地方,往往不是一整块骨头能不能分出来,而是相邻碎片之间那一点点缝隙有没有被填平,小碎片有没有被漏掉,断端附近有没有冒出奇怪的假阳性。整体 Dice 看起来可能已经很高了,但如果两个本该分开的骨片被模型粘在一起,三维重建和术前规划里就会出现完全不同的空间关系,分割的质量也不能说是多么好。

换句话说,这个任务需要的与现实世界的物体分割不同,现实世界的分割看起来差不多,然后可以识别出来物体在大部分情况下就足够了。而医学图像的要求更高,漏掉了碎片或者没有找到骨裂的部分,这次分割无论怎么也不能说是成功。

三月的那篇新文章

就在我做完 nnU-Net 和 FracSegNet 复现,正愁着怎么进一步提升的时候,和老师的一次谈话,老师又给了我一篇三月份的新文章,方法叫 SCNP,全称是 Same Class Neighbor Penalization。

这篇文章的出发点很巧妙:很多分割模型在整体区域上表现不错,但无法保证拓扑结构准确。比如细长结构断开、小区域凭空出现、原本分离的结构被粘连。SCNP 的做法是在训练时对 logits 做一个邻域传播:前景位置会受到同类邻域中较弱响应的影响,背景位置会受到同类邻域中更容易混淆响应的影响。直观地说,它让模型可以额外照顾周围同类邻居里最容易出错的地方。

SCNP方法图示

这个想法一下子和骨折分割对上了。

骨折碎片的问题恰好就是局部结构问题。一个窄缝被填平,一个小碎片被吞掉,一个断端附近冒出假阳性,都是“整体大区域看起来还行,但局部连接关系错了”的典型情况。SCNP 的设计很对口,更值得一提的是其有一个工程上的特点,让人很难拒绝:它主要改变训练阶段损失计算前的 logits,不需要改推理流程,也不需要重新设计一整套复杂网络。

不过,直接把 SCNP 用到骨盆 CT 上也有风险。CT 里有噪声,也有远离骨折面的稳定骨区域。如果在所有地方都做邻域传播,可能会把一些无关区域的局部错误也放大。

于是我在毕业设计中,取FracSegNet和SCNP的特点,做了一个折中:把 FracSegNet 里的 FDM 思想和 SCNP 结合起来,用骨折距离图做空间门控,让 SCNP 更多作用在骨折接触面附近的高风险区域。

这也相当于构造了一个与SCNP表现最好的狭窄空间的状况相符的空间。

我的毕业设计做了什么

最后,我认为核心可以概括成三件事。

第一,复现并整理骨盆骨折 CT 分割的基线流程。实验以 nnU-Net 为统一框架,对比 Max-flow、标准 nnU-Net、FracSegNet 基线以及不同 SCNP 变体。标签也被整理为背景、主要碎片、次要碎片三类语义空间。

第二,在损失计算前引入 SCNP 邻域结构传播。它不改变网络推理结构,而是在训练时让局部困难区域更直接地参与优化,从而补充 CE + Dice 对碎片边界、小缝隙和局部连接关系约束不足的问题。

第三,把 FDM 和 SCNP 结合成门控版本。FDM-gated SCNP 会把结构传播集中在骨折面附近,减少稳定骨区域或噪声区域里不必要的传播。实验里我粗略的比较了不同感受野和不同门控阈值,最后发现 rf = 3, tau = 0.3 是一个比较均衡的设置。

我的方法pipeline

在本地统一后处理和统一评价口径下,FDM-gated SCNP 相较 FracSegNet 复现基线取得了更均衡的结果:All DSC 达到 0.990 ± 0.012,All LDSC 达到 0.990 ± 0.013,All HD95 为 0.868 ± 3.766 mm,IoU 为 0.883 ± 0.142。其中 HD95 相较 FracSegNet 基线降低约 40.3%,IoU 提升约 3.2%。

这些数字当然不能被理解成“问题解决了”。医学图像里没有这么轻松的胜利。它们更像是说明了一件小而具体的事:在强 baseline 之上,如果把训练目标往局部结构上推一把,模型确实能在边界距离、局部 Dice 和碎片分离质量上变得更稳定一些。

结果之外:我真正学到的东西

这次毕设最让我受用的地方,是我对“创新”的理解变了。

最开始我以为“创新”就是加模块、换结构、把网络画得更复杂(现在看来很不可思议,当初为什么会这样想🤔)。后来发现,在一个真实任务里,创新的第一步是把问题讲清楚:到底是整体分割不准,还是局部边界不稳?是模型容量不够,还是损失函数没有惩罚到真正重要的错误?是需要更大的感受野,还是需要把传播范围收住?

我的第一次 U-Net 没有打过 baseline,这逼着我不得不承认自己对任务理解太浅。后来复现 nnU-Net 和 FracSegNet,又让我看到强方法往往不是凭空强,而是把数据组织、标签设计、训练策略、后处理和评价指标等等整个流程的所有环节都考虑进去了。而最后引入 SCNP,也不是因为它看起来新,而是它刚好回应了这个任务里反复出现的局部结构错误。

从大创到毕设,这个项目像是绕了一圈。最开始我想做一个系统,后来发现系统背后的算法不够好;我去改模型结构,结果发现随手加模块没有意义;我开始复现强 baseline,才逐渐看清真正的问题;最后回到一个很小的改动:训练时,让模型多看看附近区域里最难的那一块。

有时候毕业设计的意义大概就在这里。它不一定让你做出一个改变世界的东西,但它会让你经历一次完整的、带着挫败感的学习过程。你会知道一个结果是怎么来的,也会知道它为什么还不够好。

还没有结束的问题

当然,这个工作仍然有很多局限。

目前的标签空间仍然是背景、主要碎片、次要碎片三类语义分割,并不能真正输出每一个碎片的实例编号。对于复杂多碎片病例,尤其是非常小的碎片,统一后处理仍然可能漏掉或合并。FDM 门控也依赖任务先验,阈值和感受野主要来自实验选择,还没有变成自适应机制。

未来如果要继续做,应该把结果放进三维重建、复位规划、路径设计这些下游任务里,看局部边界的改善究竟能不能带来真实流程上的收益。

但至少对我来说,这个毕设已经完成了它最重要的任务:它让我从“能不能跑起来”走到了“为什么这样设计”,也让我第一次真正理解,一个算法项目最难的地方,往往不是写出网络,而是找到值得被优化的错误。

References

参考与延伸阅读

这里列出写作时用到的公开资料和论文线索,作为背景补充。

Copyright
  • Author:Mark Xu
  • Copyright:All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating otherwise.

© 2026 Mark Xu. All rights reserved.

All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating otherwise.

A small site built with React + Vite + Tailwind