Skip to content

Release v1.8.2: f16 训练缺陷修复与提速

Choose a tag to compare

@EthanPeng-2048 EthanPeng-2048 released this 14 Oct 15:39
· 104 commits to main since this release

neuralnet.cpp v1.8.2 Release Notes

发布范围:v1.8.1(830091f,2026-10-12)→ v1.8.2。
主题:f16 训练缺陷修复(F1–F5,含"四字段全 f16 可训练") + f16 提速(编译期精度特化 / 批量派发 / 网格粒度变体 / F16C / f16 GEMM) + RAPT / CNN 的 f16 接线 + Layer::init 参数精度自检。
兼容性:无破坏性 API 变更——引擎接口与 virtual 计数(52)不变、get_*_spirv() 名称/签名不变、create_tensor 签名保留;仅新增可选字段/函数与一处 init 期 fail-fast 收紧(见 §4 与 §6)。逐条核验见 ../todo/24-f16-training-defects.md(缺陷台账)与 ../todo/26-f16-speedup-plan.md(提速方案)。

0. 亮点速览(TL;DR)

方向 变化 收益
f16 训练缺陷(F3) GPU stable=f16 的 loss 冻结/inf 根因 = CE 的 (1,1) loss 归约按 stable 落盘溢出(Σ ≈ -log(vocab)·N 超 65504 → inf → NaN) two 条 CE 链(稀疏 + 稠密)的 (1,1) 输出恒 Precision::F32;stable=f16 曲线恢复可观测、120 步无 NaN
f16 全字段(F5) 四字段全 f16 原不可训练(Adam 二阶矩 v≈(1-β2)g² 在 f16 下下溢为 0) 优化器状态缩放(进 m/v 的梯度放大 S=4096,2 的幂无损):更新量数学不变、m/v 回到 f16 正常区 → 全 f16 现可训练
危险组合告警(F1) optimizer=f16 无任何启动提示 nn::cli::dangerous_precision_warning 在进训练循环前打印一次警告(仅警告,不改退出码/行为)
f16 提速(P2-11/13) --f16 曾比 f32 慢 11–17%;主因 = 运行期精度分派的"三元税"(#x 在热循环里每次读写都判精度) 编译期精度特化(#s / #xxxx 真实签名变体)+ 网格粒度变体(#f/#ks/#kr × 精度签名):850M --f16 2005 vs f32 2038 ms(−1.6%)、40HX −3.2%,峰值显存 850M 606 vs 818 MiB(−26%)
优化器派发(P2-12) --f16 下 Adam 复合结构逐成员降级派发([ir-c][degrade] 70/步) #b 批量变体 + "scan 与运行期同源"修复 → 每步 0 降级、优化器派发 −94%、host 阶段 −34%
CPU f16(P2-8/S4) 纯软件位操作的 f16 转换拖慢 CPU --f16 __F16C__ 硬件转换快路径(imm8 固定 RNE、与 MXCSR 无关;常量求值/可移植构建仍走软件路径):CPU f16 3.6 s → 1.9 s(不再慢于 f32)
RAPT / CNN f16(P2-3) 两架构权重/梯度此前硬编码 Precision::F32,--f16 拿不到显存收益 RAPT 侧 27 处 dsl::compute* 补精度实参 + token_emb_/残差/rms_norm_ 输出按 param/compute;Conv2D w_/b_/grad 按 param;CnnConfig.precision 新增(默认全 F32 → 历史逐位不变)
精度自检(P1-6 R2) 层把权重硬编码 F32 时 --f16 静默失效(无告警、无日志) Layer::init 后校验"param 槽声明 ↔ 参数张量实际存储精度":当场抓到并修复 3 处静默降级(Conv2D w_/b_、RAPT token_emb_、ReLULinearAttention 未把 profile 转发自持的 4 个 Linear)

1. f16 训练缺陷修复(24 F1–F5)

  • F3 修 GPU stable=f16 loss 冻结:真因不是 loss 回读链,而是 CE 的 (1,1) loss 归约输出按 p_.stable 落盘溢出(compute_loss.hpp 的稠密与稀疏 fused_forward_sparse_ 两条链)。修法 = 该 (1,1) 归约输出恒 Precision::F32。实测(850M):seq256/b32 由 inf → 9.0056 → 8.6922;stable=f16 120 步无 NaN。回归用例见 f16_precision_test / ce_fusion_test。
  • F5 全 f16 可训练:不用训练循环 loss scaling,而是在优化器状态更新处缩放(Adam::optimizer_state_scale_)——进入 m/v 的梯度放大 S=4096(2 的幂 → f16 下无损),(1-β1)·S / (1-β2)·S² / eps·S 同步换算。为什么有效:Adam 更新是尺度不变的 lr·(S·m̂)/(√(S²·v̂)+S·eps) = lr·m̂/(√v̂+eps) ⇒ 更新量数学不变,但 m~g·S、v~g²·S² 都回到 f16 正常区。边界:|g| ≳ 0.0625 时 v 会溢出 f16 ⇒ 实践配 --max-norm 裁剪。
  • F1 危险精度组合启动期告警:新增 include/neuralnet.cpp/cli/cli_precision_warning.hpp(nn::cli::dangerous_precision_warning),text_train / mnist_train 在解析后、进训练循环前打印一次到 stderr;触发 = optimizer=f16(覆盖四字段全 f16)。仅警告:不退出、不加确认开关、退出码与训练行为不变。门禁 src/cli_precision_warning_test.cpp(单测组合矩阵 + 子进程捕获 stderr)。
  • F2 覆盖缺口:profile 组合矩阵补 {F16,F16,F16,F16} 行;stable=f16 / 全 f16 形态进 GPU 分支做数值断言;CE 溢出回归。端到端全 f16 训练仍归大版本发布清单(25 第 1 项,不进默认 ctest)。

2. f16 提速(26;台账 24 · F4)

验收锚 = 任意设备(含无 shaderFloat16 的 850M)--f16 step ≤ f32 且峰值显存更低。落地四件(另有一件 S3):

  • P2-11 编译期精度特化(#s = 全 f16 静态签名 / #xxxx = 扫描期真实观察到的签名):把"精度是运行期参数"(#x + PC prec)在热路径换回"精度是编译期常量",只需 16 位存储、不依赖 shaderFloat16。850M:--f16 2260 → 2055 ms/15 步(f32 2037,+10.6% → +0.9%)。
  • P2-13 网格粒度变体(#f/#ks/#kr 叠加到精度签名):每个静态签名变体各发一份;部分和缓冲恒 f32(末段按 sig 落盘)。40HX(b64)该 wgrad f16 16.0 vs f32 25.1 ms/块;端到端 40HX 1013 vs 1027 ms(−1.4%)、850M(b16)2005 vs 2038 ms(−1.6%)。
  • P2-12 优化器 f16 批量派发(#b):对每个 extras 为空的静态签名变体各发一份批量变体;残差根因 = scan 与运行期不同源(已修)。实测(40HX):[ir-c][degrade] 70 → 0 / 步、优化器派发 138.7 → 8.55 calls/步(−94%)、host 阶段 9.94 → 6.51 ms/步;端到端墙钟在 GPU-bound(忙率 95.5%)下不可测。
  • P2-8 / S4 CPU F16C 转换:detail::float_to_half_bits / half_bits_to_float 增 __F16C__ 快路径(_cvtss_sh/_cvtsh_ss,imm8 = _MM_FROUND_TO_NEAREST_INT → 与 MXCSR 无关、恒 RNE);常量求值与 NN_ENABLE_NATIVE=OFF 可移植构建仍走软件位操作。precision_test 增「软/硬逐位一致」段(23 条 f32→f16 + 15 条 f16→f32)。
  • S3 f16 GEMM(storage-only、f32 累加):-DNN_SHADER_F16=1 编第二份 SPIR-V;小 N(N ≤ 8 且 transA=0)走 matmul_q_gemv.comp。
  • 已评估-不做(均附实测/上界证据,见 26 §4):S0(step 内固定开销——前提被实测推翻)、S1(主导张量全 f16——实测净亏)、S2(f16x2 打包)、S5 / P2-10(native16 资格不再扩大——收益有界)。

3. RAPT / CNN 的 f16 接线(roadmap P2-3)

  • RAPT:27 处 dsl::compute* 补精度实参 p_.compute(此前全部落默认 F32);两个残差分支由硬编码 Precision::F32 改 p_.compute;token_emb_/grad_token_emb_ 改 p_.param;rms_norm_forward_/backward_ 的输出改 p_.compute(否则 dst/src 精度错配 → insert_rows 断言)。
  • Conv2D:w_/b_/grad_w_/grad_b_ 改 p_.param,4 处 dsl::compute* 补 p_.compute。
  • API:CnnConfig 增 precision 字段(默认 PrecisionProfile{} = 全 F32),build_cnn_model(_from_spec) 在 add 之前注入(新增可选形参)。
  • 门禁:新增 4 条 ctest —— cnn_test_f16 / cnn_test_f16_gpu / rapt_test_f16 / rapt_test_f16_gpu(断言 loss 有限且下降、梯度无 NaN;GPU 侧另验精度变体闭合世界与槽位)。

4. Layer::init 参数精度自检(P1-6 R2;行为收紧)

Layer::init(NVI)在 init_impl 之后统一执行 check_param_precision_()——层声明的 param 槽是存储精度时,逐个校验 parameters() 的张量实际存储精度与之相同(precision_bytes(p_.param) == 0 的非存储精度声明,如 T1_58,跳过)。

它防的是一类静默降级:层把权重/梯度硬编码 F32(或忘用 p_.param)→ --f16 下该层参数仍是 f32,训练照跑、数值不错,但"f16 省字节"在其上完全落空。当场抓到 3 处:Conv2D 的 w_/b_、RAPT 的 token_emb_、ReLULinearAttention 从未把 profile 转发给它自持的 4 个 Linear(RAPT 注意力投影权重在 --f16 下一直是 f32)——前两处在 P2-3 修掉,第三处由本自检报错后发现并修复。

迁移提示(写自定义层的人):若你的层声明了 PrecisionProfile.param 为某种存储精度(f16/f32),但把参数张量硬编码成另一种,init 现在会报错(此前静默通过)。修法 = 参数创建时用 p_.param。指南见 ../development/write-a-custom-layer.md。

5. 工程化与文档

  • ctest 47 条(34 个测试目标 + 13 个变体/附加):新增 cli_precision_warning_test 与 §3 的 4 条 f16 变体;rapt_test 补 GPU 可用性预检(CPU-only 构建下 f16_gpu 用例按退出码 77 = SKIP,不假红)。
  • 文档审计回正:bench/doc_align_audit.ps1 的 [A][B][C][D][E][F] 可行动项归零——修若干处文档↔代码不一致(把 BitNet 的打包脚本改为带路径的外部引用、把文档里被当作 CLI flag 的 f32 档写回"默认、无 flag"、把 GQA 示意图中被误判成 CLI 参数的长箭头改掉),并给审计脚本补两处合法白名单(ternary_deploy.hpp/ternary_deploy_test 等已删文件/测试的历史提及;cmake/ctest 自带选项)。
  • 现状画像刷新(18-roadmap.md §2 / §8.1、12):include/ 74 hpp / 41247 行、src/ 75 cpp / 26614 行、shaders/ 22 comp、docs/ 43 md;compute_vk_backend.hpp 经 v1.8.1 拆分后 1055 行;引擎 52 virtual / Layer 直调 27 / 宿主桥 41。

6. 兼容性说明

  • 无破坏性 API 变更:引擎 virtual 计数 52 不变;get_*_spirv() 名称/签名不变;create_tensor 签名保留;Layer/Loss/Optimizer/Model 公共入口无删除。
  • 新增(可选):nn::cli::dangerous_precision_warning;CnnConfig::precision(默认全 F32);build_cnn_model_from_spec(engine, spec, precision = PrecisionProfile{}) 可选形参;profile_all_f16()。
  • 行为收紧(1 处):Layer::init 新增参数精度自检(§4)——库内层全部自洽;自定义层若声明 param 为存储精度却把参数硬编码为另一精度,会在 init 报错(此前静默降级)。
  • 数值:默认(f32)路径与 v1.8.1 逐位一致(param/compute 默认 F32 不变);--f16 的数值路径随新增特化/网格变体 shader 改变(同语义、不同 kernel),如出现旧 --f16 字节锚差异属预期。

7. 验收基线

项 结果
构建(Release,GPU 启用,build-gpu) 全绿、零告警(-Werror)
本机 ctest(Release + Vulkan 设备可用) 47/47 通过(0 skip)
bench/doc_align_audit.ps1 [A][B][D][E][F] 可行动 0
bench/doc_inventory.ps1 virtual methods 52/52、L2-VIOLATIONS: 0、宿主桥 41(仅披露)、HANDWRITTEN-RESIDUE: 0、exit 0
跨设备 --f16 ≤ f32(26 §5) 850M −1.6%、40HX −3.2%、CPU 不再慢于 f32

8. 提交索引

主题 提交
f16 缺陷定位与台账 24 886a1f3 / aab2c81
f16 提速方案(26)+ P2-9/P2-10 立项 6c0d591
S1s/S1t 编译期精度特化 2e200e8
P2-13 网格粒度变体 1377900
P2-12 优化器批量派发(+ 收口) b75f2b9 / 5bb6a51 / b8465f9
P2-3 RAPT/CNN f16 接线 0799a49
P2-8/S4 CPU F16C 快路径 399eb5e
F5 全 f16 可训练 50cf907
P1-6 R2 参数精度自检 ea15796
S0/S1/S2/S5 评估-不做 e5fa160 / f5d0084 / 6f93587
合并(PR #19) dfffbc5
Release v1.8.2(版本号 + 本文件 + 索引同步) 见本仓 git log

完整变更:git log v1.8.1..v1.8.2 / git diff v1.8.1..v1.8.2

9. 仍 Open(下一批)

  • roadmap P4(2026-10-14 立项):性能利用率 / 算子性能 / 显存开销的持续提升 + API 易用性与 Fold 表达式自动化扫描(注意力 fold 的 5 组合尚由 scan_exprs.cpp 手写登记块覆盖)。
  • P0-5:推理 top-k / top-p 采样 + 多轮对话闭环。
  • P1-1 / P1-2 / P1-5:text_train 模块化、checkpoint 优化器状态(Adam m/v 入 .nnckpt)、CI 冒烟扩 MSVC Debug + 权重变化断言。

完整台账见 ../todo/18-roadmap.md。