首页
/ Hugging Face Transformers 中的 Aria 多模态 MoE 模型:架构剖析、图像问答推理与量化部署实战

Hugging Face Transformers 中的 Aria 多模态 MoE 模型:架构剖析、图像问答推理与量化部署实战

2026-09-06 18:21:08作者:蔡怀权

Aria 是 Rhymes AI 开源的多模态 Mixture-of-Experts(MoE)模型,采用「轻量视觉编码器 + MoE 文本解码器 + 交叉注意力投影器」的组合,自 2024-12-06 起已正式集成进本仓库(关联文档),并以 AutoModel/AutoProcessor/pipeline 全链路可用。本文以该模型文档为主线,结合仓库内 src/transformers/models/aria/ 的配置、预处理、处理器与建模源码,系统讲解 Aria 的模型背景、图像问答的快速上手、torchao 权重量化部署、核心架构模块与前向融合流程、配置与图像预处理参数,让读者既能直接跑通推理,也能理解每个环节背后的源码实现。

Aria 是什么:多模态原生 MoE 模型

Aria 的目标是开源一套「从零训练多模态原生模型」的配方。其核心设计要点如下(来自模型文档):

  • 多模态 MoE 架构:文本由 MoE 解码器处理,视觉输入由一个轻量视觉编码器处理;
  • 按需激活:每个视觉 token 激活约 3.9B 参数,每个文本 token 激活约 3.5B 参数——即「总参数量更大,但每次推理只激活其中一小部分」;
  • 四阶段训练:语言预训练 → 多模态预训练 → 多模态长上下文预训练 → 多模态后训练,最终覆盖常见图文理解能力。

在模型文档右侧列出的可用算子上,Aria 同时支持 FlashAttention 与 SDPA 两种注意力实现。从源码看,AriaTextPreTrainedModelmodeling_aria.py 中声明了 _supports_flash_attn = True_supports_sdpa = TrueAriaPreTrainedModel 则继承了完整的 attention-backend 支持——这也是为什么下面示例可以在 attn_implementation="sdpa" 下运行。

快速上手:基于图片的文本生成

模型文档给出了两种等价的上手方式:Pipeline 一行调用,或 AutoModel + AutoProcessor 显式控制。两者底层走的是同一套聊天模板与生成逻辑。

方式一:Pipeline(极简入口)

image-to-text pipeline 会自动加载 rhymes-ai/Aria 并完成全部预处理:

from transformers import pipeline


pipeline = pipeline(
    "image-to-text",
    model="rhymes-ai/Aria",
    device=0,
)
pipeline(
    "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/pipeline-cat-chonk.jpeg",
    text="What is shown in this image?"
)

pipeline(...) 的第一个位置参数即图像(URL、本地路径或 PIL 图像均可),第二个 text= 为提问。适合快速验证模型效果与搭建最小 demo。

方式二:AutoModel + AutoProcessor(推荐用于生产)

通过 AutoModelForCausalLMAutoProcessor 加载模型,显式调用 apply_chat_template 构造多模态对话输入,再调用 generate

import torch

from transformers import AutoModelForCausalLM, AutoProcessor


model = AutoModelForCausalLM.from_pretrained(
    "rhymes-ai/Aria",
    device_map="auto",
    attn_implementation="sdpa",
)

processor = AutoProcessor.from_pretrained("rhymes-ai/Aria")

messages = [
    {
        "role": "user", "content": [
            # url 字段也可替换为本地图片路径或 PIL 图像
            {"type": "image", "url": "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/pipeline-cat-chonk.jpeg"},
            {"type": "text", "text": "What is shown in this image?"},
        ]
    },
]

inputs = processor.apply_chat_template(messages, add_generation_prompt=True, tokenize=True, return_dict=True, return_tensors="pt").to(model.device)
inputs = inputs.to(model.device, torch.bfloat16)

output = model.generate(
    **inputs,
    max_new_tokens=15,
    stop_strings=["<|im_end|>"],
    tokenizer=processor.tokenizer,
    do_sample=True,
    temperature=0.9,
)
output_ids = output[0][inputs["input_ids"].shape[1]:]
response = processor.decode(output_ids, skip_special_tokens=True)
print(response)

这段示例里值得注意的工程细节:

  • apply_chat_template(..., tokenize=True, return_dict=True, return_tensors="pt") 一次性完成「模板展开 + 多模态 token 占位 + tokenize」,返回可直接送入模型的 input_idspixel_valuespixel_mask 等字段;
  • 输入被显式转成 torch.bfloat16 后再送入生成;
  • stop_strings=["<|im_end|>"] 配合 tokenizer=processor.tokenizer,让生成在 Aria 的对话结束符处自然停止;
  • 解码时通过 output[0][inputs["input_ids"].shape[1]:] 截掉输入部分,仅对新增 token 调用 processor.decode(..., skip_special_tokens=True)

多图 / 多轮批量问答

modeling_aria.py 的 forward 示例中,展示了更完整的「多消息 + 多图」用法:先逐个消息调用 apply_chat_template 得到纯文本 prompt,再通过 processor(text=prompts, images=images, padding=True, return_tensors="pt") 把按消息分组的图片列表(如 [[image1, image2], [image3]])与文本对齐;generate(max_new_tokens=256) 之后用 batch_decode 批量还原答案。图片既可直接传入 PIL 图像,也可用 load_image 从 URL/本地路径加载。

用 torchao 把权重量化到 int4

量化通过低精度权重显著降低显存占用。仓库文档的 Quantization 总览 罗列了各后端;对 Aria 而言,文档推荐使用 torchaoint4_weight_only 量化,并搭配专为量化设计的 rhymes-ai/Aria-sequential_mlp 权重:

# pip install torchao
import torch

from transformers import AutoModelForCausalLM, AutoProcessor, TorchAoConfig


quantization_config = TorchAoConfig("int4_weight_only", group_size=128)
model = AutoModelForCausalLM.from_pretrained(
    "rhymes-ai/Aria-sequential_mlp",
    device_map="auto",
    quantization_config=quantization_config,
)
processor = AutoProcessor.from_pretrained(
    "rhymes-ai/Aria-sequential_mlp",
)

messages = [
    {
        "role": "user", "content": [
            {"type": "image", "url": "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/pipeline-cat-chonk.jpeg"},
            {"type": "text", "text": "What is shown in this image?"},
        ]
    },
]

inputs = processor.apply_chat_template(messages, add_generation_prompt=True, tokenize=True, return_dict=True, return_tensors="pt").to(model.device)
inputs = inputs.to(model.device, torch.bfloat16)

output = model.generate(
    **inputs,
    max_new_tokens=15,
    stop_strings=["<|im_end|>"],
    tokenizer=processor.tokenizer,
    do_sample=True,
    temperature=0.9,
)
output_ids = output[0][inputs["input_ids"].shape[1]:]
response = processor.decode(output_ids, skip_special_tokens=True)
print(response)

为什么推荐 Aria-sequential_mlp 权重:普通 Aria 的 MoE 专家使用 grouped GEMM 计算,形状为三维 (num_experts, in_features, out_features) 的权重张量;而 modeling_aria.pyAriaGroupedExpertsGemm 注释明确指出,量化工具难以直接处理 grouped GEMM。sequential_mlp checkpoint 将 grouped GEMM 替换为标准 torch.nn.Linear(对应模块化源文件 modular_aria.py 中的 sequential_experts_gemm 循环逐专家矩阵乘),从而让 int4 量化可以按普通线性层逐层生效。

源码视角:三大子模块如何拼成 Aria

Aria 的整体装配在 modeling_aria.pyAriaModel 中一目了然:

self.vision_tower = AutoModel.from_config(config.vision_config)      # 视觉编码器
self.multi_modal_projector = AriaProjector(config)                   # 多模态投影器
self.language_model = AutoModel.from_config(config.text_config)      # MoE 语言解码器

三个子模块职责清晰:vision_tower 把图片变成视觉 patch 特征;multi_modal_projector 把这些特征「压缩 + 投影」进语言模型的嵌入空间;language_model(AriaText)完成最终的自回归文本生成。

视觉塔:复用 Idefics3 视觉主干

一个值得注意的细节是:Aria 的视觉塔并没有新造架构,而是在 configuration_aria.pyAriaConfig.__post_init__ 里通过 CONFIG_MAPPING["idefics3_vision"] 直接复用 Idefics3 的视觉配置,属于该模型视觉编码器的实际实现。

视觉特征抽取路径见 modeling_aria.pyget_image_features

  1. _create_patch_attention_mask(pixel_mask) 把图像处理器产出的、分辨率为 max_image_size × max_image_sizepixel_mask 按视觉塔的 patch_size 划分网格,每个 patch 只要包含任一真实像素即为有效;
  2. 视觉塔接收 patch_attention_mask 并强制 output_hidden_states=True,返回各层 hidden states;
  3. vision_feature_layer(配置默认 -1,即最后一层)选取视觉特征;
  4. 把选出的特征交给 multi_modal_projector,得到可供文本侧使用的 pooler 输出。

多模态投影器:交叉注意力把「patch 数」映射为「query 数」

AriaProjectormodeling_aria.py)包含三部分:可学习的 query 参数、一层 AriaCrossAttention 交叉注意力,以及一个两层的 AriaProjectorMLPgelu_new 激活 + 无 bias 线性层)。

它的核心巧妙之处在于 patch 数与 query 数的映射表 patch_to_query_dict。由于 Aria 会把大图切分/缩放到不同 crop 数,不同输入会有不同 patch 总数;投影器针对每种 patch 数量预置了不同长度的 query:

  • 默认映射见 configuration_aria.py{1225: 128, 4900: 256},即 1225 个 patch 对应 128 个 query,4900 个 patch 对应 256 个 query;
  • AriaProjectorconfig.max_value_projector_patch_to_query_dict(映射表最大值 256)分配 query 参数,再在 forward 时按 query_num = self.patch_to_query_dict[num_patches] 截取前若干个 query 参与交叉注意力;
  • num_patches 不在映射表中会直接抛出 KeyError,提示当前 patch 数与支持值(见 modeling_aria.py)。

这一机制保证了:无论输入分辨率如何变化,语言模型最终拿到的是固定维度、与 query 数一致的视觉 token 序列,可无缝拼接进文本嵌入。

文本侧:Llama 骨架 + MoE 前馈

AriaTextModel/AriaTextForCausalLM 在源码中大量继承 Llama 的实现:AriaTextConfig(LlamaConfig)、注意力直接复用 Llama 的 RoPE 与注意力,唯一被替换的是每一层 decoder 的 MLP

  • AriaTextDecoderLayerself.mlp 被换成 AriaTextMoELayermodeling_aria.py);
  • MoE 层内含一个无 bias 的 router 线性层(hidden_size → moe_num_experts)、一组路由专家 AriaExperts 和一组共享专家 AriaSharedExpertsMLP
  • 前向时每个 token 由 router 选出 top-k 专家(torch.topk + softmax 加权),专家输出与共享专家输出逐元素相加作为该层 FFN 结果;
  • 路由专家的激活函数是 SwiGLU(SiLU 门控),权重形状为三维 grouped GEMM 或逐专家 Linear,取决于加载的是普通权重还是 sequential_mlp 权重。

与模型「3.5B 文本 token 激活参数」对应,上述 top-2-of-8 路由正是「大而稀疏」的来源:单层有 8 个专家(默认),每个 token 只激活 2 个,再叠加 2 个总被激活的共享专家。

前向融合:placeholder 占位与 masked_scatter

pixel_values 存在时,AriaModel.forwardmodeling_aria.py)完成图文融合:

  1. 先把 input_ids 查表得到文本 inputs_embeds
  2. 视觉特征经投影器得到 image_features,转成与文本嵌入相同的 device/dtype;
  3. get_placeholder_mask 定位 input_ids 中等于 config.image_token_index(默认 9)的占位位置,并校验「图像 token 数 × 隐藏维度 == 图像特征元素总数」,不一致即报错;
  4. inputs_embeds.masked_scatter(special_image_mask, image_features) 把视觉特征逐位置填充进文本嵌入流;
  5. 合并后的序列送入 language_model 做标准的因果建模,输出 AriaModelOutputWithPast(含可选 image_hidden_states)。

这套「占位 + 就地填充」的模式与 LLaVA 家族(LlavaModel/LlavaForConditionalGeneration)一脉相承——事实上 AriaModelAriaForConditionalGeneration 正是以 Llava 的相关类为父类再覆写视觉/投影部分实现的(可对照模块化源 modular_aria.py)。

配置参数详解

Aria 的配置类与实现一致采用「文本 + 视觉 + 总配置」的分层结构,定义在 configuration_aria.py

AriaConfig(总配置)

model_type = "aria",聚合子配置并在初始化时完成归一化:

参数 默认值 说明
text_config None 文本子配置,可为 dict(需含 model_type)或 AriaTextConfig,为空时自动创建
vision_config None 视觉子配置,dict 会被强制写入 model_type="idefics3_vision" 后实例化,为空时用默认 Idefics3 视觉配置
vision_feature_layer -1 视觉塔取哪一层特征,-1 表示最后一层,也支持列表取多层
projector_patch_to_query_dict {1225: 128, 4900: 256} patch 数 → 投影器 query 数的映射;键值统一转 int,并自动计算 max_value_projector_patch_to_query_dict
image_token_index 9 图片占位 token id,模型中用于定位并替换为视觉特征
initializer_range 0.02 参数初始化标准差
tie_word_embeddings False 是否共享输入/输出词嵌入

另外 attribute_map = {"image_token_id": "image_token_index"} 保证了旧字段名 image_token_id 仍可兼容读取。

AriaTextConfig(MoE 语言模型配置)

文本子配置继承自 Llama 系配置(model_type = "aria_text"),MoE 专属参数及典型默认值如下:

参数 默认值 说明
moe_num_experts 8 MoE 层中路由专家总数
moe_topk 2 每个 token 激活的 top 专家数
moe_num_shared_experts 2 共享专家数,处理所有 token、不参与路由
intermediate_size 4096 专家 FFN 的中间维度(共享专家的实际中间维度为 intermediate_size × moe_num_shared_experts,见 modeling_aria.py
hidden_size 4096 隐藏维度
num_hidden_layers 32 解码器层数
num_attention_heads 32 注意力头数
num_key_value_heads None KV 头数,缺省时等于注意力头数(无 GQA)
head_dim None 每头维度,缺省时由 hidden_size / num_attention_heads 自动推导
hidden_act "silu" FFN 激活
rms_norm_eps 1e-6 RMSNorm epsilon
max_position_embeddings 2048 最大位置编码
vocab_size 32000 词表大小
pad/bos/eos_token_id 2 / 1 / 2 特殊 token id
use_cache True 是否启用 KV cache
attention_bias False 注意力是否带 bias
rope_parameters None RoPE 参数(继承 Llama 的 RoPE 体系)

AriaTextConfig 还内置了架构校验:若 hidden_size 不能被 num_attention_heads 整除会直接抛错(见 configuration_aria.py);同时声明了 tensor parallelism / pipeline parallelism 的分片计划,以及 keys_to_ignore_at_inference = ["past_key_values"]

图像预处理:让不同分辨率的图都能进模型

Aria 需要支持任意分辨率图像,因此图像处理器包含「选分辨率 → 缩放 → 切分/对齐 → 生成真实像素 mask」的完整管线。仓库提供了两套等价实现:基于 torchvision 后端的 AriaImageProcessorimage_processing_aria.py)与基于 Pillow 后端的 AriaImageProcessorPilimage_processing_pil_aria.py),二者预处理逻辑一致、仅底层张量形态不同(Tensor vs NumPy)。

两者的模型输入名为 ["pixel_values", "pixel_mask", "num_crops"],类级默认参数如下:

参数 默认值 说明
max_image_size 980 单张 crop 的最大边长,只允许 490 或 980,传其他值抛 ValueError
min_image_size 336 最小边长,任意维度小于它的图会被放大
split_image False 是否把超大图按最接近的 split_resolutions 切成多个 980 方块 crop
split_resolutions 见下 候选切分分辨率 (高, 宽) 列表,格式为 (h, w) 的 490 倍数网格
resample BICUBIC 缩放插值方式
image_mean / image_std [0.5, 0.5, 0.5] 归一化均值/方差
do_convert_rgb / do_rescale / do_normalize True 流程开关

split_resolutions 默认值是在 __init__ 里动态生成的:基础网格为 19 种比例 (1,2),(1,3),…,(2,4),(2,3),(2,2),(2,1),…,(8,1),每个元素乘以 490 得到实际分辨率(如 (1,2)[490, 980]),见 image_processing_aria.py

预处理核心流程image_processing_aria.py)可概括为:

  1. split_image=True,用 get_image_patches 选最接近的分辨率,先保持宽高比 resize、再居中 pad 到目标分辨率、最后按 max_image_size(980)切块;否则整图作为单个 crop;
  2. 对每个 crop,按 max_image_size 等比缩放(短边不足 min_image_size 时补足),再在右下补零 pad 到 max_image_size × max_image_size
  3. 同步生成 pixel_mask:真实像素区域为 True、补零区域为 False,这是后续视觉塔构造 patch 注意力掩码、避免把 padding 当真实内容的关键;
  4. num_crops 记录 batch 内单图最多 crop 数(用于占位 token 计数),所有 crop 堆叠后统一 rescale + normalize,返回 BatchFeature(pixel_values, pixel_mask, num_crops)

配套的 get_number_of_image_patches(height, width, images_kwargs) 则在不真正跑图的情况下预估一张图会被切成几个 patch,供文本侧占位 token 数量计算使用。

AriaProcessor:把「图 + 文」编排成模型输入

AriaProcessorprocessing_aria.py)继承 ProcessorMixin,是图像处理器与 tokenizer 的粘合层,核心职责是决定每个图像占位符该重复多少次

  • size_conversion:图像边长 → 每 crop 的视觉 token 数,默认 {490: 128, 980: 256}(与 projector_patch_to_query_dict 的 query 数一致);
  • replace_image_token:实际展开占位符——tokens_per_image = size_conversion[pixel_values 边长]num_image_tokens = num_crops × tokens_per_image,返回对应长度的 image_token 字符串(processing_aria.py);
  • _get_num_multimodal_tokens:按图像实际尺寸调用图像处理器的 get_number_of_image_patches,估算每张图的 patch 数与 token 数;
  • 初始化时若 tokenizer 无 pad_token,自动以 unk_token 兜底(processing_aria.py)。

它的默认行为在 AriaProcessorKwargs._defaults 中固化:文本侧默认 padding=False、不返回多模态 token type ids;图像侧默认 max_image_size=980, split_image=False;返回张量类型默认 PyTorch(见 processing_aria.py)。同时 unused_input_names = ["num_crops"] 会把计数用的 num_crops 排除在模型前向输入之外。

权重转换与测试佐证

  • 权重转换:非 HF 格式的原始 Aria checkpoint 可通过 convert_aria_weights_to_hf.py 转换为 Transformers 可直接加载的格式;
  • 测试覆盖tests/models/aria 目录下包含三套针对性测试:test_modeling_aria.py 验证模型前向/生成与数值一致性、test_image_processing_aria.py 验证切分与 mask 逻辑、test_processing_aria.py 验证图文输入编排,是复现本文所述行为的现成参考;
  • 代码组织:以上实现文件均由模块化源文件 modular_aria.py 自动生成,文件头部声明了「直接编辑会被 CI 覆盖」——想改实现应改 modular 源文件。

API 类一览

按模型文档公开的接口,Aria 家族可用的核心类及其行为如下:

角色 关键方法
AriaImageProcessor 基于 torchvision 后端的图像处理器 preprocess(产出 pixel_values/pixel_mask/num_crops
AriaImageProcessorPil 基于 Pillow 后端的图像处理器 preprocess
AriaProcessor 图像处理器 + tokenizer 的多模态编排 __call__(含 apply_chat_template 能力)
AriaTextConfig / AriaConfig 文本子配置 / 总配置
AriaTextModel / AriaTextForCausalLM 纯文本 MoE 解码器 因果语言建模
AriaModel / AriaForConditionalGeneration 多模态组合模型(视觉塔 + 投影器 + MoE 解码器) forwardget_image_features

其中 AriaTextForCausalLM 可独立作为纯文本模型使用,而日常图文推理应使用 AriaForConditionalGeneration(通过 AutoModelForCausalLM 自动路由加载)。

小结

Aria 在 Transformers 中的实现呈现出清晰的「复用 + 定制」哲学:视觉编码器复用 Idefics3、文本解码器继承 Llama 并替换为 MoE 前馈、多模态装配沿用 LLaVA 的占位填充范式,真正定制的核心集中在「patch 数 → query 数」的交叉注意力投影器与 grouped-GEMM 专家计算上。配合文档给出的 pipeline、AutoModel 与 torchao int4 三种上手路径,以及 AriaProcessor/AriaImageProcessor 对任意分辨率输入的自适应处理,开发者可以快速在图像问答、多图对比理解等场景中部署这一多模态 MoE 模型,并能从 modeling_aria.pyconfiguration_aria.pyimage_processing_aria.py 中随时回溯每个参数与模块的源码级依据。

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