深度学习------专题《图像处理项目》下
目录
一、先测整体准确率:模型到底 “行不行”?
二、类别级准确率:哪些类别在 “拖后腿”?
三、优化尝试:用 “全局平均池化” 简化模型
四、总结:今天的两个关键收
CIFAR-10 分类实战(下):测试模型 + 全局池化优化,准确率从 66% 到...?
昨天把 CNN 模型训完后,今天终于能 “验收成果” 了 —— 不仅要测模型在所有测试数据上的表现,还要给模型 “动个小手术”,用「全局平均池化」优化结构。过程中发现了不少有意思的细节,比如有些类别模型就是学不明白,换个池化层居然还能减少参数、提升性能~
一、先测整体准确率:模型到底 “行不行”?
训练完模型,最直观的就是看它在10000 张测试图上的整体准确率。代码逻辑很简单:遍历测试集,把每张图喂给模型,统计 “猜对的次数” 占总次数的比例。
核心代码我加了自己的理解注释,读起来更顺:
correct = 0
total = 0
# 测试时不需要计算梯度,用with torch.no_grad()能省不少内存
with torch.no_grad():for data in testloader:images, labels = data# 把数据放到GPU(没有的话自动用CPU)images, labels = images.to(device), labels.to(device)outputs = net(images)# torch.max找“概率最大的类别”_, predicted = torch.max(outputs.data, 1)total += labels.size(0) # 累计总样本数# 统计猜对的数量:predicted == labels会生成布尔数组,sum后转成数字correct += (predicted == labels).sum().item()print(f'模型在10000张测试图上的准确率:{100 * correct // total}%')
我跑出来的结果是66%—— 比 “随机瞎猜(10%)” 强太多,但离论文里那些 “90%+ 准确率” 的顶尖模型还差不少。不过至少说明模型 “学到了东西”,不是完全在瞎蒙~
二、类别级准确率:哪些类别在 “拖后腿”?
整体准确率只能看个大概,想知道模型在每个类别上的表现,得做 “类别级统计”。毕竟 CIFAR-10 有 10 类(飞机、汽车、鸟、猫等),模型可能对 “汽车” 一认一个准,但对 “猫” 就疯狂认错。
我写了段代码,给每个类别单独计数:
# 初始化两个列表,存每个类别的“正确数”和“总数”
class_correct = list(0. for i in range(10))
class_total = list(0. for i in range(10))
with torch.no_grad():for data in testloader:images, labels = dataimages, labels = images.to(device), labels.to(device)outputs = net(images)_, predicted = torch.max(outputs, 1)# 因为batch_size=4,所以逐个样本判断c = (predicted == labels).squeeze()for i in range(4):label = labels[i]class_correct[label] += c[i].item()class_total[label] += 1# 打印每个类别的准确率
for i in range(10):print(f'类别 {classes[i]} 的准确率:{100 * class_correct[i] / class_total[i]:.0f}%')
跑出来的结果特别有意思:
-
汽车(car):82%(模型对汽车 “了如指掌”);
-
猫(cat):45%(模型对猫严重 “脸盲”,错得最多);
-
鸟(bird):51%、青蛙(frog):55%(这些 “细粒度” 类别也容易错);
-
飞机(plane):72%、船(ship):70%(大轮廓、特征明显的类别,模型学得更好)。
这说明模型对轮廓清晰、特征突出的类别(汽车、飞机)很擅长,但对细节多、类内差异大的类别(猫、鸟)容易翻车。后续优化可以针对性做 “数据增强”(比如多生成猫的图片)。
三、优化尝试:用 “全局平均池化” 简化模型
昨天的模型用了 “池化层 + 全连接层” 的组合,今天学了个更简洁的方法 ——全局平均池化(GAP)。它的核心思路是:把最后一层卷积的特征图直接 “平均成一个点”,再接分类层。
为什么用 GAP?
-
减少参数:全连接层参数特别多,容易过拟合;GAP 参数极少,模型更简洁。
-
更鲁棒:关注 “全局特征”,对物体位置变化更不敏感。
我把模型改成了这样(关键是加了AdaptiveAvgPool2d(1)
):
class Net(nn.Module):def __init__(self):super(Net, self).__init__()self.conv1 = nn.Conv2d(3, 16, 5) # 3输入通道,16个卷积核,5×5大小self.pool1 = nn.MaxPool2d(2, 2) # 池化核2×2,步长2self.conv2 = nn.Conv2d(16, 36, 5) # 16输入通道,36个卷积核,5×5大小self.pool2 = nn.MaxPool2d(2, 2) # 第二次池化# 全局平均池化:把特征图压缩成1×1self.aap = nn.AdaptiveAvgPool2d(1)self.fc3 = nn.Linear(36, 10) # 直接接36个特征(GAP后每个通道剩1个值)def forward(self, x):x = self.pool1(F.relu(self.conv1(x)))x = self.pool2(F.relu(self.conv2(x)))x = self.aap(x) # 全局平均池化x = x.view(x.size(0), -1) # 展平成一维x = self.fc3(x)return xnet_gap = Net()
net_gap = net_gap.to(device)
效果如何?
-
参数暴减:原来的模型有 17 万 + 参数,用 GAP 后只有1.6 万参数(减少了 90% 以上!);
-
准确率微涨:重新训练后,整体准确率居然涨到了70% 左右(虽然还是不高,但 “参数少了还能涨”,说明 GAP 确实有用)。
四、总结:今天的两个关键收获
-
测试要 “细拆”:不仅要看整体准确率,还要看每个类别的表现 —— 这样才能知道模型 “哪里弱”,针对性优化(比如给 “猫、鸟” 这类易错类别做数据增强)。
-
GAP 很实用:用全局平均池化代替 “池化 + 大全连接层”,能大幅减少参数,还可能提升性能,特别适合小数据集防止过拟合。
接下来打算试试:给 “猫、鸟” 这类难分类别做数据增强(比如水平翻转、调整亮度),再用 GAP 模型训练,看看能不能把准确率再提一提~
(如果有同样在玩 CIFAR-10 的朋友,欢迎交流你们的调优技巧呀~)