首页
/ Hugging Face Transformers 中的 Mllama:Llama 3.2 Vision 多模态模型架构解析与推理实战

Hugging Face Transformers 中的 Mllama:Llama 3.2 Vision 多模态模型架构解析与推理实战

2026-09-07 14:11:09作者:宣利权Counsellor

关联文档:docs/source/en/model_doc/mllama.md | 核心实现:modeling_mllama.pyprocessing_mllama.py

导读

Mllama 是 Hugging Face Transformers 对 Meta Llama 3.2-Vision(11B / 90B 图像推理大模型)的官方实现封装,采用"Llama 3.1 文本大模型 + 独立视觉适配器 + 稀疏交叉注意力"的架构,支持图像理解、推理、描述与问答。本文以模型文档为主体,结合仓库中配置、建模与预处理源码,从模型结构与全部配置默认值、<|image|> 占位与交叉注意力掩码机制、训练/生成时的 lm_head 陷阱,到 Instruct 与 Base 模型的两套可直接运行的推理代码,逐层展开讲解。读完后你将能正确选型(MllamaForConditionalGeneration vs MllamaForCausalLM)、读懂三种 Config 的全部参数,并能基于 AutoProcessor.apply_chat_template 或裸 prompt 完成多模态生成。


一、模型概述:Llama 3.2-Vision 是什么

根据 mllama.md 的 Overview,Llama 3.2-Vision 是 Llama 3.2 系列中的多模态大语言模型集合,提供 11B 与 90B 两种规模的预训练版与指令微调版,输入为"文本 + 图像"、输出为文本,属于图像推理生成模型。指令微调版针对视觉识别、图像推理、图像描述和关于图像的通用问答进行了优化。

在模型架构上,文档明确指出:

  • Llama 3.2-Vision 建立在 Llama 3.1 纯文本模型之上,后者是一个使用优化 Transformer 架构的自回归语言模型;
  • 指令微调版本通过**监督微调(SFT)基于人类反馈的强化学习(RLHF)**对齐人类的 helpfulness 与 safety 偏好;
  • 为支持图像识别,模型使用一个单独训练的视觉适配器(vision adapter)与预训练的 Llama 3.1 语言模型集成,该适配器由一系列交叉注意力层组成,将图像编码器表示送入核心 LLM。

对照仓库实现,这一"视觉编码器 + 投影 + 稀疏交叉注意力"的描述在 modeling_mllama.py 中逐一对齐:

  • MllamaModelvision_modellanguage_modelmulti_modal_projector 构成(modeling_mllama.py#L1271-L1286),其中投影层把视觉输出维度 vision_output_dim=7680 线性映射到文本 hidden_size=4096
  • 文本模型在 config.cross_attention_layers 指定的层(默认 [3, 8, 13, 18, 23, 28, 33, 38])位置插入 MllamaCrossAttentionDecoderLayer,其余层为 MllamaSelfAttentionDecoderLayermodeling_mllama.py#L1025-L1032);
  • 视觉编码器本身是"双编码器"结构:主 Transformer 编码器 + 一个被称为 global 的第二编码器,详见后文 MllamaVisionModel

一组清晰的派生结论

  • 图像不是被当作"文字前缀拼进序列"来建模的,而是由交叉注意力层在指定层按需读取(文本模型在无图像输入时会跳过这些交叉注意力层,见 modeling_mllama.py#L1122-L1132);
  • 由于 input_modalities = ("image", "text") 且支持 gradient checkpointing、SDPA、Flash Attention、Flex Attention(见 modeling_mllama.py#L762-L784),该实现可直接用于多模态训练,也可按需加载不同的注意力后端。

二、三种配置类:MllamaConfig / MllamaTextConfig / MllamaVisionConfig

完整类定义位于 configuration_mllama.pyMllamaConfig 通过 sub_configs = {"text_config": MllamaTextConfig, "vision_config": MllamaVisionConfig} 把三个配置组织成树形结构(configuration_mllama.py#L178),在 __post_init__ 中自动把 dict 形式的子配置实例化为对应配置类。

MllamaTextConfig(model_type = "mllama_text_model"

即 Llama 3.1 风格解码器的配置,源码默认值如下(configuration_mllama.py#L124-L141):

参数 默认值 说明
vocab_size 128256 词表大小
hidden_size 4096 隐藏层维度
hidden_act "silu" 激活函数(对应 SwiGLU MLP)
num_hidden_layers 40 解码器层数
num_attention_heads 32 自注意力头数
num_key_value_heads 8 KV 头数(GQA)
intermediate_size 14336 MLP 中间维度
rope_parameters None RoPE 自定义参数(default_theta = 500000.0
rms_norm_eps 1e-5 RMSNorm epsilon
max_position_embeddings 131072 最长上下文(128K)
tie_word_embeddings False 不绑定输入/输出嵌入权重
cross_attention_layers [3, 8, 13, 18, 23, 28, 33, 38] 稀疏交叉注意力层下标
bos_token_id / eos_token_id / pad_token_id 128000 / 128001 / 128004 特殊 token id

若未显式提供,cross_attention_layers 会在 __post_init__ 中补上上述 8 层默认值。

MllamaVisionConfig(model_type = "mllama_vision_model"

视觉编码器配置,源码默认值(configuration_mllama.py#L61-L75):

参数 默认值 说明
hidden_size 1280 视觉隐藏层维度
hidden_act "gelu" 激活函数
num_hidden_layers 32 主 Transformer 编码器层数
num_global_layers 8 第二个(global)Transformer 编码器的层数
attention_heads 16 注意力头数
num_channels 3 输入通道数
intermediate_size 5120 MLP 中间维度
vision_output_dim 7680 视觉模型最终输出维度(含主编码器中 5 层中间特征拼接)
image_size 448 每个 tile 的边长
patch_size 14 patch 边长,单 tile 得到 (448/14)² = 1024 个 patch 再加 1 个 class token
norm_eps 1e-5 LayerNorm epsilon
max_num_tiles 4 单张图像最多切分的 tile 数
intermediate_layers_indices [3, 7, 15, 23, 30] 需要抽取并拼接中间特征的主编码器层下标
supported_aspect_ratios [[1,1],[1,2],[1,3],[1,4],[2,1],[2,2],[3,1],[4,1]] 支持的图像切分宽高比
initializer_range 0.02 初始化范围

注意其约束校验:若 max_num_tiles != 4 而仍使用默认 supported_aspect_ratiosvalidate_architecture 会直接抛 ValueErrorconfiguration_mllama.py#L85-L91)。另外 attribute_map = {"num_attention_heads": "attention_heads"} 兼容了通用命名。

MllamaConfig(model_type = "mllama"

最外层聚合配置(configuration_mllama.py#L174-L182):

  • vision_config / text_config:两个子配置,均允许以 dict 传入,__post_init__ 会自动实例化;
  • image_token_index(即图像占位 token id):默认 128256,正是 vocab_size 之外新增的 <|image|> 占位 token;
  • attribute_map 提供兼容别名:image_token_idimage_token_index

官方用法:configuration = MllamaConfig(vision_config, text_config) 组合两个子配置后构建 MllamaForConditionalGeneration

独立构建与使用示例

from transformers import (
    MllamaConfig,
    MllamaForConditionalGeneration,
    MllamaTextConfig,
    MllamaVisionConfig,
    MllamaTextModel,
    MllamaVisionModel,
)

# 1) 组合成一个完整的多模态配置并实例化
vision_config = MllamaVisionConfig()
text_config = MllamaTextConfig()
configuration = MllamaConfig(vision_config=vision_config, text_config=text_config)
model = MllamaForConditionalGeneration(configuration)

# 2) 单独使用视觉编码器 / 文本模型(随机初始化,用于调试网络形状)
vision_model = MllamaVisionModel(MllamaVisionConfig())
text_model = MllamaTextModel(MllamaTextConfig())

三、视觉侧实现:tile 切分、宽高比与双编码器

3.1 Processor 如何把一张图变成 4 维视觉张量

MllamaProcessor 收到图像时,会由内部的图像处理器执行 resize → rescale → normalize → 切 tile 的流水线。图像处理器有两个变体:

  • MllamaImageProcessor(基于 torchvision 后端,默认使用 ImageNet 均值和标准差),定义于 image_processing_mllama.py,处理过程中会调用 split_to_tilesgroup_images_by_shapereorder_images 等图像变换;
  • MllamaImageProcessorPil(基于 NumPy/PIL 后端),定义于 image_processing_pil_mllama.py,提供 split_to_tiles_nppack_images 等同类功能。

可设置 max_image_tiles(Processor 默认 4,见 processing_mllama.py#L26-L31)控制每张图最多切多少个 tile。核心逻辑参考:

  • get_all_supported_aspect_ratios(max_image_tiles):枚举所有 宽 × 高 <= max_image_tiles 的 (w, h) 排列,例如 get_all_supported_aspect_ratios(4) 返回 [(1,1),(1,2),(1,3),(1,4),(2,1),(2,2),(3,1),(4,1)]image_processing_mllama.py#L52-L79);
  • get_image_size_fit_to_canvas / get_optimal_tiled_canvas:在"保持宽高比、尽量不放大失真"的原则下,从候选画布中选出最优 tile 布局并计算缩放尺寸(image_processing_mllama.py#L82-L179)。

处理产物正是模型需要的 4 维视觉输入:

  • pixel_values:形状 (batch_size, num_concurrent_media, num_tiles, num_channels, height, width)
  • aspect_ratio_ids:每个图像选择预计算 tile 位置嵌入时的索引。id 为"支持宽高比列表下标 + 1",0 保留给 padding(无图像)。例如宽高比 [1, 1] → id 1、[1, 2](横向切成 2 块)→ id 2,依次类推(见 modeling_mllama.py#L872-L884 的参数说明);
  • aspect_ratio_mask:形状 (batch_size, max_num_images, max_num_tiles),1 表示真实 tile、0 表示 padding tile,用于避免在 padding tile 上做注意力(modeling_mllama.py#L885-L889)。

3.2 MllamaVisionModel 的双编码器前向

MllamaVisionModel 的组成(modeling_mllama.py#L812-L852):

  1. Patch embedding:14×14 卷积(stride=14,无 bias),将每个 tile 打平成 1024 个 patch;
  2. class_embedding:在每个 tile 前拼 1 个 class token(num_patches = (448//14)² + 1 = 1025);
  3. 三类预计算位置嵌入gated_positional_embedding、以及 tile 切分前后的 pre_tile_positional_embedding / post_tile_positional_embedding,均为带门控(gated)的可学习嵌入,借助 aspect_ratio_ids 查找对应宽高比的嵌入表;
  4. 主编码器 transformer(32 层):输出 last_hidden_state 与各层 hidden states;
  5. global_transformer(8 层,gated 版):作用在"把所有 tile 拼平、并按 tile 数补 pad 到 8 的倍数"后的序列上,做 tile 间的全局交互;
  6. 中间层特征拼接:从主编码器取出 intermediate_layers_indices = [3, 7, 15, 23, 30] 5 层的中间输出,与最终 hidden state 在最后一维拼接,得到 1280 + 5×1280 = 7680 维的 vision_output_dimmodeling_mllama.py#L989-L1003)。

前向输出可用官方 docstring 中的示例直接验证形状:

from PIL import Image
import httpx
from io import BytesIO
from transformers import AutoProcessor, MllamaVisionModel

checkpoint = "meta-llama/Llama-3.2-11B-Vision"
model = MllamaVisionModel.from_pretrained(checkpoint)
processor = AutoProcessor.from_pretrained(checkpoint)

url = "https://www.ilankelman.org/stopsigns/australia.jpg"
with httpx.stream("GET", url) as response:
    image = Image.open(BytesIO(response.read()))
inputs = processor(images=image, return_tensors="pt")

output = model(**inputs)
print(output.last_hidden_state.shape)
# torch.Size([1, 1, 4, 1025, 7680])
# 含义:batch=1, 媒体数=1, tile 数=4, 每 tile 1025 个 token, 特征维度 7680

四、文本侧实现:<|image|> 占位与交叉注意力掩码

4.1 文本中的图像占位与输入布局

文档给出的关键使用规则是:

  • 传入 Processor 的文本中,图像应插入位置必须写 "<|image|>" token;
  • 每个样本可以包含多张图像,不同样本的图像数量可以不同;Processor 会把批次内各样本补齐到"样本间最大图像数",并把每张图补齐到"图内最大 tile 数"。

MllamaProcessor 在布局阶段会做两件事(processing_mllama.py#L238-L253):

  1. 把图像转成嵌套列表make_nested_list_of_images),每层 sub-list 对应一个 batch 样本里的多张图;
  2. 对文本调用 build_string_from_input:若文本以 <|image|> 开头且尚未带 <begin_of_text>,会把 BOS token 插到图像占位与正文之间,保证格式正确(processing_mllama.py#L130-L164)。

4.2 交叉注意力掩码如何生成

因为图像走的是交叉注意力而非"拼在序列前",模型需要一个 4D 交叉注意力掩码 (batch_size, seq_length, max_num_images, max_num_tiles) 来控制"哪个文本 token 能看哪张图的哪些 tile":1 = 允许关注,0 = 禁止(见 modeling_mllama.py#L1059-L1065 的参数说明)。

该掩码在 Processor 内部分两步生成:

  • get_cross_attention_token_mask:在 input_ids 中定位 <|image|> token,生成稀疏的 [start, end] 关注区间——单张图关注到序列末尾;多张图各自关注到下一个图像 token 前;连续图像 token 视为一组共同关注后续全部文本(processing_mllama.py#L34-L79);
  • convert_sparse_cross_attention_mask_to_dense:把稀疏区间 + 每张图的真实 tile 数展开成 4D dense 掩码(processing_mllama.py#L82-L127)。

最终 MllamaProcessor.model_input_names 返回的是 tokenizer 输入名 + 图像输入名 + cross_attention_maskprocessing_mllama.py#L321-L329),也就是说一次 processor(...) 调用产出的正是模型 forward 所需的整套张量。

4.3 纯文本路径与 KV 缓存优化

文档的另一个关键建议与 Mllama 的生成优化直接呼应:解码生成后续步骤里,图像 token 的视觉 hidden states 已被缓存进各交叉注意力层的 KV cache,不再需要重复过一遍视觉编码器。这体现在 MllamaForConditionalGeneration.prepare_inputs_for_generation 的实现中——仅当 is_first_iteration(pre-fill/无 cache 阶段)才保留 pixel_valuesaspect_ratio_idsaspect_ratio_mask,其余解码迭代把它们置 Nonemodeling_mllama.py#L1520-L1561);_update_model_kwargs_for_generation 则负责把新的交叉注意力掩码行续接到 mask 尾部(modeling_mllama.py#L1563-L1577)。


五、关键使用提示与 lm_head 陷阱(务必阅读)

5.1 如何选模型类

文档原文明确给出两条选型规则:

  • 图像 + 文本输入:使用 MllamaForConditionalGeneration
  • 纯文本输入:使用 MllamaForCausalLM 做生成,从而避免加载整个视觉塔(vision tower),节省显存。

MllamaForCausalLM 只由 MllamaTextModel + lm_head 组成(modeling_mllama.py#L1160-L1172),且 _can_compile_fullgraph = True,可以进行 full-graph 编译加速;而多模态的 MllamaForConditionalGeneration 包含 MllamaModel + lm_headmodeling_mllama.py#L1398-L1405)。

5.2 图像占位 token 带来的词表不一致

以下为文档以 ⚠️ Tip 形式给出的重要警告,涉及训练与生成的边界情况。

Mllama 用一个额外 token 充当文本中图像位置的占位符。在仓库实现中具体体现为:文本嵌入层 self.embed_tokens = nn.Embedding(config.vocab_size + 8, ...),比标准词表多出 8 个 tokenmodeling_mllama.py#L1022),其中包含 <|image|><|python_tag|> 等占位;而 lm_head 的输出维度却是 config.text_config.vocab_sizemodeling_mllama.py#L1404)。

由此产生的影响:

  • 输入侧的词表与输出侧 lm_head 的维度不一致,输入输出嵌入权重本来就未绑定tie_word_embeddings=False);
  • 因此,如果要在图像 token 上计算 loss,或对图像 token 位置应用某些 logit processor,会因为超出 lm_head 索引范围而失败;
  • 训练时应在 labels 中把 <|image|> 特殊 token 掩码掉(如置为 -100),模型不应被训练去预测这些占位 token;
  • 生成时若出现 CUDA 侧 index 越界错误,可按文档给出的代码把 lm_head 扩展一个 token:
old_embeddings = model.get_output_embeddings()

num_tokens = model.vocab_size + 1
resized_embeddings = model._get_resized_lm_head(old_embeddings, new_num_tokens=num_tokens, mean_resizing=True)
resized_embeddings.requires_grad_(old_embeddings.weight.requires_grad)
model.set_output_embeddings(resized_embeddings)

从源码结构看,该修复的本质是让输出头多容纳 1 个维度的 logit,与嵌入侧为图像占位 token 预留的空间对齐。


六、实战:两套可直接运行的推理示例

6.1 Instruct 模型 + Chat Template(推荐日常使用)

MllamaProcessor 自带 apply_chat_template 方法,可将 Chat 消息(含 <image> 内容项)转换成文本(并在其中自动插入 <|image|>),再交给 Processor 分词与图像预处理。使用 transformers>=4.49.0 时,apply_chat_template 还能返回向量化输出(同时产出 token 与图像张量)。完整示例(源自文档 Usage Example):

from transformers import AutoProcessor, MllamaForConditionalGeneration


model_id = "meta-llama/Llama-3.2-11B-Vision-Instruct"
model = MllamaForConditionalGeneration.from_pretrained(model_id, device_map="auto")
processor = AutoProcessor.from_pretrained(model_id)

messages = [
    [
        {
            "role": "user",
            "content": [
                {"type": "image", "url": "https://llava-vl.github.io/static/images/view.jpg"},
                {"type": "text", "text": "What does the image show?"}
            ]
        }
    ],
]
inputs = processor.apply_chat_template(
    messages, add_generation_prompt=True, tokenize=True, return_dict=True, return_tensors="pt"
).to(model.device)
output = model.generate(**inputs, max_new_tokens=25)
print(processor.decode(output[0]))

要点说明:

  • apply_chat_templatetokenize=True 让消息直接走完整向量化管线;add_generation_prompt=True 会拼上模型的系统/助手引导前缀;
  • 图像以 {"type": "image", "url": ...} 形式声明在 content 中(同样支持本地 PIL Image),模板内部会据此在文本中插入 "<|image|>" 占位,无需手工书写;
  • 加载 meta-llama/Llama-3.2-11B-Vision-Instruct 前请确认已在 Hub 上通过该模型的授权协议并完成登录。

6.2 Base 模型 + 手动 prompt(原始调用路径)

不使用 chat template 时,需要手动在 prompt 中放置 <|image|> 占位,并直接把图像对象交给 processor(text=..., images=...)

import requests
from PIL import Image

from transformers import AutoProcessor, MllamaForConditionalGeneration


model_id = "meta-llama/Llama-3.2-11B-Vision"
model = MllamaForConditionalGeneration.from_pretrained(model_id, device_map="auto")
processor = AutoProcessor.from_pretrained(model_id)

prompt = "<|image|>If I had to write a haiku for this one"
url = "https://llava-vl.github.io/static/images/view.jpg"
raw_image = Image.open(requests.get(url, stream=True).raw)

inputs = processor(text=prompt, images=raw_image, return_tensors="pt").to(model.device)
output = model.generate(**inputs, do_sample=False, max_new_tokens=25)
print(processor.decode(output[0], skip_special_tokens=True))

上述两段代码分别对应文档中"Instruct model"与"Base model"两节,差异仅在于:Instruct 版本用 apply_chat_template 统一编排对话与图像 URL;Base 版本则显式给 prompt 拼接 <|image|> 占位。两者最终都会得到包含 input_idsattention_maskpixel_valuesaspect_ratio_idsaspect_ratio_maskcross_attention_mask 的完整输入,可直接交给 model.generate

6.3 纯文本生成(跳过视觉塔)

若输入中没有图像,直接使用 MllamaForCausalLM 即可:

from transformers import AutoTokenizer, MllamaForCausalLM

model = MllamaForCausalLM.from_pretrained("meta-llama/Llama-3.2-11B-Vision")
tokenizer = AutoTokenizer.from_pretrained("meta-llama/Llama-3.2-11B-Vision")

prompt = "If I had to write a haiku, it would be:"
inputs = tokenizer(prompt, return_tensors="pt")
generate_ids = model.generate(inputs.input_ids, max_length=40, do_sample=True, temperature=0.6)
result = tokenizer.batch_decode(generate_ids, skip_special_tokens=True, clean_up_tokenization_spaces=False)[0]
print(result)

这样不会加载视觉编码器与交叉注意力所需的视觉参数,对显存更友好,也支持 full-graph 编译。


七、完整 API 参考

mllama.md 的 API 清单,本文档覆盖以下类([[autodoc]] 会自动从源码 docstring 展开参数与用法,源码路径见模型配置):

对应文件 说明
MllamaConfig configuration_mllama.py 顶层聚合配置(vision + text 子配置 + 图像 token id)
MllamaTextConfig 同上 文本解码器配置
MllamaVisionConfig 同上 视觉编码器配置
MllamaProcessor__call__ processing_mllama.py 文本/图像预处理与交叉注意力掩码生成
MllamaImageProcessorpreprocess image_processing_mllama.py 基于 torchvision 的图像流水线
MllamaImageProcessorPilpreprocess image_processing_pil_mllama.py 基于 PIL/NumPy 的图像流水线
MllamaForConditionalGenerationforward modeling_mllama.py 完整多模态生成模型(视觉 + 文本 + lm_head)
MllamaForCausalLMforward 同上 纯文本语言模型(可编译、省显存)
MllamaTextModelforward 同上 含自注意力与交叉注意力层的文本主干
MllamaModel 同上 视觉编码器 + 投影 + 语言模型(无输出头)
MllamaVisionModelforward 同上 双编码器视觉主干(含中间层特征拼接)

你还可以用 MllamaProcessor.post_process_image_text_to_textgenerate 输出批量解码成字符串(它内部转调 tokenizer 的 batch_decodeskip_special_tokens 默认 True,见 processing_mllama.py#L294-L319)。

仓库内配套的校验与转换资源同样值得参考:模型/预处理/处理逻辑的单元测试位于 tests/models/mllama/test_modeling_mllama.pytests/models/mllama/test_image_processing_mllama.pytests/models/mllama/test_processing_mllama.py;若需把原始权重/分词器转换成 Transformers 格式,可查看 convert_mllama_weights_to_hf.py(含旧权重键映射、RoPE 置换、位置嵌入插值与分片合并逻辑)。

小结

Mllama 的实践要点可归纳为三条主线:选型上区分多模态(MllamaForConditionalGeneration)与纯文本(MllamaForCausalLM);输入上依赖 Processor 统一产出含 pixel_valuesaspect_ratio_ids/maskcross_attention_mask 的整套张量,并善用 apply_chat_template训练/生成的边界上警惕 <|image|> 占位带来的词表不一致——训练时掩码 labels、生成报 CUDA index 越界时按文档扩展 lm_head。围绕这三条主线,配合本文给出的各配置默认值与源码级调用链,即可在 Transformers 中稳定驾驭 Llama 3.2-Vision 的图像推理能力。

登录后查看全文
热门项目推荐
相关项目推荐