跳到主要内容

分割与微调:让模型自己找出候选,不再靠人工清单

这一章讲三件事: 分割和分类有什么本质不同;SAM 这种「基础模型」是什么、能直接拿来干什么;以及本章的核心手法——用大模型的输出当标签,微调一个小模型替它干活。 位置:三步流水线的第 ② 步。做完这章,candidates.csv 这份人工清单就被自己的模型取代了。本章相对独立:新模型、新数据集、新训练循环。

1. 分类回答「有没有」,分割回答「在哪」

第 13–14 章的分类器有一个隐藏前提:候选点由 candidates.csv 给好。可那份清单是 LUNA 比赛方请人标出来的——换一批 CT、换一家医院,清单就没了。流水线还差一环:从整张 CT 里把「值得分类的位置」自己找出来1

这就是分割(segmentation):不再给整张图一个标签,而是给每个像素一个标签——这块像结节、那块不是,输出是一张和输入一样大的「热图」。书里的对比很形象:分类模型像漏斗或放大镜,一大把像素进去、一个标签出来;分割要的是图像进、图像出2

邻近的三个任务别混1。给每个像素分类的做法,这叫语义分割——「语义」就是「按内容是什么」:

任务输出
语义分割每个像素属于哪一类「这些像素是结节」
实例分割每个像素属于哪个对象「这是结节 1、那是结节 2」
目标检测对象外接框「结节在这个框里」

本章选语义分割(给每个像素标上「属于哪一类」):医学上要的是「精确到体素的范围」,不是框,而同类结节之间不必区分个体1

2. SAM:点一下就出掩码的通用分割模型

自己从零训一个分割模型,标签从哪来?给每个体素标「是不是结节」,是放射科医生级别的活——贵到不现实。书里的答案是站在一个现成巨人的肩膀上:SAM(Segment Anything Model,Meta 2023 年发布)3

SAM 的用法和普通模型不一样:你给它图,再给提示——点一个点、框一个框,或给一张粗略掩码——它就吐出这个提示所指物体的分割掩码。点一下照片里狗的鼻子,得到三张嵌套的掩码:整条狗、鼻子、鼻子的局部,各带一个置信度4。它是零样本(zero-shot)的:从没见过你的数据类别,不用训练直接能用——靠的是 1100 万张图、11 亿张掩码训出来的通用「什么东西算一个物体」的直觉3

结构上它是三个 ViT 系组件(第 10 章讲的 Transformer 在图像上的用法)的接力:图像编码器把整图压成特征;提示编码器把你的点/框/掩码也变成特征;掩码解码器把两者合并,生成最终掩码5

试用一把(走查的第一步):随便拿一张普通照片(书里用的是一张宇航员照)喂给自动掩码生成器,不点任何提示,它自己扫全图,返回 79 张掩码——头盔、背包、手臂,各自独立成片。每张附带的 predicted_iou 是模型自报的置信度(「我觉得这掩码有多完整」),不是真和答案比出来的分数——书里特地标了这一点,它的值甚至可以超过 16

两句冷静的提醒:

  • SAM 的训练数据是自然照片。书里的专栏直说:自然图像和医学影像之间有「领域差距」(domain gap),它在 CT 上表现如何要试了才知道,不能默认行7
  • 用别人的模型先看许可证:SAM 用 Apache 2.0(宽松、可商用),但**「网上能下载」不等于公有领域**,而且代码的许可证和权重的许可证可以是两份8

3. 主走查:从一张 CT 切片到一个微调好的分割模型

SAM 有个硬限制:它只吃 2D 图,而我们的 CT 是 3D 体。书里正面接受了这个妥协:沿轴抽出 2D 切片来用。代价有两个,都记着账:切片厚度不一被忽略(让模型学着扛);3D 上下文(上下切片里的连续性)整条丢掉9

限制变成了机会,本章的核心手法登场——既然 SAM 要提示才干活,那就让它当「标注工」10:

① 取切片:getSingleSlice 从 3D CT 沿轴切出 2D 灰度图
② 给提示:标注文件里有结节中心点——正好当 SAM 的点提示
③ 收掩码:SAM 输出该点的结节掩码,存成二值图
④ 造数据集:几千组「CT 切片 + SAM 掩码」,配 metadata.jsonl 记录出处
⑤ 微调小模型:拿 ④ 当标准答案,训练 SegFormer 不看提示直接出掩码

图说:大模型出一次力,小模型从此自立。SAM 只在造数据时登场,部署时没有它。

第 ⑤ 步是本章第二个核心概念:微调(fine-tuning,第 03 章讲嵌入表时露过一次名,这里正式定义)——拿一个已经在别的大数据集上训好的模型,在自己的数据上继续训练。它不从头学「什么是边缘、什么是纹理」,只学「在这个新任务上该怎么输出」,所以小数据、单张 GPU 就够11

被微调的模型是 SegFormer(轻量版 nvidia/mit-b0,一个专为语义分割设计的 Transformer):预训练权重全保留,只把解码头(最后把特征变成掩码的那几层)换掉重训。加载时 Hugging Face 会警告「decode_head 的权重是 newly initialized(新初始化的)」——这正是预期的,不用慌12

走查的最后两步,细节各有一句讲究:

  • 优化器用 AdamW(Adam 的变体,把权重衰减从梯度里拆出来单独施加;Adam 本身是第 05 章见过的自适应学习率优化器),学习率 6×10⁻⁵,和 SegFormer 原论文保持一致13
  • 训练循环能短得意外:Hugging Face 的 SegformerImageProcessor 管预处理,model(pixel_values=…, labels=…) 直接返回损失——把掩码当 labels 传进去,框架内部自己算好损失,20 个 epoch 训练和验证损失一起降14

保存模型时,书里重申第 08 章的规矩:存 state_dict(参数字典),别用 pickle 存整个模型对象——后者把类的路径也腌进了文件,代码一重构就加载不回来;而 load_state_dict 要求参数名一一对应,所以加载前要把模型结构原样建好15

推理端走一遍:新 CT → 切片 → 处理器 → SegFormer → 每张切片一张「疑似结节」热图 → 热图里连成片的高分区取中心,就是 candidates.csv 的替代品——流水线闭环了16

4. 作者的判断与证据

说法性质
选语义分割而非实例/检测论证选择,理由是医学上要体素级范围1
SAM 零样本能力、11M 图/1.1B 掩码论文事实(arXiv:2304.02643),书里转述3
自然图像到医学影像有领域差距作者警告 + 引外部研究(arXiv:2304.14660),方向可靠7
「SAM 掩码当标签训小模型」是合理工程本章的赌注:SAM 掩码不是医生标的,标签噪声进了训练集;书里靠最后那张分割结果图论证「够用」,未做系统的噪声分析10
只微调解码头、单卡可训有据,mit-b0 轻量版 + 训练损失实测12
AdamW lr=6×10⁻⁵沿用原论文设置,未在本任务上调过13

5. 边界与局限

  • 切片法丢了 3D 上下文:一个横跨三张切片的小结节,每张上都只剩淡影——书里承认这是拿精度换简单9
  • SAM 的掩码里混着它自己的错误,这些错误会被 SegFormer 当成真理学进去;更稳的做法是加一道筛(比如只保留和标注直径相符的掩码),书里没做。
  • predicted_iou 是自报置信度,别当质量分用6
  • 切片厚度不一致被「让模型学着扛」一句带过——换了扫描规程要重查。

6. 可带走的

  1. 先问任务要「有没有」还是「在哪」;要位置就别指望分类模型,分割/检测才是对的工具。
  2. 基础模型当标注工,小模型当工人:大模型造标签、小模型学手艺,绕开手工标注的成本墙。
  3. 零样本不等于零风险;先看领域差距,再看许可证(代码和权重可以是两份)。
  4. 模型吃不了的维度,就切片迁就它——但把丢掉的上下文记在账上。
  5. 微调 = 保留预训练权重,只重训输出头;「newly initialized」警告是预期,不是错误。
  6. 置信度和真分数是两回事(predicted_iou 能超过 1)。
  7. 存 state_dict 不存整模型;load_state_dict 要求结构 1:1 对应。

7. 原文地图

主题原书章原文位置
语义/实例/检测三分ch15text/23-ch15-15-using-segmentation-to-find-suspected-nodules.txt:113(搜「classifying individual pixels」) · :122(搜「instance segmentation」)
放大镜比喻ch15text/23-ch15-15-using-segmentation-to-find-suspected-nodules.txt:153(搜「magnifying glasses」)
SAM 与零样本ch15text/23-ch15-15-using-segmentation-to-find-suspected-nodules.txt:193(搜「2304.02643」) · :215(搜「zero-shot segmentation」) · :223(搜「1.1 billion masks」)
SAM 三组件ch15text/23-ch15-15-using-segmentation-to-find-suspected-nodules.txt:241(搜「Mask decoder」)
狗鼻子三掩码ch15text/23-ch15-15-using-segmentation-to-find-suspected-nodules.txt:269(搜「the nose of the dog」)
79 掩码与 predicted_iouch15text/23-ch15-15-using-segmentation-to-find-suspected-nodules.txt:352(搜「predicted_iou」) · :359(搜「79 masks」)
领域差距专栏ch15text/23-ch15-15-using-segmentation-to-find-suspected-nodules.txt:446(搜「domain gap」)
Apache 许可证专栏ch15text/23-ch15-15-using-segmentation-to-find-suspected-nodules.txt:282(搜「Apache 2.0 license」)
2D 限制与切片妥协ch15text/23-ch15-15-using-segmentation-to-find-suspected-nodules.txt:480(搜「2D image processing」)
SAM 掩码当 ground truthch15text/23-ch15-15-using-segmentation-to-find-suspected-nodules.txt:634(搜「ground truth」)
数据集结构ch15text/23-ch15-15-using-segmentation-to-find-suspected-nodules.txt:661(搜「metadata.jsonl」)
SegFormer 与 mit-b0ch15text/23-ch15-15-using-segmentation-to-find-suspected-nodules.txt:803(搜「nvidia/mit-b0」) · :823(搜「newly initialized」)
AdamW 与学习率ch15text/23-ch15-15-using-segmentation-to-find-suspected-nodules.txt:847(搜「AdamW」)
训练循环ch15text/23-ch15-15-using-segmentation-to-find-suspected-nodules.txt:901(搜「labels=labels」)
state_dict 保存ch15text/23-ch15-15-using-segmentation-to-find-suspected-nodules.txt:963(搜「state_dict」)
推理与结果图ch15text/23-ch15-15-using-segmentation-to-find-suspected-nodules.txt:1033(搜「ground truth mask」)

Footnotes

  1. 出处:「15 Using segmentation to find suspected nodules」第 113 段(text/23-ch15-15-using-segmentation-to-find-suspected-nodules.txt:113,搜「classifying individual pixels」)与第 122 段(:122,搜「instance segmentation」)。语义分割=给单个像素分类;实例分割逐对象区分标签;目标检测给外接框;本章选语义分割。 2 3 4

  2. 出处:「15 Using segmentation to find suspected nodules」第 117 段(text/23-ch15-15-using-segmentation-to-find-suspected-nodules.txt:117,搜「heatmap」)与第 153 段(:153,搜「magnifying glasses」)。分割输出是「标记感兴趣区域的掩码或热图」;分类模型是「漏斗或放大镜」:一大把像素聚焦成一个标签。

  3. 出处:「15 Using segmentation to find suspected nodules」第 193 段(text/23-ch15-15-using-segmentation-to-find-suspected-nodules.txt:193,搜「2304.02643」)、第 215 段(:215,搜「zero-shot segmentation」)与第 223 段(:223,搜「1.1 billion masks」)。SAM(Meta,arXiv:2304.02643)是第一个展现基础模型潜力的分割模型;在 1100 万张图、11 亿张掩码上训练,零样本分割。 2 3

  4. 出处:「15 Using segmentation to find suspected nodules」第 235 段(text/23-ch15-15-using-segmentation-to-find-suspected-nodules.txt:235,搜「points, boxes」)与第 269 段(:269,搜「the nose of the dog」)。提示可以是点/框(稀疏)或掩码(稠密);对狗鼻子的点提示输出三张嵌套掩码:整条狗、鼻子、鼻子局部。

  5. 出处:「15 Using segmentation to find suspected nodules」第 227 段(text/23-ch15-15-using-segmentation-to-find-suspected-nodules.txt:227,搜「three main compo」)与第 241 段(:241,搜「Mask decoder」)。SAM=图像编码器(ViT)+提示编码器+掩码解码器;解码器合并图像特征与提示嵌入生成掩码。

  6. 出处:「15 Using segmentation to find suspected nodules」第 352 段(text/23-ch15-15-using-segmentation-to-find-suspected-nodules.txt:352,搜「predicted_iou」)与第 359 段(:359,搜「79 masks」)。自动掩码生成器对宇航员图返回 79 张掩码;predicted_iou「只是模型在预测置信度」,值可超过 1。 2

  7. 出处:「15 Using segmentation to find suspected nodules」第 443 段(text/23-ch15-15-using-segmentation-to-find-suspected-nodules.txt:443,搜「General-purpose models vs. medical imaging」)与第 446 段(:446,搜「domain gap」)。专栏:「自然图像和医学影像之间存在领域差距」;SAM 不是为医学影像设计,引用 arXiv:2304.14660。 2

  8. 出处:「15 Using segmentation to find suspected nodules」第 282 段(text/23-ch15-15-using-segmentation-to-find-suspected-nodules.txt:282,搜「Apache 2.0 license」)。专栏:SAM 用宽松的 Apache 2.0;没有许可证不等于公有领域;代码与权重的许可证可以不同。

  9. 出处:「15 Using segmentation to find suspected nodules」第 478 段(text/23-ch15-15-using-segmentation-to-find-suspected-nodules.txt:478,搜「limitation on 2D data」)与第 486 段(:486,搜「thicker than the resolution」)。SAM 专为 2D 图像设计,需从 3D CT 抽切片;代价:切片往往比面内分辨率厚、忽略确切切片厚度,「模型最终得学着扛」。 2

  10. 出处:「15 Using segmentation to find suspected nodules」第 634 段(text/23-ch15-15-using-segmentation-to-find-suspected-nodules.txt:634,搜「ground truth」)与第 644 段(:644,搜「really expensive to annotate manually」)。用 SAM 生成的掩码当微调数据集的 ground truth——「手工标注实在太贵」,再用它微调轻得多的模型,让分割不用提示自动完成。文中提到的结果图是原书的图 15.17。 2

  11. 出处:「15 Using segmentation to find suspected nodules」第 778 段(text/23-ch15-15-using-segmentation-to-find-suspected-nodules.txt:778,搜「fine-tun」)与第 785 段(:785,搜「single GPU」)。微调=预训练模型在新数据集上继续训练;SegFormer 轻量,单张 GPU 可训。

  12. 出处:「15 Using segmentation to find suspected nodules」第 803 段(text/23-ch15-15-using-segmentation-to-find-suspected-nodules.txt:803,搜「nvidia/mit-b0」)与第 823 段(:823,搜「newly initialized」)。基座选 nvidia/mit-b0;加载时警告 decode_head 权重「newly initialized,You should probably TRAIN」——微调输出头正是计划内的事。 2

  13. 出处:「15 Using segmentation to find suspected nodules」第 847 段(text/23-ch15-15-using-segmentation-to-find-suspected-nodules.txt:847,搜「AdamW」)与第 858 段(:858,搜「0.00006」)。AdamW 是 Adam 的变体(带权重衰减);lr=0.00006 沿用 SegFormer 原论文。 2

  14. 出处:「15 Using segmentation to find suspected nodules」第 875 段(text/23-ch15-15-using-segmentation-to-find-suspected-nodules.txt:875,搜「SegformerImageProcessor」)与第 901 段(:901,搜「labels=labels」)。SegformerImageProcessor 负责预处理;model(pixel_values=…, labels=…) 直接返回损失;20 epoch 训练与验证损失同降。

  15. 出处:「15 Using segmentation to find suspected nodules」第 955 段(text/23-ch15-15-using-segmentation-to-find-suspected-nodules.txt:955,搜「pickle」)与第 970 段(:970,搜「dictionary object」)。torch.save 用 pickle,存整模型会失灵活性;state_dict 返回参数名到张量的字典;load_state_dict 要求 1:1 对应。

  16. 出处:「15 Using segmentation to find suspected nodules」第 1011 段(text/23-ch15-15-using-segmentation-to-find-suspected-nodules.txt:1011,搜「image_processor」)与第 1033 段(:1033,搜「ground truth mask」)。推理:image_processor 预处理 + post_process_semantic_segmentation 还原尺寸;图 15.17 对比 CT 原图、ground truth 掩码与模型预测。