MLX-VLM 中的 LocateAnything:在 Mac 上运行 NVIDIA 3B 视觉定位模型的完整指南
【免费下载链接】mlx-vlmMLX-VLM is a package for inference and fine-tuning of Vision Language Models (VLMs) on your Mac using MLX.项目地址: https://gitcode.com/GitHub_Trending/ml/mlx-vlm
LocateAnything 是 NVIDIA 发布的 3B 视觉语言定位模型(vision-language grounding model),专门用于在图像中定位目标对象与指代区域(referred regions)。本仓库(mlx-vlm)已将其完整移植到 Apple Silicon 上运行:包括 MoonViT 视觉编码塔、Qwen2.5 文本骨干、自定义图像处理器,以及模型专属的 Parallel Box Decoding(并行框解码)路径。读完本文,你将掌握如何通过 CLI 与 Python API 在 Mac 上运行 LocateAnything 的两种生成方式(自回归与并行框解码),并理解其从视觉塔到坐标 token 的完整架构原理。
模型概览
LocateAnything 是一个约 30 亿参数的视觉语言模型,核心任务覆盖视觉定位(visual grounding)、开放词汇目标定位(open-vocabulary object localization)与指代表达定位(referring expression localization),即根据自然语言描述在图像中输出目标的位置坐标框。其关键信息如下表:
| 项目 | 内容 |
|---|---|
| Model ID | nvidia/LocateAnything-3B |
| 架构 | MoonViT 视觉编码器 + MLP 连接器 + Qwen2.5 语言模型 |
| 参数量 | 3B |
| 模态 | 图像 + 文本 |
| 核心任务 | 视觉定位、开放词汇目标定位、指代表达定位 |
在 mlx-vlm 中,该模型位于 mlx_vlm/models/locateanything/ 目录,模型类型标识为locateanything(见 config.py),并通过 mlx_vlm/models/init.py 注册到模型工厂,因此可以直接使用nvidia/LocateAnything-3B作为模型 ID 加载。
通过 CLI 运行
与多数视觉语言模型一致,LocateAnything 支持标准的自回归生成(autoregressive generation),使用仓库提供的mlx_vlm.generate命令行入口即可:
mlx_vlm.generate \ --model nvidia/LocateAnything-3B \ --image examples/images/cats.jpg \ --prompt "Locate the cats." \ --max-tokens 128 \ --temperature 0.0参数说明:
--model:指定 Hugging Face 上的模型标识符nvidia/LocateAnything-3B,首次运行会自动下载权重;--image:输入图像路径,这里使用仓库自带的示例图 examples/images/cats.jpg;--prompt:定位指令,例如"Locate the cats.";--max-tokens:最大生成 token 数。README 特别提示,场景中目标对象较多时应适当增大该值(例如 256 或 512),以保证每个对象都能被完整输出;--temperature:采样温度,定位任务推荐设为0.0(贪心解码),保证输出坐标的确定性。
通过 Python API 运行
自回归生成
在 Python 中使用load加载模型与处理器,配合apply_chat_template组装提示词,再调用generate完成推理:
from mlx_vlm import generate, load from mlx_vlm.prompt_utils import apply_chat_template model, processor = load("nvidia/LocateAnything-3B") prompt = apply_chat_template( processor, model.config, "Locate the cats.", num_images=1, ) result = generate( model=model, processor=processor, prompt=prompt, image="examples/images/cats.jpg", max_tokens=128, temperature=0.0, ) print(result.text)要点:
apply_chat_template需要传入num_images=1,因为模板中需要放置<image-N>占位符(见下文「处理器与图像占位符展开」);- 生成结果
result.text中包含模型输出的坐标 token 序列,需要按模型约定解析为边界框; max_tokens=128对于单目标场景足够,多目标场景请按 README 建议增大。
Parallel Box Decoding(并行框解码)
LocateAnything 相比普通 VLM 的特殊之处在于它暴露了model.pbd_generate这一直接模型 API,用于fast、hybrid、slow三种生成模式。该路径不走高层generate,而是自行准备输入后直接调用模型方法:
from mlx_vlm import load from mlx_vlm.prompt_utils import apply_chat_template from mlx_vlm.utils import prepare_inputs model, processor = load("nvidia/LocateAnything-3B") prompt = apply_chat_template( processor, model.config, "Locate the cats.", num_images=1, ) inputs = prepare_inputs( processor, images=["examples/images/cats.jpg"], prompts=prompt, ) input_ids = inputs.pop("input_ids") inputs.pop("attention_mask", None) tokens = model.pbd_generate( input_ids, generation_mode="hybrid", max_tokens=128, **inputs, ) print(processor.decode(tokens, skip_special_tokens=False))几个值得注意的细节:
prepare_inputs返回的input_ids需要从字典中取出单独传入,attention_mask会被显式丢弃(inputs.pop("attention_mask", None)),因为 PBD 解码器内部使用自建的块状掩码而非标准因果掩码;model.pbd_generate的实现位于 locateanything.py:它先通过get_input_embeddings计算输入嵌入(若未提供缓存视觉特征则前向视觉塔与连接器),再构造PBDDecoder并驱动生成循环;- 解码结果直接是 token id 列表,需要调用
processor.decode(..., skip_special_tokens=False)还原为包含特殊标记的文本,才能看到完整的<box>...</box>结构。
generation_mode 的三种模式
generation_mode参数接受以下取值,对应 pbd.py 中PBDDecoder的三种运行策略:
| 模式 | 行为 |
|---|---|
hybrid | 先用 Parallel Box Decoding 生成,遇到无法并行处理的模式(如error_box)时自动回退到自回归解码,是默认推荐模式 |
fast | 仅使用 Parallel Box Decoding,任何非标准框模式都会被强制按coord_box处理,速度最快但可能牺牲精度 |
slow | 完全走自回归解码,但经由 LocateAnything 的 PBD 包装器(即复用同一套 PBD 调用框架,只是内部逐 token 生成) |
架构解析
README 用五点概括了模型架构,下面结合源码逐一展开(主模型定义见 locateanything.py)。
视觉塔:MoonViT 图像编码器
MoonViT 视觉塔的配置在 config.py 的VisionConfig中定义:hidden size 1152、27 层、16 个注意力头、intermediate size 4304,patch size 为 14,并带 2×2 的 patch 合并核(merge_kernel_size=[2, 2])。
实现位于 vision.py 的VisionModel,其关键组件包括:
- PatchEmbed(vision.py):14×14 卷积切 patch,并使用
Learnable2DInterpPosEmb学习式二维插值位置编码——位置编码以 64×64 的初始网格预置,遇到不同分辨率输入时通过双三次插值(bicubic)适配任意 grid 尺寸; - 二维 RoPE(
Rope2DPosEmb,vision.py):在 512×512 的网格上预计算二维频率(x/y 两个方向各自编码),按输入图像的实际 grid 形状切片使用,从而为任意长宽比图像提供位置信息; - patch merging(
patch_merger,vision.py):将 2×2 邻域 patch 的特征在空间维度上拼接到 channel 维,输出序列长度缩减为原来的 1/4,与处理器中的merge_kernel_size对应; - 块状注意力掩码(
make_block_attention_mask,vision.py):多图输入时按cu_seqlens将各图的 token 分组,禁止跨图注意力。
连接器:LayerNorm + 两层线性
LocateAnythingMultiModalProjector(locateanything.py)将合并后的视觉特征投影到语言模型的 hidden size:输入维度为vit_hidden * merge_kernel[0] * merge_kernel[1](即 1152×2×2 = 4608),经过 LayerNorm → Linear → GELU → Linear 后输出 2048 维(即 Qwen2.5 的 hidden size)。
语言模型:Qwen2 风格解码器
文本骨干是 Qwen2 风格解码器,配置见 config.py 的TextConfig:hidden size 2048、36 层、16 个注意力头、2 个 KV 头(GQA)、rope_theta=1e6、最大位置 32768,并开启权重绑定(tie_word_embeddings=True,同时vocab_size=152681)。
实现位于 language.py 的LanguageModel:绑定权重时直接通过embed_tokens.as_linear(out)输出 logits,省去独立的 lm_head 层(因此 locateanything.py 的sanitize会跳过language_model.lm_head.weight)。
特殊 token 体系
模型使用一组专用 token id 表达坐标框结构(config.py):
| token | id | 含义 |
|---|---|---|
image_token_index | 151665 | 图像占位符 token |
box_start_token_id | 151668 | 框开始 |
box_end_token_id | 151669 | 框结束 |
ref_start_token_id | 151672 | 指代对象开始 |
ref_end_token_id | 151673 | 指代对象结束 |
coord_start_token_id | 151677 | 坐标范围开始 |
coord_end_token_id | 152677 | 坐标范围结束 |
none_token_id | 4064 | 空框标记 |
text_mask_token_id | 151676 | MTP 掩码 token(block_size=6) |
null_token_id | 152678 | 空位填充 token |
switch_token_id | 152679 | 模式切换 token |
其中block_size=6是 PBD 并行块的大小——pbd.py 在初始化时强制断言其为 6,因为handle_pattern/decode_ref的逻辑均假设单个框块恰好包含 6 个 token。
处理器:图像占位符展开
LocateAnythingProcessor(processing_locateanything.py)的核心职责是把文本中的<image-N>占位符展开为<img>...<IMG_CONTEXT>...</img>的 token 序列:先由LocateAnythingImageProcessor把图像切成 patch 并返回image_grid_hws(每张图的 grid 高宽),再根据merge_kernel_size计算每个占位符应展开的<IMG_CONTEXT>数量(grid_h * grid_w // merge_length),并校验占位符数量与图像数量一致,否则抛出异常。
图像预处理(image_processing_locateanything.py)遵循「先缩放、后补齐」的策略:若 patch 总数超过in_token_limit(默认 25600)则按比例缩小;随后把宽高向上取整到merge_kernel * patch_size的整数倍(例如 2×14=28 的倍数),保证图像能被 2×2 合并核完整切分;最后校验 grid 不超过 512×512(否则超过 RoPE 预置范围报错)。
此外,README 特别注明该自定义处理器支持save_pretrained():调用后会写出processor_config.json、preprocessor_config.json与chat_template.json三个文件(processing_locateanything.py),其中preprocessor_config.json记录了image_mean、image_std、in_token_limit、merge_kernel_size、patch_size等完整预处理参数,便于本地保存与复加载。
PBD:并行框解码器
PBDDecoder(pbd.py)是整个 LocateAnything 移植最具特色的部分。其核心思想是:利用模型的多 token 预测(MTP)能力,一次性前向产出 6 个位置的 logits 块,然后通过模式识别从概率分布中直接「解析」出一整个坐标框 token 块,而不是逐 token 解码。
关键子流程:
- 块前向(MTP):
_forward_mtp与_mtp_prefill将「上一步生成的最后一个 token + 5 个掩码 token」作为窗口送入语言模型,配合build_magi_block_mask(language.py)构造的块状注意力掩码与错位 position ids,一次性获得 6 个位置各自的 logits,随后cache.trim(B)回退 KV 缓存; - 框合法性判定:
is_valid_box_frame(pbd.py)检查box_start概率是否超过阈值(默认 0.6)、是否被im_end/null抢占,从而区分legal_box、empty_box与illegal_box; - 坐标平均解码:
decode_bbox_avg(pbd.py)对中间 4 个坐标位置各自取 top-k(默认 5)候选,筛选出落在coord_start..coord_end范围内的 token id;hybrid模式下若首候选概率低于 0.9 且候选跨度超过 60,判定为异常并输出 0(坐标原点),否则输出首候选; - 模式回退:
handle_pattern(pbd.py)将解码结果分类为im_end(终止)、empty_box、coord_box(4 坐标框)、point_box(2 坐标点)、error_box或ref_object(指代对象);其中只有error_box会触发need_switch_to_ar,即hybrid模式在此回退到自回归逐 token 解码,而fast模式则强制按coord_box处理以保持全并行。
目录结构
本模型在仓库中的完整文件布局如下:
mlx_vlm/models/locateanything/ __init__.py # 导出 Model / Processor / 各 Config config.py # VisionConfig / TextConfig / ModelConfig image_processing_locateanything.py # 图像缩放、归一化、patchify language.py # Qwen2 风格语言模型 + MTP 块掩码 locateanything.py # 主模型、投影器、pbd_generate、权重清洗 pbd.py # PBD 解码器与坐标解析逻辑 processing_locateanything.py # 处理器:占位符展开、save_pretrained vision.py # MoonViT 视觉塔(PatchEmbed/RoPE/合并)入口文件init.py 将Model、LanguageModel、VisionModel、Processor、ImageProcessor及各配置类统一导出,供模型工厂自动发现与加载。
使用注意事项
- 多图输入:当 prompt 中包含多张图像时,
apply_chat_template的num_images必须传len(images),同时generate/prepare_inputs中传入相同数量的图像,保证占位符与图像一一对应; - max-tokens 与场景复杂度:README 明确建议在对象较多的场景增大
--max-tokens,否则可能截断后续对象的坐标输出; - 解码模式选择:追求速度且场景简单可用
fast;追求鲁棒性用默认hybrid;slow相当于经 PBD 包装器的纯自回归基线; - 保存处理器:
processor.save_pretrained(<目录>)会同时写出processor_config.json、preprocessor_config.json与chat_template.json,后续可通过LocateAnythingProcessor.from_pretrained(processing_locateanything.py)从本地目录恢复,包含完整的图像预处理参数与聊天模板。
【免费下载链接】mlx-vlmMLX-VLM is a package for inference and fine-tuning of Vision Language Models (VLMs) on your Mac using MLX.项目地址: https://gitcode.com/GitHub_Trending/ml/mlx-vlm
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考