你的显卡除了打游戏和跑 Stable Diffusion,还能自己给自己做研究。你在睡觉,它在改代码、跑实验、记录结果——早上起来看日志就行。
这是什么?
一句话:把训练 GPT 的环境搭好,让 AI agent 自己修改代码、跑实验、记录结果,无限循环。
概念很简单:
1. AI agent 修改 train.py——改模型结构、超参数、优化器,随便它
2. 在 GPU 上跑 5 分钟训练
3. 检查验证集指标 val_bpb(bits per byte,越低越好)
4. 改进了就保留修改,没改进就回退
5. 循环——你睡一觉 8 小时,它自己跑了 ~100 次实验
如果你想在 Windows 消费级显卡上跑,参考这个fork[autoresearch-win-rtx](https://github.com/jsegov/autoresearch)
在我的 4070 SUPER 上跑起来
原始代码把数据下载到 %LOCALAPPDATA%\autoresearch\,我想让它直接放在项目目录下,方便管理和删掉重来。改了三个地方:
prepare.py——把 _default_cache_dir() 的默认路径从系统缓存目录改成项目根目录下的 data/:
def defaultcache_dir():
env_cache = os.environ.get("AUTORESEARCH_CACHE_DIR")
if env_cache:
return os.path.expanduser(env_cache)
return os.path.join(os.path.dirname(os.path.abspath(__file__)), "data")train.py——autotune 缓存路径同理:
def getautotune_cache_path():
env_cache = os.environ.get("AUTORESEARCH_CACHE_DIR")
if env_cache:
base = Path(os.path.expanduser(env_cache))
else:
base = Path(__file__).resolve().parent / "data"
return base / f"{AUTOTUNE_CACHE_VERSION}.json".gitignore——加上 data/。
两个文件都保留了 AUTORESEARCH_CACHE_DIR 环境变量的覆盖能力,改的只是默认值。
准备数据
uv run prepare.py输出:
Cache directory: F:\Pyrepo\autoresearch-win-rtx\data
Dataset: tinystories
Data: downloading tinystories_gpt4_clean.parquet...
Data: downloaded to data\datasets\tinystories\data\tinystories_gpt4_clean.parquet
Tokenizer: training BPE tokenizer...
Tokenizer: trained in 20.9s, saved to tokenizer.pkl
Tokenizer: sanity check passed (vocab_size=8192)
Done! Ready to train.数据集是 TinyStories GPT-4 clean——HuggingFace 上一个用 GPT-4 生成的儿童故事语料。大概 1GB,BPE tokenizer 词表 8192,20 秒训完。
Smoke test
先跑个快速验证,确保整个链路是通的:
uv run train.py --smoke-test输出很精彩,每一步都能看到它做了什么:
GPU: NVIDIA GeForce RTX 4070 SUPER
GPU VRAM: 12.0 GB
GPU CC: 8.9
GPU profile: ada-10-15gb
Consumer matrix support: yes
TF32: enabled
AMP dtype: torch.bfloat16
Running consumer GPU autotune in eager mode...
Autotune probe: train_batch_size=16, checkpointing=on
accepted: tok/sec=62,787, peak_vram_mb=5401.7
Autotune probe: train_batch_size=8, checkpointing=on
accepted: tok/sec=64,398, peak_vram_mb=2985.3
Autotune probe: train_batch_size=4, checkpointing=on
accepted: tok/sec=63,710, peak_vram_mb=1777.1
Autotune selected candidate: batch_size=8, checkpointing=on.先跑了一遍 autotune——把候选 batch size(16, 8, 4)各试一次,选吞吐量最高的。batch_size=8 胜出,每秒 64,398 token,显存才用了 3GB,很轻松。
然后正式训练(smoke test 只跑 3 步):
Model config: n_layer=8, n_head=4, n_embd=512
Parameter counts:
total : 50,332,176
Estimated FLOPs per token: 2.390784e+08
step 00000 | loss: 9.010334 | tok/sec: 64,378 | mfu: 10.8%
step 00001 | loss: 9.400230 | tok/sec: 64,336 | mfu: 10.8%
step 00002 | loss: 9.272112 | tok/sec: 64,296 | mfu: 10.8%
Eval completed with batch_size=8
---
val_bpb: 2.645191
training_seconds: 0.0
total_seconds: 24.9
peak_vram_mb: 2985.3
total_tokens_M: 1.6
num_params_M: 50.3一切正常。autotune 结果缓存在 data/gpu-profile-v2.json,下次跑就不用重新调优了。完整训练会跑满 300 秒(5 分钟),smoke test 只验证流程。
完整训练
smoke test 过了,直接跑正式的 5 分钟训练:
uv run train.py完整输出如下,附带逐部分分析:
模型结构
wte : 4,194,304 ← token embedding (8192 × 512)
value_embeds : 16,777,216 ← Value Embedding 参数 (占了 33%)
lm_head : 4,194,304 ← 输出层 (512 × 8192,与 wte 共享权重)
transformer_matrices : 25,166,336 ← Transformer 层里的 QKV、FFN 等矩阵
scalars : 16 ← RMSNorm 的缩放因子 (可忽略)
total : 50,332,176 ← ~5030 万参数value_embeds 特别大是因为用了 Value Embedding (VE) 机制——每隔一层给 token 加一个可学习的 embedding 向量,4 层有 VE × 512 维 × 8192 词表 = 1677 万。这部分参数占了总数的 1/3。
训练配置
Estimated FLOPs per token: 2.39e8 ← 每处理 1 个 token 需要 ~2.4 亿次浮点运算
Scaling AdamW LRs: 1.224745 ← 学习率缩放因子
Time budget: 300s ← 5 分钟硬上限
Gradient accumulation steps: 32 ← 每 32 步才做一次优化器更新FLOPs/token 由模型结构和序列长度决定,用于后面算 MFU
Scaling AdamW LRs:模型宽度从 768(默认配置)缩小到 512 了,LR 跟着等比缩放
1/√(512/768) = 1.225梯度累积 32 步:batch_size=8 太小,攒 32 步一起更新,等效 batch_size = 8 × 32 = 256
训练过程
step 00048 (99.0%) | loss: 2.891586 | lrm: 0.02 | dt: 7997ms | tok/sec: 65,564 | mfu: 11.0% | epoch: 1 | remaining: 0sstep 48 / 共 49 步:5 分钟刚好跑完,时间预算用得饱满
loss: 2.89:从初始 ~9 降到了 2.89,收敛正常
lrm: 0.02:此时学习率乘数(cosine schedule 末尾,降到初始的 2%)
tok/sec: 65,564:跟 autotune 测的 64,398 基本一致
mfu: 11.0%:模型 FLOPs 利用率。4070 SUPER 理论峰值 142.2 TFLOPS,实际只用 ~15.6 TFLOPS。**这个低不是显卡的问题,是模型太小了,GPU 的并行度吃不满,大量时间花在 kernel launch 和显存搬运上**
最终结果
val_bpb: 0.903106 ← 核心指标
training_seconds: 304.9 ← 纯训练时间(略超 300s,因为跑完当前 step 才停)
total_seconds: 494.8 ← 从启动到结束的总时间(含 autotune + eval)
peak_vram_mb: 2985.3 ← 显存峰值
mfu_percent: 11.27 ← 平均 MFU
total_tokens_M: 25.7 ← 总共处理了 2570 万 token
num_steps: 49 ← 49 个训练步
num_params_M: 50.3 ← 5030 万参数几个值得注意的点
val_bpb 从初始 ~9 降到了 0.9。 5 分钟训练,2570 万 token,在一个 5030 万参数的模型上,验证集的 bits per byte 从灾难性的水平降到了还算合理的位置。作为 baseline 起点还不错。
显存只用了 3GB,远没吃满。 当前 5030 万参数 + batch_size=8 只用了不到 3GB 显存。4070 SUPER 有 12GB,理论上可以撑到 1.5~2 亿参数的模型(当前的 3~4 倍)。模型越大 MFU 也会越高——GPU 算力能被更好地利用,不再是等数据喂进去的状态。
总耗时 495 秒,其中 190 秒是"开销"。 5 分钟训练需要 8 分钟墙上时间,多出来的时间主要花在 autotune 探测(~25s)+ 数据加载 + checkpoint 保存 + 验证集评估。好消息是 autotune 结果已经缓存到 data/gpu-profile-v2.json,后续跑不会再重复探测。但如果 AI agent 要跑 100 轮实验,每轮多 3 分钟开销还是不少的。
49 个 step 也就见了不到一个 epoch 的数据。 2570 万 token ÷ 2048 seq_len ÷ batch_size 8 ≈ 1568 个 batch 才走完一个 epoch。49 步 × 梯度累积 32 = 1568 个 batch forward,刚好一个 epoch。数据还没看全,训练已经结束了——这是 5 分钟时间预算的固有特点,意味着学习率策略和 warmup 节奏非常重要。
探索出来一个好配置,然后呢?
这个问题我问过——毕竟跑一晚上,如果产出的只是个在儿童故事上 val_bpb 从 2.6 降到 2.4 的 5000 万参数小模型,那有什么用?
直接产出确实没什么实用价值。 你拿一个小 GPT 在 TinyStories 上训练 5 分钟,得到的模型你不会部署、不会做 chatbot、不会用在实际产品里。
真正的价值在于"自动化研究"这个模式本身:
1. 原型验证。 program.md 是一套实验策略的 DSL。你写好策略,AI 自己跑实验。如果这套模式在单卡小模型上跑通了,你可以把它扩展到更大规模——更大的模型、更大的集群、更复杂的搜索空间。本质上你是在迭代"怎么让 AI 做研究"这个元问题。
2. 积累直觉。 从 results.tsv 你能看到——什么改动降低了 loss?什么改动 OOM 了?什么让收敛变快了?这些直觉是能迁移到你之后真正的训练项目上的。
3. 就是好玩。 显卡买回来,晚上睡觉它自己在那做研究,早上起来看实验日志。AI 试了什么奇怪的结构?哪个莫名其妙就 work 了?这件事本身就挺有意思的。
一句话总结:这个项目的产物不是模型,而是"能做研究的 AI agent"。 你现在跑的 train.py 是给 agent 的 playground,真正被你迭代的是 program.md。
当前的实验策略有多聪明?
说老实话——比较原始。
当前的 baseline program.md 就是一个盲目爬山:
修改 train.py → 跑 5 分钟 → val_bpb 降了就保留,涨了就回退 → 无限循环它有这些规则:
只能改
train.py,不能动prepare.py5 分钟时间预算,超 10 分钟就 kill
每次实验的结果记录到
results.tsv(commit hash、val_bpb、显存、状态、描述)删代码能保持效果 > 加代码
它没有这些:
实验计划——不会先列出假说再验证,就是随机改
历史分析——不会去读
results.tsv回顾之前试过了什么、哪些方向有戏搜索策略——没有贝叶斯优化、没有"上次调学习率有效,这次在附近再搜一下"
消融实验——不会控制变量,一次改一堆
灵感来源——不会读论文、不会引用已有研究
这也是 Karpathy 的设计意图——program.md 是给人迭代的。把最简版本跑通,然后你可以加策略进去:让 agent 先分析历史结果再规划下一次实验、搞多 agent 分工协作(一个想 idea、一个写代码、一个审 review)、甚至接入论文检索让 agent 带着理论依据改代码。
接下来做什么
跑通只是第一步。如果你也想在自己的显卡上搞,流程是这样的:
# 1. 准备数据(一次性)
uv run prepare.py
# 2. 跑一次完整训练看看效果
uv run train.py
# 3. 把 Claude Code 或者别的 AI agent 丢进去,让它读 program.md 开始自主实验然后你就可以去睡觉了。早上起来看 results.tsv,它在过去 8 小时尝试了哪些改动,哪些真的把 loss 打下来了。