本页章节
ACTUAL TRAINING / DETERMINISTIC REPLAY
真实训练故障:保存现场,再验证修复
在同一个小型 decoder 的训练程序中注入指数溢出、过大学习率和错误标签。按执行顺序记录实际张量,用同一份更新前快照重跑,检查修复是否恢复基线。
更新前保存什么,才能比较同一现场?
实验导入与学习实验相同的 learning_core.py:固定词表的反转任务、两层 decoder、LayerNorm、dropout 和已对齐的监督标签。先执行八次正常 AdamW 更新,再保存第九步开始前的状态;这时优化器已有一阶和二阶矩,恢复随机种子本身不足以恢复训练现场。
快照包含 model 参数、optimizer 完整状态、Python/NumPy/CPU torch RNG、数据流的专用 RNG、完整排列、cursor/epoch、计数、模型与优化器配置、模块 train/eval 状态。失败 batch 的输入、mask、标签与 ID 另外保存。每个基线、故障和修复分支都在新 Python 进程中加载同一个文件,先断言加载状态的内容哈希一致,再执行对应步骤。
保存完整快照
已完成更新 8;待执行步骤 9。保留动量、随机状态和数据位置。
只注入一项
逐张量实测,故障 loss 使用本次实际 logits 与标签。
固定现场回放
基线、故障、修复加载相同快照;下一次前向也实际执行。
比较修复与对照
核验完整张量和最终状态相同,再测独立正常 batch。
首个异常或分歧,实际出现在哪里?
切换故障和回放分支,逐事件查看该次保存运行。精简快照保留关键层边界与损失操作;序号沿用完整 trace 的真实执行索引,跳号表示省略了中间记录。
同一现场的三种回放
保存的真实 CPU 测量 · 单位见各表头正在载入真实运行快照。
| 原始序号 | 阶段/张量 | 有限个数/总数 | RMS | 最大绝对值 | 与基线最大差 |
|---|
分层激活与实际参数梯度
RMS 和 L2 以 float64 累加本次 float32 值,避免统计器自身平方溢出。非有限张量不填造数值,显示 NaN/±∞ 个数;激活仅覆盖保存的模块边界。
| 模块输出 | shape | RMS | max |x| | 有限性 |
|---|
| 参数 | 梯度 L2 | 位移 L2 | 相对位移 | 参数有限性 |
|---|
样本、mask 与正确/实际标签
labels 已由 core 移位一次;−100 忽略,SEP 位置开始预测答案,EOS 也受监督。错误标签只轮换内容符号 ID,EOS 与监督 mask 保持不变。
| 记录 ID/位置 | 输入 token | 有效 mask | 正确标签 | 实际标签 |
|---|
损失重算、首个梯度分歧与回放证明
文件只在本地浏览器读取;结构校验检查字段和数值一致性,不能认证外部文件的来源。完整 tensor、optimizer 与 RNG 保存在本地二进制工件中。
相同 softmax 目标,为什么 naive exp 先溢出?
本案例在指定 batch 的输出头后加入共同的 +128 刺激。模型输出和加法结果仍有限;直接 exp 会产生 +∞,求和仍为 +∞,随后的 ∞/∞ 得到 NaN。日志单独记录 loss.shifted_logits、loss.exp、分母、概率、log 概率和目标选择,首个异常取自真实有限性统计。
修复和稳定参考使用 log_softmax 的稳定表达式,从相同的平移后 logits 求目标 NLL。实际 loss 从保存 logits 与 labels 独立重算,并与共享 core 的未平移 CE 比较,容差公开记录。共同的安全规则在 loss/梯度非有限时跳过更新,所以故障的下一次前向可以仍有限;跳步不会恢复基线本来应执行的更新。
过大的 LR 可以让所有张量有限,但轨迹已经偏离
第二个案例保持 logits、loss 和梯度不变,只在 optimizer.step() 前把 AdamW LR 从 0.003 改为 0.6。报告测量真实的参数位移,随后用更新后的模型和实际下一 batch 执行前向。当前保存结果出现大幅有限更新和下一前向分歧,日志按观察结果显示有限性。
修复在相同更新前快照加载完成后恢复约定 LR;比较 loss、梯度、更新后参数、下一前向和完整 optimizer/RNG/cursor 状态。仅把幅度裁小不能代替该检查,因为裁剪会改变更新方向或大小。本例不把参数变大直接称为 NaN,也不从单步现象推断长期收敛。
标签合法、loss 可能更小,目标仍然错误
第三个案例在有效答案位置执行 A→B→C→D→A 的符号 ID 轮换。输入和 teacher-forced 历史保持原记录,EOS 不改,−100 位置不改,监督 token 数完全相同。轮换后的标签都在合法词表范围;所有激活、loss 和梯度可以保持有限。
同一 logits 分别计算实际错误标签的 loss 与正确标签 loss,量化两者差值及全参数梯度差的 L2、相对 L2 和最大元素差。错误目标的 loss 可能更小,这不能说明原任务变好;完整标签对照和反转规则提供语义证据。修复必须恢复正确 labels,并从相同更新前状态重跑,不能在已经被错误目标更新过的参数上声称恢复了原轨迹。
hooks 与 anomaly detection 能定位到什么范围?
“首个”依赖监控集合
模块输出 hooks 在模块前向完成后执行,包含各 block 返回元组的 hidden/KV;损失中的关键操作另行显式记录。SDPA、GELU 内部所有 kernel 张量未被逐一记录,因此结论是监控集合中的首个异常。
反向顺序来自实际回调
Tensor gradient hooks 在梯度计算时记录。参数 hook 的顺序来自 autograd,本页不按前向层名反转来伪造反向顺序;同一输出的别名只挂一次梯度 hook。
anomaly probe 是单独诊断回放
PyTorch 2.9 的 detect_anomaly(check_nan=True) 可对失败反向给出关联前向 traceback,并在反向 NaN 时抛错。指数案例同时保留普通 trace 和定向 probe,后者可提前中止,因此不能替代完整传播记录。
高 LR probe 的具体边界
本页 high-LR probe 只重跑更新前前向和反向,没有执行 high-LR optimizer 更新或下一前向。它没有抛错,只说明这段反向有限;高 LR 后果由主实验的真实更新和下一前向测量。
标签 probe 的有限反向不会验证监督语义。诊断 hooks 有额外开销,本页不报告性能;逐位恢复一致性只在记录的软件和 CPU 环境内核验。导入文件的字符串安全显示,不执行 HTML,也不由浏览器读取或反序列化 .pt。
运行三个实际故障与新进程回放
# 沿用 PyTorch 2.9.0 / NumPy 2.3.4,创建新的工件目录
node scripts/run-failure-lab.mjs
# Python 核验实际张量、恢复、异常和控制
.venv-framework/bin/python scripts/test-failure-replay.py
# 页面报告结构、数字、导入竞态和交互测试
node --test scripts/test-failure-replay.mjs
# 指定新目录;已存在目录会被拒绝
node scripts/run-failure-lab.mjs --out-dir artifacts/failure-lab-my-run
每次输出包含 pre-step.pt、checkpoint-manifest.json、18 组 trace.json / tensors.pt / final-state.pt、各案例的 anomaly-probe.json、完整 report.json、精简 snapshot.json、环境和已读取版本文档。两种 JSON 均可本地导入,文件上限 12 MiB;默认快照的报告路径与 SHA-256 展示在面板和证明中。
所有曲线与测量来自实际保存运行。该模型只用于观察故障与恢复,不对语言模型质量、GPU 吞吐或跨机器逐位复现作结论。机制依据为本机固定版 PyTorch 的 log_softmax/CE、hooks、AdamW、RNG 与 anomaly detection 文档,读取范围记录在报告 references 与实施说明中。
适用范围与一手资料
与学习实验共用小模型和任务,从相同 checkpoint、样本与随机状态回放单一故障。区分数值非有限、更新幅度异常和有限但错误的监督;修复以同一目标和正常对照核验,定向诊断不承担普遍根因判定。
公式与机制的原始依据 · 3 项
- PyTorch 2.9 · torch.autograd.detect_anomaly本机 PyTorch 2.9.0 的完整 docstring 于 2026-10-28 读取:前向 traceback、反向 NaN 检查与调试开销;标签语义另验
- PyTorch 2.9 · Autograd mechanics本项目框架对照固定 PyTorch 2.9.0;冻结、计算图与 no-grad 的语义,2026-10-27 核对
- PyTorch · Saving and Loading Models模型 state 与恢复训练所需 optimizer state