共计 3463 个字符,预计需要花费 9 分钟才能阅读完成。
背景与痛点:梯度消失问题
在传统神经网络中,随着网络层数的增加,误差信号在反向传播过程中会逐渐减弱,导致深层网络的权重更新非常缓慢甚至停止。这种现象被称为梯度消失问题。具体来说,当使用 sigmoid 激活函数时,其导数最大值为 0.25,这意味着每经过一层,梯度至少会缩小为原来的 1 /4。对于一个 10 层的网络,梯度可能缩小到(1/4)^10 ≈ 0.00000095,几乎可以忽略不计。

数学表达式为:
$$
\frac{\partial L}{\partial w^{(l)}} \approx \prod_{k=l}^{L-1} \sigma'(z^{(k)})w^{(k+1)T} \cdot \frac{\partial L}{\partial w^{(L)}}
$$
技术突破:深度信念网络 (DBN) 的层次预训练
Hinton 等人提出的解决方案是采用无监督的逐层预训练策略。DBN 由多个受限玻尔兹曼机 (RBM) 堆叠而成,训练过程分为两个阶段:
- 逐层贪婪训练:自底向上逐层训练 RBM,将前一层的输出作为后一层的输入
- 全局微调:使用反向传播算法对整个网络进行微调
这种方法的优势在于:
- 无监督预训练可以学习到数据的有用表示
- 逐层训练避免了直接优化深层网络的困难
- 预训练得到的权重为后续微调提供了良好的初始化
实现对比:与传统反向传播的差异
传统反向传播直接从随机初始化开始训练整个网络,而 DBN 的训练方式有显著不同:
- 训练顺序:DBN 是逐层训练,传统方法是端到端训练
- 初始化方式:DBN 使用预训练权重初始化,传统方法使用随机初始化
- 收敛速度:DBN 通常需要更少的微调迭代次数
- 数据需求:DBN 可以利用无标签数据进行预训练
代码实战:PyTorch 实现 DBN 示例
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader
class RBM(nn.Module):
"""受限玻尔兹曼机实现"""
def __init__(self, visible_dim, hidden_dim):
super(RBM, self).__init__()
self.W = nn.Parameter(torch.randn(hidden_dim, visible_dim) * 0.01)
self.v_bias = nn.Parameter(torch.zeros(visible_dim))
self.h_bias = nn.Parameter(torch.zeros(hidden_dim))
def forward(self, v):
"""前向传播:计算隐藏层激活概率"""
p_h = torch.sigmoid(F.linear(v, self.W, self.h_bias))
return p_h
def sample_h(self, v):
"""从隐藏层采样"""
p_h = self.forward(v)
return torch.bernoulli(p_h)
def sample_v(self, h):
"""从可见层采样"""
p_v = torch.sigmoid(F.linear(h, self.W.t(), self.v_bias))
return torch.bernoulli(p_v)
class DBN(nn.Module):
"""深度信念网络实现"""
def __init__(self, layer_dims):
super(DBN, self).__init__()
self.rbms = nn.ModuleList([RBM(layer_dims[i], layer_dims[i+1])
for i in range(len(layer_dims)-1)
])
self.classifier = nn.Linear(layer_dims[-1], 10) # 假设是 10 分类任务
def pretrain(self, train_loader, epochs=10, lr=0.01):
"""逐层预训练"""
for i, rbm in enumerate(self.rbms):
print(f"Training RBM layer {i+1}/{len(self.rbms)}")
optimizer = optim.SGD(rbm.parameters(), lr=lr)
for epoch in range(epochs):
for batch, _ in train_loader:
batch = batch.view(-1, layer_dims[i])
# 对比散度 (CD) 算法
v0 = batch
h0 = rbm.sample_h(v0)
v1 = rbm.sample_v(h0)
h1 = rbm.sample_h(v1)
# 更新参数
pos_grad = torch.matmul(h0.t(), v0)
neg_grad = torch.matmul(h1.t(), v1)
loss = torch.mean((v0 - v1)**2)
optimizer.zero_grad()
rbm.W.grad = -(pos_grad - neg_grad) / batch.size(0)
rbm.v_bias.grad = -(torch.mean(v0, dim=0) - torch.mean(v1, dim=0))
rbm.h_bias.grad = -(torch.mean(h0, dim=0) - torch.mean(h1, dim=0))
optimizer.step()
print(f"Epoch {epoch+1}, Loss: {loss.item():.4f}")
def finetune(self, train_loader, epochs=20, lr=0.001):
"""全局微调"""
optimizer = optim.Adam(self.parameters(), lr=lr)
criterion = nn.CrossEntropyLoss()
for epoch in range(epochs):
total_loss = 0
for batch, labels in train_loader:
# 前向传播
h = batch.view(-1, layer_dims[0])
for rbm in self.rbms:
h = rbm.forward(h)
output = self.classifier(h)
# 反向传播
loss = criterion(output, labels)
optimizer.zero_grad()
loss.backward()
optimizer.step()
total_loss += loss.item()
print(f"Finetune Epoch {epoch+1}, Loss: {total_loss/len(train_loader):.4f}")
性能考量:预训练的影响
预训练对模型性能的影响主要体现在:
- 收敛速度:预训练后的模型通常需要更少的微调迭代次数就能达到较好性能
- 泛化能力:预训练学习到的特征表示往往具有更好的泛化性
- 数据效率:在标注数据有限的情况下,预训练可以显著提升模型性能
实验表明,在某些任务上,经过预训练的 DBN 可以达到比随机初始化网络高 10-20% 的准确率,同时减少约 30-50% 的训练时间。
避坑指南:常见问题与解决方案
- 预训练不收敛
- 可能原因:学习率设置不当
-
解决方案:尝试更小的学习率(如 0.001-0.01)
-
微调阶段性能下降
- 可能原因:预训练和微调的学习目标不一致
-
解决方案:适当减少预训练 epoch 数,或增加微调 epoch 数
-
梯度爆炸
- 可能原因:权重初始化过大
-
解决方案:使用更小的随机初始化范围(如标准差 0.01)
-
过拟合
- 可能原因:模型容量过大
- 解决方案:添加 dropout 层或 L2 正则化
延伸思考:现代深度学习的发展
Hinton 的这项开创性工作为现代深度学习奠定了基础,许多后续发展都可以看作是对这一思想的扩展:
- 自编码器:可以看作是对 RBM 的简化版本
- 深度残差网络:通过跳跃连接缓解梯度消失问题
- 迁移学习:预训练 - 微调范式的扩展应用
- 自监督学习:利用无标签数据进行预训练的新形式
进一步探索的问题
- 除了 RBM,还有哪些无监督学习模型可以用于深度网络的预训练?
- 在当今大数据时代,预训练是否仍然像在 2006 年那样重要?为什么?
- 如何将 DBN 的思想应用于计算机视觉或自然语言处理以外的领域?
通过理解 Hinton 等人 2006 年的这项开创性工作,我们不仅能更好地掌握深度学习的核心思想,也能更清楚地看到现代各种神经网络架构的演进脉络。虽然如今我们有更先进的训练技巧和网络结构,但解决梯度消失问题的基本思路仍然在这些方法中延续。
