深度学习作为机器学习的重要分支,通过多层神经网络学习数据的层次化特征表示。本章从神经网络的基本原理出发,系统介绍深度学习的核心概念、训练方法和主流架构,为后续学习CNN、RNN和Transformer等高级模型奠定基础。
7.1 神经网络基础
7.1.1 感知机与多层感知机
感知机(Perceptron)是神经网络的基本单元,由Rosenblatt于1958年提出。感知机接收多个输入信号,通过加权求和并经过激活函数产生输出。数学表达式为:
其中, 是输入向量, 是权重向量, 是偏置项, 是激活函数。
单层感知机只能解决线性可分问题。对于非线性问题,需要引入多层感知机(Multi-Layer Perceptron, MLP)。MLP由输入层、一个或多个隐藏层和输出层组成,相邻层之间全连接。设网络有 层,第 层的计算为:
其中, 是权重矩阵, 是偏置向量, 是第 层的激活值。
万能近似定理(Universal Approximation Theorem)指出:一个具有足够多隐藏神经元的前馈神经网络,配合非线性激活函数,可以以任意精度逼近任意连续函数。这从理论上保证了神经网络的表达能力。
7.1.2 激活函数与选择
激活函数为神经网络引入非线性,使其能够学习复杂的模式。没有激活函数,多层网络将退化为单层线性模型。以下是主流激活函数的分析:
Sigmoid函数
Sigmoid将输入映射到 区间,适合输出概率的场景。但其存在明显的梯度消失问题:当 较大时,导数 趋近于0,导致深层网络训练困难。
Tanh函数
Tanh将输入映射到 ,输出零中心化,收敛速度优于Sigmoid。但同样存在梯度消失问题。
ReLU(Rectified Linear Unit)
ReLU是深度学习中最常用的激活函数。其优势包括:
- 计算简单,梯度不会饱和(正区间导数为1)
- 引入稀疏性,部分神经元输出为0
- 加速收敛,缓解梯度消失
但ReLU存在"神经元死亡"问题:当输入持续为负时,神经元永远不被激活,梯度始终为0。
Leaky ReLU
通过给负区间一个小的斜率 (通常0.01),解决神经元死亡问题。
GELU(Gaussian Error Linear Unit)
GELU被BERT、GPT等Transformer模型广泛采用。其特点包括:
- 平滑且处处可导,有利于优化
- 非单调性,能捕捉更复杂的模式
- 输出近似零中心化
实际实现常用近似公式:
SiLU(Swish)
SiLU与GELU性质相似,都是平滑的非单调函数。研究表明,SiLU和GELU可以作为ReLU的上位替代,在多数任务上表现更优。

图7-1 常见激活函数形状对比。从左到右、从上到下依次为Sigmoid、Tanh、ReLU、Leaky ReLU、GELU和SiLU。

图7-2 激活函数(左)及其导数(右)对比。Sigmoid和Tanh的导数在输入较大时迅速衰减,而ReLU、GELU和SiLU的梯度分布更均匀。
激活函数选择建议:
| 激活函数 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| ReLU | 计算快、缓解梯度消失 | 神经元死亡 | 隐藏层默认选择 |
| Leaky ReLU | 解决神经元死亡 | 需调参 | ReLU的改进版 |
| GELU | 平滑、性能好 | 计算稍复杂 | Transformer、BERT |
| SiLU | 平滑、非单调 | 计算稍复杂 | 现代CNN、LLM |
| Sigmoid | 输出概率 | 梯度消失 | 二分类输出层 |
| Tanh | 零中心化 | 梯度消失 | 循环神经网络 |
7.1.3 损失函数设计
损失函数衡量模型预测与真实值之间的差距,指导网络参数优化。
回归损失函数
均方误差(MSE):
MSE对异常值敏感,梯度随误差增大而线性增长。
平均绝对误差(MAE):
MAE对异常值更鲁棒,但在零点不可导。
Huber损失结合两者优点:
分类损失函数
交叉熵损失(Cross-Entropy):
其中, 是类别数, 是one-hot标签, 是softmax输出的概率。
对于二分类问题,二元交叉熵(BCE)为:
标签平滑(Label Smoothing)是一种正则化技术,将硬标签 替换为软标签 ,防止模型过度自信:
7.1.4 网络初始化策略
良好的参数初始化对训练深度网络至关重要。不当的初始化可能导致梯度消失或爆炸。
Xavier初始化(Glorot初始化):
假设激活函数是线性的,Xavier初始化使每层输出的方差与输入相等。对于均匀分布:
对于正态分布:
Xavier初始化适用于Sigmoid和Tanh等对称激活函数。
He初始化(Kaiming初始化):
针对ReLU激活函数设计,考虑ReLU将负值置零的特点:
He初始化是ReLU网络的默认选择,能有效缓解前向传播中方差衰减的问题。
PyTorch初始化示例:
import torch
import torch.nn as nn
# Xavier初始化
layer = nn.Linear(784, 256)
nn.init.xavier_uniform_(layer.weight)
nn.init.zeros_(layer.bias)
# He初始化
layer = nn.Linear(784, 256)
nn.init.kaiming_normal_(layer.weight, mode='fan_in', nonlinearity='relu')
nn.init.zeros_(layer.bias)
7.2 深度网络训练
7.2.1 训练流程与数据加载
深度学习训练遵循迭代优化范式:前向传播计算预测,反向传播更新参数。
数据加载:PyTorch提供 Dataset 和 DataLoader 实现高效数据加载:
from torch.utils.data import Dataset, DataLoader
class CustomDataset(Dataset):
def __init__(self, data, labels, transform=None):
self.data = data
self.labels = labels
self.transform = transform
def __len__(self):
return len(self.data)
def __getitem__(self, idx):
sample = self.data[idx]
label = self.labels[idx]
if self.transform:
sample = self.transform(sample)
return sample, label
# 创建DataLoader
dataset = CustomDataset(data, labels)
dataloader = DataLoader(dataset, batch_size=32, shuffle=True, num_workers=4)
训练循环基本结构:
for epoch in range(num_epochs):
model.train()
for batch_idx, (data, target) in enumerate(dataloader):
# 前向传播
output = model(data)
loss = criterion(output, target)
# 反向传播
optimizer.zero_grad()
loss.backward()
optimizer.step()
7.2.2 学习率调度策略
学习率是最重要的超参数之一。固定学习率往往不够灵活,学习率调度策略能显著提升训练效果。
StepLR:每隔固定步数衰减学习率
CosineAnnealing:按余弦曲线衰减
Warmup + Cosine:训练初期线性增加学习率,之后按余弦衰减
from torch.optim.lr_scheduler import StepLR, CosineAnnealingLR
# StepLR
scheduler = StepLR(optimizer, step_size=30, gamma=0.1)
# CosineAnnealing
scheduler = CosineAnnealingLR(optimizer, T_max=100, eta_min=1e-6)
# Warmup + Cosine (自定义实现)
class WarmupCosineScheduler:
def __init__(self, optimizer, warmup_epochs, total_epochs, base_lr, min_lr):
self.optimizer = optimizer
self.warmup_epochs = warmup_epochs
self.total_epochs = total_epochs
self.base_lr = base_lr
self.min_lr = min_lr
def step(self, epoch):
if epoch < self.warmup_epochs:
lr = self.base_lr * (epoch + 1) / self.warmup_epochs
else:
progress = (epoch - self.warmup_epochs) / (self.total_epochs - self.warmup_epochs)
lr = self.min_lr + 0.5 * (self.base_lr - self.min_lr) * (1 + np.cos(np.pi * progress))
for param_group in self.optimizer.param_groups:
param_group['lr'] = lr
return lr

图7-3 常见学习率调度策略对比。StepLR阶梯式衰减简单直接;CosineAnnealing平滑衰减;Warmup+Cosine结合两者优点,适合大规模训练。
7.2.3 梯度问题与解决方案
梯度消失(Vanishing Gradient):在深层网络中,梯度通过链式法则逐层传播时呈指数级衰减,导致浅层参数难以更新。
原因分析:对于Sigmoid激活函数,最大导数为0.25。在 层网络中,梯度最多衰减 。当 时,梯度衰减约 。
梯度爆炸(Exploding Gradient):与梯度消失相反,梯度在反向传播中指数级增长,导致参数更新过大,训练不稳定。

图7-4 梯度消失(左)与梯度爆炸(右)问题可视化。Sigmoid在深层网络中梯度迅速衰减至数值精度极限;梯度裁剪能有效控制梯度爆炸。
解决方案:
- 合适的激活函数:使用ReLU、GELU等梯度不会饱和的激活函数
- 批归一化(Batch Normalization):稳定每层的输入分布
- 残差连接(Residual Connection):提供梯度捷径
- 梯度裁剪(Gradient Clipping):限制梯度范数
- 更好的初始化:Xavier、He初始化
# 梯度裁剪
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
# 批归一化
class Net(nn.Module):
def __init__(self):
super().__init__()
self.fc1 = nn.Linear(784, 256)
self.bn1 = nn.BatchNorm1d(256)
self.fc2 = nn.Linear(256, 10)
def forward(self, x):
x = self.fc1(x)
x = self.bn1(x)
x = F.relu(x)
x = self.fc2(x)
return x
7.2.4 训练监控与可视化
有效的训练监控能帮助诊断问题、优化超参数。
TensorBoard:PyTorch内置的TensorBoard支持:
from torch.utils.tensorboard import SummaryWriter
writer = SummaryWriter('runs/experiment_1')
# 记录标量
writer.add_scalar('Loss/train', loss.item(), epoch)
writer.add_scalar('Accuracy/val', acc, epoch)
# 记录直方图
writer.add_histogram('weights/layer1', model.fc1.weight, epoch)
# 记录图像
writer.add_image('input', img_grid, epoch)
writer.close()
Weights & Biases (WandB):更强大的实验跟踪工具:
import wandb
wandb.init(project="my-project", config={"lr": 0.001, "batch_size": 32})
# 记录指标
wandb.log({"loss": loss.item(), "accuracy": acc})
# 保存模型
wandb.save("model.pt")

图7-5 典型训练过程可视化。训练损失和验证损失同步下降表明模型正常学习;验证准确率稳步提升,最终达到95%以上。
7.3 卷积神经网络基础
7.3.1 卷积操作原理
卷积神经网络(CNN)是处理网格结构数据(如图像)的专用神经网络。卷积操作通过局部感受野和权重共享有效提取空间特征。
二维卷积定义:
其中, 是输入特征图, 是卷积核(滤波器)。
关键超参数:
- 卷积核大小(Kernel Size):通常为3×3或5×5
- 步长(Stride):卷积核移动的步幅,控制输出尺寸
- 填充(Padding):在输入边缘补零,控制输出尺寸
输出尺寸计算公式:

图7-6 卷积操作原理可视化。左上为5×5输入特征图,中上为3×3卷积核,右上显示卷积操作过程,左下为输出特征图,中下展示步长为2的下采样效果,右下展示填充为1的尺寸保持效果。
感受野(Receptive Field):输出特征图上某一点对应输入图像的区域大小。感受野计算公式:
其中, 是第 层的卷积核大小, 是第 层的步长。
7.3.2 池化与下采样
池化操作降低特征图空间维度,减少参数量,提供平移不变性。
最大池化(Max Pooling):取池化窗口内的最大值
最大池化保留最强激活响应,对边缘和纹理敏感。
平均池化(Average Pooling):取池化窗口内的平均值
平均池化平滑特征,保留背景信息。

图7-7 池化操作对比。左图为4×4原始特征图,中图为2×2最大池化结果(保留每个区域的最大值),右图为2×2平均池化结果(保留每个区域的平均值)。
全局平均池化(Global Average Pooling, GAP):对整个特征图做平均,将每个特征图转化为一个数值,直接用于分类,减少全连接层参数量。
7.3.3 经典CNN架构
LeNet-5(1998):最早的CNN架构之一,由LeCun等人提出。包含两个卷积层和三个全连接层,用于手写数字识别。
AlexNet(2012):ImageNet竞赛冠军,开启深度学习革命。创新包括:
- ReLU激活函数
- Dropout正则化
- GPU并行训练
- 数据增强
VGGNet(2014):使用非常小的3×3卷积核堆叠,证明网络深度的重要性。VGG-16和VGG-19成为后续研究的基准架构。
| 架构 | 年份 | 层数 | 参数量 | 关键创新 |
|---|---|---|---|---|
| LeNet | 1998 | 8 | 60K | 卷积+池化+全连接 |
| AlexNet | 2012 | 8 | 60M | ReLU、Dropout、GPU训练 |
| VGG-16 | 2014 | 16 | 138M | 小卷积核(3×3)堆叠 |
| ResNet-50 | 2015 | 50 | 25M | 残差连接 |
7.4 循环神经网络基础
7.4.1 RNN结构与展开
循环神经网络(RNN)专门处理序列数据,通过隐藏状态传递历史信息。RNN的核心是循环连接,当前时刻的输出依赖于当前输入和上一时刻的隐藏状态。
RNN基本公式:
其中, 是时刻 的隐藏状态, 是输入, 是输出, 和 是激活函数。

图7-8 RNN展开结构(左)与LSTM门控结构(右)。RNN通过隐藏状态在时序间传递信息;LSTM通过遗忘门、输入门、输出门和细胞状态实现更精细的信息控制。
BPTT算法(Backpropagation Through Time):RNN的反向传播算法。将RNN在时间上展开,然后应用标准反向传播。由于梯度需要沿时间步传播,深层时间序列容易遇到梯度消失或爆炸问题。
7.4.2 LSTM与GRU
长短期记忆网络(LSTM)通过门控机制解决RNN的长期依赖问题。
LSTM核心组件:
-
遗忘门:决定丢弃多少历史信息
-
输入门:决定存储多少新信息
-
细胞状态更新:
-
输出门:决定输出什么
GRU(Gated Recurrent Unit)是LSTM的简化版本,合并细胞状态和隐藏状态,减少门控数量:
GRU参数更少,计算更快,在许多任务上与LSTM性能相当。
7.4.3 双向RNN与深层RNN
双向RNN(BiRNN):同时处理正向和反向序列,捕获双向上下文信息。
BiLSTM和BiGRU在自然语言处理任务中广泛应用。
深层RNN:堆叠多个RNN层,学习更复杂的时序模式。
# PyTorch实现
import torch.nn as nn
# 单层LSTM
lstm = nn.LSTM(input_size=128, hidden_size=256, num_layers=1, batch_first=True)
# 双向LSTM
bilstm = nn.LSTM(input_size=128, hidden_size=256, num_layers=2,
batch_first=True, bidirectional=True)
# GRU
gru = nn.GRU(input_size=128, hidden_size=256, num_layers=2, batch_first=True)
7.5 深度学习框架实践
7.5.1 PyTorch核心概念
PyTorch是当前最流行的深度学习框架之一,以其动态计算图和直观的API设计著称。
Tensor:PyTorch的基本数据结构,支持GPU加速。
import torch
# 创建Tensor
x = torch.tensor([[1, 2], [3, 4]], dtype=torch.float32)
x = torch.zeros(3, 3)
x = torch.randn(3, 3) # 标准正态分布
# GPU加速
if torch.cuda.is_available():
x = x.cuda()
# 或
device = torch.device('cuda')
x = x.to(device)
Autograd:自动微分系统,自动计算梯度。
x = torch.tensor([2.0], requires_grad=True)
y = x ** 2 + 3 * x + 1
y.backward()
print(x.grad) # dy/dx = 2x + 3 = 7
计算图:PyTorch使用动态计算图,每次前向传播都重新构建图,便于调试和条件控制。
7.5.2 模型定义与训练
nn.Module:所有神经网络模块的基类。
import torch
import torch.nn as nn
import torch.nn.functional as F
class NeuralNetwork(nn.Module):
def __init__(self, input_size, hidden_size, num_classes):
super(NeuralNetwork, self).__init__()
self.fc1 = nn.Linear(input_size, hidden_size)
self.bn1 = nn.BatchNorm1d(hidden_size)
self.dropout = nn.Dropout(0.3)
self.fc2 = nn.Linear(hidden_size, hidden_size)
self.bn2 = nn.BatchNorm1d(hidden_size)
self.fc3 = nn.Linear(hidden_size, num_classes)
def forward(self, x):
x = self.fc1(x)
x = self.bn1(x)
x = F.relu(x)
x = self.dropout(x)
x = self.fc2(x)
x = self.bn2(x)
x = F.relu(x)
x = self.dropout(x)
x = self.fc3(x)
return x
# 实例化模型
model = NeuralNetwork(784, 512, 10)
反向传播手动实现:
为了深入理解反向传播,以下是纯NumPy实现的简单神经网络:
import numpy as np
class SimpleNN:
def __init__(self, input_size, hidden_size, output_size):
# Xavier初始化
self.W1 = np.random.randn(input_size, hidden_size) * np.sqrt(2.0 / input_size)
self.b1 = np.zeros((1, hidden_size))
self.W2 = np.random.randn(hidden_size, output_size) * np.sqrt(2.0 / hidden_size)
self.b2 = np.zeros((1, output_size))
def relu(self, x):
return np.maximum(0, x)
def relu_derivative(self, x):
return (x > 0).astype(float)
def softmax(self, x):
exp_x = np.exp(x - np.max(x, axis=1, keepdims=True))
return exp_x / np.sum(exp_x, axis=1, keepdims=True)
def forward(self, X):
# 前向传播
self.z1 = np.dot(X, self.W1) + self.b1
self.a1 = self.relu(self.z1)
self.z2 = np.dot(self.a1, self.W2) + self.b2
self.a2 = self.softmax(self.z2)
return self.a2
def backward(self, X, y, learning_rate=0.01):
m = X.shape[0]
# 输出层梯度
dz2 = self.a2 - y
dW2 = np.dot(self.a1.T, dz2) / m
db2 = np.sum(dz2, axis=0, keepdims=True) / m
# 隐藏层梯度
dz1 = np.dot(dz2, self.W2.T) * self.relu_derivative(self.z1)
dW1 = np.dot(X.T, dz1) / m
db1 = np.sum(dz1, axis=0, keepdims=True) / m
# 参数更新
self.W2 -= learning_rate * dW2
self.b2 -= learning_rate * db2
self.W1 -= learning_rate * dW1
self.b1 -= learning_rate * db1
return np.mean(np.sum(dz2**2))
# 使用示例
np.random.seed(42)
X = np.random.randn(100, 784)
y = np.eye(10)[np.random.randint(0, 10, 100)]
model = SimpleNN(784, 256, 10)
output = model.forward(X)
loss = model.backward(X, y)
print(f"Loss: {loss:.4f}")
7.5.3 模型保存与部署
模型保存与加载:
# 保存整个模型
torch.save(model, 'model.pth')
model = torch.load('model.pth')
# 仅保存参数(推荐)
torch.save(model.state_dict(), 'model_weights.pth')
model.load_state_dict(torch.load('model_weights.pth'))
# 保存检查点(包含优化器状态)
checkpoint = {
'epoch': epoch,
'model_state_dict': model.state_dict(),
'optimizer_state_dict': optimizer.state_dict(),
'loss': loss,
}
torch.save(checkpoint, 'checkpoint.pth')
# 加载检查点
checkpoint = torch.load('checkpoint.pth')
model.load_state_dict(checkpoint['model_state_dict'])
optimizer.load_state_dict(checkpoint['optimizer_state_dict'])
ONNX导出:将PyTorch模型转换为ONNX格式,便于跨平台部署。
# 导出ONNX
dummy_input = torch.randn(1, 3, 224, 224)
torch.onnx.export(model, dummy_input, "model.onnx",
input_names=['input'],
output_names=['output'],
dynamic_axes={'input': {0: 'batch_size'},
'output': {0: 'batch_size'}})
MNIST分类完整训练代码:
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader
from torchvision import datasets, transforms
# 数据预处理
transform = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.1307,), (0.3081,))
])
# 加载MNIST数据集
train_dataset = datasets.MNIST('./data', train=True, download=True, transform=transform)
test_dataset = datasets.MNIST('./data', train=False, transform=transform)
train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=1000, shuffle=False)
# 定义模型
class MNISTNet(nn.Module):
def __init__(self):
super(MNISTNet, self).__init__()
self.conv1 = nn.Conv2d(1, 32, kernel_size=3, padding=1)
self.bn1 = nn.BatchNorm2d(32)
self.conv2 = nn.Conv2d(32, 64, kernel_size=3, padding=1)
self.bn2 = nn.BatchNorm2d(64)
self.pool = nn.MaxPool2d(2, 2)
self.dropout1 = nn.Dropout(0.25)
self.dropout2 = nn.Dropout(0.5)
self.fc1 = nn.Linear(64 * 7 * 7, 128)
self.fc2 = nn.Linear(128, 10)
def forward(self, x):
x = self.pool(nn.functional.relu(self.bn1(self.conv1(x))))
x = self.pool(nn.functional.relu(self.bn2(self.conv2(x))))
x = self.dropout1(x)
x = torch.flatten(x, 1)
x = nn.functional.relu(self.fc1(x))
x = self.dropout2(x)
x = self.fc2(x)
return nn.functional.log_softmax(x, dim=1)
# 训练设置
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model = MNISTNet().to(device)
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)
scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=5, gamma=0.5)
# 训练循环
def train(epoch):
model.train()
for batch_idx, (data, target) in enumerate(train_loader):
data, target = data.to(device), target.to(device)
optimizer.zero_grad()
output = model(data)
loss = criterion(output, target)
loss.backward()
optimizer.step()
if batch_idx % 100 == 0:
print(f'Train Epoch: {epoch} [{batch_idx * len(data)}/{len(train_loader.dataset)}'
f' ({100. * batch_idx / len(train_loader):.0f}%)]\tLoss: {loss.item():.6f}')
# 测试
def test():
model.eval()
test_loss = 0
correct = 0
with torch.no_grad():
for data, target in test_loader:
data, target = data.to(device), target.to(device)
output = model(data)
test_loss += criterion(output, target).item()
pred = output.argmax(dim=1, keepdim=True)
correct += pred.eq(target.view_as(pred)).sum().item()
test_loss /= len(test_loader)
accuracy = 100. * correct / len(test_dataset)
print(f'\nTest set: Average loss: {test_loss:.4f}, '
f'Accuracy: {correct}/{len(test_dataset)} ({accuracy:.2f}%)\n')
return accuracy
# 主训练循环
if __name__ == '__main__':
best_acc = 0
for epoch in range(1, 11):
train(epoch)
acc = test()
scheduler.step()
if acc > best_acc:
best_acc = acc
torch.save(model.state_dict(), 'best_mnist_model.pth')
print(f'Best accuracy: {best_acc:.2f}%')
以上代码实现了一个完整的MNIST分类器,包含卷积层、批归一化、Dropout正则化和学习率调度。在标准设置下,该模型可以达到99%以上的测试准确率。