torch.nn.init不是装饰性操作,而是训练能否启动的开关;权重初始化不当会导致梯度消失或爆炸,使模型彻底卡死而非变慢,其方差必须严格匹配激活函数特性——如ReLU需kaiming_normal_(std=√(2/fan_in)),Sigmoid/tanh则适用xavier_uniform_(std=√(2/(fan_in+fan_out)))。

torch.nn.init 不是装饰性操作,而是训练能否启动的开关。权重初始化不当,模型根本不会动——不是慢,是彻底卡死。
快速生成专业的 Python 脚本和应用代码。一键创建完整项目结构,支持CLI、API、爬虫、Bot、Django等多种项目类型,包含完整的项目结构、配置文件、依赖管理、测试、README和文档。
梯度消失/爆炸直接由初始化方差决定
你看到nan 或 inf,或者 grad_norm=0.0002 长时间不变,大概率是某层 weight 的标准差偏离了激活函数的“适配区间”。
比如用 ReLU 却用了 xavier_uniform_,前向输出一半被砍成 0,反向梯度在前几层就衰减到机器精度以下;
反过来,用 Sigmoid 却用了 kaiming_normal_,输入过大导致激活饱和,梯度趋近于 0。
- xavier_uniform_ 和 xavier_normal_ 假设激活函数对称(如 tanh、sigmoid),要求输入输出方差一致
- kaiming_normal_(也叫 he_normal_)专为 ReLU 及其变体设计,考虑了负半轴截断,把初始方差放大了约 2 倍
- 如果你手动写 nn.Linear(768, 3072),没调 init,PyTorch 默认用 kaiming_uniform_(带 gain=1),但 gain 值要按激活函数重算
全零或常数初始化会让模型彻底失效
weight = torch.zeros(...) 看似安全,实则灾难:
- 同一层所有神经元接收相同输入、产生相同输出、计算相同梯度 → 永远更新成一样 → 对称性无法打破
- bias 设为 0 没问题,但 weight 绝不能全零
- 即使是 torch.randn(...)*0.01 这种“朴素随机”,也没考虑输入维度和激活函数,方差随 in_features 增大而失控
PyTorch 中 init 函数必须显式调用且脱离 autograd
torch.nn.init 系列函数(如 kaiming_normal_)内部自动进入 torch.no_grad(),但它们**不会自动绑定到模块上**。
常见错误:
- 在 __init__ 里写了 self.linear = nn.Linear(10, 5),却忘了加 torch.nn.init.kaiming_normal_(self.linear.weight)
- 在训练循环里反复调用 init 函数,覆盖已更新的权重
- 对 weight 初始化了,但漏掉 bias(尤其当需要偏置时,bias 设为 0 是合理默认,但必须显式赋值)
正确做法是在 __init__ 末尾集中初始化:
self.linear = nn.Linear(10, 5) torch.nn.init.kaiming_normal_(self.linear.weight, nonlinearity='relu') self.linear.bias.data.zero_()
深层网络里初始化误差会指数级放大
信号从第 1 层传到第 L 层,方差变化是乘性累积。假设每层权重方差为 σ²,输入方差为 1,则第 L 层输出方差 ≈ σ²ᴸ × 输入方差。 - σ² = 0.5 → 第 10 层方差 ≈ 0.001,信号几乎消失 - σ² = 2.0 → 第 10 层方差 ≈ 1024,梯度爆炸风险陡增 Xavier 和 Kaiming 的公式本质就是解这个乘积约束:让 σ² = 1 / fan_in(Xavier)或 2 / fan_in(Kaiming),抵消层数影响。 别指望靠学习率补偿——lr=1e-6 救不了 grad=1e-12,lr=10 也压不住 grad=1e6。
真正容易被忽略的是:同一模型里不同层可能需要不同初始化。比如 Transformer 的 qkv 投影常用 orthogonal_,而 FFN 层用 kaiming<em>normal</em>;RNN 的隐藏到隐藏矩阵适合 orthogonal_,而输入到隐藏用 kaiming<em>uniform</em>。没有银弹,只有匹配信号流的显式选择。

















