🧠 第一章 AI入门:认识人工智能模型训练
📄 一句话概括

两个同心圆的数据:线性模型准确率 0.7550,两层但不加激活函数还是 0.7550, 加上 tanh 变成 0.9925。环形采样点上判对数从 72/144 跳到 144/144。 参数多不多是次要的,有没有那道弯才是关键。

📝 本节和前面几节的关系

前面几节讲的都是「大模型」这一层。 这一节往下挖一层,看神经网络本身为什么能拟合复杂形状——这是所有模型的地基。

一、一层不够就加一层

一个线性模型只能画一条直线(高维里是一个超平面)。数据只要不是线性可分的,它就永远分不对。 加一层隐层,等于把输入先映射到一个新的空间,再在那个空间里画直线。 「在新空间里线性可分」这件事,在原空间里就等价于一条弯曲的边界。 Demo 用的是一组最好懂的数据:内圈一类、外圈一类(同心圆)。 一条直线怎么画都分不开,线性模型卡在 0.7550。 加一个 16 个单元的隐层、配上 tanh,准确率到 0.9925。环形采样点从 72 个判对变成 144 个全对。

二、没有非线性,加多少层都等于一层

这是最容易踩的坑:两层、参数更多、但中间不加激活函数,准确率还是 0.7550, 跟单层线性模型一模一样。 原因是一个矩阵等式:两个线性变换摞起来还是线性变换。 W₂(W₁x + b₁) + b₂ = (W₂W₁)x + (W₂b₁ + b₂)——括号里还是一个矩阵乘加一个偏置。 换句话说,不加激活函数的多层网络,等价于一个单层线性模型,参数再多也是白搭。 这是 1969 年让神经网络冷了十年的那个发现的现代版。 所以「深度」这个词真正的意思是「带了非线性的多层」。没有非线性,深度就是幻觉。

三、训练时到底在算什么

回到 1.1.3 那个三步循环:前向算出预测、算误差、按导数改参数。 多层网络唯一的难点是第三步——误差怎么分摊到每一层的每个参数上。 办法叫反向传播:先算最后一层的误差对输出的导数,再用链式法则一层层往前传。 每一层拿到的「责任」是上一层传下来的梯度乘以本层的局部导数。 Demo 里那段代码就是手写的最小版本:g = (p - onehot) / n 是输出层梯度, gh = (g @ W2.T) * (1 - h * h) 把梯度传回隐层,其中 (1 - h * h) 就是 tanh 的导数。 如果换成 ReLU,那个位置就变成「h 大于 0 则 1,否则 0」。 激活函数换一个,反向传播的公式就换一处——其他全不变。 这也解释了为什么激活函数必须是可导的:不可导就传不回去。 ReLU 在 0 点形式上不可导,但工程上取次梯度就够了。

四、为什么它能拟合任意形状

数学上有个结论(通用近似定理):一个足够宽的隐层,配上非线性激活,能以任意精度逼近任意连续函数。 但「存在」不等于「训得出来」。定理没说需要多少参数、也没说梯度下降能不能找到。 实际效果取决于数据量、初始化、优化器——这些就是训练师要调的东西。 还有一层现实约束:宽度和深度都受显存和算力限制。 所以真实的模型是在「能训得动」的范围内, 找「够用」的容量。 这就把 1.1.4 的过拟合接回来了:容量大不是好事,容量「刚好」才是好事。 而「刚好」是多少,只能靠验证集试出来。

五、动手 Demo:线性 0.7550、两层无激活 0.7550、两层加 tanh 0.9925

造 400 个点,按「离原点多远」分成内圈和外圈两类——这是一条直线绝对分不开的形状。 训三个模型:线性、两层但不加激活、两层加 tanh。 比较训练集准确率,再用 144 个环形采样点看它们的边界画成了什么样。

X = r.uniform(-1, 1, (n, 2))
rad = np.sqrt((X ** 2).sum(axis=1))
y = (rad > 0.55).astype(int)           # 外圈 1、内圈 0

# 隐层用 tanh 还是不用,就差一行
h = np.tanh(X @ W1 + b1) if mode == u'mlp' else X @ W1 + b1
z = h @ W2 + b2
p = softmax(z)
g = (p - np.eye(2)[y]) / n
gh = (g @ W2.T) * ((1 - h * h) if mode == u'mlp' else 1.0)
W1 -= lr * (X.T @ gh)
W2 -= lr * (h.T @ g)

实跑输出:

--------------------------------------------------------------------
神经网络的核心:一层不够就加一层,加了非线性才有用
--------------------------------------------------------------------
  结构                   训练集准确率   环形采样点判对数
  -----------------------------------------------------
  线性模型(没有隐层)         0.7550           72 / 144
  两层但没有激活函数           0.7550           72 / 144
  两层 + tanh                  0.9925          144 / 144

  这批数据是**两个同心圆**:内圈一类、外圈一类。一条直线怎么画都分不开,所以线性模型卡在 0.7550。
  第二行最能说明问题:两层、参数更多,但中间不加激活函数,准确率还是 0.7550——**两个线性变换摞起来还是线性变换**,等于一层都没加。
  加上 tanh 之后到 0.9925,环形采样点从 72 个判对变成 144 个。
  **神经网络能拟合复杂形状,靠的就是那一层非线性。** 参数多不多是次要的,有没有那道弯才是关键。

三行结果:0.7550 / 0.7550 / 0.9925。 第一行和第二行完全相同,这不是巧合,是数学上必然相等——两个线性变换摞起来还是线性变换。 第二行尤其值得盯着:它有 16 个隐单元,参数比单层多得多,但准确率一分没涨。 这就是「深度没有非线性就是幻觉」的实证。 环形采样点那一列更直观:线性模型在 144 个点上判对 72 个(正好一半,等于瞎猜), 加 tanh 之后 144 个全对。 有个细节要注意:(1 - h * h) 是 tanh 的导数。 当 h 接近 ±1 时它接近 0,梯度就传不回去了——这就是「梯度消失」,第八章 8.2 会专门算它。

30 秒实验

  1. 把激活函数从 tanh 换成 ReLU(h = np.maximum(z, 0),导数取 (z > 0)),重跑。 准确率差不多,但训练更快——因为 ReLU 不会饱和。
  2. 把隐单元从 16 降到 2,看准确率掉到多少。这就是「容量不够」:两个隐单元只能画出很粗糙的边界。
  3. 把内圈半径从 0.55 改成 0.15,让两类几乎混在一起,看三个模型的差距怎么变。 差距缩小不是因为模型变强了,是因为任务变简单了。

常见误区

  1. 「层数越多越强」。没有非线性的多层等于一层。有非线性也只是「容量更大」,容量太大反而过拟合。
  2. 「激活函数随便选一个」。tanh 会饱和(梯度消失),ReLU 会死亡(负数区永远 0),各有各的毛病。 选哪个取决于任务和数据规模。
  3. 「神经网络是个黑箱,没法解释」。结构是透明的,不透明的是几百万个参数的具体取值。 工程上更关心「输入扰动能造成多大输出变化」,而不是「第 37 个参数为什么是 0.42」。
自测三问(点开对答案) 1. 为什么两层但不加激活函数,准确率和单层线性模型完全相同? —— 因为 W₂(W₁x+b₁)+b₂ 可以化简成 (W₂W₁)x+(W₂b₁+b₂),仍是一个线性变换。 参数变多了,但能表达的函数集合没变。 2. 反向传播里 `(1 - h * h)` 是什么?为什么它会导致梯度消失? —— 是 tanh 的导数。 h 接近 ±1 时它接近 0,梯度乘上它就被压没了,前面的层拿不到有效的更新信号——这就是梯度消失。 3. 通用近似定理说「足够宽就能逼近任意函数」,为什么实际还要调参? —— 定理只保证「存在这样的参数」,不保证梯度下降能找得到,也没说需要多少参数。 能不能训出来,取决于数据、初始化、优化器和算力。

神经网络的原理到这里就够了。 但 2017 年之后所有大模型都换了个结构——下一节看那个结构做了什么别人做不到的事。

相关笔记