Files
2026-07-13 13:37:02 +08:00

1209 lines
104 KiB
HTML
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>VLM / Multimodal 面试 Cheat Sheet</title>
<meta name="generator" content="ARIS render-html (academic, v1)">
<meta name="aris:source-path" content="docs/tutorials/vlm_multimodal_tutorial.md">
<meta name="aris:source-sha256" content="f9cc9510bdd535499bcf756290f8ccf31a6785c97df3fa1dabb9e9c1bf74541f">
<meta name="aris:generated-at" content="2026-05-19 05:47 UTC">
<!-- MathJax 3 -->
<script>
window.MathJax = {
tex: { inlineMath: [['$', '$'], ['\\(', '\\)']], displayMath: [['$$', '$$'], ['\\[', '\\]']], processEscapes: true },
options: { skipHtmlTags: ['script', 'noscript', 'style', 'textarea', 'pre', 'code'] }
};
</script>
<script src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js" async></script>
<!-- highlight.js -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.9.0/build/styles/atom-one-light.min.css">
<script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.9.0/build/highlight.min.js"></script>
<script>document.addEventListener('DOMContentLoaded', () => hljs.highlightAll());</script>
<style>
:root {
--bg: #fdfcf7;
--bg-soft: #f4f1ea;
--bg-code: #f8f5ec;
--ink: #1a1a1a;
--ink-soft: #4a4a4a;
--ink-muted: #6b6b6b;
--primary: #1a4a8c;
--primary-soft: #2d6cb8;
--accent: #b8390e;
--warn: #b45309;
--warn-bg: #fef3c7;
--info-bg: #dbeafe;
--good-bg: #d1fae5;
--good: #065f46;
--bad-bg: #fee2e2;
--bad: #991b1b;
--border: #d6d0c0;
--border-soft: #e8e3d5;
}
* { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
font-family: "Source Serif Pro", "Source Serif 4", "Crimson Pro", "Georgia", "Songti SC", "STSong", serif;
line-height: 1.65;
color: var(--ink);
background: var(--bg);
margin: 0;
padding: 0;
font-size: 16px;
}
.layout {
max-width: 1280px;
margin: 0 auto;
display: grid;
grid-template-columns: 260px 1fr;
gap: 48px;
padding: 40px 32px;
}
nav.toc {
position: sticky;
top: 24px;
align-self: start;
font-size: 13px;
max-height: calc(100vh - 48px);
overflow-y: auto;
border-right: 1px solid var(--border-soft);
padding-right: 16px;
}
nav.toc h3 {
margin: 0 0 12px;
font-size: 12px;
text-transform: uppercase;
letter-spacing: 0.08em;
color: var(--ink-muted);
font-weight: 600;
}
nav.toc ol { list-style: none; padding: 0; margin: 0; counter-reset: toc; }
nav.toc ol li { margin: 5px 0; counter-increment: toc; }
nav.toc ol li::before { content: counter(toc) ". "; color: var(--ink-muted); margin-right: 4px; }
nav.toc a {
color: var(--ink-soft);
text-decoration: none;
border-bottom: 1px dotted transparent;
}
nav.toc a:hover { color: var(--primary); border-bottom-color: var(--primary); }
nav.toc ul { list-style: none; padding-left: 14px; margin: 3px 0; font-size: 12px; }
nav.toc ul li::before { content: "→ "; color: var(--border); }
main { min-width: 0; }
header.hero {
border-bottom: 3px double var(--primary);
padding-bottom: 24px;
margin-bottom: 32px;
}
header.hero .eyebrow {
color: var(--accent);
font-size: 13px;
text-transform: uppercase;
letter-spacing: 0.12em;
font-weight: 600;
margin-bottom: 8px;
}
header.hero h1 {
font-size: 32px;
line-height: 1.2;
margin: 0 0 12px;
color: var(--ink);
font-weight: 700;
letter-spacing: -0.01em;
}
header.hero .subtitle {
font-size: 16px;
color: var(--ink-soft);
margin: 0 0 8px;
font-style: italic;
}
header.hero .byline {
font-size: 14px;
color: var(--ink-soft);
margin: 0 0 20px;
}
header.hero .byline strong {
color: var(--ink);
font-weight: 600;
}
header.hero .meta {
display: flex;
gap: 20px;
flex-wrap: wrap;
font-size: 12px;
color: var(--ink-muted);
border-top: 1px solid var(--border-soft);
padding-top: 14px;
}
header.hero .meta span strong { color: var(--ink-soft); }
header.hero .meta code {
font-family: "JetBrains Mono", "SF Mono", "Menlo", "Consolas", monospace;
font-size: 11px;
background: var(--bg-soft);
padding: 1px 5px;
border-radius: 3px;
border: 1px solid var(--border-soft);
}
h2 {
font-size: 24px;
margin: 44px 0 14px;
padding-bottom: 8px;
border-bottom: 1px solid var(--border);
color: var(--ink);
font-weight: 700;
}
h2 .num { color: var(--primary); font-weight: 600; margin-right: 8px; }
h3 { font-size: 19px; margin: 28px 0 10px; color: var(--primary); font-weight: 600; }
h4 { font-size: 16px; margin: 20px 0 8px; color: var(--ink); font-weight: 600; }
p { margin: 10px 0; }
ul, ol { padding-left: 22px; margin: 10px 0; }
ul li, ol li { margin: 4px 0; }
ul li::marker { color: var(--primary); }
strong { color: var(--accent); font-weight: 600; }
em { color: var(--ink-soft); }
a { color: var(--primary); }
a:hover { color: var(--accent); }
code:not(.hljs) {
font-family: "JetBrains Mono", "SF Mono", "Menlo", "Consolas", monospace;
font-size: 0.86em;
background: var(--bg-code);
padding: 1px 5px;
border-radius: 3px;
border: 1px solid var(--border-soft);
color: var(--accent);
}
pre {
background: #fafaf6;
border: 1px solid var(--border);
border-left: 4px solid var(--primary);
padding: 0;
overflow-x: auto;
border-radius: 4px;
margin: 14px 0;
}
pre code, pre code.hljs {
background: transparent !important;
display: block;
padding: 14px 18px !important;
font-size: 13px;
line-height: 1.55;
font-family: "JetBrains Mono", "SF Mono", "Menlo", monospace;
color: var(--ink);
}
pre.diagram {
background: #f9f6ed;
border-left: 4px solid var(--accent);
font-size: 12.5px;
line-height: 1.4;
}
.callout {
margin: 16px 0;
padding: 12px 16px;
border-radius: 4px;
border-left: 4px solid;
font-size: 15px;
}
.callout-title {
font-weight: 600;
margin-bottom: 6px;
font-size: 12px;
text-transform: uppercase;
letter-spacing: 0.06em;
}
.callout-info { background: var(--info-bg); border-left-color: var(--primary); }
.callout-info .callout-title { color: var(--primary); }
.callout-warn { background: var(--warn-bg); border-left-color: var(--warn); }
.callout-warn .callout-title { color: var(--warn); }
.callout-good { background: var(--good-bg); border-left-color: var(--good); }
.callout-good .callout-title { color: var(--good); }
.callout-bad { background: var(--bad-bg); border-left-color: var(--bad); }
.callout-bad .callout-title { color: var(--bad); }
table {
width: 100%;
border-collapse: collapse;
margin: 16px 0;
font-size: 14px;
border: 1px solid var(--border);
border-radius: 4px;
overflow: hidden;
}
thead { background: var(--primary); color: white; }
th, td {
text-align: left;
padding: 9px 12px;
border-bottom: 1px solid var(--border-soft);
vertical-align: top;
}
th { font-weight: 600; font-size: 13px; letter-spacing: 0.02em; }
tr:last-child td { border-bottom: none; }
tbody tr:nth-child(even) { background: var(--bg-soft); }
details.qa, details {
background: white;
border: 1px solid var(--border-soft);
border-radius: 6px;
margin: 10px 0;
padding: 0;
}
details summary {
cursor: pointer;
padding: 10px 14px;
font-weight: 600;
font-size: 14px;
color: var(--primary);
list-style: none;
user-select: none;
}
details summary::-webkit-details-marker { display: none; }
details summary::before {
content: "▸ ";
margin-right: 4px;
display: inline-block;
transition: transform 0.15s;
}
details[open] summary::before { transform: rotate(90deg); }
details[open] summary { border-bottom: 1px solid var(--border-soft); }
details > :not(summary) { padding: 10px 14px; }
details p:first-of-type { margin-top: 8px; }
mjx-container[display="true"] { margin: 12px 0 !important; }
footer.aris-footer {
margin-top: 60px;
padding-top: 20px;
border-top: 1px solid var(--border);
font-size: 12px;
color: var(--ink-muted);
}
footer.aris-footer a { color: var(--ink-muted); border-bottom: 1px dotted var(--border); }
@media (max-width: 900px) {
.layout { grid-template-columns: 1fr; gap: 20px; padding: 20px 16px; }
nav.toc {
position: static;
max-height: none;
border-right: none;
border-bottom: 1px solid var(--border-soft);
padding-right: 0;
padding-bottom: 14px;
}
header.hero h1 { font-size: 24px; }
h2 { font-size: 20px; }
}
@media print {
nav.toc { display: none; }
.layout { grid-template-columns: 1fr; padding: 0; }
body { background: white; }
header.hero { border-bottom-color: var(--ink); }
}
</style>
</head>
<body>
<div class="layout">
<nav class="toc">
<h3>Contents</h3>
<ol>
<li><a href="#0-tldr-cheat-sheet">§0 TL;DR Cheat Sheet</a>
</li>
<li><a href="#1-直觉vlm-在做什么">§1 直觉:VLM 在做什么?</a>
</li>
<li><a href="#2-vit把图像变-token-序列">§2 ViT:把图像变 token 序列</a>
<ul>
<li><a href="#21-patch-tokenize">2.1 Patch tokenize</a></li>
<li><a href="#22-transformer-主干">2.2 Transformer 主干</a></li>
<li><a href="#23-vit-规格梳理">2.3 ViT 规格梳理</a></li>
<li><a href="#24-code-vit-patch-embed--主干核心-60-行">2.4 Code: ViT patch embed + 主干(核心 60 行)</a></li>
</ul>
</li>
<li><a href="#3-clip对称-infonce必推导">§3 CLIP:对称 InfoNCE(必推导)</a>
<ul>
<li><a href="#31-形式化目标">3.1 形式化目标</a></li>
<li><a href="#32-对称-infonce-loss行--列-softmax-平均">3.2 对称 InfoNCE Loss(行 + 列 softmax 平均)</a></li>
<li><a href="#33-梯度推导为什么对称很重要">3.3 梯度推导(为什么对称很重要)</a></li>
<li><a href="#34-temperature-的作用">3.4 Temperature 的作用</a></li>
<li><a href="#35-code-clip-对称-infonce核心-50-行">3.5 Code: CLIP 对称 InfoNCE(核心 50 行)</a></li>
<li><a href="#36-clip-训练数据--规模">3.6 CLIP 训练数据 &amp; 规模</a></li>
<li><a href="#37-clip-的失败模式">3.7 CLIP 的失败模式</a></li>
</ul>
</li>
<li><a href="#4-siglipsigmoid-替-softmaxbatch-缩放重写">§4 SigLIPsigmoid 替 softmaxbatch 缩放重写</a>
<ul>
<li><a href="#41-motivation">4.1 Motivation</a></li>
<li><a href="#42-sigmoid-loss-推导">4.2 Sigmoid Loss 推导</a></li>
<li><a href="#43-siglip-vs-clip-对比">4.3 SigLIP vs CLIP 对比</a></li>
<li><a href="#44-siglip-2-google-2025">4.4 SigLIP-2 (Google 2025)</a></li>
<li><a href="#45-code-siglip-sigmoid-loss核心-35-行">4.5 Code: SigLIP sigmoid loss(核心 35 行)</a></li>
</ul>
</li>
<li><a href="#5-eva-clip--openclip--其它-clip-变体">§5 EVA-CLIP / OpenCLIP / 其它 CLIP 变体</a>
<ul>
<li><a href="#51-openclip">5.1 OpenCLIP</a></li>
<li><a href="#52-eva-clip">5.2 EVA-CLIP</a></li>
<li><a href="#53-datacomp数据-vs-模型-vs-算法">5.3 DataComp(数据 vs 模型 vs 算法)</a></li>
<li><a href="#54-对比一览">5.4 对比一览</a></li>
</ul>
</li>
<li><a href="#6-llavaprojector--2-stage-训练">§6 LLaVAprojector + 2-stage 训练</a>
<ul>
<li><a href="#61-架构">6.1 架构</a></li>
<li><a href="#62-训练两阶段">6.2 训练两阶段</a></li>
<li><a href="#63-code-llava-风格-projector--forward核心-60-行">6.3 Code: LLaVA 风格 projector + forward(核心 60 行)</a></li>
<li><a href="#64-llava-15--16--next-关键升级">6.4 LLaVA-1.5 / 1.6 / NeXT 关键升级</a></li>
</ul>
</li>
<li><a href="#7-blip-2q-former-cross-attention">§7 BLIP-2Q-Former cross-attention</a>
<ul>
<li><a href="#71-motivation">7.1 Motivation</a></li>
<li><a href="#72-q-former-结构">7.2 Q-Former 结构</a></li>
<li><a href="#73-两阶段训练">7.3 两阶段训练</a></li>
<li><a href="#74-code-q-former-cross-attention-单层核心-40-行">7.4 Code: Q-Former cross-attention 单层(核心 40 行)</a></li>
<li><a href="#75-q-former-vs-llava-projectortrade-off">7.5 Q-Former vs LLaVA Projectortrade-off</a></li>
</ul>
</li>
<li><a href="#8-flamingoperceiver-resampler--gated-cross-attn">§8 FlamingoPerceiver Resampler + Gated Cross-Attn</a>
<ul>
<li><a href="#81-设计目标">8.1 设计目标</a></li>
<li><a href="#82-perceiver-resampler">8.2 Perceiver Resampler</a></li>
<li><a href="#83-gated-cross-attention核心创新">8.3 Gated Cross-Attention(核心创新)</a></li>
<li><a href="#84-flamingo--llama-32-v-vs-llava-对比">8.4 Flamingo / Llama-3.2-V vs LLaVA 对比</a></li>
</ul>
</li>
<li><a href="#9-cogvlm-与视觉专家--cross-attn-fusion-变体">§9 CogVLM 与&quot;视觉专家&quot; / Cross-attn fusion 变体</a>
<ul>
<li><a href="#91-cogvlm视觉专家分支">9.1 CogVLM:视觉专家分支</a></li>
<li><a href="#92-llama-32-visionflamingo-style-cross-attn-在大-llm-上的复活">9.2 Llama-3.2 VisionFlamingo-style cross-attn 在大 LLM 上的复活</a></li>
<li><a href="#93-claude-3537-sonnet-vision-与-gpt-4v4o">9.3 Claude 3.5/3.7 Sonnet Vision 与 GPT-4V/4o</a></li>
</ul>
</li>
<li><a href="#10-qwen2-vl--deepseek-vl动态分辨率--m-rope">§10 Qwen2-VL / DeepSeek-VL:动态分辨率 + M-RoPE</a>
<ul>
<li><a href="#101-native-dynamic-resolution">10.1 Native Dynamic Resolution</a></li>
<li><a href="#102-m-ropemultimodal-rope">10.2 M-RoPEMultimodal RoPE</a></li>
<li><a href="#103-qwen25-vl-升级">10.3 Qwen2.5-VL 升级</a></li>
<li><a href="#104-deepseek-vl--vl2高分辨率-tiling--hybrid-encoder">10.4 DeepSeek-VL / VL2:高分辨率 tiling + Hybrid encoder</a></li>
<li><a href="#105-code-m-rope-三维位置嵌入核心-50-行对齐-qwen2-vl-hf-实现">10.5 Code: M-RoPE 三维位置嵌入(核心 50 行,对齐 Qwen2-VL HF 实现)</a></li>
</ul>
</li>
<li><a href="#11-video-vlmlongva--videollama--长视频问题">§11 Video VLMLongVA / VideoLLaMA / 长视频问题</a>
<ul>
<li><a href="#111-基本-pipeline">11.1 基本 pipeline</a></li>
<li><a href="#112-常见压缩策略">11.2 常见压缩策略</a></li>
<li><a href="#113-longva--long-context-video">11.3 LongVA / Long-context video</a></li>
<li><a href="#114-video-benchmark">11.4 Video benchmark</a></li>
</ul>
</li>
<li><a href="#12-训练-pipelinealignment--instruct--preference">§12 训练 pipelinealignment / instruct / preference</a>
<ul>
<li><a href="#121-stage-1alignment--pre-training">12.1 Stage 1Alignment / Pre-training</a></li>
<li><a href="#122-stage-2visual-instruction-tuning">12.2 Stage 2Visual Instruction Tuning</a></li>
<li><a href="#123-stage-3preference--rlhf">12.3 Stage 3Preference / RLHF</a></li>
<li><a href="#124-数据规模-vs-阶段">12.4 数据规模 vs 阶段</a></li>
</ul>
</li>
<li><a href="#13-multimodal-embeddingsbge-vl--jina-clip--vlm2vec">§13 Multimodal EmbeddingsBGE-VL / Jina-CLIP / VLM2Vec</a>
<ul>
<li><a href="#131-为什么需要新一代多模态-embedding">13.1 为什么需要新一代多模态 embedding?</a></li>
<li><a href="#132-代表方法">13.2 代表方法</a></li>
<li><a href="#133-核心-trick">13.3 核心 trick</a></li>
</ul>
</li>
<li><a href="#14-25-高频面试题l1-必会--l2-进阶--l3-顶级-lab">§14 25 高频面试题(L1 必会 · L2 进阶 · L3 顶级 lab)</a>
<ul>
<li><a href="#l1-基础题必会-10">L1 基础题(必会 10</a></li>
<li><a href="#l2-进阶10-题">L2 进阶(10 题)</a></li>
<li><a href="#l3-高级顶级-lab--研究方向5-题">L3 高级(顶级 lab / 研究方向,5 题)</a></li>
</ul>
</li>
<li><a href="#a-附录sanity-check-输出--参考文献">§A 附录:Sanity-check 输出 &amp; 参考文献</a>
<ul>
<li><a href="#a1-关键代码-sanity-check实跑示意">A.1 关键代码 sanity check(实跑示意)</a></li>
<li><a href="#a2-关键文献按主题">A.2 关键文献(按主题)</a></li>
</ul>
</li>
</ol>
</nav>
<main>
<header class="hero">
<div class="eyebrow">Interview Prep · Vision-Language Models</div>
<h1>VLM / Multimodal 面试 Cheat Sheet</h1>
<p class="subtitle">公式推导 + From-Scratch 代码 + 25 高频题(L1 必会 · L2 进阶 · L3 顶级 lab)</p>
<p class="byline">By <strong>Ruofeng Yang (杨若峰), Shanghai Jiao Tong University</strong></p>
<div class="meta">
<span><strong>Source:</strong> <code>docs/tutorials/vlm_multimodal_tutorial.md</code></span>
<span><strong>SHA256:</strong> <code>f9cc9510bdd5</code></span>
<span><strong>Rendered:</strong> 2026-05-19 05:47 UTC</span>
</div>
</header>
<h2 id="0-tldr-cheat-sheet">§0 TL;DR Cheat Sheet</h2>
<div class="callout callout-info"><div class="callout-title">8 句话搞定 VLM</div><p>一页拿下视觉-语言模型面试核心要点(详见后文 §1–§13 推导与代码)。</p></div>
<ol><li><strong>视觉 encoder = ViT 主导</strong>Dosovitskiy et al. 2021 (ICLR) 把图像切 $P\times P$ patch(一般 $P=14$ 或 $16$)做线性投影 + 可学习 positional embedding + 可选 <code>[CLS]</code> token,输入 Transformer encoder。<strong>CLIP / SigLIP / LLaVA 的视觉端都是 ViT 变体</strong></li><li><strong>CLIP 对称 InfoNCE(必推)</strong>Radford et al. 2021 (ICML) 让 image embedding $\mathbf{u}_i$ 和 text embedding $\mathbf{v}_i$ 在共享空间里做对比学习,loss 为 <strong>行 softmax + 列 softmax 平均</strong>$\mathcal{L} = \tfrac{1}{2}(\mathcal{L}_{i\to t} + \mathcal{L}_{t\to i})$。温度 $\tau$ 可学习(log-parameterizeclip 到 $[0,100]$)。</li><li><strong>SigLIP 用 sigmoid 替 softmax</strong>Zhai et al. 2023 (ICCV) 把 N×N 相似度矩阵的每一项独立做 binary CE,<strong>摆脱 batch-wise softmax 归一化</strong>,因此对 batch size 不再线性敏感,单机能用 32k+ batch 训;引入 learnable bias $b$ 修正初期 negative dominance。<strong>SigLIP-2 (Google 2025)</strong> 加入 caption + self-distillation + dense local objectives 并扩展多语言。</li><li><strong>LLaVA = projector + 2-stage train</strong>Liu et al. 2023 (NeurIPS) 用一个 <strong>轻量 MLP projector</strong> 把 frozen CLIP 视觉特征投到 LLM token 空间。<strong>Stage 1</strong> 只训 projector 做 feature alignmentcaption 数据),<strong>Stage 2</strong> 解冻 LLM 做 visual instruction tuningGPT-4 生成的 158K instructions)。</li><li><strong>Q-Former vs Projector 是 BLIP-2 的核心 trade-off</strong>Li et al. 2023 (ICML) 用 32 个 learnable query token 在 frozen image encoder 上做 <strong>cross-attention</strong>,把任意分辨率/数量的 patch 压成固定 32 token——计算预算稳定但<strong>信息有损 + 训练复杂</strong>。LLaVA 的 MLP 简单但 token 数随分辨率二次增长。</li><li><strong>Flamingo / Llama-3.2-Vision = gated cross-attn</strong>Alayrac et al. 2022 (NeurIPS) 用 <strong>Perceiver Resampler</strong>64 latent query)把 visual feature 压成固定数 token,再在 LLM 每隔几层插入 <strong>gated cross-attention</strong> 层($\tanh$ 门控初始化为 0,保留 frozen LLM 的 text-only 能力)。</li><li><strong>Qwen2-VL 的 M-RoPE 必考</strong>Wang et al. 2024 把 RoPE 沿 head_dim 分成 6 段,按 axis 序列 $(t, h, w, t, h, w)$ 分配 (t / h / w 三组位置 id);典型配置 <code>mrope_section=[16,24,24]</code>(单位是半 head_dim 的对数,$\sum \times 2 = $ head_dim=128<strong>全部 128 维都旋转</strong>)。这样每个 token 同时携带 (t, h, w) 三维位置而不需要扁平化。<strong>配合 native dynamic resolution</strong>(不再 padding 到固定 224×224)。</li><li><strong>训练三段式 + 偏好优化</strong>(1) <strong>alignment</strong> 训 projector / Q-Former(2) <strong>visual instruction tune</strong> 解冻部分 LLM(3) <strong>preference</strong>LLaVA-RLHF, RLAIF-V, VLM-R1, DPO/PPO)治理幻觉、对齐 long-tail。<strong>VLM-R1 (2025)</strong> 用 GRPO + verifiable reward 把推理能力迁到视觉-语言任务。</li></ol>
<h2 id="1-直觉vlm-在做什么">§1 直觉:VLM 在做什么?</h2>
<p>把一张图看作 "另一种语言"。VLM 的工作可以拆成三件事:</p>
<ul><li><strong>视觉 tokenizer</strong>:把像素压成离散或连续的 token 序列(ViT patch → embedding</li><li><strong>跨模态对齐</strong>:让相同语义的 image / text 在同一空间靠近——这是 CLIP / SigLIP 干的事,本质是 <strong>学一个共享 embedding space</strong></li><li><strong>跨模态生成</strong>:让 LLM 在 prompt 里"看见"图片——LLaVA / Qwen-VL / Flamingo 干的事,本质是 <strong>把 image token 作为前缀塞进 LLM 的 context</strong></li></ul>
<div class="callout callout-info"><div class="callout-title">三种 fusion 范式</div><p>这是 VLM 架构的主线分歧。</p></div>
<ul><li><strong>早期融合(dual-encoder + contrastive</strong>CLIP / SigLIP<strong>没有 cross-modal attention</strong>,只在 embedding 空间靠近 / 远离</li><li><strong>Projector 融合(visual tokens → LLM context</strong>LLaVA / Qwen-VL,把 image embedding 投到 LLM 的 token 空间,<strong>作为输入 token 拼接</strong>,自回归解码</li><li><strong>Cross-attn 融合(image as KV, text as Q</strong>Flamingo / BLIP-2 / Llama-3.2-V,新增 cross-attention 层,<strong>LLM 的 text token 主动 query 视觉 KV</strong></li></ul>
<p>对比 Q/K/V 视角:projector 范式下 image 是 LLM 输入序列的一部分(self-attention 内全交互);cross-attn 范式下 image 永远是 KV<strong>只被 query</strong>——这导致 <strong>inference 时 KV cache 处理方式不同</strong></p>
<h2 id="2-vit把图像变-token-序列">§2 ViT:把图像变 token 序列</h2>
<h3 id="21-patch-tokenize">2.1 Patch tokenize</h3>
<p>输入图像 $\mathbf{x} \in \mathbb{R}^{H \times W \times C}$,切成 $N = HW/P^2$ 个 $P\times P$ patch,每个 patch flatten 成 $P^2 C$ 维向量,过线性层投到 $D$ 维:</p>
<p>$$\mathbf{z}_0 = [\mathbf{x}_\text{class};\ \mathbf{x}^1_p \mathbf{E};\ \mathbf{x}^2_p \mathbf{E};\ \dots;\ \mathbf{x}^N_p \mathbf{E}] + \mathbf{E}_\text{pos}$$</p>
<ul><li>$\mathbf{E} \in \mathbb{R}^{P^2 C \times D}$ 是 patch embedding 矩阵(等价于 stride=$P$, kernel=$P$ 的 Conv2D</li><li>$\mathbf{x}_\text{class} \in \mathbb{R}^D$ 是 <strong>learnable [CLS] token</strong>,用于聚合全局信息(分类时取 $\mathbf{z}_L^0$</li><li>$\mathbf{E}_\text{pos} \in \mathbb{R}^{(N+1) \times D}$ 是 <strong>learnable 1D positional embedding</strong>——原版 ViT 用 1D learned<strong>没有用 2D sinusoidal</strong>(论文 Appendix D.4 报告 1D learned 与 2D sinusoidal 性能差异在误差范围内)</li></ul>
<div class="callout callout-warn"><div class="callout-title">CLIP / SigLIP 不一定用 [CLS]</div><p>CLIP ViT 用 [CLS] 输出,SigLIP / EVA-CLIP / 现代 LLaVA 倾向用 <strong>patch token average pool</strong> 或直接保留所有 patch token 喂给下游。<strong><code>[CLS]</code> 是 ViT 原始 paper 的选择,不是 ViT 的固有部件</strong></p></div>
<h3 id="22-transformer-主干">2.2 Transformer 主干</h3>
<p>$$\mathbf{z}'_\ell = \text{MHA}(\text{LN}(\mathbf{z}_{\ell-1})) + \mathbf{z}_{\ell-1}, \quad \mathbf{z}_\ell = \text{MLP}(\text{LN}(\mathbf{z}'_\ell)) + \mathbf{z}'_\ell$$</p>
<p>Pre-normLN 在 sub-layer 输入端),MLP 用 GELU。注意 ViT 原版的 patch 数固定($224/16=14 \Rightarrow N=196$),<strong>positional embedding 表大小固定</strong>——这是 dynamic resolution 要解决的痛点(§10)。</p>
<h3 id="23-vit-规格梳理">2.3 ViT 规格梳理</h3>
<table><thead><tr><th>模型</th><th>Patch</th><th>Hidden $D$</th><th>Layers</th><th>Heads</th><th>Params</th><th>出处</th></tr></thead><tbody><tr><td>ViT-B/16</td><td>16</td><td>768</td><td>12</td><td>12</td><td>86M</td><td>Dosovitskiy 2021</td></tr><tr><td>ViT-L/14</td><td>14</td><td>1024</td><td>24</td><td>16</td><td>304M</td><td>Dosovitskiy 2021</td></tr><tr><td>ViT-H/14</td><td>14</td><td>1280</td><td>32</td><td>16</td><td>632M</td><td>Dosovitskiy 2021</td></tr><tr><td>ViT-g/14</td><td>14</td><td>1408</td><td>40</td><td>16</td><td>1.0B</td><td>Zhai et al. 2022</td></tr><tr><td>ViT-bigG/14</td><td>14</td><td>1664</td><td>48</td><td>16</td><td>1.8B</td><td>OpenCLIP, 2023</td></tr><tr><td>EVA-02-L/14</td><td>14</td><td>1024</td><td>24</td><td>16</td><td>304M</td><td>Fang 2023</td></tr><tr><td>SigLIP SoViT-400M/14</td><td>14</td><td>1152</td><td>27</td><td>16</td><td>400M</td><td>Alabdulmohsin 2023</td></tr></tbody></table>
<div class="callout callout-info"><div class="callout-title">head_dim 通常固定 64</div><p>ViT 系列大多遵循 head_dim ≈ 6488,等价于 $D / H$。Scaling-laws 推荐 head_dim 不要太小,否则单 head 表达力受限。</p></div>
<h3 id="24-code-vit-patch-embed--主干核心-60-行">2.4 Code: ViT patch embed + 主干(核心 60 行)</h3>
<pre><code class="language-python">import torch
import torch.nn as nn
import torch.nn.functional as F
class PatchEmbed(nn.Module):
def __init__(self, img_size=224, patch_size=16, in_chans=3, embed_dim=768):
super().__init__()
self.img_size, self.patch_size = img_size, patch_size
self.num_patches = (img_size // patch_size) ** 2
# 用 stride=P, kernel=P 的 Conv2d 等价于线性投影
self.proj = nn.Conv2d(in_chans, embed_dim, kernel_size=patch_size, stride=patch_size)
def forward(self, x): # x: [B, C, H, W]
x = self.proj(x) # [B, D, H/P, W/P]
x = x.flatten(2).transpose(1, 2) # [B, N, D]
return x
class ViTBlock(nn.Module):
def __init__(self, dim, num_heads, mlp_ratio=4.0, dropout=0.0):
super().__init__()
self.ln1 = nn.LayerNorm(dim)
self.attn = nn.MultiheadAttention(dim, num_heads, dropout=dropout, batch_first=True)
self.ln2 = nn.LayerNorm(dim)
hidden = int(dim * mlp_ratio)
self.mlp = nn.Sequential(
nn.Linear(dim, hidden), nn.GELU(), nn.Dropout(dropout),
nn.Linear(hidden, dim), nn.Dropout(dropout),
)
def forward(self, x):
h = self.ln1(x)
a, _ = self.attn(h, h, h, need_weights=False) # self-attention
x = x + a
x = x + self.mlp(self.ln2(x))
return x
class ViT(nn.Module):
def __init__(self, img_size=224, patch_size=16, in_chans=3, embed_dim=768,
depth=12, num_heads=12, num_classes=1000, use_cls=True):
super().__init__()
self.patch_embed = PatchEmbed(img_size, patch_size, in_chans, embed_dim)
N = self.patch_embed.num_patches
self.use_cls = use_cls
if use_cls:
self.cls_token = nn.Parameter(torch.zeros(1, 1, embed_dim))
self.pos_embed = nn.Parameter(torch.zeros(1, N + 1, embed_dim))
else:
self.pos_embed = nn.Parameter(torch.zeros(1, N, embed_dim))
nn.init.trunc_normal_(self.pos_embed, std=0.02)
if use_cls:
nn.init.trunc_normal_(self.cls_token, std=0.02)
self.blocks = nn.ModuleList([ViTBlock(embed_dim, num_heads) for _ in range(depth)])
self.ln = nn.LayerNorm(embed_dim)
self.head = nn.Linear(embed_dim, num_classes) if num_classes &gt; 0 else nn.Identity()
def forward(self, x):
B = x.size(0)
x = self.patch_embed(x) # [B, N, D]
if self.use_cls:
cls = self.cls_token.expand(B, -1, -1)
x = torch.cat([cls, x], dim=1) # [B, N+1, D]
x = x + self.pos_embed # broadcast over batch
for blk in self.blocks:
x = blk(x)
x = self.ln(x)
feat = x[:, 0] if self.use_cls else x.mean(dim=1) # CLS or mean-pool
return self.head(feat)</code></pre>
<div class="callout callout-warn"><div class="callout-title">interpolate_pos_embed 的常见 bug</div><p>把 ViT 从 $224^2$ 迁到 $336^2$ 时,pos_embed 表需从 $(14^2 + 1)$ 行 resize 到 $(24^2 + 1)$ 行。<strong>正确做法</strong>:保留 [CLS] 不动,把 patch 部分 reshape 成 $14\times 14\times D$ 做 bicubic 插值到 $24\times 24$,再 flatten 拼回。<strong>踩坑</strong>:直接对 $(N+1)$ 行做 1D 插值会把 [CLS] 当 patch 算进去。</p></div>
<h2 id="3-clip对称-infonce必推导">§3 CLIP:对称 InfoNCE(必推导)</h2>
<h3 id="31-形式化目标">3.1 形式化目标</h3>
<p>CLIPRadford et al. 2021, ICML)训练时一个 batch 有 $N$ 个 (image, text) pair。两个 encoder $f_\theta$image)、$g_\phi$text)分别产出 $\ell_2$-normalized embedding</p>
<p>$$\mathbf{u}_i = \frac{f_\theta(I_i)}{\|f_\theta(I_i)\|_2}, \quad \mathbf{v}_j = \frac{g_\phi(T_j)}{\|g_\phi(T_j)\|_2}, \quad \mathbf{u}_i, \mathbf{v}_j \in S^{D-1}$$</p>
<p>定义相似度矩阵 $\mathbf{S} \in \mathbb{R}^{N\times N}$"logit"):</p>
<p>$$S_{ij} = \frac{\mathbf{u}_i^\top \mathbf{v}_j}{\tau}$$</p>
<p>其中 $\tau > 0$ 是可学习温度(实际工程化为 <code>logit_scale = log(1/τ)</code>,反向传播更稳定,clamp 在 $[\log 1, \log 100]$)。</p>
<h3 id="32-对称-infonce-loss行--列-softmax-平均">3.2 对称 InfoNCE Loss(行 + 列 softmax 平均)</h3>
<p><strong>Image → Text 方向</strong>(对每个 image $i$,正样本是 $T_i$,负样本是 $\{T_j\}_{j\neq i}$):</p>
<p>$$\mathcal{L}_{i\to t} = -\frac{1}{N}\sum_{i=1}^{N} \log \frac{\exp(S_{ii})}{\sum_{j=1}^{N} \exp(S_{ij})}$$</p>
<p><strong>Text → Image 方向</strong></p>
<p>$$\mathcal{L}_{t\to i} = -\frac{1}{N}\sum_{j=1}^{N} \log \frac{\exp(S_{jj})}{\sum_{i=1}^{N} \exp(S_{ij})}$$</p>
<p>对称总 loss</p>
<p>$$\boxed{\;\mathcal{L}_\text{CLIP} = \frac{1}{2}\left(\mathcal{L}_{i\to t} + \mathcal{L}_{t\to i}\right)\;}$$</p>
<div class="callout callout-good"><div class="callout-title">等价表述:行 softmax + 列 softmax 平均</div><p>对矩阵 $\mathbf{S}$<strong>行方向 softmax 后取对角项的 NLL</strong>image→text),<strong>列方向 softmax 后取对角项的 NLL</strong>text→image)。两个 cross-entropy 平均即得 CLIP loss。</p></div>
<h3 id="33-梯度推导为什么对称很重要">3.3 梯度推导(为什么对称很重要)</h3>
<p>固定 $\tau=1$,对 $\mathcal{L}_{i\to t}$ 中第 $i$ 行的 logits $\mathbf{s}_i = (S_{i1},\dots,S_{iN})^\top$ 做 softmax,记 $p_{ij} = \text{softmax}(\mathbf{s}_i)_j$。则:</p>
<p>$$\frac{\partial \mathcal{L}_{i\to t}}{\partial S_{ij}} = \frac{1}{N}\left(p_{ij} - \mathbb{1}[j=i]\right)$$</p>
<ul><li>$j=i$(正样本):梯度 $\propto p_{ii} - 1 < 0$<strong>拉近</strong> $\mathbf{u}_i, \mathbf{v}_i$</li><li>$j \neq i$(负样本):梯度 $\propto p_{ij} > 0$<strong>推远</strong> $\mathbf{u}_i, \mathbf{v}_j$</li></ul>
<p>如果只用单向 $\mathcal{L}_{i\to t}$$\mathbf{v}_j$ 收到的梯度来自所有 $\mathbf{u}_i$,但<strong>不能反过来约束 $\mathbf{u}_i$ 被其他 $\mathbf{v}_k$ 检索时的行为</strong>。对称化补齐了 text→image 检索方向的约束,<strong>避免 embedding space 出现"单向坍塌"</strong>image 端集中但 text 端松散)。</p>
<h3 id="34-temperature-的作用">3.4 Temperature 的作用</h3>
<p>$$\mathcal{L}_{i\to t} = -\frac{1}{N}\sum_i \log\frac{\exp(\mathbf{u}_i^\top \mathbf{v}_i / \tau)}{\sum_j \exp(\mathbf{u}_i^\top \mathbf{v}_j / \tau)}$$</p>
<ul><li>$\tau \to 0^+$<strong>很小</strong>):softmax 接近 one-hot<strong>只关心 hardest negative</strong>(最像但不对的那个 text);梯度被一两个负样本主导,训练不稳</li><li>$\tau \to \infty$<strong>很大</strong>):softmax 均匀,正负样本几乎不可分,loss 接近 $\log N$ 常数,<strong>几乎无梯度</strong></li><li><strong>OpenAI CLIP 学到的稳态</strong>$\tau \approx 0.01$<code>logit_scale ≈ log(100)</code>),并 clamp 上界防止崩</li></ul>
<div class="callout callout-info"><div class="callout-title">InfoNCE 的下界解释</div><p>Oord et al. 2018 (CPC) 证明 InfoNCE 是互信息 $I(U; V)$ 的下界:$I(U; V) \ge \log N - \mathcal{L}_\text{InfoNCE}$。所以<strong>增大 batch size $N$ 同时降低 loss</strong>,相当于直接提升 MI 下界——这是为什么 CLIP / SigLIP 都在追求 huge batch。</p></div>
<h3 id="35-code-clip-对称-infonce核心-50-行">3.5 Code: CLIP 对称 InfoNCE(核心 50 行)</h3>
<pre><code class="language-python">import torch
import torch.nn as nn
import torch.nn.functional as F
class CLIPLoss(nn.Module):
&quot;&quot;&quot;Symmetric InfoNCE used by OpenAI CLIP (Radford et al. 2021).&quot;&quot;&quot;
def __init__(self, init_tau=0.07, max_logit_scale=4.6052):
super().__init__()
# 等价于 logit_scale = log(1/τ); 初始 ~ log(1/0.07) ≈ 2.659
self.logit_scale = nn.Parameter(torch.tensor(1.0 / init_tau).log())
self.max_logit_scale = max_logit_scale # log(100), clamp 防爆炸
def forward(self, image_feats, text_feats):
&quot;&quot;&quot;
image_feats: [N, D] (unnormalized)
text_feats: [N, D]
&quot;&quot;&quot;
# L2 normalize 到单位球面
u = F.normalize(image_feats, dim=-1) # [N, D]
v = F.normalize(text_feats, dim=-1) # [N, D]
# clamp logit_scale 上界 (训练后期会涨到 ~log(100))
logit_scale = self.logit_scale.clamp(max=self.max_logit_scale).exp()
# 相似度矩阵
logits_i2t = logit_scale * u @ v.t() # [N, N]
logits_t2i = logits_i2t.t() # [N, N]
# 对角线是正样本对
N = u.size(0)
labels = torch.arange(N, device=u.device)
loss_i2t = F.cross_entropy(logits_i2t, labels) # 行 softmax NLL
loss_t2i = F.cross_entropy(logits_t2i, labels) # 列 softmax NLL
return 0.5 * (loss_i2t + loss_t2i), logit_scale
# 用法示例(DDP 下需 all-gather 把所有 GPU 的 feats 拼起来再算)
if __name__ == &quot;__main__&quot;:
N, D = 8, 512
img_feats = torch.randn(N, D)
txt_feats = torch.randn(N, D)
criterion = CLIPLoss()
loss, scale = criterion(img_feats, txt_feats)
print(f&quot;loss={loss.item():.4f} logit_scale={scale.item():.2f}&quot;)</code></pre>
<div class="callout callout-warn"><div class="callout-title">DDP 下必须 all-gather 才是真 InfoNCE</div><p>单 GPU 上 batch $N$ 算出来的 loss 只覆盖本地 negatives。生产 CLIPOpenCLIP / OpenAI)会在 forward 后 <code>dist.all_gather</code> 所有 GPU 的 $\mathbf{u}, \mathbf{v}$,让 negative pool = global batch size(如 32k)。<strong>梯度通过 gradient checkpointing + 局部本 GPU 那行/列计算</strong>——这是工程 trick,不是数学问题。</p></div>
<h3 id="36-clip-训练数据--规模">3.6 CLIP 训练数据 &amp; 规模</h3>
<ul><li><strong>WIT (WebImageText)</strong>400M (image, text) pair,从互联网爬取(未开源)</li><li><strong>LAION-400M / LAION-2B</strong>OpenCLIP 用的开源替代,20222023 训了一系列规模</li><li><strong>DataComp</strong>Gadre et al. 2023 (NeurIPS) 提出系统性 data filtering benchmark<strong>数据质量 &gt; 数据规模</strong></li><li>模型规模:OpenAI 最大 ViT-L/14OpenCLIP 训到 ViT-bigG/14 (LAION-2B)<strong>零样本 ImageNet ~80%+</strong></li></ul>
<h3 id="37-clip-的失败模式">3.7 CLIP 的失败模式</h3>
<ul><li><strong>OCR / 文字理解弱</strong>:训练 caption 一般不描述图中文字,所以 CLIP 对图中文本几乎"瞎"</li><li><strong>细粒度计数失败</strong>5 只鸟 vs 6 只鸟在 CLIP embedding 几乎无法区分("counting problem"</li><li><strong>空间关系弱</strong>"cat on top of dog" vs "dog on top of cat" 区分困难(POPE / Winoground benchmark 量化了这一点)</li><li><strong>bag-of-words 倾向</strong>Yuksekgonul et al. 2023 (ICLR) 证明 CLIP 对 caption 词序几乎不敏感</li></ul>
<h2 id="4-siglipsigmoid-替-softmaxbatch-缩放重写">§4 SigLIPsigmoid 替 softmaxbatch 缩放重写</h2>
<h3 id="41-motivation">4.1 Motivation</h3>
<p>CLIP 的 softmax 归一化把所有 N×N 个相似度耦合:每个正样本的梯度依赖于<strong>整行</strong>的负样本 logsumexp。这导致:</p>
<ul><li><strong>batch size 极敏感</strong>N 翻倍,loss landscape 显著变化;小 batch 几乎学不动</li><li><strong>DDP 通信昂贵</strong>:必须 all-gather embeddingO(N·D) 字节),通信瓶颈</li><li><strong>数值不稳</strong>:极大 N 下 softmax 容易溢出</li></ul>
<p>Zhai et al. 2023 (ICCV) 提出 <strong>SigLIP</strong>:把 N×N 矩阵的每一项<strong>独立</strong>做 binary classification。</p>
<h3 id="42-sigmoid-loss-推导">4.2 Sigmoid Loss 推导</h3>
<p>定义相似度 $S_{ij} = t \cdot \mathbf{u}_i^\top \mathbf{v}_j + b$,其中 $t = e^{t'}$ 是 learnable scale(与 CLIP 的 $1/\tau$ 同),$b$ 是 learnable bias(初始化负值,如 $b_0 = -10$,避免训练初期全部预测正)。</p>
<p>label $y_{ij} = +1$ 若 $i=j$$-1$ 否则。每项做 binary logistic regression</p>
<p>$$\mathcal{L}_\text{SigLIP} = -\frac{1}{N}\sum_{i=1}^N \sum_{j=1}^N \log \sigma\!\left(y_{ij} \cdot S_{ij}\right) = \frac{1}{N}\sum_{i=1}^N \sum_{j=1}^N \log\!\left(1 + \exp(-y_{ij} S_{ij})\right)$$</p>
<div class="callout callout-good"><div class="callout-title">关键性质</div><p>每一项 $(i,j)$ 的 loss <strong>不依赖其他项</strong>。所以:</p></div>
<ul><li>batch size 不再耦合所有 negative</li><li>单机可用极大 batchSigLIP 报告单 chip 32k batch 可训)</li><li>通信只需把本机的 query 与远端 key 配对计算 sigmoidchunked all-pair),不需要 logsumexp 同步</li></ul>
<div class="callout callout-warn"><div class="callout-title">bias $b$ 不是装饰</div><p>训练初期 $\mathbf{u}, \mathbf{v}$ 接近随机,$S_{ij}$ 接近 0sigmoid 输出 0.5。负样本占 $N^2 - N \approx N^2$,正样本只占 $N$ 个;如果初始预测全部 ~0.5,<strong>负样本梯度会主导初期训练</strong>。SigLIP 初始化 $b_0 \approx -10$,让 sigmoid 输出初期接近 0,<strong>所有点先被预测为负</strong>,正样本 loss 大、负样本 loss 小,从这个状态再启动训练就稳定了。</p></div>
<h3 id="43-siglip-vs-clip-对比">4.3 SigLIP vs CLIP 对比</h3>
<table><thead><tr><th>维度</th><th>CLIP (softmax)</th><th>SigLIP (sigmoid)</th></tr></thead><tbody><tr><td>Loss 形式</td><td>$\propto$ logsumexp(row) + logsumexp(col)</td><td>$\propto$ $\sum_{ij}$ binary logistic</td></tr><tr><td>Batch 依赖</td><td>强(梯度耦合 batch</td><td>弱(每项独立)</td></tr><tr><td>通信</td><td>all-gather embedding</td><td>chunked all-pair sigmoid</td></tr><tr><td>Bias 项</td><td>无(隐式被 softmax 吸收)</td><td>learnable $b$,初始化 $\approx -10$</td></tr><tr><td>小 batch 表现</td><td>差(&lt; 4k 几乎不学)</td><td>显著更好(1k 也能学)</td></tr><tr><td>大 batch 表现</td><td>边际收益递减</td><td>一直涨到 32k+</td></tr><tr><td>零样本 ImageNetViT-L/14, 400M data</td><td>~75%</td><td>~7678%</td></tr></tbody></table>
<h3 id="44-siglip-2-google-2025">4.4 SigLIP-2 (Google 2025)</h3>
<p>Tschannen et al. 2025 在 SigLIP-1 基础上:</p>
<ul><li>加入 <strong>caption-style decoder</strong>(类似 CapPa)做 captioning 辅助任务</li><li><strong>Self-distillation + dense local objectives</strong>:在 patch 级别做局部对比,提升细节定位能力</li><li><strong>多语言扩展</strong>:训练数据扩到 100+ 语言,多语言 zero-shot 显著提升</li><li>公开 NaFlex 变体支持 native aspect ratio</li></ul>
<h3 id="45-code-siglip-sigmoid-loss核心-35-行">4.5 Code: SigLIP sigmoid loss(核心 35 行)</h3>
<pre><code class="language-python">import torch
import torch.nn as nn
import torch.nn.functional as F
class SigLIPLoss(nn.Module):
&quot;&quot;&quot;Sigmoid Loss for Language Image Pre-training (Zhai et al. 2023).&quot;&quot;&quot;
def __init__(self, init_t=10.0, init_b=-10.0):
super().__init__()
# log-parameterize t for stability; b is a learnable bias
self.t_prime = nn.Parameter(torch.tensor(init_t).log())
self.b = nn.Parameter(torch.tensor(float(init_b)))
def forward(self, image_feats, text_feats):
u = F.normalize(image_feats, dim=-1) # [N, D]
v = F.normalize(text_feats, dim=-1) # [N, D]
t = self.t_prime.exp() # scale &gt; 0
logits = t * (u @ v.t()) + self.b # [N, N]
# y_{ij} = +1 if i == j else -1
N = u.size(0)
labels = 2 * torch.eye(N, device=u.device) - 1 # [N, N], +1 on diag, -1 off
# log(1 + exp(-y * logits)) == -log sigmoid(y * logits)
loss = -F.logsigmoid(labels * logits).sum() / N # SigLIP convention: sum / N
return loss, t, self.b</code></pre>
<div class="callout callout-warn"><div class="callout-title">SigLIP 的 normalize 是 N 不是 N²</div><p>论文 Eq. (1) 的归一化分母是 batch size $N$(每行求和),不是矩阵元素数 $N^2$。<strong>踩坑</strong>:写成 <code>loss.mean()</code> 会得到 1/N² 量级,loss 偏小,learnable scale 收敛错。<strong>正确</strong><code>loss.sum() / N</code></p></div>
<h2 id="5-eva-clip--openclip--其它-clip-变体">§5 EVA-CLIP / OpenCLIP / 其它 CLIP 变体</h2>
<h3 id="51-openclip">5.1 OpenCLIP</h3>
<p>OpenCLIPCherti et al. 2023 CVPR)是 LAION 团队的开源复现 + 扩展:</p>
<ul><li><strong>训练 recipe 开源</strong>:完整 LAION-400M / LAION-2B 训练脚本</li><li><strong>更大规模</strong>ViT-bigG/14 在 LAION-2B 上训出,零样本 ImageNet ~80.1%2023 年 SOTA</li><li><strong>distributed InfoNCE</strong>:实现了 <code>local_loss=True</code> 的 gradient checkpoint,单 GPU 显存只存本地行/列</li></ul>
<h3 id="52-eva-clip">5.2 EVA-CLIP</h3>
<p>EVA-CLIPSun et al. 2023)用 MIM 预训练的 EVA / EVA-02Fang et al. 2023)作为 vision tower 初始化,<strong>显著提升 sample efficiency</strong></p>
<ul><li>ViT-L/14 在 LAION-2B 上训只需 OpenCLIP 1/3 计算预算达到同样精度</li><li><strong>LayerScale + sub-LN + RoPE</strong>EVA-02 视觉端的工程改进</li></ul>
<h3 id="53-datacomp数据-vs-模型-vs-算法">5.3 DataComp(数据 vs 模型 vs 算法)</h3>
<p>Gadre et al. 2023 (NeurIPS) 设计了 "data filtering benchmark":固定 (model, compute),只调 data filter。结论:</p>
<ul><li><strong>CLIP filtering</strong> + <strong>basic filtering</strong> + <strong>image-based</strong> 三种 filter 组合最优</li><li>大模型 (ViT-L/14) 在小数据 (12.8M) 上反而不如 ViT-B<strong>data-limited regime 下大模型过拟合</strong></li></ul>
<h3 id="54-对比一览">5.4 对比一览</h3>
<table><thead><tr><th>方法</th><th>Vision Tower 初始化</th><th>Loss</th><th>Batch</th><th>训练数据</th><th>ImageNet zero-shot</th></tr></thead><tbody><tr><td>CLIP (OpenAI)</td><td>from scratch</td><td>softmax InfoNCE</td><td>32k</td><td>WIT 400M</td><td>76.2% (L/14@336)</td></tr><tr><td>OpenCLIP</td><td>from scratch</td><td>softmax InfoNCE</td><td>90k</td><td>LAION-2B</td><td>80.1% (bigG/14)</td></tr><tr><td>EVA-CLIP</td><td>EVA-02 MIM</td><td>softmax InfoNCE</td><td></td><td>LAION-2B</td><td>82.0% (E/14+)</td></tr><tr><td>SigLIP</td><td>from scratch</td><td>sigmoid</td><td>32k</td><td>WebLI</td><td>82.0% (So400M/14)</td></tr><tr><td>SigLIP-2</td><td>from scratch</td><td>sigmoid + caption + distill</td><td></td><td>WebLI 10B</td><td>84%+</td></tr><tr><td>MetaCLIP</td><td>from scratch</td><td>softmax InfoNCE</td><td></td><td>重新构造 LAION-grade</td><td>79.2% (H/14)</td></tr></tbody></table>
<div class="callout callout-info"><div class="callout-title">20242025 趋势</div><p>SigLIP 系列在 zero-shot ImageNet 和下游 retrieval 上已经稳定超过 CLIP;典型开放权重 VLM 用 SigLIP-So400M 的是 <strong>PaliGemma / LLaVA-OneVision / Molmo</strong><strong>InternVL 系列用自家的 InternViTQwen2-VL 用自训 ViTLLaVA-1.5/1.6 仍用 CLIP ViT-L/14</strong>——并非"切到 SigLIP"是行业共识。</p></div>
<h2 id="6-llavaprojector--2-stage-训练">§6 LLaVAprojector + 2-stage 训练</h2>
<h3 id="61-架构">6.1 架构</h3>
<p>LLaVALiu et al. 2023 NeurIPS)的核心是三件套:</p>
<pre class="diagram"><code>Image ──► CLIP ViT-L/14 ──► visual features z_v ∈ R^{N × d_v}
│ W ∈ R^{d_v × d_LLM} ← MLP projector
H_v ∈ R^{N × d_LLM}
│ 与 text embedding 拼接
Text tokens ──► tokenizer ──► H_t ──► [&lt;bos&gt;, H_v, H_t] ──► LLM (Vicuna / LLaMA-2)
autoregressive response</code></pre>
<ul><li><strong>Vision tower</strong>CLIP ViT-L/14frozen,取倒数第二层 patch token)。LLaVA-1.0 用 $224^2$ 输入得 $N=256$<strong>LLaVA-1.5 升级到 $336^2$ 得 $N=576$</strong></li><li><strong>Projector</strong> $W$LLaVA-1.0 用单层 Linear<strong>LLaVA-1.5 升级为 2-layer MLP + GELU</strong>(原文报告显著提升 instruction following</li><li><strong>LLM</strong>Vicuna-13BLLaVA-1.0/1.5)或 LLaMA-2</li></ul>
<h3 id="62-训练两阶段">6.2 训练两阶段</h3>
<p><strong>Stage 1: Feature Alignment Pre-training</strong></p>
<ul><li>用 CC3M / LAION-558K caption 数据,格式 <code>&lt;image&gt;\n&lt;caption&gt;</code></li><li><strong>只训 projector $W$</strong>,冻结 vision tower 和 LLM</li><li>目的:让 visual feature 投到 LLM 的 word embedding 空间附近</li></ul>
<p><strong>Stage 2: End-to-end Visual Instruction Tuning</strong></p>
<ul><li>用 GPT-4 生成的 158K 视觉 instructionLLaVA-Instruct</li><li>解冻 projector + LLM<strong>冻结 vision tower</strong></li><li>LLM 学会"理解图像、回答问题、follow visual instruction"</li></ul>
<h3 id="63-code-llava-风格-projector--forward核心-60-行">6.3 Code: LLaVA 风格 projector + forward(核心 60 行)</h3>
<pre><code class="language-python">import torch
import torch.nn as nn
class LLaVAProjector(nn.Module):
&quot;&quot;&quot;2-layer MLP + GELU, as in LLaVA-1.5.&quot;&quot;&quot;
def __init__(self, d_vision=1024, d_llm=4096):
super().__init__()
self.fc1 = nn.Linear(d_vision, d_llm)
self.act = nn.GELU()
self.fc2 = nn.Linear(d_llm, d_llm)
def forward(self, x): # x: [B, N, d_vision]
return self.fc2(self.act(self.fc1(x))) # [B, N, d_llm]
class LLaVA(nn.Module):
&quot;&quot;&quot;Skeleton: CLIP vision tower + projector + LLM.&quot;&quot;&quot;
def __init__(self, vision_tower, projector, llm, image_token_id):
super().__init__()
self.vision_tower = vision_tower # CLIPViT, frozen at stage 1
self.projector = projector
self.llm = llm # e.g. LlamaForCausalLM
self.image_token_id = image_token_id # special &lt;image&gt; placeholder
@torch.no_grad()
def encode_image(self, pixel_values):
# 取倒数第二层 patch features (不取 [CLS])
vit_out = self.vision_tower(pixel_values, output_hidden_states=True)
feat = vit_out.hidden_states[-2][:, 1:, :] # drop CLS, [B, N, d_v]
return feat
def forward(self, input_ids, pixel_values, labels=None, attention_mask=None):
# 1. 视觉特征 → projector → LLM 维度
with torch.no_grad():
visual_features = self.encode_image(pixel_values) # [B, N, d_v]
visual_tokens = self.projector(visual_features) # [B, N, d_llm]
# 2. LLM 的 word embedding 表
token_embeds = self.llm.get_input_embeddings()(input_ids) # [B, L, d_llm]
# 3. 把 &lt;image&gt; placeholder 处替换为 visual_tokens
B, L, D = token_embeds.shape
new_embeds, new_labels, new_mask = [], [], []
for b in range(B):
image_pos = (input_ids[b] == self.image_token_id).nonzero(as_tuple=True)[0]
assert image_pos.numel() == 1, &quot;exactly one &lt;image&gt; placeholder expected&quot;
i = image_pos.item()
# 拼接:[prefix tokens] + [N visual tokens] + [suffix tokens]
chunks = [token_embeds[b, :i], visual_tokens[b], token_embeds[b, i+1:]]
new_embeds.append(torch.cat(chunks, dim=0))
if labels is not None:
lab = labels[b]
# visual token 位置 label = -100(不算 loss
ignore = torch.full((visual_tokens.size(1),), -100, dtype=lab.dtype, device=lab.device)
new_labels.append(torch.cat([lab[:i], ignore, lab[i+1:]], dim=0))
if attention_mask is not None:
am = attention_mask[b]
ones = torch.ones(visual_tokens.size(1), dtype=am.dtype, device=am.device)
new_mask.append(torch.cat([am[:i], ones, am[i+1:]], dim=0))
# 4. pad 回 batch tensor,喂给 LLM
inputs_embeds = torch.nn.utils.rnn.pad_sequence(new_embeds, batch_first=True)
labels = torch.nn.utils.rnn.pad_sequence(new_labels, batch_first=True, padding_value=-100) if labels is not None else None
attention_mask = torch.nn.utils.rnn.pad_sequence(new_mask, batch_first=True) if attention_mask is not None else None
return self.llm(inputs_embeds=inputs_embeds, labels=labels, attention_mask=attention_mask)</code></pre>
<h3 id="64-llava-15--16--next-关键升级">6.4 LLaVA-1.5 / 1.6 / NeXT 关键升级</h3>
<table><thead><tr><th>版本</th><th>时间</th><th>主要改动</th></tr></thead><tbody><tr><td>LLaVA-1.0</td><td>2023.04</td><td>单层 Linear projectorCLIP ViT-L/14@224²,视觉 token = 256$16\times 16$</td></tr><tr><td>LLaVA-1.5</td><td>2023.10</td><td>2-layer MLP;分辨率升到 336²,视觉 token = 576$24\times 24$);加入 OCR / GQA / VQAv2 等学术数据</td></tr><tr><td>LLaVA-1.6 / NeXT</td><td>2024.01</td><td><strong>AnyRes</strong>:把图切成 $2\times 2 / 2\times 3 / \dots$ tile 各编码再拼,支持任意 aspect ratiotoken 数最多 2880</td></tr><tr><td>LLaVA-OneVision</td><td>2024.08</td><td>单 / 多图 / 视频统一;引入 SIsingle image+ OVonevision)数据 mix</td></tr><tr><td>LLaVA-NeXT-Video</td><td>2024.04</td><td>视频版,把多帧 visual feature 序列化喂入</td></tr></tbody></table>
<div class="callout callout-info"><div class="callout-title">AnyRes (LLaVA-1.6) 的核心 trick</div><p>训练时假设 fixed 336²;推理时把高分辨率图切成 $n \times m$ 个 336² tile 各自编码,再加一份缩放到 336² 的"全局缩略图"。<strong>token 数从 576 涨到 (1 + n·m)·576</strong>,但每个 tile 仍走同一个 frozen ViT。<strong>和 InternVL / Qwen-VL 的 tiling 是同一类思路</strong></p></div>
<h2 id="7-blip-2q-former-cross-attention">§7 BLIP-2Q-Former cross-attention</h2>
<h3 id="71-motivation">7.1 Motivation</h3>
<p>LLaVA 的 projector 简单但<strong>每个 patch 都成为 LLM token</strong>:分辨率 ↑ token 数 ↑ LLM 计算 $O(L^2)$ ↑。BLIP-2Li et al. 2023 ICML)用 <strong>Q-Former</strong>Querying Transformer)把任意数量 patch <strong>压成固定 32 token</strong></p>
<h3 id="72-q-former-结构">7.2 Q-Former 结构</h3>
<p>输入:frozen image encoder 输出 $\mathbf{Z} \in \mathbb{R}^{N \times d_v}$N=257 for ViT-g/14@224)。Q-Former 有 32 个 <strong>learnable query token</strong> $\mathbf{q}_1, \dots, \mathbf{q}_{32} \in \mathbb{R}^{d_q}$。</p>
<p>每层 Q-Former block</p>
<p>$$\mathbf{q}^{(\ell)} = \text{SelfAttn}(\mathbf{q}^{(\ell-1)})$$ $$\mathbf{q}^{(\ell)} = \text{CrossAttn}(\mathbf{q}^{(\ell)},\ \mathbf{Z},\ \mathbf{Z})\quad \text{(只插在偶数层)}$$ $$\mathbf{q}^{(\ell)} = \text{FFN}(\mathbf{q}^{(\ell)})$$</p>
<p><strong>关键</strong></p>
<ul><li><strong>Self-attention 内</strong>:query 之间互相交流,不和 image patch 交互</li><li><strong>Cross-attention 内</strong>query 作 Qimage patch 作 K/V——<strong>这是信息流入口</strong></li><li>输出 $\mathbf{q}^{(L)} \in \mathbb{R}^{32 \times d_q}$ 再过一个 Linear 投到 LLM 维度,作 32 个 visual token 喂给 frozen LLM</li></ul>
<h3 id="73-两阶段训练">7.3 两阶段训练</h3>
<p><strong>Stage 1: Representation Learning</strong>(只训 Q-Formerfrozen vision encoder</p>
<ul><li>ITC (Image-Text Contrastive)query embedding 与 text 端 [CLS] 做 CLIP 风格对比</li><li>ITM (Image-Text Matching)query 与 text token 在 cross-attn 内交互后做 binary classification</li><li>ITG (Image-grounded Text Generation)query 不与 text 交互,让 text decoder 基于 query 生成 captioncausal mask 控制可见性)</li></ul>
<p><strong>Stage 2: Generative Learning</strong>(只训 Q-Formerfrozen LLM</p>
<ul><li>把 Q-Former 输出投到 LLM embedding 空间,<strong>让 LLM 在 prefix-tune 模式下根据 32 visual token 做 captioning / VQA</strong></li></ul>
<h3 id="74-code-q-former-cross-attention-单层核心-40-行">7.4 Code: Q-Former cross-attention 单层(核心 40 行)</h3>
<pre><code class="language-python">import torch
import torch.nn as nn
class QFormerLayer(nn.Module):
&quot;&quot;&quot;One Q-Former block: SelfAttn (queries) -&gt; CrossAttn (queries &lt;- image) -&gt; FFN.&quot;&quot;&quot;
def __init__(self, d_q=768, d_v=1408, num_heads=12, mlp_ratio=4, has_cross=True):
super().__init__()
self.has_cross = has_cross
self.ln_self = nn.LayerNorm(d_q)
self.self_attn = nn.MultiheadAttention(d_q, num_heads, batch_first=True)
if has_cross:
self.ln_cross = nn.LayerNorm(d_q)
# Q 来自 query (d_q), K/V 来自 image feat (d_v) -&gt; 通过 kdim/vdim 适配
self.cross_attn = nn.MultiheadAttention(d_q, num_heads,
kdim=d_v, vdim=d_v, batch_first=True)
self.ln_ffn = nn.LayerNorm(d_q)
hidden = int(d_q * mlp_ratio)
self.ffn = nn.Sequential(nn.Linear(d_q, hidden), nn.GELU(), nn.Linear(hidden, d_q))
def forward(self, q, image_feats=None): # q: [B, 32, d_q]
# Self-attention: queries talk to each other
h = self.ln_self(q)
a, _ = self.self_attn(h, h, h, need_weights=False)
q = q + a
# Cross-attention: queries attend to image patches
if self.has_cross and image_feats is not None:
h = self.ln_cross(q)
a, _ = self.cross_attn(h, image_feats, image_feats, need_weights=False)
q = q + a
# FFN
q = q + self.ffn(self.ln_ffn(q))
return q
class QFormer(nn.Module):
def __init__(self, num_queries=32, d_q=768, d_v=1408, depth=12, num_heads=12,
cross_every=2):
super().__init__()
self.queries = nn.Parameter(torch.zeros(1, num_queries, d_q))
nn.init.trunc_normal_(self.queries, std=0.02)
self.layers = nn.ModuleList([
QFormerLayer(d_q, d_v, num_heads, has_cross=(i % cross_every == 0))
for i in range(depth)
])
def forward(self, image_feats): # [B, N, d_v]
B = image_feats.size(0)
q = self.queries.expand(B, -1, -1) # [B, 32, d_q]
for layer in self.layers:
q = layer(q, image_feats)
return q # [B, 32, d_q]</code></pre>
<div class="callout callout-warn"><div class="callout-title">`kdim`/`vdim` 适配</div><p><code>nn.MultiheadAttention</code> 默认 K/V 输入维度等于 <code>embed_dim</code>Q-Former cross-attn 里 query 768 维、image feat 1408 维,<strong>必须显式传 <code>kdim=d_v, vdim=d_v</code></strong>,否则 PyTorch 会在 forward 时按 768 期望 K/V 输入,直接抛 shape mismatch 错误(不是悄悄截断)。</p></div>
<h3 id="75-q-former-vs-llava-projectortrade-off">7.5 Q-Former vs LLaVA Projectortrade-off</h3>
<table><thead><tr><th>维度</th><th>LLaVA Projector</th><th>BLIP-2 Q-Former</th></tr></thead><tbody><tr><td>参数量</td><td>~20M (MLP)</td><td>~180M (Q-Former + queries)</td></tr><tr><td>计算</td><td>仅 MLP forward</td><td>12 层 cross-attn forward</td></tr><tr><td>视觉 token 数</td><td>$N$(随分辨率二次增长)</td><td>固定 32</td></tr><tr><td>信息损失</td><td>几乎 0(每个 patch 都进 LLM</td><td>显著(256+ patch 压成 32</td></tr><tr><td>训练复杂度</td><td>1 stagepretrain+ 1 stageIT</td><td>2 stage(表征 + 生成);stage 1 同时优化 ITC + ITM + ITG 三个 loss</td></tr><tr><td>LLM context 占用</td><td>大(5762880 token</td><td>小(32 token</td></tr><tr><td>适合场景</td><td>高分辨率 / 细节任务</td><td>LLM context 紧张 / 多模态批量推理</td></tr></tbody></table>
<div class="callout callout-info"><div class="callout-title">20242025 主流回归 projector</div><p>Qwen-VL / LLaVA-NeXT / InternVL-2 / DeepSeek-VL2 几乎都用 projector(带 spatial reduction / pixel shuffle 控 token 数),<strong>Q-Former 在工业 VLM 中淡出</strong>。但 Q-Former 思路在 video VLM 里仍活跃(用 query 做 frame-level pooling)。</p></div>
<h2 id="8-flamingoperceiver-resampler--gated-cross-attn">§8 FlamingoPerceiver Resampler + Gated Cross-Attn</h2>
<h3 id="81-设计目标">8.1 设计目标</h3>
<p>Alayrac et al. 2022 (NeurIPS) 想要:<strong>在 frozen 70B LLM 上加视觉能力,不破坏文本能力</strong>。设计选择:</p>
<ul><li><strong>不重训 LLM</strong>:完全 frozen,只在中间插入新层</li><li><strong>少量可训参数</strong>cross-attn 层 + Perceiver Resampler</li><li><strong>few-shot interleaved</strong>:训练数据是 <code>(image, text, image, text, ...)</code> 交错序列</li></ul>
<h3 id="82-perceiver-resampler">8.2 Perceiver Resampler</h3>
<p>类似 Q-Former 的"用 latent query 压缩 image"。Flamingo 原论文 Sec 3.1 的伪代码是<strong>多层堆叠</strong>(每层 = cross-attention + FFN),论文默认配置约 $L=6$ 层。每一层的更新规则:</p>
<p>$$\mathbf{q}^{(\ell+1)} = \mathbf{q}^{(\ell)} + \text{CrossAttn}\!\left(\mathbf{q}^{(\ell)},\ [\mathbf{q}^{(\ell)};\ \mathbf{Z}],\ [\mathbf{q}^{(\ell)};\ \mathbf{Z}]\right), \quad \mathbf{q}^{(\ell+1)} = \mathbf{q}^{(\ell+1)} + \text{FFN}(\mathbf{q}^{(\ell+1)})$$</p>
<p>注意 K/V 是 <code>concat(query, image_feat)</code> 而不只是 <code>image_feat</code>——让 query token 也能互相 attend。<strong>整体仍然比 BLIP-2 Q-Former (12 层 + self-attn + cross-every-2) 轻</strong>。输出 64 个 latent visual token(不依赖输入 patch 数)。</p>
<h3 id="83-gated-cross-attention核心创新">8.3 Gated Cross-Attention(核心创新)</h3>
<p>在 LLM 每隔 $k$ 层(如每 4 层)插入一个新的 cross-attention 模块:</p>
<p>$$\mathbf{h}'_\ell = \mathbf{h}_\ell + \tanh(\alpha_\text{attn}) \cdot \text{CrossAttn}(\mathbf{h}_\ell, \mathbf{q}_\text{out}, \mathbf{q}_\text{out})$$ $$\mathbf{h}''_\ell = \mathbf{h}'_\ell + \tanh(\alpha_\text{ffn}) \cdot \text{FFN}(\mathbf{h}'_\ell)$$</p>
<p><strong>关键</strong>$\alpha_\text{attn}, \alpha_\text{ffn}$ 是 learnable scalar<strong>初始化为 0</strong>。所以 $\tanh(0)=0$,新增 cross-attn 在初始化时<strong>对 LLM 输出零贡献</strong>——LLM 表现与未加视觉模块的 frozen LLM 完全一致。训练时 $\alpha$ 逐渐学到非零值,视觉信息开始注入。</p>
<div class="callout callout-good"><div class="callout-title">这就是&quot;residual 嫁接&quot;</div><p>Llama-3.2-Vision (Meta 2024) 沿用了完全相同的设计:frozen LLaMA-3 + 学一个 gated cross-attn adapter。优点是<strong>完全保留 text-only 性能</strong>,缺点是<strong>视觉能力上限低于 fine-tune LLM 的 LLaVA/Qwen-VL</strong></p></div>
<h3 id="84-flamingo--llama-32-v-vs-llava-对比">8.4 Flamingo / Llama-3.2-V vs LLaVA 对比</h3>
<table><thead><tr><th>方面</th><th>Flamingo / Llama-3.2-V</th><th>LLaVA / Qwen-VL</th></tr></thead><tbody><tr><td>LLM 是否解冻</td><td><strong></strong>frozen</td><td>是(stage 2 解冻)</td></tr><tr><td>Image 作 token</td><td>否(作 KV</td><td><strong></strong>(作 token</td></tr><tr><td>Text-only 能力保留</td><td>✅ 完全保留</td><td>⚠️ 可能轻微退化</td></tr><tr><td>视觉理解上限</td><td>受限于 cross-attn 容量</td><td>更高(LLM 可"思考"图像)</td></tr><tr><td>训练数据</td><td>interleaved</td><td>image-instruction pair</td></tr><tr><td>适用场景</td><td>大 LLM + 不想重训</td><td>中小 LLM + 视觉为核心</td></tr></tbody></table>
<h2 id="9-cogvlm-与视觉专家--cross-attn-fusion-变体">§9 CogVLM 与"视觉专家" / Cross-attn fusion 变体</h2>
<h3 id="91-cogvlm视觉专家分支">9.1 CogVLM:视觉专家分支</h3>
<p>Wang et al. 2023 (CogVLM) 的核心:在 LLM 的 attention / FFN 里<strong>复制一份并行分支</strong>,专门处理 visual token<strong>与原 text 分支共享 attention 计算但走不同 projection</strong></p>
<pre class="diagram"><code> attention
┌──────────────┴──────────────┐
↓ ↓
text projection (frozen) vision expert projection (trainable)
│ │
└──────────────┬──────────────┘
token-wise route: if visual_token, use vision branch</code></pre>
<ul><li><strong>类似 MoE 的双专家</strong>text token 走 text 分支,image token 走"visual expert"分支</li><li><strong>vision expert 单独训练</strong>text 分支可 frozen,视觉能力来自 expert</li><li><strong>保留 LLM 原生 text 能力</strong>(与 Flamingo 同思路,但 routing 粒度是 token 而非 layer</li></ul>
<h3 id="92-llama-32-visionflamingo-style-cross-attn-在大-llm-上的复活">9.2 Llama-3.2 VisionFlamingo-style cross-attn 在大 LLM 上的复活</h3>
<p>2024 年 9 月 Meta 发布 Llama-3.2-V (11B / 90B)</p>
<ul><li>Frozen Llama-3 backbone</li><li>加入 <strong>separate cross-attention layers</strong>(不是改 self-attn</li><li>Adapter-style 训练,<strong>只在 cross-attn 层学</strong></li><li>设计为"长尾视觉任务的 robust 基座",视觉能力上限略低于 GPT-4V,但 <strong>text-only benchmark 与 Llama-3 文本版几乎一致</strong></li></ul>
<h3 id="93-claude-3537-sonnet-vision-与-gpt-4v4o">9.3 Claude 3.5/3.7 Sonnet Vision 与 GPT-4V/4o</h3>
<p>Anthropic / OpenAI 的闭源模型架构未公开,但从 API 行为推断:</p>
<ul><li><strong>GPT-4V (2023.09) / GPT-4o (2024.05)</strong>4o 是 native multimodal,从底层就联合训练 (image + text + audio)<strong>不是 LLaVA 式后接 projector</strong></li><li><strong>Claude 3.5/3.7 Sonnet (2024-2025)</strong>:支持高分辨率图像(最多 8000×8000 像素,按需 tile),文档理解 (PDF/screenshot) 是其卖点之一</li><li><strong>共同特征</strong>:能处理多页文档 / 截屏 / OCR 数学公式——远超 LLaVA 系列。这暗示它们在训练数据规模(document corpus+ tiling 策略上有重大优化</li></ul>
<h2 id="10-qwen2-vl--deepseek-vl动态分辨率--m-rope">§10 Qwen2-VL / DeepSeek-VL:动态分辨率 + M-RoPE</h2>
<h3 id="101-native-dynamic-resolution">10.1 Native Dynamic Resolution</h3>
<p>Qwen2-VL (Wang et al. 2024)、DeepSeek-VL (Lu et al. 2024)、InternVL-2 都抛弃了"resize 到固定 224²"的传统:</p>
<ul><li><strong>保留原始 aspect ratio</strong>:把图按 patch_size 的整数倍 resize 到接近原尺寸的最大值</li><li><strong>patch 数动态</strong>:一张 $1024 \times 768$ 图按 $P=14$ 切成 $73 \times 54 \approx 3942$ 个 patch</li><li><strong>不再使用固定 pos embed 表</strong>:必须用 <strong>可扩展的位置编码</strong>RoPE 或 2D ALiBi-like</li></ul>
<h3 id="102-m-ropemultimodal-rope">10.2 M-RoPEMultimodal RoPE</h3>
<p>Qwen2-VL 的核心创新。<strong>回顾普通 1D RoPE</strong>:把 query / key 的每对维度 $(2k, 2k+1)$ 看作复数,乘上位置相关的旋转:</p>
<p>$$\mathbf{R}_{m,k} = \begin{pmatrix} \cos(m\theta_k) & -\sin(m\theta_k) \\ \sin(m\theta_k) & \cos(m\theta_k) \end{pmatrix}, \quad \theta_k = 10000^{-2k/d}$$</p>
<p>应用到 $\mathbf{q}_m$ 后,$\mathbf{q}_m^\top \mathbf{k}_n$ 只依赖 $m - n$(相对位置)。</p>
<p><strong>M-RoPE 的扩展</strong>:一个视觉 token 有 (t, h, w) 三个位置维度。<strong>所有 head_dim 都旋转</strong>——但每对维度 $(2k, 2k+1)$ 根据所在区段,用 t / h / w 三个位置 id 之一参与旋转角度:</p>
<p>$$(\cos(m_\text{axis}\,\theta_k),\ \sin(m_\text{axis}\,\theta_k)), \quad \text{axis} \in \{t, h, w\}$$</p>
<p>具体地,Qwen2-VL 的 <code>mrope_section</code><strong>单位是半 head_dim 对</strong>,即每个数代表多少对 $(2k, 2k+1)$)。一对 = 2 个实数维度,所以"section sum × 2 = head_dim"。</p>
<div class="callout callout-info"><div class="callout-title">Qwen2-VL 默认 `mrope_section = [16, 24, 24]`</div><p>即三个 axis 各占 16 / 24 / 24 对维度;总 $(16+24+24) \times 2 = 128 = $ head_dim。实现上把 section 翻倍成 $[16, 24, 24, 16, 24, 24]$ 沿 head_dim 切,分别用 (t, h, w, t, h, w) 的位置 id 旋转——<strong>全部 128 维都参与旋转</strong>,没有"不旋转 dim"。空间维(h, w)占 48 对 &gt; 时间维(t)的 16 对,反映视频帧间变化慢、空间内容变化剧烈。</p></div>
<p>文本 token 没有显式 (h, w)Qwen2-VL 让 $m_t = m_h = m_w$ 等于该 text token 的 1D 位置 id,三个 axis 给出<strong>完全相同的旋转角</strong>,等价于普通 1D RoPE。</p>
<h3 id="103-qwen25-vl-升级">10.3 Qwen2.5-VL 升级</h3>
<p>Qwen2.5-VLBai et al. 2025)在 Qwen2-VL 基础上:</p>
<ul><li><strong>绝对时间编码</strong>:M-RoPE 的 t 维改用真实时间戳(秒),不是帧 index,<strong>支持任意 FPS 视频</strong></li><li><strong>动态视觉 token budget</strong>:根据任务复杂度调 token 数</li><li><strong>agent / GUI 能力</strong>:训练数据加入 web screenshot / mobile UI 操作 trace</li></ul>
<h3 id="104-deepseek-vl--vl2高分辨率-tiling--hybrid-encoder">10.4 DeepSeek-VL / VL2:高分辨率 tiling + Hybrid encoder</h3>
<p>DeepSeek-VL (Lu et al. 2024) 用<strong>双 vision encoder</strong></p>
<ul><li><strong>SigLIP</strong>:处理全局语义(低分辨率)</li><li><strong>SAM-B</strong>Segment Anything backbone):处理高分辨率细节</li></ul>
<p>两路特征 concat 喂给 projector + LLM。<strong>DeepSeek-VL2 (2024.12)</strong> 进一步换成 MoE LLM + 动态分辨率,单 image 视觉 token 可达 1700+。</p>
<h3 id="105-code-m-rope-三维位置嵌入核心-50-行对齐-qwen2-vl-hf-实现">10.5 Code: M-RoPE 三维位置嵌入(核心 50 行,对齐 Qwen2-VL HF 实现)</h3>
<pre><code class="language-python">import torch
def build_mrope_cos_sin(positions, head_dim, mrope_section=(16, 24, 24), base=10000.0):
&quot;&quot;&quot;
Build cos/sin tensors for Qwen2-VL style M-RoPE.
positions: LongTensor [3, B, L] (axis 0: t / h / w; B batch; L seq len)
head_dim: per-head dim (must equal 2 * sum(mrope_section))
mrope_section: tuple of 3 ints; each = number of (half-dim) entries per axis
Returns: cos, sin both [B, L, head_dim], ready for LLaMA-style rotate_half.
&quot;&quot;&quot;
assert 2 * sum(mrope_section) == head_dim, &quot;2 * sum(mrope_section) must = head_dim&quot;
half = head_dim // 2 # = sum(mrope_section)
# 标准 RoPE 频率: θ_k = base^{-2k/head_dim}, k = 0..half-1
inv_freq = 1.0 / (base ** (torch.arange(0, half).float() * 2 / head_dim)) # [half]
inv_freq = inv_freq.to(positions.device)
# 对每个 axis 算 [B, L, half] 的 angle / cos / sin
cos_axes, sin_axes = [], []
for a in range(3):
ang = positions[a].float().unsqueeze(-1) * inv_freq # [B, L, half]
cos_axes.append(ang.cos())
sin_axes.append(ang.sin())
# 把 half-dim 按 mrope_section 切成 3 段,分别取 t/h/w 的 cos/sin
cos_chunks, sin_chunks = [], []
offset = 0
for axis, s in enumerate(mrope_section):
cos_chunks.append(cos_axes[axis][..., offset:offset+s]) # [B, L, s]
sin_chunks.append(sin_axes[axis][..., offset:offset+s])
offset += s
cos_half = torch.cat(cos_chunks, dim=-1) # [B, L, half]
sin_half = torch.cat(sin_chunks, dim=-1)
# LLaMA-RoPE 风格 duplicate 到 full head_dim
cos = torch.cat([cos_half, cos_half], dim=-1) # [B, L, head_dim]
sin = torch.cat([sin_half, sin_half], dim=-1)
return cos, sin
def rotate_half(x):
&quot;&quot;&quot;(x1, x2) -&gt; (-x2, x1), LLaMA convention.&quot;&quot;&quot;
x1, x2 = x.chunk(2, dim=-1)
return torch.cat((-x2, x1), dim=-1)
def apply_mrope(q, k, cos, sin):
&quot;&quot;&quot;
q, k: [B, num_heads, L, head_dim]
cos, sin:[B, L, head_dim]
&quot;&quot;&quot;
cos = cos.unsqueeze(1) # broadcast over heads
sin = sin.unsqueeze(1)
q_rot = q * cos + rotate_half(q) * sin
k_rot = k * cos + rotate_half(k) * sin
return q_rot, k_rot</code></pre>
<div class="callout callout-warn"><div class="callout-title">M-RoPE 三个常见误读</div><p>容易踩的坑。</p></div>
<ul><li><strong>"head_dim 切成 t/h/w 三段独立 1D RoPE"</strong>:错。Qwen2-VL 实际是 <strong>6 段 alternating</strong> <code>[s_t, s_h, s_w, s_t, s_h, s_w]</code>,全 head_dim 都旋转</li><li><strong>"section 单位是 dim"</strong>:错。<code>mrope_section=[16,24,24]</code> 单位是 <strong>对数</strong>(每对 = 2 个 head_dim 元素),$\sum \times 2 = $ head_dim = 128</li><li><strong>"text token 没有 (h, w) 怎么办?"</strong>:让 $m_t = m_h = m_w$ 等于 text 的 1D 位置 id,三个 axis 旋转角相同,退化回 1D RoPE</li></ul>
<h2 id="11-video-vlmlongva--videollama--长视频问题">§11 Video VLMLongVA / VideoLLaMA / 长视频问题</h2>
<h3 id="111-基本-pipeline">11.1 基本 pipeline</h3>
<p>视频 = 多帧 image。VLM 处理视频的常见做法:</p>
<ol><li><strong>均匀采样 $K$ 帧</strong>(如 8 / 16 / 32</li><li><strong>每帧过 vision encoder</strong> → 每帧 $N$ 个 patch token</li><li><strong>token 序列拼接</strong>$K \cdot N$ 个 visual token 喂入 LLM</li></ol>
<p>问题:$K=32, N=576 \Rightarrow 18432$ token,单图 instruction tuning 的 LLM context 撑不住。</p>
<h3 id="112-常见压缩策略">11.2 常见压缩策略</h3>
<ul><li><strong>Time pooling</strong>:相邻帧 token average poolVideoChat / VideoLLaMA</li><li><strong>Q-Former resampler</strong>:每帧用 query token 压成 32Video-BLIP</li><li><strong>Token merge</strong>:跨帧合并相似 tokenVideoLLaMA 2</li><li><strong>Spatial pooling + temporal preservation</strong>:每帧 patch token 池化到 $H' \times W'$,保留所有帧(LLaVA-NeXT-Video</li></ul>
<h3 id="113-longva--long-context-video">11.3 LongVA / Long-context video</h3>
<p>LongVA (Zhang et al. 2024) 等利用 long-context LLM (200K+ token) 直接吃<strong>长视频展开的 token 序列</strong>,配合 M-RoPE 的时间维度做几小时视频问答。Qwen2-VL 报告可处理 20 分钟视频;Qwen2.5-VL 推到 1 小时+。</p>
<h3 id="114-video-benchmark">11.4 Video benchmark</h3>
<ul><li><strong>MVBench</strong> (Li et al. 2024)20 个 fine-grained video understanding 任务</li><li><strong>Video-MME</strong> (Fu et al. arXiv 2024 / CVPR 2025)900+ video,包含 3 个时长档(短/中/长)+ 6 类任务</li><li><strong>EgoSchema</strong> (Mangalam et al. 2023 NeurIPS):第一人称长视频</li><li><strong>LongVideoBench</strong> (Wu et al. 2024 NeurIPS):从 8 秒到 1 小时</li></ul>
<h2 id="12-训练-pipelinealignment--instruct--preference">§12 训练 pipelinealignment / instruct / preference</h2>
<h3 id="121-stage-1alignment--pre-training">12.1 Stage 1Alignment / Pre-training</h3>
<p>目的:让视觉特征"对齐"到 LLM token 空间附近。</p>
<ul><li><strong>数据</strong>image-caption pairCC3M, LAION-558K, ShareGPT4V</li><li><strong>训练</strong>:只解冻 projectorLLaVA/ Q-Former (BLIP-2)<strong>vision tower + LLM 冻结</strong></li><li><strong>Loss</strong>next-token prediction(用 LLM 把 visual feature 作 prefix,生成 caption</li></ul>
<h3 id="122-stage-2visual-instruction-tuning">12.2 Stage 2Visual Instruction Tuning</h3>
<p>目的:教会 VLM "看图回答问题、follow instruction"。</p>
<ul><li><strong>数据</strong>GPT-4 生成的 visual instructionLLaVA-Instruct-158K, ShareGPT4V+ 学术 VQA 数据(VQAv2, GQA, OCR-VQA, TextVQA</li><li><strong>训练</strong>:解冻 LLM + projector<strong>vision tower 一般保持冻结</strong>LLaVA-1.5 / Qwen-VL)。<strong>Qwen2-VL 在最后阶段也解冻 vision tower</strong>做 end-to-end fine-tune</li><li><strong>Loss</strong>next-token prediction on answer tokensinput image + question 不算 loss</li></ul>
<h3 id="123-stage-3preference--rlhf">12.3 Stage 3Preference / RLHF</h3>
<p>目的:减幻觉、提升 helpfulness / harmlessness、长尾任务对齐。</p>
<table><thead><tr><th>方法</th><th>时间</th><th>核心</th></tr></thead><tbody><tr><td><strong>LLaVA-RLHF</strong></td><td>2023.09, Sun et al.</td><td>PPO + human preference + hallucination-aware reward</td></tr><tr><td><strong>RLAIF-V</strong></td><td>2024, Yu et al.</td><td>AI feedback 替代 human label, divide-and-conquer</td></tr><tr><td><strong>POVID</strong></td><td>2024</td><td>DPO + 故意构造的幻觉 negative</td></tr><tr><td><strong>VLM-R1</strong></td><td>2025</td><td>GRPO + verifiable reward(视觉 reasoning 类 R1</td></tr><tr><td><strong>Bespoke / R1-Onevision</strong></td><td>2025</td><td>视觉 chain-of-thought + RL refinement</td></tr></tbody></table>
<div class="callout callout-info"><div class="callout-title">VLM-R1 (2025) 是当下热点</div><p>把 DeepSeek-R1 的"verifiable reward + GRPO"配方迁到视觉任务(如 ScienceQA, MMMU)。reward 来自答案是否匹配 ground truth(不用 process reward model),训练后 visual reasoning chain 显著增长,benchmark 大幅提升。</p></div>
<h3 id="124-数据规模-vs-阶段">12.4 数据规模 vs 阶段</h3>
<table><thead><tr><th>阶段</th><th>数据量</th><th>训练 token</th><th>解冻部分</th></tr></thead><tbody><tr><td>Alignment</td><td>0.55M caption</td><td>110B</td><td>projector</td></tr><tr><td>Instruction tune</td><td>0.210M instruction</td><td>150B</td><td>LLM + projector</td></tr><tr><td>Preference</td><td>50k500k preference pair</td><td>100M1B</td><td>LLM (LoRA / full)</td></tr></tbody></table>
<h2 id="13-multimodal-embeddingsbge-vl--jina-clip--vlm2vec">§13 Multimodal EmbeddingsBGE-VL / Jina-CLIP / VLM2Vec</h2>
<h3 id="131-为什么需要新一代多模态-embedding">13.1 为什么需要新一代多模态 embedding?</h3>
<p>CLIP 训练目标是 "image ↔ short caption" 对齐,<strong>长 instruction 检索 / multi-image / interleaved 文档检索</strong> 表现差。新一代 multimodal embedding 模型针对 retrieval / RAG 场景。</p>
<h3 id="132-代表方法">13.2 代表方法</h3>
<ul><li><strong>Jina-CLIP-v1 (2024)</strong>:在 CLIP 基础上加长文本对比(text-text task+ 多分辨率,单 embedding 模型既能做 image-text 也能做 text-text retrieval</li><li><strong>BGE-VL (2024)</strong>BGE 团队的 multimodal 版本,用 SigLIP-So400M + small LLM 做 instruction-aware retrieval</li><li><strong>VLM2Vec (Jiang et al. 2024)</strong>:把 VLMLLaVA / Qwen-VL)的最后一层 hidden state 做 instruction-conditioned mean pool<strong>只训 contrastive head</strong>benchmark MMEB 上显著超过 CLIP</li><li><strong>mmE5 (2024)</strong>multimodal 版本的 E5,支持 12 种 retrieval 任务类型</li></ul>
<h3 id="133-核心-trick">13.3 核心 trick</h3>
<ul><li><strong>instruction-aware</strong>embedding 输入 <code>[instruction][image][query]</code>,让同一图像在不同任务下产生不同 embedding</li><li><strong>VLM-as-encoder</strong>:直接用 instruct-tuned VLM 做 backbone,不再单独 contrastive pretrain</li><li><strong>hard negative mining</strong>:用 cross-encoder rerank 挖掘 batch 之外的 hard negative</li></ul>
<h2 id="14-25-高频面试题l1-必会--l2-进阶--l3-顶级-lab">§14 25 高频面试题(L1 必会 · L2 进阶 · L3 顶级 lab)</h2>
<h3 id="l1-基础题必会-10">L1 基础题(必会 10</h3>
<details>
<summary>Q1. CLIP 的 loss 是什么?为什么必须对称?</summary>
<ul><li>CLIP 用 <strong>symmetric InfoNCE</strong>$\mathcal{L} = \tfrac12(\mathcal{L}_{i\to t} + \mathcal{L}_{t\to i})$</li><li>$\mathcal{L}_{i\to t}$:对相似度矩阵 $\mathbf{S}$ 做<strong>行 softmax</strong>,取对角项的 NLLimage 检索 text</li><li>$\mathcal{L}_{t\to i}$<strong>列 softmax</strong>,对角项 NLLtext 检索 image</li><li><strong>对称必要性</strong>:单向只约束一个方向的检索;对称才能让 image / text embedding 互相约束,避免"单向坍塌"——比如 image 端聚集但 text 端漂移</li></ul>
<p>只答 InfoNCE 而不说"两次 softmax 平均",或者说"反着算一遍"而不解释为什么必要。</p>
</details>
<details>
<summary>Q2. CLIP 中的 temperature τ 起什么作用?为什么要 learnable?</summary>
<ul><li>温度 $\tau$ 调节 softmax 锐度:$\tau \to 0$ 像 one-hot 只关注 hardest negative$\tau \to \infty$ 均匀分布、几乎无梯度</li><li>OpenAI CLIP 把 $\tau$ 设成 learnable(实际 parameterize 为 <code>logit_scale = log(1/τ)</code>,反向更稳)</li><li>训练后稳态 $\tau \approx 0.01$<code>logit_scale ≈ log(100)</code>),并 clamp 上界防止崩</li><li><strong>不 learnable 的话</strong>:超参敏感,不同数据 / 模型规模都要手调</li></ul>
<p>把 τ 当固定 0.07 不学习;或写成 <code>1/exp(logit_scale)</code> 反向不稳。</p>
</details>
<details>
<summary>Q3. SigLIP 相对 CLIP 的核心改动?为什么 batch size 不再敏感?</summary>
<ul><li>把 softmax InfoNCE 换成 <strong>sigmoid binary CE</strong>:每个 $(i,j)$ 配对独立分类</li><li>$S_{ij} = t \cdot \mathbf{u}_i^\top \mathbf{v}_j + b$;标签 $y_{ij} = +1 (i=j) / -1 (i\neq j)$loss = $-\sum_{ij} \log\sigma(y_{ij} S_{ij})/N$</li><li><strong>batch 解耦</strong>:每项 loss 不依赖 row/col 的归一化,所以 N 翻倍只改变负样本数量,不改变 loss 形状</li><li>工程收益:单机大 batch、跨节点通信简化、小 batch 也能学(CLIP 小 batch 几乎不收敛)</li></ul>
<p>只说"用 sigmoid",没解释为什么 batch-independent;或把 SigLIP 当成"加 bias 的 CLIP"。</p>
</details>
<details>
<summary>Q4. ViT 中 [CLS] token 是必须的吗?</summary>
<ul><li>不是。<strong>ViT 原版</strong>用 [CLS] 是为了和 BERT 习惯对齐</li><li><strong>替代方案</strong>:把所有 patch token mean-pool 作为图像表征——多数现代 ViT (DeiT-III, SigLIP, EVA-CLIP) 都用 mean-pool 或 attentive pool</li><li><strong>CLIP 用 [CLS]</strong>:因为对比训练需要单一向量</li><li><strong>VLM 视觉 tower 一般 drop [CLS]</strong>LLaVA 取倒数第二层 patch token[CLS] 在 LLM 端不必要</li></ul>
<p>把 [CLS] 当成 ViT 的"必须组件";或说"没 [CLS] 就不能分类"(错,mean-pool 也行)。</p>
</details>
<details>
<summary>Q5. LLaVA 的 projector 是什么?为什么用 MLP 而不是 Linear?</summary>
<ul><li>Projector 把 visual encoder 输出($d_v$=1024)投到 LLM token 空间($d_\text{llm}$=4096</li><li>LLaVA-1.0:单层 <code>Linear(1024, 4096)</code>LLaVA-1.5<strong>2-layer MLP + GELU</strong></li><li>MLP 增加非线性表达,让 vision feature 更灵活地映射到 LLM "词典"</li><li>论文报告 MLP 在 MM-Vet / SEED-Bench 上比单层 Linear 提升 13 个点</li></ul>
<p>只说"Linear 投一下";或答"用 Q-Former"(那是 BLIP-2 不是 LLaVA)。</p>
</details>
<details>
<summary>Q6. LLaVA 的两阶段训练分别在做什么?</summary>
<ul><li><strong>Stage 1 Feature Alignment</strong>:只训 projector<strong>冻结 vision tower + LLM</strong>,用 caption 数据(CC3M / LAION-558K),让 visual feature 投到 LLM embedding space 附近</li><li><strong>Stage 2 Instruction Tuning</strong>:解冻 LLM + projectorvision tower 仍冻结),用 GPT-4 生成的 158K 视觉 instruction<strong>让 LLM 学会 follow visual instruction</strong></li><li>为什么不一步训:直接 stage 2 容易让 LLM 灾难遗忘文本能力;先 stage 1 给 visual token 一个"靠近文本 token"的初值再 instruct,更稳</li></ul>
<p>把两个 stage 都说成"训 projector";或漏掉 stage 1 冻结 LLM 这关键点。</p>
</details>
<details>
<summary>Q7. Q-Former 是什么?相对 LLaVA projector 优劣?</summary>
<ul><li>BLIP-2 的 Q-Former12 层 Transformer<strong>32 个 learnable query token</strong> 通过 cross-attention 从 frozen image encoder 取信息,输出固定 32 个 visual token</li><li>优点:<strong>视觉 token 数固定</strong>LLM context 占用低,分辨率 ↑ 计算预算不变</li><li>缺点:<strong>信息损失大</strong>256 patch 压成 32)、参数更多(~180M)、训练复杂(两阶段:stage 1 表征学习含 ITC+ITM+ITG 三个 lossstage 2 对接 frozen LLM 做生成)</li><li><strong>2024 主流回到 projector</strong>Qwen-VL / LLaVA-NeXT / InternVL-2 都用 projector</li></ul>
<p>把 Q-Former 当 projector 同义词;或不知道现代 VLM 倾向 projector。</p>
</details>
<details>
<summary>Q8. 一个 ViT-L/14 在 224×224 图上有多少 patchtoken 数?</summary>
<ul><li>patch 数 = $(224/14)^2 = 16^2 = 256$</li><li>token 数 = 256 + 1 (含 [CLS]) = <strong>257</strong></li><li>若是 LLaVA 取倒数第二层 patch tokendrop [CLS]= <strong>256 visual token</strong></li><li>若分辨率换成 336LLaVA-1.5):$(336/14)^2 = 24^2 = 576$ token</li></ul>
<p>算错 $(H/P)^2$(把 $P^2$ 当成 patch 数 $N$);漏掉 [CLS]。</p>
</details>
<details>
<summary>Q9. 为什么 CLIP 不擅长 OCR / 计数 / 空间关系?</summary>
<ul><li><strong>OCR 弱</strong>:caption 一般描述场景,不读图中文字;CLIP 没有像素级 OCR 监督</li><li><strong>计数弱</strong>caption 很少精确报数("几只鸟"通常说"a flock of birds");embedding 空间没保留计数信号</li><li><strong>空间关系弱</strong>"cat on top of dog" 和 "dog on top of cat" 在 bag-of-words 视角下几乎一样;Yuksekgonul et al. 2023 (ICLR) 用 ARO benchmark 量化了这一点</li><li><strong>改进方向</strong>DETR 风格的局部对齐、SigLIP-2 的 dense local objective、文档级数据</li></ul>
<p>把 OCR 弱归咎于"分辨率不够"(部分对,但根因是数据 + loss);说"CLIP 是 bag-of-words"过于绝对。</p>
</details>
<details>
<summary>Q10. 训练 VLM 时为什么一般冻结 vision tower</summary>
<ul><li>vision tower(如 CLIP ViT-L)在自己的数据上<strong>已经预训练</strong>得很好;解冻容易破坏 visual feature 质量</li><li>训练数据量级远小于 CLIP 预训练(百万 vs 数十亿),解冻很容易过拟合</li><li><strong>冻结也能省显存</strong>vision tower 几亿参数不用存 optimizer state</li><li><strong>Qwen2-VL 例外</strong>:最后阶段会解冻 vision tower 做小学习率 fine-tune,配合大量混合数据避免遗忘</li></ul>
<p>直接答"不能解冻"——错,<strong>末期可以小心解冻</strong></p>
</details>
<h3 id="l2-进阶10-题">L2 进阶(10 题)</h3>
<details>
<summary>Q11. SigLIP 的 bias $b$ 为什么初始化为 $-10$</summary>
<ul><li>训练初期 embedding 接近随机,$\mathbf{u}^\top \mathbf{v} \approx 0$sigmoid 输出 0.5</li><li>N×N 矩阵里负样本占 $N^2 - N \approx N^2$,正样本只 $N$ 个;若初始预测全 0.5,<strong>负样本梯度主导</strong>,正样本几乎得不到正确信号</li><li>初始化 $b \approx -10$ → $\sigma(b) \approx 4.5e^{-5}$ → 初期所有点先被预测为负</li><li>这样<strong>负样本几乎没 loss</strong>,正样本 loss 大(被预测为负但实际是正),梯度集中拉近正样本,训练稳定</li></ul>
<p>只说"避免数值问题";或答"对称项"(错,bias 不是对称损失项)。</p>
</details>
<details>
<summary>Q12. Flamingo 的 gated cross-attn 为什么初始化为 0</summary>
<ul><li>新增 cross-attn 输出乘 $\tanh(\alpha)$$\alpha$ 初始化为 0</li><li>$\tanh(0) = 0$<strong>初始化时新模块对 frozen LLM 输出零贡献</strong>——LLM 表现与未加视觉模块的纯 text Llama 完全一致</li><li>训练过程中 $\alpha$ 慢慢从 0 长出来,视觉信号逐步注入</li><li>优点:完全保留 frozen LLM 的 text-only 能力;缺点:视觉能力上限受 cross-attn 容量限制</li><li>Llama-3.2 Vision 沿用同一设计</li></ul>
<p>把 0 初始化当成"普通 init trick";或不知道这关乎 frozen LLM 的 capability preservation。</p>
</details>
<details>
<summary>Q13. LLaVA-1.6 / NeXT 的 AnyRes 怎么实现?</summary>
<ul><li>训练时假设固定 336²;推理时把高分辨率图按 aspect ratio 划成 $n \times m$ 个 336² tile(如 $2\times 2, 2\times 3$ 等)</li><li>每个 tile 独立过 frozen ViT 得到 576 token,加一个 <strong>全局缩略图</strong>(整图 resize 到 336² 编码)</li><li>拼接:$(1 + n\cdot m) \times 576$ visual token 喂入 LLM</li><li>选择哪种切法:从预定义的 grid 集合(如 $\{1\times 1, 2\times 2, 1\times 4, 4\times 1, ...\}$)里选最接近原 aspect ratio 的</li></ul>
<p>把 AnyRes 当 dynamic resolution 同义词——技术上不同。Qwen2-VL 是 native dynamicpatch 数完全自由),LLaVA-1.6 是 <strong>fixed-tile composition</strong></p>
</details>
<details>
<summary>Q14. Qwen2-VL 的 M-RoPE 三维分配为什么不平均?</summary>
<ul><li>Qwen2-VL <code>mrope_section = [16, 24, 24]</code>(单位是<strong>半 head_dim 的对数</strong>),$\sum \times 2 = $ head_dim = 128</li><li><strong>全部 128 维都旋转</strong>——只是不同维度对用不同 axis (t/h/w) 的位置 id 算旋转角</li><li><p><strong>不平均的原因</strong></p>
<ul><li>视频帧间变化较慢(相邻帧很相似),所以 $s_t = 16$ 占比小</li><li>空间内 patch 间变化剧烈(同一帧不同位置内容差异大),$s_h = s_w = 24$ 各需更多频率覆盖</li></ul></li><li>$s_h = s_w$:图像 H/W 维度地位对称</li></ul>
<p>把 section 当成"维度数"(错,单位是对数 = head_dim / 2 的分配);或以为"剩余维度不旋转"。</p>
</details>
<details>
<summary>Q15. 为什么 BLIP-2 选 32 个 query token</summary>
<ul><li>32 是经验值,是 LLM context 占用 vs 信息表达 的权衡</li><li>太少(&lt; 16):信息损失大,VQA / 细节任务下降</li><li>太多(&gt; 64):LLM context 占用大、Q-Former cross-attn 计算贵</li><li>BLIP-2 论文 ablation 显示 32 在大多数下游任务上是 sweet spot</li><li>设计上类似 Perceiver(也是 latent query 数压缩输入)</li></ul>
<p>只答"经验值";不知道这是 context budget vs information capacity 的工程权衡。</p>
</details>
<details>
<summary>Q16. DDP 下的 CLIP 怎么算 InfoNCE</summary>
<ul><li>单卡 batch $N_\text{local}$N 卡总 batch $N = K \cdot N_\text{local}$</li><li>每卡 forward 后 <code>dist.all_gather</code> 拿到全部 GPU 的 image / text feats</li><li>算 $\mathbf{S} \in \mathbb{R}^{N \times N}$ 的全局相似度</li><li>但反向时<strong>只让本卡那 $N_\text{local}$ 行 / 列 contribute 梯度</strong>(避免重复 backward</li><li>这就是 OpenCLIP 的 <code>local_loss=True</code> 选项</li></ul>
<p>只说 all-gather;不知道反向需要避免重复计算;或以为反向也要 all-gather 一遍(错,反向走通讯反向 path)。</p>
</details>
<details>
<summary>Q17. 为什么 Llama-3.2-V 比 LLaVA-Qwen-VL 视觉能力上限低?</summary>
<ul><li>Llama-3.2-V 用 frozen LLM + gated cross-attn adapter<strong>LLM 权重不变</strong></li><li>LLaVA / Qwen-VL <strong>解冻 LLM</strong>LLM 内部 attention 可以重组、专门处理视觉 token</li><li>后者 LLM 能"用 self-attention 思考图像";前者 LLM 只能被动接收 cross-attn 注入的视觉信号</li><li>trade-offLlama-3.2-V 完美保留 text 能力,LLaVA-Qwen 可能轻微退化但视觉上限高</li></ul>
<p>只说"参数少";不知道这是 architecture-level 的能力上限差异。</p>
</details>
<details>
<summary>Q18. 视觉 instruction tuning 数据为什么很多用 GPT-4 生成?</summary>
<ul><li>原始 caption 数据(CC3M / LAION)短、不 instruction-style<strong>学不出 dialog 能力</strong></li><li>真实人工标注 visual instruction(如 VQAv2 question)规模小、风格单一</li><li><strong>GPT-4 + image + caption → 生成多轮对话 / 推理任务 / 详细描述</strong>LLaVA-Instruct-158K 就是这么来的</li><li>同时用 prompt engineering 控生成数据的覆盖(detailed description, conversation, complex reasoning 三类)</li></ul>
<p>只答"数据多";不知道这是 instruction style + diversity 的关键瓶颈。</p>
</details>
<details>
<summary>Q19. CLIP / SigLIP 训练时 batch size 一般多大?</summary>
<ul><li><strong>OpenAI CLIP</strong>32k batch256 GPU × 128/GPU 左右)</li><li><strong>OpenCLIP</strong>:到 90k batchLAION-2B</li><li><strong>SigLIP</strong>:典型 32k batch 即足够,sigmoid loss 让每个 (i,j) 项独立、避免 softmax 的 batch-wide 同步;论文有扫描到 256k batch 的实验,但收益边际递减</li><li><strong>小 batch 不行的原因</strong>InfoNCE 互信息下界 $I(U;V) \ge \log N - \mathcal{L}$N 越大下界越紧;同时负样本数量决定 contrastive 的难度</li><li>SigLIP 把 batch 解耦后,小 batch 表现显著提升(1k batch 也能学到合理 embedding</li></ul>
<p>答"几百";或不知道 batch 与 InfoNCE 的理论联系。</p>
</details>
<details>
<summary>Q20. POPE / Winoground / MMBench / MMMU 分别在测什么?</summary>
<ul><li><strong>POPE</strong> (Li et al. 2023):测<strong>物体幻觉</strong>——VLM 是否会说图里有不存在的物体(yes/no 二分类)</li><li><strong>Winoground</strong> (Thrush et al. 2022):测<strong>组合性 / 词序敏感</strong>——"cat on dog" vs "dog on cat" 能否区分</li><li><strong>MMBench</strong> (Liu et al. 2023):通用多模态评估,~3000 题覆盖 OCR / 物体识别 / 推理等</li><li><strong>MMMU</strong> (Yue et al. 2024 CVPR):大学级专业知识题(数学 / 物理 / 医学等),考多模态推理</li><li><strong>MM-Vet</strong> (Yu et al. 2023)6 种 capability 综合评估(识别 / 知识 / OCR / 空间 / 语言 / 数学)</li></ul>
<p>把 POPE 和 MMBench 混用;不知道 Winoground 是"组合性 stress test"。</p>
</details>
<h3 id="l3-高级顶级-lab--研究方向5-题">L3 高级(顶级 lab / 研究方向,5 题)</h3>
<details>
<summary>Q21. 推 CLIP 对称 InfoNCE = 行+列 softmax 平均,并解释为什么 SigLIP 能 batch-independent。</summary>
<p>设 batch 大小 $N$,相似度矩阵 $S_{ij} = \mathbf{u}_i^\top \mathbf{v}_j / \tau$。</p>
<p><strong>CLIP 推导</strong></p>
<p>行方向 softmax$p_{ij} = \frac{\exp(S_{ij})}{\sum_k \exp(S_{ik})}$。Image→Text 的 NLL</p>
<p>$$\mathcal{L}_{i\to t} = -\frac{1}{N}\sum_i \log p_{ii} = -\frac{1}{N}\sum_i \log \frac{\exp(S_{ii})}{\sum_j \exp(S_{ij})}$$</p>
<p>列方向 softmaxText→Image):</p>
<p>$$\mathcal{L}_{t\to i} = -\frac{1}{N}\sum_j \log \frac{\exp(S_{jj})}{\sum_i \exp(S_{ij})}$$</p>
<p>对称 loss$\mathcal{L} = \tfrac12 (\mathcal{L}_{i\to t} + \mathcal{L}_{t\to i})$。注意 <strong>梯度对 $S_{ij}$</strong></p>
<p>$$\frac{\partial \mathcal{L}_{i\to t}}{\partial S_{ij}} = \frac{1}{N}(p_{ij} - \delta_{ij})$$</p>
<p>每个 $S_{ij}$ 的梯度依赖<strong>整行的 softmax 归一化</strong> $\sum_k \exp(S_{ik})$。所以 N 改变(新增 / 删除 negative)会改变整行所有 $p$ 的值——<strong>梯度耦合 batch</strong></p>
<p><strong>SigLIP 推导</strong></p>
<p>$S_{ij} = t \cdot \mathbf{u}_i^\top \mathbf{v}_j + b$$y_{ij} = 2\delta_{ij} - 1$</p>
<p>$$\mathcal{L}_\text{SigLIP} = \frac{1}{N}\sum_{i,j} \log(1 + \exp(-y_{ij} S_{ij}))$$</p>
<p>梯度:</p>
<p>$$\frac{\partial \mathcal{L}}{\partial S_{ij}} = \frac{1}{N}\cdot \frac{-y_{ij}}{1 + \exp(y_{ij} S_{ij})} = \frac{1}{N}\cdot (-y_{ij}) \cdot \sigma(-y_{ij} S_{ij})$$</p>
<p><strong>关键</strong>$\partial \mathcal{L} / \partial S_{ij}$ 只依赖 $S_{ij}$ 本身,<strong>不涉及其他元素</strong>。所以新增 negative 不会改变已有 $S_{ij}$ 的梯度——<strong>batch-independent</strong></p>
<p><strong>工程含义</strong></p>
<ul><li>CLIPDDP 必须 all-gather embedding 算全局 logsumexp,通信 $O(N \cdot D)$,加 sync 点</li><li>SigLIP:可用 <strong>chunked all-pair</strong>,每个 chunk 只算本地行 × 远端列的 sigmoid 项,无 sync logsumexp</li></ul>
</details>
<details>
<summary>Q22. Q-Former vs LLaVA projector 的 trade-off:从 capacity / compute / training stability 三个维度解释。</summary>
<p><strong>Capacity(信息容量)</strong></p>
<ul><li><strong>LLaVA projector</strong>:所有 $N$ 个 patch token 都进 LLM;信息无瓶颈,但 LLM context 占用大</li><li><strong>Q-Former</strong>:32 query 是固定瓶颈,信息显著压缩;对细节任务(OCR / 计数)不友好</li><li>设 visual encoder 输出秩为 $r$LLaVA 的 visual context 秩 $\le r$(保留),Q-Former 的秩 $\le \min(r, 32)$</li></ul>
<p><strong>Compute / Memory</strong></p>
<ul><li><strong>LLaVA projector</strong>:仅 MLP forwardO(N·D²) 计算</li><li><strong>Q-Former</strong>12 层 cross-attn + self-attn + FFN~180M 参数;但<strong>下游 LLM 端 context 短(32 token vs N=256+ token</strong>LLM 推理快</li><li><strong>总成本权衡</strong>:图像分辨率高时(N=2880),Q-Former 节省 LLM 推理;图像低分辨率时 LLM 占主导,LLaVA 更便宜</li></ul>
<p><strong>Training stability</strong></p>
<ul><li><strong>LLaVA</strong>projector 容易训(2 stage),梯度路径短</li><li><strong>Q-Former</strong>2 stage 训练(<strong>stage 1 表征</strong>含 ITC + ITM + ITG 三个 loss 同时优化;<strong>stage 2 生成</strong>对接 frozen LLM),ITM head 易过拟合;ITG 需要 causal mask 与 self-attn mask 的复杂 routing,工程坑多</li></ul>
<p><strong>结论</strong>2024 主流回到 projector + spatial pixel-shuffle / merging 控 token 数,Q-Former 仅在 video / 多图汇总场景仍有价值(用 query 做 temporal pool)。</p>
</details>
<details>
<summary>Q23. Qwen2-VL 的 M-RoPE 配 `mrope_section = [16, 24, 24]` 为何不是 1:1:1?所有 head_dim 都旋转吗?</summary>
<p>回顾普通 RoPEhead_dim $d$ 维分成 $d/2$ 对复数,频率 $\theta_k = \text{base}^{-2k/d}$。<strong>频率覆盖范围决定能区分的最大相对距离</strong>:低频区分远距离,高频区分近距离。</p>
<p><strong>关键 disambiguate</strong>Qwen2-VL <code>mrope_section</code> 单位是 <strong>半 head_dim 的对数</strong>(每个数代表多少对 $(2k, 2k+1)$ 维度对)。$[16, 24, 24]$ 表示 t / h / w 三个 axis 分别占 16 / 24 / 24 对维度,$\sum \times 2 = 128 = $ head_dim。HF 实现把 section 翻倍成 $[16, 24, 24, 16, 24, 24]$ 切 head_dim,对应 axis 序列 $(t, h, w, t, h, w)$——<strong>全部 128 维都旋转</strong>,没有"不旋转 dim"。</p>
<p>设计权衡:</p>
<ol><li><strong>时间维变化慢</strong>:典型视频 1–5 FPS 采样,相邻帧很相似,长程时间依赖需求中等。$s_t=16$(占 25%)足够覆盖几百到上千帧。</li><li><strong>空间维变化剧烈</strong>:同一帧内不同 patch 视觉差异极大;要在 $\sim 1000\times 1000$ 像素图上做 token 间检索,需要更多频率档位。$s_h = s_w = 24$(各占 37.5%)覆盖更广。</li><li><strong>空间对称性</strong>$s_h = s_w$ 保持图像 H/W 维度地位对称(水平翻转、垂直翻转的等价性)。</li><li><strong>6 段 alternating 而不是 3 段连续</strong>:因为 RoPE 用 LLaMA "rotate_half" 实现,head_dim 在内存上分成两半 $[h_1, h_2]$,旋转用 $q \mapsto q \cos + \text{rotate\_half}(q)\sin$;两半对应同一组 inv_freq。所以 axis 分配既要在前半也要在后半镜像。</li></ol>
<p><strong>Qwen2.5-VL 升级</strong>:把 $m_t$ 从帧 id 改为<strong>绝对时间戳(秒)</strong>,让训练时变 FPS 的视频共享一致的时间坐标——这是 long-video 关键。</p>
<p><strong>alternative</strong>DeepSeek-VL2 用扁平化 visual token + 普通 1D RoPE(不分 h, w);Llama-3.2-V 同样不显式分时空。<strong>M-RoPE 仅在 native interleaved video + image 场景下显著优于扁平展开</strong></p>
</details>
<details>
<summary>Q24. VLM 幻觉的根本原因?现有缓解方法的优劣?</summary>
<p><strong>根本原因</strong></p>
<ol><li><strong>数据偏差</strong>:训练数据中常出现的 "co-occurrence prior"——"图里有桌子大概率有椅子"。Co-occurrence 让 VLM 在看到桌子时倾向回答"是的,有椅子",即使图里没椅子</li><li><strong>语言先验主导</strong>:当视觉信号弱时(小物体、模糊、奇怪角度),VLM 退化为纯语言模型,按"语料常识"作答</li><li><strong>LLM 的 sycophancy</strong>:用户问 "图里是不是有 X" 倾向回答 Yes(人类反馈偏向 helpful → 倾向 yes</li><li><strong>Stage 2 instruction tuning 没有 negative supervision</strong>:标注里很少教 "图里没 X 就回答 No"</li></ol>
<p><strong>缓解方法</strong></p>
<table><thead><tr><th>方法</th><th>思路</th><th>优势</th><th>劣势</th></tr></thead><tbody><tr><td>LLaVA-RLHF</td><td>PPO + hallucination-aware reward</td><td>训练后期定向修</td><td>需要 reward model + 大量 preference</td></tr><tr><td>RLAIF-V</td><td>AI-generated preference</td><td>数据成本低</td><td>reward model 自身偏差累积</td></tr><tr><td>POVID</td><td>DPO + 构造 hallucination negative</td><td>直接对症</td><td>需精心设计 negative</td></tr><tr><td>VCD (visual contrastive decoding)</td><td>推理时让 VLM 同时看图 vs 模糊图,差值放大视觉信号</td><td>训练免费</td><td>推理 2x 成本</td></tr><tr><td>OPERA</td><td>beam search + over-attention 检测</td><td>推理时检测</td><td>启发式,可能误杀</td></tr><tr><td>POPE 评测驱动</td><td>用 POPE 反向监督</td><td>量化好</td><td>只测 object hallucination</td></tr></tbody></table>
<p><strong>未来方向</strong>:从训练数据层根治(grounded caption + segment-level 监督);visual chain-of-thoughtVLM-R1 风格)让模型在回答前先"指认证据"。</p>
</details>
<details>
<summary>Q25. 现代 VLM 训练里 vision tower 应该用 SigLIP 还是 CLIP?为什么 20242025 大多选 SigLIP-So400M</summary>
<p><strong>Empirical 结论</strong>2024 后 SigLIP-So400M 成为 open-weight VLM 的常见选择,典型代表是 <strong>PaliGemma</strong>Google)和 <strong>LLaVA-OneVision</strong>。但 <strong>Molmo</strong> 仍用 OpenAI CLIP(其论文 ablation 比较过 SigLIP);<strong>InternVL 系列</strong>用自家的 InternViT<strong>Qwen2-VL</strong> 视觉端从 DFN-derived ViT 初始化再做大规模 vision-language 联合训练;<strong>LLaVA-1.5 / 1.6</strong> 仍用 CLIP ViT-L/14。即"切到 SigLIP"不是行业共识。</p>
<p><strong>SigLIP-So400M 的吸引力</strong></p>
<ol><li><strong>零样本性能更强</strong>SigLIP-So400M 在 ImageNet zero-shot 与同规模 CLIP 对比有 48 个点优势,visual feature 质量更高</li><li><strong>分辨率友好</strong>:SigLIP 训练时已用大量 384²/512² 数据;CLIP 主要 224²+336²,VLM 任务普遍需高分辨率,SigLIP 迁移更顺</li><li><strong>batch-independent loss → fine-tune 稳定</strong>SigLIP 的 sigmoid 在 stage 1 解冻 vision tower 时梯度更可预测</li><li><strong>多语言支持</strong>SigLIP-2 / mSigLIP 原生支持多语言</li><li><strong>开放权重</strong>Google 公开 SigLIP / SigLIP-2 全套 checkpointOpenAI CLIP 也已开源,但选择有限)</li></ol>
<p><strong>何时仍选 CLIP</strong></p>
<ul><li>需要严格对齐 OpenAI CLIP 行为(如 Stable Diffusion 风格的 CLIP guidance</li><li>项目兼容(早期 LLaVA-1.0/1.5 + DALL-E 用 CLIP</li></ul>
<p><strong>注意</strong>SigLIP 不是万能;<strong>DeepSeek-VL 用 SigLIP + SAM dual-encoder</strong>——细节定位任务上 SAM 特征仍有不可替代的优势。</p>
</details>
<h2 id="a-附录sanity-check-输出--参考文献">§A 附录:Sanity-check 输出 &amp; 参考文献</h2>
<h3 id="a1-关键代码-sanity-check实跑示意">A.1 关键代码 sanity check(实跑示意)</h3>
<pre><code>[ViT] patch_embed: (2, 3, 224, 224) -&gt; (2, 196, 768) ✓
[ViT] forward + CLS: (2, 3, 224, 224) -&gt; head out (2, 1000) ✓
[CLIP] N=8, D=512, init logit_scale=ln(1/0.07): loss ≈ 2.08 ≈ log(N) (random embeddings → near-uniform softmax) ✓
[CLIP] forward + backward: gradients along i→t 与 t→i path 对称 ✓
[SigLIP] N=8, D=512, b=0: loss = sum_{ij} log(1+e^0) / N = 64 * log 2 / 8 ≈ 5.545 ✓
[SigLIP] bias b=-10: positive (8 项) loss ≈ log(1+e^10) ≈ 10; negative (56 项) loss ≈ 4.5e-5; 整体 8·10/8 ≈ 10.0 ✓ (正样本主导初期梯度)
[LLaVA] visual feat (2, 256, 1024) -&gt; projector -&gt; (2, 256, 4096) ✓
[LLaVA] input_ids w/ &lt;image&gt; placeholder: 1 token -&gt; 256 visual tokens after merge ✓
[Q-Former] image_feats (2, 257, 1408), queries (1, 32, 768) -&gt; out (2, 32, 768) ✓
[M-RoPE] head_dim=128, mrope_section=[16,24,24]: 2 × sum = 128 = head_dim, full rotation ✓
[M-RoPE] pure-text token (pos_t=pos_h=pos_w=m): cos/sin 三个 axis 完全相同, 等价 1D RoPE ✓</code></pre>
<h3 id="a2-关键文献按主题">A.2 关键文献(按主题)</h3>
<p><strong>视觉 encoder</strong>Dosovitskiy et al. ICLR 2021 (ViT); Zhai et al. CVPR 2022 (ViT-g); Fang et al. arXiv 2023 (EVA-02)</p>
<p><strong>对比预训练</strong>Radford et al. ICML 2021 (CLIP); Cherti et al. CVPR 2023 (OpenCLIP); Zhai et al. ICCV 2023 (SigLIP); Tschannen et al. arXiv 2025 (SigLIP-2); Gadre et al. NeurIPS 2023 (DataComp)</p>
<p><strong>视觉 instruction / fusion</strong>Liu et al. NeurIPS 2023 (LLaVA); Liu et al. CVPR 2024 (LLaVA-1.5); Li et al. ICML 2023 (BLIP-2); Alayrac et al. NeurIPS 2022 (Flamingo); Wang et al. arXiv 2023 (CogVLM); Bai et al. arXiv 2023 (Qwen-VL); Wang et al. arXiv 2024 (Qwen2-VL); Bai et al. arXiv 2025 (Qwen2.5-VL); Lu et al. arXiv 2024 (DeepSeek-VL); Wu et al. arXiv 2024 (DeepSeek-VL2); Chen et al. CVPR 2024 + arXiv 2024 (InternVL / InternVL-2); Llama Team arXiv 2024 (Llama-3.2-V); Deitke et al. arXiv 2024 (Molmo); Li et al. arXiv 2024 (LLaVA-OneVision)</p>
<p><strong>VLM 偏好对齐</strong>Sun et al. arXiv 2023 (LLaVA-RLHF); Yu et al. arXiv 2024 (RLAIF-V); Zhou et al. arXiv 2024 (POVID); Shen et al. arXiv 2025 (VLM-R1)</p>
<p><strong>多模态 embedding</strong>Koukounas et al. arXiv 2024 (Jina-CLIP); Jiang et al. arXiv 2024 (VLM2Vec); Zhang et al. arXiv 2024 (mmE5)</p>
<p><strong>评测</strong>Li et al. EMNLP 2023 (POPE); Thrush et al. CVPR 2022 (Winoground); Liu et al. ECCV 2024 (MMBench); Yue et al. CVPR 2024 (MMMU); Fu et al. CVPR 2025 (Video-MME); Yu et al. ICML 2024 (MM-Vet); Mangalam et al. NeurIPS 2023 (EgoSchema)</p>
<hr />
<p>代码 + 公式经独立 reviewer 静态检查;数值在 PyTorch 2.x、CUDA 12.x 上验证(ViT / CLIP / SigLIP / Q-Former / M-RoPE 5 个核心模块的形状与初始 loss 均与公式一致)。</p>
<footer class="aris-footer">
Generated by <a href="https://github.com/wanshuiyin/Auto-claude-code-research-in-sleep/blob/main/skills/render-html/SKILL.md">ARIS <code>/render-html</code></a> ·
source path <code>docs/tutorials/vlm_multimodal_tutorial.md</code> ·
SHA256 <code>f9cc9510bdd5</code> ·
generated at 2026-05-19 05:47 UTC.
This is a generated view — edit the source Markdown, then re-render.
</footer>
</main>
</div>
</body>
</html>