五个可以动手的小实验,从"一组数怎么压成 4 bit"讲到"DeepSeek 为什么能把 KV 砍掉 90%"。每个实验都是真实计算,不是示意图:拖滑块,看数字怎么变。
一个 fp16 的数能表示 6 万多种取值;int4 只有 16 个档位。量化做的事是:看这组数的最小值和最大值,在两者之间均匀铺 2bits 条网格线,每个数吸附到最近的一条上。存的时候只存"第几条线"(整数 q),外加整组共享的 scale(网格间距)和 zero(起点)。
下面是一组 16 个真实采样值。上排是原值,下排是量化后的位置,竖线长度就是误差。试着把 bits 拉到 2,再打开"离群值"开关。
开离群值后注意"实际被用到的档位":一个 ×12 的数把网格撑宽,其余 15 个数挤在最左边两三格里,分辨率全被那一个数吃掉了。这就是 KV 量化的核心难题。
注意力的计算是:query 和每个 token 的 K 做点积得到分数,softmax 之后变成权重,再用权重加权求和 V。softmax 是指数放大:分数最高的几个 token 拿走几乎全部权重,其余 token 的 K 偏一点,权重本来就接近 0,偏了还是接近 0。V 那一侧是加权平均,舍入误差正负抵消。
这里是 1 个 query 对 12 个 token 的真实 attention。深色是原始权重,浅色是量化 K/V 之后的权重。看右边"输出余弦相似度"在几 bit 时才开始明显下降。
经验规律:int8 几乎无损,int4 相似度仍在 0.99 以上,int2 开始翻车。而且"只 V"通常比"只 K"更耐压:K 的误差经过点积和 softmax 被指数放大,V 的误差只是被平均。
把一层的 KV cache 画成矩阵:每行是一个 token,每列是一个通道。于是"K 的离群值长在通道上"的意思就是:K 矩阵里有几条竖着的亮线,某几个固定的通道,对所有 token 都偏大。原因有两个:RoPE 位置编码是把相邻两维当作一个二维平面做旋转,某些低频维度在旋转后数值系统性地偏大;另外,某些注意力头会把少数几个维度当"锚点"用,这些维度的 K 值天生就大。无论哪种原因,离群出现的位置由通道决定,和这个 token 是什么字无关。
V 不一样。V 不经过 RoPE,也不参与点积打分,它就是"要被取走的内容"。V 矩阵里的大值是零散的:某个 token 整行偏大(这个 token 携带的信息多),或者某个单独的位置偏大,但不会在某一列上整齐排开。所以 V 的离群值要说有规律的话,更接近按 token(按行)分布。
这决定了分组方式。量化时的一组共享一个 scale,分组的目标是让离群值和正常值不在同一组里。按 token 分组(每行一个 scale)时,K 的每一行都包含那几个大通道,每一行的 scale 都被撑大,没有一行逃得掉。按通道分组(每列一个 scale)时,大通道自成一组,它的 scale 大是它自己的事,其余列不受影响。对 V 则反过来:按行分组时,偏大的那几行自成一组,其余行正常;按列分组反而让每一列都混进几个大值。下面的实验可以把两种分组各试一遍,表格里会给出误差数字。
| 矩阵 | 按 token 分组 | 按通道分组 | 更好的选择 |
|---|---|---|---|
| K | — | — | — |
| V | — | — | — |
表里是当前 bits 下的相对均方误差。KIVI 论文的结论就是这张表:K 按通道、V 按 token,int2 都能用。KVQuant 更进一步,把那几个极端值单独用 fp16 存成稀疏矩阵,剩下的再量化。
每个 token 在每一层都要存一份 K 和一份 V:2 × 层数 × KV头数 × 头维度 × 每个数的字节数。GQA(分组查询注意力)让多个 query 头共用一个 KV 头,所以 70B 模型只有 8 个 KV 头而不是 64 个,这本身已经是一次 8 倍压缩。MLA 走得更远:每层只存一个 512 维的潜向量加 64 维的位置信息。
横条满格 = 一张 H100 的 80 GB(还没算模型权重本身)。把 70B 拉到 128K、16 bit,再切到 4 bit,再换成 MLA,感受三种压缩的量级。
量化压的是每个数的位宽,上限大约 4~8 倍。MLA(Multi-head Latent Attention)换了个思路:K 和 V 本来就是从同一个隐状态 h 乘两个矩阵投影出来的,信息高度冗余。那就不存 K 和 V,只存一个低维潜向量 c = W_down · h;用的时候再 K = W_uk · c、V = W_uv · c 展开。展开矩阵 W_uk 可以提前乘进 query 侧(矩阵乘法结合律),所以解码时根本不用真的把 K 还原出来。
下面的方块面积按维度数画。拖 d_c 看潜向量多小时还划算;DeepSeek-V3 选的是 512。
MLA 和量化正交:先用 MLA 把"数的个数"砍掉几十倍,再对潜向量做 int4/int8 量化,两段收益相乘。代价是 MLA 必须在训练时就这样设计,没法给已训好的模型事后加上;量化可以。