1. 项目概述:AlexNet与猫狗分类实战
2012年,多伦多大学的Alex Krizhevsky等人提出的AlexNet在ImageNet竞赛中以压倒性优势夺冠,将Top-5错误率从26.2%降至15.3%,正式掀起了深度学习在计算机视觉领域的革命。这个8层卷积神经网络架构虽然现在看起来简单,但在当时通过ReLU激活函数、Dropout和数据增强等创新,成功解决了梯度消失和过拟合问题。
猫狗分类作为经典的二分类任务,是验证图像分类模型效果的理想试金石。我们使用的Kaggle数据集包含12,500张猫狗图片(各占50%),图像尺寸不统一且存在光照、姿态等差异,非常考验模型的特征提取能力。通过PyTorch框架实现AlexNet,我们不仅能理解CNN的基础工作原理,还能掌握数据预处理、模型训练调参等实用技巧。
提示:本实验需要至少6GB显存的GPU环境,若使用Colab需在"运行时"中选择T4或更高规格的GPU加速
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 环境配置与数据准备
2.1 基础环境搭建
推荐使用Python 3.8+和PyTorch 1.12+的组合,这是经过验证的稳定版本搭配。通过以下命令安装关键依赖:
bash复制pip install torch==1.12.1+cu113 torchvision==0.13.1+cu113 -f https://download.pytorch.org/whl/torch_stable.html
pip install opencv-python matplotlib tqdm
验证GPU是否可用:
python复制import torch
print(torch.cuda.is_available()) # 应输出True
print(torch.cuda.get_device_name(0)) # 显示显卡型号
2.2 数据集处理技巧
原始数据集需要按以下结构组织:
code复制data/
train/
cat/
cat001.jpg
...
dog/
dog001.jpg
...
val/
cat/
...
dog/
...
使用torchvision的ImageFolder自动加载数据时,建议采用这种增强组合:
python复制from torchvision import transforms
train_transform = transforms.Compose([
transforms.RandomResizedCrop(224),
transforms.RandomHorizontalFlip(),
transforms.ColorJitter(brightness=0.2, contrast=0.2),
transforms.ToTensor(),
transforms.Normalize(mean=[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225])
])
val_transform = transforms.Compose([
transforms.Resize(256),
transforms.CenterCrop(224),
transforms.ToTensor(),
transforms.Normalize(mean=[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225])
])
注意:Normalize参数使用ImageNet的统计值,这是经过验证的通用设置。如果数据集与ImageNet差异较大,可以重新计算均值和方差
3. AlexNet模型实现详解
3.1 网络架构拆解
原始AlexNet包含5个卷积层和3个全连接层,我们的实现做了以下适配调整:
python复制import torch.nn as nn
class AlexNet(nn.Module):
def __init__(self, num_classes=2):
super().__init__()
self.features = nn.Sequential(
nn.Conv2d(3, 64, kernel_size=11, stride=4, padding=2),
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=3, stride=2),
nn.Conv2d(64, 192, kernel_size=5, padding=2),
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=3, stride=2),
nn.Conv2d(192, 384, kernel_size=3, padding=1),
nn.ReLU(inplace=True),
nn.Conv2d(384, 256, kernel_size=3, padding=1),
nn.ReLU(inplace=True),
nn.Conv2d(256, 256, kernel_size=3, padding=1),
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=3, stride=2),
)
self.avgpool = nn.AdaptiveAvgPool2d((6, 6))
self.classifier = nn.Sequential(
nn.Dropout(),
nn.Linear(256 * 6 * 6, 4096),
nn.ReLU(inplace=True),
nn.Dropout(),
nn.Linear(4096, 4096),
nn.ReLU(inplace=True),
nn.Linear(4096, num_classes),
)
def forward(self, x):
x = self.features(x)
x = self.avgpool(x)
x = torch.flatten(x, 1)
x = self.classifier(x)
return x
关键改进点:
- 将原始两个并行GPU分支合并为单一结构
- 使用AdaptiveAvgPool替代固定尺寸池化,增强输入尺寸灵活性
- 输出层调整为2个类别(猫/狗)
3.2 模型初始化技巧
正确的参数初始化对收敛至关重要,推荐使用Kaiming初始化:
python复制def initialize_weights(m):
if isinstance(m, nn.Conv2d):
nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu')
if m.bias is not None:
nn.init.constant_(m.bias, 0)
elif isinstance(m, nn.Linear):
nn.init.normal_(m.weight, 0, 0.01)
nn.init.constant_(m.bias, 0)
model = AlexNet().to(device)
model.apply(initialize_weights)
4. 训练策略与调优实战
4.1 损失函数与优化器配置
交叉熵损失配合Adam优化器是当前最佳实践:
python复制criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=0.0001, weight_decay=1e-5)
scheduler = torch.optim.lr_scheduler.ReduceLROnPlateau(
optimizer, mode='min', factor=0.1, patience=3
)
学习率设置经验:
- 初始学习率:0.0001(比原始论文的0.01更保守)
- 当验证损失3个epoch不下降时,学习率乘以0.1
- weight_decay控制L2正则化强度,防止过拟合
4.2 训练循环实现
完整的训练流程应包含以下关键环节:
python复制def train_epoch(model, loader, optimizer, criterion, device):
model.train()
running_loss = 0.0
correct = 0
total = 0
for inputs, labels in tqdm(loader):
inputs, labels = inputs.to(device), labels.to(device)
optimizer.zero_grad()
outputs = model(inputs)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
running_loss += loss.item()
_, predicted = outputs.max(1)
total += labels.size(0)
correct += predicted.eq(labels).sum().item()
epoch_loss = running_loss / len(loader)
epoch_acc = 100. * correct / total
return epoch_loss, epoch_acc
验证阶段需特别注意:
python复制model.eval() # 切换评估模式
with torch.no_grad(): # 禁用梯度计算
# 验证代码...
4.3 训练监控与可视化
使用TensorBoard记录关键指标:
python复制from torch.utils.tensorboard import SummaryWriter
writer = SummaryWriter()
for epoch in range(epochs):
train_loss, train_acc = train_epoch(...)
val_loss, val_acc = validate(...)
writer.add_scalar('Loss/train', train_loss, epoch)
writer.add_scalar('Accuracy/train', train_acc, epoch)
writer.add_scalar('Loss/val', val_loss, epoch)
writer.add_scalar('Accuracy/val', val_acc, epoch)
scheduler.step(val_loss)
典型训练曲线分析:
- 理想情况:训练/验证损失同步下降,准确率同步上升
- 过拟合迹象:训练指标持续改善但验证指标停滞
- 欠拟合表现:两者都改善缓慢
5. 模型评估与优化技巧
5.1 性能评估指标
除准确率外,应关注:
python复制from sklearn.metrics import classification_report
report = classification_report(all_labels, all_preds,
target_names=['cat', 'dog'])
print(report)
关键指标解读:
- Precision:预测为猫/狗的样本中实际正确的比例
- Recall:实际猫/狗被正确预测的比例
- F1-score:Precision和Recall的调和平均
5.2 混淆矩阵分析
可视化分析常见错误:
python复制import seaborn as sns
from sklearn.metrics import confusion_matrix
cm = confusion_matrix(all_labels, all_preds)
sns.heatmap(cm, annot=True, fmt='d', cmap='Blues',
xticklabels=['cat', 'dog'],
yticklabels=['cat', 'dog'])
典型问题诊断:
- 猫狗严重混淆:需加强特征区分能力
- 特定类别识别差:可能数据不平衡或特征不足
5.3 模型优化策略
- 数据层面:
- 增加困难样本(模糊、遮挡等)
- 使用MixUp或CutMix增强
- 平衡类别分布
- 模型层面:
- 添加注意力机制(SE、CBAM)
- 使用更现代的激活函数(Swish)
- 尝试深度可分离卷积
- 训练技巧:
- 渐进式调整图像尺寸
- 使用标签平滑(Label Smoothing)
- 采用模型EMA(指数移动平均)
6. 常见问题与解决方案
6.1 训练不收敛排查
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| Loss波动大 | 学习率过高 | 逐步降低lr (1e-4 → 1e-5) |
| 准确率卡在50% | 数据标签错误 | 检查数据集shuffle情况 |
| 梯度爆炸 | 初始化不当 | 使用Kaiming初始化 |
6.2 过拟合处理方案
- 数据增强组合:
python复制transforms.RandomApply([
transforms.RandomRotation(10),
transforms.RandomAffine(0, shear=10),
transforms.RandomPerspective(),
], p=0.5)
- 正则化技术:
- Dropout率提高到0.5
- 增加weight_decay到1e-4
- 早停机制(patience=5)
- 模型简化:
- 减少全连接层维度(4096→2048)
- 移除最后一个卷积层
6.3 推理性能优化
- 模型轻量化:
python复制model = torch.jit.script(model) # TorchScript转换
torch.jit.save(model, 'alexnet_ts.pt')
- ONNX导出:
python复制dummy_input = torch.randn(1, 3, 224, 224).to(device)
torch.onnx.export(model, dummy_input, "alexnet.onnx",
input_names=['input'], output_names=['output'])
- TensorRT加速:
bash复制trtexec --onnx=alexnet.onnx --saveEngine=alexnet.engine \
--fp16 --workspace=2048
7. 扩展应用与改进方向
7.1 多类别分类扩展
修改最后一层即可支持更多类别:
python复制model.classifier[-1] = nn.Linear(4096, num_classes)
数据加载需保持类别文件夹结构:
code复制data/
train/
class1/
class2/
...
7.2 迁移学习实践
使用预训练权重加速收敛:
python复制from torchvision.models import alexnet
pretrained_model = alexnet(pretrained=True)
# 替换最后一层
pretrained_model.classifier[6] = nn.Linear(4096, 2)
冻结部分层技巧:
python复制for param in pretrained_model.features.parameters():
param.requires_grad = False
7.3 部署实践
Flask API示例:
python复制from flask import Flask, request, jsonify
import torchvision.transforms as T
app = Flask(__name__)
model = load_model()
transform = T.Compose([...])
@app.route('/predict', methods=['POST'])
def predict():
file = request.files['image']
img = Image.open(file.stream)
tensor = transform(img).unsqueeze(0)
with torch.no_grad():
output = model(tensor)
pred = torch.argmax(output).item()
return jsonify({'class': 'cat' if pred == 0 else 'dog'})
在实际部署中发现,将预处理和后处理与模型一起导出为ONNX/TensorRT能显著提升吞吐量。对于边缘设备,建议使用量化后的模型:
python复制model.qconfig = torch.quantization.get_default_qat_qconfig('fbgemm')
torch.quantization.prepare_qat(model, inplace=True)
# 量化训练...
torch.quantization.convert(model, inplace=True)
