目录

一、先测整体准确率:模型到底 “行不行”?

二、类别级准确率:哪些类别在 “拖后腿”?

三、优化尝试:用 “全局平均池化” 简化模型

四、总结:今天的两个关键收


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 = data
        images, 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,步长2
        self.conv2 = nn.Conv2d(16, 36, 5) # 16输入通道,36个卷积核,5×5大小
        self.pool2 = nn.MaxPool2d(2, 2)   # 第二次池化
        # 全局平均池化:把特征图压缩成1×1
        self.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 x

net_gap = Net()
net_gap = net_gap.to(device)

效果如何?

  • 参数暴减:原来的模型有 17 万 + 参数,用 GAP 后只有1.6 万参数(减少了 90% 以上!);

  • 准确率微涨:重新训练后,整体准确率居然涨到了70% 左右(虽然还是不高,但 “参数少了还能涨”,说明 GAP 确实有用)。    

四、总结:今天的两个关键收获

  1. 测试要 “细拆”:不仅要看整体准确率,还要看每个类别的表现 —— 这样才能知道模型 “哪里弱”,针对性优化(比如给 “猫、鸟” 这类易错类别做数据增强)。

  2. GAP 很实用:用全局平均池化代替 “池化 + 大全连接层”,能大幅减少参数,还可能提升性能,特别适合小数据集防止过拟合。

接下来打算试试:给 “猫、鸟” 这类难分类别做数据增强(比如水平翻转、调整亮度),再用 GAP 模型训练,看看能不能把准确率再提一提~

(如果有同样在玩 CIFAR-10 的朋友,欢迎交流你们的调优技巧呀~)

Logo

码道开发者社区,聚焦华为云码道 CodeArts 代码智能体,沉淀 Agent、Skill、鸿蒙开发实战内容,供开发者查阅资料、交流技术、分享工程实践

更多推荐