🤖 第2章 学习模型
📄 一句话概括

单隐层的 sigmoid 网络,隐单元 1 个测试误差 0.336,3 个 0.061,10 个 0.064,30 个 0.061。 一道弯不够,三道弯就到顶了。 去掉激活函数之后, 同样 31 个参数的网络测试误差是 0.664——和直接用线性最小二乘算出来的一模一样。 两层线性串起来还是线性:加层数不会增加任何表达能力,除非中间有那个弯。

📝 本节和前面几节的关系

2.1 的基由人选,2.2 的基由数据定中心、形状仍是固定核。 这一节把最后一样也交出去:基的形状由训练决定。 代價是求解从「一次闭式解」变成「反复迭代的非凸优化」。 这是本书里第一次出现「没有解析解」的情况,也是深度学习全部麻烦的源头。

一、两层线性还是线性

先把最容易搞错的一件事说清。 一个「输入乘权重加偏置、再乘权重加偏置」的两层网络,展开之后还是 a 乘 x 加 b。 中间的权重再多个,也只是改了斜率和截距。 所以层级模型的全部本事,来自中间那个非线性激活。 Demo 里那组对照就是为了把这句话钉死: 去掉激活,31 个参数的网络给出 0.664,和两参数线性模型完全一致。 这个结论不是「差不多」,是数学上相等。 矩阵乘法的结合律保证了这一点,浮点误差只是末位差异。 推论:一个网络如果想表达复杂形状,激活函数必须是非线性的, 而且不能是「几乎线性」的——用 ReLU 时如果所有单元都死了,退化也会发生。

二、每个隐单元是一道弯

单个 sigmoid 单元画出的是 S 形:左边贴 0,右边贴 1,中间一段过渡。 把 m 个这样的 S 形按不同权重叠加,就能凑出任意形状。 这是「万能近似」的直观版本:台阶够多,什么曲线都能拼。 几个台阶够用?取决于目标有几个弯。 正弦在 1.4 个周期里有两个峰一个谷, 大概三道弯——这就是 Demo 里 m 等于 3 时误差已经到位的原因。 所以「隐单元数」可以翻译成「模型能画几道弯」。 这比「参数量」直观得多,也比「层数」更贴近实际:单隐层够宽,理论上已经能逼近任何连续函数, 深层的价值在于「同样宽度下表达得更省」。 这一点教材没有展开,但值得记住:深度不是万能钥匙,宽度也不是。 决定成败的是「这道题需要几道弯」和「你有多少数据能定出这些弯」。

三、实测:三道弯就到顶

1 进、m 个 sigmoid、1 出,输出层不带激活。全批梯度下降 12000 步,学习率 0.3。

隐单元 m 参数量 训练 MSE 测试 MSE
1 4 0.370 0.336
3 10 0.026 0.061
10 31 0.027 0.064
30 91 0.024 0.061
m 等于 1:4 个参数,测试 0.336。
一个 S 形只能拐一次,它连正弦的一个完整周期都画不出来——形状就错了,参数再多也没用。
m 等于 3:10 个参数,测试 0.061,一步到位。
这份数据的正弦只有 1.4 个周期,三道弯够画了。
m 从 3 加到 30,测试误差在 0.061 到 0.064 之间晃,没有改善。
训练误差也停在 0.026 附近,而噪声方差是 0.2 的平方——0.04。
已经到噪声底了,加容量既不会更好也不会更坏。
注意这里没出现过拟合。
原因有三个:输入缩放过、只有 20 个点、目标非常平滑。
换成手写数字那种高维、噪声大、样本又少的任务,同样的 91 个参数会立刻开始记噪声。
「参数多就会过拟合」这句话是错的,正确的是「参数多而没有足够数据定住它」。

四、三种模型的真正差别

把三种模型摆在一张表里,差别只剩一列:形状由谁决定。

模型 形状由谁决定 参数个数 求解
线性 人选定的基函数 等于基的个数 一次闭式解
核 核与带宽,中心在数据上 等于训练点数 一次闭式解
层级 层数、宽度、激活 权重总数 迭代,非凸
前两种都是凸问题,都有唯一最优解,都能一次算完。
层级模型不是凸的:初始化不同,最终解不同,训练过程还会卡在局部极小或者鞍点。
换来的是「表示学习」:它不需要人告诉它该用什么基,自己从数据里长出中间表示。
这是深度学习在图像、语音、语言上碾压手工特征的根本原因——那些领域里,人根本不知道该铺什么基。
但代价也是真的:调参成本高、结果不可复现、训练要 GPU、数据少了就过拟合。
所以选模型不是「谁先进选谁」,是「你的数据量和问题结构适合谁」。

五、这一章留给后面什么

第 3 章开始处理「给定模型,怎么求参数」。 线性模型配平方损失,答案是一次闭式解;配别的损失,就要迭代。 第 4、5、6 章都在改损失和约束,第 7 到 11 章改到分类, 而 2.2 的核思想会在第 8.4 节以「核 SVM」的形式回来。 层级模型在本书里只作为对照出现,不展开训练细节。 这不是省略,是取舍:本书的主线是「凸的、能算的、能讲清性质的」方法。 神经网络的性质到今天还有很多说不清,硬塞进来会破坏这本书的结构。 但 2.3 这一节给了一个不会被时间淘汰的结论: 没有非线性,就没有表示能力。 不管你以后用多深的网络,这句话都在。

六、动手 Demo:隐单元 1 个 0.336、3 个 0.061、10 个 0.064、30 个 0.061

1 进、m 个 sigmoid、1 出的小网络,输出层线性。全批梯度下降 12000 步、学习率 0.3,反向传播手写。 输入先除以 3 缩到负 1 到 1——不缩的话梯度会指数放大,我第一次跑就吃了这个亏,最后几个数全是 nan。

z = xs @ W1 + b1                              # 隐层净输入
a = 1.0 / (1.0 + np.exp(-np.clip(z, -60, 60)))  # sigmoid
f = a @ v + c                                 # 输出层线性
d = 2.0 * (f - ys) / len(ys)                  # 平方损失的梯度

dv = a.T @ d
dc = float(d.sum())
dz = np.outer(d, v) * a * (1.0 - a)            # 关键:乘上激活的导数
W1 -= lr * (xs.T @ dz)
b1 -= lr * dz.sum(0)
v -= lr * dv
c -= lr * dc

# 对照:去掉激活,直接对 x 做线性最小二乘
A = np.column_stack([np.ones(20), X[TR] / 3.0])
w = np.linalg.lstsq(A, Y[TR], rcond=None)[0]

实跑输出:

--------------------------------------------------------------------
隐单元 1 个 0.336、3 个 0.061、10 个 0.064、30 个 0.061
--------------------------------------------------------------------
  结构                     1 进、m 个 sigmoid、1 出,输出层没有激活
  训练                     全批梯度下降,12000 步,学习率 0.3,手写反向传播
  输入                     x 先除以 3 缩到 [-1, 1],否则梯度会炸
  参数量                   3m 加 1(进到隐 m、隐偏置 m、隐到出 m、输出偏置 1)
  对照                     去掉激活,m 等于 10 的网络在数学上就是个线性模型

  隐单元 m   参数量   训练 MSE   测试 MSE
  --------------------------------------
         1        4      0.370      0.336
         3       10      0.026      0.061
        10       31      0.027      0.064
        30       91      0.024      0.061

  **m 等于 1:4 个参数,测试 0.336。**
  **一个 sigmoid 只会拐一道弯,它是 S 形,不是正弦——形状就错了,参数再多也没用。**
  **m 等于 3:10 个参数,测试 0.061,一步跨到位。**
  **因为这份数据的正弦只有 1.4 个周期,三道弯就够画了。**
  **m 等于 3、10、30,测试 MSE 分别是 0.061、0.064、0.061,在噪声里晃,没再改善。**
  **训练误差也停在 0.027 附近,而噪声方差是 0.2 的平方,也就是 0.04。**
  **到噪声底了,加容量既不会更好也不会更坏。**
  **注意这里没出现过拟合,原因有三个:输入缩放过、只有 20 个点、目标极平滑。**
  **换成手写数字那种高维噪声大的任务,同样的 91 个参数会立刻开始记噪声。**
  **最该看的是那组对照:去掉激活之后,测试 MSE 是 0.664。**
  **它就是用「对 x 直接做线性最小二乘」算出来的 0.664,两条路走到同一个解。**
  **两层线性串起来,数学上还是一个线性函数——加层数不会增加任何表达能力。**
  **所以层级模型的全部本事来自激活函数那个弯;去掉它,堆多少层都白堆。**
  **这也是深度学习为什么执着于非线性:没有它,网络只是个绕远的线性模型。**
  要说明:**单隐层、一个初值、固定 12000 步,没调参。**
  **换初值数字会动,但「m 越大越能弯」「没有激活就退化成线性」这两条不动。**

这一段反向传播里,真正体现「非线性」的只有一行:dz = ... * a * (1-a)。 那个 a * (1-a) 是 sigmoid 的导数。 去掉它,梯度就直接从输出传到输入,整条链退化成线性——这正是「无激活就退化」在代码上的样子。 m 等于 1 的 0.336 值得盯着看。 它不是「训练不充分」,是「形状不对」:一个 S 形无论怎么调斜率、位置、高度, 都画不出「先升后降再升」。 遇到这种情况,加训练步数是浪费时间,该加的是隐单元。 训练误差停在 0.026、测试 0.06 附近不再改善,说明到了噪声底。 判断依据很简单:噪声是 0.2,方差 0.04, 训练误差已经低于它——因为 25 个点里总有那么几个恰好被拟合得特别好,样本内的误差可以低于噪声。 这就是「训练误差不能用来判断模型好坏」的又一个理由。 最后那个对照是本节的主结论:去掉激活 0.664,和线性最小二乘 0.664 是同一个数。 别把它当成巧合。 矩阵乘法满足结合律,W1 之后接 v 等于一个复合的线性映射, 再多隐单元也只是换一种方式表达同一个线性函数。 要说明:单隐层、一个初值、固定 12000 步,没调参、没正则化、没早停。 换初值数字会动,但「m 越大越能弯」「没有激活就退化成线性」这两条不动。

30 秒实验

  1. 把激活从 sigmoid 换成 ReLU,m 等于 3 和 m 等于 10 的结果怎么变? 重点看有没有单元「死掉」(输出恒为 0)。
  2. 把学习率从 0.3 改成 3.0,训练会怎样?这是 2.3 里最典型的坑,值得亲手试一次。
  3. 把输入缩放去掉(不除以 3),只跑 m 等于 10。 如果出现 nan 或者巨大数,想想它和「条件数」「梯度爆炸」的关系。

常见误区

  1. 「层数越多越强」。 没有非线性激活时,任意多层都等价于一层。 有激活时,深层的优势是「省参数」,不是「万能」。 盲目加深只会带来梯度消失和训练困难。
  2. 「参数多就会过拟合」。Demo 里 91 个参数对 20 个点,测试误差并没有变差。 过拟合的条件是「容量超过了数据能定住的程度」,不是「参数量超过某个数」。
  3. 「神经网络不需要调输入缩放」。 恰恰相反,它是所有模型里对尺度最敏感的一个。 输入不缩放,第一层的净输入就很大,sigmoid 直接饱和、梯度接近 0,整个网络学不动。
  4. 「训练误差到 0 就说明模型完美」。 训练误差到 0 只说明它把训练集背下来了。 要判断好坏必须看留出集,这是本书从第 1 章讲到现在唯一没有例外的一条。
自测三问(点开对答案) 1. 为什么去掉激活函数之后,多层网络就退化成线性模型? —— **因为线性映射的复合还是线性映射。** **W1 之后接 v,等于一个矩阵 (v 乘 W1);再加偏置,等于一个仿射变换。** **中间加多少层,最后都塌缩成一个仿射变换,表达能力不变。** 2. m 等于 1 时测试误差 0.336,加大训练步数有用吗? —— **没用。**这不是优化不充分,是**模型的形状空间里根本没有这条曲线**。 **一个 S 形只有一次拐弯,画不出两个峰。** **这时候该加隐单元,不是加迭代次数——「训练不够」和「容量不够」表现相似,** **区别在于前者训练误差还会继续降,后者早就平了。** 3. 三种模型里,如果数据只有 200 条、特征 50 维,你选哪个?为什么? —— **先用核模型。**200 条数据下核矩阵只有 200 乘 200,一次闭式解,**半天能跑完,结果可复现。** **层级模型在 200 条数据上几乎必然过拟合,而且调参成本远高于它可能带来的收益。** **先用简单的打基线,是这个领域最省时间的习惯。**

三种模型讲完,第 2 章的任务结束。 下一章开始回答第二个问题: 给定模型,参数怎么求?先从最经典的一种开始—— 最小二乘,它有闭式解,而且解的性质能被完整推导出来。

相关笔记