﻿---
title: "大模型的 Flash 版和完整版，架构上差在哪"
description: "Gemini、DeepSeek、GLM 都有带 Flash 的型号，价钱只有完整版的几分之一。Flash 是 FlashAttention 吗？是把大模型压缩了一下吗？我们先把名字理清，再从“生成一个字要花什么”出发，看 Flash 版在哪几处下刀：每个字少读一点权重（缩小骨架、稀疏专家）、少读一点历史（GQA、MLA、滑动窗口、压缩注意力）、一次多猜几个字（多 token 预测），以及变小之后靠蒸馏补回本事。每一处都对照官方公开的配置和三个小实验，并标出哪些是官方说的、哪些只是推测。"
pubDate: 2026-10-29
updatedDate: 2026-10-29
tags: ["AI", "大模型", "MoE", "注意力机制", "蒸馏", "推理"]
hero: "./hero.svg"
clc: "TP183"
refs:
  - id: gemini-pricing
    title: "Gemini Developer API pricing"
    url: https://ai.google.dev/gemini-api/docs/pricing
    site: Google AI for Developers
    note: "2026 年 10 月底查看。3.1 Pro Preview 输出每百万 token 12 美元；3.8 Flash 3.75 美元（2027 年起 7.5 美元）；3.1 Flash-Lite 1.5 美元。"
  - id: flashattn
    title: "FlashAttention: Fast and Memory-Efficient Exact Attention with IO-Awareness"
    url: https://arxiv.org/abs/2205.14135
    author: Tri Dao, Daniel Y. Fu, Stefano Ermon, Atri Rudra, Christopher Ré
    site: NeurIPS 2022
    date: "2022"
  - id: gemini15
    title: "Gemini 1.5: Unlocking multimodal understanding across millions of tokens of context"
    url: https://arxiv.org/abs/2403.05530
    author: Gemini Team, Google
    date: "2024"
    note: "3.2 节：1.5 Flash 是 transformer decoder，注意力和前馈并行计算，从 1.5 Pro 在线蒸馏；1.5 Pro 是稀疏 MoE。Flash-8B 属于“个位数十亿参数”级别，沿用 Flash 的架构。"
  - id: gemini25
    title: "Gemini 2.5: Pushing the Frontier with Advanced Reasoning, Multimodality, Long Context, and Next Generation Agentic Capabilities"
    url: https://arxiv.org/abs/2507.06261
    author: Gemini Team, Google
    date: "2025"
    note: "2.1 节：2.5 家族都是稀疏 MoE；Flash 及更小的模型用蒸馏，教师的下一词分布用 k-sparse 近似存储，训练数据吞吐和存储因此变成 k 倍。"
  - id: gemini3flash
    title: "Gemini 3 Flash Model Card"
    url: https://storage.googleapis.com/deepmind-media/Model-Cards/Gemini-3-Flash-Model-Card.pdf
    site: Google DeepMind
    date: 2025-12-17
    note: "Architecture 一栏只有一句：Gemini 3 Flash is based on Gemini 3 Pro。"
  - id: gemini3pro
    title: "Gemini 3 Pro Model Card"
    url: https://storage.googleapis.com/deepmind-media/Model-Cards/Gemini-3-Pro-Model-Card.pdf
    site: Google DeepMind
    note: "Gemini 3 Pro 是稀疏混合专家（MoE）transformer，原生多模态。"
  - id: gemini38flash
    title: "Gemini 3.8 Flash Model Card"
    url: https://deepmind.google/models/model-cards/gemini-3-8-flash/
    site: Google DeepMind
    date: 2026-09-02
    note: "based on Gemini 3.7 Flash；3.7 based on 3.6，3.5 based on 3 Flash。"
  - id: gemma3
    title: "Gemma 3 Technical Report"
    url: https://arxiv.org/abs/2503.19786
    author: Gemma Team, Google DeepMind
    date: "2025"
    note: "5 层局部注意力（窗口 1024）配 1 层全局注意力，GQA；所有型号都用知识蒸馏训练，预训练时每个 token 按教师概率采样 256 个 logits。"
  - id: dsv4
    title: "DeepSeek-V4-Flash 模型卡"
    url: https://huggingface.co/deepseek-ai/DeepSeek-V4-Flash
    site: Hugging Face
    note: "284B 总参数、13B 激活，1M 上下文；CSA + HCA 混合注意力；专家权重 FP4、其余 FP8；与 V4-Pro 的跑分对照表。"
  - id: dsv4-paper
    title: "DeepSeek-V4: Towards Highly Efficient Million-Token Context Intelligence"
    url: https://arxiv.org/abs/2606.19348
    author: DeepSeek-AI
    date: "2026"
    note: "V4-Pro 1.6T/49B、V4-Flash 284B/13B，都在 32T 以上 token 上预训练；1M 上下文时 V4-Pro 单 token 计算量是 V3.2 的 27%，KV 缓存是 10%。"
  - id: glm47flash
    title: "GLM-4.7-Flash 模型卡与 config.json"
    url: https://huggingface.co/zai-org/GLM-4.7-Flash
    site: Hugging Face
    note: "卡片只写 30B-A3B MoE；config 里 `model_type` 是 `glm4_moe_lite`，带 `kv_lora_rank: 512`，即 MLA。"
  - id: glm47
    title: "GLM-4.7 config.json"
    url: https://huggingface.co/zai-org/GLM-4.7/blob/main/config.json
    site: Hugging Face
    note: "92 层、宽 5120、160 个专家选 8 个，`num_key_value_heads: 8`（GQA）。"
  - id: mimo
    title: "MiMo-V2-Flash 模型卡"
    url: https://huggingface.co/XiaomiMiMo/MiMo-V2-Flash
    site: Hugging Face
    note: "309B 总参数、15B 激活；滑动窗口注意力与全局注意力 5:1、窗口 128，KV 缓存约降为 1/6；3 层 MTP，输出速度约 3 倍；后训练用多教师在线蒸馏（MOPD）。"
  - id: step35
    title: "Step 3.5 Flash 模型卡"
    url: https://huggingface.co/stepfun-ai/Step-3.5-Flash
    site: Hugging Face
    note: "196B 总参数、约 11B 激活；滑动窗口与全注意力 3:1；MTP-3 一次前向预测 4 个 token，典型 100–300 token/s。"
  - id: qwen-coder-flash
    title: "Qwen3-Coder-30B-A3B-Instruct 模型卡"
    url: https://huggingface.co/Qwen/Qwen3-Coder-30B-A3B-Instruct
    site: Hugging Face
    note: "在 Qwen Chat 里以 Qwen3-Coder-Flash 的名字出现。"
  - id: qwen3-32b
    title: "Qwen3-32B config.json"
    url: https://huggingface.co/Qwen/Qwen3-32B/blob/main/config.json
    site: Hugging Face
    note: "64 层、宽 5120、64 个查询头、8 个 KV 头、head_dim 128。"
  - id: moe
    title: "Outrageously Large Neural Networks: The Sparsely-Gated Mixture-of-Experts Layer"
    url: https://arxiv.org/abs/1701.06538
    author: Noam Shazeer et al.
    site: ICLR 2017
    date: "2017"
  - id: gqa
    title: "GQA: Training Generalized Multi-Query Transformer Models from Multi-Head Checkpoints"
    url: https://arxiv.org/abs/2305.13245
    author: Joshua Ainslie et al.
    site: EMNLP 2023
    date: "2023"
  - id: dsv2
    title: "DeepSeek-V2: A Strong, Economical, and Efficient Mixture-of-Experts Language Model"
    url: https://arxiv.org/abs/2405.04434
    author: DeepSeek-AI
    date: "2024"
    note: "提出多头潜在注意力（MLA）：把每个 token 的 K、V 压成一个低维向量存下来。"
  - id: dsv3
    title: "DeepSeek-V3 Technical Report"
    url: https://arxiv.org/abs/2412.19437
    author: DeepSeek-AI
    date: "2024"
    note: "多 token 预测（MTP）模块，推理时可当草稿用于投机解码。"
  - id: specdec
    title: "Fast Inference from Transformers via Speculative Decoding"
    url: https://arxiv.org/abs/2211.17192
    author: Yaniv Leviathan, Matan Kalman, Yossi Matias
    site: ICML 2023
    date: "2023"
  - id: hinton
    title: "Distilling the Knowledge in a Neural Network"
    url: https://arxiv.org/abs/1503.02531
    author: Geoffrey Hinton, Oriol Vinyals, Jeff Dean
    date: "2015"
---

打开 Google 的 Gemini 价目表[^gemini-pricing]，同一代模型分成三档：3.1 Pro 每输出一百万个 token 收 12 美元，3.8 Flash 收 3.75 美元，3.1 Flash-Lite 只收 1.5 美元。国内也一样：DeepSeek 有 V4-Pro 和 V4-Flash，智谱有 GLM-4.7 和 GLM-4.7-Flash，小米和阶跃干脆直接把新模型叫 MiMo-V2-Flash、Step-3.5-Flash。

<!-- TODO: 如有自己在 API 控制台看到的价格或用量截图，可以放在这里 -->

价钱差好几倍，名字只差一个词。于是很自然地会问：Flash 版到底是什么？是把大模型压缩了一下，还是层数少了？它和常听到的 FlashAttention 有没有关系？便宜这么多，丢掉的又是什么？

这篇文章先把名字理清，再从“模型每生成一个字要花什么”出发，看 Flash 版在哪几处下刀，以及每一刀的代价。

## 一、名字里的 Flash 指什么

### 和 FlashAttention 没有关系

先排除一个常见的误会。FlashAttention 是 2022 年 Tri Dao 等人发表的一种**计算方法**[^flashattn]：它把注意力（后面第四节会讲注意力是什么）拆成小块在 GPU 的高速缓存里算完，省掉了来回读写显存的时间。它算出来的结果和普通算法一个数都不差，只是更快、更省显存。今天几乎所有模型，不论大版小版，推理时都在用它或类似的实现。

所以 FlashAttention 是“同一道题换个快的算法”，而模型名里的 Flash 是**产品档位**，意思是“快、便宜的那一档”。两者同名，不是一回事。

### 各家 Flash 和大版的关系不一样

既然是档位名，每家怎么做出这一档，就各不相同。把各家公开的材料摆在一起，大致有四种关系：

<figure class="dd dd-figure dt-figure">
<div class="dt-panel">
<div class="dt-title" id="t1-title">各家的 Flash 版和它对应的大版</div>
<div class="dt-scroll">
<table class="dt" aria-labelledby="t1-title">
<colgroup><col style="width:27%"><col style="width:20%"><col><col style="width:20%"></colgroup>
<thead>
<tr><th scope="col">Flash 型号</th><th scope="col">对应的大版</th><th scope="col">两者是什么关系</th><th scope="col">公开到什么程度</th></tr>
</thead>
<tbody>
<tr class="dt-group"><th colspan="4" scope="rowgroup">闭源：只能看官方怎么说</th></tr>
<tr><th scope="row">Gemini 1.5 Flash</th><td>1.5 Pro</td><td class="acc">从 Pro 在线蒸馏</td><td>技术报告写明</td></tr>
<tr><th scope="row">Gemini 2.5 Flash、Flash-Lite</th><td>2.5 Pro</td><td>全家都是稀疏 MoE；<span class="acc">Flash 及更小的用蒸馏</span></td><td>技术报告写明，参数量不公开</td></tr>
<tr><th scope="row">Gemini 3 Flash、3.1 Flash-Lite、3.5–3.8 Flash</th><td>Gemini 3 Pro</td><td>“based on Gemini 3 Pro”</td><td>模型卡只有这一句</td></tr>
<tr class="dt-group"><th colspan="4" scope="rowgroup">开源：配置文件可以逐项核对</th></tr>
<tr><th scope="row">DeepSeek V4-Flash<br><span class="mono">284B · 激活 13B</span></th><td>V4-Pro<br><span class="mono">1.6T · 激活 49B</span></td><td>同一套设计整体缩小，各自独立预训练</td><td>权重、配置、技术报告</td></tr>
<tr><th scope="row">GLM-4.7-Flash<br><span class="mono">30B · 激活 3B</span></th><td>GLM-4.7<br><span class="mono">355B · 激活 32B</span></td><td>同代小号，注意力换了一种</td><td>权重、配置</td></tr>
<tr><th scope="row">Qwen3-Coder-Flash<br><span class="mono">30B · 激活 3B</span></th><td>Qwen3-Coder<br><span class="mono">480B · 激活 35B</span></td><td>同代小号（Flash 是聊天界面里的名字）</td><td>权重、配置</td></tr>
<tr><th scope="row">MiMo-V2-Flash、Step-3.5-Flash</th><td>没有</td><td>Flash 是整个模型的定位：快</td><td>权重、配置、技术报告</td></tr>
</tbody>
</table>
</div>
<p class="dt-note">B 表示十亿（billion）个参数，T 表示万亿；“激活”的意思第三节会讲。</p>
</div>
<figcaption>表 1 · 同样叫 Flash，各家和大版的关系并不一样</figcaption>
</figure>

表里有的 Flash 只是个显示名，比如 Qwen3-Coder-Flash 就是开源的 Qwen3-Coder-30B-A3B[^qwen-coder-flash]。这张表已经回答了一半问题：**Flash 不是一种固定的技术，各家用了不同的组合**。但这些组合里反复出现几个手法：更少的激活参数、更省的注意力、一次多猜几个 token、蒸馏。要看懂它们分别省的是什么，得先知道一次回答的成本花在了哪。

## 二、一次回答的成本花在哪

### 模型是一个字一个字往外吐的

大模型处理的单位叫 **token**，可以粗略理解成“一个字或半个词”。模型回答问题时并不是一下子写出整段话，而是每次只生成一个 token，把它接到末尾，再生成下一个。一段 500 字的回答，就要这样循环几百次。API 按 token 收钱，也是这个原因。

模型内部是几十层叠起来的计算，每一层主要有两个部件：

- **注意力**：让当前这个 token 回头看前面所有 token，决定该参考谁。
- **前馈网络**：对每个 token 做同样的一串乘法和加法。

这些乘法里用到的系数，就是模型的**参数**（也叫**权重**）。比如 $y = 0.8x_1 - 0.5x_2 + 0.1$ 里的 0.8、−0.5、0.1 就是三个参数。训练就是把这些系数一点点调对，模型“知道的东西”就存在它们里面。一个大模型有几百亿到上万亿个这样的数。模型里的每个 token 也用一串数字表示，叫**向量**，这串数字的长度叫模型的**宽度**。

### 每生成一个 token，都要把两样东西读一遍

这两个部件决定了生成一个 token 要从显存里读出多少数据：

<figure class="dd dd-figure">
<div class="dd-scroll">
<svg class="dd-svg" viewBox="0 0 800 424" xmlns="http://www.w3.org/2000/svg" role="img" aria-labelledby="fl1-title fl1-desc">
<title id="fl1-title">生成一个 token 要读的两样东西</title>
<desc id="fl1-desc">左边以 Qwen3-32B 为例：每层要读前馈和注意力的权重，以及前文所有 token 的 KV 缓存；64 层加起来，权重约 64 GB，12.8 万 token 前文的 KV 缓存约 32 GB，H100 每秒读 3.35 TB，所以单个用户每秒最多生成约 35 个 token。右边对比四个模型在同样前文下每个 token 要读的权重和 KV 缓存：Qwen3-32B 64 加 32 GB，GLM-4.7 65.8 加 46 GB，GLM-4.7-Flash 7.2 加 6.6 GB，MiMo-V2-Flash 29.6 加 2.8 GB。</desc>
<defs>
<marker id="fl1-arrow" markerWidth="8" markerHeight="8" refX="7" refY="4" orient="auto"><path class="dd-head" d="M1 1 L7 4 L1 7"/></marker>
<marker id="fl1-arrow-acc" markerWidth="8" markerHeight="8" refX="7" refY="4" orient="auto"><path class="dd-head-acc" d="M1 1 L7 4 L1 7"/></marker>
</defs>
<rect class="dd-frame" width="800" height="424" rx="10"/>
<rect class="dd-zone" x="16" y="16" width="360" height="392" rx="8"/>
<text class="t-zone" x="32" y="44">Qwen3-32B 生成 1 个 token</text>
<rect class="dd-card" x="32" y="64" width="328" height="110" rx="6"/>
<path class="dd-card-head" d="M32.6 96 V70 Q32.6 64.6 38 64.6 H354 Q359.4 64.6 359.4 70 V96 Z"/>
<text class="t-name" x="48" y="85">每一层要读的</text>
<text class="t-note" x="344" y="85" text-anchor="end">共 64 层</text>
<text class="t-mono" x="48" y="113">前馈网络的权重</text><text class="t-val" x="344" y="113" text-anchor="end">3 × 5120 × 25600</text>
<line class="dd-hair" x1="33" y1="122" x2="359" y2="122"/>
<text class="t-mono" x="48" y="139">注意力的权重</text><text class="t-val" x="344" y="139" text-anchor="end">约 0.5 亿个参数</text>
<line class="dd-hair" x1="33" y1="148" x2="359" y2="148"/>
<text class="t-mono t-acc" x="48" y="165">KV 缓存（历史）</text><text class="t-val t-acc" x="344" y="165" text-anchor="end">8 头 × 128 × 2 × 前文</text>
<rect class="dd-card" x="32" y="198" width="328" height="136" rx="6"/>
<path class="dd-card-head" d="M32.6 230 V204 Q32.6 198.6 38 198.6 H354 Q359.4 198.6 359.4 204 V230 Z"/>
<text class="t-name" x="48" y="219">64 层加起来</text>
<text class="t-note" x="344" y="219" text-anchor="end">每个数 2 字节</text>
<text class="t-mono" x="48" y="247">权重</text><text class="t-val" x="344" y="247" text-anchor="end">320 亿参数 ≈ 64 GB</text>
<line class="dd-hair" x1="33" y1="256" x2="359" y2="256"/>
<text class="t-mono t-acc" x="48" y="273">KV 缓存，前文 12.8 万</text><text class="t-val t-acc" x="344" y="273" text-anchor="end">≈ 32 GB</text>
<line class="dd-hair" x1="33" y1="282" x2="359" y2="282"/>
<text class="t-mono" x="48" y="299">H100 每秒能读</text><text class="t-val" x="344" y="299" text-anchor="end">3.35 TB</text>
<line class="dd-hair" x1="33" y1="308" x2="359" y2="308"/>
<text class="t-mono" x="48" y="325">单个用户最快</text><text class="t-val" x="344" y="325" text-anchor="end">约 35 token/秒</text>
<text class="t-say" x="32" y="364">权重每个 token 都一样；KV 缓存随前文变长。</text>
<text class="t-say" x="32" y="384">多个用户可以共读一次权重，KV 缓存各读各的。</text>
<rect class="dd-zone" x="392" y="16" width="392" height="392" rx="8"/>
<text class="t-zone" x="408" y="44">同样 12.8 万 token 前文，每个 token 读多少</text>
<text class="t-name" x="408" y="76">Qwen3-32B</text><text class="t-note" x="738" y="76" text-anchor="end">稠密</text>
<rect class="dd-bar" x="408" y="86" width="188.6" height="22" rx="3"/>
<rect class="dd-fill-acc" x="596.6" y="86" width="94.3" height="22" rx="3"/>
<text class="t-mono" x="408" y="126">权重 64 GB + <tspan class="t-acc">KV 32 GB</tspan></text>
<text class="t-name" x="408" y="148">GLM-4.7</text><text class="t-note" x="738" y="148" text-anchor="end">大版</text>
<rect class="dd-bar" x="408" y="158" width="193.9" height="22" rx="3"/>
<rect class="dd-fill-acc" x="601.9" y="158" width="135.5" height="22" rx="3"/>
<text class="t-mono" x="408" y="198">权重 65.8 GB + <tspan class="t-acc">KV 46 GB</tspan></text>
<text class="t-name" x="408" y="220">GLM-4.7-Flash</text><text class="t-note" x="738" y="220" text-anchor="end">Flash</text>
<rect class="dd-bar" x="408" y="230" width="21.2" height="22" rx="3"/>
<rect class="dd-fill-acc" x="429.2" y="230" width="19.4" height="22" rx="3"/>
<text class="t-mono" x="408" y="270">权重 7.2 GB + <tspan class="t-acc">KV 6.6 GB</tspan></text>
<text class="t-name" x="408" y="292">MiMo-V2-Flash</text><text class="t-note" x="738" y="292" text-anchor="end">Flash</text>
<rect class="dd-bar" x="408" y="302" width="87.2" height="22" rx="3"/>
<rect class="dd-fill-acc" x="495.2" y="302" width="8.2" height="22" rx="3"/>
<text class="t-mono" x="408" y="342">权重 29.6 GB + <tspan class="t-acc">KV 2.8 GB</tspan></text>
<text class="t-say" x="408" y="372">权重按每 token 激活的参数算（第三节）。</text>
<text class="t-say" x="408" y="392">GLM-4.7-Flash 读的总量约为大版的 1/8。</text>
</svg>
</div>
<figcaption>图 1 · 每生成一个 token，都要把激活的权重和全部历史读一遍；Flash 版在这两样上都少读</figcaption>
</figure>

**一是权重。** 每生成一个 token，都要拿用到的全部参数参与一次计算，也就是把它们从显存读到计算单元里。以阿里开源的 Qwen3-32B 为例[^qwen3-32b]，它有 320 亿个参数，每个参数占 2 个字节，生成一个 token 就要读约 64 GB。

**二是历史。** 注意力要回头看前面所有 token，所以每个 token 算过之后会留下一份“备查资料”存在显存里，叫 **KV 缓存**（第四节细讲）。上下文越长，这份资料越多。Qwen3-32B 在 12.8 万个 token 的上下文里，KV 缓存有 32 GB，每生成一个新 token 都要把它从头到尾读一遍。

GPU 每秒能读的数据量（叫**显存带宽**）是有上限的，一块 H100 大约每秒 3.35 TB。一个 token 要读将近 100 GB，单个用户一秒钟最多也就生成三十几个 token。服务商可以把很多用户的请求拼在一起算，让读一次权重服务多个人，但每个人自己那份 KV 缓存还是各读各的。

所以，想让模型又快又便宜，能下刀的地方就是这几处：

1. 每个 token **少读一点权重**；
2. 每个 token **少读一点历史**；
3. 读一遍能**多出几个 token**；
4. 前三刀砍下去模型会变笨，要想办法**把本事补回来**。

下面四节就按这四个目的来看 Flash 版是怎么做的。

## 三、少读权重：小一号的骨架和稀疏的专家

### 不改会怎样

最直接的办法是把模型整体做小：层数少一点，每层窄一点。参数少了，读得就少。问题是模型的知识和能力大体随参数量增长，直接做小，模型就变笨。

那有没有办法让模型“存”的参数多，每个 token“用”的参数少？

### 稀疏专家：存得多，用得少

这就是**混合专家**（Mixture of Experts，简称 MoE）[^moe]。它把每层的前馈网络拆成很多份小的前馈网络，叫**专家**；前面放一个小小的**路由器**，对每个 token 打分，只挑分数最高的几个专家来算，其余专家这一次完全不碰。

于是一个 MoE 模型有两个参数量：**总参数**（所有专家加起来，决定模型能装多少知识）和**激活参数**（每个 token 实际用到的，决定每个 token 要读多少）。模型名里的 “30B-A3B” 就是“总共 300 亿、每次激活 30 亿”的意思。

我们用 Python 的数值计算库 numpy 写了一层前馈网络做实验，宽度 2048，比较三种做法每生成一个 token 的耗时：

<figure class="dd dd-figure">
<div class="dd-scroll">
<svg class="dd-svg" viewBox="0 0 800 376" xmlns="http://www.w3.org/2000/svg" role="img" aria-labelledby="fl2-title fl2-desc">
<title id="fl2-title">MoE 每个 token 只读选中的专家</title>
<desc id="fl2-desc">左边：一层宽 2048 的前馈网络，生成一个 token 的耗时。1.5 GB 的稠密网络 42.4 毫秒；同样 1.5 GB 拆成 64 个专家、每次选 4 个的 MoE 3.8 毫秒；0.09 GB 的小稠密网络 2.7 毫秒。右边：一批里 token 越多，用到的专家越多，1 个 token 用 4 个，64 个 token 用到 63 个。</desc>
<defs>
<marker id="fl2-arrow" markerWidth="8" markerHeight="8" refX="7" refY="4" orient="auto"><path class="dd-head" d="M1 1 L7 4 L1 7"/></marker>
<marker id="fl2-arrow-acc" markerWidth="8" markerHeight="8" refX="7" refY="4" orient="auto"><path class="dd-head-acc" d="M1 1 L7 4 L1 7"/></marker>
</defs>
<rect class="dd-frame" width="800" height="376" rx="10"/>
<rect class="dd-zone" x="16" y="16" width="440" height="344" rx="8"/>
<text class="t-zone" x="32" y="44">每生成 1 个 token 的耗时（一层前馈，batch=1）</text>
<text class="t-name" x="32" y="72">大的稠密</text>
<text class="t-note" x="32" y="92">1.5 GB，全读</text>
<rect class="dd-bar" x="32" y="104" width="245.0" height="24" rx="3"/>
<text class="t-mono" x="287.0" y="121">42.4 ms</text>
<text class="t-name t-acc" x="32" y="164">MoE：64 个专家选 4 个</text>
<text class="t-note" x="32" y="184">共 1.5 GB，只读 0.09 GB</text>
<rect class="dd-fill-acc" x="32" y="196" width="21.8" height="24" rx="3"/>
<text class="t-mono t-acc" x="63.8" y="213">3.8 ms</text>
<text class="t-name" x="32" y="256">小的稠密</text>
<text class="t-note" x="32" y="276">0.09 GB，全读</text>
<rect class="dd-bar" x="32" y="288" width="15.9" height="24" rx="3"/>
<text class="t-mono" x="57.9" y="305">2.7 ms</text>
<text class="t-say" x="32" y="336">MoE 存的和大网络一样多，耗时却接近小网络。</text>
<rect class="dd-zone" x="472" y="16" width="312" height="344" rx="8"/>
<text class="t-zone" x="488" y="44">一批 token 一共用到几个专家</text>
<line class="dd-edge" x1="520" y1="296" x2="760" y2="296"/>
<line class="dd-hair" x1="520" y1="96" x2="760" y2="96"/>
<text class="t-note" x="512" y="100" text-anchor="end">64</text><text class="t-note" x="512" y="300" text-anchor="end">0</text>
<rect class="dd-fill-acc" x="532" y="283.5" width="30" height="12.5" rx="3"/>
<text class="t-mono" x="547" y="277.5" text-anchor="middle">4</text>
<text class="t-note" x="547" y="314" text-anchor="middle">1</text>
<rect class="dd-bar" x="578" y="249.1" width="30" height="46.9" rx="3"/>
<text class="t-mono" x="593" y="243.1" text-anchor="middle">15</text>
<text class="t-note" x="593" y="314" text-anchor="middle">4</text>
<rect class="dd-bar" x="624" y="158.5" width="30" height="137.5" rx="3"/>
<text class="t-mono" x="639" y="152.5" text-anchor="middle">44</text>
<text class="t-note" x="639" y="314" text-anchor="middle">16</text>
<rect class="dd-bar" x="670" y="99.1" width="30" height="196.9" rx="3"/>
<text class="t-mono" x="685" y="93.1" text-anchor="middle">63</text>
<text class="t-note" x="685" y="314" text-anchor="middle">64</text>
<rect class="dd-bar" x="716" y="96.0" width="30" height="200.0" rx="3"/>
<text class="t-mono" x="731" y="90.0" text-anchor="middle">64</text>
<text class="t-note" x="731" y="314" text-anchor="middle">256</text>
<text class="t-note" x="640.0" y="334" text-anchor="middle">一批里的 token 数</text>
</svg>
</div>
<figcaption>图 2 · MoE 的每 token 开销跟着激活参数走；但一批 token 多了，几乎每个专家都会被用到</figcaption>
</figure>

大的稠密网络有 1.5 GB 参数，每个 token 要 42 毫秒；把同样 1.5 GB 拆成 64 个专家、每次只选 4 个，耗时降到 3.8 毫秒，和只有 0.09 GB 的小稠密网络（2.7 毫秒）差不多。**MoE 的每 token 开销跟着激活参数走，而不是总参数**。

Google 在 Gemini 2.5 的技术报告里明说，整个 2.5 家族（Pro、Flash、Flash-Lite）都是稀疏 MoE，理由正是“把模型的总容量和每个 token 的计算、服务成本脱钩”[^gemini25]。Gemini 3 Pro 的模型卡也写着稀疏 MoE[^gemini3pro]。也就是说，**MoE 本身不是 Flash 独有的，大版也是 MoE**。

### Flash 版在 MoE 上再缩一圈

那 Flash 和大版的差别在哪？看开源模型的配置文件就清楚了。下表的数字是我们从 Hugging Face 上的 `config.json` 逐层数出来的，和官方公布的参数量对得上：

<figure class="dd dd-figure dt-figure">
<div class="dt-panel">
<div class="dt-title" id="t2-title">大版和 Flash 版的配置</div>
<div class="dt-scroll">
<table class="dt" aria-labelledby="t2-title">
<colgroup><col style="width:32%"><col><col class="hl"><col><col class="hl"></colgroup>
<thead>
<tr><th scope="col">项目</th><th scope="col">V4-Pro</th><th scope="col">V4-Flash</th><th scope="col">GLM-4.7</th><th scope="col">GLM-4.7-Flash</th></tr>
</thead>
<tbody>
<tr><th scope="row">层数</th><td class="mono num">61</td><td class="mono num hl">43</td><td class="mono num">92</td><td class="mono num hl">47</td></tr>
<tr><th scope="row">宽度（每个 token 的向量长度）</th><td class="mono num">7168</td><td class="mono num hl">4096</td><td class="mono num">5120</td><td class="mono num hl">2048</td></tr>
<tr><th scope="row">专家：共几个，每次选几个</th><td class="mono">384 选 6 +1</td><td class="mono hl">256 选 6 +1</td><td class="mono">160 选 8 +1</td><td class="mono hl">64 选 4 +1</td></tr>
<tr><th scope="row">注意力</th><td>压缩注意力</td><td class="hl">压缩注意力</td><td>GQA</td><td class="hl acc">MLA</td></tr>
<tr><th scope="row">总参数</th><td class="mono num">1.6T</td><td class="mono num hl">284B</td><td class="mono num">353B</td><td class="mono num hl">29.9B</td></tr>
<tr><th scope="row">每 token 激活参数</th><td class="mono num">49B</td><td class="mono num hl acc">13B</td><td class="mono num">32.9B</td><td class="mono num hl acc">3.6B</td></tr>
</tbody>
</table>
</div>
<p class="dt-note">“+1”是每个 token 都会用到的共享专家。DeepSeek 的参数量用官方数字；GLM 两列是我们按 config.json 数出来的主干参数（官方写 355B/32B、30B/3B）。</p>
</div>
<figcaption>表 2 · Flash 版仍是 MoE，但层数、宽度、专家数都缩了一圈</figcaption>
</figure>

DeepSeek V4 的两个版本用的是**同一套设计**：同样的注意力、同样“每个 token 选 6 个专家再加 1 个共享专家”，只是 Flash 层数从 61 降到 43，宽度从 7168 降到 4096，专家从 384 个降到 256 个[^dsv4]。激活参数从 490 亿降到 130 亿，约为四分之一。智谱的 GLM-4.7-Flash 缩得更狠[^glm47][^glm47flash]：层数减半，宽度降到 2048，每次只选 4 个专家，激活参数只剩大版的九分之一左右。

所以第一刀的答案是：**Flash 版仍是 MoE，但整体缩小了一号，尤其是激活参数**。总参数也跟着缩，这是代价的来源，第六节再讲。

### 每个参数也可以存得更小

还有一种少读的办法：每个参数少占几个字节。参数本来用 2 个字节（16 位）存，DeepSeek V4 把专家参数压到 4 位（半个字节）、其余压到 8 位[^dsv4]，要读的数据量直接少了一大半。不过 V4-Pro 的配置里专家也是 4 位，这一刀**大版小版一起砍**，不是 Flash 的区别。

### MoE 的省，主要省给单个用户

实验里还有一个现象值得一提。服务商会把很多用户的 token 拼成一批一起算，而不同 token 选中的专家不一样。我们统计了一批 token 一共用到多少个专家：1 个 token 用 4 个，16 个 token 就用到 44 个，64 个 token 几乎把 64 个专家全用上了。批越大，每次要读的权重越接近全部参数，MoE“少读”的优势越小，剩下的主要是“少算”。这也是为什么 Flash 版不只缩专家，还要在下一处——历史——上下功夫：**历史是每个用户各读各的，拼批省不掉**。

## 四、少读历史：KV 缓存的几种省法

### 注意力为什么要存历史

先说注意力在做什么。读到“他把苹果给了她，因为**她**饿了”，模型要知道“她”指谁，就得回头看前面的字。做法是：每个 token 生成一个“我想找什么”的向量，叫 **Q**（query，查询）；前面每个 token 各有一个“我是什么”的向量 **K**（key，键）和一个“我能提供什么”的向量 **V**（value，值）。Q 和每个 K 比一比相似度，越相似的那个 token，它的 V 就占越大的比重，加权平均后就是注意力的结果。

前面 token 的 K 和 V 不会变，所以算一次就存起来，后面每个新 token 都来查，这就是 **KV 缓存**。它的大小是：

$$\text{KV 缓存} = \text{层数} \times \text{上下文长度} \times (\text{每个 token 每层存的 K、V 数字个数}) \times \text{每个数字的字节数}$$

层数和上下文长度没法随便减，能动的是中间那一项：每个 token 每层存多少，以及要不要真的存“全部”上下文。

### 第一次尝试：多个头共用一份 K、V

注意力通常分成很多个“头”并行地找不同的东西（有的头找指代、有的头找语法）。最早的做法（多头注意力，MHA）是每个查询头各配一份 K、V。**分组查询注意力**（GQA）[^gqa]让几个查询头共用一份 K、V，比如 32 个查询头只配 8 份，KV 缓存就降到四分之一。Qwen3、GLM-4.7、Gemma 3 都用 GQA，它已经是标配，大版小版都有。

但 GQA 只是把常数变小，上下文翻一倍，缓存照样翻一倍。到了几十万、上百万 token 的上下文，还是太大。

### 再往前：压缩，或者干脆不看那么远

接下来的几种做法，就是 Flash 型号之间拉开差距的地方。我们把四种存法画在一起：

<figure class="dd dd-figure">
<div class="dd-scroll">
<svg class="dd-svg" viewBox="0 0 800 504" xmlns="http://www.w3.org/2000/svg" role="img" aria-labelledby="fl3-title fl3-desc">
<title id="fl3-title">KV 缓存的五种存法</title>
<desc id="fl3-desc">五行分别是 MHA、GQA、MLA、滑动窗口和压缩注意力。MHA 每个 token 每层存 8192 个数；GQA 让 4 个查询头共用一份，存 2048 个；MLA 把 K、V 压成 576 个数；滑动窗口只保留最近一段 token，更早的不存；压缩注意力把几个 token 合成一条存。</desc>
<defs>
<marker id="fl3-arrow" markerWidth="8" markerHeight="8" refX="7" refY="4" orient="auto"><path class="dd-head" d="M1 1 L7 4 L1 7"/></marker>
<marker id="fl3-arrow-acc" markerWidth="8" markerHeight="8" refX="7" refY="4" orient="auto"><path class="dd-head-acc" d="M1 1 L7 4 L1 7"/></marker>
</defs>
<rect class="dd-frame" width="800" height="504" rx="10"/>
<rect class="dd-zone" x="16" y="16" width="768" height="472" rx="8"/>
<text class="t-zone" x="32" y="44">每个前文 token 在一层里存什么（12 格代表最近的 12 个 token）</text>
<text class="t-name" x="32" y="84">MHA</text><text class="t-note" x="32" y="104">每个查询头一份 K、V</text>
<text class="t-mono" x="32" y="124">32 × 128 × 2 = 8192 个数</text>
<rect class="dd-card" x="352" y="72" width="28" height="56" rx="3"/>
<rect class="dd-card" x="384" y="72" width="28" height="56" rx="3"/>
<rect class="dd-card" x="416" y="72" width="28" height="56" rx="3"/>
<rect class="dd-card" x="448" y="72" width="28" height="56" rx="3"/>
<rect class="dd-card" x="480" y="72" width="28" height="56" rx="3"/>
<rect class="dd-card" x="512" y="72" width="28" height="56" rx="3"/>
<rect class="dd-card" x="544" y="72" width="28" height="56" rx="3"/>
<rect class="dd-card" x="576" y="72" width="28" height="56" rx="3"/>
<rect class="dd-card" x="608" y="72" width="28" height="56" rx="3"/>
<rect class="dd-card" x="640" y="72" width="28" height="56" rx="3"/>
<rect class="dd-card" x="672" y="72" width="28" height="56" rx="3"/>
<rect class="dd-card" x="704" y="72" width="28" height="56" rx="3"/>
<line class="dd-hair" x1="32" y1="138" x2="768" y2="138"/>
<text class="t-name" x="32" y="164">GQA</text><text class="t-note" x="32" y="184">4 个查询头共用一份</text>
<text class="t-mono" x="32" y="204">8 × 128 × 2 = 2048 个数</text>
<rect class="dd-card" x="352" y="180" width="28" height="28" rx="3"/>
<rect class="dd-card" x="384" y="180" width="28" height="28" rx="3"/>
<rect class="dd-card" x="416" y="180" width="28" height="28" rx="3"/>
<rect class="dd-card" x="448" y="180" width="28" height="28" rx="3"/>
<rect class="dd-card" x="480" y="180" width="28" height="28" rx="3"/>
<rect class="dd-card" x="512" y="180" width="28" height="28" rx="3"/>
<rect class="dd-card" x="544" y="180" width="28" height="28" rx="3"/>
<rect class="dd-card" x="576" y="180" width="28" height="28" rx="3"/>
<rect class="dd-card" x="608" y="180" width="28" height="28" rx="3"/>
<rect class="dd-card" x="640" y="180" width="28" height="28" rx="3"/>
<rect class="dd-card" x="672" y="180" width="28" height="28" rx="3"/>
<rect class="dd-card" x="704" y="180" width="28" height="28" rx="3"/>
<line class="dd-hair" x1="32" y1="218" x2="768" y2="218"/>
<text class="t-name" x="32" y="244">MLA</text><text class="t-note" x="32" y="264">K、V 压成一个小向量</text>
<text class="t-mono t-acc" x="32" y="284">512 + 64 = 576 个数</text>
<rect class="dd-fill-acc" x="352" y="278" width="28" height="10" rx="3"/>
<rect class="dd-fill-acc" x="384" y="278" width="28" height="10" rx="3"/>
<rect class="dd-fill-acc" x="416" y="278" width="28" height="10" rx="3"/>
<rect class="dd-fill-acc" x="448" y="278" width="28" height="10" rx="3"/>
<rect class="dd-fill-acc" x="480" y="278" width="28" height="10" rx="3"/>
<rect class="dd-fill-acc" x="512" y="278" width="28" height="10" rx="3"/>
<rect class="dd-fill-acc" x="544" y="278" width="28" height="10" rx="3"/>
<rect class="dd-fill-acc" x="576" y="278" width="28" height="10" rx="3"/>
<rect class="dd-fill-acc" x="608" y="278" width="28" height="10" rx="3"/>
<rect class="dd-fill-acc" x="640" y="278" width="28" height="10" rx="3"/>
<rect class="dd-fill-acc" x="672" y="278" width="28" height="10" rx="3"/>
<rect class="dd-fill-acc" x="704" y="278" width="28" height="10" rx="3"/>
<line class="dd-hair" x1="32" y1="298" x2="768" y2="298"/>
<text class="t-name" x="32" y="324">滑动窗口</text><text class="t-note" x="32" y="344">只留最近一段</text>
<text class="t-mono" x="32" y="364">窗口外的 token 不存</text>
<rect class="dd-derived" x="352" y="340" width="28" height="28" rx="3"/>
<rect class="dd-derived" x="384" y="340" width="28" height="28" rx="3"/>
<rect class="dd-derived" x="416" y="340" width="28" height="28" rx="3"/>
<rect class="dd-derived" x="448" y="340" width="28" height="28" rx="3"/>
<rect class="dd-derived" x="480" y="340" width="28" height="28" rx="3"/>
<rect class="dd-derived" x="512" y="340" width="28" height="28" rx="3"/>
<rect class="dd-derived" x="544" y="340" width="28" height="28" rx="3"/>
<rect class="dd-derived" x="576" y="340" width="28" height="28" rx="3"/>
<rect class="dd-card" x="608" y="340" width="28" height="28" rx="3"/>
<rect class="dd-card" x="640" y="340" width="28" height="28" rx="3"/>
<rect class="dd-card" x="672" y="340" width="28" height="28" rx="3"/>
<rect class="dd-card" x="704" y="340" width="28" height="28" rx="3"/>
<line class="dd-hair" x1="32" y1="378" x2="768" y2="378"/>
<text class="t-name" x="32" y="404">压缩注意力</text><text class="t-note" x="32" y="424">几个 token 压成一条</text>
<text class="t-mono" x="32" y="444">每 4 个（或 128 个）存一条</text>
<rect class="dd-card" x="352" y="420" width="124" height="28" rx="3"/>
<rect class="dd-card" x="480" y="420" width="124" height="28" rx="3"/>
<rect class="dd-card" x="608" y="420" width="28" height="28" rx="3"/>
<rect class="dd-card" x="640" y="420" width="28" height="28" rx="3"/>
<rect class="dd-card" x="672" y="420" width="28" height="28" rx="3"/>
<rect class="dd-card" x="704" y="420" width="28" height="28" rx="3"/>
<text class="t-say" x="32" y="468">格子高度代表存的数字多少（示意）；虚线格是窗口外、已经不存的 token。</text>
</svg>
</div>
<figcaption>图 3 · 前三种让每个 token 存得越来越少；后两种让远处的 token 不存或合并着存，缓存不再跟着前文同比例变大</figcaption>
</figure>

- **多头潜在注意力**（MLA）：DeepSeek-V2 提出[^dsv2]。不直接存 K、V，而是把它们压成一个 512 维的小向量存下来，用的时候再还原。GLM-4.7 用的是 GQA，**GLM-4.7-Flash 却换成了 MLA**[^glm47flash]，这是 Flash 版并非“等比例缩小”的一个例子。
- **滑动窗口**：大部分层只看最近的一小段（MiMo-V2-Flash 是 128 个 token，Step-3.5-Flash 是 512 个），每隔几层才放一层能看全部的“全局层”[^mimo][^step35]。窗口层的缓存不随上下文增长。谷歌开源的 Gemma 3 也是 5 层窗口配 1 层全局[^gemma3]。
- **压缩注意力**：DeepSeek V4 把远处的历史按 4 个或 128 个 token 一组压成一条，再配一个小窗口保留最近的原样[^dsv4-paper]。官方说，在 100 万 token 的上下文里，V4-Pro 的 KV 缓存只有上一代 V3.2 的 10%。

我们同样用 numpy 测了一层注意力，32 个查询头，看上下文从 1 千涨到 6.5 万时，每生成一个 token 要多久：

<figure class="dd dd-figure">
<div class="dd-scroll">
<svg class="dd-svg" viewBox="0 0 800 424" xmlns="http://www.w3.org/2000/svg" role="img" aria-labelledby="fl4-title fl4-desc">
<title id="fl4-title">注意力的耗时随前文变长</title>
<desc id="fl4-desc">横轴是前文长度，从 1K 到 64K token；纵轴是一层注意力生成一个 token 的耗时，对数刻度。MHA 从 11.7 毫秒涨到 1266 毫秒，GQA-8 从 2.0 涨到 209，GQA-4 从 1.6 涨到 134，5 层滑动窗口加 1 层全局的混合只从 0.44 涨到 21.5 毫秒。</desc>
<defs>
<marker id="fl4-arrow" markerWidth="8" markerHeight="8" refX="7" refY="4" orient="auto"><path class="dd-head" d="M1 1 L7 4 L1 7"/></marker>
<marker id="fl4-arrow-acc" markerWidth="8" markerHeight="8" refX="7" refY="4" orient="auto"><path class="dd-head-acc" d="M1 1 L7 4 L1 7"/></marker>
</defs>
<rect class="dd-frame" width="800" height="424" rx="10"/>
<rect class="dd-zone" x="16" y="16" width="768" height="392" rx="8"/>
<text class="t-zone" x="32" y="44">一层注意力，生成 1 个 token 的耗时（毫秒，纵轴每格 10 倍）</text>
<line class="dd-hair" x1="96" y1="344.0" x2="600" y2="344.0"/>
<text class="t-note" x="88" y="348.0" text-anchor="end">0.1</text>
<line class="dd-hair" x1="96" y1="276.0" x2="600" y2="276.0"/>
<text class="t-note" x="88" y="280.0" text-anchor="end">1</text>
<line class="dd-hair" x1="96" y1="208.0" x2="600" y2="208.0"/>
<text class="t-note" x="88" y="212.0" text-anchor="end">10</text>
<line class="dd-hair" x1="96" y1="140.0" x2="600" y2="140.0"/>
<text class="t-note" x="88" y="144.0" text-anchor="end">100</text>
<line class="dd-hair" x1="96" y1="72.0" x2="600" y2="72.0"/>
<text class="t-note" x="88" y="76.0" text-anchor="end">1000</text>
<text class="t-note" x="96.0" y="366" text-anchor="middle">1K</text>
<text class="t-note" x="264.0" y="366" text-anchor="middle">4K</text>
<text class="t-note" x="432.0" y="366" text-anchor="middle">16K</text>
<text class="t-note" x="600.0" y="366" text-anchor="middle">64K</text>
<text class="t-note" x="348.0" y="388" text-anchor="middle">前文长度（token）</text>
<path class="dd-edge" style="stroke-width:1.6;fill:none" d="M96.0 203.3 L264.0 160.2 L432.0 121.2 L600.0 65.0"/>
<circle class="dd-dot" cx="96.0" cy="203.3" r="3.5"/>
<circle class="dd-dot" cx="264.0" cy="160.2" r="3.5"/>
<circle class="dd-dot" cx="432.0" cy="121.2" r="3.5"/>
<circle class="dd-dot" cx="600.0" cy="65.0" r="3.5"/>
<text class="t-name" x="616" y="69.0">MHA（32 份 K、V）</text>
<text class="t-mono" x="616" y="87.0">1266 ms · 1024 MB</text>
<path class="dd-edge" style="stroke-width:1.6;fill:none" d="M96.0 255.0 L264.0 196.3 L432.0 160.9 L600.0 118.2"/>
<circle class="dd-dot" cx="96.0" cy="255.0" r="3.5"/>
<circle class="dd-dot" cx="264.0" cy="196.3" r="3.5"/>
<circle class="dd-dot" cx="432.0" cy="160.9" r="3.5"/>
<circle class="dd-dot" cx="600.0" cy="118.2" r="3.5"/>
<text class="t-name" x="616" y="112.2">GQA（8 份）</text>
<text class="t-mono" x="616" y="130.2">209 ms · 256 MB</text>
<path class="dd-edge" style="stroke-width:1.6;fill:none" d="M96.0 262.1 L264.0 224.3 L432.0 172.3 L600.0 131.4"/>
<circle class="dd-dot" cx="96.0" cy="262.1" r="3.5"/>
<circle class="dd-dot" cx="264.0" cy="224.3" r="3.5"/>
<circle class="dd-dot" cx="432.0" cy="172.3" r="3.5"/>
<circle class="dd-dot" cx="600.0" cy="131.4" r="3.5"/>
<text class="t-name" x="616" y="151.4">GQA（4 份）</text>
<text class="t-mono" x="616" y="169.4">134 ms · 128 MB</text>
<path class="dd-edge-acc" style="stroke-width:2.5;fill:none" d="M96.0 300.5 L264.0 268.4 L432.0 226.5 L600.0 185.4"/>
<circle class="dd-fill-acc" cx="96.0" cy="300.5" r="3.5"/>
<circle class="dd-fill-acc" cx="264.0" cy="268.4" r="3.5"/>
<circle class="dd-fill-acc" cx="432.0" cy="226.5" r="3.5"/>
<circle class="dd-fill-acc" cx="600.0" cy="185.4" r="3.5"/>
<text class="t-name t-acc" x="616" y="189.4">5 层窗口 + 1 层全局</text>
<text class="t-mono t-acc" x="616" y="207.4">22 ms · 22 MB</text>
</svg>
</div>
<figcaption>图 4 · GQA 只是把整条线往下平移；滑动窗口让大部分层不再随前文变慢</figcaption>
</figure>

MHA 随上下文线性变慢，6.5 万 token 时一层就要 1.3 秒；GQA 把它降到几分之一，但还是线性增长；5:1 的窗口混合（MiMo 的做法）只剩 21 毫秒，因为六层里只有一层的开销随上下文增长。

拿真实模型的配置算一下，差距就更具体了（12.8 万 token 上下文，每个数字 2 字节）：

<figure class="dd dd-figure dt-figure">
<div class="dt-panel">
<div class="dt-title" id="t3-title">12.8 万 token 前文时，一个请求的 KV 缓存有多大</div>
<div class="dt-scroll">
<table class="dt" aria-labelledby="t3-title">
<colgroup><col style="width:28%"><col><col style="width:18%"><col style="width:18%"></colgroup>
<thead>
<tr><th scope="col">模型</th><th scope="col">注意力</th><th scope="col">每个 token（KB）</th><th scope="col">12.8 万 token</th></tr>
</thead>
<tbody>
<tr class="dt-group"><th colspan="4" scope="rowgroup">大版和稠密模型</th></tr>
<tr><th scope="row">Qwen3-32B（稠密）</th><td>GQA，8 份</td><td class="mono num">256</td><td class="mono num">32 GB</td></tr>
<tr><th scope="row">GLM-4.7</th><td>GQA，8 份</td><td class="mono num">368</td><td class="mono num">46 GB</td></tr>
<tr><th scope="row">DeepSeek-V3</th><td>MLA</td><td class="mono num">68.6</td><td class="mono num">8.6 GB</td></tr>
<tr class="dt-group"><th colspan="4" scope="rowgroup">Flash 版和同级小模型</th></tr>
<tr><th scope="row">Qwen3-30B-A3B</th><td>GQA，4 份</td><td class="mono num">96</td><td class="mono num">12 GB</td></tr>
<tr><th scope="row">GLM-4.7-Flash</th><td>MLA</td><td class="mono num">52.9</td><td class="mono num acc">6.6 GB</td></tr>
<tr><th scope="row">Step-3.5-Flash</th><td>3 层窗口（512）+ 1 层全局</td><td class="mono num">48.5</td><td class="mono num acc">6.1 GB</td></tr>
<tr><th scope="row">MiMo-V2-Flash</th><td>5 层窗口（128）+ 1 层全局</td><td class="mono num">22.7</td><td class="mono num acc">2.8 GB</td></tr>
</tbody>
</table>
</div>
<p class="dt-note">按 config.json 计算，每个数 2 字节；带窗口的模型按 12.8 万 token 时的平均值算。DeepSeek V4 的压缩注意力结构较复杂，这里没有估，只引官方的相对数字。</p>
</div>
<figcaption>表 3 · Flash 型号普遍换了更省的注意力，同样的前文，缓存小到大版的几分之一</figcaption>
</figure>

同样是三四十亿激活参数，GLM-4.7-Flash 因为用了 MLA，KV 缓存只有 Qwen3-30B-A3B 的一半多；MiMo-V2-Flash 总参数是它们的十倍，靠滑动窗口把缓存压到了 2.8 GB，比 GLM-4.7 大版少了 94%。

### 代价：远处看得没那么清楚

窗口和压缩都是有损的：窗口层看不到远处，只能指望那几层全局层；压缩层看到的是一组 token 的“摘要”。所以这些模型都不敢把全局层全去掉。代价体现在需要从超长上下文里精确找回信息的任务上：在 DeepSeek 自己公布的 100 万 token 多轮检索测试（MRCR）里，V4-Flash 得 78.7 分，V4-Pro 得 83.5 分[^dsv4]。

## 五、少走几步：一次猜几个 token

### 不改会怎样

前两节都是让“每一步”更便宜，但步数没变：500 个 token 的回答还是要走 500 步，而且必须一步接一步，后一步要等前一步的结果。对单个用户来说，等待时间就是步数乘以每步的时间。

### 先猜后验

**多 token 预测**（MTP）在模型最后加几层小小的“草稿头”，让模型在生成当前 token 时顺便猜出后面几个。下一步主模型一次性把这几个猜测一起检验：猜对的直接收下，从第一个猜错的地方重新来。这个“先打草稿再一次性验证”的思路叫**投机解码**[^specdec]，DeepSeek-V3 把 MTP 做进了模型本身[^dsv3]。

<figure class="dd dd-figure">
<div class="dd-scroll">
<svg class="dd-svg" viewBox="0 0 800 464" xmlns="http://www.w3.org/2000/svg" role="img" aria-labelledby="fl5-title fl5-desc">
<title id="fl5-title">多 token 预测：先猜后验</title>
<desc id="fl5-desc">上面是普通生成：return、a、加号、b 四个 token 分四步生成。下面是多 token 预测：第 1 步生成 return，同时草稿头猜出 a、加号、c；第 2 步一次检验这三个猜测，a 和加号对了直接收下，c 错了改成 b，两步出了四个 token。</desc>
<defs>
<marker id="fl5-arrow" markerWidth="8" markerHeight="8" refX="7" refY="4" orient="auto"><path class="dd-head" d="M1 1 L7 4 L1 7"/></marker>
<marker id="fl5-arrow-acc" markerWidth="8" markerHeight="8" refX="7" refY="4" orient="auto"><path class="dd-head-acc" d="M1 1 L7 4 L1 7"/></marker>
</defs>
<rect class="dd-frame" width="800" height="464" rx="10"/>
<rect class="dd-zone" x="16" y="16" width="768" height="168" rx="8"/>
<text class="t-zone" x="32" y="44">普通生成：每读一遍权重，出 1 个 token</text>
<rect class="dd-card" x="40" y="64" width="160" height="36" rx="6"/>
<text class="t-note" x="54" y="87">第 1 步</text><text class="t-mono" x="186" y="87" text-anchor="end">"return"</text>
<line class="dd-edge" x1="200" y1="82" x2="222" y2="82" marker-end="url(#fl5-arrow)"/>
<rect class="dd-card" x="224" y="64" width="160" height="36" rx="6"/>
<text class="t-note" x="238" y="87">第 2 步</text><text class="t-mono" x="370" y="87" text-anchor="end">" a"</text>
<line class="dd-edge" x1="384" y1="82" x2="406" y2="82" marker-end="url(#fl5-arrow)"/>
<rect class="dd-card" x="408" y="64" width="160" height="36" rx="6"/>
<text class="t-note" x="422" y="87">第 3 步</text><text class="t-mono" x="554" y="87" text-anchor="end">" +"</text>
<line class="dd-edge" x1="568" y1="82" x2="590" y2="82" marker-end="url(#fl5-arrow)"/>
<rect class="dd-card" x="592" y="64" width="160" height="36" rx="6"/>
<text class="t-note" x="606" y="87">第 4 步</text><text class="t-mono" x="738" y="87" text-anchor="end">" b"</text>
<text class="t-say" x="40" y="136">4 个 token 走 4 步，每一步都要等上一步出结果。</text>
<text class="t-say" x="40" y="156">每一步都把激活的权重完整读一遍。</text>
<rect class="dd-zone" x="16" y="200" width="768" height="248" rx="8"/>
<text class="t-zone" x="32" y="228">多 token 预测：先猜后验</text>
<rect class="dd-card" x="40" y="248" width="352" height="36" rx="6"/>
<text class="t-note" x="54" y="271">第 1 步</text><text class="t-mono" x="108" y="271">"return"</text>
<text class="t-note" x="182" y="271">草稿头猜：</text>
<text class="t-mono t-acc" x="252" y="271">" a" " +" " c"</text>
<line class="dd-edge-acc" x1="392" y1="266" x2="406" y2="266" marker-end="url(#fl5-arrow-acc)"/>
<rect class="dd-card" x="408" y="248" width="352" height="36" rx="6"/>
<text class="t-note" x="422" y="271">第 2 步：一次检验 3 个猜测</text>
<rect class="dd-card" x="408" y="304" width="104" height="36" rx="6"/>
<text class="t-mono t-acc" x="422" y="327">" a" ✓</text>
<rect class="dd-card" x="528" y="304" width="104" height="36" rx="6"/>
<text class="t-mono t-acc" x="542" y="327">" +" ✓</text>
<rect class="dd-derived" x="648" y="304" width="104" height="36" rx="6"/>
<text class="t-mono" x="662" y="327">" c" ✗</text>
<rect class="dd-card" x="408" y="356" width="104" height="36" rx="6"/><text class="t-mono" x="422" y="379">改成 " b"</text>
<path class="dd-edge" style="fill:none" d="M712 340 V366 Q712 374 704 374 H514" marker-end="url(#fl5-arrow)"/>
<text class="t-say" x="40" y="320">检验 3 个和生成 1 个，读的是同一遍权重。</text>
<text class="t-say" x="40" y="340">猜对的直接收下，从第一个猜错处改正。</text>
<text class="t-say" x="40" y="416">结果：2 步出了 4 个 token。虚线框是猜错、被丢掉的草稿。</text>
</svg>
</div>
<figcaption>图 5 · 读一遍权重能检验好几个猜测，猜对多少就省下多少步</figcaption>
</figure>

关键在于：主模型检验 4 个 token 和生成 1 个 token，要读的权重是同样的一遍。猜对得越多，每读一遍权重产出的 token 就越多。

### 谁在用

DeepSeek V4 的 Pro 和 Flash、GLM-4.7 和 GLM-4.7-Flash 的配置里都带 1 层 MTP，所以这也不是 Flash 独有。把它用得更重的，是那些整个定位就是“快”的型号：MiMo-V2-Flash 带了 3 层 MTP，官方说输出速度约为原来的 3 倍[^mimo]；Step-3.5-Flash 用 MTP-3 一次前向出 4 个 token，典型速度每秒 100 到 300 个 token[^step35]。

### 代价

草稿头本身要算，猜错的部分白算了。代码、格式化输出这类好猜的内容收益大，开放式写作收益小。还要推理框架支持，比如 Step-3.5-Flash 的模型卡就提到，当时 vLLM 还没完整支持它的 MTP-3。

## 六、小了还不能太笨：蒸馏

### 不改会怎样

前三刀砍下去，模型的参数少了、看历史看得粗了。如果小模型和大模型用同样的方法训练，它就是一个普通的小模型，能力差一截。Flash 版要的是“小，但尽量像大版那么聪明”。

### 让大模型当老师

普通的训练方式是：给模型一段文字，让它猜下一个字，标准答案就是文字里实际出现的那个字，其余字一律算错。

**知识蒸馏**[^hinton]换了一种答案：先让大模型（老师）看同一段文字，给出它对下一个字的**整个概率分布**，比如“猫 60%、狗 25%、兔 8%……”，再让小模型（学生）去学这个分布。这样学生不只知道正确答案是什么，还知道“狗也说得通、兔子勉强、汽车完全不对”。一条训练样本带的信息多了很多。

存整个分布太贵（词表有十几万个词，每个位置都存十几万个概率），所以 Gemini 2.5 只存老师概率最高的 k 个词（k-sparse），代价是训练数据的存储和吞吐变成原来的 k 倍[^gemini25]；Gemma 3 是每个位置采样 256 个词[^gemma3]。

我们做了一个小实验来看蒸馏到底有多大用。设一个“老师”给出 256 个词上的概率分布，让一个容量小得多的“学生”分别用三种答案去学：普通答案（按老师的分布抽一个词当标准答案）、老师前 k 个词的概率、老师的完整分布。然后在没见过的上下文上量学生和老师有多像。衡量的尺子叫 **KL 散度**：学生给每个词的概率和老师的越接近，它就越小，完全一样时为 0。实验同样用 numpy 写成，学生的“脑容量”故意做得比老师小，模拟 Flash 和大版的关系：

<figure class="dd dd-figure">
<div class="dd-scroll">
<svg class="dd-svg" viewBox="0 0 800 456" xmlns="http://www.w3.org/2000/svg" role="img" aria-labelledby="fl6-title fl6-desc">
<title id="fl6-title">蒸馏：学老师的整个分布</title>
<desc id="fl6-desc">左边是同一个空的三种答案：普通答案只有“狗”一个字；老师前 2 个只保留猫和狗的概率；完整分布给每个字一个概率。右边是实验：横轴训练样本数，纵轴学生和老师的 KL 散度。100 条样本时普通答案 1.74、前 8 个 1.74、完整分布 0.96；300 条样本时普通答案 1.74、前 8 个 1.74、完整分布 0.78；1000 条样本时普通答案 1.74、前 8 个 1.41、完整分布 0.66；3000 条样本时普通答案 1.74、前 8 个 1.02、完整分布 0.61；10000 条样本时普通答案 0.86、前 8 个 0.87、完整分布 0.59；30000 条样本时普通答案 0.67、前 8 个 0.84、完整分布 0.59。</desc>
<defs>
<marker id="fl6-arrow" markerWidth="8" markerHeight="8" refX="7" refY="4" orient="auto"><path class="dd-head" d="M1 1 L7 4 L1 7"/></marker>
<marker id="fl6-arrow-acc" markerWidth="8" markerHeight="8" refX="7" refY="4" orient="auto"><path class="dd-head-acc" d="M1 1 L7 4 L1 7"/></marker>
</defs>
<rect class="dd-frame" width="800" height="456" rx="10"/>
<rect class="dd-zone" x="16" y="16" width="352" height="424" rx="8"/>
<text class="t-zone" x="32" y="44">“我家养了一只___”的三种答案（示意）</text>
<text class="t-name" x="32" y="78">普通答案</text><text class="t-note" x="32" y="96">语料里实际出现的那个字</text>
<rect class="dd-track" x="40" y="106" width="36" height="56" rx="3"/>
<text class="t-note" x="84" y="160">猫</text>
<rect class="dd-track" x="100" y="106" width="36" height="56" rx="3"/>
<rect class="dd-dot" x="100" y="106.0" width="36" height="56.0" rx="3"/>
<text class="t-note" x="144" y="160">狗</text>
<rect class="dd-track" x="160" y="106" width="36" height="56" rx="3"/>
<text class="t-note" x="204" y="160">兔</text>
<rect class="dd-track" x="220" y="106" width="36" height="56" rx="3"/>
<text class="t-note" x="264" y="160">鸟</text>
<rect class="dd-track" x="280" y="106" width="36" height="56" rx="3"/>
<text class="t-note" x="324" y="160">鱼</text>
<text class="t-name" x="32" y="198">老师前 2 个</text><text class="t-note" x="32" y="216">只存最大的 k 个，重新归一</text>
<rect class="dd-track" x="40" y="226" width="36" height="56" rx="3"/>
<rect class="dd-dot" x="40" y="250.6" width="36" height="31.4" rx="3"/>
<text class="t-note" x="84" y="280">猫</text>
<rect class="dd-track" x="100" y="226" width="36" height="56" rx="3"/>
<rect class="dd-dot" x="100" y="257.4" width="36" height="24.6" rx="3"/>
<text class="t-note" x="144" y="280">狗</text>
<rect class="dd-track" x="160" y="226" width="36" height="56" rx="3"/>
<text class="t-note" x="204" y="280">兔</text>
<rect class="dd-track" x="220" y="226" width="36" height="56" rx="3"/>
<text class="t-note" x="264" y="280">鸟</text>
<rect class="dd-track" x="280" y="226" width="36" height="56" rx="3"/>
<text class="t-note" x="324" y="280">鱼</text>
<text class="t-name t-acc" x="32" y="318">老师的完整分布</text><text class="t-note" x="32" y="336">每个字都有一个概率</text>
<rect class="dd-track" x="40" y="346" width="36" height="56" rx="3"/>
<rect class="dd-fill-acc" x="40" y="376.8" width="36" height="25.2" rx="3"/>
<text class="t-note" x="84" y="400">猫</text>
<rect class="dd-track" x="100" y="346" width="36" height="56" rx="3"/>
<rect class="dd-fill-acc" x="100" y="382.4" width="36" height="19.6" rx="3"/>
<text class="t-note" x="144" y="400">狗</text>
<rect class="dd-track" x="160" y="346" width="36" height="56" rx="3"/>
<rect class="dd-fill-acc" x="160" y="396.4" width="36" height="5.6" rx="3"/>
<text class="t-note" x="204" y="400">兔</text>
<rect class="dd-track" x="220" y="346" width="36" height="56" rx="3"/>
<rect class="dd-fill-acc" x="220" y="398.6" width="36" height="3.4" rx="3"/>
<text class="t-note" x="264" y="400">鸟</text>
<rect class="dd-track" x="280" y="346" width="36" height="56" rx="3"/>
<rect class="dd-fill-acc" x="280" y="399.8" width="36" height="2.2" rx="3"/>
<text class="t-note" x="324" y="400">鱼</text>
<text class="t-say" x="32" y="424">完整分布还告诉学生：狗也说得通，鱼不太像。</text>
<rect class="dd-zone" x="384" y="16" width="400" height="424" rx="8"/>
<text class="t-zone" x="400" y="44">学生和老师差多少（KL 散度，越小越像）</text>
<line class="dd-hair" x1="440" y1="360.0" x2="760" y2="360.0"/>
<text class="t-note" x="432" y="364.0" text-anchor="end">0</text>
<line class="dd-hair" x1="440" y1="216.0" x2="760" y2="216.0"/>
<text class="t-note" x="432" y="220.0" text-anchor="end">1</text>
<line class="dd-hair" x1="440" y1="72.0" x2="760" y2="72.0"/>
<text class="t-note" x="432" y="76.0" text-anchor="end">2</text>
<text class="t-note" x="440.0" y="380" text-anchor="middle">100</text>
<text class="t-note" x="501.6" y="380" text-anchor="middle">300</text>
<text class="t-note" x="569.2" y="380" text-anchor="middle">1,000</text>
<text class="t-note" x="630.8" y="380" text-anchor="middle">3,000</text>
<text class="t-note" x="698.4" y="380" text-anchor="middle">10,000</text>
<text class="t-note" x="760.0" y="380" text-anchor="middle">30,000</text>
<text class="t-note" x="600.0" y="400" text-anchor="middle">训练样本数</text>
<path class="dd-edge" style="stroke-width:1.2;fill:none;stroke-dasharray:4 4" d="M440.0 112.2 L760.0 112.2"/>
<text class="t-note" x="452" y="102.2">均匀瞎猜 1.72</text>
<path class="dd-edge" style="stroke-width:1.6;fill:none" d="M440.0 108.8 L501.6 108.8 L569.2 108.8 L630.8 108.8 L698.4 236.5 L760.0 263.6"/>
<circle class="dd-dot" cx="440.0" cy="108.8" r="3.5"/>
<circle class="dd-dot" cx="501.6" cy="108.8" r="3.5"/>
<circle class="dd-dot" cx="569.2" cy="108.8" r="3.5"/>
<circle class="dd-dot" cx="630.8" cy="108.8" r="3.5"/>
<circle class="dd-dot" cx="698.4" cy="236.5" r="3.5"/>
<circle class="dd-dot" cx="760.0" cy="263.6" r="3.5"/>
<text class="t-name" x="630" y="312">普通答案</text>
<path class="dd-edge" style="stroke-width:1.6;fill:none" d="M590.0 308.0 L620.0 308.0"/>
<path class="dd-edge" style="stroke-width:1.6;fill:none;stroke-dasharray:6 4" d="M440.0 108.8 L501.6 108.8 L569.2 157.3 L630.8 213.8 L698.4 234.3 L760.0 238.8"/>
<circle class="dd-dot" cx="440.0" cy="108.8" r="3.5"/>
<circle class="dd-dot" cx="501.6" cy="108.8" r="3.5"/>
<circle class="dd-dot" cx="569.2" cy="157.3" r="3.5"/>
<circle class="dd-dot" cx="630.8" cy="213.8" r="3.5"/>
<circle class="dd-dot" cx="698.4" cy="234.3" r="3.5"/>
<circle class="dd-dot" cx="760.0" cy="238.8" r="3.5"/>
<text class="t-name" x="630" y="330">老师前 8 个</text>
<path class="dd-edge" style="stroke-width:1.6;fill:none;stroke-dasharray:6 4" d="M590.0 326.0 L620.0 326.0"/>
<path class="dd-edge-acc" style="stroke-width:2.5;fill:none" d="M440.0 222.0 L501.6 247.9 L569.2 264.9 L630.8 271.6 L698.4 274.9 L760.0 275.7"/>
<circle class="dd-fill-acc" cx="440.0" cy="222.0" r="3.5"/>
<circle class="dd-fill-acc" cx="501.6" cy="247.9" r="3.5"/>
<circle class="dd-fill-acc" cx="569.2" cy="264.9" r="3.5"/>
<circle class="dd-fill-acc" cx="630.8" cy="271.6" r="3.5"/>
<circle class="dd-fill-acc" cx="698.4" cy="274.9" r="3.5"/>
<circle class="dd-fill-acc" cx="760.0" cy="275.7" r="3.5"/>
<text class="t-name t-acc" x="630" y="348">完整分布</text>
<path class="dd-edge-acc" style="stroke-width:2.5;fill:none" d="M590.0 344.0 L620.0 344.0"/>
<text class="t-say" x="400" y="424">300 条完整分布（0.78）胜过 10,000 条普通答案（0.86）。</text>
</svg>
</div>
<figcaption>图 6 · 同样多的样本，学老师的完整分布比学普通答案像得多；只存前几个会丢掉一部分好处</figcaption>
</figure>

用普通答案时，100、300 乃至 3000 条样本，学生都没学到东西：KL 一直停在 1.74，和“每个词概率都一样”的瞎猜没有区别，再往下训只会把训练样本死记下来（这叫**过拟合**），对没见过的上下文反而更差。要到 1 万条，它才开始像老师。

用老师的完整分布，100 条就降到 0.96；300 条时的 0.78，已经好过普通答案 1 万条时的 0.86。**一条带完整分布的样本，抵得上几十条普通样本。**

只存前 k 个词的做法介于两者之间。存前 32 个和完整分布差不多；只存前 8 个，样本少时帮助很大，样本多了反而不如普通答案（3 万条时 0.84 对 0.67），因为被截掉的那些小概率词，学生永远学不到。k 取多大，就是在“存储变成 k 倍”和“学得像不像”之间取舍。

### 谁在蒸馏

Google 写得最明白：Gemini 1.5 Flash 从 1.5 Pro 在线蒸馏而来[^gemini15]，2.5 代“Flash 及更小的模型”都用蒸馏[^gemini25]。Gemini 3 之后的模型卡只写“based on Gemini 3 Pro”[^gemini3flash]，没有再说怎么来的。

但蒸馏也不是 Flash 专属。DeepSeek V4 的两个版本是各自在 32 万亿以上的 token 上**独立预训练**的，官方没有说 Flash 是从 Pro 蒸馏出来的；两者后训练都用了“先训几个领域专家模型，再在线蒸馏合成一个”的方法[^dsv4]。小米 MiMo-V2-Flash 的后训练也是多教师在线蒸馏[^mimo]。

### 代价：老师教得会推理，教不会记忆

蒸馏能把“怎么想”传下去，但“记住多少事”还是受学生的总参数限制。DeepSeek V4 的跑分很能说明问题：

<figure class="dd dd-figure dt-figure">
<div class="dt-panel">
<div class="dt-title" id="t4-title">DeepSeek V4：Flash 和 Pro 的跑分（最高推理档）</div>
<div class="dt-scroll">
<table class="dt" aria-labelledby="t4-title">
<colgroup><col style="width:24%"><col><col style="width:13%"><col style="width:13%"><col style="width:11%"></colgroup>
<thead>
<tr><th scope="col">测试</th><th scope="col">考什么</th><th scope="col">Flash</th><th scope="col">Pro</th><th scope="col">差</th></tr>
</thead>
<tbody>
<tr class="dt-group"><th colspan="5" scope="rowgroup">推理、数学、代码</th></tr>
<tr><th scope="row">MMLU-Pro</th><td>多学科综合题</td><td class="mono num">86.2</td><td class="mono num">87.5</td><td class="mono num">1.3</td></tr>
<tr><th scope="row">GPQA Diamond</th><td>研究生难度理科题</td><td class="mono num">88.1</td><td class="mono num">90.1</td><td class="mono num">2</td></tr>
<tr><th scope="row">LiveCodeBench</th><td>编程竞赛题</td><td class="mono num">91.6</td><td class="mono num">93.5</td><td class="mono num">1.9</td></tr>
<tr><th scope="row">SWE-bench Verified</th><td>修真实仓库的 bug</td><td class="mono num">79.0</td><td class="mono num">80.6</td><td class="mono num">1.6</td></tr>
<tr class="dt-group"><th colspan="5" scope="rowgroup">记忆和长任务</th></tr>
<tr><th scope="row">SimpleQA-Verified</th><td>冷门事实问答</td><td class="mono num">34.1</td><td class="mono num">57.9</td><td class="mono num bad hl">23.8</td></tr>
<tr><th scope="row">Terminal Bench 2.0</th><td>自主操作终端完成任务</td><td class="mono num">56.9</td><td class="mono num">67.9</td><td class="mono num bad">11</td></tr>
<tr><th scope="row">MRCR 1M</th><td>百万 token 里找回细节</td><td class="mono num">78.7</td><td class="mono num">83.5</td><td class="mono num warn">4.8</td></tr>
</tbody>
</table>
</div>
<p class="dt-note">数字来自 DeepSeek-V4-Flash 模型卡，均为官方自测。</p>
</div>
<figcaption>表 4 · 推理题只差一两分，记忆题差了 24 分</figcaption>
</figure>

推理、数学、代码这类题，Flash 和 Pro 只差一两分；考事实知识的 SimpleQA 却差了 24 分，需要长时间自主操作终端的 Terminal Bench 差了 11 分。总参数从 1.6 万亿降到 2840 亿，丢掉的主要是记忆，而不是推理。

## 七、Flash 版到底差在哪

### 回到开头

现在可以回答开头的问题了。Flash 和 FlashAttention 无关；它也不是把大模型“压缩”一下，而是在四个地方各下一刀，每家下刀的位置和深浅不一样：

<figure class="dd dd-figure dt-figure">
<div class="dt-panel">
<div class="dt-title" id="t5-title">Flash 版的四刀</div>
<div class="dt-scroll">
<table class="dt" aria-labelledby="t5-title">
<colgroup><col style="width:15%"><col><col style="width:28%"><col style="width:22%"></colgroup>
<thead>
<tr><th scope="col">下刀处</th><th scope="col">Flash 版的做法</th><th scope="col">大版也用吗</th><th scope="col">代价</th></tr>
</thead>
<tbody>
<tr><th scope="row">少读权重</th><td>层数、宽度、专家数一起缩：V4 激活 49B → 13B，GLM 32.9B → 3.6B</td><td>MoE、低精度：大版也用。<br><span class="acc">整体缩小：Flash 才有</span></td><td>总参数少，记住的东西少</td></tr>
<tr><th scope="row">少读历史</th><td>GLM-4.7-Flash 换 MLA；MiMo 5:1 窗口；Step 3:1 窗口；V4 压缩注意力</td><td>GQA：大家都用。<br>V4 大小版同一种压缩</td><td>超长前文里精确找回变弱</td></tr>
<tr><th scope="row">少走几步</th><td>MiMo 3 层 MTP；Step 一次出 4 个 token</td><td>V4、GLM 大版也带 1 层</td><td>猜错白算，收益看内容</td></tr>
<tr><th scope="row">补回本事</th><td>Gemini 1.5、2.5 Flash 从 Pro 蒸馏；MiMo 多教师蒸馏</td><td>V4 大小版后训练都蒸馏；V4-Flash 是独立预训练的</td><td>推理传得下去，记忆传不下去</td></tr>
</tbody>
</table>
</div>
</div>
<figcaption>表 5 · Flash 的区别不在某一项独门技术，而在每一处都砍得更深</figcaption>
</figure>

### 哪些是官方说的，哪些是推测

开源模型（DeepSeek、GLM、MiMo、Step、Qwen）的架构写在配置文件里，可以逐项核对。闭源的 Gemini 只能看官方公开的几句话：

- **官方说过的**：1.5 Flash 从 1.5 Pro 在线蒸馏，注意力和前馈并行计算；2.5 全家族是稀疏 MoE，Flash 及以下用 k-sparse 蒸馏；3 代 Flash 和 Flash-Lite “based on Gemini 3 Pro”，3 Pro 是稀疏 MoE；3.5 到 3.8 Flash 一代基于一代[^gemini38flash]。
- **没说过的**：参数量、层数、激活参数、注意力用什么、3 代以后还蒸不蒸馏。网上流传的“Gemini 3 Flash 有 1.2 万亿参数”之类的数字都是第三方猜的。
- **合理但未经证实的推测**：Gemini 的开源兄弟 Gemma 3 用了 5:1 的滑动窗口和全量蒸馏[^gemma3]，Gemini Flash 很可能也用了类似的长上下文省法和蒸馏，但这只是推测。

### 什么时候用 Flash

从上面的代价倒推，选型就比较清楚了：

- **用 Flash**：量大、要快、上下文不算极长、靠推理多于靠冷知识的活。比如分类、抽取、改写、代码补全、大批量的文档处理，以及 agent 里那些高频的小步骤。
- **用完整版**：需要大量事实知识（冷门问答、专业领域）、需要在几十万 token 里精确找回细节、长链条的自主任务，或者 Flash 已经反复做错的时候。

## 小结

1. **模型名里的 Flash 是什么？** 是“快、便宜”的产品档位，不是 FlashAttention。各家做法不同：Google 从 Pro 蒸馏或“基于 Pro”；DeepSeek 同一设计缩小后独立训练；GLM、Qwen 的 Flash 是同代的小号开源模型；MiMo、Step 直接用 Flash 命名整个模型。
2. **成本花在哪？** 每生成一个 token，都要把激活的权重和全部历史（KV 缓存）读一遍，显存带宽是瓶颈。
3. **怎么少读权重？** 大小版都是 MoE，Flash 把层数、宽度、专家数整体缩小，激活参数降到大版的四分之一到九分之一。低精度存储大小版一起用。
4. **怎么少读历史？** GQA 是标配；Flash 型号更多用 MLA、滑动窗口、压缩注意力，把 12.8 万 token 时的 KV 缓存从几十 GB 压到几 GB，代价是超长上下文的精确检索变弱。
5. **怎么少走几步？** 多 token 预测让读一遍权重多出几个 token，定位为“快”的型号用得最重。
6. **变小了怎么不变笨？** 蒸馏让学生学老师的整个概率分布，推理能力能传下去，记忆还是受总参数限制，所以 Flash 和大版在知识题上差得最多。

## 参考资料
