# 显存预算与模型选型矩阵：4 个 VLM Reference 模型

目标：为 minivLLM 的 paged KV 实验选出几条可运行的 VLM 路线，并把显存、visual token 和降级策略之间的关系讲清楚。

> 所有数字为 bf16/fp16 下的**设计估算**，非精确测量。实际运行时需用 `torch.cuda.memory_stats()` 校准。
>
> 这份笔记不再把学习范围锁死在某一张显卡上。下面先用一张**中档单卡**做保守估算锚点，只是为了方便把公式落到数字上；如果你的显存更大，就按同一套公式放宽 `max_pixels`、`max_model_len` 和 batch。

---

## 1. 通用假设

| 参数 | 值 | 来源 |
|------|-----|------|
| 保守估算锚点 | 一张中档单卡（可用空间大约十 GB 级） | 方便落地计算，不代表上限 |
| 利用率系数 | 0.9 | `minivLLM/minivllm/config.py` |
| 有效预算 | 约 10.8GB（含权重、KV、激活） | 以 12 × 0.9 为保守锚点 |
| CUDA context + 框架固定开销 | 约 0.5-1.0GB | 经验值 |
| `kvcache_block_size` | 16 | `config.py` 固定 |
| 数据类型 | bf16（主路径）/ fp16（fallback） | 默认 bf16 |
| max_model_len（默认） | 4096 | `config.py` 默认，可由 HF config 覆盖 |

### KV 预算公式

```
KV_per_token = 2 × num_layers × num_kv_heads × head_dim × dtype_bytes
KV_total     = KV_per_token × max_seq_len
```

其中 `dtype_bytes = 2`（bf16/fp16）。若使用 GQA（Grouped Query Attention），`num_kv_heads` 通常远小于 `num_attention_heads`。

### Visual Token 预算公式

```
visual_tokens ≈ (image_area / min_pixels) 下取整后按 ViT patch 数计算
典型范围：256（单图 thumb） ~ 4096（单图高分辨率）
```

多数 VLM 在 `min_pixels` 配置下产生 256-1024 个 visual token；高分辨率 (`max_pixels`) 可能产生 2048-4096+。

---

## 2. 各模型显存预算

### 2.1 Qwen3-VL-4B-Instruct（主路径）

**参数量**：约 4.0B（含 ViT + LLM + projector）

**架构简况**（估计）：
- LLM 层数：约 36 层
- num_kv_heads：约 4（GQA，8 attention heads 共享）
- head_dim：128
- ViT：约 400M params

**主路径**：

| 项目 | 数值 | 备注 |
|------|------|------|
| bf16 权重总大小 | ~8.0 GB | 4B × 2 bytes |
| 权重后剩余显存 | ~2.8 GB | 10.8 - 8.0 |
| 减去激活峰值预留（~1GB） | ~1.8 GB | prefill 时 activation 占用 |
| KV 可用预算 | ~1.8 GB | |
| KV per token | ~36 × 4 × 128 × 2 × 2 = 73,728 bytes ≈ 72 KB | |
| 可存储最大 token 数 | ~1.8 GB / 72 KB ≈ 25,000 tokens | 远超 max_model_len=4096 |
| 块数（block_size=16） | ~25,000 / 16 ≈ 1,562 blocks | |
| 推荐 max_pixels | 256×256 ~ 512×512（约 256-1024 visual tokens） | 留足 KV 余量 |
| visual token 预算 | 256-1024 tokens（min_pixels 档位） | 避免高分辨率 |

**结论**：如果你先按一张中档单卡做保守估算，Qwen3-VL-4B 仍然有机会作为主路径运行，KV 预算也够用。更大的显存只会把这条路放宽，不会推翻这里的判断。`max_pixels` 仍建议先从 512×512 往下试，再按余量逐步放大。

**失败时降级**：

1. 如果 OOM：先降为 **fp16**（同大小，但某些 GPU 的 bf16 实现有额外开销）。
2. 如果依然 OOM：将 `min_pixels`/`max_pixels` 降至 128×128，visual tokens 压到 64-256。
3. 再不行：换 Qwen2.5-VL-3B（见 §2.2）。
4. 末路：只跑 text-only mode，用 design 文档中的纯文本对齐测试验证 block 逻辑即可。

---

### 2.2 Qwen2.5-VL-3B-Instruct（稳定 fallback）

**参数量**：约 3.0B

**架构简况**（估计）：
- LLM 层数：约 28 层
- num_kv_heads：约 4（GQA）
- head_dim：128
- ViT：约 300M params

**主路径**：

| 项目 | 数值 | 备注 |
|------|------|------|
| bf16 权重总大小 | ~6.0 GB | 3B × 2 bytes |
| 权重后剩余显存 | ~4.8 GB | 10.8 - 6.0 |
| 减去激活峰值预留（~1GB） | ~3.8 GB | |
| KV 可用预算 | ~3.8 GB | |
| KV per token | ~28 × 4 × 128 × 2 × 2 = 57,344 bytes ≈ 56 KB | |
| 可存储最大 token 数 | ~3.8 GB / 56 KB ≈ 68,000 tokens | 远超需要 |
| 推荐 max_pixels | 512×512 ~ 1024×1024（约 1024-4096 visual tokens） | 更从容 |
| visual token 预算 | 512-2048 tokens | 适中 |

**结论**：余量比 4B 大，适合作为"什么都能跑"的 fallback。即使高分辨率图像也能应付。

**失败时降级**：

1. 以中档单卡为锚点时，此模型通常不会先撞显存墙。万一出现（如超大 batch），先降低 `max_model_len` 到 2048。
2. 还可将 `gpu_memory_utilization` 降至 0.85。
3. 终极后备：SmolVLM2-2.2B（§2.4）。

---

### 2.3 InternVL3.5-4B（先进对照）

**参数量**：约 4.0B（InternViT-300M + InternLM3-4B）

**架构简况**（估计）：
- LLM 层数：约 32 层
- num_kv_heads：约 8（GQA）
- head_dim：128
- ViT：InternViT-300M，支持动态分辨率

**主路径**：

| 项目 | 数值 | 备注 |
|------|------|------|
| bf16 权重总大小 | ~8.0 GB | 4B × 2 bytes |
| 权重后剩余显存 | ~2.8 GB | |
| 减去激活峰值预留（~1GB） | ~1.8 GB | |
| KV 可用预算 | ~1.8 GB | |
| KV per token | ~32 × 8 × 128 × 2 × 2 = 131,072 bytes ≈ 128 KB | 比 Qwen 同参数量大（num_kv_heads 更多） |
| 可存储最大 token 数 | ~1.8 GB / 128 KB ≈ 14,000 tokens | |
| 推荐 max_pixels | 256×256 ~ 448×448（约 256-1024 visual tokens） | 动态分辨率需谨慎 |
| visual token 预算 | 256-1024 tokens | 受限于 KV 预算 |

**结论**：KV per token 约是 Qwen 同参数量的 1.8 倍（因为 kv_heads=8 vs 4），显存压力更大。需要更保守的 `max_pixels`。适合作为"先进对照"，验证 paged KV 在较高 KV 密度下的行为。

**失败时降级**：

1. 优先降 `max_pixels` → 最小 128×128，甚至关闭视觉输入做 text-only。
2. 如果依然 OOM：说明这条路对当前硬件不合适，先退化为"只跑 design 文档中的 text-only 对齐对照"。
3. 换 Qwen2.5-VL-3B（§2.2）或 SmolVLM2-2.2B（§2.4）。

---

### 2.4 SmolVLM2-2.2B（轻量对照）

**参数量**：约 2.2B

**架构简况**（估计）：
- LLM 层数：约 24 层
- num_kv_heads：约 4（GQA）
- head_dim：96（可能）
- ViT：SigLIP 400M 的轻量变体

**主路径**：

| 项目 | 数值 | 备注 |
|------|------|------|
| bf16 权重总大小 | ~4.4 GB | 2.2B × 2 bytes |
| 权重后剩余显存 | ~6.4 GB | 10.8 - 4.4 |
| 减去激活峰值预留（~1GB） | ~5.4 GB | |
| KV 可用预算 | ~5.4 GB | 最宽裕 |
| KV per token（估计） | ~24 × 4 × 96 × 2 × 2 = 36,864 bytes ≈ 36 KB | head_dim 较小 |
| 可存储最大 token 数 | ~5.4 GB / 36 KB ≈ 150,000 tokens | 绰绰有余 |
| 推荐 max_pixels | 1024×1024（约 4096 visual tokens） | 随便跑 |
| visual token 预算 | 512-4096 tokens | 无压力 |

**结论**：显存最宽裕，适合作为"底线对照"，如果 paged KV 在这个模型上都不对齐，说明实现有根本性 bug。

**失败时降级**：

1. 以中档单卡为保守锚点时，此模型几乎不该先因显存失败。如果出问题，先查 torch 版本和环境兼容性。
2. 只跑 text-only mode 验证 BlockManager 逻辑正确性。
3. 确认 `torch.cuda.empty_cache()` 和 `gc.collect()` 有效。

---

## 3. 汇总对比表

| 模型 | 参数量 | bf16 权重 | 推荐 max_pixels | visual token 预算 | KV per token | KV 可用预算 | fallback 路径 |
|------|--------|-----------|-----------------|-------------------|-------------|-------------|--------------|
| **Qwen3-VL-4B** | ~4.0B | ~8.0 GB | 256²-512² | 256-1024 | ~72 KB | ~1.8 GB | 降 pixels → 换 3B → text-only |
| **Qwen2.5-VL-3B** | ~3.0B | ~6.0 GB | 512²-1024² | 512-2048 | ~56 KB | ~3.8 GB | 降 max_model_len → 换 SmolVLM2 |
| **InternVL3.5-4B** | ~4.0B | ~8.0 GB | 256²-448² | 256-1024 | ~128 KB | ~1.8 GB | 降 pixels → text-only → 换模型 |
| **SmolVLM2-2.2B** | ~2.2B | ~4.4 GB | 1024² | 512-4096 | ~36 KB | ~5.4 GB | 查 torch 兼容性 → text-only |

## 4. 决策建议

**开发期主路径**：Qwen3-VL-4B-Instruct。它和 minivLLM 生态最贴近，参数规模也还合理。若你只有中档显存，先从它开始；如果你显存更大，就直接把分辨率和上下文开宽。

**CI / 快速验证**：SmolVLM2-2.2B。加载最快，显存极宽裕，适合高频测试。

**失败保护**：任何一个模型 OOM，自动降级到 text-only mode（不加载 ViT，不处理图像），仅验证 BlockManager / PagedKVCache / logits 对齐。

**不做的事**：不将 FP8/Q4 量化升格为推荐主路径。量化虽省显存，但引入精度噪声，混淆 paged vs contiguous 对齐验证。量化留给 Wave 3（性能优化阶段）。
