批量大小降低了预训练 CNN 集合的准确性

Posted

技术标签:

【中文标题】批量大小降低了预训练 CNN 集合的准确性【英文标题】:Batch size reduces accuracy of ensemble of pretrained CNNs 【发布时间】:2021-10-21 06:01:18 【问题描述】:

我正在尝试实现基本的基于 softmax 的投票,我采用几个预训练的 CNN,对其输出进行 softmax,将它们相加,然后使用 argmax 作为最终输出。

所以我从 "chenyaofo/pytorch-cifar-models" 加载了 4 个不同的预训练 CNN(vgg11vgg13vgg16vgg19),它们在 CIFAR10 上进行了训练——我没有训练它们。

当我使用 batch_size=128/256 使用 DataLoader 迭代测试集时,准确率达到 94%;

当我使用 batch_size=1 迭代测试集时,准确率达到 69%。

怎么可能?

这是代码:

import torch
from tqdm import tqdm
from torchvision import datasets, transforms, models
from torch.utils.data import DataLoader
import torch.nn as nn
import torch

torch.cuda.empty_cache()

model_names = [
        "cifar10_vgg11_bn",
        "cifar10_vgg13_bn",
        "cifar10_vgg16_bn",
        "cifar10_vgg19_bn",
        # "cifar10_resnet56",
]

batch_size = 2

test_transform = transforms.Compose([
                    transforms.ToTensor(),
])

def load_models():
    models = []
    for model_name in model_names:
        model = torch.hub.load("chenyaofo/pytorch-cifar-models", model_name, pretrained=True)
        models.append(model)
    return models

testset = datasets.CIFAR10(root='./data', train=False,
                                       download=True, transform=test_transform)
testloader = torch.utils.data.DataLoader(testset, batch_size=batch_size,
                                         shuffle=False)

import torch.nn as nn
import torch

class MyEnsemble(nn.Module):

    def __init__(self, modelA, modelB, modelC, modelD):
        super(MyEnsemble, self).__init__()
        self.modelA = modelA
        self.modelB = modelB
        self.modelC = modelC
        self.modelD = modelD
        # self.modelE = modelE

    def forward(self, x):
        out1 = self.modelA(x)
        out2 = self.modelB(x)
        out3 = self.modelC(x)
        out4 = self.modelD(x)
        # out5 = self.modelE(x)

        # print(out1.shape)

        out1 = torch.softmax(out1, dim=1)
        out2 = torch.softmax(out2, dim=1)
        out3 = torch.softmax(out3, dim=1)
        out4 = torch.softmax(out4, dim=1)

        out = out1 + out2 + out3 + out4

        return out

from EnsembleModule import MyEnsemble
from data import load_models, testloader
import torch
from tqdm import tqdm

device = 'cuda' if torch.cuda.is_available() else 'cpu'

models = load_models()

model = MyEnsemble(models[0], models[1], models[2], models[3])

model.to(device)

total = 0
correct = 0
with torch.no_grad():
    for images, labels in tqdm(testloader):
        images, labels = images.to(device), labels.to(device)
        outputs = model(images)
        _, predictions = torch.max(outputs, 1)
        total += labels.size(0)
        correct += (predictions == labels).sum().item()

print('Accuracy of the network on the 10000 test images: %d %%' % (
    100 * correct / total))


【问题讨论】:

【参考方案1】:

你忘记打电话给model.eval()

# ...

model.to(device)
model.eval() # <<<<<<<<<<<<<

total = 0
correct = 0
with torch.no_grad():
    for images, labels in tqdm(testloader):
        images, labels = images.to(device), labels.to(device)
        outputs = model(images)

# ...

由于您的模型具有 BatchNorm 层,因此 batch_size=1 尤其会降低性能。

预处理也应遵循用于训练的预处理。正如您在模型的repository of the author 中看到的那样,您应该使用以下statistics 进行归一化:

test_transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize(mean=(0.4914, 0.4822, 0.4465), std=(0.2023, 0.1994, 0.2010))
])

【讨论】:

现在是 30%,批量大小 = 1 和 model.eval()... @JojoHalastra 你从batch_size=128/256.eval() 得到了多少? 批量大小为 256 和 .eval() 我也得到了 30%...这真的很奇怪.. @JojoHalastra 我用你正在使用的模型的作者使用的统计数据进行了更新,这与通常的数据略有不同。 @JojoHalastra 顺便说一句,Ivan 答案的 std 值对于您的模型不正确。正确的定义为here【参考方案2】:

您正在使用包含 batchnorm 层的模型(由 torchvision 的模型名称中的 _bn 后缀表示)。

这反过来意味着结果将取决于当前批次的统计数据。这些在使用batch_size=2batch_size=128 时是不同的。在评估时,您应该始终调用nn.Module.eval 函数。这使得该层使用运行统计信息(那些在训练期间学习的)而不是批次的统计信息。阅读this post了解更多信息。

请注意,调用 eval 将递归地传播到所有子模块,因此您只需直接对 yoru ensemble 模块进行一次调用:

model = MyEnsemble(models[0], models[1], models[2], models[3])
model.eval()

完成后,批量大小应该不会影响模型的性能。

训练时,您需要使用nn.Module.train 重新开启训练模式。


您需要根据dataset's statistics 对数据进行归一化处理,您可以在torchvision 预处理管道中执行此操作:

test_transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize((0.4914, 0.4822, 0.4465), (0.247, 0.243, 0.261))])) 

【讨论】:

现在是 30%,批量大小 = 1 和 model.eval()... 您是否在预处理中标准化了您的测试数据? 成功了!你是怎么知道标准化的值的? 我找到了here 和here。但是您应该使用用于训练模型的统计数据。下面的@Berriel 将回购与正确的值相关联。它们与torchvision提供的模型所使用的略有不同。

以上是关于批量大小降低了预训练 CNN 集合的准确性的主要内容,如果未能解决你的问题,请参考以下文章

预测取决于 Keras 中的批量大小

你都有哪些炼丹神器深度学习(rnn、cnn)调参的经验?

CNN基础二:使用预训练网络提取图像特征

keras结合了预训练模型

如何在 Keras 中使用预训练的 CNN 实现连体网络?

带有keras的CNN,准确性保持不变并且没有提高