返回专辑
·Johan·6 分钟阅读

CE target long dtype 断言

PyTorch 广播让错误 shape 的 loss 能跑起来——silent bug 产生错误梯度;写 forward 时 assert shape 或用 torchdim 习惯。

CE target long dtype 断言

1. loss 在降,metric 不动——target 是 float

分割改分类时,CrossEntropyLoss 的 target 仍是 float one-hot 风格 (B,C,H,W),PyTorch 广播没报错,loss 正常下降——梯度却在错空间更新,val acc 随机。广播让 shape/dtype 错误 silent 跑通,是训练里最贵的 bug。tensorboard loss 好看而 acc 平,应第一时间 print logits.shape, targets.shape, targets.dtype。这类 bug 可跑数天才发现,因为 loss 确实在降。

2. CE 与 BCE 契约

python
assert logits.shape == (B, num_classes)
assert targets.shape == (B,)
assert targets.dtype == torch.long

loss = F.cross_entropy(logits, targets)

CrossEntropyLoss:logits (N,C),target (N,) long class index,不是 one-hot。BCEWithLogitsLoss:logits 与 target shape 必须一致,[N] vs [N,1] squeeze 不一致会广播成 (N,N) 灾难——BCE 路径更要显式 squeeze 或 assert。多 label 任务用 BCEWithLogitsLoss 时 target 是 float 0/1,别与 CE 混用同一 head。

3. layout:NCHW vs NHWC

(N,C,H,W)(N,H,W,C) 混用——permute 后忘记 contiguous()view。forward 入口 assert:

python
def forward(self, x):
    assert x.dim() == 4 and x.shape[1] == 3, f"expected NCHW, got {x.shape}"

从 TF 或 ONNX 迁模型时 layout 是头号回归点;CI 里固定小 tensor 验 contract。部署 C++ 预处理若 NHWC 而训练 NCHW,acc 直接随机。

4. einsum 与 reduction

python
attn = torch.einsum("bhd,bhe->bhde", q, k)
assert attn.shape == (B, H, D, E)

字母写错 silent 错 reduction——单元测试比事后 print 便宜。复杂 einsum 旁注释期望 shape,review 时核对字母与维度。attention mask broadcast 到 (B,H,L,L) 时少一维会 silent 错 mask 区域。

5. DDP 与 multi-GPU

各 rank batch shape 一致;gather/all_gather 前 assert。drop_last=True 避免末 batch BN 不稳。logits 最后一维 C 必须与 num_classes、dataset label map 同步——改 head 忘改 dataset 是常见 regression。label 从 0 开始且连续;新增类后 old checkpoint 加载会 size mismatch,应报错而不是 silent truncate。

6. 分割与 detection 的 shape

分割:logits (N,C,H,W) vs target (N,H,W) long。detection:anchor match 后 target 格式因框架而异(YOLO vs DETR),loss 入口 assert 比事后 gdb 便宜。multi-task 各 head output shape 写进 model card。mask RCNN 的 target 是 list of tensors,更要在 collate 后 assert len 与 batch 一致。

7. 调试习惯

python
def test_forward_shapes():
    x = torch.randn(2, 3, 224, 224)
    logits = model(x)
    assert logits.shape == (2, num_classes)

torch.jit.trace / torch.export 暴露 shape 假设。NaN 回溯:LR、loss scale、augment、num_classes 与 logits C 维。第一个 batch 训练前跑 test_forward_shapes,能挡掉一半低级错误。overfit 单 batch 实验也应 acc→100%,否则优先 shape/dtype 而非调 LR。

8. 失败模式

后果
CE target float / one-hot错梯度
BCE [N] vs [N,1]广播成 (N,N)
permute 未 contiguousview silently wrong

9. 验收

每个 model forward PR 带 shape unit test。val best → test 一次。DDP 各 rank 跑同一 test_forward。生产 export 前 assert deploy input shape(如 1×3×224×224)。

10. 案例:one-hot float 训三天

某二分类把 target 做成 (B,2) float one-hot 喂 CE,loss 从 0.69 降到 0.42,acc 一直在 50% 附近——改成 (B,) long 后一个 epoch acc 到 94%。教训:新 head 合并前强制 test_forward_shapes + 单 batch overfit。shape 错误时 loss 仍可「学习」,metric 才揭穿。

11. 与 torch.compile / export 的 shape

torch.compile 对 dynamic shape 敏感,compile 前仍应用 assert 固定 contract。export 时 trace 的 dummy shape 必须与 deploy 一致,否则 deploy 输入 size 变会 recompile 或 fail。multi-task head 各写 test_forward_shapes,CI 矩阵跑不同 input size smoke。

12. 与 DataLoader collate 的 shape

collate_fn 堆叠后 batch 维可能静默变 (B,1,H,W) 而 model 假定 (B,H,W),CE target 仍 long 但 spatial 维错位。detection collate 返回 tuple of tensors,loss 里要对每 field assert。video 输入 (B,T,C,H,W) 与 3D CNN 期望 (B,C,T,H,W) permute 错误时,loss 降而 val 随机——在 dataset __getitem__ 返回前 assert ndim。label smoothing 与 CE target long 并存时,target 仍是 index 而非 soft vector——别与 BCE soft label 混用。torchdim 或 typed shape 库在 large codebase 值得引入,小项目 at least forward assert 即可。Loss 里 reduction='none' 后手动 mean 时,shape 与 weight 广播再次 assert——二次 silent bug 高发区。ONNX export 前跑 shape test 可减少 deploy 才发现 layout 错的成本。

13. 与 mixed precision 的 shape

AMP autocast 下 logits 仍应是 (B,C) float;half input 不会改 target long dtype。GradScaler step 前 unscale 再 clip,clip 阈值与 shape 无关但 NaN 常伴 shape bug 出现——先 assert shape 再查 scale。

14. 案例:BCE 广播成 (N,N)

多标签 head logits (B,L) target (B,L,1) float,BCE 广播梯度错,micro-F1 永远低。改成 (B,L) squeeze 后一轮收敛。教训:multi-label PR 强制 assert logits.shape == targets.shape;## 15. 与 Dataloader 拼 batch

default_collate 对 list of dict 拼不齐时 silent 变 object array——自定义 collate 末尾 assert batch tensor shapes。Variable length 序列 padding 后 lengths 向量与 packed output 对齐 assert。Video (B,T,C,H,W) 与 3D CNN (B,C,T,H,W) 在 README 写清 layout,code review 见到 permute 必问 target layout。

单标签 CE 与多标签 BCE 分支分文件写 test,防 copy 错。Export 前 test_forward_shapes 用 min/max deploy resolution 各跑一遍;## 16. 训练 loop 入口 assert 清单

每个 epoch 第一个 batch:logits.shape[-1]==num_classes;CE 时 targets.dtype==long;spatial task 时 spatial dims 一致。DDP 下 rank0 打印一次,其余 rank silent。## 17. 与 EMA / 多 head 的 shape

EMA 权重 shadow 与 model 同 shape,load 错 checkpoint 会 silent partial load。Multi-head 输出 dict 时 loss 键名与 tensor shape 在 type hint 或 dataclass 里固定,避免字符串键拼错。TorchScript trace 对 Python dict 支持有限,export 前改 tuple 输出并 assert。JAX/torch 互转时 layout 与 dtype 双重 assert;bf16 logits 与 fp32 loss 混用时 target 仍 long index 不变。Unit test 覆盖 train 与 eval 两种 mode 的 forward shape,防 eval 改 head 未改 test。Collate stack 后 batch 维与 label 维对齐写进 dataset 文档,新人 onboarding 第一课。

18. 案例:permute 后 view 静默错

Video (B,T,C,H,W) 误 permute 为 (B,C,T,H,W) 未 contiguous,view 后 loss 仍降 val 随机——view 前 assert x.is_contiguous() 暴露。Fix 后单 batch overfit 100% 再 full train。3D/4D PR 必须附 shape 单测。CI 失败信息应打印 expected vs actual shape,而不是只报 RuntimeError stack。Shape 单测加入 pre-commit,改 forward 必跑,与 lint 同级。Multimodal batch 里 image tensor 与 text token shape 在 collate 后各 assert 一次,别假定「能对齐就能训」。Detection 里 anchor grid 与 label grid spatial size 不一致时,assign 阶段 assert 比 loss NaN 更早报警。Training script 模板在第一个 batch 自动 print shape summary,零成本 habit。Open source 贡献若改 forward signature,CI 要求更新 shape test,与 API breaking change 同级对待。Weekly oncall 若遇「loss 降 metric 不动」,runbook 第一步永远是 print shape/dtype。把常见 shape bug 收进团队 FAQ,比 repeated slack 答疑省时间。Shape 错了能训起来,才是最难查的那类 bug。

Tensor shape 是 forward 的 API——assert 比事后 grep print 便宜一个数量级。

← 全部文章

johan's blog