9.1 仿射层、激活与 Logits:深度来自函数复合,不来自“神经元”这个名字
线性分类器完全能接收
复杂函数可以拆成一连串局部运算。神经网络用可学习的仿射变换和非线性函数构造表示,再由同一个损失端到端调整参数。圆圈画得再像神经元,也不改变这套计算。
本课目标
- 用张量形状写清 affine layer 与 batch 计算;
- 解释为什么连续线性层会退化为一个线性层;
- 用 XOR 看隐藏表示,而不是背“多一层就行”;
- 区分 logits、probabilities、predictions 和 loss;
- 理解 MLP 的容量、归纳偏置与适用边界。
1. 一个 Layer 是批量仿射变换
对单个输入
其中
对 batch
PyTorch 的 nn.Linear(in_features, out_features) 正是 weight.shape 是 [out_features, in_features],不是反过来。参数数:
下面是可直接运行的 Python 3 / PyTorch 例子,用断言核对 batch 与参数形状,通过时无输出:
import torch
from torch import nn
layer = nn.Linear(in_features=784, out_features=128)
assert layer.weight.shape == (128, 784)
assert layer.bias.shape == (128,)
x = torch.randn(32, 784)
z = layer(x)
assert z.shape == (32, 128)广播让同一个 bias 加到 batch 每一行。理解 shape 比把每个标量画成圆圈更重要:卷积、attention 和并行训练最终都依赖张量维度契约。
2. 只堆线性层不会增加表达能力
两个仿射层:
可合并为:
无论堆多少层,只要中间没有非线性,整体仍是一个 affine map。深度只增加了参数化方式,没有让决策边界变成非线性。
窄隐藏层还可能限制合并矩阵的秩。因此分解形式会改变优化与容量约束,只是不会把整体变成非线性。
激活函数
3. XOR 展示的是表示变换
Binary XOR:
(0, 0) -> 0
(0, 1) -> 1
(1, 0) -> 1
(1, 1) -> 0原输入空间不存在一条直线分开两类。令
则在四个 binary 点上:
恰为 XOR 的 0/1 输出。网络不是神秘地“学会相等与不等”,而是把输入映射到一个可由输出层组合的 piecewise-linear 表示。训练是否真的找到这个解,仍取决于数据、初始化、loss 和优化。
4. 常见激活函数在做什么
ReLU
正半轴导数 1、计算简单,使 MLP 成为分段线性函数。负半轴梯度 0 可能使某些单元长期不激活;但“正半轴导数为 1”不能保证深层梯度不消失,因为梯度还要连续乘权重矩阵/Jacobian。
Leaky ReLU / PReLU
负半轴保留小斜率,降低永久零梯度风险,但不自动解决坏初始化、学习率或激活分布问题。
Sigmoid / Tanh
Sigmoid 把值映射到
GELU / SiLU
平滑、非单纯 hard gate,常见于现代架构。哪个更好依赖模型、初始化、归一化和算力;“Transformer 标准激活”不是跨所有实现的固定事实。
不可导点(如 ReLU 的 0)由框架选择一个 subgradient/convention,几乎处处可导已足以使用 gradient-based optimization。
5. MLP 输出的是 Logits
十分类模型最后一层通常输出
这些 logits 不是概率。Softmax:
把它们归一化为互斥类别概率。训练时不要手工 softmax 后再交给 PyTorch CrossEntropyLoss;该 loss 直接接收 logits,并用数值稳定方式组合 log-softmax 与 negative log-likelihood。
下例接续上面的 torch、nn 导入,用随机输入和标签检查 loss 接口,不代表已训练出有效分类器:
class MLP(nn.Module):
def __init__(self, input_dim=784, hidden_dim=128, classes=10):
super().__init__()
self.network = nn.Sequential(
nn.Linear(input_dim, hidden_dim),
nn.ReLU(),
nn.Linear(hidden_dim, classes),
)
def forward(self, x):
return self.network(x) # logits, shape [batch, classes]
model = MLP()
x = torch.randn(32, 784)
target = torch.randint(0, 10, (32,), dtype=torch.long)
logits = model(x)
loss = nn.CrossEntropyLoss()(logits, target)
probability = logits.softmax(dim=1) # 用于解释/评估,不送回同一个 loss
prediction = logits.argmax(dim=1)CrossEntropyLoss 也支持 soft/probability targets 等模式,shape 和 dtype 规则不同。先明确标签语义,不要靠广播“恰好能跑”。
6. Binary、Multiclass 与 Multilabel 不要混用
Single-label binary
可输出一个 logit [B] 或 [B,1],配 BCEWithLogitsLoss 与 float targets 0/1。它把 sigmoid 与 BCE 合并以改善数值稳定。
Single-label multiclass
输出 [B,K] logits,配 CrossEntropyLoss 与 long class indices [B]。各类互斥。
Multilabel
输出 [B,K] separate logits,配 BCEWithLogitsLoss 与 [B,K] multi-hot/soft targets。每类分别 sigmoid,不使用互斥 softmax。
逐类 sigmoid 不意味着标签在统计上独立,它们仍可共享隐藏表示。BCE 的 target 必须与 logits 形状完全一致,不能把 [B] 和 [B,1] 混用。
pos_weight、class weights 会改变训练 objective,不保证原总体概率仍校准;第 8 章的 calibration/threshold 原则仍适用。
7. 宽度、深度与 Piecewise-linear Regions
ReLU MLP 通过多层分段线性映射形成复杂区域。宽度增加每层可用 features,深度复用前层表示,某些函数能以更少单元表达。
Universal approximation theorem 只说明在条件满足、容量足够时存在近似,不保证:
- 有限数据能识别目标函数;
- SGD 找得到那个参数;
- 所需网络/样本/计算量可接受;
- 分布外仍正确;
- 预测可校准或因果。
表达能力、可优化性和泛化是三个不同问题。
8. MLP 没有自动理解结构
把
架构是一种 inductive bias:
- CNN 偏向局部连接与权重共享;
- RNN/state-space 偏向序列递推;
- attention 允许内容相关的跨位置交互;
- graph network 偏向节点与边关系。
不能因为 MLP 理论上能拟合就忽略数据结构。下一章会把这些 bias 展开。
9. 输入、输出与单位边界
神经网络仍需要数据契约:
- 数值输入尺度会影响初始化和优化;
- 类别需要 embedding/编码,ID 数字大小通常没有顺序意义;
- 缺失值要显式表示;
- target transform 与 inverse transform 要一致;
- padding/mask 不能误当真实 token;
- 训练与部署 normalization statistics 要冻结。
网络并不会自动修复泄漏、错误标签或时间穿越。
常见误区
- 线性模型不能处理 784 像素:能处理;限制是边界与 inductive bias,不是输入数本身。
- 多加一层就能学会 XOR:存在解不等于优化一定找到。
- ReLU 解决了梯度消失:它缓解某些饱和问题,Jacobian 链仍可能消失/爆炸。
- Softmax 要写进模型再用 CrossEntropyLoss:训练时传 raw logits。
- 神经网络会自动发现局部结构:架构与数据增强决定许多先验。
练习
- 写出
Linear(12, 5)的 weight/bias/output shape 与参数数。 - 用两个 ReLU hidden units 手算四个 XOR 点。
- 分别为 binary、multiclass、multilabel 写出 logits/target shape 和 loss。
- 删除 MLP 中间 ReLU,代数合并两层并验证输出等价。
- 比较 flatten MLP 与具有局部权重共享的模型需要哪些先验。
小结
神经网络是可微函数的组合。Affine layer 变换坐标,激活函数引入非线性,最后一层产生与任务 loss 匹配的 logits。隐藏表示扩大表达能力,但不保证可训练、泛化或理解数据结构。
下一课沿计算图反向走一遍:backpropagation 不是模糊的“分摊责任”,而是 reverse-mode automatic differentiation 对链式法则的高效复用。