【免费下载链接】Mini-InternVL2-4B-DA-DriveLM
Mini-InternVL2-4B-DA-DriveLM 是 OpenGVLab 面向自动驾驶领域微调的多模态大模型,它如何让"看得懂"的图像真正"说得出来"?答案藏在一座精巧的桥里:视觉特征先经过Pixel Shuffle(像素重排降采样),再由一个MLP 连接器映射成语言模型能理解的词嵌入空间。本文带你快速读懂这座"视觉→语言"桥梁的完整实现。
1. 先看清整体架构:三块拼图的分工 🧩
模型由三个部分组成,配置文件 config.json 中可以看到全貌:
| 组件 | 模型 | 关键参数 |
|---|---|---|
| 视觉编码器(ViT) | InternVisionModel | image_size: 448、patch_size: 14、hidden_size: 1024 |
| 连接器 | Pixel Shuffle + MLP | downsample_ratio: 0.5、ps_version: "v2" |
| 语言模型(LLM) | Phi3ForCausalLM | hidden_size: 3072、32 层 |
一次推理的数据流是这样的:
图片(448×448) → ViT 输出 32×32 个 patch 特征(1024维) → Pixel Shuffle:2×2 邻域拼接 → 16×16 个特征(4096维) → MLP 连接器:4096 → 3072 维 → 替换文本中的 <IMG_CONTEXT> 占位符 → Phi-3 语言模型生成文字回答关键洞察:ViT 和 LLM 是两个"语言不通"的模型。ViT 吐出的特征向量,LLM 完全看不懂。连接器就是翻译官,而 Pixel Shuffle 在翻译前先做了一次"压缩排版"。
2. 为什么必须先降采样?从 1024 个 token 到 256 个
448×448 的图片按 14×14 切 patch,会得到 32×32 =1024 个patch 特征。如果每个 patch 都变成一个视觉 token 塞给 LLM,序列会非常长,注意力开销平方级上涨,还会挤占用户问题和回答的上下文空间。
Mini-InternVL 的解法是:把空间分辨率减半、通道数加倍。降采样比例由 config.json 中的downsample_ratio: 0.5控制,每张图片最终对应多少个 token,在 modeling_internvl_chat.py 中一眼可见:
self.num_image_token = int((image_size // patch_size) ** 2 * (config.downsample_ratio ** 2)) # (448 // 14)**2 * 0.5**2 = 1024 * 0.25 = 256也就是说,每张 448×448 图片只消耗 256 个 token,且信息并没有被插值丢掉——它被"打包"进了更宽的向量里。
3. Pixel Shuffle 实现原理:零参数、零插值的纯重排 🔁
Pixel Shuffle 的全部逻辑集中在 modeling_internvl_chat.py 的pixel_shuffle方法:
def pixel_shuffle(self, x, scale_factor=0.5): n, w, h, c = x.size() x = x.view(n, w, int(h * scale_factor), int(c / scale_factor)) x = x.permute(0, 2, 1, 3).contiguous() x = x.view(n, int(h * scale_factor), int(w * scale_factor), int(c / (scale_factor * scale_factor))) if self.ps_version == 'v1': warnings.warn("In ps_version 'v1', the height and width have not been swapped back, " 'which results in a transposed image.') else: x = x.permute(0, 2, 1, 3).contiguous() return x拆开看它在做什么(以本模型的 32×32×1024 输入为例):
- reshape:把高度维拆成
16 × 2,同时把 1024 个通道看作512 × 2; - permute:调整维度顺序,让 2×2 邻域内的 4 个 patch 在内存中排到一起;
- view:合并后得到16×16×4096—— 每个新位置的特征,正是原来 2×2 小方块里 4 个 patch 特征按行优先顺序首尾拼接。
它的巧妙之处:
- 纯
view/permute操作:不经过任何矩阵乘法和插值,没有可学习参数,开销几乎为零; - 信息无损:4 个 patch 的 4×1024 = 4096 维信息一个不少地保留在新 token 中,空间细节只是被"压缩打包"了;
- 空间对应关系正确:第 (i, j) 个新 token 精确对应原图 2×2 的方块,后续 LLM 能建立"这个 token 负责图像哪一块"的映射。
ps_version 的小坑:v1 与 v2 的区别 ⚠️
注意方法末尾的分支。ps_version: "v1"时跳过最后一步permute(0, 2, 1, 3),结果就是宽和高没有换回去,输出特征图相当于被转置(横向信息错放到纵向)。本仓库的 config.json 中配置为"ps_version": "v2",即会执行这次交换,保证空间对应关系正确。如果你加载旧版本权重或旧配置,需要留意这个参数。
4. 特征提取的完整流程:extract_feature 逐步拆解
上面两步被组织在 modeling_internvl_chat.py 的extract_feature中,逐行对应:
vit_embeds = self.vision_model(pixel_values=..., ...).last_hidden_state vit_embeds = vit_embeds[:, 1:, :] # ① 去掉 CLS token,只留 32×32=1024 个 patch 特征 h = w = int(vit_embeds.shape[1] ** 0.5) # ② 推出 32×32 网格 vit_embeds = vit_embeds.reshape(vit_embeds.shape[0], h, w, -1) vit_embeds = self.pixel_shuffle(vit_embeds, scale_factor=self.downsample_ratio) # ③ 重排为 16×16×4096 vit_embeds = vit_embeds.reshape(vit_embeds.shape[0], -1, vit_embeds.shape[-1]) # ④ 展平成 256×4096 vit_embeds = self.mlp1(vit_embeds) # ⑤ MLP 连接器 → 256×3072其中select_layer: -1(见 config.json)表示取 ViT 最后一层输出;若设为正整数,则取对应中间层(源码中hidden_states[self.select_layer]的分支),这是 InternVL 系列常用的消融手段。
5. MLP 连接器:把 4096 维"翻译"进 LLM 词汇空间
降采样后的特征还是 4096 维,而 Phi-3 的隐藏维度是 3072。真正的"语言翻译"由mlp1完成,定义见 modeling_internvl_chat.py:
self.mlp1 = nn.Sequential( nn.LayerNorm(vit_hidden_size * int(1 / self.downsample_ratio) ** 2), # 4096 nn.Linear(vit_hidden_size * int(1 / self.downsample_ratio) ** 2, llm_hidden_size), # 4096 → 3072 nn.GELU(), nn.Linear(llm_hidden_size, llm_hidden_size) # 3072 → 3072 )结构是经典的LayerNorm → Linear → GELU → Linear:
- 输入 4096 =
1024 × (1/0.5)²,正好是 Pixel Shuffle 拼接后的维度,两者是"上下游"关系; - 输出 3072,与 config.json 中
llm_config.hidden_size: 3072完全对齐,可以直接和文本词嵌入并列; - 没有加投影层数上的花活,保持轻量,符合 Mini-InternVL "口袋模型"的定位。
6. 最后一公里:视觉嵌入如何"替换"进文本序列
连接器输出的 256 个特征向量,如何进入对话?看 modeling_internvl_chat.py 的generate:
vit_embeds = self.extract_feature(pixel_values) input_embeds = self.language_model.get_input_embeddings()(input_ids) selected = (input_ids == self.img_context_token_id) # 找到 <IMG_CONTEXT> 位置 input_embeds[selected] = vit_embeds.reshape(-1, C) # 原地替换成视觉特征机制很直白:
chat接口把问题中的<image>展开为<img>+ 256 个<IMG_CONTEXT>× 图片块数 +</img>(见 modeling_internvl_chat.py);- 这些特殊 token 来自 added_tokens.json,其中
<IMG_CONTEXT>对应词表 id32013(词表大小 32020); - 编码时,所有
<IMG_CONTEXT>位置的文本词嵌入被视觉特征整体覆盖(* 0.0 + vit_embeds的技巧保证梯度不回流到原嵌入,见 modeling_internvl_chat.py); - 于是 Phi-3 看到的序列是:
... <img> [256 个视觉向量] </img> 用户的问题 ...,随后逐词生成回答。
顺带一提:得益于动态分辨率(dynamic_image_size: true、max_dynamic_patch: 12),长宽比不规则的图会被切成多块 448×448 的 tile(最多 12 块 + 1 张缩略图),每块都贡献 256 个 token,这也是该模型能看清道路细节、小字标识的关键。
7. 快速体验:跑通一次图文对话 🚀
使用transformers >= 4.37.2,按 README.md 的 Quick Start 即可:
from transformers import AutoModel, AutoTokenizer import torch path = 'OpenGVLab/Mini-InternVL2-4B-DA-DriveLM' model = AutoModel.from_pretrained(path, torch_dtype=torch.bfloat16, low_cpu_mem_usage=True, trust_remote_code=True).eval().cuda() tokenizer = AutoTokenizer.from_pretrained(path, trust_remote_code=True, use_fast=False) question = '<image>\nPlease describe the image shortly.' response = model.chat(tokenizer, pixel_values, question, dict(max_new_tokens=1024, do_sample=True))图片预处理(load_image/dynamic_preprocess)的完整代码见 README.md。
8. 核心文件与参数速查表
| 内容 | 位置 |
|---|---|
| Pixel Shuffle 实现 | modeling_internvl_chat.py |
| 特征提取流程 extract_feature | modeling_internvl_chat.py |
| MLP 连接器 mlp1 定义 | modeling_internvl_chat.py |
| 占位符替换逻辑 | modeling_internvl_chat.py |
连接器超参(downsample_ratio、ps_version等) | config.json |
| 连接器超参定义 | configuration_internvl_chat.py |
| ViT 结构(24 层、hidden 1024) | modeling_intern_vit.py |
<IMG_CONTEXT>等新增 token | added_tokens.json |
| 推理示例代码 | README.md |
9. 小结
回到开头的问题——视觉特征如何变成语言?
- Pixel Shuffle用一次无参数的张量重排,把 1024 个 1024 维 patch 特征打包成 256 个 4096 维特征,空间减半、通道翻倍,信息无损;
- MLP 连接器(LayerNorm + 两层 Linear + GELU)再把 4096 维投影进 Phi-3 的 3072 维嵌入空间;
- 最后通过
<IMG_CONTEXT>占位符替换,视觉向量与文本词嵌入并肩进入 LLM,模型自然输出"看得懂"的文字。
这套"重排 + 轻 MLP"的连接器设计,正是 Mini-InternVL2-4B-DA-DriveLM 以不到 5% 的参数拿到 90% 性能的秘密之一,也是理解 InternVL 系列多模态对齐机制的最佳切入点。
【免费下载链接】Mini-InternVL2-4B-DA-DriveLM
相关推荐
Mini-InternVL2-2B-DA-DriveLM如何"看懂"图片?mlp1跨模态连接器原理全解读
Mini InternVL2 2B DA DriveLM如何"看懂"图片?mlp1跨模态连接器原理全解读 Mini InternVL2 2B DA DriveL
Mini-InternVL2-1B-DA-BDD架构深度拆解:PixelShuffle+MLP视觉语言连接器源码逐行讲解
Mini InternVL2 1B DA BDD架构深度拆解:PixelShuffle+MLP视觉语言连接器源码逐行讲解 Mini InternVL2 1B D
深入Docopilot-2B核心原理:Pixel Shuffle下采样与MLP投影的视觉语言对齐实现
深入Docopilot 2B核心原理:Pixel Shuffle下采样与MLP投影的视觉语言对齐实现 Docopilot 2B 是一个面向 文档级理解 的多模态
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考