news 2026/10/20 11:53:50

parameter-golf 16MB 极限压缩实战:11L MLP3x + Int6 QAT + zstd-22 记录详解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
parameter-golf 16MB 极限压缩实战:11L MLP3x + Int6 QAT + zstd-22 记录详解

【免费下载链接】parameter-golf

Train the smallest LM you can that fits in 16MB. Best model wins!

项目地址:https://gitcode.com/gh_mirrors/pa/parameter-golf
点击查看免费下载

导读

本文解析 parameter-golf(OpenAI Model Craft Challenge)仓库中一个达到val_bpb = 1.1502(3 个种子均值)的经典提交记录:11 层 Transformer + 3x MLP 扩展 + Int6 量化感知训练 + zstd-22 压缩 + 滑动窗口评估。通过对照该记录目录下的 README.md、submission.json 与 train_gpt.py,读者可以掌握一条完整的"训练—量化—压缩—评估"技术链路:如何在 16MB 工件预算与 8×H100 十分钟训练预算下,用 Int6 量化省出的空间换取更深的网络,再用 zstd-22 与滑动窗口评估把每一点精度都榨干。

背景:在 16MB 与 10 分钟约束下做 L(N) 优化

parameter-golf 的挑战规则非常明确:训练一个压缩后工件不超过 16MB、在8×H100 上训练不超过 10 分钟的语言模型,按 FineWeb 验证集上的"与分词器无关的压缩率(bits per byte, BPB)"排名(见仓库根目录 README.md)。正如项目根 README 所述,这可以理解为一种 L(N) 优化:在参数量(N)固定的前提下,用数据、算力、步数、架构的自由度换取最低损失。因此,参赛记录的每一次精度提升,几乎都来自"压缩省空间 → 换取更多参数/更深网络 → 用更聪明的评估方式找回精度"的正循环。

本记录正是这一思路的典型样本:基线是 9 层 512 宽、2x MLP 扩展、int8 量化 + zlib 压缩;本记录通过11 层 + 3x MLP + Int6 QAT将模型做大,同时用zstd-22把压缩率再推高一档,最后用滑动窗口评估白捡约 0.034 BPB,最终跨 3 个种子稳定跑出 1.1502 BPB。

提交总览与关键结果

指标数值
模型参数量26,502,020(约 26.5M)
压缩后工件均值 15,427,455 字节(约 15.4MB,3 个种子均在 16MB 内)
val_bpb(滑动窗口)1.15015359(3 种子均值)
val_bpb(roundtrip,重载后复评)1.18454250
val_loss(滑动窗口,nats)1.94198447
相对基线提升0.1307 nats
t 统计量313.20(df=2,p << 0.001)
训练步数 / 单步耗时~10,070 步 / 59.6 ms
硬件8×H100 SXM(RunPod),600 秒训练

以上数据均可在 submission.json 中逐项核对,该文件是train_gpt.py运行时自动生成的官方记录。

架构:11 层 U-Net 变压器

在 Int6 量化与 zstd 压缩"腾出"的预算内,模型从基线的 9 层加宽到 11 层,具体规格为:

  • 11 个 Transformer 块,模型维度 512;
  • GQA 注意力:8 个查询头、4 个 KV 头,RoPE 位置编码;
  • ReLU² MLP,3x 扩展:隐藏维度 512×3 = 1536(对应 train_gpt.py 中MLP类的torch.relu(self.fc(x))后接self.proj(x.square()));
  • Tied embeddings:输入输出共享词嵌入,1024 BPE 词表(SentencePiece);
  • U-Net 跳跃连接:5 个编码层 + 6 个解码层,解码层按逆序逐层叠加skip_weights缩放后的编码层残差;
  • 26.5M 参数,zstd-22 压缩后约 15.4MB 工件。

从 train_gpt.py 的GPT类(约 L733 起)可以看到 U-Net 的实现方式:num_encoder_layers = num_layers // 2,前 5 层把中间激活压入skips栈,后 6 层按skips.pop()逆序叠加skip_weights[i],与深度上的残差resid_mix一起构成双层跳连结构。这种结构让 11 层网络在深层信息传递上更高效,是小模型在有限参数下常用的"深度换宽度"手段。

Int6 QAT:训练时就把量化噪声"喂"给模型

本记录最核心的改动是Int6 量化感知训练(QAT)。思路是:既然导出时要把权重压到 int6,不如在训练中就用直通估计器(STE)模拟 int6 舍入误差,让模型提前适应低精度。

训练时的模拟量化

在 train_gpt.py 的CastedLinear.forward(约 L567 起)中,开启_qat后每个前向都会:

  1. 按每行绝对值的最大值amax计算缩放scale = amax / 127.0;
  2. 对权重做q_raw = (w_f / scale).round();
  3. 若为 Int6 模式(_qat_int6),再做q = (q_raw / 4).round() * 4并clamp(-128, 124)——int6 等价实现:用 int8 存储但只保留 4 的倍数,即 64 个量化等级;
  4. 用 STE 技巧w = w + (w_q - w_f).detach()让前向使用量化权重、反向传播仍按原始权重计算梯度。

关键点:脚本并非真正实现 6-bit 位宽,而是**"int8 容器 + 4 的倍数",64 级离散值正好等价于 6 比特信息量,同时规避了非对齐位宽的工程复杂度。在 quantize_float_tensor_int6(导出阶段)中可以看到完全一致的舍入逻辑,保证训练模拟与导出量化严格对齐**——这正是 QAT 有效的先决条件。

后训练量化导出

训练结束后,quantize_state_dict_int8(约 L385 起)按如下规则导出:

  • 2D 矩阵:per-row int8(或 int6),每行一个 fp16 缩放因子,按 99.99984 分位数裁剪后再量化,降低离群值影响;
  • 非 2D 张量与小于 65,536 元素的小张量:直接以fp16透传(INT8_KEEP_FLOAT_MAX_NUMEL = 65_536),避免元数据反客为主;
  • tied embedding 单独走 fp16 透传(fp16_embed开关):输入/输出头共享的 1024×512 矩阵精度敏感,用 fp16 保留质量;
  • 层范围由INT6_LAYER_START/END控制,本记录设为 0–10,即11 层全部块权重走 int6。

导出的格式标记为int8_clean_per_row_v1,保存量化值、缩放、原始 dtype 与 per-row 元数据;最终写出final_model.int8.ptz。

zstd-22:比 zlib 再省约 1.5MB

量化后的 state_dict 先用torch.save序列化,再压缩。脚本默认回退方案是zlib.compress(..., level=9);当安装了zstandard且USE_ZSTD=1时,改用zstd_mod.ZstdCompressor(level=args.zstd_level)(train_gpt.py)。

zstd-22 的收益在 README 中被明确量化:比 zlib 节省约 1.5MB。对一个 16MB 上限的挑战而言,这 1.5MB 恰好是"11 层 + 3x MLP"能塞进预算的关键,压缩环节因此从"最后收尾"变成了"架构决策的输入"。运行前提:pip install zstandard。

值得留意的是,README 中报出的 val_bpb(1.1502)是滑动窗口评估口径,而 roundtrip 口径(重载量化工件后在相同脚本内复评)为 1.18454——两个口径相差约 0.034 BPB,这正是滑动窗口评估贡献的"免费精度",详见下文。

优化器与学习率:低 LR + 解耦权重衰减 + 动量预热

本记录对优化器做了三处针对性调整,全部指向"让权重更适合量化":

解耦权重衰减(WD=0.04)

Muon 与 AdamW 均启用 0.04 的解耦权重衰减。其中 Muon 侧的实现并非标准权重衰减,而是在opt.step()之后手动执行p.mul_(1.0 - muon_lr * muon_weight_decay)(train_gpt.py)。较小的权重范数直接缩小了量化的动态范围,让 per-row 缩放更稳、舍入误差更小——README 明确称其"keeps weights small and quantization-friendly"。

三组学习率

参数组学习率优化器
矩阵参数(Transformer 块内 2D 权重)MATRIX_LR=0.025Muon
标量/向量/控制张量SCALAR_LR=0.025AdamW
Tied embedding(tok_emb 权重)TIED_EMBED_LR=0.035AdamW

从 train_gpt.py 的优化器拆分逻辑可见:矩阵参数走 Muon(内含zeropower_via_newtonschulz5的 Newton-Schulz 正交化,约 L116),标量与控制张量走 AdamW,embedding 独立一组 AdamW。三组学习率都比基线(0.04)显著调低,README 将其归为第 10 项改动。

Muon 动量预热(0.92 → 0.99)

动量从 0.92 起步,在 1500 步内线性爬升到 0.99。实现位于训练主循环(约 L1256-L1259):frac = min(step / muon_momentum_warmup_steps, 1.0),muon_momentum = (1-frac)*start + frac*target,每个 step 写入优化器参数组。训练初期动量低 → 更新幅度更保守,配合 3000 步的 warmdown(WARMDOWN_ITERS=3000)共同构成"慢启动、稳收尾"的节奏,为后期量化友好的小权重收敛创造条件。

滑动窗口评估:每 64 token 计一次分

验证时,脚本支持两种口径(train_gpt.py):

  • 普通 roundtrip:按 train_seq_len=1024 切块,每块独立前向,val_bpb = 1.18454;
  • 滑动窗口:stride=64,窗口起点range(0, total_len - seq_len, stride),每窗口只对最后 64 个 token(score_start = seq_len - stride)计分。

滑动窗口让每个 token 都以"前面最多 1024 token 的上下文"被评估,相当于把模型的实际有效上下文喂满,得到更低的 loss——本记录中约 0.034 BPB 的提升即来源于此。评估实现eval_val_sliding在 rank 间交错分配窗口位置(rank_positions = positions[rank::world_size]),全程torch.inference_mode+ bf16 autocast,3 个种子的滑动评估合计约 88 秒,仍在 10 分钟评估预算内(README 注明"Sliding window eval takes ~88s on 8xH100")。

注意:滑动窗口并非"作弊",而是改变评估口径;提交时须同时诚实报告 roundtrip 口径(见 submission.json 的val_bpb_roundtrip字段),挑战对口径有合规要求。

完整复现命令与全部环境变量

运行前需安装zstandard:

pip install zstandard

本记录的标准复现命令(来自 README.md):

RUN_ID=submission \ SEED=1337 \ NUM_LAYERS=11 \ MLP_MULT=3 \ MATRIX_LR=0.025 \ SCALAR_LR=0.025 \ TIED_EMBED_LR=0.035 \ FP16_EMBED_EXPORT=1 \ INT6_LAYER_START=0 \ INT6_LAYER_END=10 \ QAT_ENABLED=1 \ QAT_INT6=1 \ MUON_WEIGHT_DECAY=0.04 \ ADAM_WEIGHT_DECAY=0.04 \ MUON_MOMENTUM=0.99 \ MUON_MOMENTUM_WARMUP_START=0.92 \ MUON_MOMENTUM_WARMUP_STEPS=1500 \ WARMDOWN_ITERS=3000 \ USE_ZSTD=1 \ EVAL_STRIDE=64 \ torchrun --standalone --nproc_per_node=8 train_gpt.py

以上环境变量在 train_gpt.py 的Hyperparameters类(约 L38-L107)中逐一定义,除本记录用到的取值外,还有一批默认值与可选项,整理如下:

环境变量本记录取值默认值作用
NUM_LAYERS119Transformer 层数(U-Net 编码/解码层自动分配)
MLP_MULT32MLP 隐藏层扩展倍数(hidden = MLP_MULT × model_dim)
MODEL_DIM/NUM_HEADS/NUM_KV_HEADS512 / 8 / 4同左模型宽度与 GQA 头数
VOCAB_SIZE10241024BPE 词表大小,须与 tokenizer 匹配
FP16_EMBED_EXPORT10tied embedding 导出为 fp16(保留质量)
INT6_LAYER_START/INT6_LAYER_END0 / 10-1 / -1int6 量化的层范围(-1 表示关闭)
QAT_ENABLED/QAT_INT61 / 10 / 0开启 QAT 模拟量化 / 使用 int6 模式
MATRIX_LR/SCALAR_LR/TIED_EMBED_LR0.025 / 0.025 / 0.0350.04 / 0.04 / 0.05矩阵、标量、embedding 三组学习率
MUON_WEIGHT_DECAY/ADAM_WEIGHT_DECAY0.04 / 0.040.0 / 0.0解耦权重衰减
MUON_MOMENTUM0.990.95Muon 最终动量
MUON_MOMENTUM_WARMUP_START/_STEPS0.92 / 15000.85 / 500动量预热起点与步数
WARMDOWN_ITERS30001200学习率线性退火步数
USE_ZSTD/ZSTD_LEVEL1 / 220 / 22使用 zstd 压缩(否则回退 zlib level 9)
EVAL_STRIDE640滑动窗口步长(0 表示关闭滑动评估)
EVAL_SEQ_LEN/EVAL_BATCH_SIZE0 / 320 / 32滑动评估窗口长度与批大小
SEED1337 / 42 / 1231337随机种子(3 种子取均值)
ITERATIONS20000(受墙钟截断)20000目标训练步数
MAX_WALLCLOCK_SECONDS600.0600.0训练墙钟上限,到时即停

训练脚本自带WARMUP_STEPS=20步编译预热:先完整跑一小段前反向,把torch.compile的编译成本消化掉,再恢复初始权重与优化器状态,确保计时从真实训练起点开始——这是 10 分钟计时公平性的关键实现(train_gpt.py)。

数据路径与分词器同样由环境变量控制(DATA_PATH、TOKENIZER_PATH,默认指向./data/datasets/fineweb10B_sp1024与./data/tokenizers/fineweb_1024_bpe.model),脚本仅支持 SentencePiece.model分词器,并校验词表大小一致后才开始训练。

多种子统计:0.1307 nats 的提升不是噪声

本记录用 3 个种子(1337 / 42 / 123)验证稳定性:

Seedslide_loss (nats)slide_bpbrt_bpbArtifact(字节)
13371.942656071.150551351.1848407515,360,260
421.942077951.150208961.1845668115,556,813
1231.941219401.149700471.1842199315,365,293
Mean1.941984471.150153591.1845425015,427,455
Std0.00072288

(数据来源:README.md 与 submission.json)

三个种子 BPB 的标准差仅约 0.0004,均值相对基线提升 0.1307 nats,t 统计量高达 313.20(df=2,p << 0.001),统计显著性毋庸置疑;三个工件事务大小在 15.36MB~15.56MB 之间,全部落在 16MB 上限之内。这份记录因此成为 2026-03-19 时点参数高尔夫赛道上"深度 × 量化 × 压缩"组合的可靠基准。

小结:这条技术链路的可迁移要点

纵观整个记录,可复用的方法论清晰可辨:

  1. 压缩先行、架构跟进:先确定 zstd-22 能比 zlib 多省约 1.5MB,再决定把层数从 9 加到 11、MLP 从 2x 扩到 3x;
  2. QAT 要与导出严格同构:训练中的 STE 模拟舍入(round(q_raw/4)*4)与导出量化函数完全一致,杜绝"训练与部署脱节";
  3. 量化友好从优化器抓起:0.04 解耦权重衰减 + 全面调低学习率 + 动量预热,从源头控制权重量级;
  4. 评估口径要诚实且精细:滑动窗口(stride=64)贡献约 0.034 BPB,同时如实报告 roundtrip 口径;
  5. 多种子 + 统计检验兜底:3 种子、t 统计量与工件大小逐一留档,保证记录可复核。

若想亲自跑通该记录,仓库根目录的 train_gpt.py 是官方推荐的新手起点脚本,本记录的 train_gpt.py 则提供了上述全部特性的完整实现,可作为深入阅读与二次开发的最佳模板。

【免费下载链接】parameter-golf

Train the smallest LM you can that fits in 16MB. Best model wins!

项目地址:https://gitcode.com/gh_mirrors/pa/parameter-golf
点击查看免费下载
上一篇:SerenityOS进程调度:多任务与线程同步原理
下一篇:Tock 内核设计决策实录:ErrorCode 表示优化与 AppID 应用身份校验机制解析

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/18 5:14:31

React框架核心解析:组件化、虚拟DOM与性能优化实践

1. React框架到底在解决什么问题做前端开发这些年&#xff0c;我见过太多人一上来就问"React怎么学""Vue和React哪个好"&#xff0c;但真正让一个开发者进阶的&#xff0c;往往是先搞明白框架为什么存在&#xff0c;它到底在解决什么痛点。React框架的核心…

作者头像 李华
网站建设 2026/10/19 6:36:16

AI编码代理上下文压缩后如何恢复?用init.d思路构建外部记忆系统

压缩发生后&#xff0c;我盯着终端发呆了三秒钟。我的 AI 编码代理刚刚经历了一次彻头彻尾的“脑叶切除术”——二十万 token 的项目上下文被平台自动压缩成一个干巴巴的摘要&#xff0c;它从“记得每一个设计取舍的资深协作者”变成了“一个热情但失忆的新实习生”。就在压缩前…

作者头像 李华
网站建设 2026/10/18 17:33:14

XSS三类型本质:反射型、存储型、DOM型攻击面解析

1. 项目概述&#xff1a;为什么DOM型、反射型、存储型XSS不是三种“并列技巧”&#xff0c;而是一张攻击面地图的三个坐标点你打开DVWA靶场&#xff0c;点开XSS&#xff08;Reflected&#xff09;那一关&#xff0c;输入<script>alert(1)</script>&#xff0c;弹窗…

作者头像 李华
网站建设 2026/10/19 16:54:43

Windows下YOLOv3目标检测环境搭建与稳定推理实战

1. 项目概述&#xff1a;为什么在Windows上跑YOLOv3目标检测不是“装个包就完事”你搜到这个标题&#xff0c;大概率正卡在某个环节&#xff1a;PyTorch装不上、CUDA版本对不上、模型加载报错“no module named ‘cv2’”、或者好不容易跑起来了&#xff0c;但一张图要推理12秒…

作者头像 李华