• 【小实验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类别)
    实验模型参数量
    ResNet5025.56M
    ResNet10144.55M
    ViT_B_1686.57M
    ViT_L_16304.33M
    swin_t28.29M
    swin_s49.61M

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

    nn.init.trunc_normal_
    nn.init.normal_
    nn.init.kaiming_normal_
    
    • 1
    • 2
    • 3

    具体用法参考Pytorch官方文档

    3. 实验结果

    3.1 结果

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

    模型预测正确数量测试集数量准确率训练时间batch_size显卡使用情况
    resnet5050500001‰35.485s256在这里插入图片描述
    resnet10150500001‰45.556s256在这里插入图片描述
    vit_b_1650500001‰83.587s256在这里插入图片描述
    vit_l_1650500001‰370.091s64在这里插入图片描述
    swin_t4350000~1‰44.616s256在这里插入图片描述
    swin_s4950000~1‰67.754s256在这里插入图片描述

    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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123

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

    2. Pytorch官方文档

  • 相关阅读:
    如何将MindSpore模型转ONNX格式并使用OnnxRuntime推理---全流程开发指导
    QGC中如何实现无人机视频流的接收、解码和显示。
    面试题 Android 如何实现自定义View 固定帧率绘制
    智慧海关集装箱RFID物流运输管理系统解决方案
    JUC第二十五讲:JUC线程池-CompletableFuture 实现原理与实践
    在 Chrome 开发者工具里通过 network 选项模拟网站的离线访问模式
    OAuth2 的基本概念
    《QT从基础到进阶·二十七》进度条QProgressBar
    HiveServer2 Service Crashes(hiveServer2 服务崩溃)
    Java中 Lambda表达式如何实现匿名函数
  • 原文地址:https://blog.csdn.net/qq_44166630/article/details/127757741