FP16保存不是简单调用model.half()后直接保存,而是需在GPU上将state_dict转为torch.float16类型后保存,加载时须显式匹配dtype并验证输出一致性,否则易致精度漂移或报错。

FP16保存 ≠ 简单调用 .half()
直接对模型调用 model.half() 再保存,会导致权重在 CPU 上被强制转为 FP16 后截断——这不是无损转换,而是有损量化。FP32 到 FP16 的“无损”仅在数学意义上成立(即不引入额外量化误差),但前提是:原始 FP32 权重必须能被精确表示为 FP16。而现实中,很多 FP32 数值(如 3.1415927)在 FP16 下无法精确表示,会四舍五入丢失精度。所谓“无损”,实际指**不额外引入缩放、伪量化或 clip 操作**,只做 IEEE 标准的类型转换。
正确做法:用 torch.float16 保存,但保持计算图和加载逻辑一致
PyTorch 支持原生保存 FP16 权重,关键不是“转换模型”,而是“以 FP16 类型保存 state_dict”。你需要:
- 确保模型已在 GPU 上(FP16 转换在 CUDA 上更可靠,CPU 上部分值会因舍入规则不同而偏差更大)
- 用
model.half()或model.to(torch.float16)将权重转为torch.float16类型(注意:这步只是 dtype 变更,不改变数值本身) - 调用
torch.save(model.state_dict(), "fp16_weights.pt")—— 此时保存的是真正的 FP16 tensor,而非转成 FP32 再存 - 加载时需显式指定
map_location并重建模型结构,不能依赖torch.load(..., map_location="cuda")后直接load_state_dict,否则可能因默认 dtype 不匹配报错
加载 FP16 权重时最常踩的坑
错误现象:RuntimeError: expected dtype float32 but got dtype float16 或数值全为 nan / inf。
- 模型定义必须先完成(不能靠
torch.load反序列化模型类),且模型参数初始化后要立刻.to(torch.float16),再load_state_dict - 不要用
torch.load(..., map_location="cuda")直接加载到模型上——它默认把所有 tensor 映射为torch.float32;应先加载为 CPU 上的 dict,再逐层检查 key 对应 tensor 的dtype - 如果模型里混用了
nn.BatchNorm2d等含 running_mean / running_var 的模块,它们默认是 FP32,加载 FP16 权重时会出错;要么统一转为 FP16,要么单独跳过这些 buffer(但会影响推理一致性) - 验证是否真为 FP16:加载后执行
next(model.parameters()).dtype,输出应为torch.float16
FP16 保存后体积减半,但不等于能直接用于 FP16 推理
保存为 FP16 只解决了存储问题。真正运行时是否启用 FP16 推理,取决于你是否开启 AMP(torch.cuda.amp.autocast)或手动 cast 输入/模型。单纯加载 FP16 权重,若输入仍是 FP32,PyTorch 会自动 upcast,反而可能引入隐式类型转换开销。
立即学习“Python免费学习笔记(深入)”;
- 若目标是部署轻量模型,建议配合
torch.jit.trace或torch.export.export(PyTorch 2.0+)导出,它们能固化 dtype 行为 - FP16 权重在 A100/H100 上可加速,但在 T4 或旧卡上可能反而变慢,因为缺乏原生 Tensor Core 支持
- 某些算子(如
torch.nn.functional.interpolate在某些 mode 下)对 FP16 输入不稳定,需实测验证输出一致性
真正容易被忽略的是:FP16 权重文件虽然体积小,但加载时若没对齐设备与 dtype,比 FP32 更容易静默失败——比如数值截断不报错,但推理结果漂移。务必在保存后立即用同一环境加载并比对 FP32 输出的 L2 差距(torch.norm(fp32_out - fp16_out)),阈值建议设为 1e-3 以内。


















