Transformers 中的 MobileViT:面向移动设备的轻量级视觉 Transformer 全面指南
导读
MobileViT 是一个专为移动设备设计的轻量级视觉 Transformer(Vision Transformer, ViT),它把 CNN 的高效率与归纳偏置和 Transformer 的全局上下文建模能力融合在一起,将 Transformer 当作"卷积"来使用,从而在不付出标准 ViT 那样高昂计算代价的前提下完成全局信息建模。本指南以 Transformers 官方模型文档(mobilevit.md)为主体,结合本仓库中 MobileViT 的配置、模型与图像预处理源码(src/transformers/models/mobilevit),系统讲解 MobileViT 的架构原理、配置参数、图像分类与语义分割的实战用法。读完本文,你将掌握如何用 pipeline 与 AutoModel 一行加载 MobileViT 完成推理、如何配置并训练分类/分割模型、以及理解其背后"展开为 patch 做全局注意力再折叠回特征图"的核心设计。
MobileViT 是什么:把 Transformer 当作卷积的轻量架构
MobileViT(论文发布于 2021-10-05,2022-06-29 由社区贡献者 matthijs 合入 Transformers)主打一个核心思想:把 Transformer 当作卷积来使用。卷积运算天然只关注局部感受野,而标准 Transformer 的全局自注意力代价高昂,难以在移动端运行;MobileViT 则在 CNN 骨干之上按 patch 组织信息,用全局自注意力在 patch 之间交换信息,从而在保持移动端友好计算量的同时获得全局上下文建模能力。
官方原始 MobileViT 权重均可从 Apple 组织下(搜索 mobilevit)获取,例如 apple/mobilevit-small、apple/mobilevit-xx-small 等检查点。模型完全面向图像任务设计,其骨干编码器基于 MobileNetV2 的逆残差结构(见 configuration_mobilevit.py 与 modeling_mobilevit.py)。
移动端友好与全局建模如何兼得:MobileViTLayer 源码解析
从 modeling_mobilevit.py 的实现可以看到,MobileViT 的基本单元 MobileViTLayer 由下列子模块顺序堆叠而成:
- 下采样:当
stride == 2时,先经过一个 MobileNetV2 风格的逆残差块(MobileViTInvertedResidual)降低空间分辨率; - 局部表征(local representation):先用
conv_kxk(默认conv_kernel_size=3卷积)提取局部特征,再用conv_1x1将通道投影到 Transformer 的hidden_size; - Unfolding(展开为 patch):把
(batch, channels, height, width)的特征图按patch_size × patch_size切成序列,得到(batch * patch_area, num_patches, channels)的张量; - 全局表征(global representation):patch 序列送入多个
MobileViTTransformerLayer(带 Pre-LN 的注意力 + FFN),再做一次 LayerNorm; - Folding(折叠回特征图):将全局建模后的 patch 重新拼回
(batch, channels, height, width)特征图; - 融合:
conv_projection投影后,与步骤 1 保存的残差residual在通道维度拼接,由fusion卷积融合输出。
关键点在于 Unfolding/Folding 的可逆几何变换(对应源码中的 unfolding 与 folding 方法,modeling_mobilevit.py):若空间尺寸不能被 patch_size 整除,源码会先用 nn.functional.interpolate 做双线性上采样到可整除尺寸,全局注意力计算完成后再插值回原尺寸。这种"局部卷积 + patch 级全局注意力 + 残差融合"的组合,让 MobileViT 在不大幅增加参数量和延迟的前提下获得了 ViT 式的全局感受野。
MobileViTConfig:完整的配置参数说明
MobileViTConfig 继承自 PreTrainedConfig,通过 configuration_mobilevit.py 定义。下表汇总了其全部公开参数及默认值:
| 参数 | 默认值 | 说明 |
|---|---|---|
num_channels |
3 |
输入图像的通道数(RGB/BGR 均为 3) |
image_size |
256 |
输入图像尺寸,可为整数或 (height, width) |
patch_size |
2 |
Unfolding/Folding 使用的 patch 边长,默认 2×2 |
hidden_sizes |
(144, 192, 240) |
三组 MobileViT(Transformer)模块的隐藏层维度 |
neck_hidden_sizes |
(16, 32, 64, 96, 128, 160, 640) |
骨干各阶段特征图通道数,头部会据此取值 |
num_attention_heads |
4 |
自注意力头数 |
mlp_ratio |
2.0 |
Transformer 层中 FFN 中间维度 = hidden_size × mlp_ratio |
expand_ratio |
4.0 |
逆残差块的通道扩展系数 |
hidden_act |
"silu" |
隐藏层激活函数(MobileViT 原始实现使用 Swish/SiLU) |
conv_kernel_size |
3 |
局部卷积核尺寸 |
output_stride |
32 |
编码器输出相对输入的空间缩放;8/16 用于分割任务(详见下文 dilation 说明) |
hidden_dropout_prob |
0.1 |
Transformer 输出的 dropout 概率 |
attention_probs_dropout_prob |
0.0 |
注意力权重 dropout 概率 |
classifier_dropout_prob |
0.1 |
分类/分割头前的 dropout 概率 |
initializer_range |
0.02 |
权重初始化标准差范围 |
layer_norm_eps |
1e-5 |
LayerNorm 的 eps |
qkv_bias |
True |
Q/K/V 投影是否带 bias |
aspp_out_channels |
256 |
语义分割 ASPP 层输出通道数 |
atrous_rates |
(6, 12, 18) |
ASPP 空洞卷积的空洞率(dilation factors) |
aspp_dropout_prob |
0.1 |
ASPP 层 dropout 比率 |
semantic_loss_ignore_index |
255 |
分割损失中被忽略的像素标签索引 |
配置与模型骨架如何对应(编码器结构)
在 MobileViTEncoder(modeling_mobilevit.py)中,编码器由 5 个阶段组成,neck_hidden_sizes 依次指定各阶段通道数:
- 第 1、2 阶段为纯 MobileNet 卷积层(
MobileViTMobileNetLayer),stage 数分别为 1 与 3; - 第 3~5 阶段是 MobileViT 层(
MobileViTLayer),分别使用hidden_sizes[0/1/2](144/192/240)作为 Transformer 隐层维度,stage 数分别为 2、4、3; - 编码器入口
conv_stem是一个stride=2的 3×3 卷积,把 3 通道输入提升到neck_hidden_sizes[0]=16通道(见MobileViTModel.__init__)。
有意思的是 output_stride 参数:DeepLab 系分割骨干常通过修改分类骨干的 stride 来控制输出分辨率。源码中当 output_stride == 8 时会同时 dilate 第 4、5 层,当 output_stride == 16 时只 dilate 第 5 层(dilate_layer_4 / dilate_layer_5),并通过把 dilation 传入后续 MobileViT 层来保持更大的特征图分辨率。这就是从结构上印证 MobileViT 能同时服务分类与语义分割两种任务的原因。
快速上手:图像分类
MobileViT 的分类模型在 ImageNet-1k 上预训练,可直接用于图像分类推理。官方文档同时给出了 pipeline 与 AutoModel 两种方式,下面完整复现并逐行说明。
方式一:用 pipeline 一行完成分类
Pipeline 方式最简洁,只需指定 task="image-classification" 与模型名即可:
from transformers import pipeline
classifier = pipeline(
task="image-classification",
model="apple/mobilevit-small",
device=0,
)
preds = classifier("https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/pipeline-cat-chonk.jpeg")
print(f"Prediction: {preds}\n")
device=0 表示将模型放到第一块 GPU 上(CPU 环境可省略)。pipeline 会在内部自动加载配套的 MobileViTImageProcessor 完成图像预处理。
方式二:用 AutoModel + AutoImageProcessor 精确控制
若需要更精细的控制,推荐使用 AutoImageProcessor 与 MobileViTForImageClassification 组合:
import requests
import torch
from PIL import Image
from transformers import AutoImageProcessor, MobileViTForImageClassification
image_processor = AutoImageProcessor.from_pretrained(
"apple/mobilevit-small",
use_fast=True,
)
model = MobileViTForImageClassification.from_pretrained("apple/mobilevit-small", device_map="auto")
url = "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/pipeline-cat-chonk.jpeg"
image = Image.open(requests.get(url, stream=True).raw)
inputs = image_processor(image, return_tensors="pt").to(model.device)
with torch.no_grad():
logits = model(**inputs).logits
predicted_class_id = logits.argmax(dim=-1).item()
class_labels = model.config.id2label
predicted_class_label = class_labels[predicted_class_id]
print(f"The predicted class label is:{predicted_class_label}")
分类模型内部的 head 结构
从源码(modeling_mobilevit.py)看,MobileViTForImageClassification 由三部分组成:
MobileViTModel骨干输出特征图后,用conv_1x1_exp将通道从neck_hidden_sizes[5]=160扩展为neck_hidden_sizes[6]=640,随后在空间维度上做全局平均池化得到pooler_output(torch.mean(..., dim=[-2, -1]));dropout(classifier_dropout_prob,默认 0.1);classifier:输入维度为neck_hidden_sizes[-1](640)的线性层,输出num_labels个类别;当num_labels == 0时退化为nn.Identity()。
前向时,若传入 labels 且 num_labels > 1 会计算交叉熵损失,num_labels == 1 则计算均方误差(回归)损失,训练时可直接返回 loss 用于反向传播。
语义分割:DeepLabV3 头 + MobileViT 骨干
MobileViT 的分割模型使用 DeepLabV3 head,并在 PASCAL VOC 上预训练。与分类模型不同,分割需要保留空间信息,因此 MobileViTForSemanticSegmentation 内部以 MobileViTModel(config, expand_output=False) 构造骨干(modeling_mobilevit.py),即不做最后的 1×1 通道扩展。
分割头的三个关键组件(源码均有注释说明,见 modeling_mobilevit.py):
MobileViTASPP:Atrous Spatial Pyramid Pooling,由 1×1 卷积、三个空洞率分别为atrous_rates=(6, 12, 18)的 3×3 空洞卷积、以及一个全局平均池化分支组成,五路输出拼接后经 1×1 卷积投影到aspp_out_channels=256,并施加aspp_dropout_prob=0.1的 dropout。源码在MobileViTASPP.__init__中会校验len(config.atrous_rates) == 3,因此该参数必须为三元组;MobileViTDeepLabV3:在 ASPP 之上再接一个Dropout2d(classifier_dropout_prob)与 1×1 卷积分类器,把通道映射到num_labels;- 前向需要编码器中间层特征,故
forward中强制output_hidden_states=True,把第 4 阶段(分辨率最大的最后一层编码特征)喂给分割头(hidden_states[-1])。
训练时若传入 labels,模型先把 logits 双线性上采样到与标签相同尺寸,再用 CrossEntropyLoss(ignore_index=semantic_loss_ignore_index)(默认忽略索引 255)计算损失。
分割模型的推理示例
模型自带的 docstring 示例可直接参考(完整代码见 modeling_mobilevit.py),示意如下:
import httpx
from io import BytesIO
import torch
from PIL import Image
from transformers import AutoImageProcessor, MobileViTForSemanticSegmentation
url = "http://images.cocodataset.org/val2017/000000039769.jpg"
with httpx.stream("GET", url) as response:
image = Image.open(BytesIO(response.read()))
image_processor = AutoImageProcessor.from_pretrained("apple/deeplabv3-mobilevit-small")
model = MobileViTForSemanticSegmentation.from_pretrained("apple/deeplabv3-mobilevit-small")
inputs = image_processor(images=image, return_tensors="pt")
with torch.no_grad():
outputs = model(**inputs)
# logits are of shape (batch_size, num_labels, height, width)
logits = outputs.logits
图像预处理与后处理:MobileViTImageProcessor 完全解读
文档的 Notes 明确要求:使用 MobileViTImageProcessor 预处理图像;如果自行实现预处理,务必保证图像是 BGR 格式(而非 RGB),因为预训练权重是按 BGR 通道顺序训练的。 这是 MobileViT 与大多数默认按 RGB 训练的视觉模型最大的区别之一。
本仓库中 MobileViT 同时提供了两个图像处理器(它们在功能上等价,仅后端不同):
MobileViTImageProcessor:基于 torchvision 后端的默认实现,见 image_processing_mobilevit.py;MobileViTImageProcessorPil:基于 PIL/numpy 后端的实现,见 image_processing_pil_mobilevit.py。
二者的类级默认配置完全相同:
resample = PILImageResampling.BICUBIC # 缩放插值使用 BICUBIC
image_mean = IMAGENET_STANDARD_MEAN # ImageNet 均值
image_std = IMAGENET_STANDARD_STD # ImageNet 标准差
size = {"shortest_edge": 224} # 先缩放到最短边 224
crop_size = {"height": 256, "width": 256} # 再中心裁剪为 256×256
do_resize = True
do_center_crop = True
do_rescale = True
do_flip_channel_order = True # 默认把 RGB 翻转为 BGR
do_reduce_labels = False
preprocess:分类与分割的差异化处理
preprocess 除接收 images 外还可接收 segmentation_maps。两者的处理链在 _preprocess 中体现(image_processing_mobilevit.py):
- 图像路径:
resize(BICUBIC) → center_crop(256×256) → rescale(1/255) → flip_channel_order(RGB→BGR); - 分割标签图路径则单独走一套配置:
do_rescale=False、do_flip_channel_order=False,并且插值方式从 BICUBIC 换成 NEAREST(源码注释明确说明"Nearest interpolation is used for segmentation maps instead of BICUBIC"),避免标签类别被平滑污染。预处理后标签图被squeeze并转为int64存入labels。
flip_channel_order 的实现(image_processing_mobilevit.py)支持单图 (C,H,W) 与 batch (B,C,H,W) 两种情形,把前三个通道按 [2,1,0] 逆序重排。do_reduce_labels=True 时会把标签图逐像素减 1、背景 0 替换为 255(用于 ADE20k 这类背景不出现在类别集合中的数据集)。
结果后处理:post_process_semantic_segmentation
post_process_semantic_segmentation(image_processing_mobilevit.py)把分割模型的原始 logits 转成分割图:
- 不传
target_sizes:直接对每张图的 logits 沿类别维取argmax,返回形状(height, width)的类别索引图; - 传入
target_sizes(每张图期望的(height, width)):先用双线性插值把 logits 上采样到目标尺寸再argmax; return_segmentation_scores=True时返回带segmentation(类别索引)与segmentation_scores(形状(num_classes, height, width)的概率图)的结构化输出。
同时该处理器还要求 batch 尺寸与 target_sizes 数量一致,否则会抛出 ValueError 提示用户检查输入。
使用注意事项(官方 Notes 完整汇总)
结合 mobilevit.md 的 Notes 与原论文设置,使用 MobileViT 时有以下几点需要牢记:
- 不处理序列数据:MobileViT 纯粹为图像任务设计,不适用于 NLP 等序列任务;
- 特征图直接参与建模:与把图像切块展平为标准 token embedding 的经典 ViT 不同,MobileViT 直接基于卷积特征图运作,只是按 patch 组织后做 Transformer 前向;
- 预处理必须用专属处理器:请通过
MobileViTImageProcessor/AutoImageProcessor.from_pretrained(..., use_fast=True)进行预处理; - 自定义预处理必须是 BGR:若你绕开处理器自行实现,务必在送入模型前把通道从 RGB 翻转为 BGR,否则预训练权重的效果会明显退化;
- 分类权重来源:分类模型在 ImageNet-1k 上预训练;
- 分割权重来源:分割模型使用 DeepLabV3 head,在 PASCAL VOC 上预训练。
从零构建自定义 MobileViT 与相关源码索引
若想基于本仓库做二次开发(如自定义 backbone 通道数、patch 大小或注意力头数),可参考如下代码模式(对应 configuration_mobilevit.py 的 docstring):
from transformers import MobileViTConfig, MobileViTModel
# Initializing a mobilevit-small style configuration
configuration = MobileViTConfig()
# Initializing a model from the mobilevit-small style configuration
model = MobileViTModel(configuration)
# Accessing the model configuration
configuration = model.config
模型测试样例(test_modeling_mobilevit.py)大量使用 apple/mobilevit-small、apple/mobilevit-xx-small 及 apple/deeplabv3-mobilevit-small 等真实检查点进行集成验证,可作为端到端行为的参考。仓库中值得继续深读的相关文件索引如下:
- 模型配置:configuration_mobilevit.py
- 模型实现(含卷积层、逆残差、MobileViT 层、编码器、分类头、ASPP/DeepLabV3 分割头):modeling_mobilevit.py
- 图像处理器(torchvision 后端):image_processing_mobilevit.py
- 图像处理器(PIL 后端):image_processing_pil_mobilevit.py
- 权重转换脚本(将 Apple ml-cvnets 权重转换为 Transformers 格式):convert_mlcvnets_to_pytorch.py
- 模型集成测试:tests/models/mobilevit/test_modeling_mobilevit.py
- 图像处理器测试:tests/models/mobilevit/test_image_processing_mobilevit.py
总的来说,MobileViT 通过"局部卷积 + patch 展开的全局 Transformer + 残差融合"三步式 MobileViT 层,在移动端延迟预算内获得了全局上下文建模能力;在 Transformers 中,它提供了分类与语义分割两种开箱即用的头部,加上默认帮用户完成 RGB→BGR 翻转的专属图像处理器,是轻量级边缘视觉任务中一个非常实用的选择。
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