异或这个任务,一条直线分不开。 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 秒实验
- 把隐层从 4 个神经元改成 2 个,看还能不能学会。
- 把 tanh 换成「恒等函数」(即不做非线性),看损失停在哪。
- 把学习率从 0.5 改成 5,看会不会发散。
常见误区
- 「参数越多越聪明」——17 个参数就能学会异或,关键是结构。
- 「反向传播很神秘」——就是链式法则,乘两次矩阵的事。
- 「准确率 1.0 就好了」——概率 0.51 也算判对,但模型其实没把握。
自测三问(点开对答案)
1. 异或为什么单层网络学不会? —— 它不能被一条直线分开,单层等价于一条直线。 2. 反向传播的三步是什么? —— 算输出层误差、乘权重转置往前传、再乘激活函数导数。 3. 这个网络的参数量是多少? —— 17 个(2×4 权重 + 4 偏置 + 4×1 权重 + 1 偏置)。代码能跑通了,下一节看一个更实际的问题:特征怎么挑。
- ☐ (本节完,下一节 2.1.4 特征能力工程:挖掘数据深层价值的艺术)