第5周:神经网络的学习
课程简介
反向传播算法、梯度检验、参数随机初始化、神经网络训练流程。
🎬 本课程视频:吴恩达机器学习 (2014) — 交互式学习笔记
一、神经网络的代价函数
1.1 记号回顾
在详细讨论反向传播之前,先统一记号:
- L:神经网络的总层数
- s_l:第 l 层的神经元个数(不计数偏置单元)
- K:输出层神经元个数(分类类别数)
- h_Θ(x)_k:第 k 个输出神经元的预测值
- y:标签向量,y_k ∈ {0, 1} 表示是否属于第 k 类
1.2 代价函数定义
对于一个多分类神经网络,代价函数是逻辑回归中交叉熵损失的推广:
$$J(\Theta) = -\frac{1}{m} \sum_{i=1}^{m} \sum_{k=1}^{K} [y_k^{(i)} \log (h_\Theta(x^{(i)})k) + (1-y_k^{(i)}) \log (1 - h\Theta(x^{(i)})k)] + \frac{\lambda}{2m} \sum{l=1}^{L-1} \sum_{i=1}^{s_l} \sum_{j=1}^{s_{l+1}} (\Theta_{ji}^{(l)})^2$$
这个公式虽然看起来复杂,但可以分解理解:
1. 求和符号 Σᵢ 遍历所有 m 个训练样本
2. 求和符号 Σₖ 遍历所有 K 个输出类别
3. 内部是每个输出节点的交叉熵损失
4. 最后一项是正则化项——对所有层、所有权重的平方和
二、反向传播算法
2.1 反向传播的核心思想
反向传播(Backpropagation)是神经网络学习最核心的算法。它的目标是高效地计算代价函数对所有参数的偏导数。
核心思想:通过链式法则,将输出层的误差信号逐层向后传播,每一层利用后一层传来的误差计算本层参数的梯度。
直观理解:如果输出层第 k 个神经元预测错误,这个错误信号会沿着网络连接反向传播——告诉前一层你给我的输入不准确,需要调整——层层递推直到输入层。
2.2 反向传播的四个核心公式
定义 δ_j^{(l)} 为第 l 层第 j 个神经元的误差。
输出层误差:
$$\delta^{(L)} = a^{(L)} - y$$
对于 K 类分类,这是一个 K 维向量——每个元素是预测值与真实值的差。
反向传播误差:
$$\delta^{(l)} = (\Theta^{(l)})^T \delta^{(l+1)} \odot g'(z^{(l)})$$
其中 ⊙ 是逐元素乘法,g'(z^{(l)}) 是激活函数的导数。对于 Sigmoid 函数:g'(z^{(l)}) = a^{(l)} ⊙ (1 - a^{(l)})。
梯度计算:
$$\frac{\partial}{\partial \Theta_{ij}^{(l)}} J(\Theta) = a_j^{(l)} \delta_i^{(l+1)}$$
2.3 反向传播的算法步骤
- 初始化 Δ_{ij}^{(l)} = 0(所有梯度累加器)
- 对于每个训练样本 t = 1 到 m:
a. 设 a^{(1)} = x^{(t)}
b. 执行前向传播,计算每层的 a^{(l)}
c. 计算输出层误差 δ^{(L)} = a^{(L)} - y^{(t)}
d. 从后向前逐层计算 δ^{(L-1)}, δ^{(L-2)}, ..., δ^{(2)}
e. 累加梯度:Δ_{ij}^{(l)} += a_j^{(l)} δ_i^{(l+1)} - 计算正则化梯度
三、梯度检验
反向传播的实现非常容易出错。一个微小的 bug 可能导致梯度计算错误,而错误计算的梯度仍然能让代价函数下降——只是训练出来的模型性能很差。
梯度检验(Gradient Checking)提供了一个简单的验证方法:用数值方法近似计算梯度。
利用导数的定义:
$$\frac{d}{d\theta} J(\theta) \approx \frac{J(\theta + \epsilon) - J(\theta - \epsilon)}{2\epsilon}$$
其中 ε 是一个非常小的数(通常取 10⁻⁴)。双边差分方法比单边差分更精确。
def check_gradient(theta, epsilon=1e-4):
num_grad = np.zeros_like(theta)
for i in range(len(theta)):
theta_plus = np.copy(theta)
theta_plus[i] += epsilon
theta_minus = np.copy(theta)
theta_minus[i] -= epsilon
num_grad[i] = (cost_func(theta_plus) - cost_func(theta_minus)) / (2*epsilon)
return num_grad
对比数值梯度与反向传播梯度,检查相对误差。如果 diff < 10⁻⁹,说明反向传播实现正确。
重要警告:梯度检验非常慢——因为需要为每个参数计算两次代价函数。验证正确后一定要关闭梯度检验。
四、参数的随机初始化
4.1 为什么不能全零初始化
如果所有权重初始化为零,同一层所有神经元在每次迭代中都会计算完全相同的梯度——它们学到的特征永远相同。这就是对称性问题。无论网络有多少个神经元,都退化为只有一个神经元的网络。
4.2 随机初始化的策略
解决方案是对权重进行随机初始化,打破对称性。
$$\Theta_{ij}^{(l)} \in [-\epsilon_{init}, \epsilon_{init}]$$
选择一个 ε 值(如 0.12),在 [-ε, ε] 范围内均匀随机采样。
epsilon_init = 0.12
Theta1 = np.random.rand(hidden_size, input_size+1) * 2 * epsilon_init - epsilon_init
Theta2 = np.random.rand(num_labels, hidden_size+1) * 2 * epsilon_init - epsilon_init
五、神经网络的完整训练流程
- 选择网络结构:输入层单元数 = 特征维度,输出层单元数 = 类别数,隐藏层通常多于特征数
- 随机初始化权重:打破对称性
- 实现前向传播:计算 h_Θ(x) 和代价函数 J(Θ)
- 实现反向传播:计算偏导数
- 梯度检验:验证反向传播的正确性,然后关闭
- 使用优化器最小化 J(Θ):梯度下降或高级优化器
# TensorFlow/Keras 实现
from tensorflow import keras
model = keras.Sequential([
keras.layers.Dense(25, activation='relu', input_shape=(n,)),
keras.layers.Dense(25, activation='relu'),
keras.layers.Dense(K, activation='sigmoid')
])
model.compile(optimizer='adam',
loss='categorical_crossentropy',
metrics=['accuracy'])
model.fit(X, y, epochs=100, batch_size=32, validation_split=0.2)
第 5 周是课程中技术性最强、也最令人兴奋的部分。反向传播是神经网络学习的核心引擎——理解它,你就真正理解了神经网络是如何工作的。虽然现代深度学习框架自动处理了反向传播,但理解它的原理对于调试模型、设计新的网络结构至关重要。梯度检验是保护你免受实现 bug 困扰的安全网。而随机初始化则是打破对称性、让网络真正开始学习的触发器。
反向传播的详细推导
反向传播的核心是链式法则(Chain Rule)在神经网络中的系统化应用。对于单个训练样本,输出层第 k 个神经元的误差 δ_k^{(L)} = a_k^{(L)} - y_k 来源于代价函数对该神经元的偏导。
对于隐藏层,误差传播公式 δ^{(l)} = (Θ^{(l)})ᵀ δ^{(l+1)} ⊙ g'(z^{(l)}) 可以分解为:
1. (Θ^{(l)})ᵀ δ^{(l+1)}:将后一层的误差加权求和(按权重反向传播)
2. ⊙ g'(z^{(l)}):乘以激活函数的导数(考虑本层的梯度衰减)
忘记实现细节后,反向传播的直观理解就是:如果输出层的预测有误差,这个误差按照连接权重的大小比例,反向分配给前一层各神经元——谁的权重大谁就承担更多的责任。
参数更新计算示例
以一个 3 层网络(2 个输入、3 个隐藏、1 个输出)为例,前向传播和反向传播的计算过程:
前向传播:
1. z₁² = Θ₁₀¹·x₀ + Θ₁₁¹·x₁ + Θ₁₂¹·x₂,a₁² = g(z₁²)
2. z₂² = Θ₂₀¹·x₀ + Θ₂₁¹·x₁ + Θ₂₂¹·x₂,a₂² = g(z₂²)
3. z₃² = Θ₃₀¹·x₀ + Θ₃₁¹·x₁ + Θ₃₂¹·x₂,a₃² = g(z₃²)
4. z₁³ = Θ₁₀²·a₀² + Θ₁₁²·a₁² + Θ₁₂²·a₂² + Θ₁₃²·a₃²,a₁³ = σ(z₁³) = ŷ
反向传播:
1. δ₁³ = a₁³ - y
2. δ₁² = Θ₁₁²·δ₁³ · g'(z₁²)
3. δ₂² = Θ₁₂²·δ₁³ · g'(z₂²)
4. δ₃² = Θ₁₃²·δ₁³ · g'(z₃²)
5. ∂J/∂Θ₁₁² = a₁² · δ₁³
梯度检验的详细实现
梯度检验是反向传播正确性的必要保障。以下是一个完整的检验实现:
def gradient_checking(theta, X, y, epsilon=1e-4):
# 计算反向传播梯度
grad_bp = backpropagation(theta, X, y)
# 计算数值梯度
grad_num = np.zeros_like(theta)
for i in range(len(theta)):
theta_plus = theta.copy()
theta_plus[i] += epsilon
theta_minus = theta.copy()
theta_minus[i] -= epsilon
loss_plus = cost_function(theta_plus, X, y)
loss_minus = cost_function(theta_minus, X, y)
grad_num[i] = (loss_plus - loss_minus) / (2 * epsilon)
# 计算相对误差
diff = np.linalg.norm(grad_bp - grad_num) / (np.linalg.norm(grad_bp) + np.linalg.norm(grad_num))
return diff
# 如果 diff < 1e-9,反向传播正确
梯度消失与梯度爆炸
梯度消失(Vanishing Gradient)和梯度爆炸(Exploding Gradient)是深度网络训练中的核心难题:
- 梯度消失:浅层梯度接近 0,参数几乎不更新。原因:激活函数饱和区梯度 < 1,连乘后指数衰减
- 梯度爆炸:浅层梯度极大,训练不稳定。原因:权重初始化过大
解决方案:合理的权重初始化(Xavier、He)、使用 ReLU 类激活函数、Batch Normalization、残差连接(ResNet)。
神经网络的超参数选择
- 隐藏层数量:从 1 层开始,根据验证集性能逐步增加
- 隐藏层神经元数:通常多于输入特征数,使用 2 的幂次方便内存对齐
- 学习率:最关键的参数,推荐从 0.001 开始调优
- 批量大小(Batch Size):通常 32、64、128、256
- 正则化参数 λ:从 0.01 到 10 的范围搜索
延伸阅读
- 📺 B 站播放列表:吴恩达机器学习 (2014) — 交互式学习笔记
- 📚 更多学习资源,请访问 deeplearning.ai 官网