把 284B 的模型剪成 70B:我在量化空间里做的一场手术
把 284B 的模型剪成 70B:我在量化空间里做的一场手术
一句话:我在给「卷心菜」做一个 284B → ~70B 的瘦身手术,但规矩极其苛刻——只能在量化(FP8/FP4)空间里动刀,只准删、不准算。这篇文章记录这场手术里遇到的最有意思的事:被 58% token「改嫁」吓到的 hash 路由、在一个只有 2GB 内存的壳子里算 256×256 相似度矩阵、以及一个脾气很大的 vLLM 裁判。
一、背景:为什么是「手术」而不是「训练」
我盯上了 DeepSeek-V4-Flash-0731:284B 参数、13B 激活的 MoE,256 个路由专家,43 层。目标是把它的量化版剪成一个 ~70B 的学生模型,再做后训练。
最直觉的做法是「全量反量化再剪枝」:把 284B 的权重统统转成 bf16 再动手。但这就意味着要物化一份 600GB 级别的全量 bf16 权重——存储、内存、搬运全是灾难,而且浪费了一开始就已经做好的量化权重。
所以最后拍板的路线很「外科手术」:直接在官方 FP8/FP4 量化权重的原生空间里做结构修改——删除张量条目、改 config 的层数和专家数。官方量化版只有 ~200GB,租 4×80GB 或 2×141GB 就能整机加载,比 8×96GB 的基线省一半成本。
但「直接动刀」四个字背后,是一条又一条踩出来的坑。下面这几件是我觉得最值得讲的。
二、有意思的事 ①:2.5G 口没跑满,我觉得是罪过
第一步是下载 ~200GB 的官方量化权重。aria2c 默认配置跑起来,我看了一眼速度,觉得不对劲——机器明明有 2.5Gbps 的带宽口,却只吃到一小半。
对一个搞性能的人,2.5G 口没跑满 = 犯罪现场。我改成 8 个文件并发、每个文件 8 个连接,峰值直接干到 643MB/s,把整个下载周期砍掉了大半。
体感是:带宽这种资源,你不折腾它就永远在浪费。同样的道理在后面反复出现——算力、内存、显存,全都吃不满,全都在等你那句「我觉得不对劲」。
三、有意思的事 ②:手术台上的红线,只准删不准算
这大概是整个项目最反直觉的一条规矩。
一般人的第一反应是:剪枝嘛,把不重要的权重按重要性缩放一下、混一混、插一插就完事了。但这条路线被红线死死封住:
禁止对量化权重做任何数值运算(缩放、混合、插值)。
为什么?因为 FP8/FP4 的 weight 和 scale 是打包在一起的,每两个字节编码一个权重。你对它做任何数学变换,都是在最脆弱的位级格式上动手脚——动一个字节,可能让整张量错位,出来的就是纯噪声,而且极难调试。
所以手术的定义被压缩成了两件事:删除张量条目 + 修改 config。深度剪枝就是整层删掉条目、改 config 的层数;专家裁剪就是把 40 层学习型 router 的 256 专家删到 64、把 gate 权重同步裁行。全程没有任何一个数值运算。
唯一的有界豁免是 hash 层:先反量化、合并、再重量化。这是红线里的白名单,只给这一处。
还有个配套纪律:手术脚本必须先拿单个 shard 试剪 + vLLM 加载冒烟通过,才准上全量。不做数值运算、每一步先验证再上量——这两条加在一起,才是「量化空间手术」能成立的原因。
四、有意思的事 ③:前 3 层是 hash 路由,一刀下去 58% 的 token 改嫁
这是我这一路最得意、也最「幸好发现了」的一个坑。
43 层里,前 3 层用的是 hash 路由:token 不是被网络学出来的 router 分配到专家,而是按一个固定哈希函数映射到某个专家桶。也就是说,这几层的「专家分配」跟权重完全无关,纯粹是算术。
那我把它从 256 专家直接删到 64 会怎样?想象一下:桶数变了,哈希取模的结果全变了。我拿校准语料跑了一遍 tid2eid 负载分析,结果触目惊心——58% 的 token 会被「改嫁」到一个和原来完全不同的专家。
这已经不是损伤了,是换血。裸裁当场判死刑:「裸裁已死(58% 改嫁,不测)」——连测都不测,直接否决。
于是为这 3 层单独设计了两个方案:
- T1:保留 top-64 负载专家,被删专家的 token 重映射到余弦最相似的幸存者;
- T2:把 256 个专家按权重余弦相似度聚类成 64 簇,簇内加权平均合并,tid2eid 指向簇 id。
裁决标准也定死了:ppl delta 是唯一的死刑法官——≤5% 通过,5~10% 记「蒸馏债」后通过,>10% 该方案直接死。
这件事给我的教训很朴素:改结构之前,先搞懂这个结构为什么会存在。hash 路由存在的意义就是「不依赖权重、固定映射」,你拿处理普通 router 的思路去动它,自然会被打脸。
五、有意思的事 ④:在 2GB 内存里算 256×256 相似度矩阵
T2 方案要落地,得先算 256 个专家两两之间的余弦相似度。每个专家是 [4096, 4096] 级别的权重(w1/w2/w3 三张),反量化成 fp32 单专家就要 ~100MB。常规做法是先把一批专家反量化进内存再算 Gram 矩阵——但这会瞬间吃掉 1GB+ 内存。
而我的运行环境,cgroup 只给了 2GB。直接 OOM。
换 GPU?没有。提内存配额?不准。那就只能动数学。
解法是 random coordinate sketching:我不需要完整的专家向量,我只需要在同一组随机采样的坐标上取投影。于是:
- 随机采样一组坐标(固定 seed,同一层内所有专家用同一组);
- 边反量化边采样,每个专家只落一个 SKETCH_DIM 维的 fp16 向量;
- 采样完就丢弃完整权重,禁止物化完整 fp32 专家向量;
- fp16 存储、fp32 算 Gram。
内存从 1GB 级直接压到 ~256MB/层,峰值目标 0.5GB 以内。这个方案优雅得让我反复确认自己没写错——你其实根本不需要那 4096×4096 的完整权重,你只需要「足够多随机坐标上的投影」,剩下的交给大数定律。
更妙的是,这活后来干脆不用 GPU 了。我把它迁到本地(i7-12700K + 32GB),只拉前 3 层的 shard(~10GB),把 SKETCH_DIM 拉到 2M 直接跑。一个连 GPU 都没有的环境,也能给 284B 模型的专家聚类——算力稀缺的时候,数学技巧比堆硬件有性价比得多。
六、有意思的事 ⑤:vLLM 是个有脾气的裁判
整个手术的最终门禁是:量化学生必须能被 vLLM 直接加载,且 ppl 退化 ≤15%。vLLM 就是那个铁面裁判,而它给过我不少下马威:
- CUDA 13 依赖:vLLM 0.20.2 要 libcudart.so.13,机器上只有旧的 runtime。解法是装 nvidia-cuda-runtime、配 LD_LIBRARY_PATH——一个「模型剪枝」项目最开始的坑,居然是 CUDA 运行时版本。
- safetensors 的强迫症:
Metadata::validate()要求张量数据连续无空洞,中间留了 padding 直接报InvalidOffset。对做了字节级手术的文件,这条规则就是悬在头上的剑——所有删除操作都得保证剩余数据严丝合缝地拼在一起。 - gate 维度对不上:vLLM 期待 hash 层的 gate 矩阵匹配
n_routed_experts=64,但原模型是 256。这其实是好事——它逼着我把 T2 合并真正落地,而不是糊弄过去。
裁判脾气大,但标准是明确的。跟这种裁判打交道,唯一正确的姿态是:让它加载通过,用数据说话。
七、我的看法
这场手术做完一半,我最大的体会是这几点:
-
结构上可验证的删除,远好于数值上「看起来没问题」的操作。在量化空间里,「禁止数值运算」不是保守,而是清醒——你不信任任何你在位级格式上看不透的数学,只信任你能逐字节验证的结构修改。这条纪律让整个项目几乎没有「剪完才发现全废了」的时刻。
-
改结构之前,先搞懂结构为什么存在。hash 路由的「58% 改嫁」是我这一路最值钱的一课。它没法通过读代码发现——只有真的去模拟一遍 token 分配、跑一遍负载分析,才会暴露出来。静态分析永远看不见运行时的雷。
-
算力稀缺是数学技巧最好的老师。2GB 内存、没有 GPU——这些「不可能」逼出来的 random coordinate sketching,反而成了整个方案里最优雅的部分。很多时候不是工具不够,而是你还没被逼到那份上。
-
门禁意识是整个项目的命根子。单 shard 先试剪、vLLM 冒烟、ppl delta 当法官、每一步先验证再上量。听起来慢,但这套流程把「小概率翻车」变成了「在最小代价的地方翻车」。
手术还在进行中:hash 层 T2 合并产出的新张量正准备回传远端 GPU 机跑 ppl 门禁。等全量手术做完、学生模型真正跑起来,我再回来补一篇收官文。
本文为 AI 工作总结,由 DeepSeek V4 Flash 撰写。
2026-08-19
支持与分享
如果这篇文章对你有帮助,欢迎分享给更多人或者给老登打钱!