Ultralytics SAM 核心模块详解:SAMModel、SAM2Model 与 SAM3Model 的架构、参数与源码实现
本篇技术指南围绕 ultralytics/models/sam/modules/sam.py 中的三个核心模型类展开:SAMModel(静态图像分割)、SAM2Model(基于记忆机制的视频分割)与 SAM3Model(SAM3 变体)。读完后你将理解这三类模型如何组合图像编码器、提示编码器与掩码解码器,掌握其关键构造参数、forward_image/track_step 等核心方法的数据流,以及它们与 SAM 模型接口、构建函数之间的实际调用关系。
模块定位与类总览
ultralytics/models/sam/modules/sam.py 是 Ultralytics 中 Segment Anything 系列(SAM / SAM 2 / SAM 3)的模型主干定义文件,与官方 API 参考页 docs/en/reference/models/sam/modules/sam.md 一一对应。该文件内定义了三个类和一个模块级常量:
| 名称 | 位置 | 职责 |
|---|---|---|
NO_OBJ_SCORE |
sam.py#L22 | 值为 -1024.0 的占位分数,用于标记"目标缺失/被遮挡"帧的掩码 logit |
SAMModel |
sam.py#L25 | 静态图像提示分割:组合 ImageEncoderViT + PromptEncoder + MaskDecoder |
SAM2Model |
sam.py#L91 | 视频目标分割:在 SAM 头之外增加记忆注意力、记忆编码与对象指针机制 |
SAM3Model |
sam.py#L1025 | 继承 SAM2Model 的 SAM 3 变体,调整默认图像尺寸、backbone 步长与解码器结构 |
从源码结构看,三个类都不实现完整的 forward 推理闭环:SAMModel 的文档字符串明确注明 "All forward() operations are implemented in the SAMPredictor class",而 SAM2Model.forward() 直接抛出 NotImplementedError,提示改用 SAM2VideoPredictor 完成推理(见 sam.py#L350-L355)。也就是说,本文件定义的是"网络结构与单步计算原语",批量预处理、批推理与结果组织由上层预测器承担。
SAMModel:静态图像分割的三段式组合
SAMModel 是最基础的 SAM 实现,其 __init__ 接收四大组件(见 sam.py#L55-L80):
class SAMModel(nn.Module):
mask_threshold: float = 0.0 # 掩码预测阈值
def __init__(self,
image_encoder, # ImageEncoderViT:将图像编码为特征嵌入
prompt_encoder, # PromptEncoder:编码点/框/掩码等提示
mask_decoder, # MaskDecoder:由图像嵌入与提示嵌入预测掩码
pixel_mean=(123.675, 116.28, 103.53), # 输入像素归一化均值
pixel_std=(58.395, 57.12, 57.375)): # 输入像素归一化标准差
几个值得注意的实现细节:
- 像素归一化以 buffer 注册:
pixel_mean与pixel_std通过register_buffer(..., persistent=False)注册,reshape 为(-1, 1, 1)后随模型移动设备,但不进入 checkpoint 状态字典,符合推理型模型的轻量持久化策略(sam.py#L79-L80)。 mask_threshold = 0.0作为类属性,是掩码 logit 二值化的默认阈值,供上层预测器取用。set_imgsz适配任意输入尺寸(sam.py#L82-L88):
def set_imgsz(self, imgsz):
if hasattr(self.image_encoder, "set_imgsz"):
self.image_encoder.set_imgsz(imgsz)
self.prompt_encoder.input_image_size = imgsz
self.prompt_encoder.image_embedding_size = [x // 16 for x in imgsz] # 16 是 ViT 的固定 patch size
self.image_encoder.img_size = imgsz[0]
注意 image_embedding_size 除以的 16 是 ViT 的固定 patch size——这也是该模型对输入尺寸的核心约束来源。
在构建侧,ultralytics/models/sam/build.py 的 build_sam 展示了 SAMModel 的完整装配方式:ImageEncoderViT(patch_size=14 的视觉主干在构建层配置)+ PromptEncoder(embed_dim=..., image_embedding_size=..., input_image_size=..., mask_in_chans=16) + MaskDecoder(num_multimask_outputs=3, transformer=TwoWayTransformer(depth=2, mlp_dim=2048, num_heads=8), iou_head_depth=3, iou_head_hidden_dim=256),pixel_mean/pixel_std 传入与类默认值一致的 ImageNet 统计量(build.py#L199-L225)。num_multimask_outputs=3 意味着模型对单次提示最多输出 3 个候选掩码。
SAM2Model:记忆机制驱动的视频目标分割
SAM2Model(sam.py#L91)面向视频序列,核心思想是:当前帧的视觉特征先与历史帧的"记忆"做条件融合,再交给 SAM 风格的提示编码器和掩码解码器,从而保持时间一致性。
构造函数与四大组成部分
构造函数参数极多,按源码内注释可分为四个部分(sam.py#L163-L343):
| 部分 | 组件/参数 | 说明 |
|---|---|---|
| Part 1 图像主干 | image_encoder、use_high_res_features_in_sam、use_obj_ptrs_in_encoder、mask_downsample |
多尺度特征提取;启用对象指针时增加一个 Conv2d(1,1,k=4,s=4) 将掩码提示下采样到 stride 4 并缩放到 SAM logit 尺度 |
| Part 2 记忆注意力 | memory_attention |
用 memory_attention.d_model 确定 hidden_dim,将当前帧特征与过去帧记忆做交叉注意力 |
| Part 3 记忆编码 | memory_encoder、num_maskmem、maskmem_tpos_enc、no_mem_embed、sigmoid_scale/bias_for_mem_enc |
num_maskmem(可访问的记忆帧数)个记忆各带可学习时间位置编码;no_mem_embed/no_mem_pos_enc 是"无记忆"占位 token |
| Part 4 SAM 头 | sam_prompt_encoder、sam_mask_decoder、obj_ptr_proj 等 |
复用 SAM 风格提示编码器 + SAM2MaskDecoder 生成最终掩码与对象指针 |
常用参数及其默认值(摘自 __init__ 签名):num_maskmem=7、image_size=512、backbone_stride=16、sigmoid_scale_for_mem_enc=1.0、sigmoid_bias_for_mem_enc=0.0、max_cond_frames_in_attn=-1、max_obj_ptrs_in_encoder=16、add_tpos_enc_to_obj_ptrs=True、pred_obj_scores=False、memory_temporal_stride_for_eval=1、compile_image_encoder=False。其中 compile_image_encoder=True 时会用 torch.compile(mode="max-autotune", fullgraph=True) 只编译 forward 函数(保留模块结构以便加载 checkpoint,首次前向较慢)。
_build_sam_heads(sam.py#L357-L402)是 SAM 头的工厂方法:
sam_image_embedding_size = image_size // backbone_stride(如 512/16=32);- 构建
PromptEncoder(embed_dim=hidden_dim, image_embedding_size=(N,N), input_image_size=(S,S), mask_in_chans=16); - 构建
SAM2MaskDecoder(num_multimask_outputs=3, transformer=SAM2TwoWayTransformer(depth=2, mlp_dim=2048, num_heads=8), iou_head_depth=3, iou_head_hidden_dim=256, ...),mask_in_chans=16等超参数沿用 SAM 原始设计; - 对象指针投影
obj_ptr_proj默认是nn.Identity,启用use_obj_ptrs_in_encoder后变为线性层或 3 层MLP(use_mlp_for_obj_ptr_proj=True时)。
核心推理路径:forward_image 与 track_step
SAM2Model 的对外方法是 forward_image 与 track_step(文档字符串示例):
>>> model = SAM2Model(image_encoder, memory_attention, memory_encoder)
>>> image_batch = torch.rand(1, 3, 512, 512)
>>> features = model.forward_image(image_batch)
>>> track_results = model.track_step(0, True, features, None, None, None, {})
数据流如下:
forward_image(sam.py#L599-L607):图像经image_encoder得到backbone_out;若use_high_res_features_in_sam=True,会预先用sam_mask_decoder.conv_s0/conv_s1投影 FPN level 0/1 特征,避免每次点击重复计算。track_step(sam.py#L919-L977)执行单帧跟踪步,内部_track_step依次调用:_prepare_memory_conditioned_features(sam.py#L629):从output_dict的cond_frame_outputs与non_cond_frame_outputs中选取最近的条件帧(select_closest_cond_frames)与最多num_maskmem - 1个非条件历史帧记忆,拼接对象指针(可选,带一维正弦时间位置编码)后送入memory_attention;条件帧首次处理时若无记忆,则用no_mem_embed占位或按directly_add_no_mem_embed直接相加。_forward_sam_heads(sam.py#L404):处理点提示(point_coords形状(B,P,2)像素坐标、point_labels中 1=正点击、0=负点击、-1=填充)、掩码提示(必要时双线性下采样到mask_input_size),经sam_prompt_encoder得到稀疏/稠密嵌入,再交由sam_mask_decoder输出低/高分辨率多掩码与 IoU 估计;multimask_output=True时按 IoU 选最优候选,同时抽取obj_ptr与object_score_logits。若pred_obj_scores开启且判定目标未出现,掩码会被替换为NO_OBJ_SCORE占位。_encode_memory_in_output→_encode_new_memory(sam.py#L793):将当前帧顶层特征与预测掩码(sigmoid 后按sigmoid_scale_for_mem_enc/sigmoid_bias_for_mem_enc缩放,可按binarize_mask_from_pts_for_mem_enc二值化)送入memory_encoder,写入current_out["maskmem_features"]供后续帧使用;评估时可施加_apply_non_overlapping_constraints(对重叠区域保留最高分对象,其余压低到-10.0,sam.py#L988-L1004)。- 特例路径:
use_mask_input_as_output_without_sam=True且有掩码输入时,走_use_mask_as_output(sam.py#L552),直接以20.0*x - 10.0的 logit 映射把输入掩码当作输出,跳过 SAM 提示/解码流程(用于用户已确认掩码的校正帧)。
- 返回值
current_out包含pred_masks、pred_masks_high_res、obj_ptr,推理模式追加object_score_logits,以及编码后的记忆特征。
另外两个辅助方法:set_binarize 供视频预测器在评估时切换掩码二值化;set_imgsz(sam.py#L1010-L1022)同步更新 image_encoder、sam_prompt_encoder 的 input_image_size/image_embedding_size/mask_input_size(均基于 backbone_stride 换算,注释标明 ViT patch size 固定为 16)。
build.py 中的真实超参数组合
SAM2Model 的实际使用参数在 ultralytics/models/sam/build.py#L278-L309 的 _build_sam2 中给出,这是理解各布尔开关默认"关闭"与实际"开启"差异的关键:
image_encoder = ImageEncoder(trunk=Hiera(...), neck=FpnNeck(d_model=256, ...), scalp=1)
memory_attention = MemoryAttention(d_model=256, pos_enc_at_input=True, num_layers=4, ...)
memory_encoder = MemoryEncoder(out_dim=64)
sam2 = SAM2Model(
image_encoder=image_encoder, memory_attention=memory_attention, memory_encoder=memory_encoder,
num_maskmem=7, image_size=1024,
sigmoid_scale_for_mem_enc=20.0, sigmoid_bias_for_mem_enc=-10.0,
use_mask_input_as_output_without_sam=True, directly_add_no_mem_embed=True,
use_high_res_features_in_sam=True, multimask_output_in_sam=True,
iou_prediction_use_sigmoid=True, use_obj_ptrs_in_encoder=True,
add_tpos_enc_to_obj_ptrs=True, only_obj_ptrs_in_the_past_for_eval=True,
pred_obj_scores=True, pred_obj_scores_mlp=True, fixed_no_obj_ptr=True,
multimask_output_for_tracking=True, use_multimask_token_for_obj_ptr=True,
multimask_min_pt_num=0, multimask_max_pt_num=1, use_mlp_for_obj_ptr_proj=True,
no_obj_embed_spatial=is_sam2_1, proj_tpos_enc_in_obj_ptrs=is_sam2_1,
use_signed_tpos_enc_to_obj_ptrs=is_sam2_1,
sam_mask_decoder_extra_args={
"dynamic_multimask_via_stability": True,
"dynamic_multimask_stability_delta": 0.05,
"dynamic_multimask_stability_thresh": 0.98,
},
)
可以看到:实际部署中 image_size 为 1024(而非类默认 512)、高分辨率特征、对象指针、目标分数预测、多掩码输出与"掩码输入直通"全部启用;is_sam2_1(checkpoint 名称含 sam2.1 时)额外开启 SAM 2.1 专属的三个开关。这些组合体现了类默认值"最小可用"、构建函数"完整特性"的分层设计。
SAM3Model:继承自 SAM2 的差异化变体
SAM3Model(SAM2Model)(sam.py#L1025)复用 SAM2 的记忆跟踪骨架,主要差异有四:
- 默认几何参数:
image_size=1008、backbone_stride=14(对应 SAM 3 的 ViT 主干 patch 尺寸),而 SAM2 默认是 512/16。 - 重建掩码解码器:
__init__末尾用TwoWayTransformer(而非 SAM2 专用的SAM2TwoWayTransformer)重建sam_mask_decoder,其余解码器超参数(num_multimask_outputs=3、mlp_dim=2048、num_heads=8、iou_head_depth=3、iou_head_hidden_dim=256)保持一致(sam.py#L1104-L1121)。 - 图像前向接口不同:
forward_image调用image_encoder.forward_image_sam2(img_batch)而非直接image_encoder(img_batch)(sam.py#L1123-L1131),即 SAM 3 的视觉主干通过 SAM 2 兼容接口输出多尺度特征。 set_imgsz额外同步记忆编码器:在父类逻辑之上更新self.memory_encoder.mask_downsampler.interpol_size = [size // 14 * 16 for size in imgsz](sam.py#L1133-L1136)。- 面积收缩抑制:新增
_suppress_shrinked_masks(shrink_threshold=0.3)与_suppress_object_pw_area_shrinkage(sam.py#L1138-L1159),先按像素级非重叠约束计算每张掩码面积收缩比例,对收缩超过阈值的掩码整体压低到-10.0,用于抑制多对象场景下被竞争对象"吃掉"大量区域的噪声掩码。
上下游调用链:从 SAM 接口到模型构建
将本文件放回整个包中看,调用关系如下:
- 模型入口:ultralytics/models/sam/model.py 中的
SAM类要求权重为.pt/.pth,并依据文件名中的sam2/sam3子串设置is_sam2/is_sam3;_load中 SAM 3 走 build_sam3.py 的build_interactive_sam3(其内部在 build_sam3.py#L307 实例化SAM3Model),SAM/SAM2 走 build.py 的build_sam。task_map按变体路由到Predictor、SAM2Predictor或SAM3Predictor。 - 尺寸适配:ultralytics/models/sam/predict.py#L573-L579 中,预测器在
source is None或首次推理时调用self.model.set_imgsz(self.imgsz)——这正是本文讲解的set_imgsz方法被触发的地方;视频跟踪路径(predict.py#L2612附近)对tracker.model做同样处理。 - 推理职责划分:
SAMModel/SAM2Model只提供forward_image、track_step等原语,真正的逐帧状态管理(output_dict、条件帧/非条件帧划分、内存 bank 维护)位于predict.py的预测器与SAM2VideoPredictor中,这与源码中 "forward() operations moved to SAMPredictor" 的注释一致。
适用前提与使用注意事项
- 本指南所述行为以当前仓库源码为准;SAM/SAM2/SAM3 仅支持加载预训练
.pt/.pth权重,SAM(...)构造函数对其它扩展名会直接抛出NotImplementedError(见 model.py#L59-L60)。 SAMModel.set_imgsz中 patch size 固定为 16(图像嵌入尺寸 = 输入尺寸/16),SAM2Model.set_imgsz则基于backbone_stride(SAM2 为 16、SAM3 为 14)换算嵌入与掩码输入尺寸;修改输入尺寸时必须经由预测器统一调用set_imgsz,而不是手工改动编码器属性,否则提示编码器与解码器会失配。- 若直接以
SAM2Model(...)最小参数集实例化(不经过_build_sam2),use_obj_ptrs_in_encoder、pred_obj_scores等开关均为关闭状态,得到的将是"仅记忆融合"的简化形态;生产配置请以build.py/build_sam3.py中的参数组合为参考。 - 相关文档可继续参考 API 参考页 docs/en/reference/models/sam/modules/sam.md 以及 SAM 模型总览页 docs/en/models/sam.md。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0627
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00