【小实验1】比较ResNet、ViT、SwinTransformer的归纳偏置(然而并没有达到预期结果)

写在前面:本实验并未获得预期的结果,更多的是当作实验记录。

1. idea

1.1 实验思路

这个实验的思路是这样的:通过随机初始化(正态分布)的未经过训练的ResNet、ViT和SwinTransformer,来对ImangeNet-1k(2012)的验证集(val,共50000张图片,1000类别)进行预测,对比预测结果和随机猜(准确率为1‰)的区别。

1.2 灵感来源

灵感来自Deep Clustering for Unsupervised Learning of Visual Features这篇论文,论文中提到这么一段话:
在这里插入图片描述
这段话说的是:“当模型(如CNN)中的参数 θ \theta θ使用高斯分布随机初始化时,在未经训练时,预测出的结果很差。但是,要比随即猜测(对于ImageNet-1k来讲随机猜对的概率为千分之1)要好不少。”

作者给出的解释是,这是因为我们模型,如CNN,引入了先验知识(即CNN关于平移等变性、局部性等归纳偏置),所以随机初始化后,虽然没有经过数据训练,但得出的结果也要比随机猜要好。

所以我就在想,用未经训练的随机初始化的ResNet、ViT和SwinTransformer来预测ImageNet-1k(val),准确率越高,从某种程度上来讲说明引入的归纳偏置越强。

2. 实验设置

实验环境 Pytorch1.12、2xRTX3090(24G)
模型 ResNet、ViT、SwinTransformer
测试数据集 ImageNet-1k(2012,val,50000张,1000类别)
实验模型 参数量
ResNet50 25.56M
ResNet101 44.55M
ViT_B_16 86.57M
ViT_L_16 304.33M
swin_t 28.29M
swin_s 49.61M

其中初始化基本都是采用各种类型的正态分布进行初始化,比如

nn.init.trunc_normal_
nn.init.normal_
nn.init.kaiming_normal_

具体用法参考Pytorch官方文档

3. 实验结果

3.1 结果

实验结果是:未经训练的、随机初始化的ResNet、ViT和SwinTransformer,最后预测的结果都和随机猜的差不多(千分之一)。

模型 预测正确数量 测试集数量 准确率 训练时间 batch_size 显卡使用情况
resnet50 50 50000 1‰ 35.485s 256 在这里插入图片描述
resnet101 50 50000 1‰ 45.556s 256 在这里插入图片描述
vit_b_16 50 50000 1‰ 83.587s 256 在这里插入图片描述
vit_l_16 50 50000 1‰ 370.091s 64 在这里插入图片描述
swin_t 43 50000 ~1‰ 44.616s 256 在这里插入图片描述
swin_s 49 50000 ~1‰ 67.754s 256 在这里插入图片描述

3.2 结果分析

3.2.1 一个奇怪的现象

有个比较有意思的现象,我们可以看到对于ResNet系列和ViT系列,都是预测对了50个样本。这是因为对于未经训练的随机初始化的ResNet和ViT,不论输入什么样本,预测出的标签是一样的。

注意:不是说输出都是一样的,而是说最后经过softmax输出的1000维特征,最大值对应的索引都相同。 所以才会有正好50个预测对的。(Imagnet-1K的val有50000张图片,1000个类别,每个类别都有50张图片)。

而对于SwinTransformer,最后预测的标签确实看上去是随机分布的,但也跟瞎猜的一样(都是1‰),并没有像Deep Cluster的作者说的那样:要比瞎猜的好上不少。

3.2.2 分析

我觉得Deep Cluster的作者说的应该是没错的(毕竟是大佬写的文章,还是顶会),问题应该出在我的代码实现的细节上,比如初始化的方式?我是使用TorchVision源码中自带的初始化,感觉也没什么问题啊。

这个现象我不打算再深入了,因为本身就只是想读一下Deep Cluster,看到作者说的这个现象觉得很有趣就像试一试,但没出现预期的效果。如果想深入找一下原因的话可以看看Deep Cluster的引用文献。

4. 代码

import os
import time

import torch
import torch.nn as nn
from torchvision.models import resnet50, resnet101, vit_b_16, vit_l_16, swin_t, swin_s
from torchvision import datasets, transforms
from tqdm import tqdm


def get_dataloader(data_dir=None, batch_size=64):
    '''
    :param data_dir: val dataset direction
    :return: imageNet
    '''
    assert data_dir is not None

    transform = transforms.Compose([
        transforms.RandomResizedCrop(224, scale=(0.2, 1.)),
        transforms.ToTensor(),
        # 这里我在考虑是否要进行标准化,可以做个对比实验
        transforms.Normalize(mean=[0.485, 0.456, 0.406],
                             std=[0.229, 0.224, 0.225])
    ])

    val_dataset = datasets.ImageFolder(
        data_dir,
        transform,
    )
    val_loader = torch.utils.data.DataLoader(
        val_dataset,
        batch_size=batch_size,
        num_workers=8,
        shuffle=True,
    )

    return val_loader


def get_models():
    '''
    :return: res50, res101, ViT, SwinTransformer
    '''
    # load model with random initialization
    res50 = resnet50()
    res101 = resnet101()
    vit_B_16 = vit_b_16()
    vit_L_16 = vit_l_16()
    swin_T = swin_t()  # 参数量和res50相近
    swin_B = swin_s()  # 参数量和res101相近
    model_list = [res50, res101, vit_B_16, vit_L_16, swin_T, swin_B]
    model_names = ['res50', 'res101', 'vit_B_16', 'vit_L_16', 'swin_T', 'swin_B']
    for name, model in zip(model_names, model_list):
        print(f'{
      
      name:10}parametersize is {
      
      compute_params(model): .2f}M')

    return model_list, model_names


def compute_params(model):
    '''
    :param model: nn.Module, model
    :return: float, model parameter size
    '''
    total = sum(p.numel() for p in model.parameters())
    size = total / 1e6
    # print("Total params: %.2fM" % (total / 1e6))
    return size


def model_evaluate(model, data_loader):
    '''
    :param model: test model
    :param data_loader: val_loader
    :return: list[float] acc list
    '''
    device = torch.device('cuda:1' if torch.cuda.is_available() else 'cpu')
    device_ids = [1, 2]  # use 2 GPUs
    model = nn.DataParallel(model, device_ids=device_ids)
    model.to(device)
    model.eval()
    total = 0
    correct = 0
    loop = tqdm((data_loader), total=len(data_loader))
    for imgs, labels in loop:
        imgs.to(device)
        outputs = model(imgs)
        outputs = outputs.argmax(dim=1)
        labels = labels.to(device)

        # print(outputs.shape, '\n', outputs, outputs.argmax(dim=1))
        # print(labels.shape, '\n', labels)
        total += len(labels)
        res = outputs==labels
        correct += res.sum().item()
        loop.set_description(f'inference test:')
        loop.set_postfix(total=total, correct=correct, acc=f'{
      
      correct/total:.2f}')



if __name__ == '__main__':
    seed = 2022
    torch.manual_seed(seed)
    torch.cuda.manual_seed_all(seed)

    data_dir = os.path.join('..', '..', '..', 'data', 'ImageNet2012', 'imagenet', 'val')
    val_loader = get_dataloader(data_dir, batch_size=256)

    get_models()  # 输出模型的大小,本来想写一个循环自动训练所有的model,但测试会爆显存,所以就单独测试每个model了
    net = swin_s()  # 这里换成我们测试的模型,可以用resnet50, resnet101, vit_b_16, vit_l_16, swin_t, swin_s
    t1 = time.time()
    model_evaluate(net, val_loader)
    print(f'total time: {
      
      time.time() - t1:.3f}s')

    # nets, net_names = get_models()
    # for net, name in zip(nets, net_names):
    #     if name == 'vit_L_16':
    #         val_loader = get_dataloader(data_dir, batch_size=16)
    #     val_loader = get_dataloader(data_dir, batch_size=128)
    #
    #     t1 = time.time()
    #     model_evaluate(net, val_loader)
    #     print(f'{name:.10} total time: {time.time()-t1:.3f}s')

参考:
1. Deep Clustering for Unsupervised Learning of Visual Features

2. Pytorch官方文档

猜你喜欢

转载自blog.csdn.net/qq_44166630/article/details/127757741