rearchitecting-llms 通读笔记(边读边记)
出处行号=txt 文件行号(一行=一段)。短语都已确认在原文该行。
原书结构(44 个 txt,正文 9 章)
- ch1 Why rearchitecting LLMs matters(text/04…,27.8k)
- ch2 An end-to-end rearchitecting project(text/05…,46.8k)— 全书先给一个完整实操:270m 模型剪 2 层+蒸馏
- ch3 A blueprint to modern transformers(text/06…,55.9k)
- ch4 depth pruning(7 个文件,共 ~64k)
- ch5 width pruning(6 个文件,~80k)
- ch6 distillation(9 个文件,~85k)
- ch7 Model specialization(1 个文件,57.3k)— 含激活分析/fairness?
- ch8 Attention optimization(8 个文件,~76k)— KV cache、vLLM、注意力冗余、移除注意力模块
- ch9 MoE dynamic routing(7 个文件,~75k)
- 导航:01(copyright)、02 welcome、03 brief contents — 跳过
ch1 为什么要改造(text/04-ch01…)
- 通用 LLM:上千亿参数近万亿(hundreds of billions of parameters):04:32
- prompt engineering 两大局限:长 期不省钱、无差异化:04:38
- SLM 定义:几百万~几十亿参数,轻快,生态积木;rearchitecting=从已有模型重塑结构,物理改架构而非只调行为:04:44
- 三类挑战:运营成本/差异化/可解释性:04:62
- 成本:POC→生产,API 调用从几十到成百上千:04:74;agent 系统输出 token 不可预测:04:80
- 制药团队案例:高端 GPU 7×24 跑「一天用几次」的模型:04:86;解法=换更小专用模型,推理变快后团队实验变多:04:92
- generic trap:人人用 GPT-5/Claude/Gemini→同一推荐;差异化=你独有的数据+围绕它特化的模型:04:104
- RAG 不改变模型本体,给信息不改处理方式;与改造互补(很多改造就是为了让模型在 RAG 管线里好用):04:116
- 闭源微调=「租来的地上盖定制房」:vendor lock-in,基模升级就得重跑:04:143
- 开源模型同样有病:为刷榜设计,超大上下文/多语言在领域模型里没用甚至有害:04:149;根源=学术实验室/大厂研究团队导向:04:155
- FineScope 论文(arXiv 2505.00624):领域数据引导结构化剪枝,Llama 3.1 8B,领域引导 35% 剪枝仍稳,通用数据引导 25% 就开始掉:04:167 ← 全书中心主题「用数据决定删什么」的预告
- 可解释性:监管行业法定要求;API 只有 endpoint,还会悄悄更新:04:179;手术式优化→看神经元激活(层输出的数值):04:185
- NOTE:特化不只调知识,还改结构(减层、重配注意力、调块大小):04:204
- 管线:pruning→KD→LoRA 特化(+可选 teacher correction:NVIDIA 术语,先用目标数据轻微微调基模再剪,让蒸馏期更好对齐):04:230,236,243,315
- 「这个顺序最省算力:每步微调都跑在更小更便宜的模型上」:04:303
- 数据贯穿:领域数据集=骨干,引导校准/剪枝决策/蒸馏目标/最终特化;恢复用通 用数据集(双轨):04:339,346
- 金融例子:7B→分析季报,校准→剪枝→恢复→LoRA:04:352-370
- 也可只优化不特化(纯效率),或用 BoolQ/IFEval 这类基准数据集定向提升某能力:04:376-388
- 工具:NVIDIA GPU/T4/12GB VRAM;PyTorch+HF+lm-evaluation-harness:04:421-445
- OptiPFair=作者自己的开源库,先手写再封装:04:466;BENCHMARK_LIMIT/RECOVERY_SAMPLES/RUN_FULL_BENCHMARKS/EPOCHS:04:472
- 模型:DistilGPT、LLaMA、Gemma、SmolLM、Qwen:04:478
- 每章三段式:fundamentals→practice→deep dive(读论文):04:508-520
- 最后部分:激活分析看模型「怎么想」→fairness pruning:04:531
- 技术来源:NVIDIA、DeepSeek、TU Munich、Imperial College、NEC Labs:04:555
- 作者动机(welcome):第一个项目=创业公司内容审核(DistilGPT2),要更快;技术散落论文中且很多没有可复现代码;GLU 架构的宽度剪枝文档稀缺,得自己结合 ShortGPT+Minitron:02:24-30
ch2 端到端实操(text/05-ch02…)
- NVIDIA 靠「剪枝+蒸馏」造模型家族,只需完整训练家族里最大的那个:05:57
- 性能=能力(capabilities,ARC/HellaSwag 等基准)+推理效率(tokens/s、latency):05:96
- 五步:基线→剪枝→评估损失→恢复→对比:05:110-122
- 选 google/gemma-3-270m(2.7 亿参数,T4 可跑):05:134,263
- 基线:268,098,176 参数,18 层:05:339-340
- 结构细节(listing 2.1):vocab 262144,hidden 640;q_proj 640→1024,k/v 640→256(GQA 迹象),mlp gate/up 640→2048,down 2048→640;每层 4 个 RMSNorm(attn 前/后+ffn 前/后):05:344-373 ←ch3 的素材
- 生成参数:do_sample=False+num_beams=3(束搜索,探索多条序列)+no_repeat_ngram_size=2:05:432
- 基线 prompt「Paris is the capital of」:模型答 France,1000 万人口(有事实错):05:446,468,476
- 四个基准:ARC-Easy(4 选 1,随机 25%)、Winogrande(指代消解,2 选 1,随机 50%)、HellaSwag(常识续写 4 选 1)、LAMBADA(生成最后一个词,非选择题,更严):05:488-497;其他:GSM8K 数学、MMLU 多领域、BoolQ 阅读:05:509
- lm-eval=LLM 的自动化测试台:05:517
- 基线分:arc_easy 0.59 / hellaswag 0.34 / lambada 0.43 / winogrande 0.54(0.5375,贴近随机线 0.50,没多少可掉的空间):05:569,654
- 基线推理 4.611±0.233s:05:645
- 深度剪枝实操:删最后 2 层(假设:末端层专精输出润色,删掉保住通用知识);slice+nn.ModuleList 包装+改 config.num_hidden_layers:05:691,723-759
- .config=模型的「内部说明书」:05:777
- 剪后:16 层,256,950,912 参数,-4.16%(删 11% 的层只减 4.16% 参数:embed_tokens 和 lm_head 不变;层少了还省随上下文增长的内存):05:783,789
- 剪后 Paris 答案:出现幻觉(250 万公顷、被山环绕)、跑题:05:850
- 量化:小 4.16%、快 10%,基准掉 8-12 个百分点(arc -16.55%,lambada -16.58%,hellaswag -9.6%,winogrande 0%但已贴随机线):05:882,表 2.2:911-971
- 注:书内小矛盾——表 2.2 剪后推理 4.611s,正文 L1002 写 4.181s
- KD:teacher=原 18 层,student=剪后 16 层;student 学 teacher 的完整预测分布(不只最终答案,还有对其他可能性的置信度):05:1047-1061
- 数据:SlimPajama(DKYoon/SlimPajama-6B)流式取 15000 条,通用数据:05:1039,1124,1138
- 数据管线三步:tokenize(max_length 128,labels=input_ids 克隆)→Dataset 类→DataLoader(batch 8,shuffle):05:1153-1247
- KL 散度=「两个概率分布之间的意外度/差异」;student 分布接近 teacher→低:05:1271-1277
- logits-only KD;训练四步:取课→teacher 出 logits(no_grad:省显存、快)→student 出 logits(要梯度)→F.kl_div(log_softmax(student), softmax(teacher), batchmean)算差、AdamW(lr 1e-5)调权:05:1290,1302-1311,1366,1404-1434
- 结果(table 2.3):student:arc 0.54(恢复到基线的 91.5%,KD 前只有 83.1%)、winogrande 0.55(102%)、hellaswag 0.33(97.6%)、lambada 0.38(87.2%):05:1487-1591
- 结论:小 4.16%、快 10%,恢复 87-98% 能力:05:1600
- Paris 复测:幻觉(山)消失,答案与基模相当:05:1613-1619
- summary:深度剪枝通常瞄末端层——装的是「润色细节而非核心知识」:05:1727
ch3 现代 Transformer 蓝图(text/06-ch03…)— 全书枢纽
- 加载模型时内存≠模型大小:注意力建 cache 随 token 涨;MLP 占大部分参数和计算:06:41
- DistilGPT2:82M 参数:06:65;结构:wte(50257,768)/wpe(1024,768)/6×GPT2Block/c_attn Conv1D(2304,768)/mlp c_fc 3072/c_proj/lm_head 768→50257:06:121-149
- PyTorch 模型=Python 类,init 声明层,forward 定执行顺序;嵌套调用链 GPT2Model→GPT2Block→attn→mlp:06:165,234-257
- wte=ID→向量的大字典:06:276;50257 词表、768 维:06:282;wpe 位置 0-1023=上下文窗口,位置向量与词向量直接相加:06:288;「The worker called the lawyer」≠倒装:06:294
- dropout=训练期随机关神经元防过拟合,推理时失效:06:300
- 块内顺序:LayerNorm→Attention→LayerNorm→MLP:06:319,325
- 分工铁律:「注意力做语境化(contextualization),MLP 做知识处理」:06:331,337,343,650
- 输出:ln_f 稳定向量→lm_head 投到 50257 维 logits;「snow 30%/trees 15%/fog 10%」,5 万词表上 30% 已是强预测:06:349-355
- 英文 1 词≈1.3-1.5 token:06:368
- 注意力背景:「Attention is all you need」2017(arXiv 1706.03762):06:394
- 输入嵌入的局限:「spring」在两句话里的 token 嵌入完全相同,注意力负责用上下文区分:06:406,412
- 注意力头=完整独立的注意力机制,各司其职;特化在训练中涌现(句法/语义/长距):06:418,424;DistilGPT2 12 头×64 维:06:452-459
- QKV:Query=当前词的提问、Key=词的标签、Value=词的真实内容;点积算对齐度,softmax 归一成权重,按权重取 Value 混合:06:502-525
- MHA:12 头各自生成全套 QKV,c_attn 2304=768×3 一次投出:06:538
- KV Cache:生成时逐 token 预测,新 token 要看所有旧 token→K/V 存 cache 免重算;随 token 线性涨,是内存瓶颈:06:550
- MQA=全部 Q 头共享一套 KV;GQA=KV 分组共享;GQA 成事实标准:省大部分内存又不牺牲质量:06:562-570
- 算例:8 token×12 头×2(KV)×6 层=1152 个「槽」:06:583;同预算下 GQA 4:1 能存 32 token(+300%),MQA 96 token(+1100%):06:596
- 关 KV cache=用计算换内存,推理极慢;只有训练期合法:06:608-614
- MLP 实际执行序:扩张(c_fc 768→3072)→激活(GELU)→收缩(c_proj→768)→dropout;init 里顺序≠执行顺序:06:674,702-713
- 没有激活函数,扩张+收缩数学上等价一次矩阵乘(线性∘线性=线性);GELU=「带调节器的开关」,打破线性:06:725,731
- 神经元在扩张期特化(地理/时间/句法模式);width pruning 的立 足点:「为金融专家模型留着菜谱神经元干什么」:06:737,743
- depth=堆叠块数(更深=更多序贯精化、更高延迟);width=MLP 中间维(3072):06:767-770
- wide 模型:Llama-3.2-1B、DistilGPT2(x4 扩张);deep-narrow:Qwen3-0.6B、Gemma-3-270M(x3):06:794-797
- 经典架构两大扩展瓶颈:注意力内存、MLP 处理深度不足→GQA+GLU 两大创新:06:814-828
- Llama-3.2-1B:词表 128256、2048 维、16 块;q_proj 2048→2048,k/v 2048→512(GQA 铁证),o_proj 合并各头:06:929,883-909,997-1015
- RoPE 替代 wpe:编码相对距离→上下文 128K vs 1024:06:935;RMSNorm 替代 LayerNorm:更便宜效果相近:06:941
- MHA 时代剪头=删独立权重、干净;GQA 下头共享 KV,删单头动共享张量、危险→改用 ch8 的 Attention Bypass:06:1047-1053
- GLU:固定激活对一切输入用同一标准;GLU=每个神经元一个学出来的乘法门(up_proj 内容路×gate_proj 门路,逐元素乘,down_proj 收缩):06:1086,1098,1186-1195
- SwiGLU(SiLU)/GeGLU/ReGLU 变体;本书统称 GLU(宽度剪枝对各变体同样适用):06:1153
- spring 例:up 激活自然/季节/机械多种含义神经元;gate 给地理高值、金融近零;相乘=按上下文过滤:06:1200-1212
- IMPORTANT:gate_proj 与 up_proj 成对,不能只删一边,重要性必须成对评估:06:1231 ←ch5 的核心约束
- 实验预告:Llama-3.2-1B/3B 宽度剪枝有 sweet spot,某些任务甚至超过原模型;Gemma 结果完全不同:06:1239
- 激活=神经元处理输入后输出的值;权重静态、激活动态;Llama-3.2-1B MLP 每层 8192 神经元×每个 2048 权重:06:1269
- (虚构例)「doctor」→1247/2891 号神经元强激活;he/she 激活差在 8-12 层高 300%:06:1305-1311 ←书里明说是虚构示意
- Table 3.1 优化地图:块→深度剪枝(4 章);经典 MLP/G LU→宽度剪枝(5 章);注意力→Attention Bypass(8 章)/Adaptive(9 章);激活→Fair Pruning(10/11 章,本文本未含):06:1334-1414
ch4 深度剪枝(text/07-13)
- 动机:作者曾用「保头尾」启发式,但任务模型不知道删哪层对;正确答案可能取决于数据:07:35
- 术语 NOTE:block=完整重复单元(Attention+MLP);module=功能组件(self_attn/mlp);layer=单个操作(Linear/LayerNorm);ShortGPT 叫 Block Influence;HF 存于 model.model.layers:08:26
- 删块=丢掉一整个训练中始终存在的信息变换过程;激进:08:53
- 初始越深的模型越扛剪——块间功能冗余:08:65
- 主收益不是省计算而是省「数据搬运」:GPU 推理时只用到 50-70% 数学能力,其余时间在等数据:08:77,83
- memory-bound:删一块=少一轮 VRAM 读-算-写:08:89
- Qwen3-06B:所有 transformer 块占模型 58%,每块 2.1%:08:95
- KV-Cache 随序列线性涨,可能超过模型参数本身;删 25% 的层=每生成一个 token 少涨 25% 内存:08:101
- 收益只看删了几块,损失取决于删了哪块(fig 4.2:同样删 4 块,推理 +15% 相同,能力损失差很远):08:113,120
- 静态法(4.2):按位置/按权重;零计算成本,意外地有效:09:7
- 删中间 4 块(28 层删 12-15):中层冗余、头层学基础表示、尾层做任务精化;保留 85.7% 深度:09:94
- 保护启发式源自 Shortened LLaMA(Kim et al. 2024):保前 4 后 2:09:100
- 按权重范数选块:torch.norm 加总,升序删 4;Qwen3-0.6B 选出 [8,2,7,6]:09:118-158
- 静态法弱点:不认识上下文;权重小可能只 因训练数据没激活;「尾层可弃」对多阶段微调的模型未必真;相似 magnitude 的两块可能角色完全不同:09:176,195
- 数据驱动(4.3):「块的重要性不是模型的绝对属性,而是相对于数据与任务的属性」:10:7
- 三个要素:hooks(间谍)、余弦相似度(度量)、真实任务数据:10:97-109
- PyTorch hook:attach 函数到模块,每次数据经过自动执行;register_forward_hook(module,input,output);.detach() 免梯度省显存;工厂函数闭包 layer_idx:10:132,144-150,573-630
- 8 token 输入 shape [1,8,1024] (batch×seq×hidden):10:236,249
- 余弦相似度:比欧氏距离好在只看方向(方向承载语义,幅度可以变而义不变);同向 1、垂直 0、反向 -1:10:279-297
- 演示:all-MiniLM-L6-v2,384 维;「cat naps on sofa」vs「feline slumbering on couch」=0.6106,vs「bus stops at corner」=0.0563:10:332-408
- 点积=对应元素乘后求和;归一化后点积即余弦;[0.6,0.8]·[1,0]=0.6:10:420
- 管线四步:挂 hooks→跑 WinoGrande 数据→算每块余弦→聚合成重要性分:10:456-465
- WinoGrande 格式化:句子+两选项+答案拼成一段文本:10:499-545
- 重要性=1−平均余弦相似度(逐 token 算,dim=-1,attention_mask 屏蔽 padding——padding 的输出几乎等于输入,会人为拉高相似度):10:654,688,714
- 例:相似度 0.99→重要性 0.01;0.60→0.40:10:708
- Qwen3-0.6B 结果:块 0 重要性 0.947 一骑绝尘;1-4 在 0.12-0.13;27=0.111;最低:25/26≈0.025、24=0.030、23=0.031、13=0.045:10:852-861,945-1007
- WinoGrande vs WikiText 校准:top6 完全一致;底部不同——WinoGrande 删 [25,26,24,23] (聚在尾部),WikiText 删 [25,24,13,23] (保尾、伸进中层):10:1015,1040-1062
- 两道保护:保头 4 尾 2(heuristic)+不删相邻块(adjacent);无保护选 [25,26,24,23],双保护选 [25,23,13,15]:10:897,1090-1144
- 基准对比(table 4.3,删 4/28 块):基线 wino 0.566/lambada 0.404;删尾 4 块:lambada 崩到 9%;data guided protected [25,23,13,15] 最佳:wino 97.2% 保留、lambada 73.1% 保留:10:1195-1342
- 反直觉:中间连续删 4 块 lambada 保留 63.7%,散开删(11,13,15,17)反而只有 34.6%——散开比连续更伤:10:1419
- WinoGrande 是 2 选 1,底线≈50%,压缩了可观察的退化区间;Lambada 长程预测,崩了就近 0——压力测试:10:1389,1407
- 「收益由块数决定,不由哪块决定」再证:两个 data guided 模型推理性能几乎相同:+12.7~16.8%(T4:2.834s→2.43s,TTFT 41ms→54ms?不对——TTFT 64.49→54.40ms,吞吐 17.65→20.61 tok/s):10:1483-1523,1532
- 论文对照(4.4):Shortened LLaMA:剪 35% LLaMA-7B,延迟 -33%(2.4s→1.6s)、吞吐 +49%(53.7→80.1 tok/s),宽度剪枝反而更慢:11:19
- Shortened LLaMA 两指标:Perplexity(逐块试删跑推理,28 块=28 次推理)与 Taylor 梯度近似(一次前反向):11:25-31
- ShortGPT 的 BI(Block Influence)=1−输入输出余弦相似度——与本章实现同源:11:37
- ShortGPT 数字:剪 27.1% Llama2-7B,不做任何恢复保留 86.3% 平均性能(14 基准);LLMPruner 72.8%、SliceGPT 68.7%:11:43-49
- BI 相关性:Llama2-7B 中层 BI≈0.05 删了几乎不涨困惑度;头层 BI>0.4 删了暴涨;验证到 70B:11:55
- 两论文的恢复:Shortened LLaMA 比较 LoRA 与 CPT(继续预训练):中度剪枝 LoRA 够,>40% 剪枝 CPT 更好;ShortGPT 用轻量 MLP 替换被删块再训:11:67
- 本书选 KD 做恢复(跟随 NVIDIA Minitron、Mistral Ministral 3),LoRA 留给领域特化(ch7):11:73-79
- 「很多时候我们不是在找新技术,是在给自己的想法找验证」;0.6B 上做实验,靠 70B 论文确认可扩展:11:85-91
- summary:深度剪枝的靶子是内存-计算之间的数据搬运;保护头 4 尾 2 是通用规则非定律;校准数据在边缘处塑造选择,模型越大效应越强:13:7-19
ch5 宽度剪枝(text/14-19)
- 深度剪枝=激进整块删;宽度剪枝=精准打 MLP 神经元,「不止提性能,还能改模型的性格」;理想对象=wide 模型(Llama-3.2-1B,x4 扩张):14:38
- 静态选择=权重幅度范围(peak-to-peak);数据驱动=hooks 盯 GLU 内部激活:14:56-59
- SiLU=Swish,「带调节器的开关」(ch3 GELU 同款比喻):15:27
- 改 intermediate_size 不破坏模块间连接(下一块注意力仍收 2048),「MLP 内部手术」:15:35
- gate/up 成对删(否则破坏门控同步);指标必须同时看两个权重:15:47-53
- 峰峰值幅度:神经元权重最大值+|最小值|;幅度大=生成显著激活的潜力大;gate+up 幅度相加=对的变换潜力:15:63-69
- compute_neuron_pair_importance:权重矩阵 [8192,2048] (行=神经元,列=输入权重);Neuron X [-0.2,0.1]→0.3、Y [-0.5,0.4]→0.9、Z [0.0,0.4]→0.4,X 最先删:15:109-167
- prune_neuron_pairs:torch.topk 保 k 个最高分;.sort() 恢复原顺序——保持权重排布尽量接近原模型,对后续 KD 重要:15:248-254
- float32 转换防舍入(模型常以 fp16/bf16 加载):15:242
- gate/up 新层 [2048→k],down_proj [k→2048];down 选列(gate/up 选行):15:260
- 必须更新 config.intermediate_size,否则下游代码读到旧尺寸:15:316
- 40% 剪枝:k=8192−3277=4915,扩张 4x→2.4x,总参数 -26.06%(1,235,814,400→913,770,496,减 3.22 亿):15:248,322,378-380
- 对照:每块约 6000 万参数 →相当于删掉 5 个块的量,但 16 块全保留(深度不损):15:386
- 架构无关:SwiGLU/GeGLU/ReGLU 通吃(同三层的结构);已在 LLaMA/Gemma/Qwen 上验证:15:399
- 剪后无恢复直测:Paris 回答结构完整但质量降(26% 减重、零恢复的前提下):15:443-451
- 基准体系(lm-eval;细节在附录):MMLU(57 任务多选)、GSM8K(多步算术=思维链指标)、ARC-C(难科学题)、HellaSwag(常识)、WikiText/Lambada 困惑度(低=好,Lambada 长程最难)、BoolQ(阅读)、PIQA(物理常识)、IFEval(指令遵循)、MUSR(长文约束推理)、TruthfulQA(抗误区)、WinoGrande:15:484-536
- 40% 静态剪枝结果「二象性」:崩的:MMLU -13.6%、GSM8K -68.9%、ARC-C -30.8%、HellaSwag -41.3%;WikiText PPL +387%(11.57→56.33)、Lambada +1472%(5.75→90.38):15:598,16:?15:51(5.3 文件)
- 升的:IFEval +46.5%、MUSR +26.1%、TruthfulQA-MC2 +13.9%、MC1 +6.3%:15:634,17:63-72
- 规律:崩的全是「知识与推理」,升的全是「忠实与精确」——「失去精致,获得纪律」;推理路径比事实存储更受伤(WinoGrande -4.8%、BoolQ -1.9% 几乎无损):15:616-627,657
- 用途:结构化抽取(ETL 发票→JSON,不许「好心改错字」)、agent 工具调用(参数必须严格 schema)、PII 脱敏(漏一个名字就是事故)、确定性分类(只许输出 SPAM/NOT_SPAM):15:663-672
- 「这不是一个更差的模型,是一个能力画像不同的模型」:15:677
- 3B 上 IFEval +74.4%(0.0943→0.1645);IFEval 随剪枝率非单调:10%→0.1423,30%→0.1811,50%→0.1534;3B 在 1.6x 扩张(40% 剪)达基线 174%:17:77,90,84
- 论文来源:Martra 2025「Fragile Knowledge, Robust Instruction-Following: The Width Pruning Dichotomy in Llama-3.2」(作者自己的论文!)+ CFSP(Wang et al. 2024,激活引导):17:7
- 数据驱动(5.2):hybrid 分=静态结构分×激活动态分;只用激活会在小数据上误删「结构重要但没被校准数据激活」的神经元:16:46
- 动态分量:hooks 挂在 down_proj 输入(门控+SiLU 之后、压缩之前——在输出端测就失去逐神经元可追溯性):16:77,206-212
- 结构分量:三层(gate/up/down)幅度归一化到 [0,1] 后相加(down_proj 神经元在列 dim=0);不归一化会被量纲大的层支配:16:140-146
- 融合=乘法:权重大的死神经元×激活≈0→低分→删;fig 5.6 例:神经元 0 权重 0.9+激活 32→活;神经元 1/2 权重同或更高但激活 0.7/0.2→删:16:83,165-177
- 显存技巧:在 hook 里就地算 L2 范数,累积到 CPU(dict+torch.zeros on cpu);torch.norm(X_d, p=2, dim=(0,1)) 把 [B,S,I] 压成 [I]:16:218,234-316
- 为什么 L2:值 [+5,-5,+5,-5]:简单求和=0(误判死亡);L1=20(不辨大小);L2=10(惩罚大值、奖励尖峰);[10,0,0,0] 与 [5,5,0,0] 的 L1 都是 10 但 L2 是 10 vs 7.07——L2 保「专家」神经元,压「恒定低噪」神经元:16:329-352
- 校准循环:model.eval()+no_grad+循环前向,每 10 步 empty_cache(T4 保守策略):16:531-573
- 双模型:PRUNE_PERCENT=20,8192→6554(删 1638);参数 1.2358B→1.0748B(-13.03%):16:698-711,724-726
- 「Paris is the capital of」:wiki_model 答得规矩(210 万人口、东北部);sms_model 出现事实错+更直白——「我们强迫模型在物理上特化到校准数据的领域」:16:739-764
- 困惑度验证(table 5.1):Base:Wiki 25.69/SMS 122.91;wiki_model:36.28/182.54;sms_model:48.65/165.54;static 20%:52/217(全場最差)——每个模型都在自己的校准域最好:16:834-910
- 特化成立但剪后都比原模型差(容量真的变小);数据驱动剪枝的价值=「在我们关心的域里少受伤」,让后续恢复更快更省:16:928-934
- 推理性能只跟剪枝率有关、与删哪些神经元无关:18.31→21.9 tok/s,+19.6%(T4,batch 4):16:946,1004-1041
- 能耗(codecarbon):1.322→1.236 J/token(-6.5%);注意共享 Colab 数据只当对比基线:16:1047-1122
- 剪枝模型生成更多 token(行为变了),单 token 能耗不变(架构决定);可用微调纠正行为但单价不变:16:1128
- 收益上限:真正瓶颈常在注意力模块的内存搬运;宽度剪枝只在 compute-bound 场景对速度有明显收益:16:1140-1152
- GPU 对齐:intermediate_size 应可被 32/64/128/256 整除(Tensor Core 按 tile 处理,16×16 FP16);6554 会被补零;T4(内存带宽受限)上看不出差别,A100/H100 高负载才显著;OptiPFair 有 expansion_divisor=64:16:1165-1216
- 「学术论文不是要逐字遵守的圣经,是基本思想的来源,拿来适配你的场景」:17:120
ch6 蒸馏恢复知识(text/20-28)
- 「恢复不是从训练循环开始的,是从决定保留哪些块开始的」——同样删 4 块,按重要性删的模型恢复耗时显著更短(作者第一个项目学到的):20:44
- ch2 的 KD 只用 logits(soft labels);本章加:hard labels(数据集)+hidden states(中间块对齐,Feature Alignment)——引出「块数不一致怎么对齐」问题:20:62
- KD 训练贵,选错块再蒸馏=浪费;选择决定恢复时间与恢复上限:21:7,19
- 实验设定:gemma-3-270m 18 块删 4(≈22% 深度);A100;2K 样本 Cosmopedia;四策略:EXP01 数据驱动散选[9,8,14,16]、EXP02 数据驱动连续[8,9,10,11]、EXP03 删尾 4[14-17]、EXP04 删 12-15(保尾 2):21:31,84-93
- Cosmopedia=HF 高质量合成数据集(stories/wikihow/openstax/web_samples_v1 四子集混装);无噪声,适合蒸馏;也可用基模自造合成数据:21:135-153
- teacher 冻结(requires_grad=False)+eval+bf16;student 要记得解冻(继承 frozen 状态→静默训练失败,「容易犯的错」):21:231-245,255,677
- 90/10 train/val(大实验 80/20);用训练数据验证=测记忆不是测抽象:21:362,314
- 剪后即刻(table 6.1):teacher PPL 13.38;EXP01 PPL 126(9 倍)、能力保留 78.6%;EXP02 490(36 倍)、65.8%;EXP03 264、66.6%;EXP04 464、67%——直接挑战保护假设:保尾不保证好,若删掉的中层关键:21:437-506
- 2K 样本 KD 5 epoch 后(table 6.2):EXP01 PPL 18.9/保留 86.7%(+10.31pp 增益最小但终态最好);EXP02 +31.31pp;EXP03 82.4%;EXP04 86%——「起点越差恢复增益越大,但终态仍追不上起点好的」:21:518-620
- 复现代码:opf.analyze_layer_importance→取最低 4 块→opf.prune_model_depth→解冻:21:636-667
- 三种层映射(6.2):uniform(按比例,i*18/14;块 7→块 9)、last(整体偏移 +4,student 0→teacher 4)、original(保原索引,被删处留洞):22:26-125
- 复合损失(6.3):total=α·task+β·logits+γ·hidden;默认 0.4/0.4/0.2;和为 1(>1 放大梯度不稳,<1 学习变慢):23:67-112
- task loss=cross_entropy 硬标签;token shifting:logits 去尾、labels 去头(logits[0] 对 labels[1];「Work hard play hard」预测 [hard,play,hard,
]);ignore_index=-100 跳过 padding:23:163-200 - temperature 软化 teacher 分布:「Work hard play」T=1.0:hard 94.5%/well 3.7%/good 0.3%/harder 0.2%;T=2.0:23.9%/4.7%/1.3%/1.2%——让 student 看到 teacher 也考虑过替代项:23:131
- logit loss=F.kl_div(log_softmax(student/T), softmax(teacher/T))*T²;T² 防梯度因除 T 变太小;student 用 log_softmax(kl_div 要求第一参数在 log 空间):23:219-251
- softmax vs log_softmax NOTE:[2.0,1.0,0.1]→[0.659,0.242,0.099] vs [-0.417,-1.417,-2.317];log 空间防下溢(0.0001→-9.21):23:264
- hidden loss:逐对层余弦距离(1−cos)平均;「不是结果相同,而是过程相同」——对 embedding/RAG retriever,中间表示本身就是产品:22:7,23:290
- [1,2,3] vs [10,20,30] 余弦=1——只看方向;[2,3,4] reshape→[6,4] 逐 token 向量;Gemma 270M batch8×512×640→[4096,640]:23:290,364-491
- γ 权重按目标调:embedding/分类模型 γ→0.3-0.4(中间表示是产品);纯生成 γ→0.1 甚至 0(小模型可找到自己的路):23:556-559,25:25
- 训练循环:student/teacher 同步前向;teacher no_grad 显存省一半;hidden_states[1:] 丢弃位置 0(那是嵌入层输出,不是任何块的):23:644-746
- 梯度累积:loss/accumulation_steps,4 步一更新;batch16×4=等效 64 行,显存只占 16:23:752
- 收敛实验(fig 6.6,table 6.3):2K→15K 困惑度陡降(18-19→12-13),15K→40K 渐进;数据量是恢复的第一决定因素:23:776,795-955
- 关键数字:teacher PPL 12.91(40K 时点);labels-only:2K=18.86/15K=12.65/40K=11.73——15K 起超过 teacher(12.96);能力保留 90.1%;LAMBADA 0.295→0.335(teacher 0.43,剪后 0.177):23:841-925
- advanced KD(Feature+FDD+SKD,Selected 映射):40K PPL 11.54 vs 11.73;能力 90.0 vs 90.1;多花约 15% 训练时间;数据越多优势越大:23:961,24:43
- LAMBADA 永远恢复不满:0.177→0.32-0.335,teacher 0.43——「长上下文语言建模是删层伤最重、最难完全恢复的能力」:24:485
- Skew KLD(6.4.1):标准 KL 在 student 分布离 teacher 很远时梯度过陡(teacher 给 France 70%,残破 student 只给 30%→强行拉齐);skew_alpha=0.1→目标=90% teacher+10% student 的混合;student 变好→混合自动变严:24:87-93
- 混合实现:mixed_probs = skew_alpha*student_probs + (1-skew_alpha)*teacher_probs;KL 对混合分布算;1e-9 防 log(0):24:136-166
- 适用:激进剪枝(270m 删>20% 块)+小数据(<10K);数据大(30K+)且剪枝温和→标准 KL 足够;>15K 开始有一致优势:24:172-178
- FDD(6.4.2):对齐「变化率」而非绝对状态:delta = h[i+1]−h[i],对 delta 做余弦;「你的第 5 层像我的第 9 层吗?」→「你从第 5 到第 6 层的变化方向,像不像我从第 9 到第 10 层的变化方向」:24:190-286
- 实用配置表(table 6.5):通用>30K:0.4/0.4/0-0.2/skew 0.1/delta 0.1;15K-30K:skew 0.1、delta 0;<10K:全关(基础蒸馏同样有效且便宜);激进剪枝>20%:0.25/0.45/0.2/skew 0.1-0.15/delta 0.1(以模仿 teacher 为主);embedding/RAG:0.25/0.25/0.4(隐状态是产品);高质量任务数据:0.5/0.3/0.1(更信数据):25:92-230
- temperature 选择:2.0 通用;teacher 分布极尖(>90% 单 token)时用 2.5-3.5;teacher 本来就平滑用 1.0-2.0:25:37
- 与宽度剪枝兼容:宽度剪枝不动 hidden_dim,块级输入输出维度不变→对齐照常;可先宽后深再蒸馏,管线可叠:25:67-80
- 论文对照(6.6):Minitron(NVIDIA 2024,arXiv 2408.11796)+ Ministral 3(Mistral 2026,arXiv 2601.08584)——「剪枝+蒸馏是造高性能 SLM 的行业现行标准」:26:13
- NVIDIA:蒸馏式恢复一直优于普通微调;Mistral:纯 logits 蒸馏目标优于任何蒸馏+next-token 混合(暗示 task loss 可选);本书保留 hard labels 是为更早收敛到任务结果:26:37-43
- NVIDIA 映射经验:压缩率<50% 用 uniform;>50% 才轮到 last;NVIDIA 的「激进」=删 50%+,本书 20% 就算激进——模型越大越扛删:26:61-74
- IMPORTANT:高级蒸馏技巧不能替代好数据与好剪枝决策:「数据嘈杂或选块糟糕,任何损失函数都救不回来」:26:107
- 挑战基线模型:oopere/gemma-3-270m-14L-distilled(α=0.6/β=0.4、skew_alpha=0.4、lr 4e-5、40K×5 epoch、纯标签):89.5% 保留、PPL 10.85;A100 上 68 分钟:27:13,26
- summary:恢复始于设计阶段;复合损失三信号按目标配平;层映射解决深浅 不一;Skew KLD/FDD 服务数据稀缺或激进剪枝;这是 NVIDIA/Mistral 的生产标准:28:7-19
ch7 模型特化(text/29,单文件)
- 前几章改结构换速度;本章改行为适配数据与格式;两条路:RAG(推理时给信息,模型不变)vs 微调(直接改权重、重塑行为):29:38
- 通用模型爱加「Here's the JSON...」之类废话破坏生产管线;微调把指令从 prompt 移进权重——鲁棒、可预测、省 prompt:29:44
- 全量微调两大问题:显存(权重+梯度+优化器状态=模型大小的 4-8 倍)与灾难性遗忘:29:68-71
- PEFT:冻结基模,只训「增量(delta)」;LoRA/DoRA 同源于低秩矩阵分解;训练 <1% 甚至 <0.1% 参数:29:76-82
- LoRA(7.1.1):SmolLM2-1.7B 的 q_proj 是 2048×2048=419 万参数,×24 块×4 个注意力矩阵;ΔW=AB,A[d×r]、B[r×d],r=8→只训 32,768 个(-99.2%);r 是超参,4/8/16 常用,r=8 是合理默认:29:94,117-148
- B 初始化为零(ΔW=AB=0 起步,原论文做法);推理时 W+ΔW 直接相加,无额外成本:29:172,221,233
- DoRA(7.1.2):LoRA 的 ΔW 同时改幅度与方向,二者相关性 +0.83;全量微调幅度与方向近乎独立变化,相关性 −0.62;DoRA 把 W 分解为幅度向量 m+方向矩阵 V,LoRA 部分管方向、m 独立可训,相关性降到 −0.31(接近全量微调):29:251-257,1536
- DoRA 四步:分解(torch.norm 提每列幅度,V=W/m)→更新(V+A@B)→重归一化→重组(m×归一化后方向);推理时合并如普通 LoRA:29:364-379
- DoRA 三优势:能力损失更小(可用更小 r)、语义学习容量更大、小数据集更有效:29:282-288
- 量化(7.2):PEFT 解决梯度/优化器,但冻结基模本身仍占原显存;量化 16/8/4 bit 省显存;「能塞下就别量化,更快」:29:396-415
- 16→8 bit 省一半,→4 bit 省到四分之一——常是「塞不塞得进显卡」的分界:29:436
- NF4(QLoRA 论文 arXiv 2305.14314 提出):4 bit 只有 16 个档位;均匀量化在 [−1,1] 等距摆档,但 LLM 权重正态分布挤在 0 附近(0.08/0.10/0.12 全被舍到 0.13);NF4 把档位密处排在 0 附近——「最小的 4bit 足迹+最小的退化」,已成事实标准:29:442-448
- NF4 是存储格式不是计算格式:没有 GPU 原生 4bit 矩阵乘,运算前现场反量化到 16bit:29:461
- 实测:T4 上 QLoRA 全套(基模 NF4+适配器+梯度+优化器)峰值 2.34GB,QDoRA 2.69GB,8GB 卡轻松:29:469
- 任务:SmolLM2-1.7B-Instruct 从病历自由文本抽取临床信息→严格 schema JSON;目标:15 行 prompt 换成单词「Extract:」+可靠性不降+能力不崩:29:493-499,604-610
- 合成数据集 400 条(360 训/40 测),故意注噪分五类:clean 100/缩写 80/隐晦 80(「觉得房间在转」=vertigo)/错字 70/无关 70:29:569-587
- 基线:15 行 STRICT_PROMPT 下 85% schema 合规;分类看:clean/缩写/隐晦/错字全 100%,irrelevant 只有 58.3%——「prompt 能定义任务,不能把行为泛化到指令没覆盖的输入」:29:796-834
- schema 校验函数:逐字段查类型,「age 返回字符串 "32" 而非整数 32 也算失败」:29:557,702-760
- 能力基线(arc_easy 0.652/hellaswag 0.61 acc_norm/lambada PPL 6.08/piqa 0.794/winogrande 0.654):29:880-885
- 聊天模板三角色:system 注入「Extract:」、user 放病历、assistant 放标准 JSON——教会模型「system 说了 Extract: 就只许输出严格 JSON」:29:934-951
- LoRA 配置:r=8、lora_alpha=16(惯例=2×rank)、dropout 0.05、target 7 个模块(q/k/v/o+gate/up/down——注意力与 MLP 全覆盖):29:1025-1064
- 可训参数:9,043,968 / 1,720,420,352 = 0.5257%;QDoRA 9,682,944(多出的 ~63.9 万=各目标矩阵的幅度向量 m):29:1052,1279-1285
- 训练:SFTTrainer,3 epoch×batch 8×360 条=135 步;lr 2e-4(LoRA 标准)、cosine 调度、warmup 7 步(防开局猛更新)、bf16:29:1082-1123
- 结果:QLoRA 与 QDoRA 均 100% 合规(含 irrelevant 类);「原来住在 prompt 里的知识,现在住进了权重」:29:1142-1159
- 能力代价:加权平均降 3.2%;arc_easy 相对 -10.3%(唯一明显下滑,与临床无关的常识推理);lambada PPL 5.96→6.90(+15.8%)——解释:输出风格收窄,不是推理退化:29:1178-1190
- QDoRA 没赢 QLoRA(两者几乎可互换);「论文提出,数据裁决」——本任务是结构适配(改怎么说),不是语义适配(改说什么),幅度-方向解耦无用武之地;DoRA 论文在 Llama-7B r=8 上优势明显,但那些任务是常识推理/视觉/多模态:29:1335-1348,1542-1548
- 三目标兑现:效率✓(15 行→1 词)、可靠性✓(85%→100%)、能力保持~(平均 -3.2%,可接受);选型:结构/格式任务用 QLoRA(更快),深度语义适配才考虑 QDoRA:29:1359-1377
- 适配器两种用法:merge_and_unload() 永久合并成标准模型(单一特化、最快);PeftModel.from_pretrained() 动态换适配器(一个基模多个特化,如不同医院不同格式);切换有成本→按任务分批路由(fig 7.7):29:1395-1512
- 论文:LoRA(Hu et al. 2021, arXiv 2106.09685)、QLoRA(Dettmers 2023, 2305.14314——48GB 卡微调 65B)、DoRA(Liu et al., 2402.09353)、小医疗数据微调(Losch 2025, 2503.21349——300 例让 LLaMA3-8B 追平 70B 零样本,解析错误 17→1):29:1524-1554
- 实验室彩蛋(7.5):r=1、1 epoch、更大 lr 也能 100% 合规且能力保持更好:29:1578
ch8 注意力优化(text/30-37)
- 本章两个主题:KV cache 量化(不动架构)+注意力模块移除(动架构);注意力=推理期主瓶颈,计算与动态内存都随上下文涨:30:35-47
- KV cache 量化:K/V 张量默认 FP16/BF16 存储;引擎在写入前压到 FP8(1 字节)或 4bit(0.5 字节),与新 Query 相乘时反量化回来——每 token 一次「压缩-写入-读出-还原」循环:31:37-56
- HF 实验(8.1):Llama-3.2-3B 三配置:基线 / 4bit 权重(BitsAndBytes)/ 4bit KV cache(HQQ 后端);测静态 VRAM、动态增量、吞吐;quanto 已并入 Optimum,HQQ 对 Turing 老卡兼容更好:31:80-124
- cache_implementation="quantized" 不传的话 cache_config 会被静默忽略:31:240
- T4 结果:4bit 权重把静态 VRAM 6.1GB→2.2GB(近 3 倍);KV 量化把动态增量降约 3 倍;HQQ 内核在 T4 上有一次性的静态内存预留(L4 上无此现象):31:316-347
- 质量对照(生成文本前 100 字符):基线与 KV 量化连贯;4bit 权重从第一个 token 起复读循环 500 token——3B 小模型参数冗余少,对权重量化极敏感;BitsAndBytes 均匀压缩、不保护离群权重;AWQ/GPTQ 有校准步保护关键权重;KV 量化不动权重故无此问题:31:365-389
- vLLM(8.2):PagedAttention+启动时预分配固定比例 VRAM(85%);省下的权重内存自动变成更多 KV token 容量——「容量以 KV token 数计」:32:7-13
- vLLM 不用 4bit cache:字节是 GPU 最小可寻址单位,4bit 每次读取都要额外计算,罚延迟→用 FP8:32:59
- T4 结果:FP8 cache 容量 51,248→108,192 token(+111.1%)但吞吐 10.8→8.6 tok/s(T4 无原生 FP8,转换开销);NF4 权重容量 +68.4%、吞吐 10.8→31.64(近 3 倍):32:230-242
- L4 结果:FP8 容量 +105.2%(226,768 token)且吞吐不变(35.8 vs 35.6);NF4 容量 +31.8%、吞吐 82.5(2.3 倍);NF4 在两种 GPU 上都复读循环:32:255-273
- 原理:自回归解码是内存带宽受限不是计算受限——每步都要把权重从 VRAM 流过一遍,模型占内存少→每步搬运字节少→吞吐直接涨:32:242
- 结论:「没有 universally better 的技术」——要容量选 KV 量化(新卡免费,老卡罚速),要吞吐选 NF4 权重;注意力模块的内存搬运才是真瓶颈(呼应 ch5):32:267
- 注意力冗余(8.3):He et al. 2024「What Matters in Transformers? Not All Attention is Needed」(arXiv 2406.15786, U Maryland):块内冗余分布不均,注意力层通常比 MLP 层更可弃:33:19
- hooks 挂两点:input_layernorm(捕捉 X_A,注意力前)与 post_attention_layernorm(捕捉 Y_A,注意力输出+残差之后、MLP 之前);register_forward_pre_hook(pre-hook 只收输入参数);所有 decoder-only 家族(Llama/Mistral/Qwen/Gemma)标准化有这两个属性;Gemma3 多两个自己的 norm 不影响:33:49-115
- 残差连接 NOTE:块学的是 F(x)−x 的差而不是直接变换;Y_A=X_A+Attention(LayerNorm(X_A)):33:293
- 必须含残差测量(8.5):论文消融实验证明不含残差的度量会误导——「不含残差,删几层性能就明显掉;含残差,度量才可靠」:35:25
- 校准集:Cosmopedia 六子集按权重混合(stories 0.3/web 0.2+0.15/wikihow 0.15/openstax 0.125/stanford 0.075)=400 条;配比逻辑=覆盖评测基准探查的能力面:33:146-182
- Llama-3.2-3B 分数分布:层 0、1 远高于其他;2-17 中等;18-25 低洼地(候选全在此);26、27 略回升(输出前整合表示);层 21 最低 0.009:33:396-444
- 3B vs 8B:结构一致(头尾活跃、中间冗余);最低分层都在 60-80% 深度处;阈 0.015 下 3B 有 18%(5/28)低于它,8B 有 34%(11/32)——冗余空间随模型变大而变 宽→大模型更扛注意力剪枝:33:440-515
- 移除(8.4):按分升序取前 3(21/22/23);「3B 的冗余空间窄,从可控退化到生成崩塌 transition 很快」:34:7-29
- 手术:delattr(layer,"self_attn")+delattr(layer,"input_layernorm")——「只删 self_attn 不删 input_layernorm 毫无意义,空算一步白烧周期」;然后 type() 造 PrunedDecoderLayer 子类、类级替换 forward(实例级会被 transformers 内部机制覆盖):34:60-135
- forward_no_attn:residual=hidden→post_attention_layernorm→mlp→residual+——残差保留、注意力阶段整个跳过:34:98-129
- 保存/重载难题(8.4.1):save_pretrained 只存权重+config.json 不存自定义 forward;直接 from_pretrained 会按原类重建→缺失权重随机初始化→「模型加载无错但生成垃圾」:34:228
- 解法:config.dropped_attn_layers=[21,22,23]+config.auto_map 指向自定义 PrunedLlamaForCausalLM+trust_remote_code=True;init 先建原始骨架→读配置→delattr 补丁→然后才载权重——「checkpoint 里每个键都找到归宿」:34:254-402
- 效果(8.4.2):3 层注意力≈2500 万参数×3=7500 万=FP16 150MB;动态 VRAM:基线 109.57MB→3 层 96.06(-12.3%)→6 层 78.72(-28.2%)→15 层 58.63(-46.5%):34:414-529
- 基准:3 层删 4/5 基准几乎不动(ARC +0.9%~HellaSwag -1.4%);Lambada 一枝独敏感:3 层 -9.2%、6 层 -19.1%、15 层 -93.3% 全崩:34:439
- 生成质量:6 层起复读循环(「23 million tourists in 2013…16.8 million tourists in 2011…」);15 层短 prompt 即崩(「The. The. is the is the is.」);3 层长文生成出现细微事实不一致——「静态选择题基准测不出的退化」:34:535-603
- 选择题基准只测「一次静态前向里挑选项」,不需要逐 token 生成;Lambada/自由生成暴露自回归退化:34:445-451,603
- 恢复:OptiPFair distill_model(α=0.6/β=0.4),A100 上 10 分钟、Cosmopedia 10K 条→复读循环消失,「不是不可逆的能力损失,是架构与权重之间的暂时错位,恢复能校正」:34:615-655
- 论文数字(8.5.1):四种策略 Block Drop/MLP Drop/Attention Drop/Joint Layer Drop;SDR=性能下降%÷加速%;Attention Drop 的 SDR 恒比其他低 10-20 倍:Llama-2-13B 删 8 个注意力层平均 68.1 vs 基线 68.2(几乎不动),删 16 层只掉 2.1 点;Llama-2-70B 删 40%(32/80)SDR=0.00(不可测退化)+1.35 加速;删一半 SDR 0.05+1.48 加速:35:43-86
- 本书 3B 实测:删 11%(3/28)无可见加速(25.0 vs 25.4 tok/s);删 54%(15/28)约 +6% 吞吐但生成即崩:35:86
- 反转(8.5.2):Maryland 后续论文「Making LLMs Efficient Dense Retrievers」(arXiv 2512.20612):把 LLM 改成稠密检索器(embedding/RAG)后结论完全反转——注意力层变关键,MLP 高冗余可大量剪;「最优结构取决于生产任务的本质;差异来自度量,度量直接对准任务 demands」:35:134-138
- Attention Drop 与量化可叠加:论文与 AWQ 4bit 结合,差距 <1%——KV 量化与注意力移除是「累积」不是「二选一」:35:128
ch9 MoE 动态路由(text/38-44)
- 背景:Mixtral 8x7B 证明 MoE 出实验室;DeepSeek 走细粒度(许多小专家):38:35
- 三个一观察:稠密模型每次前向成本恒定——「为最难的情况设计的架构,尽管多数输入不需要」:39:7
- 三种动态推理:early exiting(何时停;logit 集中度=信心)、token dropping(处理哪些 token;注意力权重当指标;直接缩 KV cache)、MoE(路由到哪个专家;可训线性路由器;MLP 层):39:25-49,table 9.1:79-136
- MoE 精神:「不是做得更少的模型,是做对的事的模型」:39:55
- 共同短板:与 vLLM/TGI 等标准推理库不兼容(假设统一前向);MoE 最成熟:39:61-67
- MoE 三组件:专家(独立处理)、路由器(线性层决定分配)、组合前向(按路由器比例混合输出):40:7-19
- sparse upcycling:复制已训练的稠密 MLP 来填专家,不从随机初始化开始:「给模型一个领跑优势」:40:49
- Trainable2ExpertMoE:expert_general=原 MLP(冻结)、expert_clinical=deepcopy(可训)、router=Linear(hidden,2,no bias)、last_routing 存路由权重供检查:40:109-150
- 训练期 router logits 加高斯噪声(×0.35):起点两专家权重全同,无噪声路由器从第一步就塌向一边→受宠专家拿到更多梯度更快变好→恶性循环;噪声逼路由器先探索:40:127,184
- softmax 在 FP32 算(fp16 下 softmax 会出退化值把某专家贡献清零):40:130-132,190
- 只改 13-23 层(深层最抽象、最宜特化);Mixtral 全部块都改,也有交替改/单块塞几百专家的:40:233,254-260
- 冻结策略:全冻结→解冻 expert_clinical+router;总参数 2,265,069,568,可训 553,693,184(24.44%):40:354-394
- 数据=临床(oopere/clinical-ner-qdora)+通用(smoltalk 单轮短对话)混装再 shuffle——只训临床数据路由器没有判别信号;smoltalk 在基模训练数据里也没关系(不是教新知识,是教路由);不混洗会让路由器先学会全送一边再「反学习」:40:406-465
- 训练细节:use_cache=False;可训参数升 FP32(AdamW 在低精度下不稳,更新小到被舍入为零);双学习率 router 1e-6 / expert 1e-5(router 从零开始要细,expert 从预训权重开始要猛);GradScaler+autocast fp16;3 epoch:40:481-560
- 结果:Barcelona 常识题答对(专家 0 未受伤);临床 JSON 正确;schema 合规 97.5%(irrelevant 类 91.7% vs 基线 58.3%):40:585-663
- 路由可视化(analyze_routing,last_routing):Barcelona:所有 token ~0.55-0.60 偏向专家 0;临床句:开头平衡,「old/male/with/chronic」起倒向专家 1,「with」最高 0.62:40:735-800
- soft routing=每 token 两专家都跑加权混合:「有特化,但激活了全部权重」;通常的 MoE 是几十个专家只激活少数——「总容量可以很大而推理成本不成比例增长」:40:806-812
- hard routing:同一套权重,只改推理分支:argmax 每 token 二选一;布尔掩码选择 token,零初始化输出张量+掩码写回保证顺序;F.one_hot 存 last_routing:40:824-943
- hard 结果:Barcelona 全部→专家 0(1.0/0.0);临床句混杂:「Patient」→通用、「with/chronic」→临床——「token 去哪取决于路由器学到了什么,不是 token 的内在属性」;改一个词(chronic back pain)「pain」就换边——路由器看的是累积上下文的 hidden states:40:1014-1085
- NOTE(诚实):「Clinical Expert」是叫着方便——没有直接证据它的权重长出了临床专门表示;知道的只是它用临床数据训过、路由器爱把临床 token 送给它:40:1098
- hard 与 soft 合规率同为 97.5%:40:1106
- 同权重不同 forward→不同行为:Barcelona 答案 base 4 句/soft 2 句/hard 1 句,文本全同只差「何时停」;假说:新专家学的是 JSON 这种「闭环生成」,输出齐了就收:40:1112-1176
- 基准:5 项里 4 项升(ARC +0.17pp 到 Lambada +4.37pp),PIQA 微降(soft -1.14/hard -1.52);「特化同时没有伤害通用能力,甚至变好」:40:1189-1195
- 移植专家(9.7-9.10):ch7 模型的 MLP 当 Expert 1;问题:ch7 微调同时动了注意力→「不同步」:移植来的 MLP 习惯接收微调过的注意力给的张量,现在对面是原装注意力:41:7,473
- TrainableTopKMoE:num_experts、top_k、extra_experts 参数化;专家表=[原 MLP]+外部专家+补拷贝;推理时 topk 选专家→softmax 只在选中的里归一化→按专家重组(mask);没分到 token 的专家直接跳过——「稀疏就在这;真正省计算的就是 hidden_states[token_mask] 那一行,其余都是后勤」:41:41-53,151-251
- 这次不用加噪声:移植的专家已经充分分化,路由器没有塌缩风险:41:220
- 两阶段:阶段一只训路由器(0.0034% 参数!lr 1e-3)→90% 合规(irrelevant 66.7%);阶段二解冻临床专家尾部 6 层(18-23,10.35% 参数,router lr 降到 1e-6,expert 1e-5,1 epoch)→100%:41:388-585
- 「如果 ch7 时就知道要做 MoE,可以只训 MLP 模块;但现实从不这样——你能捡到的模型 99.99% 训过的不止 MLP」:41:585
- 论文(9.11-9.13):Sparse Upcycling(Komatsuzaki 2022,arXiv 2212.05055,T5+ViT;升级版胜过稠密版与同算力从零训的 MoE,约 50% 预训练成本[40-60% 因模型而异]);Mixtral of Experts(Jiang 2024,arXiv 2401.04088:32 块×8 专家 top-2;46.7B 总参数,激活仅 12.9B;追平 Llama 2 70B):42:7-61
- 论文的噪声结论与本书相反:他们给路由器+专家参数加噪,小了没用大了有害;本书只给 router logits 加噪声 ×0.35 有效:42:37
- 冻结专家的风险:「冻结的专家不会适应,路由器训过头可能不再给它送信息」:42:43
- Mixtral 的反直觉发现:预期领域特化(数学归一个专家、代码归另一个),实际 token 分布几乎与领域无关;只有句法特化(Python 的 self、缩进 token 稳定去同一个专家)——「路由器学的是结构,不是领域语义」;本书两专家(冻结通用+临床)情形不同:路由器必须学会领域区分才有用:42:67-75
- 实验室:三专家 MoE(加 Python 代码专家,flytech/python-codes-25k):43:7