Browse this library

Chinese source article

02 · DeepSeek-VL 技术解析: 混合视觉编码器与先保语言的多模态预训练

DeepSeek-VL 用 SigLIP $384$ 加 SAM-B $1024$ 的混合编码器把一张图压成 $576$ 个 token, 再让纯文本在联合预训练里占 $70\%$ 来保住语言能力; 1.3B 版只用 SigLIP, 官方代码的 SAM 支路还多一个论文没写的跨层 $\alpha$ 项.

View original Markdown ↗

DeepSeek-VL 出自 DeepSeek-AI 的技术报告「DeepSeek-VL: Towards Real-World Vision-Language Understanding」(arXiv 2403.05525, v2 发布于 2024-03-11, 正文加附录共 33 页), 开源了 1.3B 和 7B 两档模型, 每档有 base 与 chat 两个版本, 代码与权重在 deepseek-ai/DeepSeek-VL. 逐段中英对照见 DeepSeek-VL 对照译稿. 文中代码行号均指提交 681bffb.

报告把工作拆成数据, 结构, 训练策略三块. 数据侧整理了一份来自真实使用场景的 SFT 分类体系; 结构侧用两个视觉编码器在 576576 个 token 内处理 1024×10241024\times1024 的图; 训练侧发现直接拿多模态数据续训 LLM 会拖垮语言能力, 于是让纯文本在阶段 2 占七成, 再配上模态预热和模态分组两个做法. 后来的 Janus 系列沿用了 1.3B 版的视觉理解通路, VL2 则在编码器和语言模型两侧都换了方案.

1. 混合视觉编码器: 两条支路, 576 个 token

DeepSeek-VL 7B 的视觉侧由 SigLIP-L 和 SAM-B 两个预训练编码器组成. SigLIP-L 吃 384×384384\times384 的图, 负责语义; SAM-B 吃 1024×10241024\times1024 的图, 负责细节. 两条支路的输出在空间上都被压到 24×2424\times24, 每个空间位置拼成一个视觉 token, 所以高分辨率支路再高, 进入语言模型的始终是 576576 个 token.

这套结构只属于 7B. 1.3B 发布配置里的 vision_config 是单个 CLIPVisionTower, 加载 siglip_large_patch16_384, adaptor 是普通的两层 mlp_gelu, 没有任何 SAM 组件; Table 4 和 Table 7 的表头也写着 1B 用 SigLIP. 摘要和 3.1 节把混合编码器当作 DeepSeek-VL 的通用结构来写, 读者容易以为两档模型的视觉侧相同.

1.1. 单个 SigLIP 的两处短板

CLIP 系编码器 (SigLIP 也在其中) 用图文对比目标训练, 表征偏向能被一句描述概括的语义. Tong et al. (2024) 把视觉上明显不同却被编码成相近向量的图片对称作 CLIP-blind pairs, 这类信息在编码器出口就已经丢了, 下游 LLM 无从补回. 第二处是分辨率. SigLIP-L 的 patch 边长是 1616, 在 384384 输入下得到 24×24=57624\times24=576 个 patch; 若把输入直接提到 10241024, patch 数变成 64×64=409664\times64=4096, 一张图就占满 DeepSeek LLM 的 40964096 上下文, 何况 SigLIP 的位置编码只在 384384 上预训练过.

报告的应对是补一个纯视觉自监督编码器. SAM-B 的图像编码器源自 ViTDet, 在分割任务上预训练, 原生输入就是 1024×10241024\times1024, 并用窗口注意力控制计算量. 4.4 节在阶段 2 只训 8000 步的设置下比较了四种编码器组合的训练损失, 结果见图 1.

Image block

图 1: 四种视觉编码器组合在阶段 2 前 8000 步的训练损失曲线, 依次为 CLIP, SigLIP, SigLIP+DINO, SigLIP+SAM (原文 Figure 10).

图 1 解析. 四条曲线在前 500 步几乎重合, 从约 2.72.7 降到 2.22.2; 之后 SigLIP+SAM 一直在最下方, 8000 步时约 1.821.82, 其余三条挤在 1.921.92 到 2.02.0 之间. 正文说「加入纯视觉自监督编码器能明显改善训练损失」, 但 SigLIP+DINO 和单 SigLIP 的曲线基本叠在一起, 拉开差距的只有 SigLIP+SAM. 也就是说图里的收益来自 SAM 支路带来的 10241024 分辨率, 并不能说明「自监督目标」本身的作用.

这张图还有两处需要读者自己补的口径. 第一, 纵轴是语言模型在混合数据上的训练损失, 其中七成是纯文本 token, 视觉编码器的差异只作用在三成多模态样本上, 曲线差 0.10.1 对应多模态样本上的差距会更大. 第二, 文中没有给出这四个设置在下游基准上的分数, 训练损失低不等于评测高, 4.4 节没有补这一组下游对比.

数据流与形状: 图像进入模型前先经过 image_processing_vlm.py: 长边缩放到 10241024, 再用 ImageNet 均值色把短边补齐成正方形 (expand2square). HybridVisionTower 对这张 10241024 方图执行 Resize(384), 得到低分辨率支路的输入, 两条支路的张量形状如下表.

步骤 低分辨率支路 SigLIP-L 高分辨率支路 SAM-B
输入 384×384384\times384 1024×10241024\times1024
patch 化 边长 1616, 得 24×2424\times24 个, 宽 10241024 边长 1616, 得 64×6464\times64 个, 宽 768768
主干 24 层 ViT, 全局注意力 12 层 ViT, 窗口 1414, 第 3, 6, 9, 12 层全局注意力
出口变换 无 neck 降到 256256 通道, 插值到 96×9696\times96, 两次 stride 22 卷积升到 512512 再到 10241024
输出 576×1024576\times1024 24×24×102424\times24\times1024, 展平为 576×1024576\times1024

两路输出在同一个空间网格上逐位置对齐, 拼成 576576 个 token. 按 3.1 节的写法, 拼接后每个 token 20482048 维, 经 GeLU 和一层嵌入映射送入 LLM. 代码的顺序略有不同, 放到 2.1 节再看. 这里先算一笔压缩比: SAM-B 主干的 40964096 个 patch 经过「插值到 9696, 再两次减半」变成 576576 个位置, 每个位置对应原图约 42.7×42.742.7\times42.7 像素 (1024/241024/24), 比 SAM 自己的 1616 像素 patch 粗了约 2.72.7 倍. 插值这一步先把 6464 放大到 9696, 只是为了让两次 stride 22 恰好落到 2424, 与 SigLIP 的网格对齐.

补边带来一个文中没有讨论的代价. 宽高比为 2:12:1 的文档截图缩放后实际内容只占 1024×5121024\times512, 另一半是均值色, 对应约 288288 个 token 没有信息量; 长条网页截图的浪费更多. 固定 576576 个 token 的设计对方图友好, 对文档和网页这两类报告重点宣传的真实场景反而吃亏, VL2 改成按宽高比动态切图, 处理的就是这个问题.

代码里的跨层 α\alpha 项: 论文对 SAM-B 支路的描述只有「64×64×25664\times64\times256 特征图, 插值, 两次卷积」一句, 官方代码多了一条旁路. 见 deepseek_vl/models/sam.py 第 161 到 196 行: 构造函数里直接设 self.sam_hd = True, 新建一个初值为零的标量参数 hd_alpha_downsamples, 并把 neck 整个深拷贝一份为 neck_hd; 前向时把所有全局注意力块 (window_size == 0) 的输出收进 global_features, 取其中第一个, 即第 3 层 (下标 2) 的输出, 走 neck_hd, 插值到 96×9696\times96, 再经过与主路共用的 self.downsamples, 乘以 α\alpha 后加到主路输出上. 写成公式:

F=D(Interp96(N(h12)))+α D(Interp96(Nhd(h3)))F = D\big(\mathrm{Interp}_{96}(N(h_{12}))\big) + \alpha\, D\big(\mathrm{Interp}_{96}(N_{\mathrm{hd}}(h_{3}))\big)

其中 hlh_{l} 是 SAM-B 第 ll 层的输出, NN 与 NhdN_{\mathrm{hd}} 是两份结构相同的 neck, DD 是共用的两层下采样卷积, α\alpha 初值为 00. 初值为零保证加载原始 SAM 权重时这条旁路不改变输出, 训练中再由梯度决定浅层特征掺多少进来. 第 3 层只经过两层窗口注意力和一层全局注意力, 保留的是更接近边缘, 笔画的低层信息, 这与报告「SAM 负责细节」的叙述一致, 但论文正文, 消融和附录都没有提到它.

这一项是否真的参与了训练, 要看哪些参数可训. clip_encoder.py 里 freeze_high=False 时只放开名字含 downsamples 或 neck 的参数, hd_alpha_downsamples 与 neck_hd 恰好都命中, 所以只要高分辨率支路的出口层可训, α\alpha 就会被更新. 论文 3.1 节把插值和两次卷积算作 VL adaptor 的一部分, 阶段 1 到 3 adaptor 一直在训练, 两者对得上. 发布权重里 α\alpha 的取值文中没有给出, 要读权重文件才能确认. Hugging Face transformers 的 deepseek_vl_hybrid 移植也保留了这一项 (high_res_vision_alpha, 取 global_attn_indexes[0] 对应层的隐状态), 说明它是加载官方权重必需的结构. GitHub issue #19 中有用户指出这条旁路论文里没有消融, 官方没有回应这一追问. 同一文件夹的 clip_encoder.py 第 143, 144 行还定义了 high_layer_norm 与 low_layer_norm, 前向里没有用到, 属于残留代码.

Adaptor 与语言底座: 视觉 token 进入 LLM 之前还要经过 adaptor. DeepSeek-VL 的 adaptor 很小, 7B 版约 2.1×1072.1\times10^{7} 个参数, 不到语言模型的千分之三. 阶段 1 只训这一层, 能学到的东西有限, 报告把重心放到解冻 LLM 的阶段 2, 原因就在这个比例上.

语言底座是 DeepSeek LLM 的中间 checkpoint, 结构沿用 LLaMA 式的 Pre-Norm, RMSNorm, SwiGLU 与 RoPE, 分词器也相同. 选中间 checkpoint 而不是训完的模型, 是为了让多模态联合预训练接在语言预训练的学习率曲线上继续走, 2.3 节会从超参数上核对这一点.

混合 MLP: 先分后合: projector.py 里 7B 用的是 low_high_hybrid_split_mlp_gelu. 设 SAM 支路某位置的特征为 xh∈R1024x_{h}\in\mathbb{R}^{1024}, SigLIP 支路同一位置为 xl∈R1024x_{l}\in\mathbb{R}^{1024}, LLM 隐层宽度为 n=4096n=4096, 它的计算是

z=W2 GELU([ Whxh ; Wlxl ]),Wh,Wl∈R(n/2)×1024, W2∈Rn×nz = W_{2}\,\mathrm{GELU}\big([\,W_{h}x_{h}\,;\,W_{l}x_{l}\,]\big),\quad W_{h},W_{l}\in\mathbb{R}^{(n/2)\times1024},\ W_{2}\in\mathbb{R}^{n\times n}

第一层两路各有一套权重, 把各自的特征投到 20482048 维, 拼成 40964096 维后过 GELU, 第二层共享. 参数量为 2×1024×2048+40962≈4.2×106+1.68×107≈2.1×1072\times1024\times2048+4096^{2}\approx4.2\times10^{6}+1.68\times10^{7}\approx2.1\times10^{7} (偏置忽略). SAM 支路出口的两次下采样卷积另有 3×3×256×512+3×3×512×1024≈5.9×1063\times3\times256\times512+3\times3\times512\times1024\approx5.9\times10^{6} 个参数, 报告把它们也算作 adaptor. 1.3B 用的是普通两层 mlp_gelu, 形状为 1024→2048→20481024\to2048\to2048, 约 6.3×1066.3\times10^{6} 个参数.

3.1 节说两路特征拼接后得到「576 个 2048 维 token」, 再经 GeLU 和嵌入层进入 LLM, 这是两路原始 10241024 维特征直接拼接的维度, 省掉了各自的第一层投影. 同一节 adaptor 段写的「先用两个单层 MLP 分别处理高低分辨率特征, 拼接后再过一层 MLP」与代码一致, 拼接处实际是 40964096 维. 两段正文互相对不上, 读形状时以 adaptor 段和代码为准.

1.2. Table 10 的 adaptor 消融

4.4 节 Table 10 比较了两类组合方式. 序列拼接一类为了不让序列翻倍, 先把每路特征沿宽或高方向两两合并 (Token Pooling W/H), 再沿序列方向接起来, 总长仍是 576576; 嵌入拼接一类保持逐位置对齐, 比较三种 MLP: 两路共享 (Shared), 两路完全独立 (Separate), 以及上面的先分后合 (Hybrid). 平均分依次是 Pooling W 55.555.5, Pooling H 54.254.2, Hybrid 55.955.9, Shared 55.255.2, Separate 54.554.5.

表里的平均分有个隐藏口径: OCRBench 一列是 10001000 分制的原始分 (291291 到 318318), 直接平均会把均值拉到 9090 以上. 按 Hybrid 一行核对, (61.7+60.1+62.9+87.8+56.6+31.3+30.9)/7≈55.9(61.7+60.1+62.9+87.8+56.6+31.3+30.9)/7\approx55.9, 可见平均时把 OCRBench 除以了 1010, 表注没有写. 再看差距, Hybrid 只比第二名高 0.40.4, 而 Shared 在 MMB (62.062.0) 和 OCRB (318318) 两列都比 Hybrid 好, Separate 在 SEED 上最高; 这些都是单次运行, 文中没有给出方差. 还有一个缺口: Tong et al. 推荐的是不做池化, 让序列长度翻倍到 11521152 的拼接, Table 10 只测了池化后的版本, 没有测这个上限. Table 10 用的是哪个规模的模型文中没有给出; 它的 MMB (61.761.7) 低于 Table 6 的 1.3B (64.664.6), 又必须带 SAM 支路, 只能是一个没有发布的「1.3B 加混合编码器」实验变体.

语言底座与两档规模: 7B 的语言模型是 30 层, 隐层 40964096, FFN 中间维度 1100811008, 1.3B 是 24 层, 隐层 20482048, 16 个头, FFN 中间维度 56325632, 两者词表都是 102400102400. SwiGLU 的中间维度按 8/38/3 倍隐层取整: 8/3×4096≈109238/3\times4096\approx10923, 上取到 256256 的倍数得 1100811008; 8/3×2048≈54618/3\times2048\approx5461, 上取得 56325632. 对 DeepSeek LLM 结构的展开见 DeepSeek LLM 技术解析.

报告说 1B 底座训练了约 5000 亿文本 token, 7B 底座训练了约 2 万亿, 同时又说选的是中间 checkpoint, 两句放在一起有歧义: 2 万亿是 DeepSeek LLM 7B 完整训练的 token 数, 中间 checkpoint 应当少于这个数. 文中没有给出中间 checkpoint 的位置. 从超参数可以推一个范围: DeepSeek LLM 7B 的 batch 是 23042304, 序列长 40964096, 峰值学习率 4.2×10−44.2\times10^{-4}, 用多段阶梯调度, 训到 80% token 时降到峰值的 31.6%, 90% 时降到 10%; VL-7B 阶段 2 的 batch 同为 23042304, 学习率 4.2×10−54.2\times10^{-5} 恰好是峰值的 10%, 调度也是 Step. 2 万亿 token 按每步 2304×4096≈9.4×1062304\times4096\approx9.4\times10^{6} token 约为 2.1×1052.1\times10^{5} 步, 最终 10% 约 2.1×1042.1\times10^{4} 步, 而阶段 2 走了 4200042000 步. 这些数字吻合「从 LLM 进入末段学习率之前或之中取 checkpoint, 再用多模态混合数据走完并延长末段」的做法, 但这只是两份报告超参数的对照, 报告本身没有这样写. 另外 DeepSeek LLM 报告公开的是 7B 和 67B, 1B 底座没有单独发布, 它的学习率曲线也就无从对照.

三阶段训练与模态预热: 训练分三段: 阶段 1 只训 adaptor, 阶段 2 解冻 LLM 做视觉语言联合预训练, 阶段 3 做监督微调. 前后两段是 LLaVA 一类模型的常规做法, 报告的新东西集中在阶段 2: 多模态数据怎么混进去才不伤语言能力.

阶段 2 的结论都来自 1.3B 上的实验, 再用到 7B. 1.3B 在这一阶段的生成式指标波动很大, 报告因此更换了评测口径, 相关结果见 3.4 节.

三个阶段各训什么.

Image block

图 2: 三阶段训练流程, 标出每个阶段冻结与可训的模块和所用数据 (原文 Figure 3).

图 2 解析. 图中冰晶表示冻结, 火焰表示可训. 阶段 1 只有 adaptor 可训, 数据是图文对; 阶段 2 adaptor 和 LLM 可训, 数据是图文交错数据加纯语言序列; 阶段 3 三个模块都标了火焰, 数据是视觉对话加纯语言对话. 阶段 3 的画法与题注和 3.2.3 节不一致: 题注说这一阶段训练的是 SigLIP-L, adaptor 和 LLM, 正文说 SAM-B 因显存限制保持冻结, 图上混合编码器整体标为可训.

把 Table 4 的超参数换算成样本和 token 数, 能看清每个阶段的体量. 阶段 1 两档模型都是 1500015000 步, batch 256256, 共 3.84×1063.84\times10^{6} 条样本, 与阶段 1 数据量 (ShareGPT4V 125 万对加文档 OCR 250 万对, 合 375 万) 基本相当, 约一个 epoch, 序列长只有 512512. 阶段 2 开了 sequence packing, 7B 为 42000×2304×4096≈3.96×101142000\times2304\times4096\approx3.96\times10^{11} token, 1.3B 为 96000×1024×4096≈4.03×101196000\times1024\times4096\approx4.03\times10^{11} token, 都是填满时的上限, 两档模型训练量几乎相同. 按纯文本占 70% 算, 7B 在阶段 2 见了约 2770 亿文本 token 和约 1190 亿多模态 token. 阶段 3 为 10000×256=2.56×10610000\times256=2.56\times10^{6} 条对话, 不打包. 训练时长方面, 7B 在 64 个节点 (每节点 8 张 A100) 上用了 5 天, 1.3B 在 16 个节点上用了 7 天. 文中没有给出实际 token 数, 以上都是从步数, batch 和序列长度推出的上限.

语言能力为什么会掉: 报告先试了只用多模态数据续训 LLM, 结果多模态指标稳步上升, 语言指标明显下滑. 它给出两个解释: 多模态语料普遍比纯文本简单, 分布差异大; 两种模态在有限容量里互相争夺, 造成语言能力的灾难性遗忘. 对策是在阶段 2 混入大比例纯文本, 图 3 是 1.3B 上五种混合比例的对比.

Image block

图 3: 1.3B 模型在阶段 2 用五种「多模态比语言」混合比例训练 16000 步时, 三个多模态基准与三个语言基准的变化 (原文 Figure 4).

图 3 解析. 六张子图里信号最清楚的是右下角的 Pile-test 困惑度. 纯多模态 (100:0) 一条从 2.162.16 一路涨到 2.2452.245; 75:25 和 60:40 两条在 2.172.17 附近走平; 25:75 和 10:90 两条停在 2.142.14 到 2.152.15. MMLU 和 HellaSwag 的纵轴范围只有一到两个百分点, 曲线噪声比各比例之间的差距还大, 只能看出 100:0 整体偏低. 多模态一侧, 10:90 在 SeedBench 上始终停在 3030 左右, 25:75 涨到约 3838, 60:40, 75:25, 100:0 三条都到 4343 到 4545, 彼此难分. 也就是说多模态数据占到 60% 后, 再加多模态比例已经换不来多模态分数, 只会继续推高 Pile 困惑度.

这张图与最终选择的比例有两处对不上. 第一, 题注写「合适的比例是 multimodal:language=70%:30%」, 正文 3.2.2 节写「语言与多模态约 7:3」, Table 1 里纯文本占 70.0%, 4.4 节模态预热也说语言比例从 1 降到目标值 (例如 0.7); 正文, Table 1, 4.4 节三处一致, 题注把方向写反了. 第二, 按正文的比例, 最终配方对应 multimodal:language =30:70=30:70, 图中五条曲线没有这一条, 它夹在 25:75 和 60:40 之间, 而这两条在多模态基准上差了约 55 个点. 30:70 的多模态表现落在哪里, 文中没有给出实测. 4.4 节的模态分组和模态预热消融用的又是 60:40. 到了 VL2, 预训练数据改成约 70% 视觉语言数据加 30% 纯文本, 方向正好反过来, 但那时的语言模型已换成 MoE, 两者不能直接对比.

1.3. 模态预热与模态分组

模态分组针对的是吞吐. 同一个 batch 里混着纯文本和带图样本时, 一步要等最慢的样本算完, 带图样本还要多跑一遍视觉编码器, 纯文本样本只能陪着等. 分组的做法是每个全局步只采一种模态, 要么全是语言数据, 要么全是多模态数据. 报告说训练效率因此提高 20%, 表现不受影响. 模态预热针对的是训练初期的稳定性: 语言数据比例从 1 开始, 逐步降到目标值 0.7. 降得多快, 按步数还是按 token 线性降, 文中都没有给出.

Image block

Image block

Image block

图 4: 有无模态预热时 Pile-test 困惑度, MMBench 与 MMBench-CN 准确率随训练步数的变化, 三张子图依次排列 (原文 Figure 9).

图 4 解析. 预热的效果主要在前 4000 步. Pile-test 上, 有预热一条在 500 步时约 2.14172.1417, 无预热约 2.14852.1485, 差距在 2000 步后迅速缩小, 约 13000 步时两条曲线交叉, 16000 步时只差约 0.0020.002. MMBench 上情况相反, 2500 到 5000 步之间有预热的一条低了 22 到 33 个点 (例如 5000 步时 39.939.9 对 42.742.7), 这与预热期间多模态数据少是一致的, 8000 步以后两条交替领先. 题注说有预热「在所有任务上始终持平或更好」, 前 5000 步的 MMBench 并不支…5429 tokens truncated…的输入图, 一张 C4 语言建模的验证困惑度曲线.

An = a 1+录∑(t)k k=1

图注: 图 1 全图, 十组提示与 DeepSeek-VL 回答样例的拼版, 涵盖流程图, 户型图, 网页, 公式, 论文首页, 绘画和厨房场景.

这幅图片是Minecraft官方网站的生动欢迎界面. 背景是一片辽阔的蓝天, 点缀着朵朵蓬松的白云. 下方是一个充满活力的像素艺术世界, 特色是一个村庄、一片茂密的森林和一条蜿蜒的小路. 在图片的中心, 一个绿色的按钮吸引着注意力, 上面用粗体白色字母写着「获取Minecraft」. 这个按钮似乎邀请观看者加入游戏并开始他们的Minecraft冒险. 在图片的顶部, 一段文字热情地欢迎观众来到官方Minecraft网站. 它写道:「欢迎来到官方 Minecraft网站! 加入我们的社区, 了解如何玩这款游戏, 加入我们最大的社区之一, 并在今天开始创造吧! 」这段文字传达了游戏的包容性和社区精神, 鼓励新玩家探索和创造. 画面借助开放的像素世界和欢迎文案, 同时表达探索、创造与玩家互动三层含义.

引用样本保留原文.

DeepSeek-VL 的预训练数据集汇集自多种来源, 包括但不限于 Common Crawl, Web Code, 电子书, 教育资料和 arXiv 论文. 这批数据完整覆盖了网页截图, PDF, OCR, 图表和知识类内容 (专业知识, 教科书) 等真实场景, 目标是在保持可扩展的同时, 做到广泛且贴近实际.

预训练数据涵盖了大量世界知识, 指令微调数据则经过精心整理, 以反映真实的使用场景. 为此, 我们从互联网上人工收集了 GPT-4V 和 Gemini 的真实测试用例, 并把它们系统地组织成一个完整的分类体系. 我们用这个结构化的分类体系为每张测试图片选择提示, 保证指令微调数据集实用且贴题. 同一个分类体系也用来构建评测集, 以有效衡量真实场景下的表现.

视觉模块的设计目标是在固定 token 预算内充分利用高分辨率视觉输入, 以控制推理成本. 为此我们采用混合视觉编码器: 一个与文本对齐的编码器在 384 × 384 分辨率下提取粗粒度语义, 一个高分辨率编码器在 1024 × 1024 分辨率下捕捉细节视觉信息. 两个编码器融合后, 混合方案能把一张 1024×1024 的图像 (大多数场景下已经够用) 高效压缩成 576 个 token. 这个 token 数在视觉表征的丰富程度与 token 开销之间取得平衡, 使图文交错输入和多轮推理场景都可行.

多模态模型预训练中常见的一个难题是: 训练过程过度依赖视觉语言数据时, 语言能力可能退化. 我们的研究发现, 保持相当比例的语言数据, 具体来说至少 70%, 是保住模型内语言知识完整的必要条件. 这一平衡是获得不牺牲语言表现的稳健多模态能力的关键. 此外, 我们提出一种新的「模态预热」策略. 它在训练中仔细调整模态比例, 逐步加入更多视觉语言数据. 模态比例的细致调节加上预热策略, 让两种模态的表现达到平衡.

迭代模型时, 我们先在小规模上做实验, 再扩到更大的模型. 但较小的模型, 例如 1B 模型, 在基准上给不出合理的成绩 (Schaeffer et al., 2024), 也无法如实反映模型的真实水平. 我们用两种办法解决这个问题. 第一, 把评测协议从多选题生成改为比较各选项的困惑度. 第二, 为了不让指令跟随能力成为瓶颈, 我们在预训练阶段混入少量指令微调数据. 这样 1B 模型也能给出合理的成绩, 实验中每次迭代的影响也能测得更准.

通过对通用视觉与语言基准的大量评测, DeepSeek-VL 系列在真实应用中展现出更好的用户体验, 在同等规模下于大量视觉语言基准上取得最优或有竞争力的成绩, 同时保持稳健的语言能力. 为了促进创新并支持广泛应用, 我们公开了 1.3B 和 7B 两个版本, 希望满足不同算力条件的需求.

5. 语言侧的稀疏计算与缓存压缩

局部 tile 保留字符, 小物体与细节, 但每块只观察图像的一部分. 全局缩略图保留页面结构与远距离关系, 分辨率却不足以读取细小内容. 两类视图由同一个 SigLIP 编码器处理, 后续通过特殊 token 拼成一个视觉序列. 语言模型可以同时利用整体布局和局部纹理, 但必须从序列位置判断局部 tile 在整图中的位置.

该设计没有让 tile 之间在视觉编码器内部直接做全局注意力. 跨 tile 关系要到语言模型层面通过自注意力建立. 这降低视觉编码成本, 却把一部分空间整合压力转给语言模型. 如果任务依赖跨越多个 tile 的细线, 表格行列或网页组件, 局部边界与二维排列编码都会影响结果.

全局缩略图也承担去重作用. 相邻局部 tile 可能分别包含同一大对象的不同部分, 语言模型若只看局部序列, 容易把它们当成多个对象. 全局视图提供整体数量和布局参照. 反过来, 全局图中的小字可能不可读, 必须回到局部 tile. 两类视图的互补需要语言模型在注意力层中学会匹配同一对象的不同尺度表示.

候选分辨率以 padding 面积最小为选择准则, 没有直接使用任务内容. 同样长宽比的低密度照片与密集网页会选择相同网格, 尽管所需细节不同. 内容自适应切图可能进一步节省 token, 但会引入区域选择器和漏检风险. VL2 选择几何规则, 优点是确定, 易复现且不依赖额外模型.

报告的定性样例覆盖自然图像, 文档, 网页 UI, meme 与 anime, 说明同一切图策略可以支持多类输入. 但报告没有按 tile 数给出准确率和延迟曲线, 也没有比较相同视觉 token 预算下的固定分辨率与动态切图. 因而可以确认动态策略提升了高分辨率适应性, 不能仅凭最终总分量化每个局部视图的独立贡献.

5.1. pixel shuffle 与序列排布

SigLIP 为每个 tile 输出 27×27=72927\times27=729 个 1152 维视觉 embedding. VL adaptor 使用 2×22\times2 pixel shuffle, 把空间网格压到 14×14=19614\times14=196 个 token. pixel shuffle 把邻近位置的特征重排并合并到通道维, 随后的两层 MLP 再投影到语言模型 embedding 空间. 这一步把单 tile 的语言侧序列长度缩短约 3.72 倍.

全局缩略图的 14 行各追加一个 <tile_newline>, 因而占 14×15=21014\times15=210 个 token. 局部 tile 按二维网格重排为 (14mi,14ni)(14m_i,14n_i), 每行末尾也加换行 token, 共 14mi(14ni+1)14m_i(14n_i+1) 个 token. 全局视图与局部视图之间再插入一个 <view_separator>. 完整视觉序列长度为

Lv=210+1+14mi(14ni+1).L_v=210+1+14m_i(14n_i+1).

当 mi=ni=1m_i=n_i=1 时, 视觉序列为 421 个 token; 当 mini=9m_in_i=9 且采用 3×3 网格时, 长度为 201? 这里直接代入可得 210+1+42×43=2017210+1+42\times43=2017. 也就是说, 动态切图虽然限制在 9 个局部 tile, 视觉前缀仍可超过两千 token. 特殊换行和分隔 token 没有携带图像 patch 特征, 但为语言模型提供二维行边界和视图边界.

pixel shuffle 并未把 729 恰好除以 4 得到整数. 原始网格是 27×27, 实现需要处理奇数边界后形成 14×14 输出. 报告直接给出最终 token 数, 没有逐项说明边界 padding. 因而复现时应以仓库 adaptor 实现为准, 不能只按理想 2×22\times2 网格重排推测 shape.

视觉序列的换行 token 让一维自回归模型获得二维行结构, 但没有显式列分隔 token. 同一行内的位置由序列顺序与位置编码表达, tile 网格边界则依赖拼接规则. 如果下游实现改变局部 tile 排列顺序, 即使每块图像特征相同, 语言模型接收到的空间关系也会变化.

语言侧的稀疏计算与缓存压缩: 共享专家承担公共模式,路由专家提供条件容量;视觉 token 与文本 token 共用这套稀疏计算.

三个 MoE 变体的共同路由: Tiny 使用 12 层, 64 个路由专家与 2 个共享专家, 每个 token 选择 6 个路由专家; Small 使用 27 层, 64 个路由专家; 标准版使用 30 层, 72 个路由专家. 三者都保留 2 个共享专家与 top-6 路由. 共享专家持续处理所有 token, 用于容纳跨领域的共同模式; 路由专家只处理被选中的 token, 将额外参数容量分配给不同输入分布.

Tiny 的语言模型总参数为 3B, 激活约 0.57B; Small 为 16B 与 2.4B; 标准版为 27B 与 4.1B. 视觉编码器约 0.4B 参数也在推理中运行, 因而系列最终激活规模约为 1.0B, 2.8B 与 4.5B. Figure 1 以这个合计值比较开源模型, 不是用语言模型总参数比较.

Tiny 与 Small 的路由函数为 softmax, 标准版改用 sigmoid 并加入专家校正 bias. sigmoid 路由允许各专家分数独立变化, 校正 bias 用于负载均衡. 报告没有给路由分布, 专家利用率和去掉 bias 的消融, 所以无法从表格判断标准版增量中有多少来自新路由方式.

MLA 解决 Decode 阶段的 KV cache: Small 与标准版采用 MLA, rank 为 512; Tiny 仍使用标准 MHA. MLA 把各注意力头的 Key 与 Value 表示压到共享低维潜变量, 推理时缓存潜变量而非每层每头的完整 KV. 详细推导应参见 MLA 相关章节, DeepSeek-VL2 报告主要说明它在多模态模型中的工程用途.

视觉前缀会在 Prefill 阶段一次进入语言模型, 随后生成回答. 动态切图让视觉前缀可能超过两千 token, 如果继续使用完整 KV cache, 每层缓存会随视觉 token 数, 头数和维度增长. MLA 不减少视觉前缀自身的 Prefill 计算, 但会降低这些视觉 token 在后续 Decode 阶段占据的缓存空间和读取带宽.

这一区分解释了动态切图与 MLA 的互补关系. tile 增加改善细节感知, 同时拉长视觉序列; MLA 缓解长前缀造成的推理缓存压力. MoE 又降低每个 token 的前馈计算. 三者分别作用于视觉输入分辨率, 注意力状态和参数激活, 不能互相替代.

5.2. 多模态 token 的路由边界

视觉 embedding 经 MLP 投影后与文本 token 进入同一个语言模型. MoE 路由器因而会同时看到视觉位置, 特殊 tile token, 用户 prompt 与回答 token. 论文没有规定视觉 token 固定进入特定专家, 也没有报告专家是否按模态分化. 从公开结构看, 路由由每个位置的隐藏状态动态决定.

这种统一路由允许视觉与语言知识共享专家容量, 也带来负载波动. 高分辨率单图会集中产生大量视觉 token, 纯文本 batch 则完全没有视觉 token. 标准版使用全局专家 bias 调节负载; 训练系统还必须在数据并行 rank 之间平衡 tile 数. 路由层与系统调度共同决定实际吞吐.

MoE 的「激活参数较少」也不等于权重显存只需保存激活部分. 部署仍要存储全部路由专家, 并在设备间安排专家并行通信. Figure 1 的横轴适合比较每 token 计算规模, 不代表总模型存储或跨卡通信成本.

视觉任务还会改变专家负载的时间分布. Prefill 时连续处理大量视觉 token, Decode 时每步只增加一个文本 token. 如果视觉与文本隐藏状态偏向不同专家, 两个阶段的通信热点可能不同. 报告只给全局负载均衡机制, 没有分别报告 Prefill 与 Decode 的专家命中率和吞吐.

Tiny 保留 MHA, 因此 KV cache 不享受 MLA 压缩, 但层数和 hidden size 更小. Small 与标准版使用 MLA 后, cache 优势会随序列和生成长度增长. 对短问答, 视觉编码与 Prefill 可能主导延迟; 对长回答和并发服务, cache 带宽与容量更重要. 报告没有给分阶段 latency, 不能用激活参数直接预测所有请求形态.

三阶段数据与训练管线: 从对齐到全参数预训练

Stage 1 使用约 1.2M 条 ShareGPT4V caption 与对话样本, 训练视觉编码器和 VL adaptor, 冻结语言模型. 与常见固定视觉编码器的对齐阶段不同, VL2 还要让 SigLIP 适应动态高分辨率 tile, 因而视觉编码器并未冻结. 三个变体在该阶段都训练约 2.0B token, batch size 为 256, sequence length 为 4096.

Stage 2 解冻视觉编码器, adaptor 与语言模型全部参数, 使用约 796.5B–808.9B token 做视觉语言预训练. VL 数据约占 70%, 纯文本约占 30%. 纯文本数据用于维持基础语言能力, 视觉语言数据覆盖交错图文, caption, OCR, VQA, 图表文档, web-to-code, visual prompt, grounding 与 grounded conversation.

三个模型 Stage 2 token 数接近, 并没有随参数规模成比例增加. Tiny, Small 和标准版分别约为 798.5B, 808.9B 与 796.5B token. 因而系列比较更接近固定数据规模下扩大模型容量, 而非同时扩大数据. batch size 对 Tiny 与 Small 为 2304, 标准版为 3360; sequence length 都是 4096 并开启 sequence packing.

视觉编码器学习率是主学习率的 0.1 倍, 说明联合训练时仍限制预训练视觉权重的更新幅度. Stage 2 使用 step 调度, Stage 3 改为 constant. 报告没有公布 warmup step, step 衰减节点和各数据源采样温度, 完整训练日程不能仅凭 Table 2 复原.

next-token prediction loss 只在文本 token 上计算. 视觉 token 作为条件输入, 梯度通过语言损失回传到 adaptor 与视觉编码器. 这意味着视觉表示的学习目标由文本生成任务间接定义, 并没有额外的图像重建损失. 任务分布和回答质量会直接决定视觉编码器保留哪些信息.

数据清洗比简单扩量更关键: caption 数据来源质量差异较大, 包括过短描述, 图文错配和幻觉. 团队把 OCR hint, 地点和相机等元信息, 以及原 caption 一起交给内部 captioner 重写, 再用 DeepSeek Chat 按写作质量评分过滤. 该流程提高文本质量, 但评分器是否验证视觉忠实度没有单独说明; 写得流畅的错误 caption 仍可能通过纯文本质量评分.

重写流程还会改变数据风格. 多个来源经同一 captioner 生成后, 语句结构和关注点可能趋同, 原数据的人类表达差异被削弱. 团队使用多种指令引导 caption 生成来减少重复, 又以写作质量过滤明显模板化结果. 报告没有公开过滤阈值和保留率, 无法判断质量提升与多样性损失的取舍.

文本数据占预训练的 30%, SFT 也加入 DeepSeek-V2 纯文本对话, 用于减轻多模态训练造成的语言能力退化. 视觉 batch 与纯文本 batch 的计算路径不同, 训练系统因此准备两种流水线策略并按需切换. 报告没有给出加入纯文本数据前后的语言榜单消融, 这部分作用无法从现有结果中单独量化.

OCR 数据包含 LaTeX OCR, 12M RenderedText 和内部中英文文档数据. 图表与文档理解加入 PubTabNet, FinTabNet 和 Docmatix; web-to-code 使用 Websight, 部分样本由 DeepSeek V2.5 重建; visual prompt 数据在图像上叠加箭头, 框, 圆和涂画. 不同来源被统一成自回归回答格式, 使模型在同一输出空间学习识别, 定位和代码生成.

grounding 坐标归一化到 0–999, 用 <ref> 与 <det> 特殊 token 包裹对象描述和 bounding box. 训练集还加入目标不存在的负样本, 避免模型无论提问什么都输出一个框. 这种离散坐标格式便于文本解码, 精度上限受 1000 个坐标 bin 与图像缩放影响.

5.3. SFT 的回答长度与模型容量

Stage 3 使用约 19.5B–20.0B token 做 SFT, 所有参数继续训练, 但只监督回答和特殊 token, mask 掉 system 与用户 prompt. 数据混合多模态指令与 DeepSeek-V2 的纯文本对话, 覆盖通用 VQA, OCR, 文档, 图表, visual-to-code, 推理, grounding 和语言理解.

公开数据常见三个问题是回答过短, OCR 质量差与幻觉. 团队结合原问题, 图像和 OCR 信息重写回答. Tiny 早期版本还会在中文回答中错误插入英文词, 报告把原因指向模型容量有限与视觉语言预训练阶段中英文失衡, 因而额外构建中文图像描述和单轮, 多轮对话数据.

报告还观察到详细推理回答对 Tiny 效果较差, 简洁回答反而更合适. 这说明同一 SFT 目标不能不加区分地用于三个容量档位. 标准化长推理过程可能超过小模型稳定表示和生成的能力, 也会挤占 4096 token 序列中的图像与问题空间. 论文没有给回答长度的定量消融, 但明确记录了容量相关的数据选择.

训练系统与评测口径: 学习率与并行配置

三个阶段都使用 AdamW, β1=0.9\beta_1=0.9, β2=0.95\beta_2=0.95, weight decay 0.1, gradient clip 1.0. 视觉编码器学习率乘数固定为 0.1. Stage 1 和 Stage 2 的主学习率按模型不同约为 4.2×10−44.2\times10^{-4} 到 5.4×10−45.4\times10^{-4}, Stage 3 降到 1.4×10−51.4\times10^{-5} 到 3.0×10−53.0\times10^{-5}. 调度器分别为 cosine, step 与 constant.

视觉编码器位于流水线首段, 计算形态与均匀堆叠的 LLM block 不同. 动态切图还使每条样本 tile 数不同, 如果直接分配到数据并行 rank, 某些 rank 会等待 tile 更多的样本. HAI-LLM 对视觉编码器做细粒度层切分, 并在 forward 与 backward 中跨数据并行 rank 平衡 image tile 数, 以减少 pipeline bubble.

训练同时使用流水线并行, tensor parallel 与 expert parallel. Tiny, Small 和标准版分别用 16, 33 与 42 个节点, 每节点 8 张 A100, 用时约 7, 10 与 14 天. 报告给出节点和时间, 没有给实际 GPU 利用率与故障重启成本, 因而只能得到名义卡时, 不能复原有效训练 FLOPs.

文档与 OCR 结果怎样读取: 在文档评测表中, Tiny 的 DocVQA 为 88.9, ChartQA 81.0, InfoVQA 66.1, TextVQA 80.7, OCRBench 809. Small 分别为 92.3, 84.5, 75.8, 83.4, 834. 标准版为 93.3, 86.0, 78.1, 84.2, 811. 模型变大时大多数项目提高, OCRBench 却由 Small 的 834 降到标准版的 811.

这个反例说明综合能力与单一 OCR 榜单不必单调一致. 标准版改变了语言模型层数, 路由专家数, 路由函数与训练系统, 不能把 OCRBench 回落归因于某一个部件. MiniCPM-V2.6 的 OCRBench 852 标注使用 CoT, 评测协议也与普通直接生成可能不同.

Figure 1 平均 MMBench v1.1, MMStar, MMMU, MathVista, AI2D 与 OCRBench, 其中 OCRBench 除以 10 映射到 0–100. 简单平均默认六项同等重要, 也会掩盖单项回退. 它适合展示激活参数与综合分数的整体位置, 不能替代逐任务表格.

参数比较还要区分视觉编码器与语言模型. DeepSeek-VL2-Tiny 的语言侧激活 0.57B, 加上 0.4B 视觉编码器后按约 1.0B 标示; 其他基线有的只报告语言参数, 有的报告总激活参数. 表格脚注用匕首标明 MoE 激活值, 但闭源模型没有可比参数. Figure 1 主要用于开源模型内部的效率位置, 不能据此比较闭源服务的真实计算量.

标准版 DocVQA 93.3 低于 Claude 3.5 Sonnet 的 95.2 与 Qwen2-VL-7B 的 94.5, ChartQA 86.0 低于 Claude 的 90.8, InfoVQA 78.1 接近但低于 Gemini-1.5-Pro 的 80.1. 报告用 competitive 或 state-of-the-art 描述不同任务, 具体读取仍应逐列比较, 不能把平均图理解为每个项目都领先.

已公开结果不能回答什么: 动态切图, pixel shuffle, MLA, MoE 与数据扩充在同一版本同时出现, 报告没有完整组件消融. 因而 VL2 相对 VL 的提升不能被拆成「多少来自切图, 多少来自语言架构, 多少来自数据」. 三个变体也不是只改变规模: Tiny 使用 MHA 和 softmax 路由, Small 使用 MLA, 标准版进一步改为 sigmoid 路由与校正 bias.