🏠 总目录📚 本教程 07 · 从线性到神经网络 ← →
📑 本页目录(点开跳转)

07 / 先看直觉,再看细节

先看直线分不开什么,
再给模型加一层变化。

非线性激活让多层网络不再等价于一个线性变换。先理解这一点,再记层数和形状。

先抓住一个具体结果

异或:对角线上的点属于同类

(0,0)、(1,1) 是一类,(0,1)、(1,0) 是另一类。单条直线无法把四个点全部分对;加入非线性隐藏层,才有机会表示这种边界。

一层网络的计算

$$\mathbf{a}^{(l)}=f\left(W^{(l)}\mathbf{a}^{(l-1)}+\mathbf{b}^{(l)}\right)$$

接下来,按需要选一项

你现在想看什么?

这些入口是选择,不是必须按顺序完成的任务。

可以停在这里

非线性激活让多层网络不再等价于一个线性变换。先理解这一点,再记层数和形状。

需要更多细节时,继续看完整正文 →
继续探索

原有正文、图解和例子都在下方。按需跳转,不必一次读完。

07 · 从线性到神经网络

⏱ 28 分钟 | ⭐ 核心 | 🔨 有代码


🎯 一句话

神经网络 = 线性层 + 非线性激活,堆起来。 关键在「非线性激活」这四个字——没有它,堆一百层也还是一条直线。

层数(从输出往输入数)梯度大小(对数刻度)ReLU (f′=1)tanh 实际sigmoid (f′≤0.25)110⁻⁴10⁻⁸10⁻¹²每穿过一层,梯度就乘一次 W·f′ —— 这是【连乘】不是相加⭐ sigmoid 10 层后梯度只剩 10⁻⁶,前几层等于没在训练 —— 这就是 ReLU 是转折点的数学原因
纵轴是对数刻度。每穿过一层,梯度就乘一次 W·f′ —— 这是连乘不是相加。⭐ sigmoid 10 层后只剩 10⁻⁶,前几层等于没在训练 —— 这就是 ReLU 成为转折点的数学原因。

💥 一、先看线性模型的死穴:异或问题

XOR 在两个输入不同时输出 1。同类点处于对角位置,无法用一条直线把实心点与空心点完全分开。虚线用于显示对角关系,不是分类边界。x₁x₂(0,0) → 0(1,0) → 1(0,1) → 1(1,1) → 0实心是 1,空心是 0;同类占据对角
XOR 在两个输入不同时输出 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):有能力分对全部四点,能否训练到取决于优化过程

MLP(identity 激活):仍是线性模型,增加隐藏层不能解决非线性可分问题

🔑 第三行是本章的灵魂: 有隐层不等于有能力。真正带来能力的是「非线性激活」。 因为线性套线性还是线性:W₂(W₁x) = (W₂W₁)x,等价于一个单层线性模型。


🧱 二、神经网络的结构

输入层x₁、x₂、x₃
→
隐藏层3 个神经元
→
隐藏层3 个神经元
→
输出层ŷ
每个圆圈(神经元)做两件事
  • ① 线性: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→【旋转 + 拉伸 + 平移】空间
② 非线性激活→【折叠 / 弯曲】空间
⭐ 多层堆叠 = 反复"拉伸 + 折叠",
把原本【纠缠在一起】的两类数据,掰成【一条直线就能分开】的形状

💡 换个说法:

本节的分类 MLP 最后一层是线性输出层,再配合相应的分类损失。其他任务的输出头可以不同,不能推广成所有神经网络都如此。 前面所有层的工作,都是把数据变换成"线性可分"的样子。

⭐ 所以"深度学习"本质上是"自动的特征工程" —— 你在第 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两端变平 → 梯度趋近 0ReLU正区间直线 → 梯度恒为 1两者都能产生非线性;区别在于深层反向传播时,梯度能否稳定地传回去。
读图只看形状:Sigmoid 两端“躺平”,ReLU 的正半边保持斜率,所以深网络更容易训练。

🔑 ReLU 为什么是深度学习的转折点: Sigmoid 两端梯度接近 0,反向传播时层层相乘会指数级衰减 → 深层网络根本训不动。 ReLU 正区间导数为 1,不会在这一段像 Sigmoid 那样继续缩小梯度。但完整梯度还要经过权重矩阵等运算,ReLU 不保证没有梯度消失或爆炸。

算一下差距有多大:

对照

只比较激活导数的连乘,暂时忽略权重矩阵

Sigmoid 导数最大为 0.25:10 个相乘约为 9.5×10⁻⁷

30 个相乘约为 8.7×10⁻¹⁹:很小,但不是数学上的零

ReLU 在正区间导数为 1:这些局部导数连乘仍是 1

完整网络的梯度还受权重、路径和激活区间影响

⚠️ ReLU 的代价:神经元死亡

关键信息

ReLU 负区间的导数 = 0
如果某个神经元的输入【一直是负的】
它的梯度永远是 0
权重永远不更新
这个神经元【彻底死了】💀
常见诱因:学习率太大,一次大更新把权重推到"永远输出负数"的区域

三个解法:

解法 说明
降学习率 ⭐ 最常见的根因就是 lr 太大
Leaky ReLU / GELU 负区间给一点点梯度,不会彻底死
合适的初始化 He 初始化(PyTorch 的 kaiming_normal_)就是为 ReLU 设计的

💡 自检方法:训练后统计 (activation == 0).float().mean()。 单个批次中一半激活为 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 章逻辑回归 就是"零隐层的神经网络"

✅ 检查点

  1. 异或实验里,activation="identity" 的 MLP 为什么也是 50%?
  2. 一层神经网络做哪两件事?写出那个式子。
  3. 用几何直觉说:神经网络到底在做什么?为什么说它是"自动的特征工程"?
  4. 为什么 ReLU 是深度学习的转折点?Sigmoid 10 层后梯度剩多少?
  5. ReLU 的代价是什么?三个解法?怎么自检?
  6. 初始化太小/太大分别会怎样?He 初始化为什么要乘 2?
  7. 万能近似定理有哪三个误导性?
  8. 为什么二分类的输出层不加 Sigmoid?
👀 答案
  1. identity 激活 = 没有非线性,线性套线性还是线性(W₂W₁x 等价于单层),所以退化成逻辑回归,画不出异或的分界。
  2. ①线性变换 z=Wx+b ②非线性激活 a=f(z)。式子:a⁽ˡ⁾ = f(W⁽ˡ⁾a⁽ˡ⁻¹⁾ + b⁽ˡ⁾)。
  3. 线性变换与非线性激活逐层学习表示。本节分类 MLP 的末端用线性层输出分类分数;这是常见结构,不是所有网络的固定形式。
  4. 单看激活导数,10 个 Sigmoid 导数的乘积最多约为 9.5×10⁻⁷;正区间的 ReLU 导数连乘为 1。完整梯度还包括权重等因素,不能把这两个数当成任意网络的最终梯度。
  5. 神经元持续处在 ReLU 负区间时,该路径局部导数为 0。检查学习率、初始化,或尝试有负区间响应的激活。要跨多个批次和步骤追踪单个神经元;单批次零激活比例超过 50% 不是死亡判据。
  6. 太小→信号层层衰减深层收不到信息;太大→激活值爆炸。好的初始化让每层激活值方差保持一致。He 乘 2 是因为 ReLU 砍掉了一半(负区间归零),要补回来。
  7. 只说存在不说怎么找到、一层需要指数级宽、能拟合不代表能泛化。实践上深比宽更高效。
  8. 用 BCEWithLogitsLoss,它内部合并了 Sigmoid 和交叉熵,数值上更稳定(避免 log(0))。

🛑 可以停在这里

⚡ 走神救援

  • 线性层后加非线性,才有机会表示异或这类单条直线分不开的问题。
  • 一层的动作:加权求和、加偏置、过激活;矩阵只是把多个单元一起算。
  • ReLU 不保证梯度畅通;检查初始化,并跨批次观察是否有持续不激活的单元。
  • 能表示不等于能学会,更不等于能泛化。表格任务仍应和树模型基线比较。

下一节 👉 08-反向传播.md

打卡记录保存在你的浏览器里,首页能看到总进度