手把手教你用PyTorch ResNet18完成图像分类(附预训练模型下载)
PyTorch实战:从零开始掌握ResNet18图像分类全流程
在计算机视觉领域,ResNet18作为轻量级残差网络的代表,凭借其优异的性能和适中的计算复杂度,成为入门深度学习的最佳选择之一。本文将带您完整走通使用PyTorch实现ResNet18图像分类的每个环节,从环境搭建到模型推理,特别针对初学者容易遇到的坑点提供解决方案。
1. 开发环境配置与工具准备
搭建合适的开发环境是项目成功的第一步。推荐使用Anaconda创建独立的Python环境,避免包版本冲突:
conda create -n pytorch_resnet python=3.8
conda activate pytorch_resnet
PyTorch的安装需要根据CUDA版本进行选择。对于没有NVIDIA显卡的用户,可以使用CPU版本:
pip install torch torchvision torchaudio --extra-index-url https://download.pytorch.org/whl/cu113
验证安装是否成功:
import torch
print(torch.__version__) # 应输出1.12.0及以上版本
print(torch.cuda.is_available()) # 检查CUDA是否可用
必备的辅助工具包包括:
pip install opencv-python matplotlib tqdm numpy pillow
提示:如果在Jupyter Notebook中运行代码,建议安装ipykernel并将新环境添加到Notebook选项:
python -m ipykernel install --user --name=pytorch_resnet
2. ResNet18模型加载与解析
PyTorch的torchvision.models模块提供了预定义的ResNet18实现,我们可以直接调用:
import torchvision.models as models
# 加载预训练模型(自动下载权重文件)
model = models.resnet18(pretrained=True)
model.eval() # 设置为评估模式
理解模型结构对后续调试至关重要。ResNet18的基本构成如下表所示:
| 层级名称 | 输出尺寸 | 组成模块 |
|---|---|---|
| conv1 | 112x112 | 7x7卷积, stride=2 |
| maxpool | 56x56 | 3x3最大池化 |
| layer1 | 56x56 | 2个基础残差块 |
| layer2 | 28x28 | 2个基础残差块 |
| layer3 | 14x14 | 2个基础残差块 |
| layer4 | 7x7 | 2个基础残差块 |
| avgpool | 1x1 | 全局平均池化 |
| fc | 1000 | 全连接层(ImageNet分类) |
查看模型各层参数的示例代码:
for name, param in model.named_parameters():
print(f"{name}: {param.shape}")
当需要自定义输入输出时(如处理非ImageNet数据集),可以修改最后的全连接层:
import torch.nn as nn
num_classes = 10 # 假设我们的数据集有10个类别
model.fc = nn.Linear(model.fc.in_features, num_classes)
3. 数据预处理与增强策略
图像数据的标准化处理对模型性能至关重要。ImageNet的标准化参数如下:
from torchvision import transforms
# 标准ImageNet预处理流程
transform = transforms.Compose([
transforms.Resize(256), # 缩放至256x256
transforms.CenterCrop(224), # 中心裁剪224x224
transforms.ToTensor(), # 转为Tensor
transforms.Normalize( # 标准化
mean=[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225]
)
])
数据增强能有效提升模型泛化能力。训练时可添加以下增强操作:
train_transform = transforms.Compose([
transforms.RandomResizedCrop(224),
transforms.RandomHorizontalFlip(),
transforms.ColorJitter(brightness=0.2, contrast=0.2, saturation=0.2),
transforms.RandomRotation(15),
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
])
使用torchvision.datasets加载自定义数据集的示例:
from torchvision.datasets import ImageFolder
dataset = ImageFolder('path/to/your/dataset', transform=transform)
dataloader = torch.utils.data.DataLoader(
dataset,
batch_size=32,
shuffle=True,
num_workers=4
)
注意:Windows系统下多进程数据加载(num_workers>0)可能存在问题,建议在if name == 'main'块中运行或设置为0
4. 模型训练与验证实现
完整的训练流程需要定义损失函数和优化器:
import torch.optim as optim
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=0.001, momentum=0.9)
训练循环的基本框架:
for epoch in range(num_epochs):
model.train()
running_loss = 0.0
for inputs, labels in train_loader:
optimizer.zero_grad()
outputs = model(inputs)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
running_loss += loss.item()
# 验证阶段
model.eval()
correct = 0
total = 0
with torch.no_grad():
for inputs, labels in val_loader:
outputs = model(inputs)
_, predicted = torch.max(outputs.data, 1)
total += labels.size(0)
correct += (predicted == labels).sum().item()
print(f'Epoch {epoch+1}, Loss: {running_loss/len(train_loader):.4f}, Accuracy: {100*correct/total:.2f}%')
学习率调度策略可以提升训练效果:
scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=7, gamma=0.1)
# 在每个epoch后调用 scheduler.step()
5. 模型推理与结果可视化
单张图片的预测示例:
from PIL import Image
def predict_image(image_path):
img = Image.open(image_path)
img_t = transform(img)
batch_t = torch.unsqueeze(img_t, 0)
with torch.no_grad():
out = model(batch_t)
_, index = torch.max(out, 1)
percentage = torch.nn.functional.softmax(out, dim=1)[0] * 100
return index.item(), percentage[index].item()
可视化模型关注区域的Grad-CAM实现:
import cv2
import numpy as np
def apply_grad_cam(model, img_tensor):
# 获取最后一层卷积层的输出和梯度
features = model.layer4
features.register_forward_hook(lambda m, i, o: features.__setattr__('output', o))
features.register_backward_hook(lambda m, gi, go: features.__setattr__('gradient', go[0]))
# 前向传播
output = model(img_tensor.unsqueeze(0))
pred_idx = output.argmax().item()
# 反向传播
model.zero_grad()
one_hot = torch.zeros_like(output)
one_hot[0][pred_idx] = 1
output.backward(gradient=one_hot)
# 计算权重并生成热力图
gradients = features.gradient[0].mean(dim=(1,2), keepdim=True)
activations = features.output[0]
cam = (activations * gradients).sum(0).relu().cpu().numpy()
# 归一化处理
cam = cv2.resize(cam, (224,224))
cam = (cam - cam.min()) / (cam.max() - cam.min())
return cam
在实际项目中,我经常遇到显存不足的问题。这时可以采用梯度累积技巧,通过多次小批量计算模拟大批量训练:
accumulation_steps = 4
optimizer.zero_grad()
for i, (inputs, labels) in enumerate(train_loader):
outputs = model(inputs)
loss = criterion(outputs, labels) / accumulation_steps
loss.backward()
if (i+1) % accumulation_steps == 0:
optimizer.step()
optimizer.zero_grad()
更多推荐
所有评论(0)