深度学习入门站 · 理解深度学习
首页 › 第 3 章

浅层神经网络

一层隐藏层如何让模型从"只会画直线"进化成"能画任意曲线"。
图 3-1:浅层神经网络结构(输入层—隐藏层—输出层)
图 3-1:浅层神经网络结构(输入层—隐藏层—输出层)

上一章我们学会了用一条直线去拟合数据,但现实世界里的关系很少是笔直的。这一章,我们要给模型装上"隐藏层",让它从"一条直线"进化成"能拐弯的曲线"——这就是浅层神经网络。学完这一章,你会看懂神经网络那张"圆圈加箭头"的结构图,明白激活函数是干什么的,以及为什么"隐藏单元足够多"的神经网络几乎什么函数都能模拟。

为什么需要非线性?直线不够用了

还记得第 2 章的线性回归吗?它的模型是一条直线:

$$y = \phi_0 + \phi_1 x$$

这个式子意思是:输出 $y$ 和输入 $x$ 的关系只有"斜率"和"截距"两个旋钮可调,画出来永远是一条笔直的线。但真实数据很少这么听话。比如广告费投入越多,带来的销量增长会越来越慢(边际递减);再比如一天中气温随时间的变化,是一条先升后降的曲线。这些关系用直线硬套,误差会很大。

所以我们想要更"灵活"的函数 $y = f[x, \phi]$:它既能是直线,也能弯成各种形状。问题来了——怎么让一台"直线机器"学会拐弯?答案是:把直线掰断,再拼接。

网络结构:输入层 → 隐藏层 → 输出层

浅层神经网络的图纸只有三层。最左边是输入层,放着输入 $x$;中间是隐藏层,藏着若干个"隐藏单元"(也叫神经元);最右边是输出层,给出预测 $y$。层与层之间密密麻麻的连线,每条线都代表一个参数,叫权重;每个单元还自带一个偏移参数,叫偏置。数据从输入到输出一路向右流动,所以这类网络叫前馈网络;每一层的每个单元都和下一层的所有单元相连,所以又叫全连接网络。

举个只有 3 个隐藏单元的例子,它的完整公式是:

$$y = \phi_0 + \phi_1 a[\theta_{10} + \theta_{11}x] + \phi_2 a[\theta_{20} + \theta_{21}x] + \phi_3 a[\theta_{30} + \theta_{31}x]$$

这个式子看着吓人,其实只做了三步,写成流水线就清楚了:

  1. 拧一拧:每个隐藏单元先把输入 $x$ 做一个线性变换 $\theta_{d0} + \theta_{d1}x$,相当于给直线调斜率和高度;
  2. 掰一掰:结果经过激活函数 $a[\cdot]$(马上介绍),负数被处理掉,直线在这里被"掰弯";
  3. 拼一拼:把几根掰弯后的直线各自加权(乘上 $\phi_d$)再求和,最后加上偏置 $\phi_0$。

把隐藏单元记作 $h_d = a[\theta_{d0} + \theta_{d1}x]$,输出就是它们的加权和:

$$y = \phi_0 + \sum_{d=1}^{D} \phi_d h_d$$
🏭
生活类比
神经网络就像一条乐高流水线:每个隐藏单元是一个工作站。第一步,工作站把原材料(输入)按自己的配方拧成一根直条;第二步,用"闸门"(激活函数)把直条负的部分切掉,直条就变成带拐角的积木;第三步,流水线把几块积木按不同力度拼在一起,就得到一件形状复杂的成品。改一改配方(调参数),成品形状就跟着变。

激活函数:让直线"拐弯"的开关

激活函数是整个网络能"拐弯"的关键,它必须是非线性的。最常用的是 ReLU(整流线性单元),规则简单得像个开关:

$$\mathrm{ReLU}[z] = \begin{cases} 0 & z < 0 \\ z & z \ge 0 \end{cases}$$

这个式子意思是:输入是负数,输出 0(闸门关死);输入是正数,原样放行(闸门打开)。它把一条直线在 $z = 0$ 处掰出一个拐点。

另外两种常见激活函数是 sigmoid 和 tanh,它们不"硬切",而是把输入平滑地压缩:

$$\sigma[z] = \frac{1}{1 + e^{-z}}, \qquad \mathrm{tanh}[z] = \frac{e^{z} - e^{-z}}{e^{z} + e^{-z}}$$

sigmoid 把任何输入压到 0 到 1 之间,tanh 压到 -1 到 1 之间。它们的缺点是:输入很大或很小时,曲线几乎变平,梯度趋近于零(叫"饱和"),训练更新会变得很慢;而 ReLU 在正半轴导数恒为 1,不会饱和,所以成了现代网络最常用的选择。

💡
核心要点:激活函数必须是非线性的。如果去掉它(或换成线性函数),那么"线性变换叠线性变换"的结果仍然是线性变换——隐藏层再多也白搭,整个网络塌缩成一条直线,和线性回归没有区别。
⚠️
ReLU 也有缺点:输入为负时导数恒为 0。如果某个隐藏单元对几乎所有训练样本都"被关闸",它就再也学不到东西了,这叫ReLU 消亡。改进版如 Leaky ReLU 会给负数留一条小坡,用来缓解这个问题。

万能近似:拼直线逼近一切

回到乐高思路:一个 ReLU 隐藏单元在 $x$ 轴上贡献一个拐点,把输出函数分成两段直线;两个单元贡献两个拐点、三段直线。推而广之,D 个隐藏单元最多贡献 D 个拐点,把函数切成 D+1 段直线——也就是一个"分段线性函数"。

分段直线有什么了不起?想想折线图:只要折点够多、每段够短,折线就能"以假乱真"地贴住任何一条光滑曲线,误差可以任意小。这就是万能近似定理(通用逼近定理)的内容:只要隐藏单元足够多,一个带隐藏层的神经网络,就能以任意精度逼近定义在有限范围内的任何连续函数。每个隐藏单元就像一块小积木,积木越多,能拼出的形状越复杂。

同样地,输入和输出也能扩展成多个:每个隐藏单元可以接收多个输入的线性组合,多个输出则共享同一批隐藏单元、各自加权求和。隐藏单元的数量,就是网络的"容量"——衡量它处理复杂关系的能力。

损失与训练:还是第 2 章的老套路

网络结构只是"公式家族",选哪一位成员由参数 $\phi$(所有权重和偏置)决定。判断参数好坏的标准和第 2 章一模一样——最小二乘损失:

$$L(\phi) = \sum_{i=1}^{I} (f[x_i, \phi] - y_i)^2$$

这个式子意思是:把每个训练样本的预测值和真实值之差平方后加起来,损失越小,模型越好。训练就是用梯度下降不断调参数 $\phi$,让 $L(\phi)$ 一路"下山"到最低点。下面用 NumPy 手写一个"2 输入 → 4 隐藏 → 1 输出"网络的前向计算,看看这三步流水线跑起来是什么样:

import numpy as np

# 网络结构:2 个输入 -> 4 个隐藏单元(ReLU)-> 1 个输出
x = np.array([1.0, 0.5])                 # 输入 x = [x1, x2]

# 第一组参数:输入 -> 隐藏层(每行 = 偏置 + 2 个权重)
theta = np.array([[0.1, 0.8, -0.3],
                  [0.0, 0.5, 0.5],
                  [-0.2, -0.6, 0.9],
                  [0.3, 0.4, -0.7]])

# 第二组参数:隐藏层 -> 输出(偏置 + 4 个权重)
phi = np.array([0.2, 0.7, -0.5, 0.6, 0.3])

# 第一步:每个隐藏单元的线性组合(预激活值)
z = theta[:, 0] + theta[:, 1:] @ x
# 第二步:ReLU 激活,负数清零(掰弯)
h = np.maximum(z, 0.0)
# 第三步:加权求和,得到输出
y = phi[0] + phi[1:] @ h

print(f"预激活值 z = {z}")
print(f"激活值   h = {h}")
print(f"网络输出 y = {y:.3f}")

跑一下就能看到数据如何在网络里"流"过:线性变换 → 激活 → 线性组合。真正的训练只多一件事:算出损失对每个参数的梯度,再沿反方向更新(这种"反向"计算的技巧叫反向传播,第 7 章会细讲)。模型变复杂了,但"定义损失 → 梯度下降 → 最小化损失"的总套路,和上一章完全一致。

🎯 小测验

1. 为什么浅层神经网络一定要用非线性的激活函数?
2. 一个有 1 个输入、1 个输出、4 个 ReLU 隐藏单元的网络,输出函数最多有几个线性区域?
3. 训练浅层神经网络和上一章的线性回归相比: