7095 字
35 分钟
深度学习入门指南

什么是深度学习?#

深度学习(Deep Learning)是机器学习的一个分支,它使用多层神经网络来学习数据的层次化表示。深度学习在计算机视觉、自然语言处理、语音识别等领域取得了突破性进展。

与传统机器学习方法不同,深度学习能够自动从原始数据中学习特征表示,无需人工特征工程。这使得深度学习在处理高维数据(如图像、语音、文本)时表现出色。

核心概念#

神经网络的数学基础#

1. 单个神经元#

一个神经元接收多个输入,对它们进行加权求和,然后通过激活函数产生输出。数学表达式为:

y=f(i=1nwixi+b)=f(wTx+b)y = f\left(\sum_{i=1}^{n} w_i x_i + b\right) = f(w^T x + b)

其中:

  • x=[x1,x2,...,xn]Tx = [x_1, x_2, ..., x_n]^T 是输入向量
  • w=[w1,w2,...,wn]Tw = [w_1, w_2, ..., w_n]^T 是权重向量
  • bb 是偏置项
  • ff 是激活函数
  • yy 是输出

2. 多层神经网络的矩阵表示#

对于一个 LL 层的神经网络,我们可以用矩阵运算来表示前向传播过程:

ll 层的前向传播:

Z[l]=W[l]A[l1]+b[l]A[l]=f[l](Z[l])\begin{aligned} Z^{[l]} &= W^{[l]} A^{[l-1]} + b^{[l]} \\ A^{[l]} &= f^{[l]}(Z^{[l]}) \end{aligned}

其中:

  • A[l]Rn[l]×mA^{[l]} \in \mathbb{R}^{n^{[l]} \times m} 是第 ll 层的激活值(mm 是样本数量)
  • W[l]Rn[l]×n[l1]W^{[l]} \in \mathbb{R}^{n^{[l]} \times n^{[l-1]}} 是第 ll 层的权重矩阵
  • b[l]Rn[l]×1b^{[l]} \in \mathbb{R}^{n^{[l]} \times 1} 是第 ll 层的偏置向量
  • Z[l]Z^{[l]} 是第 ll 层的线性组合
  • f[l]f^{[l]} 是第 ll 层的激活函数
  • A[0]=XA^{[0]} = X 是输入数据

激活函数详解#

激活函数为神经网络引入非线性,使其能够学习复杂的模式。

1. Sigmoid函数#

定义:

σ(z)=11+ez\sigma(z) = \frac{1}{1 + e^{-z}}

导数:

σ(z)=σ(z)(1σ(z))\sigma'(z) = \sigma(z)(1 - \sigma(z))

特点:

  • 输出范围:(0,1)(0, 1)
  • 适合二分类问题的输出层
  • 缺点:容易出现梯度消失问题

2. Tanh函数#

定义:

tanh(z)=ezezez+ez\tanh(z) = \frac{e^z - e^{-z}}{e^z + e^{-z}}

导数:

tanh(z)=1tanh2(z)\tanh'(z) = 1 - \tanh^2(z)

特点:

  • 输出范围:(1,1)(-1, 1)
  • 零中心化,收敛速度比Sigmoid快
  • 仍然存在梯度消失问题

3. ReLU(修正线性单元)#

定义:

ReLU(z)=max(0,z)\text{ReLU}(z) = \max(0, z)

导数:

ReLU(z)={1if z>00if z0\text{ReLU}'(z) = \begin{cases} 1 & \text{if } z > 0 \\ 0 & \text{if } z \leq 0 \end{cases}

特点:

  • 计算简单,训练速度快
  • 缓解梯度消失问题
  • 缺点:可能出现”神经元死亡”(dying ReLU)

4. Leaky ReLU#

定义:

Leaky ReLU(z)=max(0.01z,z)\text{Leaky ReLU}(z) = \max(0.01z, z)

特点:

  • 解决了ReLU的”神经元死亡”问题
  • 允许负值有小的梯度

反向传播算法:完整数学推导#

反向传播是训练神经网络的核心算法,它利用链式法则计算损失函数对每个参数的梯度。

1. 损失函数#

均方误差(MSE):

L(y,y^)=1mi=1m(y(i)y^(i))2L(y, \hat{y}) = \frac{1}{m} \sum_{i=1}^{m} (y^{(i)} - \hat{y}^{(i)})^2

交叉熵损失(二分类):

L(y,y^)=1mi=1m[y(i)log(y^(i))+(1y(i))log(1y^(i))]L(y, \hat{y}) = -\frac{1}{m} \sum_{i=1}^{m} \left[y^{(i)} \log(\hat{y}^{(i)}) + (1-y^{(i)}) \log(1-\hat{y}^{(i)})\right]

交叉熵损失(多分类):

L(y,y^)=1mi=1mj=1kyj(i)log(y^j(i))L(y, \hat{y}) = -\frac{1}{m} \sum_{i=1}^{m} \sum_{j=1}^{k} y_j^{(i)} \log(\hat{y}_j^{(i)})

2. 反向传播的链式法则#

对于第 ll 层的参数,我们需要计算:

LW[l]Lb[l]\frac{\partial L}{\partial W^{[l]}} \quad \text{和} \quad \frac{\partial L}{\partial b^{[l]}}

步骤1:计算输出层的误差

对于输出层 LL

δ[L]=LZ[L]=LA[L]f[L](Z[L])\delta^{[L]} = \frac{\partial L}{\partial Z^{[L]}} = \frac{\partial L}{\partial A^{[L]}} \odot f'^{[L]}(Z^{[L]})

其中 \odot 表示元素级乘法(Hadamard积)。

步骤2:反向传播误差

对于隐藏层 lll=L1,L2,...,1l = L-1, L-2, ..., 1):

δ[l]=(W[l+1])Tδ[l+1]f[l](Z[l])\delta^{[l]} = (W^{[l+1]})^T \delta^{[l+1]} \odot f'^{[l]}(Z^{[l]})

步骤3:计算梯度

LW[l]=1mδ[l](A[l1])TLb[l]=1mi=1mδ[l](i)\begin{aligned} \frac{\partial L}{\partial W^{[l]}} &= \frac{1}{m} \delta^{[l]} (A^{[l-1]})^T \\ \frac{\partial L}{\partial b^{[l]}} &= \frac{1}{m} \sum_{i=1}^{m} \delta^{[l](i)} \end{aligned}

步骤4:更新参数

W[l]:=W[l]αLW[l]b[l]:=b[l]αLb[l]\begin{aligned} W^{[l]} &:= W^{[l]} - \alpha \frac{\partial L}{\partial W^{[l]}} \\ b^{[l]} &:= b^{[l]} - \alpha \frac{\partial L}{\partial b^{[l]}} \end{aligned}

其中 α\alpha 是学习率。

优化算法详解#

1. 随机梯度下降(SGD)#

标准SGD:

θ:=θαθL(θ)\theta := \theta - \alpha \nabla_\theta L(\theta)

带动量的SGD(Momentum):

动量方法通过累积历史梯度来加速收敛并减少振荡。

vt=βvt1+(1β)θL(θ)θ:=θαvt\begin{aligned} v_t &= \beta v_{t-1} + (1-\beta) \nabla_\theta L(\theta) \\ \theta &:= \theta - \alpha v_t \end{aligned}

其中 β\beta 通常取 0.9,vtv_t 是速度(velocity)。

2. RMSprop#

RMSprop通过使用梯度平方的移动平均来自适应调整学习率。

st=βst1+(1β)(θL(θ))2θ:=θαst+ϵθL(θ)\begin{aligned} s_t &= \beta s_{t-1} + (1-\beta) (\nabla_\theta L(\theta))^2 \\ \theta &:= \theta - \frac{\alpha}{\sqrt{s_t + \epsilon}} \nabla_\theta L(\theta) \end{aligned}

其中 ϵ\epsilon 是一个很小的数(如 10810^{-8})用于防止除零。

3. Adam优化器#

Adam(Adaptive Moment Estimation)结合了动量和RMSprop的优点。

mt=β1mt1+(1β1)θL(θ)vt=β2vt1+(1β2)(θL(θ))2m^t=mt1β1tv^t=vt1β2tθ:=θαv^t+ϵm^t\begin{aligned} m_t &= \beta_1 m_{t-1} + (1-\beta_1) \nabla_\theta L(\theta) \\ v_t &= \beta_2 v_{t-1} + (1-\beta_2) (\nabla_\theta L(\theta))^2 \\ \hat{m}_t &= \frac{m_t}{1-\beta_1^t} \\ \hat{v}_t &= \frac{v_t}{1-\beta_2^t} \\ \theta &:= \theta - \frac{\alpha}{\sqrt{\hat{v}_t} + \epsilon} \hat{m}_t \end{aligned}

其中:

  • β1=0.9\beta_1 = 0.9(一阶矩估计的指数衰减率)
  • β2=0.999\beta_2 = 0.999(二阶矩估计的指数衰减率)
  • ϵ=108\epsilon = 10^{-8}
  • m^t\hat{m}_tv^t\hat{v}_t 是偏差修正后的估计

从零实现神经网络(NumPy)#

下面我们使用NumPy从零实现一个简单的两层神经网络,用于二分类问题。

import numpy as np
import matplotlib.pyplot as plt
class NeuralNetwork:
"""
两层神经网络实现
架构:输入层 -> 隐藏层 -> 输出层
"""
def __init__(self, input_size, hidden_size, output_size, learning_rate=0.01):
"""
初始化网络参数
参数:
input_size: 输入特征维度
hidden_size: 隐藏层神经元数量
output_size: 输出维度
learning_rate: 学习率
"""
# 使用He初始化权重
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))
self.learning_rate = learning_rate
def sigmoid(self, z):
"""Sigmoid激活函数"""
return 1 / (1 + np.exp(-np.clip(z, -500, 500))) # clip防止溢出
def sigmoid_derivative(self, z):
"""Sigmoid导数"""
s = self.sigmoid(z)
return s * (1 - s)
def relu(self, z):
"""ReLU激活函数"""
return np.maximum(0, z)
def relu_derivative(self, z):
"""ReLU导数"""
return (z > 0).astype(float)
def forward(self, X):
"""
前向传播
参数:
X: 输入数据,形状 (m, input_size)
返回:
A2: 输出预测,形状 (m, output_size)
"""
# 第一层:输入 -> 隐藏层
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.sigmoid(self.Z2)
return self.A2
def compute_loss(self, y_true, y_pred):
"""
计算二分类交叉熵损失
参数:
y_true: 真实标签,形状 (m, 1)
y_pred: 预测值,形状 (m, 1)
返回:
loss: 损失值
"""
m = y_true.shape[0]
epsilon = 1e-8 # 防止log(0)
loss = -np.mean(y_true * np.log(y_pred + epsilon) +
(1 - y_true) * np.log(1 - y_pred + epsilon))
return loss
def backward(self, X, y_true):
"""
反向传播
参数:
X: 输入数据,形状 (m, input_size)
y_true: 真实标签,形状 (m, output_size)
"""
m = X.shape[0]
# 计算输出层误差
dZ2 = self.A2 - y_true # 交叉熵+sigmoid的导数简化形式
dW2 = (1/m) * np.dot(self.A1.T, dZ2)
db2 = (1/m) * np.sum(dZ2, axis=0, keepdims=True)
# 计算隐藏层误差
dA1 = np.dot(dZ2, self.W2.T)
dZ1 = dA1 * self.relu_derivative(self.Z1)
dW1 = (1/m) * np.dot(X.T, dZ1)
db1 = (1/m) * np.sum(dZ1, axis=0, keepdims=True)
# 更新参数
self.W2 -= self.learning_rate * dW2
self.b2 -= self.learning_rate * db2
self.W1 -= self.learning_rate * dW1
self.b1 -= self.learning_rate * db1
def train(self, X, y, epochs=1000, verbose=True):
"""
训练网络
参数:
X: 训练数据,形状 (m, input_size)
y: 标签,形状 (m, output_size)
epochs: 训练轮数
verbose: 是否打印训练信息
返回:
losses: 每个epoch的损失值列表
"""
losses = []
for epoch in range(epochs):
# 前向传播
y_pred = self.forward(X)
# 计算损失
loss = self.compute_loss(y, y_pred)
losses.append(loss)
# 反向传播
self.backward(X, y)
# 打印训练信息
if verbose and (epoch % 100 == 0 or epoch == epochs - 1):
accuracy = np.mean((y_pred > 0.5) == y)
print(f"Epoch {epoch}/{epochs}, Loss: {loss:.4f}, Accuracy: {accuracy:.4f}")
return losses
def predict(self, X):
"""
预测
参数:
X: 输入数据,形状 (m, input_size)
返回:
predictions: 预测类别,形状 (m, 1)
"""
y_pred = self.forward(X)
return (y_pred > 0.5).astype(int)
# 示例:生成螺旋数据集并训练
def generate_spiral_data(n_points=100, n_classes=2):
"""生成螺旋分类数据"""
X = np.zeros((n_points * n_classes, 2))
y = np.zeros((n_points * n_classes, 1))
for class_num in range(n_classes):
ix = range(n_points * class_num, n_points * (class_num + 1))
r = np.linspace(0.0, 1, n_points)
t = np.linspace(class_num * 4, (class_num + 1) * 4, n_points) + \
np.random.randn(n_points) * 0.2
X[ix] = np.c_[r * np.sin(t), r * np.cos(t)]
y[ix] = class_num
return X, y
# 生成数据
X_train, y_train = generate_spiral_data(n_points=100, n_classes=2)
# 创建并训练网络
print("训练两层神经网络(NumPy实现)")
print("=" * 50)
nn = NeuralNetwork(input_size=2, hidden_size=10, output_size=1, learning_rate=0.5)
losses = nn.train(X_train, y_train, epochs=1000, verbose=True)
# 评估
predictions = nn.predict(X_train)
accuracy = np.mean(predictions == y_train)
print(f"\n最终训练准确率: {accuracy:.4f}")

输出示例:

训练两层神经网络(NumPy实现)
==================================================
Epoch 0/1000, Loss: 0.6932, Accuracy: 0.5000
Epoch 100/1000, Loss: 0.3251, Accuracy: 0.8850
Epoch 200/1000, Loss: 0.1823, Accuracy: 0.9400
Epoch 300/1000, Loss: 0.1245, Accuracy: 0.9600
...
Epoch 999/1000, Loss: 0.0431, Accuracy: 0.9900
最终训练准确率: 0.9900

PyTorch实战:MNIST手写数字识别#

现在我们使用PyTorch实现一个完整的MNIST手写数字识别项目。

import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader
from torchvision import datasets, transforms
import torch.nn.functional as F
# 设置随机种子以保证结果可复现
torch.manual_seed(42)
# 定义神经网络模型
class MNISTNet(nn.Module):
"""
用于MNIST数字识别的全连接神经网络
架构:784 -> 128 -> 64 -> 10
"""
def __init__(self):
super(MNISTNet, self).__init__()
# 定义网络层
self.fc1 = nn.Linear(28 * 28, 128) # 输入层到第一隐藏层
self.fc2 = nn.Linear(128, 64) # 第一隐藏层到第二隐藏层
self.fc3 = nn.Linear(64, 10) # 第二隐藏层到输出层
# Dropout层用于防止过拟合
self.dropout = nn.Dropout(0.2)
def forward(self, x):
"""
前向传播
参数:
x: 输入张量,形状 (batch_size, 1, 28, 28)
返回:
输出张量,形状 (batch_size, 10)
"""
# 展平图像:(batch_size, 1, 28, 28) -> (batch_size, 784)
x = x.view(-1, 28 * 28)
# 第一层:线性变换 + ReLU激活
x = F.relu(self.fc1(x))
x = self.dropout(x)
# 第二层:线性变换 + ReLU激活
x = F.relu(self.fc2(x))
x = self.dropout(x)
# 输出层:线性变换(不需要激活函数,因为我们使用CrossEntropyLoss)
x = self.fc3(x)
return x
def train_epoch(model, device, train_loader, optimizer, criterion, epoch):
"""
训练一个epoch
参数:
model: 神经网络模型
device: 设备(CPU或GPU)
train_loader: 训练数据加载器
optimizer: 优化器
criterion: 损失函数
epoch: 当前epoch编号
"""
model.train() # 设置为训练模式
train_loss = 0
correct = 0
total = 0
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()
# 统计
train_loss += loss.item()
_, predicted = output.max(1)
total += target.size(0)
correct += predicted.eq(target).sum().item()
# 打印训练信息
if batch_idx % 100 == 0:
print(f'Epoch: {epoch} [{batch_idx * len(data)}/{len(train_loader.dataset)} '
f'({100. * batch_idx / len(train_loader):.0f}%)]\t'
f'Loss: {loss.item():.6f}')
avg_loss = train_loss / len(train_loader)
accuracy = 100. * correct / total
print(f'\nTraining Set: Average loss: {avg_loss:.4f}, '
f'Accuracy: {correct}/{total} ({accuracy:.2f}%)\n')
return avg_loss, accuracy
def test(model, device, test_loader, criterion):
"""
测试模型
参数:
model: 神经网络模型
device: 设备(CPU或GPU)
test_loader: 测试数据加载器
criterion: 损失函数
返回:
平均损失和准确率
"""
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_loader.dataset)
print(f'Test Set: Average loss: {test_loss:.4f}, '
f'Accuracy: {correct}/{len(test_loader.dataset)} ({accuracy:.2f}%)\n')
return test_loss, accuracy
# 主训练流程
def main():
"""主函数:完整的MNIST训练流程"""
# 超参数设置
batch_size = 64
learning_rate = 0.001
epochs = 10
# 设置设备
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(f"使用设备: {device}\n")
# 数据预处理
transform = transforms.Compose([
transforms.ToTensor(), # 转换为张量
transforms.Normalize((0.1307,), (0.3081,)) # 标准化(MNIST的均值和标准差)
])
# 加载MNIST数据集
print("加载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=batch_size, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False)
print(f"训练集大小: {len(train_dataset)}")
print(f"测试集大小: {len(test_dataset)}\n")
# 创建模型
model = MNISTNet().to(device)
print("模型架构:")
print(model)
print()
# 定义损失函数和优化器
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=learning_rate)
# 学习率调度器(可选)
scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=5, gamma=0.5)
# 训练和测试
train_losses = []
test_losses = []
train_accuracies = []
test_accuracies = []
print("开始训练...")
print("=" * 70)
for epoch in range(1, epochs + 1):
# 训练
train_loss, train_acc = train_epoch(model, device, train_loader,
optimizer, criterion, epoch)
train_losses.append(train_loss)
train_accuracies.append(train_acc)
# 测试
test_loss, test_acc = test(model, device, test_loader, criterion)
test_losses.append(test_loss)
test_accuracies.append(test_acc)
# 更新学习率
scheduler.step()
print("-" * 70)
print("训练完成!")
print(f"最终测试准确率: {test_accuracies[-1]:.2f}%")
# 保存模型
torch.save(model.state_dict(), 'mnist_model.pth')
print("模型已保存到 mnist_model.pth")
return model, train_losses, test_losses, train_accuracies, test_accuracies
# 运行训练
if __name__ == "__main__":
model, train_losses, test_losses, train_accs, test_accs = main()

预期输出:

使用设备: cuda
加载MNIST数据集...
训练集大小: 60000
测试集大小: 10000
模型架构:
MNISTNet(
(fc1): Linear(in_features=784, out_features=128, bias=True)
(fc2): Linear(in_features=128, out_features=64, bias=True)
(fc3): Linear(in_features=64, out_features=10, bias=True)
(dropout): Dropout(p=0.2, inplace=False)
)
开始训练...
======================================================================
Epoch: 1 [0/60000 (0%)] Loss: 2.305841
...
Training Set: Average loss: 0.3421, Accuracy: 54231/60000 (90.39%)
Test Set: Average loss: 0.1823, Accuracy: 9456/10000 (94.56%)
...
训练完成!
最终测试准确率: 98.12%

卷积神经网络(CNN)详解#

卷积神经网络是专门为处理网格结构数据(如图像)设计的神经网络。

卷积层的数学原理#

1. 二维卷积运算#

对于输入 XRH×WX \in \mathbb{R}^{H \times W} 和卷积核 KRh×wK \in \mathbb{R}^{h \times w},卷积运算定义为:

(XK)i,j=m=0h1n=0w1Xi+m,j+nKm,n(X * K)_{i,j} = \sum_{m=0}^{h-1} \sum_{n=0}^{w-1} X_{i+m, j+n} \cdot K_{m,n}

2. 多通道卷积#

对于输入 XRCin×H×WX \in \mathbb{R}^{C_{in} \times H \times W} 和卷积核 KRCout×Cin×h×wK \in \mathbb{R}^{C_{out} \times C_{in} \times h \times w}

Ycout,i,j=cin=0Cin1m=0h1n=0w1Xcin,i+m,j+nKcout,cin,m,n+bcoutY_{c_{out}, i, j} = \sum_{c_{in}=0}^{C_{in}-1} \sum_{m=0}^{h-1} \sum_{n=0}^{w-1} X_{c_{in}, i+m, j+n} \cdot K_{c_{out}, c_{in}, m, n} + b_{c_{out}}

3. 输出尺寸计算#

给定:

  • 输入尺寸:Hin×WinH_{in} \times W_{in}
  • 卷积核大小:h×wh \times w
  • 步长(stride):ss
  • 填充(padding):pp

输出尺寸为:

Hout=Hin+2phs+1Wout=Win+2pws+1\begin{aligned} H_{out} &= \left\lfloor \frac{H_{in} + 2p - h}{s} \right\rfloor + 1 \\ W_{out} &= \left\lfloor \frac{W_{in} + 2p - w}{s} \right\rfloor + 1 \end{aligned}

池化层#

最大池化(Max Pooling)#

Yi,j=max0m<h,0n<wXis+m,js+nY_{i,j} = \max_{0 \leq m < h, 0 \leq n < w} X_{i \cdot s + m, j \cdot s + n}

平均池化(Average Pooling)#

Yi,j=1h×wm=0h1n=0w1Xis+m,js+nY_{i,j} = \frac{1}{h \times w} \sum_{m=0}^{h-1} \sum_{n=0}^{w-1} X_{i \cdot s + m, j \cdot s + n}

批归一化(Batch Normalization)#

批归一化通过标准化每一层的输入来加速训练并提高稳定性。

对于一个mini-batch B={x1,...,xm}\mathcal{B} = \{x_1, ..., x_m\}

步骤1:计算均值和方差

μB=1mi=1mxiσB2=1mi=1m(xiμB)2\begin{aligned} \mu_{\mathcal{B}} &= \frac{1}{m} \sum_{i=1}^{m} x_i \\ \sigma_{\mathcal{B}}^2 &= \frac{1}{m} \sum_{i=1}^{m} (x_i - \mu_{\mathcal{B}})^2 \end{aligned}

步骤2:标准化

x^i=xiμBσB2+ϵ\hat{x}_i = \frac{x_i - \mu_{\mathcal{B}}}{\sqrt{\sigma_{\mathcal{B}}^2 + \epsilon}}

步骤3:缩放和平移

yi=γx^i+βy_i = \gamma \hat{x}_i + \beta

其中 γ\gammaβ\beta 是可学习的参数,ϵ\epsilon 是防止除零的小常数。

PyTorch实战:CNN图像分类(CIFAR-10)#

现在我们使用PyTorch实现一个完整的CNN模型来处理CIFAR-10数据集。

import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader
from torchvision import datasets, transforms
import torch.nn.functional as F
# 定义CNN模型
class CIFAR10CNN(nn.Module):
"""
用于CIFAR-10图像分类的卷积神经网络
架构:Conv -> Conv -> Pool -> Conv -> Conv -> Pool -> FC -> FC
"""
def __init__(self):
super(CIFAR10CNN, self).__init__()
# 第一个卷积块
self.conv1 = nn.Conv2d(3, 32, kernel_size=3, padding=1)
# 输入: 3通道(RGB), 输出: 32通道, 卷积核: 3x3
self.bn1 = nn.BatchNorm2d(32)
self.conv2 = nn.Conv2d(32, 64, kernel_size=3, padding=1)
self.bn2 = nn.BatchNorm2d(64)
# 第一个池化层: 32x32 -> 16x16
self.pool1 = nn.MaxPool2d(kernel_size=2, stride=2)
# 第二个卷积块
self.conv3 = nn.Conv2d(64, 128, kernel_size=3, padding=1)
self.bn3 = nn.BatchNorm2d(128)
self.conv4 = nn.Conv2d(128, 128, kernel_size=3, padding=1)
self.bn4 = nn.BatchNorm2d(128)
# 第二个池化层: 16x16 -> 8x8
self.pool2 = nn.MaxPool2d(kernel_size=2, stride=2)
# 全连接层
self.fc1 = nn.Linear(128 * 8 * 8, 512)
self.dropout1 = nn.Dropout(0.5)
self.fc2 = nn.Linear(512, 10) # 10个类别
def forward(self, x):
"""
前向传播
参数:
x: 输入张量,形状 (batch_size, 3, 32, 32)
返回:
输出张量,形状 (batch_size, 10)
"""
# 第一个卷积块
x = self.conv1(x)
x = self.bn1(x)
x = F.relu(x)
x = self.conv2(x)
x = self.bn2(x)
x = F.relu(x)
x = self.pool1(x)
# 第二个卷积块
x = self.conv3(x)
x = self.bn3(x)
x = F.relu(x)
x = self.conv4(x)
x = self.bn4(x)
x = F.relu(x)
x = self.pool2(x)
# 展平
x = x.view(x.size(0), -1)
# 全连接层
x = self.fc1(x)
x = F.relu(x)
x = self.dropout1(x)
x = self.fc2(x)
return x
class ResidualBlock(nn.Module):
"""
残差块实现
包含两个卷积层和一个跳跃连接
"""
def __init__(self, in_channels, out_channels, stride=1):
super(ResidualBlock, self).__init__()
self.conv1 = nn.Conv2d(in_channels, out_channels, kernel_size=3,
stride=stride, padding=1, bias=False)
self.bn1 = nn.BatchNorm2d(out_channels)
self.conv2 = nn.Conv2d(out_channels, out_channels, kernel_size=3,
stride=1, padding=1, bias=False)
self.bn2 = nn.BatchNorm2d(out_channels)
# 如果输入输出维度不同,需要调整残差连接
self.shortcut = nn.Sequential()
if stride != 1 or in_channels != out_channels:
self.shortcut = nn.Sequential(
nn.Conv2d(in_channels, out_channels, kernel_size=1,
stride=stride, bias=False),
nn.BatchNorm2d(out_channels)
)
def forward(self, x):
"""
前向传播
实现: H(x) = F(x) + x
"""
# 主路径
out = F.relu(self.bn1(self.conv1(x)))
out = self.bn2(self.conv2(out))
# 残差连接
out += self.shortcut(x)
out = F.relu(out)
return out
class SimpleResNet(nn.Module):
"""
简化版ResNet用于CIFAR-10
"""
def __init__(self, num_classes=10):
super(SimpleResNet, self).__init__()
self.conv1 = nn.Conv2d(3, 64, kernel_size=3, stride=1, padding=1, bias=False)
self.bn1 = nn.BatchNorm2d(64)
# 残差块
self.layer1 = self._make_layer(64, 64, num_blocks=2, stride=1)
self.layer2 = self._make_layer(64, 128, num_blocks=2, stride=2)
self.layer3 = self._make_layer(128, 256, num_blocks=2, stride=2)
# 全局平均池化
self.avg_pool = nn.AdaptiveAvgPool2d((1, 1))
# 全连接层
self.fc = nn.Linear(256, num_classes)
def _make_layer(self, in_channels, out_channels, num_blocks, stride):
"""创建残差块层"""
layers = []
layers.append(ResidualBlock(in_channels, out_channels, stride))
for _ in range(1, num_blocks):
layers.append(ResidualBlock(out_channels, out_channels, stride=1))
return nn.Sequential(*layers)
def forward(self, x):
"""前向传播"""
out = F.relu(self.bn1(self.conv1(x)))
out = self.layer1(out)
out = self.layer2(out)
out = self.layer3(out)
out = self.avg_pool(out)
out = out.view(out.size(0), -1)
out = self.fc(out)
return out
def train_cifar10():
"""训练CIFAR-10分类器"""
# 超参数
batch_size = 128
learning_rate = 0.001
epochs = 50
# 设备
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(f"使用设备: {device}\n")
# 数据增强和预处理
transform_train = transforms.Compose([
transforms.RandomCrop(32, padding=4), # 随机裁剪
transforms.RandomHorizontalFlip(), # 随机水平翻转
transforms.ToTensor(),
transforms.Normalize((0.4914, 0.4822, 0.4465),
(0.2023, 0.1994, 0.2010)) # CIFAR-10的均值和标准差
])
transform_test = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.4914, 0.4822, 0.4465),
(0.2023, 0.1994, 0.2010))
])
# 加载数据集
print("加载CIFAR-10数据集...")
train_dataset = datasets.CIFAR10(root='./data', train=True,
download=True, transform=transform_train)
test_dataset = datasets.CIFAR10(root='./data', train=False,
download=True, transform=transform_test)
train_loader = DataLoader(train_dataset, batch_size=batch_size,
shuffle=True, num_workers=2)
test_loader = DataLoader(test_dataset, batch_size=batch_size,
shuffle=False, num_workers=2)
# CIFAR-10类别
classes = ('plane', 'car', 'bird', 'cat', 'deer',
'dog', 'frog', 'horse', 'ship', 'truck')
print(f"训练集大小: {len(train_dataset)}")
print(f"测试集大小: {len(test_dataset)}")
print(f"类别: {classes}\n")
# 创建模型(可以选择CIFAR10CNN或SimpleResNet)
# model = CIFAR10CNN().to(device)
model = SimpleResNet(num_classes=10).to(device)
print("模型架构:")
print(model)
print(f"\n总参数量: {sum(p.numel() for p in model.parameters()):,}")
print(f"可训练参数量: {sum(p.numel() for p in model.parameters() if p.requires_grad):,}\n")
# 损失函数和优化器
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=learning_rate, weight_decay=5e-4)
# 学习率调度器
scheduler = optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=epochs)
# 训练循环
best_acc = 0.0
print("开始训练...")
print("=" * 80)
for epoch in range(epochs):
# 训练阶段
model.train()
train_loss = 0
correct = 0
total = 0
for batch_idx, (inputs, targets) in enumerate(train_loader):
inputs, targets = inputs.to(device), targets.to(device)
optimizer.zero_grad()
outputs = model(inputs)
loss = criterion(outputs, targets)
loss.backward()
optimizer.step()
train_loss += loss.item()
_, predicted = outputs.max(1)
total += targets.size(0)
correct += predicted.eq(targets).sum().item()
if batch_idx % 100 == 0:
print(f'Epoch: {epoch+1}/{epochs} | Batch: {batch_idx}/{len(train_loader)} | '
f'Loss: {loss.item():.3f} | Acc: {100.*correct/total:.2f}%')
train_acc = 100. * correct / total
avg_train_loss = train_loss / len(train_loader)
# 测试阶段
model.eval()
test_loss = 0
correct = 0
total = 0
with torch.no_grad():
for inputs, targets in test_loader:
inputs, targets = inputs.to(device), targets.to(device)
outputs = model(inputs)
loss = criterion(outputs, targets)
test_loss += loss.item()
_, predicted = outputs.max(1)
total += targets.size(0)
correct += predicted.eq(targets).sum().item()
test_acc = 100. * correct / total
avg_test_loss = test_loss / len(test_loader)
print(f'\nEpoch {epoch+1}/{epochs}:')
print(f'Train Loss: {avg_train_loss:.3f} | Train Acc: {train_acc:.2f}%')
print(f'Test Loss: {avg_test_loss:.3f} | Test Acc: {test_acc:.2f}%')
# 保存最佳模型
if test_acc > best_acc:
print(f'保存模型... (准确率从 {best_acc:.2f}% 提升到 {test_acc:.2f}%)')
best_acc = test_acc
torch.save(model.state_dict(), 'best_cifar10_model.pth')
print('-' * 80)
# 更新学习率
scheduler.step()
print(f'\n训练完成! 最佳测试准确率: {best_acc:.2f}%')
return model
# 运行训练
if __name__ == "__main__":
model = train_cifar10()

预期输出:

使用设备: cuda
加载CIFAR-10数据集...
训练集大小: 50000
测试集大小: 10000
类别: ('plane', 'car', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck')
总参数量: 1,378,186
可训练参数量: 1,378,186
开始训练...
================================================================================
Epoch: 1/50 | Batch: 0/391 | Loss: 2.305 | Acc: 10.16%
...
Epoch 1/50:
Train Loss: 1.543 | Train Acc: 43.26%
Test Loss: 1.234 | Test Acc: 55.42%
保存模型... (准确率从 0.00% 提升到 55.42%)
...
Epoch 50/50:
Train Loss: 0.156 | Train Acc: 94.63%
Test Loss: 0.412 | Test Acc: 88.72%
训练完成! 最佳测试准确率: 89.15%

正则化技术详解#

正则化用于防止过拟合,提高模型的泛化能力。

1. L2正则化(权重衰减)#

在损失函数中添加权重的L2范数:

Ltotal=Ldata+λ2iwi2L_{total} = L_{data} + \frac{\lambda}{2} \sum_{i} w_i^2

对应的梯度更新:

w:=wα(Ldataw+λw)=(1αλ)wαLdataww := w - \alpha \left(\frac{\partial L_{data}}{\partial w} + \lambda w\right) = (1 - \alpha\lambda)w - \alpha\frac{\partial L_{data}}{\partial w}

2. L1正则化#

添加权重的L1范数,促进稀疏性:

Ltotal=Ldata+λiwiL_{total} = L_{data} + \lambda \sum_{i} |w_i|

3. Dropout数学原理#

Dropout在训练时随机丢弃神经元,丢弃概率为 pp

训练时:

h~i={0with probability phi1pwith probability 1p\tilde{h}_i = \begin{cases} 0 & \text{with probability } p \\ \frac{h_i}{1-p} & \text{with probability } 1-p \end{cases}

除以 (1p)(1-p) 是为了保持期望值不变。

测试时: 使用所有神经元,不需要缩放。

4. 早停法(Early Stopping)#

监控验证集损失,当验证集损失不再下降时停止训练,防止过拟合。

高级优化技巧#

学习率调度策略#

1. 步长衰减(Step Decay)#

αt=α0γt/k\alpha_t = \alpha_0 \cdot \gamma^{\lfloor t / k \rfloor}

其中 kk 是步长,γ\gamma 是衰减因子(如0.1)。

2. 指数衰减(Exponential Decay)#

αt=α0ekt\alpha_t = \alpha_0 \cdot e^{-kt}

3. 余弦退火(Cosine Annealing)#

αt=αmin+12(αmaxαmin)(1+cos(tTπ))\alpha_t = \alpha_{min} + \frac{1}{2}(\alpha_{max} - \alpha_{min})\left(1 + \cos\left(\frac{t}{T}\pi\right)\right)

其中 TT 是总的训练步数。

梯度裁剪(Gradient Clipping)#

防止梯度爆炸,限制梯度的范数:

按值裁剪:

g:=max(min(g,threshold),threshold)g := \max(\min(g, \text{threshold}), -\text{threshold})

按范数裁剪:

if g>threshold:g:=ggthreshold\text{if } \|g\| > \text{threshold}: \quad g := \frac{g}{\|g\|} \cdot \text{threshold}

主要应用领域#

计算机视觉#

深度学习在计算机视觉领域取得了革命性突破。

图像分类#

使用CNN对图像进行分类。经典架构包括:

  • AlexNet (2012):首次在ImageNet上使用深度CNN,top-5错误率15.3%
  • VGGNet (2014):使用更深的网络(16-19层),证明深度的重要性
  • ResNet (2015):引入残差连接,解决梯度消失问题,可训练152层
  • EfficientNet (2019):通过复合缩放优化网络效率

目标检测#

检测图像中的物体及其位置。主要方法:

  • R-CNN系列:Region-based CNN
  • YOLO:You Only Look Once,实时检测
  • SSD:Single Shot Detector
  • Faster R-CNN:使用RPN(Region Proposal Network)

图像分割#

像素级别的分类任务:

  • FCN:全卷积网络
  • U-Net:医学图像分割的经典架构
  • Mask R-CNN:实例分割
  • DeepLab:使用空洞卷积

人脸识别#

  • FaceNet:使用三元组损失学习人脸嵌入
  • DeepFace:Facebook的人脸识别系统
  • ArcFace:改进的损失函数,提高识别准确率

自然语言处理#

词嵌入(Word Embeddings)#

将词语映射到连续向量空间:

Word2Vec的Skip-gram模型目标函数:

max1Tt=1Tcjc,j0logp(wt+jwt)\max \frac{1}{T} \sum_{t=1}^{T} \sum_{-c \leq j \leq c, j \neq 0} \log p(w_{t+j} | w_t)

其中 cc 是上下文窗口大小。

循环神经网络(RNN)#

RNN能够处理序列数据,但存在梯度消失问题。

RNN的前向传播:

ht=tanh(Whhht1+Wxhxt+bh)yt=Whyht+by\begin{aligned} h_t &= \tanh(W_{hh} h_{t-1} + W_{xh} x_t + b_h) \\ y_t &= W_{hy} h_t + b_y \end{aligned}

LSTM(长短期记忆网络)#

LSTM通过门控机制解决长期依赖问题。

LSTM的数学表达:

ft=σ(Wf[ht1,xt]+bf)(遗忘门)it=σ(Wi[ht1,xt]+bi)(输入门)C~t=tanh(WC[ht1,xt]+bC)(候选值)Ct=ftCt1+itC~t(细胞状态)ot=σ(Wo[ht1,xt]+bo)(输出门)ht=ottanh(Ct)(隐藏状态)\begin{aligned} f_t &= \sigma(W_f \cdot [h_{t-1}, x_t] + b_f) \quad \text{(遗忘门)} \\ i_t &= \sigma(W_i \cdot [h_{t-1}, x_t] + b_i) \quad \text{(输入门)} \\ \tilde{C}_t &= \tanh(W_C \cdot [h_{t-1}, x_t] + b_C) \quad \text{(候选值)} \\ C_t &= f_t \odot C_{t-1} + i_t \odot \tilde{C}_t \quad \text{(细胞状态)} \\ o_t &= \sigma(W_o \cdot [h_{t-1}, x_t] + b_o) \quad \text{(输出门)} \\ h_t &= o_t \odot \tanh(C_t) \quad \text{(隐藏状态)} \end{aligned}

其中 σ\sigma 是sigmoid函数,\odot 是元素级乘法。

Transformer架构#

Transformer完全基于注意力机制,是当前NLP的主流架构。

自注意力机制(Self-Attention):

Attention(Q,K,V)=softmax(QKTdk)V\text{Attention}(Q, K, V) = \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right)V

其中:

  • QQ(Query)、KK(Key)、VV(Value)分别由输入通过线性变换得到
  • dkd_k 是key的维度
  • 除以 dk\sqrt{d_k} 是为了防止点积过大

多头注意力(Multi-Head Attention):

MultiHead(Q,K,V)=Concat(head1,...,headh)WOwhere headi=Attention(QWiQ,KWiK,VWiV)\begin{aligned} \text{MultiHead}(Q, K, V) &= \text{Concat}(\text{head}_1, ..., \text{head}_h)W^O \\ \text{where } \text{head}_i &= \text{Attention}(QW_i^Q, KW_i^K, VW_i^V) \end{aligned}

预训练语言模型#

  • BERT:双向Transformer,使用掩码语言模型预训练
  • GPT:生成式预训练,自回归语言模型
  • T5:Text-to-Text Transfer Transformer
  • GPT-4:大规模多模态模型

语音识别#

  • DeepSpeech:端到端语音识别
  • WaveNet:生成原始音频波形
  • Tacotron:文本转语音(TTS)
  • Whisper:OpenAI的多语言语音识别模型

学习路径#

1. 数学基础(2-3个月)#

线性代数#

  • 向量和矩阵运算
  • 特征值和特征向量
  • 矩阵分解(SVD、LU分解等)
  • 正交性和投影

微积分#

  • 导数和偏导数
  • 链式法则(反向传播的基础)
  • 梯度和Jacobian矩阵
  • 泰勒展开

概率论与统计#

  • 概率分布(高斯分布、伯努利分布等)
  • 期望和方差
  • 贝叶斯定理
  • 最大似然估计

2. 编程基础(1-2个月)#

Python核心#

# 必备Python技能
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
# NumPy数组操作
arr = np.array([[1, 2], [3, 4]])
print(arr.shape) # (2, 2)
print(arr.T) # 转置
print(arr @ arr.T) # 矩阵乘法
# 广播机制
a = np.array([1, 2, 3])
b = np.array([[1], [2], [3]])
print(a + b) # 广播
# Pandas数据处理
df = pd.DataFrame({
'A': [1, 2, 3],
'B': [4, 5, 6]
})
print(df.describe()) # 统计描述

3. 机器学习基础(1-2个月)#

监督学习#

  • 线性回归
  • 逻辑回归
  • 决策树和随机森林
  • 支持向量机(SVM)

非监督学习#

  • K-means聚类
  • PCA降维
  • 自编码器

模型评估#

  • 交叉验证
  • 混淆矩阵
  • ROC曲线和AUC
  • 精确率、召回率、F1分数

4. 深度学习框架(2-3个月)#

PyTorch基础#

import torch
import torch.nn as nn
# 张量操作
x = torch.tensor([[1, 2], [3, 4]], dtype=torch.float32)
print(x.shape)
print(x.device)
# 自动微分
x = torch.tensor([2.0], requires_grad=True)
y = x ** 2 + 3 * x + 1
y.backward()
print(x.grad) # dy/dx = 2x + 3 = 7
# 定义简单模型
model = nn.Sequential(
nn.Linear(10, 20),
nn.ReLU(),
nn.Linear(20, 1)
)
# 优化器
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
# 训练循环框架
for epoch in range(100):
# 前向传播
output = model(input_data)
loss = criterion(output, target)
# 反向传播
optimizer.zero_grad()
loss.backward()
optimizer.step()

4. 深度学习框架(2-3个月)#

PyTorch基础#

import torch
import torch.nn as nn
# 张量操作
x = torch.tensor([[1, 2], [3, 4]], dtype=torch.float32)
print(x.shape)
print(x.device)
# 自动微分
x = torch.tensor([2.0], requires_grad=True)
y = x ** 2 + 3 * x + 1
y.backward()
print(x.grad) # dy/dx = 2x + 3 = 7
# 定义简单模型
model = nn.Sequential(
nn.Linear(10, 20),
nn.ReLU(),
nn.Linear(20, 1)
)
# 优化器
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
# 训练循环框架
for epoch in range(100):
# 前向传播
output = model(input_data)
loss = criterion(output, target)
# 反向传播
optimizer.zero_grad()
loss.backward()
optimizer.step()

迁移学习:利用预训练模型#

迁移学习让我们可以利用在大规模数据集上预训练的模型,快速解决新问题。

使用预训练ResNet进行图像分类#

import torch
import torch.nn as nn
import torchvision.models as models
from torchvision import transforms
# 加载预训练的ResNet50模型
model = models.resnet50(pretrained=True)
# 冻结所有层的参数
for param in model.parameters():
param.requires_grad = False
# 替换最后的全连接层
num_features = model.fc.in_features
num_classes = 10 # 你的数据集类别数
model.fc = nn.Linear(num_features, num_classes)
# 只训练最后一层
optimizer = torch.optim.Adam(model.fc.parameters(), lr=0.001)
# 或者,微调整个网络(学习率要小)
for param in model.parameters():
param.requires_grad = True
optimizer = torch.optim.Adam([
{'params': model.fc.parameters(), 'lr': 0.001},
{'params': model.layer4.parameters(), 'lr': 0.0001},
{'params': model.layer3.parameters(), 'lr': 0.00001}
])

模型评估与可视化#

混淆矩阵#

from sklearn.metrics import confusion_matrix, classification_report
import seaborn as sns
import matplotlib.pyplot as plt
def evaluate_model(model, test_loader, device, class_names):
"""评估模型并生成混淆矩阵"""
model.eval()
all_preds = []
all_labels = []
with torch.no_grad():
for inputs, labels in test_loader:
inputs, labels = inputs.to(device), labels.to(device)
outputs = model(inputs)
_, preds = outputs.max(1)
all_preds.extend(preds.cpu().numpy())
all_labels.extend(labels.cpu().numpy())
# 计算混淆矩阵
cm = confusion_matrix(all_labels, all_preds)
# 绘制混淆矩阵
plt.figure(figsize=(10, 8))
sns.heatmap(cm, annot=True, fmt='d', cmap='Blues',
xticklabels=class_names,
yticklabels=class_names)
plt.title('Confusion Matrix')
plt.ylabel('True Label')
plt.xlabel('Predicted Label')
plt.tight_layout()
plt.savefig('confusion_matrix.png', dpi=150)
# 打印分类报告
print(classification_report(all_labels, all_preds,
target_names=class_names))
return cm
# 使用示例
# cm = evaluate_model(model, test_loader, device,
# ['class1', 'class2', 'class3'])

特征可视化#

def visualize_features(model, image, device, layer_name='layer4'):
"""可视化CNN的特征图"""
model.eval()
# 注册钩子以获取中间层输出
features = []
def hook(module, input, output):
features.append(output)
# 获取指定层
target_layer = dict(model.named_modules())[layer_name]
handle = target_layer.register_forward_hook(hook)
# 前向传播
with torch.no_grad():
image = image.unsqueeze(0).to(device)
_ = model(image)
# 移除钩子
handle.remove()
# 可视化特征图
feature_maps = features[0].squeeze(0).cpu()
n_features = min(64, feature_maps.shape[0])
fig, axes = plt.subplots(8, 8, figsize=(12, 12))
for i, ax in enumerate(axes.flat):
if i < n_features:
ax.imshow(feature_maps[i], cmap='viridis')
ax.axis('off')
else:
ax.axis('off')
plt.tight_layout()
plt.savefig('feature_maps.png', dpi=150)
print(f"特征图已保存到 feature_maps.png")
# 使用示例
# visualize_features(model, test_image, device, layer_name='layer3')

常见问题与解决方案#

1. 过拟合#

症状: 训练准确率高,验证准确率低

解决方案:

  • 增加训练数据或使用数据增强
  • 使用正则化(L2、Dropout)
  • 减小模型复杂度
  • 早停法(Early Stopping)

2. 欠拟合#

症状: 训练和验证准确率都低

解决方案:

  • 增加模型复杂度(更多层或更多神经元)
  • 训练更长时间
  • 减少正则化强度
  • 检查数据质量

3. 梯度消失/爆炸#

解决方案:

  • 使用ReLU等激活函数
  • 使用批归一化
  • 使用残差连接
  • 梯度裁剪(针对梯度爆炸)
  • 合适的权重初始化

4. 训练速度慢#

解决方案:

  • 使用更大的batch size(如果GPU内存允许)
  • 使用更快的优化器(如Adam)
  • 混合精度训练
  • 使用更快的数据加载(num_workers)
  • 使用分布式训练

实战技巧总结#

超参数调优#

# 常用超参数范围
hyperparameters = {
'learning_rate': [0.1, 0.01, 0.001, 0.0001],
'batch_size': [16, 32, 64, 128],
'optimizer': ['SGD', 'Adam', 'RMSprop'],
'weight_decay': [0, 1e-5, 1e-4, 1e-3],
'dropout': [0.0, 0.2, 0.5]
}
# 网格搜索或随机搜索
import itertools
def grid_search(train_loader, val_loader, param_grid):
"""简单的网格搜索实现"""
best_acc = 0
best_params = None
# 生成所有参数组合
keys = param_grid.keys()
values = param_grid.values()
for params in itertools.product(*values):
param_dict = dict(zip(keys, params))
print(f"Testing: {param_dict}")
# 创建并训练模型
model = create_model()
val_acc = train_and_validate(model, param_dict,
train_loader, val_loader)
if val_acc > best_acc:
best_acc = val_acc
best_params = param_dict
return best_params, best_acc

模型保存与加载#

# 保存完整模型
torch.save(model, 'complete_model.pth')
# 只保存参数(推荐)
torch.save(model.state_dict(), 'model_weights.pth')
# 保存训练状态(用于断点续训)
checkpoint = {
'epoch': epoch,
'model_state_dict': model.state_dict(),
'optimizer_state_dict': optimizer.state_dict(),
'loss': loss,
'best_acc': best_acc
}
torch.save(checkpoint, 'checkpoint.pth')
# 加载模型
model = TheModelClass()
model.load_state_dict(torch.load('model_weights.pth'))
model.eval() # 设置为评估模式
# 加载检查点
checkpoint = torch.load('checkpoint.pth')
model.load_state_dict(checkpoint['model_state_dict'])
optimizer.load_state_dict(checkpoint['optimizer_state_dict'])
epoch = checkpoint['epoch']
loss = checkpoint['loss']

实践建议#

  1. 从小项目开始:选择简单的数据集(如MNIST)进行练习
  2. 理解原理:不要只调用API,要理解背后的数学原理
  3. 动手实现:尝试从零实现简单的神经网络
  4. 阅读论文:关注顶会论文(CVPR、NeurIPS、ICML等)
  5. 参与竞赛:在Kaggle等平台上参与实际项目

推荐资源#

在线课程#

  • 吴恩达的深度学习专项课程
  • Fast.ai实用深度学习课程
  • CS231n:卷积神经网络视觉识别

书籍推荐#

  • 《深度学习》(Goodfellow等著)
  • 《动手学深度学习》
  • 《Python深度学习》

社区资源#

  • GitHub开源项目
  • Hugging Face模型库
  • Papers with Code

总结#

深度学习是一个快速发展的领域,需要持续学习和实践。从基础开始,循序渐进,注重理论与实践相结合,就能在这个领域取得进步。

记住:实践是学习深度学习最好的方式!

深度学习入门指南
https://myblog-590.pages.dev/posts/deep-learning/
作者
谢星宇
发布于
2026-01-19
许可协议
CC BY-NC-SA 4.0