李宏毅深度学习课程实战:从零搭建PyTorch模型(附作业解析)
李宏毅深度学习课程实战:从零搭建PyTorch模型(附作业解析)
深度学习正在重塑我们解决问题的思维方式。想象一下,当你第一次看到神经网络识别手写数字的演示时,那种"机器竟然能学习"的震撼感。PyTorch作为当前最受欢迎的深度学习框架之一,以其动态计算图和直观的API设计,成为了学术研究和工业实践的首选工具。本文将带你从零开始,用PyTorch实现李宏毅课程中的核心算法,不仅理解理论,更能亲手构建可运行的模型。
1. 环境准备与PyTorch基础
1.1 PyTorch安装与配置
在开始之前,我们需要搭建PyTorch开发环境。PyTorch支持多种安装方式,推荐使用conda进行管理:
conda create -n pytorch_env python=3.8
conda activate pytorch_env
conda install pytorch torchvision torchaudio -c pytorch
验证安装是否成功:
import torch
print(torch.__version__) # 应输出如1.12.1的版本号
print(torch.cuda.is_available()) # 检查GPU是否可用
提示:如果使用GPU加速训练,确保安装了对应版本的CUDA驱动。PyTorch官网提供了详细的版本匹配指南。
1.2 PyTorch核心概念
PyTorch的核心是其张量(Tensor)对象,可以理解为Numpy数组的GPU加速版本。理解以下几个关键特性对后续开发至关重要:
- 动态计算图:PyTorch使用动态图机制,允许在运行时修改网络结构
- 自动微分:autograd模块自动计算梯度,极大简化了反向传播实现
- 模块化设计:nn.Module提供了构建神经网络的基础抽象
# 简单的张量操作示例
x = torch.tensor([[1, 2], [3, 4]], dtype=torch.float32)
y = torch.randn(2, 2)
z = x.mm(y) # 矩阵乘法
print(z)
2. 线性回归与反向传播实现
2.1 数据准备与模型定义
让我们从最简单的线性回归开始,实现课程中的播放量预测案例。首先创建合成数据:
import numpy as np
# 生成训练数据
np.random.seed(42)
x_train = np.linspace(0, 10, 100)
y_train = 3 * x_train + 5 + np.random.normal(0, 2, 100)
# 转换为PyTorch张量
x_tensor = torch.from_numpy(x_train).float().view(-1, 1)
y_tensor = torch.from_numpy(y_train).float().view(-1, 1)
定义线性回归模型:
class LinearRegression(torch.nn.Module):
def __init__(self):
super().__init__()
self.linear = torch.nn.Linear(1, 1) # 输入输出维度均为1
def forward(self, x):
return self.linear(x)
2.2 训练循环与梯度下降
实现完整的训练流程,包括损失函数和优化器:
model = LinearRegression()
criterion = torch.nn.MSELoss() # 均方误差损失
optimizer = torch.optim.SGD(model.parameters(), lr=0.01) # 随机梯度下降
# 训练参数
epochs = 1000
for epoch in range(epochs):
# 前向传播
outputs = model(x_tensor)
loss = criterion(outputs, y_tensor)
# 反向传播与优化
optimizer.zero_grad() # 清空梯度
loss.backward() # 计算梯度
optimizer.step() # 更新参数
if (epoch+1) % 100 == 0:
print(f'Epoch [{epoch+1}/{epochs}], Loss: {loss.item():.4f}')
注意:每次反向传播前必须调用zero_grad(),否则梯度会累积导致训练不稳定。
2.3 作业解析:温度预测实战
李宏毅课程的第一个作业要求实现温度预测模型。我们可以扩展上面的线性回归模型:
class TempPredictor(torch.nn.Module):
def __init__(self, input_dim):
super().__init__()
self.net = torch.nn.Sequential(
torch.nn.Linear(input_dim, 64),
torch.nn.ReLU(),
torch.nn.Linear(64, 1)
)
def forward(self, x):
return self.net(x)
关键改进点:
- 增加了隐藏层和非线性激活
- 使用更复杂的网络结构捕捉非线性关系
- 添加了数据标准化预处理步骤
3. 卷积神经网络实战
3.1 CNN基础架构实现
课程中讲解的CNN结构可以用PyTorch轻松实现。以下是一个典型的图像分类网络:
class CNNClassifier(torch.nn.Module):
def __init__(self, num_classes):
super().__init__()
self.features = torch.nn.Sequential(
torch.nn.Conv2d(3, 32, kernel_size=3, padding=1),
torch.nn.ReLU(),
torch.nn.MaxPool2d(2),
torch.nn.Conv2d(32, 64, kernel_size=3, padding=1),
torch.nn.ReLU(),
torch.nn.MaxPool2d(2)
)
self.classifier = torch.nn.Sequential(
torch.nn.Linear(64*8*8, 128),
torch.nn.ReLU(),
torch.nn.Linear(128, num_classes)
)
def forward(self, x):
x = self.features(x)
x = x.view(x.size(0), -1) # 展平
return self.classifier(x)
3.2 数据增强与批标准化
为了提高模型泛化能力,我们需要实现数据增强和批标准化:
from torchvision import transforms
# 数据增强
train_transform = transforms.Compose([
transforms.RandomHorizontalFlip(),
transforms.RandomRotation(10),
transforms.ToTensor(),
transforms.Normalize(mean=[0.5], std=[0.5])
])
# 在模型中添加BatchNorm层
torch.nn.BatchNorm2d(64) # 在卷积层后添加
3.3 作业解析:宝可梦分类
针对课程中的宝可梦分类作业,我们可以构建更复杂的网络:
class PokemonCNN(torch.nn.Module):
def __init__(self):
super().__init__()
self.main = torch.nn.Sequential(
torch.nn.Conv2d(3, 32, 3, 1, 1),
torch.nn.BatchNorm2d(32),
torch.nn.ReLU(),
torch.nn.MaxPool2d(2, 2, 0),
torch.nn.Conv2d(32, 64, 3, 1, 1),
torch.nn.BatchNorm2d(64),
torch.nn.ReLU(),
torch.nn.MaxPool2d(2, 2, 0),
torch.nn.Conv2d(64, 128, 3, 1, 1),
torch.nn.BatchNorm2d(128),
torch.nn.ReLU(),
torch.nn.MaxPool2d(2, 2, 0)
)
self.fc = torch.nn.Sequential(
torch.nn.Linear(128*4*4, 256),
torch.nn.ReLU(),
torch.nn.Linear(256, 18) # 18个宝可梦类别
)
4. 自注意力机制与Transformer实现
4.1 Self-Attention基础实现
课程中讲解的自注意力机制可以用PyTorch实现如下:
class SelfAttention(torch.nn.Module):
def __init__(self, embed_size, heads):
super().__init__()
self.embed_size = embed_size
self.heads = heads
self.head_dim = embed_size // heads
self.values = torch.nn.Linear(self.head_dim, self.head_dim, bias=False)
self.keys = torch.nn.Linear(self.head_dim, self.head_dim, bias=False)
self.queries = torch.nn.Linear(self.head_dim, self.head_dim, bias=False)
self.fc_out = torch.nn.Linear(heads * self.head_dim, embed_size)
def forward(self, values, keys, query, mask):
N = query.shape[0]
value_len, key_len, query_len = values.shape[1], keys.shape[1], query.shape[1]
# 分割嵌入维度到多个头
values = values.reshape(N, value_len, self.heads, self.head_dim)
keys = keys.reshape(N, key_len, self.heads, self.head_dim)
queries = query.reshape(N, query_len, self.heads, self.head_dim)
# 计算注意力分数
energy = torch.einsum("nqhd,nkhd->nhqk", [queries, keys])
if mask is not None:
energy = energy.masked_fill(mask == 0, float("-1e20"))
attention = torch.nn.functional.softmax(energy / (self.embed_size ** (1/2)), dim=3)
# 应用注意力权重
out = torch.einsum("nhql,nlhd->nqhd", [attention, values])
out = out.reshape(N, query_len, self.heads * self.head_dim)
return self.fc_out(out)
4.2 Transformer编码器实现
基于自注意力机制,我们可以构建完整的Transformer编码器:
class TransformerBlock(torch.nn.Module):
def __init__(self, embed_size, heads, dropout, forward_expansion):
super().__init__()
self.attention = SelfAttention(embed_size, heads)
self.norm1 = torch.nn.LayerNorm(embed_size)
self.norm2 = torch.nn.LayerNorm(embed_size)
self.feed_forward = torch.nn.Sequential(
torch.nn.Linear(embed_size, forward_expansion * embed_size),
torch.nn.ReLU(),
torch.nn.Linear(forward_expansion * embed_size, embed_size)
)
self.dropout = torch.nn.Dropout(dropout)
def forward(self, value, key, query, mask):
attention = self.attention(value, key, query, mask)
x = self.dropout(self.norm1(attention + query))
forward = self.feed_forward(x)
out = self.dropout(self.norm2(forward + x))
return out
4.3 作业解析:序列标注任务
针对序列标注作业,我们可以构建基于Transformer的模型:
class SequenceTagger(torch.nn.Module):
def __init__(self, vocab_size, embed_size, num_classes, heads, num_layers):
super().__init__()
self.embedding = torch.nn.Embedding(vocab_size, embed_size)
self.position_embedding = PositionalEncoding(embed_size)
self.layers = torch.nn.ModuleList(
[TransformerBlock(embed_size, heads, dropout=0.1, forward_expansion=4)
for _ in range(num_layers)]
)
self.fc_out = torch.nn.Linear(embed_size, num_classes)
def forward(self, x, mask):
x = self.embedding(x)
x = self.position_embedding(x)
for layer in self.layers:
x = layer(x, x, x, mask)
return self.fc_out(x)
5. 模型调试与优化技巧
5.1 学习率调整策略
课程中强调的学习率问题可以通过多种策略解决:
# 学习率预热
optimizer = torch.optim.Adam(model.parameters(), lr=0)
scheduler = torch.optim.lr_scheduler.LambdaLR(
optimizer,
lr_lambda=lambda step: min((step+1)**-0.5, (step+1)*1000**-1.5)
)
# 余弦退火
scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=100)
5.2 梯度裁剪与早停
防止梯度爆炸和过拟合的实用技巧:
# 梯度裁剪
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
# 早停实现
best_loss = float('inf')
patience = 5
counter = 0
for epoch in range(epochs):
# ...训练代码...
if val_loss < best_loss:
best_loss = val_loss
counter = 0
torch.save(model.state_dict(), 'best_model.pth')
else:
counter += 1
if counter >= patience:
print("Early stopping")
break
5.3 可视化与调试工具
利用PyTorch内置工具和第三方库进行调试:
# 使用TensorBoard
from torch.utils.tensorboard import SummaryWriter
writer = SummaryWriter()
writer.add_graph(model, input_tensor)
writer.add_scalar('Loss/train', loss.item(), epoch)
# 使用torchviz可视化计算图
from torchviz import make_dot
make_dot(loss, params=dict(model.named_parameters()))
6. 实战项目:从理论到完整实现
6.1 图像分类完整流程
结合课程内容和PyTorch实现完整图像分类流程:
def train_model(model, train_loader, val_loader, epochs, device):
model.to(device)
criterion = torch.nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters())
for epoch in range(epochs):
model.train()
for images, labels in train_loader:
images, labels = images.to(device), labels.to(device)
optimizer.zero_grad()
outputs = model(images)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
# 验证阶段
model.eval()
with torch.no_grad():
correct = 0
total = 0
for images, labels in val_loader:
images, labels = images.to(device), labels.to(device)
outputs = model(images)
_, predicted = torch.max(outputs.data, 1)
total += labels.size(0)
correct += (predicted == labels).sum().item()
print(f'Epoch [{epoch+1}/{epochs}], Accuracy: {100*correct/total:.2f}%')
6.2 自然语言处理案例
实现课程中提到的文本分类任务:
class TextClassifier(torch.nn.Module):
def __init__(self, vocab_size, embed_dim, num_classes):
super().__init__()
self.embedding = torch.nn.Embedding(vocab_size, embed_dim)
self.lstm = torch.nn.LSTM(embed_dim, 128, batch_first=True, bidirectional=True)
self.fc = torch.nn.Linear(256, num_classes)
def forward(self, x):
x = self.embedding(x)
_, (hidden, _) = self.lstm(x)
out = torch.cat((hidden[-2], hidden[-1]), dim=1)
return self.fc(out)
6.3 模型部署与生产化
将训练好的模型部署为可用的服务:
from flask import Flask, request, jsonify
import torch
app = Flask(__name__)
model = load_model() # 加载训练好的模型
model.eval()
@app.route('/predict', methods=['POST'])
def predict():
data = request.get_json()
tensor = preprocess(data['input']) # 预处理输入
with torch.no_grad():
output = model(tensor)
return jsonify({'prediction': output.tolist()})
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
更多推荐


所有评论(0)