SAM2这套模型出来之后,多数人的第一反应是直接用预训练权重做推理,毕竟效果摆在那里,视频目标分割做得确实漂亮。但真到了项目落地阶段,你很快就会意识到一个现实问题:通用模型不等于专用模型。拿工业场景来说,你检测的可能是某种特殊零件的表面缺陷,也可能是一批特定品类的农作物,这些数据在预训练阶段根本没出现过,直接拿SAM2去分割,结果往往差强人意。这时候你就得考虑自己训练(或者说微调)SAM2了。这篇文章我打算把SAM2训练的整个流程拆开讲清楚,从底层逻辑到数据准备、从脚本配置到参数调优、再到各种坑的排查方法。内容主要面向已经跑通SAM2推理、准备进入训练阶段的开发者,以及正在训练过程中被各种问题卡住的人。我会尽量把每个关键节点的why讲清楚,不只告诉你按钮在哪,还会告诉你为什么该按这个按钮。
1. SAM2训练的底层逻辑与方案选择
1.1 模型结构的可训练模块拆解
先搞清楚你在训练什么。
SAM2整个模型其实是一套多组件协作的流水线,主要包含四个结构块:图像编码器(Hiera)、记忆注意力模块(Memory Attention)、提示编码器(Prompt Encoder)、掩码解码器(Mask Decoder)。当你执行训练脚本的时候,这四个模块会根据你的配置进入不同的训练状态。
图像编码器负责把每一帧图像转换成语义特征,它是整个模型里参数量最大的部分,也是计算开销的大头。如果你了解视觉Transformer,可以把Hiera理解为一种带层次化结构的ViT变体,它对空间分辨率比较敏感,你输入多大的图,它就在多少分辨率下做特征提取。
记忆注意力模块是SAM2区别于初代SAM的关键所在。它会把历史帧的掩码和特征编码成记忆,然后在处理当前帧时用注意力机制参考这些记忆信息,从而实现对视频目标的一致跟踪。这也是SAM2能做视频分割的核心原因。提示编码器把用户输入的点、框、掩码转换为特征向量,掩码解码器则负责生成最终的分割结果。
从训练的角度来说,你要重点区分两个问题:你是只为了让模型适应你的数据分布,还是要同时增强模型对时间维度的记忆能力?这两个问题对应不同的微调策略。
如果只是让模型认识新的目标类型,那通常只需要微调掩码解码器和提示编码器,图像编码器和记忆模块可以冻结。因为你的目标类型可能只是在特征层面跟已有类别有差异,解码器这层已经足够处理这种差异。但如果你的场景对时间一致性要求很高,比如要让模型在长时间视频里不丢失目标,那记忆注意力模块也得一起参与训练。
我自己测下来的经验是,大多数业务场景下,微调解码器加提示编码器就能解决70%的问题,完全不需要动那个最大的图像编码器,训练时间和显存都能省下一大截。
1.2 从零训练、领域微调、参数高效微调怎么选
这里插一段方案选型的内容。很多人一上来就在问“能不能从头训练SAM2”,我的建议是能,但不要轻易做。
从零训练意味着你要有足够量的数据,还要准备足够长的训练时间。SAM2这种规模的模型,训练通常都是在上百块GPU上跑出来的,普通条件根本扛不住。而且从零训练要处理的事情特别多,比如学习率预热策略、不同阶段的损失配比、数据加载的吞吐优化等等,光是调通这些问题就要花掉好几周。更现实的问题是,官方预训练模型已经学习了海量图像和视频的通用规律,你从头训练反而很难超过这个基线。
相比之下,领域微调是最务实的方案。你在官方权重的基础上,用自己标注的数据去做有监督训练,让模型适应你的领域。这里有个细节,官方提供了好几种预训练权重,微调时用哪个?我的建议是直接加载和你输入分布最接近的那个,比如你做视频任务就优先加载视频训练出来的权重,做单张图像的任务就加载图像训练的权重,不要盲目选一个。
还有一种思路是参数高效微调,比如LoRA。这个方案在SAM2上也有人尝试过,好处是显存占用小、训练速度快,适合那种只有一两张卡、但又需要频繁迭代模型的团队。但要注意,LoRA会引入对基础权重的近似误差,如果你对最后的mask质量要求很高,还是老老实实做部分模块微调更稳妥。
我遇到过一个小团队,手里只有两张消费级显卡,用了LoRA微调,训练24小时就搞定了,效果能到全量微调的90%左右。如果你的场景本身分割难度不大,这个方案确实值得考虑。
1.3 训练数据流的关键链路
训练时的数据流大致是这样的:视频序列按固定帧率采样,每一帧和对应的标注掩码配对送入模型,图像编码器提取特征,记忆模块维护历史上下文,解码器生成预测结果,最后计算损失并反向传播。
听起来很顺,但实际操作中有几个点特别容易出问题。
第一个是视频帧采样策略。SAM2训练时不能像图像分类那样随机打乱样本,它必须按时间顺序组织数据,因为记忆模块依赖时间序列上的上下文。很多人在准备数据时会把帧顺序打乱,结果训练出来的模型在视频上表现极差,就是这个原因。
第二个是参考帧的选择。训练时要给模型指认“这一帧是初始帧,带标注和提示”,后面帧再逐步让模型自己预测。如果你在标注时没有区分初始帧和后续帧的标签,模型很可能学到错误的相关性,比如以为每一帧都需要外部提示,而不是靠记忆自动延续。
第三个是数据增强策略。图像和视频组成一个batch时,增强方式应该保持一致,比如同一个视频序列的所有帧用相同的随机缩放和裁剪参数,否则时间一致性会被破坏,模型学到的跟踪能力就会变差。
这些链路细节在官方文档里通常只是一笔带过,但恰恰是它们决定了你训练出来的模型是“能用”还是“好用”。
2. 训练前的准备:环境、硬件与数据
2.1 硬件配置怎么选
我先讲一个最直接的问题:多少人卡在训练上了?绝大多数人卡住是因为显存不够。
SAM2的图像编码器输入分辨率比较灵活,通常设置成1024×1024。在这个分辨率下,如果batch size设成1,一张大概需要20GB左右的显存,这还没算记忆模块和优化器的额外开销。我自己测下来,一张24GB显存的显卡跑batch size 1是最舒服的,再往上加batch size就会面临显存溢出的风险。
如果你手里的卡只有12GB左右,也不是完全没法跑,但就得做一些妥协:降低输入分辨率、使用梯度累积模拟更大的batch size、或者用混合精度训练。官方实现里默认是bfloat16混合精度,这个建议保留,能省接近一半的显存,而且对最终精度影响很小。
这里还要注意一点,训练SAM2并不是显卡越多越好。因为视频数据的时间序列特性,数据并行的效率会受限。多卡训练时,最好先确认一下你的数据加载是否成为瓶颈,否则你买了8张卡跟4张卡的速度差不多,那就很尴尬了。
从性价比角度看,我个人建议先拿一张24GB的卡把流程跑通,确认效果之后再考虑要不要加卡。不要一上来就租一堆A100级别的GPU,很多问题在小卡上更容易暴露出来,也更容易调试。
2.2 训练数据集怎么准备
这是整个训练流程中最耗时的一步,也是决定模型效果上限的一步。很多人在这一步偷懒,后面花了成倍的时间去调模型,最后还是不得不回头补数据。
官方训练用的SA-V数据集,标注格式是以视频序列为单位组织的。每一帧有一个JSON文件记录物体的边界框、掩码路径以及提示信息,同时还有一个视频级别的文件描述整体结构。如果你用的是自己的数据,最省事的方式是让你的标注工具导出成类似格式。
我这里推荐的标注工具,目前业内用得比较多的是CVAT和Label Studio这一类开源标注平台。CVAT对视频标注的支持比较完善,可以按帧逐帧标记,或者用SAM2做半自动标注来辅助人工校对,效率能提升很多。
我给一个可参考的最佳实践流程:
第一步,选一组有代表性的视频数据,覆盖你实际场景中的主要目标类型、光照变化和遮挡情况,不要只收集一种类型的样本,否则模型会有明显的偏向性。
第二步,按3到5帧的间隔从视频中抽取标注帧,这样既能保证时序覆盖,又能控制标注成本。如果你的视频帧率是30fps,抽帧间隔太大可能导致目标快速移动时前后帧之间没有重叠,太小则标注成本过高,3到5帧是个比较平衡的区间。
第三步,标注时建议先标注初始帧的掩码和提示点,后续帧让模型来做半自动分割,人工只做修正,这样比从头到尾逐帧手标要快好几倍。
第四步,数据量方面,我的经验值是:微调解码器部分,几百个视频序列、每段视频标注10到15帧就够起步了;要带着记忆模块一起微调,数据量至少要翻一倍,而且视频要更长一些,否则记忆模块学不到长程依赖。
还有一点非常重要:标注一致性。同一辆车的轮廓,前几帧你标得比较粗糙,后几帧标得很精细,模型学到的就是混乱的掩码标准。建议在标注完成后做一次随机抽查,选几个视频从头到尾过一遍掩码,确认标注风格统一再开始训练。这个检查步骤很多人会跳过,但我在实际项目里发现,几乎每个标注团队都会存在一定程度的不一致问题。
2.3 依赖安装与权重选择
训练前把环境装好,这里给一个我实测可用的依赖版本组合:
- Python 3.10
- PyTorch 2.1,CUDA 12.1
- torchvision 0.16
- hydra-core 1.3
- opencv-python 4.8
- 以及官方仓库里列的若干图像处理库
不过版本不必完全一致,只要PyTorch和CUDA版本匹配、能跑起来就行。如果用了太新的PyTorch,官方很久没更新的某些API可能出现兼容问题,这时候先在官方issue区搜一下,通常能找到对应的修复方案。
权重选择上,官方提供很多种checkpoint。我的建议是,训练前先想清楚你最终要推理哪些任务。只做图像分割就加载图像训练的权重;做视频任务就加载带记忆模块的权重;如果内存特别紧张,可以加载蒸馏版的小模型权重,训练开销更小,就是精度会略降。
这个选择看似简单,但实际上很多人会忽略一个点:你加载预训练权重时,要确保权重结构和你的模型定义完全一致。如果你改了模型配置里的某个维度,比如编码器深度、注意力头数,那么预训练权重就没法直接加载,只能随机初始化,训练效果自然大打折扣。
3. 训练脚本实操:配置文件到命令行参数
3.1 官方训练流程的代码结构
训练脚本的总入口做的事情比较常规:读取Hydra配置文件,加载模型定义和预训练权重,实例化数据集和数据加载器,然后启动训练循环。
这里我要提一下Hydra这个配置框架。它跟常见的yaml配置不太一样,支持配置文件嵌套、命令行参数覆盖、多组配置组合。SAM2选择Hydra来管理配置,好处是灵活,坏处是新手看到那一堆yaml文件会懵圈。
config目录里最核心的两个文件:一个是模型和数据总配置,定义了模型结构、数据路径、训练超参等;另一个是训练专用配置,包含batch size、学习率、优化器设置。实际使用中,很多参数不需要直接改文件,因为Hydra支持命令行覆盖。比如你想把batch size改成2,直接在命令后面加上batch_size=2就行。这个特性在模型调参阶段特别方便,不需要反复编辑配置文件再重启。
不过这也带来一个坑:配置文件里的嵌套层级如果不熟悉,命令行覆盖的路径很容易写错。比如你以为是training.batch_size,实际嵌套可能是training.optimizer.batch_size。这时候建议先不加任何覆盖参数,用默认配置跑通一次,再逐步加上自己的覆盖项,出错了也知道是哪里错。
3.2 核心参数配置详解
我挑一些真正影响训练结果的参数详细说明一下。
首先是batch size。注意这个参数的含义是每个GPU上每次迭代处理的视频片段数量,不是单帧数量。batch size为1时,一个step就包含了一段视频的多帧输入,这也是SAM2显存开销大的原因。很多人误以为batch size=1就是单张图,结果看到显存占用瞬间飙到20GB以上才反应过来。
其次是学习率。官方初始配置通常是1e-4这个量级,但微调场景下我建议从1e-5起步。理由很简单:预训练模型的参数已经具备较好的特征表达能力,学习率过大很容易直接把已有知识冲掉,导致性能不升反降。我见过不少人在微调时用了1e-4,训了2000步之后模型分割效果变得一团糟,把学习率调到5e-5之后情况才慢慢好转。
再讲讲学习率预热步数。SAM2训练用的调度器会先让学习率从零慢慢涨到目标值,再按阶梯或余弦方式衰减。预热的作用是避免模型在训练初期因为更新幅度过大而震荡。微调时预热步数不需要太多,200到500步之间比较合适。
权重衰减和随机增强也是关键。权重衰减官方默认值通常在0.05左右,主要负责防止过拟合。随机水平翻转这个增强项需要注意:如果你的目标具有方向性,比如文字识别或者某种带方向属性的零件,就不要开启翻转增强,否则模型会学到错误的对称性。
还有一个参数值得关注:视频序列在训练时最多采样的帧数,默认一般设置成十几帧。这个参数影响非常大。采样帧数越多,模型对长时间记忆的建模能力就越好,但显存和时间成本也线性上升。如果你只有单卡,建议先设置8帧左右,跑通之后再逐渐增加。
3.3 实际跑起来的命令
假设你已经把数据准备好了,最普通的从头微调命令大致长这样:
cd training torchrun --nproc_per_node=1 train.py \ data.train_dataset=video_train \ data.val_dataset=video_val \ training.batch_size=1 \ training.lr=5e-5 \ training.warmup_steps=200 \ training.steps=5000 \ model.load_pretrained_base=truetorchrun不是必须的,但单机多卡时它能帮你自动分配GPU,比手动设置环境变量省心。跑起来之后,日志会逐步打印每个step的loss、学习率和GPU显存使用情况。
第一次跑通之后,强烈建议先拿极小的数据量快速验证整个pipeline。比如只放5个视频序列进去,跑100个step,看看loss能不能往下走、模型能不能保存。这一步能帮你排除掉很多环境问题,而不是等到完整训练跑到一半才发现数据读取有bug。我每次在新的机器上跑训练都会先做这个“冒烟测试”,看起来多花了几分钟,实际上省下的调试时间远超这些。
4. 训练过程中的监控、优化与效果评估
4.1 损失曲线怎么看
SAM2训练的损失包含几个部分:分割mask的二元交叉熵损失、IoU损失,以及一些辅助损失。日志里会打出关键的均值损失,你主要看这个曲线是否平滑下降。
正常情况,loss曲线是单调下降然后趋于平缓。我遇到过一种假象:前500个step loss降得很快,之后几乎不动了,但验证集效果还是稀烂。这种情况大概率是模型过拟合了,你可以尝试加大数据增强、降低学习率或引入更多数据。另一个可能是你的数据分布跟预训练差异太大,解码器的随机初始化部分没有足够信息来拟合新任务。
还有一个经常被忽略的地方:观察训练过程中内存和显存的稳定性。如果显存使用量随着训练逐渐增长,说明可能存在显存泄漏,长时间训练会出现OOM。这种情况下可以定期重启训练或者降级到固定大小的输入来规避。
如果loss直接不降或者出现NAN,常见的原因有学习率过大、数据里有异常值(比如某个mask的路径不存在,或者mask尺寸异常)、混合精度与某些算子的兼容性问题。处理方式很简单,先用小学习率加小batch size跑通一个小数据集,排除数据问题再逐步放大训练规模。
4.2 验证集评估与参考指标
训练过程中你需要不断评估模型效果,建议每隔几百个step跑一次验证集。验证集合不要跟训练集合有任何交叉,包括同一视频的帧也不行,否则评估结果会虚高。
评估指标上,视频分割任务主要关注两个:mask的平均IoU,以及mask的时域稳定性。平均IoU比较好理解,就是预测掩码和真实掩码之间的交并比。时域稳定性怎么衡量?一个比较笨的办法是手动播放一段测试视频,观察模型输出的mask是否在相邻帧间剧烈跳变。如果mask边界一直在发抖,说明模型没有学到时间上的平滑性,这时需要加强记忆模块的微调力度。
我在实际项目里还遇到过一个比较隐蔽的问题:验证集loss比训练集还低。仔细查才发现,评估代码顺手把训练时的模型状态带过来了,等于用训练状态做了验证,数据严重泄露。这里提醒大家,评估的时候一定要确认是从干净加载的权重去推理,不要沿用训练进程里的缓存状态。
4.3 训练步数与checkpoint选择
训练步数多少合适?这个问题没有标准答案。以我的经验,在任务不太复杂的情况下,数千到一万个step以内基本能看到不错的效果。如果你的数据集比较小,甚至几百个step就可能达到不错的效果,继续训练反而过拟合。
有一个非常实用的技巧:保存checkpoint时给每个节点都留一份,最后选验证集效果最好的那个checkpoint,而不是直接用最后一个step的权重。因为训练后期loss可能轻微震荡,最后一步的权重未必是泛化性最好的。官方代码里通常有按验证集指标保存最优模型的功能,建议打开。我自己在项目里吃过这个亏,图省事直接用最后一个checkpoint,结果验证集效果比中间某个检查点低了3个百分点,重新回滚再部署才解决。
另一个技巧是训练过程中关注辅助损失的变化。SAM2的损失函数里除了主分割损失,还有辅助损失用来稳定训练。如果你看到主损失降了但辅助损失在涨,说明模型在主任务上可能过拟合,需要调整正则项或增强策略。
5. 常见问题与排查技巧实录
5.1 显存爆掉怎么办
这是训练过程中出现频率最高的问题。这里的排查思路是有优先级的,不要一上来就换卡。
第一步先检查batch size和视频采样帧数,这两个是最占显存的部分。如果你的batch size已经是1了,帧数也调小了,还是爆,那就考虑降低输入分辨率。SAM2的输入分辨率可以配置成768×768,效果有下降但不会差太多,显存压力能缓解不少。
第二步,启用梯度检查点。这个技术用起来简单,原理是用额外的计算量换取显存空间,在反向传播时不保留中间激活,而是在需要时重新计算。通常情况下显存能下降三分之一左右。
第三步,使用混合精度。bfloat16对训练精度的负面影响很小,能节省接近一半显存,有条件一定要用。如果你用的是比较老的GPU,不支持bfloat16,那就用float16,但要注意加上动态loss scaling。
如果这些都做了还爆,我给你一个实在的建议:租卡或者换卡。训练模型这种事情,显存就是硬约束,省来省去还是浪费时间。我在一台12GB卡上折腾了一周,最后还是换到24GB卡,半天就把问题解决了。
5.2 Loss不减或者Loss为NAN
Loss为NAN,优先检查数据读取。你可以单独写一个小脚本,手动从dataloader里取一个batch,观察mask值和shape是否正常。如果mask是空文件或者路径错误,模型在算损失的时候很可能出现无效值。这种问题特别隐蔽,因为训练脚本启动时不一定会报错,只有跑到某个特定的step才会崩溃。
再看学习率。学习率过大会让模型参数更新幅度过大,导致数值溢出。把学习率降到1e-6试一下,如果恢复正常,说明是学习率的问题。我遇到过一个案例,训练曲线在2000步时突然跳变成NAN,排查半天发现是一个learning rate scheduler在某个节点把学习率乘了10倍。
混精度也可能导致NAN。遇到这种情况,最简单的方式是先用纯FP32跑一次,如果NAN消失,那就是兼容性问题,可以升级PyTorch或者关闭某些算子的混合精度支持。
Loss不减的情况,除了前面说的过拟合,还有一个可能:你的数据分布跟预训练分布差异太大,解码器的随机初始化部分没有足够信息来拟合新任务。这时候可以考虑解冻图像编码器的后面几层,一起参与微调,效果往往会立刻改善。
5.3 分割结果差:细碎掩码与漏检问题
训练完之后发现模型分割效果很碎,这可能有三个原因,排查顺序有讲究。
第一个是掩码后处理参数的问题。SAM2在推理时有一个mask阈值和最小区域面积参数,如果设置不合适,很小的噪声区域也会被保留下来,视觉上就显得非常碎。调整推理参数比重新训练速度快得多,先检查这个,往往一个阈值改一下效果就干净很多。
第二个是训练数据里边缘标注本身就不够精细。模型的决策边界是跟着数据走的,你给它粗糙的标注它就学成粗糙的模型。很多人以为分割模型会自动学习出比标注更精细的边界,这个想法在深度学习里基本不成立。所以数据质量真的就是模型的极限。
第三个是学习率设置得不合理,导致模型对细粒度特征关注不够。这种时候可以降低学习率、增大训练步数,让模型有更长时间去细化掩码边界。
漏检问题则要重点看数据平衡。如果你的场景中目标非常小,但训练数据大部分是大目标,模型自然倾向忽略小目标。解决方法是增加小目标样本的比例,或者对小目标区域做上采样增强。还有一种做法是在推理时使用多尺度测试,但在视频场景下代价比较高,不太推荐。
5.4 一个独家技巧:用半自动标注来扩大数据集
最后分享一个我一直在用的方法。
很多人训练数据不够,又不想花时间去标。我的做法是:先用官方预训练权重在自己的几段数据上跑半自动分割,生成候选掩码,再由人去修正那些明显错误的部分。修正完之后,把这些数据放进训练集训练一个新的模型,新的模型分割效果又会更好,于是可以再用这个更好的模型去标注更多的数据。如此循环,数据量越滚越大。
这个做法本质上就是一个主动学习环路,实施起来没有额外成本,就是重复“用模型预标注→人工修正→训练新模型”这三个步骤。注意点只有一个:每轮循环后要检查新模型在验证集上是否真的有提升,如果没有提升,说明新增的数据可能是低质量的,需要回退到上一轮的模型重新标注。
我在一个工业检测项目里用这个方法,把标注数据从200段视频扩大到了800段,模型在验证集上的mIoU提升了将近6个百分点,而人工标注的工作量只多花了不到一倍。几轮下来,模型在垂直领域的表现会稳定提升一个台阶。
最后再说说我自己实际折腾SAM2训练的整体感受。模型的架构再先进,真正决定项目成败的往往是数据准备的细节。很多团队都经历过“别人用SAM2效果好,我用SAM2效果差”的困惑,背后绝大多数原因不是模型问题,而是数据质量、标注一致性和参数设置不对齐。这篇文章里的内容全部来自我自己的实操踩坑记录,如果你在训练过程中碰到我上面描述的问题,照着排查方向去改,大概率能少走几个弯路。训练这件事没有捷径,但把常见坑提前规避掉,你省下的时间就能花在更有意义的事情上,比如打磨数据、设计更合理的评估指标。最后再提醒一句:拿到任何训练任务,先跑通小规模验证再上全量数据,这是我坚持到现在最有用的一条底线。