📑 本页目录(点开跳转)
07 · 从线性到神经网络
⏱ 28 分钟 | ⭐ 核心 | 🔨 有代码
🎯 一句话
神经网络 = 线性层 + 非线性激活,堆起来。 关键在「非线性激活」这四个字——没有它,堆一百层也还是一条直线。
💥 一、先看线性模型的死穴:异或问题
XOR(异或):两个输入不同时输出 1
x2
1 │ ●(0,1)=1 ○(1,1)=0
│
0 │ ○(0,0)=0 ●(1,0)=1
└──────────────────────► x1
0 1
⚠️ 你画不出一条直线,把 ● 和 ○ 分开。
🔨 实测(三行代码,结论惊人)
import numpy as np
from sklearn.linear_model import LogisticRegression
from sklearn.neural_network import MLPClassifier
X = np.array([[0,0],[0,1],[1,0],[1,1]], dtype=float)
y = np.array([0, 1, 1, 0], dtype=float) # XOR
print("逻辑回归(线性) ", LogisticRegression().fit(X,y).score(X,y))
print("MLP(隐层4,tanh) ", MLPClassifier(hidden_layer_sizes=(4,), activation="tanh",
max_iter=5000, random_state=1).fit(X,y).score(X,y))
print("MLP但激活=identity", MLPClassifier(hidden_layer_sizes=(4,), activation="identity",
max_iter=5000, random_state=1).fit(X,y).score(X,y))
逻辑回归(线性) 50% ← 永远做不对
MLP(隐层4,tanh) 100% ← 加了隐层 + 非线性,解决了
MLP但激活=identity 50% ← ⭐ 有隐层但没非线性 → 照样不行!
🔑 第三行是本章的灵魂: 有隐层不等于有能力。真正带来能力的是「非线性激活」。 因为线性套线性还是线性:
W₂(W₁x) = (W₂W₁)x,等价于一个单层线性模型。
🧱 二、神经网络的结构
- ① 线性:z = Σwᵢxᵢ + b —— 就是第 3 章的线性模型
- ② 非线性:a = 激活函数(z) —— 关键!
一层的完整计算(记住这个式子,整个深度学习都是它的堆叠):
$$\mathbf{a}^{(l)} = f\left(W^{(l)}\mathbf{a}^{(l-1)} + \mathbf{b}^{(l)}\right)$$
💡 人话:上一层的输出 → 乘个矩阵加个偏置 → 过一个非线性函数 → 交给下一层。
🔗 连接:这就是全景导论第 2 章里 Transformer 那个 FFN 的结构。 Transformer 只是在这基础上加了注意力机制。
👁️ 神经网络到底在做什么(一个几何直觉)
每一层做两件事:
① 线性变换 Wx+b → 【旋转 + 拉伸 + 平移】空间
② 非线性激活 → 【折叠 / 弯曲】空间
⭐ 多层堆叠 = 反复"拉伸 + 折叠",
把原本【纠缠在一起】的两类数据,掰成【一条直线就能分开】的形状
💡 换个说法:
最后一层永远是个线性分类器(就是第 3 章的逻辑回归)。 前面所有层的工作,都是把数据变换成"线性可分"的样子。
⭐ 所以"深度学习"本质上是"自动的特征工程" —— 你在第 16 章手工造的那些交叉特征, 神经网络在用梯度下降自己造。
回头看异或:
原始空间: 变换后(隐层空间):
● ○ ●
○ ● → ● ← 折叠之后,一条直线就能分开 ✅
○ ○
⚡ 三、激活函数(选 ReLU 就对了)
| 函数 | 公式 | 特点 |
|---|---|---|
| Sigmoid | $\frac{1}{1+e^{-z}}$ | 压到 (0,1)。⚠️ 两端梯度趋近 0 → 梯度消失。现在只用在输出层做二分类 |
| Tanh | $\frac{e^z-e^{-z}}{e^z+e^{-z}}$ | 压到 (-1,1),零中心。比 Sigmoid 好,但仍会梯度消失 |
| ReLU ⭐ | $\max(0, z)$ | 默认首选。计算极快、正区间梯度恒为 1 不消失 |
| Leaky ReLU | $\max(0.01z, z)$ | 解决 ReLU 的"神经元死亡"(负区间梯度为 0) |
| GELU / SwiGLU | 平滑版 ReLU | Transformer 时代的主流 |
Sigmoid ReLU
1 ┤ ╭──── ┤ ╱
0.5 ┤ ╱ ┤ ╱
0 ┤─╯ ┤────╱
└────── └──────
两端斜率≈0 💀 正区间斜率=1 ✅
梯度消失 梯度不消失
🔑 ReLU 为什么是深度学习的转折点: Sigmoid 两端梯度接近 0,反向传播时层层相乘会指数级衰减 → 深层网络根本训不动。 ReLU 正区间梯度恒为 1,梯度能一路传到底。这一个改动让"深"变成了可能。
算一下差距有多大:
Sigmoid 的导数最大值 = 0.25(在 z=0 处,两端更小)
10 层:0.25¹⁰ ≈ 0.00000095 ← 梯度只剩百万分之一 💀
30 层:0.25³⁰ ≈ 10⁻¹⁸ ← 完全归零
ReLU 正区间导数 = 1
10 层:1¹⁰ = 1 ← 原样传回 ✅
⚠️ ReLU 的代价:神经元死亡
ReLU 负区间的导数 = 0
→ 如果某个神经元的输入【一直是负的】
→ 它的梯度永远是 0
→ 权重永远不更新
→ 这个神经元【彻底死了】💀
常见诱因:学习率太大,一次大更新把权重推到"永远输出负数"的区域
三个解法:
| 解法 | 说明 |
|---|---|
| 降学习率 ⭐ | 最常见的根因就是 lr 太大 |
| Leaky ReLU / GELU | 负区间给一点点梯度,不会彻底死 |
| 合适的初始化 | He 初始化(PyTorch 的 kaiming_normal_)就是为 ReLU 设计的 |
💡 自检方法:训练后统计
(activation == 0).float().mean()。 如果某层超过 50% 的神经元恒为 0,多半有大量死亡神经元。
🔧 初始化:一个容易被跳过但很关键的细节
初始化太小 → 信号层层衰减 → 深层几乎收不到信息
初始化太大 → 信号层层放大 → 激活值爆炸
⭐ 好的初始化让【每一层的激活值方差保持一致】
Xavier/Glorot:为 tanh/sigmoid 设计,方差 ∝ 1/(fan_in + fan_out)
He/Kaiming ⭐:为 ReLU 设计,方差 ∝ 2/fan_in
(因为 ReLU 砍掉了一半,所以要乘 2 补回来)
💡 PyTorch 的默认初始化对
nn.Linear已经不错,但用 ReLU 时显式指定更稳:nn.init.kaiming_normal_(layer.weight, nonlinearity="relu")
🌈 四、万能近似定理(以及它的误导性)
理论:只要隐藏层够宽,一个隐藏层的神经网络就能逼近任意连续函数。
⚠️ 但这个定理有很强的误导性:
| 定理说的 | 定理没说的 |
|---|---|
| 存在这样的网络 | 怎么找到它(能否用梯度下降训出来) |
| 一层就够 | 需要指数级宽才行 |
| 能拟合训练数据 | 能否泛化到新数据 |
实践结论:深比宽更高效。 同样的参数量,深层网络能表达的函数复杂度远高于浅层—— 因为每一层都在前一层的特征上做组合,是层级抽象(第 12 章 CNN 会看得很清楚)。
🏗️ 五、一个完整 MLP 的样子
import torch.nn as nn
model = nn.Sequential(
nn.Linear(30, 64), # 输入 30 个特征 → 64 个神经元
nn.ReLU(), # ⭐ 非线性
nn.Dropout(0.2), # 正则化(第 10 章)
nn.Linear(64, 32),
nn.ReLU(),
nn.Linear(32, 1), # 输出 1 个数
# 二分类不用加 Sigmoid —— 用 BCEWithLogitsLoss,它内部做了且数值更稳
)
结构怎么定(经验值):
| 决策 | 经验 |
|---|---|
| 几层 | 表格数据 2–4 层就够;再深收益很小 |
| 每层多宽 | 常见 64/128/256,逐层递减 |
| 激活 | ReLU(不确定时的默认) |
| 输出层 | 回归:不加激活;二分类:不加(用 BCEWithLogitsLoss);多分类:不加(用 CrossEntropyLoss) |
⚠️ 表格数据的现实:神经网络在中小规模表格数据上经常打不过 GBDT(第 4 章)。 别默认上神经网络——第 19 章挑战 B 会让你亲手验证这一点。
🔗 和站内其他章的关系
| 相关的地方 | 根因在这 |
|---|---|
| 推荐算法第 8 章的 DeepFM、DIN | 它们的"Deep"部分就是本章的 MLP |
| 全景导论第 2 章的 FFN | 就是本章的线性层+激活 |
| Kaggle 板块里 NN 常输给 LightGBM | 表格数据上 GBDT 的归纳偏置更合适 |
| 第 3 章逻辑回归 | 就是"零隐层的神经网络" |
✅ 检查点
- 异或实验里,
activation="identity"的 MLP 为什么也是 50%? - 一层神经网络做哪两件事?写出那个式子。
- 用几何直觉说:神经网络到底在做什么?为什么说它是"自动的特征工程"?
- 为什么 ReLU 是深度学习的转折点?Sigmoid 10 层后梯度剩多少?
- ReLU 的代价是什么?三个解法?怎么自检?
- 初始化太小/太大分别会怎样?He 初始化为什么要乘 2?
- 万能近似定理有哪三个误导性?
- 为什么二分类的输出层不加 Sigmoid?
👀 答案
- identity 激活 = 没有非线性,线性套线性还是线性(W₂W₁x 等价于单层),所以退化成逻辑回归,画不出异或的分界。
- ①线性变换 z=Wx+b ②非线性激活 a=f(z)。式子:a⁽ˡ⁾ = f(W⁽ˡ⁾a⁽ˡ⁻¹⁾ + b⁽ˡ⁾)。
- 线性层旋转拉伸空间,激活函数折叠空间——反复拉伸折叠,把纠缠的两类掰成一条直线能分开的形状。最后一层永远是线性分类器,前面所有层都在把数据变换成"线性可分"的样子——所以它是自动的特征工程。
- Sigmoid 两端梯度趋近 0,反传层层相乘指数衰减,深层训不动。10 层后 0.25¹⁰ ≈ 百万分之一,30 层后 10⁻¹⁸。ReLU 正区间梯度恒为 1,原样传回。
- 神经元死亡——输入一直为负则梯度永远是 0,权重再也不更新。解法:①降学习率(最常见根因)②Leaky ReLU/GELU ③He 初始化。自检:统计
(activation == 0).float().mean(),某层超 50% 恒为 0 就有问题。 - 太小→信号层层衰减深层收不到信息;太大→激活值爆炸。好的初始化让每层激活值方差保持一致。He 乘 2 是因为 ReLU 砍掉了一半(负区间归零),要补回来。
- 只说存在不说怎么找到、一层需要指数级宽、能拟合不代表能泛化。实践上深比宽更高效。
- 用
BCEWithLogitsLoss,它内部合并了 Sigmoid 和交叉熵,数值上更稳定(避免 log(0))。
🛑 可以停在这里
⚡ 走神救援
神经网络=线性层+非线性激活堆叠。异或实验三行结论:线性50%、MLP+tanh 100%、MLP但identity激活还是50%——证明带来能力的是非线性不是隐层(线性套线性还是线性)。一层=Wx+b后过激活。⭐几何直觉:线性层旋转拉伸空间,激活折叠空间;最后一层永远是线性分类器,前面所有层都在把数据掰成"线性可分"的形状——所以深度学习本质是自动的特征工程。激活选ReLU(正区间梯度恒为1;Sigmoid 10层后只剩百万分之一,30层后10⁻¹⁸,这就是ReLU成为转折点的原因)。⚠️ ReLU的代价是神经元死亡(输入恒负→梯度永远0→彻底死掉),根因多半是学习率太大;自检:某层超50%激活恒为0就有问题。初始化要让每层激活方差一致,He初始化乘2是因为ReLU砍掉了一半。万能近似定理有误导性:只说存在不说怎么找到,实践上深比宽高效。表格数据上NN常输给GBDT。
下一节 👉 08-反向传播.md