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,完整走通了分词器、预训练、微调和基准测试的全流程,并将成本控制在了一杯咖啡的价格以内。

模型架构: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 125M | 125M | 通用网页文本 | 0.8% |
| Keera 预训练 | 97.5M | 2B token Python | 1.8% |
| codeparrot-small | 110M | 同数据集 | 3.8% |
| GPT-Neo 1.3B | 1.3B | 通用网页文本 | 4.8% |
| Keera 微调(初步) | 97.5M | +50k 指令 | 5.5% |
| Codex 85M | 85M | GitHub 代码 | 8.2% |
| phi-1-small | 350M | “教科书级”数据 | 45% |
phi-1-small 的 45% 尤其引人注目——它用了过滤后的网页数据、合成教科书和编码练习。作者认为这暗示数据质量值得大力投入,但 phi-1-small 参数量更大、训练预算不同,无法单独归因于数据。
pass@1 是代码生成领域的标准评估指标:对每道题只生成一个答案,若该答案通过全部单元测试则计为 1,否则为 0,最终取平均通过率。它衡量的是“一次尝试就写对”的概率,比 pass@10(生成 10 个答案中至少一个通过)更严格。HumanEval 包含 164 道手写 Python 题,每道题附带隐藏测试用例,模型无法看到测试代码,因此能较好地反映泛化能力,但需警惕训练数据与测试集重叠造成的数据污染。
成本清单
| 步骤 | 位置 | 时间 | 成本 |
|---|---|---|---|
| 分词器 + 数据 | MacBook | 分钟级 | – |
| 100M token 测试 | MacBook M3 Pro | 9 小时 24 分 | – |
| 2B token 预训练 | RTX 5090, Vast.ai | 2 小时 23 分 | $1.40 |
| 微调 | MacBook | 4 小时 40 分 | – |
| HumanEval | MacBook | 约 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 还很远。价值在于:用极低的成本走通全流程,让每个环节都变得可理解、可调整。对于想深入理解语言模型内部机制的人来说,这比读十篇论文更有说服力。
相关推荐

Gemini 4 Argon实测:AI自主造桌游、建寺庙、训机器人
Gemini 4 Argon 实测汇总:AI 自主用 Blender 搭建书桌赛道与寺庙、编写光线追踪引擎、训练人形机器人连进7球,并附 API 定价与开发者接入方式。

魔法八边形硬币:为何仅9%的人能预测它的翻转?
魔法八边形硬币仅9%的人能正确预测其翻转方向。本文解析其空间旋转机制、常见认知误区,并介绍基于相关性理论的新假说,探讨人类空间推理的局限。

Sam Altman:AI正迈入科学发现时代,数学突破将复制到生物与能源
Sam Altman 表示 AI 正进入科学发现时代,数学领域的突破方法将复制到生物学、材料科学和能源领域,帮助研发新药与新能源。他认为这种影响不会让人直接感知,而是融入社会日常运转。