RepVGG - RepVGG:让 VGG 风格的 ConvNets 再次伟大

Created at: 2020-12-31 16:53:43
Language: Python
License: MIT

RepVGG:让 VGG 风格的 ConvNets 再次伟大 (CVPR-2021) (PyTorch)

更新

2021 年 8 月 21 日

VGG 又是 SOTA!重磅出炉!

84.16%的 top-1 准确和更高的吞吐量胜过最近的几个视觉转换器。

该模型使用Swin Transformer代码库在 300 个 epoch 中进行训练。吞吐量也使用 Swin 代码库进行测试。我们要感谢Swin的作者,感谢他们干净且结构良好的代码。

模型 训练图像大小 测试尺寸 ImageNet 前 1 吞吐量(示例/秒),320,batchsize=128,2080Ti)
RepVGGplus-L2pse 256 320 84.16% 147
斯温变压器 320 320 84.0% 102

与 RepVGGs 相比,训练时 RepVGGplus 模型更深,并且具有三个辅助分类器,可以将其移除以进行推理。请检查

repvggplus.py

测试它与测试 RepVGG 没有什么不同:

python convert.py RepVGGplus-L2pse-train.pth RepVGGplus-L2pse-deploy.pth -a RepVGGplus-L2pse
python test.py [imagenet-folder] deploy RepVGGplus-L2pse-deploy.pth -a RepVGGplus-L2pse -r 320

它有 126M 的推理时间参数。训练时间权重文件在 Google Drive 和百度云上发布。请检查以下链接。

要训​​练或微调它,请像这样稍微更改你的训练代码:

        #   Build model and data loader as usual
        for samples, targets in enumerate(train_data_loader):
            #   ......
            outputs = model(samples)                        #   Your original code
            if type(outputs) is dict:                       
                #   A training-time RepVGGplus outputs a dict. The items are:
                    #   'main':     the output of the final layer
                    #   '*aux*':    the output of auxiliary classifiers
                    #   'L2':       the custom L2 regularization term
                loss = WEIGHT_DECAY * 0.5 * outputs['L2']
                for name, pred in outputs.items():
                    if name == 'L2':
                        pass
                    elif 'aux' in name:
                        loss += 0.1 * criterion(pred, targets)          #  Assume "criterion" is cross-entropy for classification
                    else:
                        loss += criterion(pred, targets)
            else:
                loss = criterion(outputs, targets)          #   Your original code
            #   Backward as usual
            #   ......

要将其用于语义分割等下游任务,只需丢弃辅助分类器和最终的 FC 层。我将在此 repo 中发布一个示例。

2021 年 6 月 22 日纯 VGG 模型(没有SE)似乎优于一些具有更好训练方案的视觉变换器模型。训练。

2021 年 6 月 11 日使用简单工具箱 torch.quantization 量化 RepVGG 的示例。请在下面检查。

2021 年 6 月 10 日使用自定义权重衰减的训练已经过测试。只需添加

--custwd
到训练命令中即可。

2021 年 6 月 8 日发现高性能量化需要自定义权重衰减。这种重量衰减也提高了全精度精度。将在调整超参数并完成 QAT 后发布量化模型。

2021 年 4 月 25 日,更深的 RepVGG 模型具有SE块和 320x320 输入分辨率的ImageNet 上实现了83.55% 的 top-1 精度(更宽的版本在没有 SE 的情况下实现了83.67% 的精度 )。请注意,它使用 224x224 进行训练,但使用 320x320 进行测试,因此它仍然可以在具有 8 个 1080Ti GPU 的单台机器上以 256 的全局批大小进行训练。如果用 224x224 进行测试,top-1 的准确率将是 81.82%。它在 5 个阶段中分别具有 1、8、14、24、1 层。宽度乘数为 a=2.5 和 b=5(与 RepVGG-B2 相同)。型号名称为“RepVGG-D2se”。用于构建模型(repvgg.py)和使用 320x320 进行测试(下面的测试示例)的代码已更新,权重已在 Google Drive 和百度云上发布。请检查以下链接。

2021 年 4 月 4 日更好的实施。对于 RepVGG 模型或以 RepVGG 作为其组件之一的模型(例如,主干),你可以通过简单地调用每个 RepVGG 块的switch_to_deploy来转换整个模型。这是推荐的方式。示例显示在convert.pyexample_pspnet.py 中

    for module in model.modules():
        if hasattr(module, 'switch_to_deploy'):
            module.switch_to_deploy()

2021 年 4 月 4 日使用 RepVGG 作为 PSPNet 主干进行语义分割的示例 ( example_pspnet.py )。它展示了如何 1) 使用 RepVGG 主干构建 PSPNet,2) 加载 ImageNet 预训练权重,3) 使用switch_to_deploy转换整个模型,4) 保存并使用转换后的模型进行推理。

2021 年 1 月 13 日 - 2 月 5 日你可以随时以可微分的方式获得等效内核和偏差(repvgg.py 中的 get_equivalent_kernel_bias)。这可能有助于基于训练的修剪或量化。这个训练脚本(一个超级简单的 PyTorch-official-example-style 脚本)已经用 RepVGG-A0 和 B1 进行了测试。结果甚至略好于论文中报道的结果。

介绍

这是一个超级简单的 ConvNet 架构,使用 3x3 conv 和 ReLU 堆栈在 ImageNet 上实现了超过80% 的 top-1 准确率!该 repo 包含预训练模型、构建模型的代码、训练以及从训练时间模型到推理时间的转换,以及使用 RepVGG 进行语义分割的示例

MegEngine 版本:https : //github.com/megvii-model/RepVGG

@upczww https://github.com/upczww/TensorRT-RepVGG使用 C++ API 实现 TensorRT 。做得好!

@zjykzj https://github.com/ZJCV/ZCls 的另一个 PyTorch 实现。他还在https://zcls.readthedocs.io/en/latest/benchmark-repvgg/ 上介绍了详细的基准测试。干得好!

包含在著名的模型动物园(超过 7k 星)https://github.com/rwightman/pytorch-image-models

@benjaminjellis 的 Objax 实现和模型。做得好!https://github.com/benjaminjellis/Objax-RepVGG

引文:

@inproceedings{ding2021repvgg,
title={Repvgg: Making vgg-style convnets great again},
author={Ding, Xiaohan and Zhang, Xiangyu and Ma, Ningning and Han, Jungong and Ding, Guiguang and Sun, Jian},
booktitle={Proceedings of the IEEE/CVF Conference on Computer Vision and Pattern Recognition},
pages={13733--13742},
year={2021}
}

抽象的

我们提出了一个简单但功能强大的卷积神经网络架构,它具有类似 VGG 的推理时间体,仅由一堆 3x3 卷积和 ReLU 组成,而训练时间模型具有多分支拓扑结构。这种训练时间和推理时间架构的解耦是通过结构重新参数化技术实现的,因此该模型被命名为 RepVGG。据我们所知,在 ImageNet 上,RepVGG 达到了超过 80% 的 top-1 准确率,这是普通模型的第一次。在 NVIDIA 1080Ti GPU 上,RepVGG 模型的运行速度比 ResNet-50 快 83% 或比 ResNet-101 快 101%,具有更高的准确度,并且与 EfficientNet 和 RegNet 等最先进的模型相比显示出有利的准确度-速度权衡.

图片 图片 图片

使用我们的预训练模型

你可以下载所有从谷歌硬盘(本文报道的ImageNet-预训练模式的https://drive.google.com/drive/folders/1Avome4KvNp0Lqh2QwhXO6L5URQjzCjUq?usp=sharing)或百度云(https://pan.baidu。 com/s/1nCsZlMynnJwbUBKn0ch7dQ,访问代码是“rvgg”)。为了便于在其他任务上进行迁移学习,它们都是训练时间模型(具有身份和 1x1 分支)。你可以通过运行来测试准确性

python test.py [imagenet-folder with train and val folders] train [path to weights file] -a [model name]

默认输入分辨率为 224x224。这里的“train”表示训练时架构,有效的模型名称包括

RepVGG-A0, RepVGG-A1, RepVGG-A2, RepVGG-B0, RepVGG-B1, RepVGG-B1g2, RepVGG-B1g4, RepVGG-B2, RepVGG-B2g2, RepVGG-B2g4, RepVGG-B3, RepVGG-B3g2, RepVGG-B3g4

例如,

python test.py [imagenet-folder with train and val folders] train RepVGG-B2-train.pth -a RepVGG-B2

要使用 320x320 输入测试最新模型 RepVGG-D2se,

python test.py [imagenet-folder with train and val folders] train RepVGG-D2se-200epochs-train.pth -a RepVGG-D2se -r 320

将训练时间模型转换为推理时间

你可以将经过训练的模型转换为推理时间结构

python convert.py [weights file of the training-time model to load] [path to save] -a [model name]

例如,

python convert.py RepVGG-B2-train.pth RepVGG-B2-deploy.pth -a RepVGG-B2

Then you may test the inference-time model by

python test.py [imagenet-folder with train and val folders] deploy RepVGG-B2-deploy.pth -a RepVGG-B2

Note that the argument "deploy" builds an inference-time model.

ImageNet training

We trained for 120 epochs with cosine learning rate decay from 0.1 to 0. We used 8 GPUs, global batch size of 256, weight decay of 1e-4 (no weight decay on fc.bias, bn.bias, rbr_dense.bn.weight and rbr_1x1.bn.weight) (weight decay on rbr_identity.weight makes little difference, and it is better to use it in most of the cases), and the same simple data preprocssing as the PyTorch official example:

            trans = transforms.Compose([
                transforms.RandomResizedCrop(224),
                transforms.RandomHorizontalFlip(),
                transforms.ToTensor(),
                transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])])

此 repo 中的多处理训练脚本基于官方 PyTorch 示例,以简化和提高可读性。唯一的修改包括模型构建部分、余弦学习率调度器和对某些参数不使用权重衰减的 SGD 优化器。你可能会发现这些代码段对你的训练代码很有用。我们使用 RepVGG-A0 和 RepVGG-B1 测试了这个训练脚本。准确率分别为 72.44 和 78.38,与我们在论文中报告的结果(72.41 和 78.37)几乎相同(甚至更好)。你可以这样训练和测试:

python train.py -a RepVGG-A0 --dist-url 'tcp://127.0.0.1:23333' --dist-backend 'nccl' --multiprocessing-distributed --world-size 1 --rank 0 --workers 32 [imagenet-folder with train and val folders] --tag hello --custwd --wd 4e-5
python test.py [imagenet-folder with train and val folders] train RepVGG-A0_hello_best.pth.tar -a RepVGG-A0

如果你与我分享你与其他模型的重新实现结果,我将不胜感激。

在你自己的代码中像这样使用

from repvgg import repvgg_model_convert, create_RepVGG_A0
train_model = create_RepVGG_A0(deploy=False)
train_model.load_state_dict(torch.load('RepVGG-A0-train.pth'))          # or train from scratch
# do whatever you want with train_model
deploy_model = repvgg_model_convert(train_model, save_path='RepVGG-A0-deploy.pth')
# do whatever you want with deploy_model

或者

deploy_model = create_RepVGG_A0(deploy=True)
deploy_model.load_state_dict(torch.load('RepVGG-A0-deploy.pth'))
# do whatever you want with deploy_model

如果你使用 RepVGG 作为另一个模型的组件,转换就像调用每个 RepVGG 块的switch_to_deploy一样简单。

量化

量化的最佳解决方案是将等效内核(repvgg.py 中的 get_equivalent_kernel_bias())限制为低位(例如,将 {-127, -126, .., 126, 127} 中的每个参数设为 int8),而不是为普通模型单独约束每个内核的参数。

为简单起见,我们还可以使用现成的量化工具箱来量化 RepVGG。我们以 torch.quantization 中的简单 QAT(量化感知训练)工具为例。

  1. 基本模型使用自定义权重衰减 (
    --custwd
    ) 进行训练并转换为推理时间结构。我们在转换后的 3x3 conv 层之后插入 BN,因为带有 torch.quantization 的 QAT 需要 BN。具体来说,我们在 ImageNet 训练集上运行模型并记录均值/标准统计数据并使用它们来初始化 BN 层,并相应地初始化 BN.gamma/beta,以便保存的模型具有与推理时间模型相同的输出。
python train.py -a RepVGG-A0 --dist-url 'tcp://127.0.0.1:23333' --dist-backend 'nccl' --multiprocessing-distributed --world-size 1 --rank 0 --workers 32 [imagenet-folder] --tag hello --custwd
python convert.py RepVGG-A0_hello_best.pth.tar RepVGG-A0_base.pth -a RepVGG-A0 
python insert_bn.py [imagenet-folder] RepVGG-A0_base.pth RepVGG-A0_withBN.pth -a RepVGG-A0 -b 32 -n 40000
  1. 构建模型,为 QAT 做准备(torch.quantization.prepare_qat),并进行 QAT。超参数可能不是最佳的,我正在调整它们。
python quantization/quant_qat_train.py [imagenet-folder] -j 32 --epochs 20 -b 256 --lr 1e-3 --weight-decay 4e-5 --base-weights RepVGG-A0_withBN.pth --tag quanttest

常见问题

:推理时间模型的输出训练时间模型的输出相同吗?

:是的。你可以通过以下方式验证

import torch
train_model = create_RepVGG_A0(deploy=False)
train_model.eval()      # Don't forget to call this before inference.
deploy_model = repvgg_model_convert(train_model)
x = torch.randn(1, 3, 224, 224)
train_y = train_model(x)
deploy_y = deploy_model(x)
print(((train_y - deploy_y) ** 2).sum())    # Will be around 1e-10

:如何将预训练的 RepVGG 模型用于其他任务?

:最好在你的数据集上微调训练时间 RepVGG 模型。然后,你应该在微调之后和部署模型之前进行转换。例如,假设你想使用 PSPNet 进行语义分割,你应该构建一个以训练时 RepVGG 模型作为主干的 PSPNet,将预训练的权重加载到主干中,并在你的分割数据集上微调 PSPNet。然后,你应该按照此 repo 中提供的代码转换主干,并保留其他特定于任务的结构(在本例中为 PSPNet 部分)。伪代码将像

#   train_backbone = create_RepVGG_B2(deploy=False)
#   train_backbone.load_state_dict(torch.load('RepVGG-B2-train.pth'))
#   train_pspnet = build_pspnet(backbone=train_backbone)
#   segmentation_train(train_pspnet)
#   deploy_pspnet = repvgg_model_convert(train_pspnet)
#   segmentation_test(deploy_pspnet)

example_pspnet.py 中有一个例子。

如果你在每次 conv 之后插入一个 BN(请参阅量化部分的步骤 1),则使用转换后的 RepVGG 进行微调也是有意义的,但性能可能会稍低一些。

:我尝试使用多个 GPU 对你的模型进行微调,但出现错误。为什么下载的权重文件中的参数名称像“stage1.0.rbr_dense.conv.weight”,但有时像“module.stage1.0.rbr_dense.conv.weight”(由nn.Module.named_pa​​rameters()显示)在我的模型中?

A : DistributedDataParallel 可以前缀“module”。到 params 的名称并在按名称加载权重时导致不匹配。最简单的解决方案是在 DistributedDataParallel(model) 之前加载权重 (model.load_state_dict(...))。否则,你可以插入“模块”。在这样的名字之前

checkpoint = torch.load(...)    # This is just a name-value dict
ckpt = {('module.' + k) : v for k, v in checkpoint.items()}
model.load_state_dict(ckpt)

同样,如果检查点文件中的参数名称以“module”开头。但是你的模型中没有,你可以去掉 test.py 中第 50 行的名称。

ckpt = {k.replace('module.', ''):v for k,v in checkpoint.items()}   # strip the names
model.load_state_dict(ckpt)

:所以 RepVGG 模型会在每次转发之前派生出等效的 3x3 内核以节省计算量?

:不!更准确地说,我们只在训练后进行一次转换。然后可以丢弃训练时间模型,结果模型只有 3x3 内核。我们只保存和使用生成的模型。

接触

dxh17@mails.tsinghua.edu.cn

谷歌学术档案:https : //scholar.google.com/citations?user=CIjw0KoAAAAJ&hl=en

我的开源论文和回购:

结构重新参数宇宙

  1. (预印本,2021 年)强大的 MLP 式 CNN 构建块
    RepMLP:将卷积重新参数化为用于图像识别
    代码的全连接层

  2. (CVPR 2021)一个超级简单而强大的 VGG 风格的 ConvNet 架构。ImageNet top-1 准确率高达83.55%
    RepVGG:让 VGG-style ConvNets Great Again
    代码

  3. (预印本,2020 年)最先进的通道剪枝
    Lossless CNN Channel Pruning via Decoupling Remembering and Forgetting
    code

  4. ACB (ICCV 2019) 是一个 CNN 组件,没有任何推理时间成本。我们的结构重新参数化宇宙的第一部作品。
    ACNet:通过非对称卷积块加强强大 CNN 的内核骨架
    代码

  5. DBB (CVPR 2021) 是一个 CNN 组件,其性能比 ACB 更高,并且仍然没有推理时间成本。有时我称它为 ACNet v2,因为“DBB”比 ASCII 中的“ACB”大 2 位(笑)。
    Diverse Branch Block:将卷积构建为类 Inception 的单元
    代码

模型压缩和加速

  1. (CVPR 2019) 通道修剪:用于修剪具有复杂结构代码的非常深的卷积网络的向心 SGD

  2. (ICML 2019) Channel pruning: Approximated Oracle Filter Pruning for Destructive CNN Width Optimization
    代码

  3. (NeurIPS 2019) 非结构化修剪:用于修剪非常深的神经网络代码的全局稀疏动量 SGD