SAM本地部署从零到实战:零样本分割、PVS与自动掩码生成

发布时间:2026/9/17 20:14:41
SAM本地部署从零到实战:零样本分割、PVS与自动掩码生成 上周有个做遥感的朋友找我说他们手头一批高分卫星影像要做地块提取问我有没有办法不上云、不调用 API纯在自己机器上跑一套分割模型。我第一反应就是提醒他别急着上大模型微调先把 SAM 拿来当零样本分割的底座试试。SAM全称 Segment Anything Model是 Meta 开源的那套可提示分割模型核心能力是你给一个点、一个框或者什么都不给让它自己撒点它就能把图里的物体轮廓抠出来。这篇文章我想聊聊 SAM 模型从零开始的本地部署全过程——包括为什么值得本地跑、模型结构里那些容易看懵的术语比如很多人搜的 PVS 到底指什么、显卡和环境的实际盘点、权重下载与推理代码落地、自动掩码生成的参数调优以及在耕地识别这类遥感场景里怎么用。文章面向的是有一定 Python 基础、手里有独立显卡、想把分割能力握在自己手里的开发者和算法同学小白跟着一步步抄也能跑通。1. 先把话说清楚SAM 本地部署到底解决什么问题我见过太多团队的分割需求最后卡在两个地方一是数据不能出内网二是调用外部接口的成本和延迟撑不住业务量。SAM 的本地部署恰好同时解决这两件事。先说第一点很多遥感、医疗、工业质检的场景原始影像本身就带隐私或涉密属性传到第三方平台这件事从一开始就过不了合规评审本地部署是唯一出路。第二点是成本云端分割接口按调用次数计费一张大图如果要生成几百个掩码单价乘以量级之后账单会非常难看而本地部署属于一次性投入显卡、后续边际成本接近零。再说 SAM 本身为什么适合当这块底座。它的训练数据集 SA-1B 包含约 1100 万张图像和超过 10 亿个掩码这个规模让模型学到了相当通用的物体边界直觉。你不需要针对自己的数据训练直接拿预训练权重做零样本推理就能在很多领域拿到可用的轮廓。这一点对没有标注预算的团队极其友好——先跑通再决定要不要微调比一上来就攒数据集务实得多。那本地部署到底难不难说实话如果只是跑通一个点提示分割的 demo半小时能搞定。真正让人抓狂的是后面几件事显卡选型不知道自己够不够、权重下到一半断了、CUDA 版本和 PyTorch 对不上报一堆错、自动掩码生成出来的结果碎成一片一片、显存不够在 4K 图上直接 OOM。这些坑我基本都踩过所以这篇文章的重点不会放在复制粘贴官方 README而是把我实际调通过的那套流程和判断依据摊开讲。还有一点值得提前说明SAM 只是一个分割模型它本身不认字、不懂语义没法告诉你这是耕地还是这是建筑。它的定位是给定提示做精细分割语义标签要靠你自己后接分类器或者规则。理解这一点你在方案设计时就不会跑偏。2. SAM 的模型结构拆解三个组件和一个 PVS2.1 图像编码器、提示编码器、掩码解码器SAM 的结构拆开看其实非常清爽就三块。第一块是图像编码器Image Encoder用的是 ViTVision Transformer把 1024×1024 的输入图像压成一个 64×64 的特征图通道数 256。这是整个模型里最重的部分参数量和计算量都集中在这里。第二块是提示编码器Prompt Encoder负责把你给的点、框、粗掩码、文本这些提示转成 embedding。点和框走位置编码加可学习 embedding掩码走卷积文本走 CLIP 那套。第三块是掩码解码器Mask Decoder用轻量的 Transformer 结构把图像特征和提示特征做交叉注意力最后输出掩码。这个设计最妙的地方在于解耦。图像编码器是整个推理里最耗时的一步但它只跟图像有关跟提示无关。所以你可以在同一张图上反复给不同的点图像编码只算一次后面的提示编码和解码几乎瞬时完成。这正是可提示分割能做成交互式工具的原因——在网页上点一下就能出结果靠的就是这个缓存机制。我自己做标注工具时就是按这个思路把 image embedding 缓存起来用户点几十下都不会有卡顿感。模型有三个规格ViT-B、ViT-L、ViT-H。参数量分别是约 9100 万、3.08 亿、6.36 亿。权重文件大小对应约 375MB、1.2GB、2.4GB。规格越大效果越好但对显存和算力的要求也越高。同分辨率下 ViT-H 的编码耗时大概是 ViT-B 的三倍以上这个差距在实际部署里非常明显选型时要权衡。2.2 PVS 到底是什么意思网上搜 SAM 的时候PVS 这个词出现的频率很高但官方原始论文里其实没有直接用这个缩写它的出处主要在 SAM 2 以及相关的数据集和代码库里。在 SAM 2 的语境下PVS 一般指 Promptable Video Segmentation即可提示视频分割也就是把图像上的点一下分割能力扩展到视频帧序列上模型需要沿着时间轴追踪同一个目标。而在更早的一些分割代码约定里PVS 也被人拿来指代 Prompt-Vision-Segmentation 这套提示驱动分割的流程本身。所以如果你看到有人问sam模型中的pvs是什么意思先看他讨论的是 SAM 1 还是 SAM 2语境不同答案不一样。理解 PVS 对本地部署有实际意义。如果你要做的是视频里逐帧追踪某个目标那单纯部署 SAM 1 是不够的你需要上 SAM 2它对视频有记忆机制能把前一帧的分割结果作为提示传给下一帧避免目标抖动和跳变。但如果只是静态图像分割SAM 1 完全够用而且部署更简单、权重更轻。选错版本会导致你白折腾一圈。2.3 为什么这个结构适合零样本迁移很多人好奇凭什么没在遥感数据上训练过的模型拿到卫星图也能分割得七七八八。核心原因是掩码解码器学到的是一种通用的边界先验——它见过足够多的物体边缘形态知道什么东西在视觉上是一个独立单元。加上提示机制把你想分割哪个这个意图显式交给你模型只需要专注回答这个位置上的物体边界在哪任务被大大简化了。这就是零样本能力的来源也是它在耕地、道路、建筑这类遥感地物上能直接试用的底气。3. 硬件与环境的盘点和搭建3.1 显卡和显存怎么算才够选显卡这件事我的建议是先确定你要跑哪个规格、以及单张图的最大分辨率。推理阶段ViT-H 在 1024×1024 输入下显存占用大约在 6 到 7GB 区间ViT-L 约 4GBViT-B 大概 2GB 出头。自动掩码生成因为要批量采样点峰值显存会比单次推理再高一些通常要预留 20% 到 30% 的余量。所以我的经验是8GB 显存起步可以比较舒服地跑 ViT-B 和自动掩码12GB 能安心跑 ViT-H 的单图推理如果想要批量处理大图或者做多尺度裁剪16GB 以上更稳。如果你手上只有一张消费级 8GB 卡别慌有办法。一是用 ViT-B效果在多数场景下够用二是把输入图像缩到 1024 以下模型内部会自己插值精度会掉一点但通常可接受三是开半精度推理能省下大约三成显存。这几招组合起来我试过在 8GB 卡上跑大图的分块处理虽然慢但能出活。注意显存够不够不能只看权重文件大小。ViT-H 权重 2.4GB但推理时激活值、注意力矩阵、中间特征都会占显存实际占用远大于权重大小。用权重大小估算显存是新手最容易犯的错。3.2 Python 环境和 CUDA 的配对逻辑环境搭建的核心是让 PyTorch、CUDA、显卡驱动三者版本对齐。顺序是这样的先看你的显卡驱动支持到哪个 CUDA 版本用nvidia-smi看右上角那个 CUDA Version再根据这个版本去 PyTorch 官网找对应的安装命令。千万别自己瞎装 CUDA ToolkitPyTorch 的 pip 包会自带需要的 CUDA 运行时你只需要保证驱动版本够新就行。Python 版本我用 3.10 最省心3.8 到 3.11 之间大都没问题。环境隔离一定用 conda 或者 venv因为 SAM 依赖的 torch 版本可能和你别的项目冲突。具体操作是建一个干净环境然后装 torch、torchvision再装 SAM。装完立刻验证python -c import torch; print(torch.cuda.is_available())能打印 True 才算过了第一关。如果是纯 CPU 环境也能跑但速度会让你怀疑人生。ViT-H 在 CPU 上单张编码要几十秒自动掩码生成可能要几分钟一张。我的态度是CPU 只适合验证代码逻辑生产千万别用。3.3 依赖安装的完整流程下面这套是我实际用的顺序可以直接抄conda create -n sam python3.10 -y conda activate sam # 根据你的 CUDA 版本选择这里以 CUDA 11.8 为例 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 # 安装 SAM 本体 pip install githttps://github.com/facebookresearch/segment-anything.git # 常用配套 pip install opencv-python matplotlib numpy pillow如果你不想用 git 安装也可以把官方仓库 clone 下来用pip install -e .做可编辑安装方便你改源码调试。另外提醒一句opencv 一定要装opencv-python而不是opencv-contrib-python里的某些版本后者在某些镜像环境里会跟 PyTorch 的依赖打架我遇到过 segmentation fault排查了很久才发现是 opencv 版本冲突。提示安装完成后别急着下权重先用一段几十行的最小脚本验证环境。能导入segment_anything、能加载模型结构哪怕权重是随机的、能跑完一次 forward说明环境没问题。把环境和权重分开验证出问题时能快速定位是哪一层的锅。4. 权重下载与推理代码的落地4.1 权重从哪来、怎么下才不断权重文件是本地部署里最容易被忽略的一环。官方在仓库里给了三个 checkpoint 的下载链接对应 ViT-H、ViT-L、ViT-B。文件放在 GitHub 的 release 或者元数据服务上国内直连下载 2.4GB 的大文件很容易中途断掉。我的做法是用支持断点续传的工具下载或者从国内模型社区比如 ModelScope找对应的镜像权重速度和稳定性都好很多。下载完一定要校验。最简单的方式是看文件大小对不对ViT-H 的sam_vit_h_4b8939.pth大概是 2.4GB 出头。更严谨一点可以比对官方给出的哈希值。我有一次下到一个被截断的权重加载时报的错非常诡异说张量形状对不上折腾半天才发现是文件不完整。所以校验这一步不能省。4.2 最小可运行推理脚本环境好了、权重齐了先跑最小闭环。下面这段是点提示分割的核心代码我加了注释说明每一步在干什么import cv2 import numpy as np import torch from segment_anything import sam_model_registry, SamPredictor # 1. 选择模型规格并加载权重 sam sam_model_registry[vit_h](checkpointsam_vit_h_4b8939.pth) # 2. 送进显卡有卡一定送CPU 会慢到离谱 sam.to(devicecuda) # 3. 构建预测器内部会缓存图像编码结果 predictor SamPredictor(sam) # 4. 读图注意 OpenCV 默认是 BGR image cv2.imread(test.jpg) image cv2.cvtColor(image, cv2.COLOR_BGR2RGB) # 5. 关键一步计算图像编码耗时大头只算一次 predictor.set_image(image) # 6. 给一个点提示坐标是 (x, y)label 1 表示前景点 input_point np.array([[500, 375]]) input_label np.array([1]) masks, scores, logits predictor.predict( point_coordsinput_point, point_labelsinput_label, multimask_outputTrue, # 输出三个候选掩码按质量排序 ) print(三个候选掩码的置信度:, scores)这段代码里最值得说的是set_image和predict的分离。很多人第一次看会疑惑为什么不在 predict 里一次性做完其实就是为了复用。你在交互式标注里一张图 set_image 一次之后用户点多少下、框多少个框都只走后面的轻量分支。理解这个机制你才知道性能优化该往哪使劲。4.3 框提示和组合提示怎么写点提示适合点选单个物体框提示适合你把目标圈出来让模型抠细节。用法是把point_coords换成box参数input_box np.array([100, 80, 600, 500]) # x1, y1, x2, y2 masks, scores, logits predictor.predict( boxinput_box, multimask_outputFalse, )框和点还能组合比如给一个框再加几个前景点和背景点让模型知道哪些区域要包含、哪些要排除。背景点的 label 给 0 就行。这套组合提示在处理粘连物体时特别管用——比如遥感图里两块挨着的耕地单靠一个框容易把它们分成一个整体加一个背景点落在分界线上模型就能切开了。我实际调的时候通常先用框粗定位再看结果决定补不补点。5. 从点提示到自动掩码三条主流用法的实操5.1 交互式分割最适合做标注工具交互式分割是 SAM 最成熟的用法也是我最推荐的落地形式。逻辑就是上面那套 set_image 加 predict套一层前端界面用户在图上点几下掩码实时更新。为什么它比全自动更适合上手因为把分割哪个物体的判断交给人模型只负责抠边界容错率高得多。我拿它给团队做辅助标注原本一天的活能压到两三个小时。做这类工具时图像编码缓存要放在内存里同时要注意一次只缓存一张或几张图不然显存会被 embedding 撑爆。5.2 自动掩码生成一次性把图里的东西都抠出来SamAutomaticMaskGenerator是 SAM 提供的全自动模式它在图上均匀撒一批点对每个点生成掩码再去重、过滤、排序最后返回一大堆掩码。用法很直接from segment_anything import SamAutomaticMaskGenerator mask_generator SamAutomaticMaskGenerator( modelsam, points_per_side32, pred_iou_thresh0.88, stability_score_thresh0.95, crop_n_layers1, min_mask_region_area100, ) masks mask_generator.generate(image)这里面每个参数都值得说。points_per_side控制采样密度32 表示每边撒 32 个点总共 1024 个点密度越高小物体越容易被抓到但耗时和显存也线性上涨。pred_iou_thresh是预测质量阈值低于这个分的掩码被扔掉调高会减少碎掩码但可能漏掉小目标。stability_score_thresh控制掩码在阈值扰动下的稳定性太低会保留一堆噪声。crop_n_layers是分块层数设为 1 会把图切成多块分别处理能提升小目标召回代价是耗时翻好几倍。min_mask_region_area用来过滤太小的掩码区域防止输出一堆像素级碎块。我实测下来的调参心法是先固定points_per_side32把两个阈值调到你满意的干净程度再决定要不要开 crop。很多人一上来就开 crop_n_layers2结果一张图跑几分钟最后发现小目标召回并没有明显提升纯属浪费。5.3 批量处理把脚本变成能干活的流水线单张图跑通之后你会自然想批量处理。批量有两个关键点一是图像编码的显存要及时释放二是要把结果落盘成可用的格式。我的做法是写一个循环每张图处理完后把掩码转成 RLE 或者 PNG坐标信息存 JSON然后手动清一下缓存。这里有个坑PyTorch 的缓存分配器不会立刻还给系统长时间跑批量容易出现显存缓慢上涨最后 OOM。解决办法是定期torch.cuda.empty_cache()或者干脆把批处理拆成多个进程每个进程处理固定张数后退出重开。用法适用场景交互成本效果稳定性显存压力点提示交互标注工具、单目标提取高高低框提示交互已知大致范围的抠图中高低自动掩码生成全图要素盘点、预标注低中高批量流水线离线处理大量影像无中高6. 显存、速度与效果的优化实操6.1 半精度推理能省多少把模型和输入转成半精度fp16是最简单的一招。加载权重后调用sam.half()输入张量也转半精度显存能省下大约三成速度也有提升。代价是数值精度下降在极端情况下掩码边缘会出现细微抖动。我的经验是推理用 fp16 完全可接受如果你要做的是像素级精度的测量任务再切回 fp32 对照验证一下。sam sam_model_registry[vit_h](checkpointsam_vit_h_4b8939.pth) sam.half().to(cuda)6.2 图像缩放与分块策略SAM 内部固定处理 1024×1024你给更大的图它也会先缩放。所以真正影响效果的是你输入时的分辨率选择。遥感图动辄几千像素直接缩小会丢小目标。这时候要用分块把大图切成带重叠的小块每块送进模型最后把掩码拼回来。重叠是为了避免物体正好被切在块边缘。块大小取 1024 附近最省算力重叠建议留 100 到 200 像素。分块会成倍增加处理时间所以只在你确实需要小目标时用。6.3 导出 ONNX 或 TensorRT 的路子如果你要把 SAM 部署到生产环境做高并发服务Python 原版推理的延迟和资源占用是不够看的。可行方向是导出 ONNX再用 TensorRT 或者 ONNX Runtime 加速。注意 SAM 的动态输入比较麻烦导出时要固定图像尺寸提示部分单独处理。这条路的坑不少导出脚本要处理 attention 里的动态 shape我建议先用 ONNX Runtime 跑通再考虑 TensorRT。如果你的业务量不大原版 PyTorch 加 fp16 加缓存复用已经够用没必要为了几毫秒去折腾编译。注意优化要有优先级。先解决能不能跑选对模型规格再解决显存够不够fp16、缩放最后才考虑快不快ONNX、TensorRT。很多人一开始就冲最后一步结果基础环境都没验证清楚白费功夫。7. 踩坑记录与常见问题速查下面这些是我和身边人实际遇到过的问题整理成速查表方便你对照排查。问题现象可能原因排查与解决加载权重报张量形状不匹配权重文件下载不完整或下错了规格核对文件大小重新下载确认 checkpoint 和 registry 里的规格名一致推理报 CUDA out of memory模型规格太大或图太大换 ViT-B、开 fp16、缩小输入、减小 points_per_sidetorch.cuda.is_available()返回 Falsetorch 装了 CPU 版或驱动太旧重装对应 CUDA 版本的 torch更新显卡驱动自动掩码结果碎成一片阈值设得太低调高 pred_iou_thresh 和 stability_score_thresh加大 min_mask_region_area小目标总是漏掉采样密度不够或图被缩太小提高 points_per_side开 crop_n_layers或用分块处理交互式界面点一下就卡图像编码没缓存确保 set_image 只调用一次复用 embedding长时间批量处理显存持续上涨缓存未释放定期 empty_cache或拆分多进程处理分割边缘有锯齿输入分辨率太低提高输入图尺寸或后处理做一次形态学平滑再说几个表格里放不下的经验。第一颜色空间一定要转对。OpenCV 读图是 BGRSAM 要 RGB忘了转会导致颜色通道错位分割结果莫名其妙这个错误新手几乎都犯过一次。第二点提示的坐标是 (x, y) 不是 (row, col)跟 numpy 数组索引是反的写代码时特别容易搞混。第三如果你用 Jupyter 调试记得每个 cell 重新加载模型会吃掉大量显存建议把模型加载放在单独 cell 只跑一次。第四网络下载的权重最好放在固态盘上机械盘加载 2.4GB 权重会明显拖慢启动速度。还有个隐蔽的坑不同版本的 torch 和 torchvision 混用会导致torchvision::nms相关的报错或者某些算子找不到。解决方法是先用pip list看清楚版本然后按 PyTorch 官网的兼容表重新装一遍不要单独升级其中一个。8. 场景延展从耕地识别到更多垂直应用回到开头那个遥感耕地的需求。SAM 在这里的用法是先把大图分块用自动掩码生成把候选地块都抠出来再对每个地块做后处理——计算面积、形状指数、纹理特征最后接一个轻量分类器或者规则判断这块是不是耕地。耕地的纹理和光谱特征比较规律通常加上一个简单的颜色或纹理阈值就能筛掉大部分非耕地掩码。为什么这套流程能成立因为 SAM 负责的是它最擅长的边界分割语义判断交给了后端的规则和分类各司其职。你要是硬让 SAM 直接输出耕地标签那是用错工具了。除了耕地这套思路可以平移到很多场景。工业质检里用框提示抠出缺陷区域医疗影像里用点提示圈出病灶边缘做预标注电商场景里做商品抠图换背景视频里用 SAM 2 做目标逐帧追踪。共同点都是边界分割是难点语义相对好判断。你只要抓住了这个规律就能判断一个需求到底适不适合用 SAM 本地部署。我在实际项目里的体会是SAM 本地部署最大的价值不在模型本身而在于它把精细分割这个能力变成了一块可以自由拼接的积木。你可以把它塞进任何需要轮廓的流水线不用担心调用次数、不用担心数据出门。真正需要花心思的是工程侧的细节——选对规格、管好显存、调好阈值、设计好提示策略这些才决定了它最后是能落地干活还是只停留在 demo 阶段。最后再分享一个小技巧如果你要长期做同一类图像的批量分割先拿十几张典型样本把 points_per_side 和两个阈值调到最优把参数固化成配置文件之后所有批次都复用这套参数。这样既保证了结果一致性也避免了每次重新试参的时间浪费。分割任务的调参很依赖数据分布一旦找到一组在你数据上稳定的参数就别轻易动它。