[控场AI]
· 10 分钟阅读· 5,262 字

97.5M参数Python模型从零训练仅花1.4美元

97.5M参数Python模型从零训练仅花1.4美元

开发者用1.40美元租GPU从零训练97.5M参数Python代码模型,走通全流程。

一位开发者从零构建了97.5M参数的Python代码模型Keera,完整走通分词器、预训练、微调和HumanEval基准测试全流程。模型采用GPT-2 small架构并引入RoPE、RMSNorm和权重共享等现代组件,自研16,384词表分词器将嵌入层参数压缩至12.6M。在Vast.ai租用RTX 5090进行2B token预训练仅耗时2小时23分钟,成本1.40美元。微调50k指令样本后,HumanEval pass@1从1.8%提升至5.5%,但作者明确指出评估设置不同且存在数据污染风险。文章强调损失下降不等于任务准确率提升,并总结了租GPU前先跑基准估算成本等实用经验。

训练一个语言模型需要多少钱?一位开发者用实践给出了答案:1.40 美元。他从零构建了一个 97.5M 参数的 Python 代码模型 Keera,完整走通了分词器、预训练、微调和基准测试的全流程,并将成本控制在了一杯咖啡的价格以内。

Keera模型架构图

模型架构:GPT-2 small 的现代复刻

Keera 的骨架几乎是 GPT-2 small 的翻版:12 层 Transformer 块,隐藏维度 768,总参数量 97.5M。每个块执行两个操作——先做注意力(token 之间互相看),再做 MLP(每个 token 独立处理),中间用残差连接让信息直通。

x = x + Attention(RMSNorm(x))
x = x + MLP(RMSNorm(x))

参数分布上,12 个块占了 84.9M(87%),嵌入层因共享输出权重控制在 12.6M(13%)。作者的选择逻辑很清晰:复制已知在这个规模可行的方案,只在有更便宜或更好的新组件时才替换。

几个关键设计决策值得注意:

  • 自研分词器,16,384 词表:在 Python 代码上训练,保留缩进作为独立 token,平均 3.9 字符/token。相比 GPT-2 的 50k 词表,嵌入层参数量从 38.6M 降到 12.6M,把更多参数预算留给了 Transformer 块。
  • RoPE 位置编码:通过旋转 query 和 key 编码相对位置,不增加学习参数。
  • RMSNorm:在每个子层前归一化,不减均值、不加偏置,训练稳定。
  • 权重共享嵌入:输入输出层共用矩阵,省下 12.6M 参数。
  • 1,024 token 上下文:大多数 Python 函数能装下,注意力成本随长度平方增长,不宜过大。
  • 2B token 训练数据:按 Chinchilla 约 20 token/参数的规则,20 × 97.5M ≈ 1.95B,刚好匹配。

残差连接是深度 Transformer 能够稳定训练的关键结构:它将子层(注意力或 MLP)的输入直接加到其输出上,形成 x = x + f(x) 形式。这样一来,梯度可以通过恒等路径无损地回传,缓解深层网络的梯度消失问题;同时每个子层只需学习对输入的“增量修正”,优化目标更简单。Keera 的 12 层堆叠之所以能顺利收敛,残差连接与 RMSNorm 的配合功不可没。

预训练:2小时23分钟,1.40美元

数据用的是 codeparrot-clean——GitHub 上经过去重的 Python 文件集合,被 tokenize 成一个 4GB 文件,文件之间插入 EOF 标记。训练时随机采样 1,024 token 窗口。

作者先在 MacBook 上跑了 100M token 的测试,花了 9 小时 24 分钟。按这个速度,2B token 需要约 190 小时,于是转向租用 GPU。

在 Vast.ai 上租用 RTX 5090,每小时 0.584 美元。配合 bf16 和 torch.compile,吞吐达到约 234,000 token/秒——是 MacBook 训练设置下的 77 倍。最终 2B token 预训练耗时 2 小时 23 分钟,验证损失 1.501(困惑度约 4.5)。

对比 MacBook 测试运行停止时的损失 2.125,下降明显。但作者也坦承,两者的批次大小和调度策略不同,并非受控的批次大小对比。

不过,预训练后的模型能写出“看起来对但经常不对”的代码。比如让 is_even(n) 判断偶数,它给出 if n > 0: return False——形状对了,逻辑全错。

租 GPU 的实用经验

作者在 Vast.ai 上踩了几个坑,值得记录:

  • 先跑基准再训练:30 步就能测出 tokens/秒,成本估算公式为 tokens ÷ tok/s ÷ 3600 × 每小时价格。本例即 2e9 ÷ 236,000 ÷ 3600 × $0.584 ≈ $1.40。
  • 选按需实例:先试了两台更便宜的可中断机器,训练还没开始就被出价更高的人抢占暂停了。
  • 用 git archive 同步代码:排除未跟踪文件和缓存,但注意已提交的文件(包括密钥)会被一起打包。
  • 用完立即销毁实例:停止的实例仍会为磁盘计费。

bf16(Brain Floating Point 16) 是一种 16 位浮点格式,与 FP16 相比保留了与 FP32 相同的 8 位指数范围,只是尾数精度较低。这意味着训练时不容易出现梯度下溢或溢出,通常无需损失缩放即可直接使用。在 RTX 5090 等支持 bf16 的 GPU 上,矩阵乘法吞吐远高于 FP32,同时显存占用减半,是大模型预训练的标准精度选择。

微调:让模型学会回答问题

预训练模型只会续写文本,问它问题它可能继续写更多问题。作者用 self-oss-instruct-sc2-exec-filter-50k 数据集微调——50k 条 Python 指令和答案,每条答案的代码都实际运行并通过了测试。

数据包装成简单的对话格式,模型读完整段但只对答案部分计算损失:

<|user|> Write a function that reverses a string. <|end|> <|assistant|>
Here's how … def reverse(s): return s[::-1] … <|end|>

49,549 条训练样本打包成 16,121 行(每行 1,025 token,约 96% 填充率),在 MacBook 上跑了约三个 epoch,耗时 4 小时 40 分钟。

验证损失在第三个 epoch 基本停滞在 0.81,而训练损失继续降到 0.47,提示收益递减和潜在过拟合。作者表示下次会对比更早的检查点,但尚未测量两个 epoch 是否能达到相同的 HumanEval 分数。

模型对答案格式的掌握几乎立竿见影——第 100 步就能写出简短介绍、一个代码块、一句解释然后停止。但逻辑正确性来得慢得多。作者自测了 10 个提示:1 个正确,3 个接近,6 个错误。

指令微调(Instruction Tuning) 是在预训练之后,用“指令-回答”配对数据继续训练模型,使其从单纯续写文本转变为遵循用户意图作答。关键在于损失只计算在答案部分(即 <|assistant|> 之后),问题部分被掩码掉,这样模型学习的是“给定问题生成答案”的条件分布,而非复述问题。数据质量比数量更重要——答案若经过执行验证,能显著减少模型学到错误模式。

HumanEval 表现:5.5%,但有前提

HumanEval 有 164 道 Python 题,单元测试对模型不可见。作者用贪婪解码每题生成一个答案,报告 pass@1。每个答案在独立进程中运行,带临时工作目录和 10 秒超时。评分器经过双向验证:164 个官方答案全部通过,空答案全部失败。

结果:预训练模型 3/164(1.8%),微调后 9/164(5.5%)。

但作者明确标注了这些结果的局限性:

  • 未检查数据污染:训练数据是否与 HumanEval 重叠尚未验证。
  • 前后评估设置不同:预训练用 300 token 续写,微调用对话指令加 512 token,提取第一个代码块测试。提示格式和 token 预算的变化意味着这并非微调效果的受控测量。
  • 结果文件不完整:保存的微调逐题结果文件含 NUL 字节,需要恢复或重跑才能视为完全可复现。

与公开模型对比(评估设置不同,非同一基准下的排名):

模型参数量训练数据HumanEval pass@1
GPT-Neo 125M125M通用网页文本0.8%
Keera 预训练97.5M2B token Python1.8%
codeparrot-small110M同数据集3.8%
GPT-Neo 1.3B1.3B通用网页文本4.8%
Keera 微调(初步)97.5M+50k 指令5.5%
Codex 85M85MGitHub 代码8.2%
phi-1-small350M“教科书级”数据45%

phi-1-small 的 45% 尤其引人注目——它用了过滤后的网页数据、合成教科书和编码练习。作者认为这暗示数据质量值得大力投入,但 phi-1-small 参数量更大、训练预算不同,无法单独归因于数据。

pass@1 是代码生成领域的标准评估指标:对每道题只生成一个答案,若该答案通过全部单元测试则计为 1,否则为 0,最终取平均通过率。它衡量的是“一次尝试就写对”的概率,比 pass@10(生成 10 个答案中至少一个通过)更严格。HumanEval 包含 164 道手写 Python 题,每道题附带隐藏测试用例,模型无法看到测试代码,因此能较好地反映泛化能力,但需警惕训练数据与测试集重叠造成的数据污染。

成本清单

步骤位置时间成本
分词器 + 数据MacBook分钟级–
100M token 测试MacBook M3 Pro9 小时 24 分–
2B token 预训练RTX 5090, Vast.ai2 小时 23 分$1.40
微调MacBook4 小时 40 分–
HumanEvalMacBook约 20 分钟–

$1.40 仅覆盖主预训练运行的租用费用,不含环境搭建、数据准备、存储、传输、早期租用尝试和笔记本硬件折旧。

关键收获

作者总结了几个值得记下的教训:

损失和任务准确率是两回事。 20 倍训练 token 把损失从 2.13 降到 1.50,但两个模型在作者 9 个非正式测试任务上都只解决了 2 个,且成功项不同。

微调时要盯验证曲线。 第三个 epoch 的验证损失改善甚微,更早的检查点也值得跑一次基准。

Mac 上限制 GPU 内存有帮助。 设置 PYTORCH_MPS_HIGH_WATERMARK_RATIO=0.6(低水位 0.5)解决了 18GB M3 Pro 上的交换和 GPU 卡顿。

先在小模型上测试。 一个 CPU 大小的微缩版模型在花钱之前就抓到了大部分 bug。

下一步计划

  • 更好的数据:教育类 Python,吸取 phi-1 的经验。
  • 更可复现的数字:恢复完整 HumanEval 输出,检查污染,在匹配评估设置下对比检查点,再加入 MBPP 和 pass@10。
  • 填充中间训练(FIM):让模型能在编辑器内自动补全,特殊 token 已在分词器中。
  • 扩大规模:下一个目标是 350M,然后可能 1B。按 20 token/参数,1B 模型需要约 20B token,会先跑基准再估算租用成本。
  • 更长上下文:将 RoPE 从 1,024 扩展到 4,096 token。
  • 偏好调优(DPO):在微调之上叠加。

值得一提的是,作者在构建过程中大量使用了 AI:Claude Opus 5.5(在 Claude Code 中)写了大部分代码和测试,Codex 帮助审查实现和检查结果。但决策、实验、租 GPU 和验证结果都是作者本人完成的。

这个项目的价值不在于 5.5% 这个数字本身——它离实用 coding assistant 还很远。价值在于:用极低的成本走通全流程,让每个环节都变得可理解、可调整。对于想深入理解语言模型内部机制的人来说,这比读十篇论文更有说服力。

#Python代码模型#从零训练语言模型#GPU训练成本#HumanEval基准#GPT-2架构#模型微调#开源语言模型
分享:

相关推荐