RegNet(CVPR 2020)原理与代码解析

paper:Designing Network Design Spaces

official implementation:https://github.com/facebookresearch/pycls

third-party implementation:https://github.com/open-mmlab/mmpretrain/blob/main/mmpretrain/models/backbones/regnet.py

本文的创新点

本文提出了一个新的网络设计范式,并不是专注于设计单个网络实例,而是设计了一个网络设计空间network design space。整个过程类似于经典的手工网络设计,但被提升到了设计空间的水平。使用本文的方法,作者探索了网络设计的结构方面,并得到了一个由简单、规则的网络构成了低维设计空间并称之为RegNet。RegNet设计空间提供了各个范围flop下简单、快速的网络。在类似的训练设置和flops下,RegNet的效果超过了EfficientNet同时在GPU上快了5倍。

3 Design space desin

本文的目标是设计出更好的视觉识别网络。我们不是在特定的设置下设计或寻找单一的最佳模型,而是研究模型种群population的行为。我们的目标是发现可以应用于和改进整个模型种群的一般设计原则。这样的设计原则可以提供对网络设计的理解,并且更有可能推广到新的设置(而不是针对特定场景调整的单个模型)。

3.1 Tools for Design Space Design

本文使用了《On network design spaces for visual recognition》这篇文章提出的方法,通过从设计空间抽样一组模型并用模型的误差分布来量化设计空间的质量。这种方法背后的直觉是比较分布与使用搜索并比较两个搜索找到的最佳模型相比,更加鲁棒信息量更丰富。

为了得到模型的分布,我们从设计空间中采样并训练n个模型。为了提高效率,这一步在low-compute, low-epoch训练设置下进行。具体我们使用400 million flop(400MF)并在ImageNet上每个模型训练10个epoch。在400MF下100个模型训练10个epoch的flops大致相当于训练一个4GF的ResNet-50模型100个epoch。

我们分析设计空间质量的主要工具误差经验分布函数(error empirical distribution function, EDF)。如下

\(F(e)\) 给出了误差小于 \(e\) 的模型的比例。图2(left)展示了来自AnyNetX(见3.2)设计空间n=500个采样模型的error EDF。

给定一个模型的种群,我们可以通过网络误差绘制和分析各种网络的属性,来自AnyNetX的两个例子见图2(middle, right)。对于这些图,我们empirical bootstrap来估计最佳模型可能落入的区域。 

总结如下(1)从一个设计空间中通过采样和训练n个模型得到模型的分布(2)计算和绘制误差EDFs来总结设计空间的质量(3)可视化设计空间的各种属性并用empirical bootstrap to gain insights(4)使用这些insights来完善设计空间

3.2 The AnyNet Design Space

作者首先提出了初始的AnyNet设计空间。作者的重点是探索假定标准的、固定blocks(例如residual bottleneck block)的网络的结构。网路结构包括blocks的数量(即网络深度)、block宽度(即通道数)以及其它的block参数例如bottleneck ratio或group widths。网络结构决定了计算的分布、参数、内存占用,并且是决定精度和效率的关键。

AnyNet设计空间中网络的基础设计非常简单,一个网络包括一个简单的stem,然后是执行了大部分计算的body部分,最后是一个预测输出类别的head,如图3a所示。作者保持stem和head不变并尽可能的简单,并主要关注于body部分的结构,因为这是决定网络计算和精度的核心。

网络由4个stage组成,逐步降低分辨率,如图3b所示。每个stage包括多个相同的blocks,如图3c。总得来说,对每个stage \(i\),自由度包括block的数量 \(d_{i}\)、block宽度 \(w_{i}\) 和任何其它block的参数。

我们大多数实验采用了带有组卷积的标准residual bottleneck block,如图4。作者称之为x block,使用它的AnyNet设计空间称为AnyNetX。

AnyNetX设计空间共有16个自由度,4个stage每个stage有4个参数:number of blocks \(d_{i}\)、block width \(w_i\)、bottleneck ratio \(r_i\)、group width \(g_i\)。输入分辨率固定为224。为了得到有效的模型,对 \(d_i\le 16,w_i\le 1024\) 进行对数均匀采样并可被8整除,\(b_i\in\{1,2,4\},g_i\in\{1,2,...,32\}\)。重复采样直到在目标复杂度下(360MF-400MF)得到500个模型,每个模型训练10个epoch。AnyNetX的基本统计数据见图2。

AnyNetX设计空间中共有 \((16\cdot128\cdot3\cdot6)^4\approx 10^{18}\) 个可能的模型配置。我们并不是要从这 \(10^{18}\) 个模型中寻找最好的一个,而是研究是否有通用的设计原则,可以帮助我们理解和完善这个设计空间。为此作者应用了design deisng space的方法,其中每一步,目标都是

  1. 简化设计空间的结构
  2. 提高设计空间的可解释性
  3. 提高或保持设计空间的质量
  4. 在设计空间中保持模型的多样性

我们现在将这种方法应用于AnyNetX的设计空间。

\(\mathbf{AnyNetX_A.} \) 接下来我们将初始的、无约束的AnyNetX设计空间称为 \(AnyNetX_A\)。

\(\mathbf{AnyNetX_B.} \) 我们首先测试将 \(AnyNetX_A\) 中所有stage的bottleneck ratio \(b_i\) 设为相同,并将新的设计空间称为 \(AnyNetX_B\)。和之前一样,我们从 \(AnyNetX_B\) 中采样并训练500个模型,\(AnyNetX_A\) 和 \(AnyNetX_B\) 的EDFs如图5(left)所示,可以看出两者在平均水平和最好水平下几乎相同,这表明当耦合 \(b_i\) 时精度没有下降。除了更简单,\(AnyNetX_B\) 还更易于分析,如图5(right)所示。

\(\mathbf{AnyNetX_C.} \)  我们在 \(AnyNetX_B\) 的基础上,设置所有stage的 \(g_i\) 相同得到 \(AnyNetX_C\)。和之前一样,EDFs几乎没变,如图5(middle)所示。\(AnyNetX_C\) 比 \(AnyNetX_B\) 少了6个自由度,并将设计空间的size缩小了近4个数量级。

\(\mathbf{AnyNetX_D.} \) 接下里我们研究 \(AnyNetX_C\) 中典型的好模型和差模型,如图6所示。我们发现了一种规律:好的网络的宽度是逐渐增加的。于是我们测试了 \(w_{i+1}\ge w_i\) 的设计准则并将具有此约束的设计空间称为 \(AnyNetX_D\)。

如图7(left)所示,这极大的改进了EDF。

\(\mathbf{AnyNetX_E.} \)  在观察了许多模型后,作者又发现了另一个有趣的趋势,和 \(w_i\) 一样最佳模型的stage深度 \(d_i\) 也逐渐增加,除了最后一个stage。因此作者在图7(right)中用 \(d_{i+1}\ge d_i\) 的准则测试了变体 \(AnyNetX_D\) 并看到了改进的结果。最后我们发现,对 \(w_i,d_i\) 的约束各减少了设计空间4!,和 \(AnyNetX_A\) 相比累计减少了 \(O(10^7)\)。

3.3 The RegNet Design Space

为了进一步深入理解模型结构,我们展示了来自 \(AnyNetX_E\) 中最好的20个模型,如图8(left-top)。

虽然单个模型(灰色曲线)中有显著的差异,但总体上显现出一种模式。同一幅图中还画出了线 \(w_j=48\cdot(j+1),0\le j\le 20\)(黑色实线,y轴取了对数)。这种线性拟合似乎解释了最优模型宽度增长的整体趋势。

为了看看类似的模式是否适用于单个模型,我们需要将一条线量化为一个分段常数函数。首先我们针对block宽度提出了线性参数化

其中包含三个参数,深度 \(d\), 初始宽度 \(w_0>0\),斜率 \(w_a>0\)。并为每个 \(j<d\) 的block生成一个不同的宽度 \(u_j\)。为了量化 \(u_j\),我们提出了一个额外的参数 \(w_m>0\) 按如下方式控制量化。首先根据式(2)得到 \(u_j\),我们按下式计算每个block \(j\) 的 \(s_j\)

然后为了量化 \(u_j\) 我们对 \(s_j\) 四舍五入(表示为\(\left\lfloor s_j\right\rceil\)),并按下式计算量化的每个block的宽度 \(w_j\)

我们可以通过计算具有常量宽度的block的数量将per-block \(w_j\) 转换为per-stage的形式,即每个stage \(i\) 的block宽度为 \(w_i=w_0 \cdot w_m^i\),block数量为 \( d_i={\textstyle \sum_{j}^{}} \mathbf{1}\left[\left\lfloor s_j\right\rceil=i\right]\)。

我们通过拟合来自AnyNetX的模型来测试这种参数化方法,给定一个模型,我们计算拟合,通过将 \(d\) 设置为网络深度,并对 \(w_0,w_a,w_m\) 进行网格搜索来最小化预测和观察的per-block宽度的平均对数比 \(e_{fit}\)。来自 \(AnyNetX_E\) 的最好的两个网络见图8(top-right)。可以看出量化线性拟合(虚线)和这些最好的模型(实现)拟合的很好。

为了进一步测试线性参数化,我们设计了一个只包含具有这种线性结构模型的设计空间,我们通过6个参数 \(d,w_0,w_a,w_m,b,g\) 来指定一个网络结构。然后根据式(2)-(4)得到block宽度和深度。这个设计空间称为RegNet,它只包含简单有规律的模型。我们像之前一样采样 \(d<64,w_0,w_a<256,1.5\le w_m \le 3,b,g\)(根据 \(AnyNetX_E\) 的 \(e_{fit}\) 设置的采样范围)。

RegNetX的error EDF如图9(left)所示。RegNetX中模型的平均误差低于AnyNetX同时还保留了最佳模型。在图9(middle)中我们测试了两个进一步简化的版本。首先使用 \(w_m=2\)(stages之间宽度翻倍)略微提高了EDF,但我们发现 \(w_m\ge 2\) 表现的更好。其次我们测试了 \(w_0=w_a\),进一步简化线性参数化为 \(u_j=w_a\cdot(j+1)\)。有趣的是表现也更好了。但是为了保持模型的多样性,这两个约束我们都不添加。最后如图9(right)所示,RegNetX随机搜索的效率要高得多,只搜索32个模型就可能得到好的模型。

表1总结了设计空间的大小(对于RegNet我们通过量化其连续参数来估计大小)。在设计RegNetX中,我们将原始AnyNetX设计空间的维度从16维降到6维,减少了接近10个数量级。但我们注意到,RegNet仍然包含各种不同的模型,可以针对各种设置进行调整。

3.4 Design Space Generalization

我们是在一个low-compute、low-epoch的配置下设计的RegNet设计空间,其中只包含一种block类型。但我们的目标不是为单个配置设计一个设计空间,而是找到可以推广到新的网络设计的通用原则。

在图10中,我们在更高flops、更多的epochs、5个stage的网络、以及各种不同类型的block设置下将 \(RegNetX\) 与 \(AnyNetX_A\) 和 \(AnyNetX_E\) 进行比较。所有情况下设计空间的顺序都是一致的,即 \(RegNetX>AnyNetX_E>AnyNetX_A\)。换句话说,我们没有看到过拟合的迹象。这个结果表明RegNet可以推广到新的设置,5个stage的结果表明RegNet的规则结构可以推广到更多stage,即使 \(AnyNetX_A\) 有更多的自由度。

4 Analyzing the RegNetX Design Space

接下来我们进一步分析RegNetX的设计空间,并重新审视常见的深度网络的设计选择。我们的分析得到了令人惊讶的结果并与流行的实践不匹配,这让我们可以用简单的模型获得好的结果。

由于RegNetX设计空间有丰富的好模型,接下来的我们改为采样较少的模型(100个)并训练更长时间(25个epoch)学习率为0.1。我们这样做是为了观察网络行为中更细粒度的趋势。

RegNet trends. 我们在图11中展示了不同flop下RegNetX参数的趋势。值得注意的是,最佳模型的深度在不同区域下是稳定的(top-left),最佳深度约为20个block(60层)。这与更高的flop用更深的模型的常规做法形成鲜明的对比。我们还观察到最佳模型使用的bottleneck ratio为1(top-middle),这有效地去除了bottleneck(实际应用中通常会使用)。接下来我们观察到好模型的宽度multiplier约为2.5(top-right),与常见的跨阶段宽度加倍的配方相似但不完全相同。其余的参数 \((g,w_a,w_0)\) 随着复杂度的更加而增加(bottom)。

Complexity analysis. 除了flops和参数外,我们还分析了网络激活activations,我们将其定义为所有卷积层输出tensor的size(我们在图12的left-top中列出了常见卷积算子的复杂性度量)。激活虽然不是网络复杂度的常见度量,但激活会严重影响memory-bound硬件加速器(如GPU,TPU)上的运行时间,例如图12(top)。在图12(bottom)中,我们观察到,对于种群中的最佳模型,激活随着flops平方的增加而增加,参数随着flops线性增加,运行时间最好同时用一个线性项和一个平方项进行建模,因为它同时依赖于flops和activations。

RegNetX constrained. 利用这些发现,我们改进了RegNetX的设计空间。首先,基于图11(top),我们设置 \(b=1,d\le40,w_m\ge2\)。其次,我们根据图12(bottom)限制了参数和激活。这得到了快速、低计算、低内存的模型同时又不影响精度。在图13中,我们用这些约束测试了RegNetX并观察到约束版本在所有flop范围内都表现的更好。我们在后续于其它模型的对比中使用这个版本,并进一步限制深度 \(12\le d\le128\)。

Alternate design choices. 当前的移动端网络常使用inverted bottleneck(b<1)和深度卷积(g=1)。在图14(left)中,我们观察到inverted bottleneck略微降低了EDF而深度卷积表现的更差。接下来作者测试不同的输入分辨率如图14(middle),发现对于RegNetX,固定分辨率224x224效果最好,即使在更高的flops下。

SE. 最后作者评估了RegNetX加上Squeeze-and-Excitation的效果,如图14(right)可以看到得到了很好的提升。

代码解析

这里是MMPretrain中的实现,以400MF为例,不同flop的RegNetX的参数如下

代码和表中的值有一些出入,具体如下 

arch_settings = {
    'regnetx_400mf':
    dict(w0=24, wa=24.48, wm=2.54, group_w=16, depth=22, bot_mul=1.0),
    'regnetx_800mf':
    dict(w0=56, wa=35.73, wm=2.28, group_w=16, depth=16, bot_mul=1.0),
    'regnetx_1.6gf':
    dict(w0=80, wa=34.01, wm=2.25, group_w=24, depth=18, bot_mul=1.0),
    'regnetx_3.2gf':
    dict(w0=88, wa=26.31, wm=2.25, group_w=48, depth=25, bot_mul=1.0),
    'regnetx_4.0gf':
    dict(w0=96, wa=38.65, wm=2.43, group_w=40, depth=23, bot_mul=1.0),
    'regnetx_6.4gf':
    dict(w0=184, wa=60.83, wm=2.07, group_w=56, depth=17, bot_mul=1.0),
    'regnetx_8.0gf':
    dict(w0=80, wa=49.56, wm=2.88, group_w=120, depth=23, bot_mul=1.0),
    'regnetx_12gf':
    dict(w0=168, wa=73.36, wm=2.37, group_w=112, depth=19, bot_mul=1.0),
}

然后根据式(2)-(4)得到每个stage的宽度 \(w_j\),如下

def generate_regnet(self,
                    initial_width,  # 24
                    width_slope,  # 24.48
                    width_parameter,  # 2.54
                    depth,  # 22
                    divisor=8):
    """Generates per block width from RegNet parameters.

    Args:
        initial_width ([int]): Initial width of the backbone
        width_slope ([float]): Slope of the quantized linear function
        width_parameter ([int]): Parameter used to quantize the width.
        depth ([int]): Depth of the backbone.
        divisor (int): The divisor of channels. Defaults to 8.

    Returns:
        tuple: tuple containing:
            - list: Widths of each stage.
            - int: The number of stages.
    """
    assert width_slope >= 0
    assert initial_width > 0
    assert width_parameter > 1
    assert initial_width % divisor == 0
    widths_cont = np.arange(depth) * width_slope + initial_width  # 式(2)
    ks = np.round(
        np.log(widths_cont / initial_width) / np.log(width_parameter))  # 式(3)
    widths = initial_width * np.power(width_parameter, ks)  # 式(4)
    widths = np.round(np.divide(widths, divisor)) * divisor  # 这里文中好像没提到
    num_stages = len(np.unique(widths))
    widths, widths_cont = widths.astype(int).tolist(), widths_cont.tolist()
    return widths, num_stages

  • 29
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: RegNet 是一个研究人员提出的深度神经网络架构,它在计算效率和准确性之间取得了良好的平衡。在 PyTorch 中,可以使用以下代码实现 RegNet: ```python import torch import torch.nn as nn class ConvBlock(nn.Module): def __init__(self, in_channels, out_channels, kernel_size, stride, padding): super(ConvBlock, self).__init__() self.conv = nn.Conv2d(in_channels, out_channels, kernel_size, stride, padding) self.bn = nn.BatchNorm2d(out_channels) self.relu = nn.ReLU(inplace=True) def forward(self, x): x = self.conv(x) x = self.bn(x) x = self.relu(x) return x class RegNet(nn.Module): def __init__(self, width_multiplier, depth_multiplier, input_channels=3, output_classes=1000): super(RegNet, self).__init__() # 输入特征图尺寸 input_size = 224 # 定义 RegNet 的基础参数 base_channels = 16 b = 1.0 w_a = 26.0 w_0 = 96.0 d = 1.0 # 计算 RegNet 的参数 depths = [1, 1, 4, 7, 12, 10, 7, 4, 1] w_b = w_a * b / 1.5 widths = [] for i in range(len(depths)): di = depths[i] wi = int(round(w_0 * w_b ** i)) wi = int((wi / w_a) * width_multiplier) wi = (wi // 8) * 8 widths.append(wi) # 构建 RegNet 的网络结构 self.stem = ConvBlock(input_channels, base_channels, kernel_size=3, stride=2, padding=1) self.stages = nn.ModuleList([]) stage_index = 0 in_channels = base_channels for i in range(len(depths)): depth = int(round(depth_multiplier * depths[i])) out_channels = widths[i] for j in range(depth): stride = 2 if j == 0 and i > 0 else 1 if in_channels == out_channels: if j == 0: block = nn.Identity() else: block = ConvBlock(in_channels, out_channels, kernel_size=3, stride=stride, padding=1) else: block = ConvBlock(in_channels, out_channels, kernel_size=1, stride=stride, padding=0) self.stages.append(block) in_channels = out_channels stage_index += 1 self.avgpool = nn.AdaptiveAvgPool2d((1, 1)) self.fc = nn.Linear(widths[-1], output_classes) def forward(self, x): x = self.stem(x) for stage in self.stages: x = stage(x) x = self.avgpool(x) x = torch.flatten(x, 1) x = self.fc(x) return x ``` 在这段代码中,我们首先定义了一个 `ConvBlock` 类,用于构建 RegNet 的基本卷积块。然后我们定义了 `RegNet` 类,该类继承自 PyTorch 中的 `nn.Module` 类,是 RegNet 的主体部分。在 `__init__` 方法中,我们首先定义了 RegNet 的基本参数,然后计算了 RegNet 的宽度和深度,最后使用这些参数构建 RegNet 的网络结构。 在 `forward` 方法中,我们首先将输入图像经过 `stem` 卷积块进行特征提取,然后将特征图传递给 `stages` 中的每一个卷积块,最后经过全局平均池化和全连接层输出分类结果。 ### 回答2: RegNet是一种高效的神经网络架构,它通过参数化的规则来设计网络结构,以实现更好的性能和效果。RegNet Python实现了RegNet的网络结构和训练过程。 RegNet的实现通常基于Python深度学习框架,如PyTorch或TensorFlow等。在Python中,我们可以使用这些框架提供的各种功能和库,来实现RegNet。 首先,我们需要定义RegNet的网络结构。这包括网络的深度、宽度、分辨率和特征数量等。我们可以使用Python代码来定义RegNet的模型对象,并通过设置不同的参数来创建不同的网络结构。 然后,我们需要实现RegNet的前向传播算法。这涉及到将输入样本输入网络,并通过一系列的卷积、激活函数、池化等操作层层传递,最后输出预测结果。在Python中,我们可以使用框架提供的卷积、激活函数和池化等函数来实现这些操作。 接下来,我们需要实现RegNet的训练过程。这包括定义损失函数、选择优化器和调整模型参数等。在Python中,我们可以使用框架提供的损失函数和优化器,并结合训练数据来训练网络。通过将数据输入网络并根据真实标签计算误差,我们可以使用优化器来调整网络参数,以最小化误差。 最后,我们可以使用训练好的RegNet模型来进行预测。通过将测试数据输入网络,并获得网络输出,我们可以得到模型对输入数据的预测结果。 总而言之,RegNet Python实现可以通过定义网络结构、编写前向传播算法、训练模型和进行预测等步骤来实现。这些步骤都借助了Python深度学习框架提供的功能和库。 ### 回答3: RegNet是一种用于神经网络架构设计的方法,它基于自动化搜索技术,通过对大规模神经网络模型的训练和评估,自动选择并生成一组最优的网络架构。 RegNet的python实现可以通过使用深度学习框架如PyTorch、TensorFlow等来实现。以下是一个基本的RegNet实现的步骤: 1. 数据准备:首先,准备训练和验证数据集。根据具体的任务和数据集,加载和预处理数据,例如图像分类任务中的图像预处理。 2. 构建网络:使用深度学习框架创建RegNet网络结构。RegNet通常由多个块组成,每个块包含一系列的卷积和池化操作。可以根据具体需求和任务,在每个块中选择合适的卷积和池化操作。 3. 定义损失函数:根据任务类型,选择适当的损失函数,例如交叉熵损失函数用于分类任务。 4. 网络训练:使用训练数据集对网络进行训练。通过向前传播计算损失,并通过反向传播更新网络权重。可以使用优化器,如随机梯度下降(SGD)或Adam来优化网络。 5. 模型评估:使用验证数据集评估模型的性能和准确度。通过将验证数据集传递给网络,计算预测结果并与真实标签进行比较,从而计算准确度、精确度、召回率等指标。 6. 模型调整:根据评估结果和需求,对网络进行调整和优化。可以尝试不同的超参数、网络结构和优化算法,以获得更好的性能。 7. 模型应用:在完成训练和调优后,可以使用训练好的RegNet模型进行预测和应用。将待预测的数据输入网络中,获取网络的输出结果并进行后续的处理和分析。 总的来说,RegNet是一种用于指导神经网络架构设计的自动化方法,通过在深度学习框架下实现,可以根据任务需求和数据集特点来构建并优化网络,从而实现高性能的深度学习模型。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

00000cj

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值