Repository navigation
Release v1.8.2: f16 训练缺陷修复与提速
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=f16loss 冻结:真因不是 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=f16120 步无 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+ PCprec)在热路径换回"精度是编译期常量",只需 16 位存储、不依赖shaderFloat16。850M:--f162260 → 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。