Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
886a1f3
f16问题定位
EthanPeng-2048 Oct 13, 2026
aab2c81
f16 训练缺陷台账 24:修 stable=f16 溢出 + 危险精度组合告警 + 覆盖缺口
EthanPeng-2048 Oct 13, 2026
6c0d591
docs:f16 提速方案(26 号,台账 24 · F4)+ P2-9/P2-10 立项
EthanPeng-2048 Oct 13, 2026
2e200e8
f16 提速 S1s/S1t:精度编译期特化(去运行期分派三元),850M 上由 +10.6% 到 +0.9%
EthanPeng-2048 Oct 13, 2026
1377900
f16 提速 P2-13:网格粒度变体(#f/#ks/#kr)叠加到精度特化签名
EthanPeng-2048 Oct 13, 2026
b75f2b9
f16 提速 P2-12(部分):批量派发 #b 支持 extras 为空的静态签名变体
EthanPeng-2048 Oct 13, 2026
0799a49
f16 提速 P2-3:RAPT / CNN 的 f16 接线(含 4 条新 ctest 门禁)
EthanPeng-2048 Oct 13, 2026
e5fa160
f16 提速 S0/P2-9:实测推翻"step 内固定开销"前提(已评估-不做)
EthanPeng-2048 Oct 13, 2026
f5d0084
f16 提速 S1:实测净亏 + 质量下降 → 已评估-不做(回退实现)
EthanPeng-2048 Oct 13, 2026
399eb5e
f16 提速 P2-8/S4:CPU F16C 转换快路径(f16 训练不再慢于 f32)
EthanPeng-2048 Oct 13, 2026
50cf907
f16 提速 F5:四字段全 f16 可训练(Adam 状态缩放,替代训练循环 loss scaling)
EthanPeng-2048 Oct 13, 2026
6f93587
f16 提速 S2 / S5(P2-10):实测后评估 → 不做(附上界证据)
EthanPeng-2048 Oct 13, 2026
ea15796
f16 提速 P1-6 R2:Layer::init 参数存储精度自检(当场抓到第 3 处静默降级)
EthanPeng-2048 Oct 13, 2026
9f9bb41
f16 提速:rapt_test 补 GPU 可用性预检(CPU-only 构建下 f16_gpu 用例应为 SKIP)
EthanPeng-2048 Oct 13, 2026
329f8f1
f16 台账收口:F4 标完成(跨设备 −1.6%/−3.2%)、S3 标完成(小 N GEMV 已由 op-level 覆盖)
EthanPeng-2048 Oct 13, 2026
5bb6a51
f16 提速 P2-12 完成:优化器复合结构在 f16 下批量派发(scan 签名同源)
EthanPeng-2048 Oct 14, 2026
b8465f9
docs:P2-12 收口(AGENTS §2 不变量 + roadmap/24/26 台账 + history 两则)
EthanPeng-2048 Oct 14, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,7 @@ GPT 序列展平: 列序 i = b*seq + t(batch-major,全局唯一约定)
2. **生成**:`tools/fused_generate.hpp`(原独立工具 `gen_fused`)对**每个结构发三份**:V0 全 f32(键 = `key`)/ **V1 运行期精度分派**(键 = `key#x`)/ V2 native16(键 = `key#a`,按 `expr_prec_sig_native16` 的**结构谓词**判定)→ 经 `emitter_registry` 选后端(默认 `"glsl"` = `GlslEmitter`)生成 GLSL → glslc → 内联 SPIR-V → `build/generated/fused_registry.hpp`。**不经 `.bin` 中间序列化**(`write_registry/read_registry` 已删除)。
- **精度签名不再是构建期集合**("签名从身份降级为参数"):V1 的 GLSL 对每个输入/输出各声明 f32 + `float16_t` 双视图,加载/存储处按 push constant `uint prec`(bit i = 输入 i 为 f16,bit16 = 输出 f16)走 uniform 分支,**一份 shader 覆盖任意签名**。因此构建期不需要枚举签名,也**不存在回填清单**(`tools/prec_backfill.txt` 已删除,`NN_PREC_TRACE` 的"补变体"流程随之失效)。⚠ 精度路径仍有**三档**、不是"永不 miss":`key#a`(native16)→ `key#x`(V1 运行期分派)→ **基类边界 cast**;当某个结构的 `#x` 生成被跳过(`[skip] 分派变体…`)、`NN_SCAN_NO_DISPATCH=1`、或设备无 16bit 存储时,运行期会 miss 并落到边界 cast(`[prec][miss]` 仍会触发,它是通用诊断)。**V2 谓词含常量值判据而常量值不进 key**:运行时选 `#a` 前会用**真实实例的 spec 重跑** `expr_prec_sig_native16`,否则"生成时那一个实例的常量"会被套用到同结构但常量不同的实例上(微值在 f16 下 flush-to-0 = 静默错值)。**变体选择事件(V1/V2)一律打 `NN_PREC_TRACE` 日志**(V0 默认路径不打——f32 热路径会淹没输出;V1 分支曾漏打,导致"V1 从未被使用"的错误判断),且**只在确认命中后打、只由真正 dispatch 的入口打**(`supports_expr_precision_variant` 只是查询,它打日志会让计数翻倍:实测修复前 12040 行 = 6020 次真实分派 × 2)。**实测 V1 覆盖**(`NN_PREC_TRACE=1 f16_precision_test`):6022 条命中日志 / 24 条 distinct 结构 = **20 条 V1(`#x`)+ 4 条 V2(`#a`)**,V1 覆盖**全部四个生成器族**(元素 9 / 归约 6 / matmul 4 / fold 1),且该测试用 `CHECK_NEAR` 容差断言;miss 为 0。
- **新增调用点无需改动其他文件**(实测):在 Layer 头里新写一个 `dsl::compute`(其结构不在任何 dry-run / 模型 pass 覆盖内)→ 锚点独有 +1、结构 +1、注册表新增 `key` / `key#x` / `key#a` 三条,**其他文件零改动**。
- **编译期精度特化(`key#s` / `key#xxxx`,2026-10-13)——改 f16 变体选路前必读**:`#x` 的"一个 shader 覆盖任意签名"在热循环里每次读写都要带 `((prec >> i & 1u) ? float(bNh[…]) : bN[…])` 三元,**实测这一项就是 f16 变慢的主因**(850M:CE 融合注意力 146.1→120.0 ms,同机 f32 基线 117.2)。因此生成期追加两类**静态签名**变体(与 `#x` 数值逐字等价,只需 16 位存储、**不需要 `shaderFloat16`**):`key#s` = "全输入 f16 + 输出 f16"(无条件发射,兜底)与 `key#xxxx` = **扫描期真实观察到的签名**(`ExprRegistry::variants`;`scan_exprs` 的 dry-run **与模型 pass 各跑 f32 + f16 两遍**,`scan_register`(`dsl::compute*`)与 CPU 引擎 `eval_expr*` 入口(fold / 直接求值**不经** dsl::compute,必须在引擎入口补记)按真实张量精度 + 真实 P 登记)。**运行期查找序 = `#a`(原生 f16)→ `#xxxx` → `#s` → `#x` → 基类边界 cast**;未枚举到的签名照旧落 `#x`,所以特化集合**只是优化、不是身份**(`ExprRegistry::variants` 是"优化清单",`specs` 才是结构身份)。⚠ **网格粒度变体也可叠加在特化签名上(2026-10-14)**:`#f`(细 tile)与 `#ks`/`#kr`(K 切分)现在对**每个静态签名变体**各发一份(键形如 `key#10003#ks`),网格饥饿形状(大 batch 的 wgrad)在 f16 下同样拿到 K 切分——40HX 实测该结构 **f16 16.0 ms/块 < f32 25.1 ms/块**(此前 f16 掉回 64-tile = 79 ms/块)。两条不变量:① `#ks` 的输出**恒 f32**(写的是部分和缓冲,后端按 f32 分配)→ 生成器把输出声明钉成 `float`,注入 sig 只决定 A/B 读取类型;② 后缀可叠加 ⇒ 后端"`#f`/`#ks`/`#kr` 无需 16 位存储"的豁免**只对纯 V0 键**成立(判据 = 去掉网格尾后缀后仍含 `#` 即需要 16 位存储)。运行期派发哨兵 `#x` **不**叠加网格变体(它多一个 `prec` PC 槽、绑定翻倍)。**批量变体同理(2026-10-14,P2-12 已完成)**:`#b` 对**每个 extras 为空的静态签名变体**各发一份(键形如 `key#0002#b`;extras 的元素类型不在 `ExprPrecSig` 编码里 → 带 extras 的 f16 结构仍逐 kernel 降级)。⚠ **P2-12 的残差(Adam 复合 kernel 在 `--f16` 下逐成员降级、绕开批量队列)根因是 "scan 与运行期不同源",不是派发器**——两条不变量:① **scan 的优化器 dry-run 必须把当前 pass 的 precision profile 透传给 `create_optimizer`**(文本路径 `text_train` 本来就在传;漏传 = f16 pass 仍按默认 f32 profile 定 `upd_prec`/写目标精度 ⇒ 融合出的复合结构与运行期不同 ⇒ 运行期命中不到 ⇒ 降级);② **复合 kernel(≥2 成员)的运行期精度签名必须在 scan 的 CPU `end_expr` 补记**(= `add_variant(spec, expr_prec_sig_of(输入张量, tail 输出精度))`)——单节点结构有 `scan_register`/`eval_expr*` 入口,**融合结构只在 GPU `end_expr` 执行时才算出签名** ⇒ 否则 f16 下复合结构只剩 V0/`#s`/`#x` 三种载体,拿不到 `#xxxx` 特化与其 `#b` 批量变体。两条坑:① 运行期**组键必须带精度身份**——批量变体的元素类型是编译期定死的(f16 槽走 `float16_t` 的 buffer reference),同组混签名会把 f16 缓冲按 f32 读;② 取实例缓冲必须按 `t.precision()` 选槽位(`gpu_tensor()` 的默认模板实参是 F32,f16 张量会命中"无 P 精度存储"断言)。
- **库外使用者:`nn_enable_gpu_fusion(<target> MAIN <含 main 的源>)`**(`CMakeLists.txt`):使用者在自己的层头里写 `dsl::compute` + 在 CMake 加这一行 → 自动生成**该目标专属**的 `fused_registry.hpp` 并加进它的 include path。机制 = 收集器由**库内收集逻辑**(`tools/scan_exprs.cpp`:锚点 + dry-run + 模型 pass + 显式登记)**加上本目标的全部 TU**(`-DNN_EXPR_SCAN` 编译,让使用者的调用点自登记)组成。**⚠ 必须保留库内收集逻辑**:99 个结构里只有 17 个来自编译期锚点、82 个来自 dry-run/模型 pass,"纯锚点收集器"会漏库自身的结构。**⚠ include 顺序有语义**:`${NNF_OUT_DIR}` 必须排在 `${CMAKE_BINARY_DIR}/generated` 之前(后者含库自身注册表与手写原语 shader 嵌入头),否则 `__has_include("fused_registry.hpp")` 会解析到库的那一份、使用者的自定义结构全部闭合世界报错。样例见 `examples/fusion_custom_layer*`(**库内目标**形态;函数本身定义在本仓 `CMakeLists.txt` 的 `if(Vulkan_FOUND)` 内,且仓库无 `install()`/export——真正**树外**工程要复用它需先做 install/export,见 `docs/todo/18-roadmap.md` E2)。
- **端到端验收(库外 + f16 分派)**:`build/fusion_custom_layer_example.exe` 实测两种**自研算子形态**:①逐元素 `exp(|x|·α)/(√|y|+α)`;②归一化形态 `rsqrt(col_reduce_sum(x)·inv + ε)`(= 库内 LayerNorm/RMSNorm 的 `std_inv` 同形,`raxis=1`、`viewkinds=[0,8]`,属库里"只有执行 Layer 才拿得到"的那一类)。两者都只用**锚点**(自定义层没有 dry-run)→ 都命中并跑通 ⇒ `nn_enable_gpu_fusion` 对常见自定义层可靠。实测值:CPU f32 `0.6712035`(差 0);**GPU f32(V0)`0.6712036`(差 5.96e-08)**;**GPU f16(V1 运行期精度分派)`0.6708984`(差 3.05e-04,f16 舍入内)**;归一化形态 GPU f16 `1.2246094`。含 rparams ⇒ native16 谓词不通过 ⇒ f16 命中必然是 `key#x`;注册表若漏了它就是闭合世界硬报错,"跑通"本身即断言。**残余不确定性**:库内确有 25 条结构只有执行 Layer/模型才拿得到(锚点的符号实例 `Expr{}` 与运行期实例折叠结果不同);实测的两种自定义形态不在其中,但无法先验排除。若使用者的自定义层遇到闭合世界报错,下一步是给 `nn_enable_gpu_fusion` 加 `COLLECT <file>` 钩子(由使用者提供"跑一遍自己的层"的收集函数,等价于库内的模型 pass)。
- **库是 header-only,多 TU 必须可链接**:头里的类外定义一律要 `inline`(曾漏 `GpuBuffer::~GpuBuffer` → ≥2 个 TU 的程序链接期 duplicate symbol,`examples/fusion_custom_layer*` 抓到)。
Expand Down Expand Up @@ -352,7 +353,7 @@ optimizer.step();
| `todo/08-pitfalls-and-lessons.md` | **踩坑警示录,改代码前读** |
| `todo/10-development-standards.md` | C++ 编码规范全文 |
| `todo/12-compute-engine-inventory.md` | **引擎接口盘点(复现:`bench/doc_inventory.ps1`):52 个 virtual 方法、Layer 直调 23 个(基础设施/数据搬运/状态扫描/fold 登记/IR-C 录制段开合)、两套 CPU 求值机制(DSL 模板 + IR 解释器)、ctest 29 个测试;演进记录见 `docs/history/history.md`** |
| `todo/14-f16-stable-gpu-loss-frozen.md` | **故障报告(2026-09-26,未修):GPU `stable=f16` 训练 loss 打印冻结(权重不冻结)——触发矩阵、测试覆盖缺口、证据与复现** |
| `todo/14-f16-stable-gpu-loss-frozen.md` | **故障报告(2026-09-26;2026-10-13 ✅ 已修/根因订正):GPU `stable=f16` 训练 loss 打印冻结(权重不冻结)——真因 = CE 的 (1,1) loss 归约按 `stable=f16` 落盘溢出,非回读链;触发矩阵、测试覆盖缺口、证据与复现** |
| `todo/17-unified-tensor-engine.md` | **统一 Tensor/ComputeEngine/MemoryPool 底层架构总纲(2026-09-30 裁定;M1-M6 全部已实施 2026-09-30,仅 M7 未立项):访问不变量(Tensor 存储私有、一切经引擎,已立为铁律 #11)、InitSpec 声明式初始化、批量 read/write、Matrix 降级为宿主 I/O 载体(L2+ 禁用 = 铁律 #12)、内存池契约统一、`import` 跨设备拉取、Layer 删每调用 engine 形参(M6 段 C,Loss/DSL/辅助对象保留形参见 §8);吸收原 15 未实施的 P2-P6(改期 M1-M7)。立项前先读本文件 + §5 分期 + §8** |
| `todo/18-roadmap.md` | **未来方向路线图 + 未完成项裁定台账(2026-10-01,13 号清单的承接者):P0-P3 分期、13 号逐条裁定(做/条件触发/不做)、触发式立项条件、非目标、门禁与验收口径。要规划下一步、查某件事该不该做、避免重复立项时读本文** |
| `todo/19-unified-dataset.md` | **统一数据集格式与加载(`.nndataset`/`.nnvocab`/KVRecord v2,2026-10-05 设计定稿、阶段一~三已实施):裁决记录、KVRecord v2 规范、文件布局、`nn::Dataset`/`dataset_gen`/`dataset_convert`/模型 v6 分期落地与兼容迁移。接数据集相关任务前读本文** |
Expand All @@ -376,7 +377,7 @@ optimizer.step();

### 已知问题

- **GPU `stable=f16` 训练 loss 打印冻结(未修)**:`text_train --precision-stable f16` 时 step 恒 4.0137、跨 epoch/跨进程逐位相同;**权重照常更新**(同进程跨 epoch 模型快照 63% 参数字节不同,冻结仅在 loss 回读链);CPU 同配置健康;ctest 全绿(27/27)但**无 GPU+stable=f16 用例**。触发矩阵、覆盖缺口与证据见 `docs/todo/14-f16-stable-gpu-loss-frozen.md`。推荐路径 `--f16`(stable/optimizer=f32)实测健康。
- **GPU `stable=f16` 训练 loss 打印冻结(✅ 2026-10-13 已修,原判断已订正)**:真因**不是** loss 回读链,而是 **CE 的 `(1,1)` loss 归约输出按 `stable=f16` 落盘溢出**(`Σ ≈ -log(vocab)·N` 超 65504 → inf → NaN;旧报告里的 `4.0137 ≈ 65504/num_valid` 是饱和表现)。修法 = `compute_loss.hpp` 稀疏(`fused_forward_sparse_`)与稠密两条链的 `(1,1)` reduce 输出**恒 `Precision::F32`**。实测(850M):`seq256/b32` 由 `inf` → `9.0056→8.6922`;`stable=f16` 120 步无 NaN。回归门禁见 `docs/todo/14-f16-stable-gpu-loss-frozen.md` 与 24 号 §3.1。推荐路径仍是 `--f16`(stable/optimizer=f32)。
- **数值性限制**:四字段全 f16 不可训练——`optimizer=f16` 单独即令 Adam 更新爆炸、`stable=f16` 链约 200 步 NaN,见 `docs/development/05-mixed-precision.md` §12.5;常规 f32 训练健康收敛。
- **不存在的 CLI 参数**:`--tdr-retry`/`--max-tdr-retries` 与 `mnist_train --osc-guard`/`--osc-window`/`--osc-threshold`(**均已移除**:前两者从未实现,后三者属"帮助声明了、解析分支不存在"的幽灵选项,已从帮助/控制器/GUI 三层清除,详见 `docs/history/history.md`「GUI / CLI 参数一致性清理」)。
- **`text_train --model zipt` 启动即中止(未修)** → **已随 ZiPT 整体移除而消失**:AttnZip/ZiPT 已从主线移除(`--model zipt` 现在直接报"未知模型架构"并给出迁移提示),代码保留在 **`legacy/zipt` 分支**。**恢复前提(两条同时满足)**:① **算法层**——压缩向量必须因果(压缩器只能看当前位置之前的内容);② **代码层**——实现质量对齐 RAPT/GPT(支持 `forward_recompute` 梯度检查点与 activation offload、CLI/序列化/测试齐备)。裁决依据见 `docs/history/history.md`「ZiPT 移除」条。已移除的 API:`ZiPTModel`/`ZiPTBlock`/`CrossAttention`、`build_zipt_model*`/`make_zipt_spec`/`ZiPTConfig`/`ZIPT_MEMORY_TOKENS`、`ModelSpec::is_zipt()` 与 `memory_tokens`/`window` 字段、CLI `--model zipt`/`--window`/`--memory-tokens`、`zipt_test`;`ModelType` 的 6 号枚举保留为 `Reserved_ZiPT` 占位,用于对旧 `.bin`(type=6)给出明确错误。
Expand Down
21 changes: 21 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -604,6 +604,12 @@ if(NN_BUILD_APPS OR NN_ENABLE_TESTS)
nn_add_executable(assert_always_test src/assert_always_test.cpp)
list(APPEND NN_TEST_TARGETS assert_always_test)

# ── F1:危险精度组合启动期告警(仅警告,不拒绝;docs/todo/24 §2.3)────
# [1][2] 头文件函数组合矩阵(含 all=f16)单测;[3] 子进程跑 text_train/
# mnist_train,捕获 stderr 断言告警出现 + 退出码不变;二进制缺失 → 77 = SKIP
nn_add_executable(cli_precision_warning_test src/cli_precision_warning_test.cpp)
list(APPEND NN_TEST_TARGETS cli_precision_warning_test)

# ── RV-04 优化器元数据表(唯一真源 ↔ 工厂)+ RV-03 同目标写序回归 ──────
# [1] 表内每个 name 都能被 create_optimizer 构造、名单文本与表一致;
# [2] AdamW(wd≠0)单步 vs 手写参考 + 顺序对调的负例;
Expand Down Expand Up @@ -655,6 +661,21 @@ if(NN_ENABLE_TESTS)
add_test(NAME cnn_test_gpu COMMAND cnn_test --gpu)
set_tests_properties(cnn_test_gpu PROPERTIES SKIP_RETURN_CODE 77)
endif()
# CNN / RAPT 的 f16 端到端变体(P2-3):Conv2D 权重/梯度按 param、融合算子
# 按 compute 走 f16 → 断言"loss 有限且下降 / 梯度无 NaN"。CPU 与 GPU
# 各一条(GPU 才暴露精度变体的闭合世界 miss 与 f16/f32 槽位错配)。
if(TARGET cnn_test)
add_test(NAME cnn_test_f16 COMMAND cnn_test --f16)
add_test(NAME cnn_test_f16_gpu COMMAND cnn_test --f16 --gpu)
set_tests_properties(cnn_test_f16 cnn_test_f16_gpu
PROPERTIES SKIP_RETURN_CODE 77)
endif()
if(TARGET rapt_test)
add_test(NAME rapt_test_f16 COMMAND rapt_test --f16)
add_test(NAME rapt_test_f16_gpu COMMAND rapt_test --f16 --gpu)
set_tests_properties(rapt_test_f16 rapt_test_f16_gpu
PROPERTIES SKIP_RETURN_CODE 77)
endif()
# BatchNorm 的 GPU 变体:推理态/EMA 表达式的 ExprSpec 校验只在 GPU
# 路径发生(CPU 模板路径不校验),且闭合世界 miss 只在 GPU 运行期暴露。
# 无 Vulkan 构建下返回 77 = SKIP。
Expand Down
4 changes: 4 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,10 @@ clang-format -i path/to/new_file.hpp

> 本地复现 CI 的三条门禁:`ctest --preset dev`、`NN_BIND_DEBUG=1 ctest --test-dir build`(都应全绿)。

**大版本发布**(版本号 `1.x.y → 1.(x+1).0`)在 CI 门禁之上还有一份必做清单——端到端模型训练测试
(这类测试**不进默认 ctest**)、发布文档、完整 ctest + **Debug** 构建、多编译器 × 标准库矩阵——
见 [`docs/todo/25-release-requirements.md`](docs/todo/25-release-requirements.md)。

---

## 7. 文档贡献
Expand Down
3 changes: 2 additions & 1 deletion docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
- `todo/00-code-review-remediation.md` — **2026-10-11 代码审查整改立项台账**(本批新增)。
- `todo/18-roadmap.md` — "下一步该做什么"的唯一入口。
- `todo/12` / `todo/17` / `todo/19` / `todo/21` / `todo/22` / `todo/23` — 现行设计/盘点。
- `todo/24` / `todo/25` — f16 训练缺陷台账 / **大版本发布要求**(`1.x.y → 1.(x+1).0`)。

### 总档

Expand All @@ -89,7 +90,7 @@
| [`../CMakePresets.json`](../CMakePresets.json) | 预设构建(`cmake --preset dev` 等)——见 CONTRIBUTING §1 |
| [`benchmarks/2026-09-25-vulkan-vs-cuda.md`](benchmarks/2026-09-25-vulkan-vs-cuda.md) | Vulkan 后端 vs CUDA(torch)基准 |
| [`benchmarks/2026-10-06-vulkan-vs-torch.md`](benchmarks/2026-10-06-vulkan-vs-torch.md) | 第二轮复测 |
| `release-notes/v1.5.0.md` … `v1.8.1.md` | 版本变更说明 |
| `release-notes/v1.5.0.md` … `v1.8.1.md` | 版本变更说明(**大版本发布要求**见 [`todo/25-release-requirements.md`](todo/25-release-requirements.md)) |

## 3. 2026-10-11 文档整理记录

Expand Down
Loading
Loading