# SVD-LLM 忠实移植差异表 (svdllm_port_diff.md) 官方 repo: https://github.com/AIoT-MLSys-Lab/SVD-LLM 本地参照: `/home/tl356/SVD-LLM/SVDLLM.py`(逐函数比对 `profle_svdllm_low_resource()` + `whitening()`,本仓库不直接运行官方 repo)。 **为什么不直接跑官方 repo**:官方硬编码层遍历 `model.model.layers`、压缩后模块类 `SVD_LlamaAttention / SVD_LlamaMLP / SVDOPTDecoderLayer`、`transformers==4.35.2`。 LLaDA 层路径是 `model.model.transformer.blocks`(block_type=llama), 上述组件类对 LLaDA 不存在。故忠实移植:**数学(白化、rank 公式、full SVD、 Cholesky 及其 except 分支)逐行照搬,只把层遍历与模块替换改写为 LLaDA 结构。** 此表进论文附录,证明 baseline 不是稻草人。 > **裁决原则(见 README 第 0 节)**:SVD-LLM 仅在【压缩算法本身】上作为权威 > (白化数学、rank 公式、full SVD、Cholesky 及其 except 分支、校准数据的角色 —— > 即本表覆盖的内容)。**评测协议一律以 LLaDA 官方为准**(逐任务 fewshot/cfg/mc_num、 > acc/acc_norm、生成参数),不采用 SVD-LLM 的 AR 评测惯例。目标模型 = LLaDA-8B-Base。 --- ## 逐项差异 | # | 项目 | 官方 SVD-LLM 做法 | 我们的做法(LLaDA port) | 影响数学等价? | 理由 | |---|---|---|---|---|---| | 1 | **层遍历路径** | `layers = model.model.layers`(llama)/ `model.model.decoder.layers`(opt),硬编码;每层用 `find_layers()` 找 `nn.Linear` | `layers = model.model.transformer.blocks`(LLaDA, block_type=llama),32 个 `LLaDALlamaBlock`;`named_modules()` 遍历其中 `nn.Linear` | **否** | LLaDA 权重挂在 `transformer.blocks`。仅路径不同,被压缩的 Linear 集合语义一致(每块 q/k/v/attn_out/ff_proj/up_proj/ff_out)。 | | 2 | **模块替换方式** | 用自定义类 `SVD_LlamaAttention`/`SVD_LlamaMLP` 整体替换 `layer.self_attn`/`layer.mlp`,把 `svd_u/svd_v` 塞进 `q_u_proj/q_v_proj…` | 用通用 `LowRankLinear(B: in→k, A: k→out)` **逐个替换单个 `nn.Linear`**(`forward = A(B(x))`) | **否** | 官方的 `SVD_Llama*` 类对 LLaDA 不存在。逐 Linear 替换的前向 `A@B@x` 与官方 `svd_u @ (svd_v @ x)` **逐元素相同**,只是打包粒度不同。 | | 3 | **XtX 收集方式(主管线)** | `profle_svdllm_low_resource`: 全量缓冲 `inps/outs` (N×seqlen×d),逐层串行前向,hook `scaling_diag_matrix += Σ Xᵀ X` | `compress.py`: **单趟前向 + forward hook 增量累加** `XtX += xᵀx`(O(d²)/层, ~64MB),不缓冲 inps/outs | **否**(结果等价) | 二者都得到同一 `Σ_tokens x xᵀ = XᵀX`。差别纯在内存/调度:官方为低资源逐层重放而缓冲 N 份激活;我们一次前向即可,因为不需要逐层替换后重放。官方结构另在 `official_collect.py` 复现,仅用于内存实测(第 7a 节),**不产出压缩权重**。 | | 4 | **XtX / 分解精度** | hook 累加用 `inp.detach().float()`(fp32);Cholesky 前 `.double()`(fp64);SVD 用 `W.float()`(fp32) | **完全相同**:hook fp32 累加;Cholesky/inv 在 fp64;whitened-SVD 在 fp32 | **是(相同)** | 逐字对齐官方精度阶梯。eigh 备份路径的 fp64-vs-Cholesky 等价性另做匹配精度验证(见下)。 | | 5 | **分解路径(白化平方根)** | `L = torch.linalg.cholesky(XtX.double())`;失败 → `eigvalsh` 取 λ_min,`XtX += (−λ_min+1e-6)I` 后重试 Cholesky。`scaling_matrix_inv = inv(L)`;inv 失败 → `L += 1e-6·I` 重试 | **逐行照搬** Cholesky + except 分支 + inv + except 分支。`eigh` 实现为**备份且默认关闭**,附匹配精度(fp64 vs fp64)等价性验证 | **是(相同)** | Cholesky 因子 L 满足 L Lᵀ = XᵀX,是官方选定的白化平方根。截断在白化空间进行,平方根的选择影响结果,故必须用 Cholesky(非 eigh 对称根)。 | | 6 | **whitening 数学** | `W_scale = W @ L`;`U,S,VT = svd(W_scale, full_matrices=False)`;截断 top-k;`svd_u = U[:,:k] @ √Σ_k`,`svd_v = √Σ_k @ (VT[:k] @ inv(L))` | **逐行照搬**:`W_white = W @ L` → full SVD → `A = U[:,:k]·√S_k`,`B = diag(√S_k) @ Vt[:k] @ L_inv` | **是(相同)** | `A@B = U S Vt @ inv(L) = W_scale @ inv(L) = W`(满秩恒等),截断在白化空间——与官方逐符号一致。 | | 7 | **rank 公式** | `k = int(W.shape[0]·W.shape[1]·ratio / (W.shape[0]+W.shape[1]))`,无 `max(1,·)` 下限 | `k = int(ratio·d_out·d_in/(d_out+d_in))`;加 `max(1,·)` 与 `k(d_out+d_in) ≥ d_out·d_in` 跳过守卫 | **否(工作点内惰性)** | 公式逐字相同。守卫仅防病态维度;在本实验维度(d=4096/12288)与 ratio(0.7/0.8)下 k∈[数百,数千],`max(1,·)` 永不触发、跳过守卫永不触发,数学上惰性。 | | 8 | **压缩层范围** | 每块 `find_layers` 找到的全部 `nn.Linear`(q/k/v/o_proj, gate/up/down_proj),`lm_head` 不压 | `model.model.transformer.blocks` 内全部目标 Linear = 32×7 = **224 层**;`lm_head` 保持 dense(加载器硬防线,检测到 lm_head 被替换即报错) | **否** | 与官方"压 block 内 Linear、留 lm_head"策略一致;层数由 LLaDA 结构决定。 | | 9 | **bias 处理** | Llama/Mistral 分支不搬 bias(无 bias);OPT 分支搬 bias | LLaDA `include_bias=False`,目标 Linear 无 bias,`LowRankLinear` 不建 bias | **否** | 与 Llama 分支一致(无 bias)。 | | 10 | **模型释放** | 官方逐层 `.cpu()` 卸载 | 释放时**不先 `.to("cpu")` 再 del**(避免把 8B 参数搬进 RAM 再删,峰值 RSS +16GB) | **否(仅内存卫生)** | 峰值 RSS 是要报告的效率证据,不能被卸载动作污染。 | | 11 | **校准语料与数量** | **256 samples from WikiText-2** | **256 samples from C4-en**(连续 2048 窗口) | **否(对 BASE 无削弱)** | **样本数已与官方一致(均 256),唯一差异是语料 WikiText-2 → C4-en**。偏离是**刻意且双臂共享**的,故单变量隔离不受影响。更关键:SVD-LLM **原文自己的消融**表明改变校准数量/seed/来源数据集时性能变化 **<3%**,即按他们自己的结论这个偏离无关紧要 —— BASE 不因此被削弱。该消融同时是 claim 1 的主要攻击面,正面回应见 README "Related work / limitation" 一节(mask 维度 ≠ 换文本数据集)。 | --- ## 缺席组件(本轮不实现,列 future ablation) 官方 repo 含以下未纳入本轮主实验的组件,逐一说明,避免"挑软柿子"质疑: | 组件 | 官方位置 | 本轮为何不做 | 归属 | |---|---|---|---| | **(ii) data whitening 变体** | 官方另一条 whitening 路径 | 主实验只对齐 truncation-aware whitening 单一路径,做最忠实的单变量对比 | future ablation | | **closed-form sequential / local update** | `whitening_local_update()` + `local_update()`(逐层 SVD 后用后续激活闭式微调 U/S/V) | 属于压缩后的**再优化**,会引入第二个变量,破坏 BASE/OURS 单变量隔离;且 BASE 与 OURS 都不用它,对比仍公平 | future ablation | | **LoRA / 微调后处理** | 官方 `--run_low_resource` 之外的 finetune 脚本 | 同上,本轮不做任何压缩后训练 | future ablation | **结论**:BASE = 官方 truncation-aware whitening 逐符号照搬,仅层遍历/模块打包适配 LLaDA。 OURS 与 BASE 唯一差异是校准输入加噪(`calib/build_calib.py` 的 t 开关),其余管线共用同一份 `compress/compress.py`。因此主表 BASE 是忠实、非稻草人的 baseline。