15. 算力账:这个任务从来没有把显卡吃满,所以"加参数"是免费的
这一节回答一个很自然的质疑:「我记得早期训练时显卡是吃满的,怎么现在只有 17%? 是不是模型被缩水了?」
答案:不是缩水,是这个任务的算力需求本来就远小于机器的供给。 而且顺着这条线查下去,发现了一个我自己的设计错误(15.3)。
#15.1 为什么 GPU 只有 17%(逐项实测)
| 项 | 实测 | 出处 |
|---|---|---|
| 屏幕 | 96×96,4 通道视觉 → 36864 维 | read_screen.py |
| 参数量 | 1,311,604 | diag_capacity_budget.py |
| batch × T | 16 × 16 = 256 刻/步 | exp31 默认 |
| 每刻 GPU 时间 | 0.64 ms(rollout 全程 10.2 ms / 16 刻) | diag_sampling_profile.py |
| 每刻要发几次内核 | Crystal 4 层 + 编码器 + 循环核 + 两个头 ≈ 10 次 |
0.64 ms / 10 次 = 每次内核 64 µs。 而一次内核启动本身要 5–20 µs, 一层在 16×96×96 上的卷积只要 20–40 µs。
★★ 显卡不是算得慢,是"派活"比"干活"贵。 它在等 CPU 把内核一次次递进队列。
这与项目早期的一条记录一致(§13 的 launch-overhead bound, not compute bound:
0.072% 算力利用率、75% 的迭代时间在 ~10 次内核启动上)。
早期任务(延迟匹配/觅食)每次前向的内核更大,所以能填满;
换成认字(96×96 + 小 batch + 逐刻)之后就进入 launch-bound 区间。
#15.2 ★★ 免费容量的边界(diag_capacity_budget.py,240 秒预算)
| 配置 | 参数量 | 秒/步 | 240 秒内步数 | 240 秒内总刻数 |
|---|---|---|---|---|
现状 units256 bb16 B16 | 1,311,604 | 0.185 | 1,295 | 331,520 |
units256 bb16 B64 | 1,311,604 | 0.403 | 595 | 609,280 |
units1024 bb64 B64 | 1,844,644 | 0.381 | 629 | 644,096 |
units2048 bb128 B64 | 3,128,804 | 0.401 | 597 | 611,328 |
units4096 bb256 B128 | 7,663,204 | 0.686 | 349 | 714,752 |
★★ 参数 ×5.84,同样 240 秒内能跑的刻数 ×2.16。 容量涨近 6 倍,数据吞吐还翻倍,时间一分没多。
怎么读这张表(两点必须一起看,否则会误读)
- 在同一个 batch 下比:
B64那一列,units从 256 → 2048(参数 ×2.4), 秒/步 0.403 → 0.401(+0.5%)。→ 同 batch 下加参数是免费的。 - 加大 batch 会变慢,但总吞吐上去了:
B160.185 秒/步 →B640.403(×2.17), 但刻/秒 1382 → 2541(×1.84)。→ 每个参数更新更贵了, 可每个更新用了 4 倍数据。 - ★ "免费"只说明跑得动,不说明更聪明。 更聪明必须真训一次、比判据 5。
#15.3 ⚠️ 查这条线时发现的我自己的设计错误
⚠️⚠️ 更正(2026-10-03):本节下面的数字是
units=256时代的,而且"认知核"这个 标签把【循环核整块】与【循环单元本身】混在一起了。实测(默认units=4096)见本节末尾。 ★ 我把原始数字保留在下面(历史价值),但【请以末尾的实测表为准】。
编码器 1,215,376 92.7% ← 其中 fc 一层就 1,200,896
循环核(认知核) 25,616 2.0% ← 真正的"思维"只有 2.6 万参数
策略头 21,074 1.6%
价值头 33,025 2.5%units 只控制循环核与两个头,而 92.7% 的参数卡在编码器那一层固定的 fc
(4690 → 256)上 —— 它由屏幕尺寸 96×96 与 img_down=3 决定,units 动不了。
★★ 也就是说:这个项目真正的"认知核"只有 2.6 万参数。 我一直把"参数量"当模型规模,实际上我一直在调的那个旋钮没在调模型。 这是注意力错位,不是配置写错。而且
backbone_units一直只有 16 —— 一个 256 维的状态,内部主干被压到 16 维再展开。
两个旋钮必须分开调:
| 旋钮 | 现在 | 控制什么 |
|---|---|---|
img_width / fc 结构 | 32 / flatten 4690→256 | 视觉容量(92.7% 的参数) |
units + backbone_units | 256 / 16 | 认知容量(2.6 万参数) |
#★★★ 15.3b 实测更正(2026-10-03):参数分配随 units 变化,而编码器是固定成本
工具:tools/verify_param_split_claim.py(VANILLA ReadPolicy,backbone=256,img_down=3)
units | 总参数 | 编码器 | enc% | 循环核 | core% | 各头 | heads% |
|---|---|---|---|---|---|---|---|
| 256 | 1,845,221 | 1,215,376 | 65.9% | 394,496 | 21.4% | 235,349 | 12.8% |
| 1024 | 3,336,677 | 1,215,376 | 36.4% | 1,380,608 | 41.4% | 740,693 | 22.2% |
| ★ 4096(默认) | 9,302,501 | 1,215,376 | ★ 13.1% | 5,325,056 | ★ 57.2% | 2,762,069 | 29.7% |
| 16384 | 33,165,797 | 1,215,376 | 3.7% | 21,102,848 | 63.6% | 10,847,573 | 32.7% |
★★★★ 编码器是【固定成本】(1,215,376,与
units无关,由屏幕尺寸与img_down决定) → 所以units越大它占比越低。★★★ -> "92.7% 卡在编码器上"只在【很小的
units+ 很窄的backbone】下成立。 在默认配置(units=4096且backbone=256)下:编码器 13.1%、循环核 57.2%, 认知核已是编码器的 4.4 倍。★★ 而"认知核 2.6 万"那个数字还有第二个口径问题: 它大概是【循环单元本身】(含
backbone投影),而"循环核 394,496"是整个core模块。 两个数说的是不同的东西,不该并列。★ -> 所以"注意力错位"这个判断:在旧配置下【成立且重要】; 在默认配置下【已不成立】。而这条更正本身就是"文档会过时"的一个现场。
#15.4 待验证的猜想(先写下来,别当成结论)
| 猜想 | 怎么验 | 预期 |
|---|---|---|
| 加宽认知核能提高判据 5 | units4096 bb256 B128 重跑 s2 配置 | 不确定,但成本为零,值得试 |
backbone_units=16 是学不动长序列的原因之一 | 只改它(16 → 64/256),隔离变量 | 有一定道理(信息瓶颈) |
| 加大 batch 会让学习变慢(更新次数少) | 固定总刻数,B16-4000步 vs B128-500步 | 会慢,所以要和"加参数"分开评价 |
#15.5 第 4 轮实验:把"免费"换成真读数(g_small vs g_big)
公平比较的设计(这是关键,否则读不出东西):
两者都跑 1000 步 × batch 128 × T 16 = 205 万刻 ← 数据量完全相同
只有 units / backbone 不同:
g_small : units 256, backbone 16 -> 1,311,604 参数
g_big : units 4096, backbone 256 -> 8,712,036 参数 (6.6 倍)
其余全部相同:echo-drop 0.5 / emit-cost 0.15 / drive-emit 0 / lr 2e-3 / 课程相同★ 为什么把 batch 一起放大到 128:实测 B16 -> B128 的吞吐是 1382 -> 3406 刻/秒。 如果还留在 B16,"加参数"的收益会被"喂不饱"掩盖。
g_small 的结果(先出来):
| 指标 | g_small(B=128,1000 步) | 对照:e50(B=16,2500 步) |
|---|---|---|
| β=0 正确率 | 52.1% | 22.9% |
| 首字对 | 77.1% | 50.0% |
| 平均距离 | 0.75 | 1.53 |
| 吞吐 | 4221 刻/秒 | 1742 刻/秒 |
★★ batch 从 16 加到 128、总刻数还更多(205 万 vs 64 万),正确率 22.9% -> 52.1%。 也就是说:之前那些跑次全都在"喂不饱"的区间里,一半的算力是浪费掉的。
★ 注意 52.1% 与 β=0 的口径:这一次课程在 600 步就长满(--grow-until 600),
所以最后的池是完整的 142 字。判据 5 的分母(纯模仿 30.2%)要重新测
—— 那是 B16/2500 步下的数,不能直接拿来比。