nn.Linear 只做一件事:可学习的仿射变换。但 weight 的 shape 约定、默认初始化的来历、封装与函数式的边界都藏在这件小事里——MLP、attention 投影、分类头全部由它拼装。
在 torch.nn 的常用层里,Linear 是最基础的参数化层:往下它只是矩阵乘法加偏置,往上一切可学习结构都以它为积木。
数学定义与 shape 约定
一切从这条式子出发:
其中 是形状 的输入, 是形状 的权重, 是形状 的偏置。注意那个转置:PyTorch 把输出维排在前,与数学惯用的 相反,这正是维度写反事故的直接源头。
变换只作用于最后一维,前置的 batch 维(含序列维)原样保留:
import torch
import torch.nn as nn
fc = nn.Linear(in_features=4, out_features=2)
fc.weight.shape # torch.Size([2, 4]),输出维在前
fc.bias.shape # torch.Size([2])
fc(torch.randn(8, 16, 4)).shape # torch.Size([8, 16, 2]),(N, L, D) 序列直接进默认初始化与显式重初始化
shape 定了,下一个问题是:训练开始前 里装的是什么。
构造时 weight 与 bias 均按 Kaiming uniform()采样,折算后近似 ,方差量级与 Xavier 初始化 同阶,对普通前馈层够用。 也不是随意取值:它让 leaky_relu 的增益折算恰好落回 ,其中 即 in_features。但残差分支、attention 投影这类深层串联又对方差敏感的结构,默认分布会让信号方差逐层漂移,需要用 nn.init.*_ 配合 apply 显式重初始化。
bias 与 F.linear:封装的边界
初始化之外,另一组偏离发生在封装本身:什么时候不要 bias,什么时候干脆不要 Module。
bias=False 有两类典型场景。后接 BatchNorm 时 bias 纯冗余——BN 本身要减均值,前一层偏置的贡献会被原样减掉。weight tying 时,输入侧 Embedding 与输出侧投影共享同一矩阵,后者不应再挂独立偏置。
绕开封装则是 F.linear(x, w) 的领地:不建 Module、不注册参数,权重完全手动管理。分工原则很清晰——需要参数被 torch.optim 追踪、随 .to() 迁移、进 state_dict,就用 nn.Linear(多层堆叠交给 nn.Sequential 与 ModuleList);只是借别处的权重做一次投影,比如手写 attention 的 Q/K/V 变换,用 F.linear。
常见坑
落到实操,两条高频错误都指向开头的 shape 约定。
维度写反:nn.Linear(10, 5) 之后误接 nn.Linear(10, ...),报 mat1 and mat2 shapes cannot be multiplied。记法只有一条:上一层的 out_features 就是下一层的 in_features。
忘记 flatten:Conv2d 输出 ,Linear 只看最后一维——若 in_features 恰好等于 ,还不报错,静默算出错误语义。进全连接层前 x = x.flatten(1),把通道与空间维并成特征维。
宽度选择、激活搭配、收敛异常这些训练期经验收在 MLP训练经验;至此,从定义、初始化到排错的链路闭合。