🧠 第二章 能力培养
📄 一句话概括

异或这个任务,一条直线分不开。 Demo 用 17 个参数的两层网络(2 → 4 → 1)训练 4000 轮,把四个输入全判对:0.0001、0.9984、 0.9989、0.0017,损失从 0.8299 掉到 0.001110。

📝 本节和前面几节的关系

上一节讲了梯度怎么算,这一节把它写成能跑的代码——而且只用一个层次:numpy 数组加两层矩阵乘法。

一、为什么拿异或开刀

异或的四个输入里,(0,0) 和 (1,1) 该输出 0,(0,1) 和 (1,0) 该输出 1。 你没法画一条直线把它们分开——这是它跟「与」「或」最大的差别。 所以单个神经元(等价于一条直线)永远学不会异或,这是 1969 年就写进书里的结论。 加一个隐层就能学会,这就是「深度」两个字最朴素的含义。 Demo 的网络是 2 → 4 → 1:输入两个数,隐层 4 个神经元用 tanh,输出 1 个用 sigmoid。 参数量 2×4 + 4 + 4×1 + 1 = 17。 17 个参数学会异或,这个数字值得记:能力不来自参数多,来自「多了一层非线性」。

二、四行核心:前向、损失、反向、更新

前向:h = tanh(X @ W1 + b1),p = sigmoid(h @ W2 + b2)。两步矩阵乘法,加上两个非线性函数。 损失:交叉熵,-mean(y·ln p + (1-y)·ln(1-p))。它衡量「预测概率离真值多远」。 反向:先算输出层误差 d2 = (p - y)/n,再往前乘一次 W2.T、乘一次 tanh 的导数 1 - h², 就是隐层的误差。这三步就是「反向传播」的全部。 更新:W -= lr * grad。学习率这里取 0.5,跑 4000 轮。

三、实测结果

四个输入的概率:0⊕0 = 0.0001、0⊕1 = 0.9984、1⊕0 = 0.9989、1⊕1 = 0.0017。全判对。 损失从 0.8299 掉到 0.001110。曲线在前几百轮下降最快,后面基本贴着零。 值得注意的一点:两个「该输出 1」的样本概率是 0.9984 和 0.9989, 两个「该输出 0」的是 0.0001 和 0.0017——不对称。 这是随机初始化带来的,训练轮数够了才会一起压到两端。

四、动手 Demo:17 个参数学会异或

四个样本、两层网络(2→4→1),前向用 tanh 加 sigmoid,损失用交叉熵,反向手推梯度, 梯度下降 4000 轮,最后把四个输入的概率打出来。

h = np.tanh(X @ W1 + b1)
p = H.sigmoid(h @ W2 + b2)
d2 = (p - y) / len(X)
d1 = (d2 @ W2.T) * (1 - h ** 2)      # tanh 的导数是 1 - h²
W1 -= 0.5 * (X.T @ d1)
W2 -= 0.5 * (h.T @ d2)

实跑输出:

--------------------------------------------------------------------
二十行神经网络学会异或
--------------------------------------------------------------------
  输入    期望   输出概率   判成
  -----------------------------
  0 ⊕ 0      0     0.0001      0
  0 ⊕ 1      1     0.9984      1
  1 ⊕ 0      1     0.9989      1
  1 ⊕ 1      0     0.0017      0

  参数量                   17(2×4 + 4 + 4×1 + 1)
  准确率                   4/4 = 1.000
  损失                     0.8299 → 0.001110

  损失曲线(每 100 步取一点,共 40 点):
  *



   *


    *
     *************************************
  ^ 高 0.8300  低 0.0010

  异或的特别之处:它不是一条直线能分开的。单个神经元(线性模型)做不到,
  加一个 4 个神经元的隐层就成了——这就是「深度」两个字最朴素的含义:
  多一层,能把直线掰弯。

四个概率 0.0001、0.9984、0.9989、0.0017,全判对。损失 0.8299 → 0.001110。 参数量 17 是我按 2×4 + 4 + 4×1 + 1 算的,代码里也这么打印。 这么小的网络能学会异或,说明「深度」的关键不是大,是非线性层。 如果去掉 tanh(隐层变线性),整个网络等价于一层,异或马上学不会。 这一步我没在 Demo 里跑,但你可以改一行验证——把 tanh 换成恒等函数即可。 4000 轮这个数是我试出来的:轮数少了概率压不到两端(比如 0.3 和 0.7),判对但没「确信」。 训练轮数也是训练师要调的一个参数。

30 秒实验

  1. 把隐层从 4 个神经元改成 2 个,看还能不能学会。
  2. 把 tanh 换成「恒等函数」(即不做非线性),看损失停在哪。
  3. 把学习率从 0.5 改成 5,看会不会发散。

常见误区

  1. 「参数越多越聪明」——17 个参数就能学会异或,关键是结构。
  2. 「反向传播很神秘」——就是链式法则,乘两次矩阵的事。
  3. 「准确率 1.0 就好了」——概率 0.51 也算判对,但模型其实没把握。
自测三问(点开对答案) 1. 异或为什么单层网络学不会? —— 它不能被一条直线分开,单层等价于一条直线。 2. 反向传播的三步是什么? —— 算输出层误差、乘权重转置往前传、再乘激活函数导数。 3. 这个网络的参数量是多少? —— 17 个(2×4 权重 + 4 偏置 + 4×1 权重 + 1 偏置)。

代码能跑通了,下一节看一个更实际的问题:特征怎么挑。

相关笔记