Kaggle狗品种识别实战:用PyTorch微调ResNet50,从数据整理到提交完整流程
Kaggle狗品种识别实战从数据探索到模型优化的全流程解析第一次接触Kaggle的狗品种识别竞赛时我被120种犬类的细微差异难住了——贵宾犬和比熊犬的卷毛、哈士奇和阿拉斯加的面部特征这些对人类都容易混淆的细节机器该如何区分这个项目不仅教会了我PyTorch微调技巧更让我理解了计算机视觉在实际应用中的挑战与乐趣。下面将完整分享从数据准备到模型提交的每个关键环节特别适合想要系统掌握图像分类实战技能的朋友。1. 数据准备与探索性分析Kaggle的狗品种识别数据集包含10,222张训练图像和10,357张测试图像涵盖120个犬种。数据质量直接影响模型效果我们需要特别注意几个方面数据分布分析是首要步骤。通过统计发现某些品种的样本量不足30张而热门品种如金毛猎犬则有近100张。这种不均衡会导致模型偏向多数类。解决方法包括对少数类采用过采样技术在损失函数中引入类别权重调整数据增强策略强化少数类特征import pandas as pd import matplotlib.pyplot as plt labels pd.read_csv(labels.csv) breed_counts labels[breed].value_counts() plt.figure(figsize(12,6)) breed_counts.plot(kindbar) plt.title(Dog Breed Distribution) plt.xlabel(Breed) plt.ylabel(Count) plt.xticks([]) plt.show()数据组织最佳实践创建标准目录结构/data /train /affenpinscher /afghan_hound ... /valid /test使用torchvision.datasets.ImageFolder自动关联图像与标签保留15-20%数据作为验证集确保各类别比例一致注意Kaggle测试集没有标签验证集必须从训练数据划分建议使用分层抽样保持分布一致2. 图像预处理与增强策略ImageNet预训练模型要求输入为224x224的RGB图像但原始数据尺寸不一。我们的增强策略需要平衡多样性与现实性训练集增强组合随机缩放裁剪224x224比例0.8-1.0水平翻转概率0.5颜色抖动亮度±0.2对比度±0.2饱和度±0.2随机旋转±15度标准化ImageNet均值与标准差from torchvision import transforms train_transform transforms.Compose([ transforms.RandomResizedCrop(224), transforms.RandomHorizontalFlip(), transforms.ColorJitter(0.2, 0.2, 0.2), transforms.RandomRotation(15), transforms.ToTensor(), transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) ])验证/测试集处理等比缩放至256px中心裁剪224x224相同标准化参数关键点测试增强必须确定性强避免随机性影响结果可比性3. ResNet50模型微调实战预训练模型的选择与微调方式是项目核心。ResNet50在精度和计算成本间取得了良好平衡模型改造关键步骤加载预训练ResNet50model torchvision.models.resnet50(pretrainedTrue)冻结底层参数前4个卷积块for param in model.parameters(): param.requires_grad False替换全连接层适配120类别model.fc nn.Sequential( nn.Linear(2048, 1024), nn.ReLU(), nn.Dropout(0.5), nn.Linear(1024, 120) )优化器配置对比实验优化器学习率动量权重衰减验证准确率SGD1e-30.91e-482.1%Adam1e-4-1e-484.3%AdamW3e-4-1e-385.7%学习率调度策略scheduler torch.optim.lr_scheduler.CosineAnnealingLR( optimizer, T_max10, eta_min1e-6)4. 训练过程监控与调优有效的训练监控能节省大量调参时间关键监控指标训练/验证损失曲线类别准确率特别是最少样本类别GPU显存利用率批次处理时间常见问题与解决方案过拟合增加Dropout率0.3→0.5添加L2正则化weight_decay1e-4早停机制patience5显存不足# 梯度累积技巧 accumulation_steps 4 for i, (inputs, labels) in enumerate(train_loader): outputs model(inputs) loss criterion(outputs, labels) loss loss / accumulation_steps loss.backward() if (i1) % accumulation_steps 0: optimizer.step() optimizer.zero_grad()类别不平衡class_weights compute_class_weight( balanced, classesnp.unique(labels), ylabels) criterion nn.CrossEntropyLoss(weighttorch.FloatTensor(class_weights))5. 结果分析与竞赛提交模型评估不应仅看整体准确率还需分析混淆矩阵分析from sklearn.metrics import confusion_matrix cm confusion_matrix(true_labels, pred_labels) plt.figure(figsize(15,15)) sns.heatmap(cm, annotTrue, fmtd, cmapBlues) plt.show()提交文件生成要点测试时启用model.eval()使用torch.no_grad()禁用梯度计算确保输出格式完全匹配Kaggle要求def generate_submission(model, test_loader): model.eval() preds [] with torch.no_grad(): for inputs, _ in test_loader: outputs model(inputs) preds.extend(torch.softmax(outputs, dim1).cpu().numpy()) sample_sub pd.read_csv(sample_submission.csv) submission pd.DataFrame({ id: sample_sub[id], **{breed: [p[i] for p in preds] for i, breed in enumerate(breeds)} }) submission.to_csv(submission.csv, indexFalse)在最终实验中经过精细调参的ResNet50模型在验证集达到87.2%准确率Kaggle提交得分0.865对数损失。这个成绩说明预训练模型的特征提取能力强大数据增强有效缓解了过拟合仍有提升空间如模型集成、测试时增强