§1.1.4

EfficientNet / RegNet 的 scaling 设计?

核心概念

EfficientNet 和 RegNet 的核心都是关于模型缩放(Model Scaling),即如何在给定的计算资源(如 FLOPS)下,通过调整网络架构的维度(深度、宽度、分辨率)来获得最佳性能。

  • EfficientNet 提出了一种复合缩放(Compound Scaling)方法。它认为,与其独立地、凭经验地缩放网络深度(depth)、宽度(width)和图像分辨率(resolution),不如用一个统一的复合系数 ϕ\phi均衡地缩放这三个维度,以达到更优的效率和精度。

  • RegNet 则采用了一种完全不同的思路。它并非从一个固定的基线网络出发进行缩放,而是首先定义一个包含众多可能网络结构的巨大设计空间(Design Space),然后通过对该空间中大量模型进行采样和统计分析,发现并提炼出优秀网络所共有的设计原则(Design Principles),最终形成一个简单、规则化、可预测的"网络族"。

原理与推导

EfficientNet: 复合缩放

1. 问题定义

一个卷积网络可以被定义为一个函数 N=i=1..sFiLi(XHi,Wi,Ci)N = \bigodot_{i=1..s} F_i^{L_i}(X_{\langle H_i, W_i, C_i \rangle}),其中 FiF_i 是第 ii 个阶段的操作,重复 LiL_i 次,输入张量为 XXHi,Wi,CiH_i, W_i, C_i 分别是其高、宽和通道数。模型缩放的目标是优化精度,同时受限于计算和内存资源。

网络缩放主要涉及三个维度:

  • 深度 (Depth, dd): 网络的层数,即 LiL_i
  • 宽度 (Width, ww): 网络的通道数,即 CiC_i
  • 分辨率 (Resolution, rr): 输入图像的尺寸 H,WH, W

2. 复合缩放公式

EfficientNet 提出用一个简单而有效的复合系数 ϕ\phi 来统一缩放这三个维度。缩放规则如下:

depth: d=αϕwidth: w=βϕresolution: r=γϕ\begin{align*} \text{depth: } & d = \alpha^\phi \\ \text{width: } & w = \beta^\phi \\ \text{resolution: } & r = \gamma^\phi \end{align*}

其中,α,β,γ\alpha, \beta, \gamma 是通过在基线小模型(EfficientNet-B0)上进行网格搜索得到的常数。这些系数需要满足一个约束条件,以确保总计算量(FLOPS)的增长是可控的。

3. FLOPS 约束与推导

一个网络的 FLOPS 大致与 dw2r2d \cdot w^2 \cdot r^2 成正比。

  • 深度加倍,FLOPS 加倍。
  • 宽度加倍,FLOPS 变为 4 倍(卷积核的输入和输出通道都加倍)。
  • 分辨率加倍,FLOPS 变为 4 倍(特征图面积变为 4 倍)。

因此,当我们使用复合缩放时,总的 FLOPS 增长大约是:

FLOPS(αϕ)(βϕ)2(γϕ)2=(αβ2γ2)ϕ\text{FLOPS} \propto (\alpha^\phi) \cdot (\beta^\phi)^2 \cdot (\gamma^\phi)^2 = (\alpha \cdot \beta^2 \cdot \gamma^2)^\phi

为了让用户指定的缩放系数 ϕ\phi 能够以一种可预测的方式控制计算量,例如,我们希望当 ϕ\phi 增加 1 时,FLOPS 增加一倍,即总 FLOPS 增长约为 2ϕ2^\phi。这就导出了核心约束:

αβ2γ22\alpha \cdot \beta^2 \cdot \gamma^2 \approx 2

通过在 EfficientNet-B0 上的小型网格搜索,作者发现最优的系数组合为:

α1.2,β1.1,γ1.15\alpha \approx 1.2, \quad \beta \approx 1.1, \quad \gamma \approx 1.15

这些值是在固定 ϕ=1\phi=1 的情况下,在满足上述约束的条件下搜索得到的。

4. 直观解释

复合缩放的直观解释是平衡。想象一个圆柱体,其体积是 V=πr2hV = \pi r^2 h。如果你想让体积加倍,可以只把高度 hh 加倍,也可以只把半径 rr 增加 2\sqrt{2} 倍。但最有效的方式(在表面积增长相对较小的情况下)是同时增加 rrhh。类似地,对于神经网络,同时平衡地增加深度、宽度和分辨率,比极端地只增加一个维度能更有效地利用新增的计算资源来提升模型精度。

RegNet: 从设计空间中发现规律

RegNet 的方法论是其精髓,它分为三个步骤:

1. 定义一个灵活的初始设计空间 (AnyNet)

AnyNet 设计空间非常宽泛,它允许网络有不同的深度、宽度、block 类型(如 standard, bottleneck)、group width 等。例如,每个 stage 的 block 数量和宽度都可以自由选择。这个空间的目的是为了不带偏见地覆盖尽可能多的网络结构。

2. 采样、训练与分析

从 AnyNet 空间中随机采样数千个模型,在相同的计算资源(如 400M FLOPS)下,对它们进行短暂的训练(例如 10 个 epoch),并记录其误差。然后,选取表现最好的模型(例如误差最低的 500 个)。

接下来,对这些"好模型"的结构参数进行统计分析。作者使用**经验分布函数(Empirical Distribution Function, EDF)**来观察这些参数的分布规律。

3. 提炼设计原则并构建 RegNet 空间

通过分析 EDF,作者发现了一些惊人的规律,这些规律就是优秀网络共有的设计原则:

  • 规律一: 优秀网络的 stage 宽度是单调递增的。
  • 规律二: 随着网络深度增加,最优的 bottleneck ratio(通道扩展比例)趋向于一个常数。
  • 规律三(核心): 优秀网络的各 stage 宽度 wiw_i(其中 ii 是 stage 索引)可以用一个量化的线性函数来描述。

具体来说,一个线性的 stage 宽度分配规则可以表示为:

wi=w0+waifor i=0,,d1w_i = w_0 + w_a \cdot i \quad \text{for } i=0, \dots, d-1

其中,dd 是 stage 数量,w0w_0 是初始宽度,waw_a 是线性斜率。

为了使网络结构更规整且对硬件友好,作者对这个线性函数进行了量化。他们发现,将连续的宽度值"吸附"到由一个几何级数定义的离散值上效果很好。量化过程如下:

  1. 计算每个 stage 的连续宽度 ui=w0+waiu_i = w_0 + w_a \cdot i
  2. 计算量化步长 si=\round(log(ui/w0)log(wm))s_i = \round\left( \frac{\log(u_i / w_0)}{\log(w_m)} \right),其中 wm>1w_m > 1 是量化参数。
  3. 计算最终的量化后宽度 wi=w0wmsiw_i = w_0 \cdot w_m^{s_i}

这个过程将连续的线性增长宽度转化为了分段常数(阶梯状)的几何增长宽度,这与 ResNet 等经典网络的设计不谋而合,但 RegNet 是通过数据驱动的方式发现的。

最终,RegNet 设计空间由几个简单的参数定义:深度 dd,初始宽度 w0w_0,斜率 waw_a,量化参数 wmw_m,以及 group width gg。通过在不同 FLOPS 等级下搜索这些参数,就得到了一系列的 RegNetX 和 RegNetY 模型。

代码实现

以下代码展示了如何根据给定的缩放系数或设计参数来生成 EfficientNet 和 RegNet 的网络维度配置。

python
1import math
2import numpy as np
3
4# --- EfficientNet Scaling Logic ---
5
6def get_efficientnet_params(phi, base_model_params, scaling_coeffs):
7 """
8 根据复合系数 phi 计算 EfficientNet 的缩放参数。
9
10 Args:
11 phi (float): 用户定义的复合缩放系数。
12 base_model_params (dict): EfficientNet-B0 的基础参数。
13 scaling_coeffs (dict): 缩放系数 alpha, beta, gamma。
14
15 Returns:
16 dict: 缩放后的模型参数。
17 """
18 alpha = scaling_coeffs['alpha'] # for depth
19 beta = scaling_coeffs['beta'] # for width
20 gamma = scaling_coeffs['gamma'] # for resolution
21
22 # 1. 计算深度、宽度和分辨率的乘数
23 # depth_mult = alpha ** phi
24 # width_mult = beta ** phi
25 # resolution_mult = gamma ** phi
26
27 # 论文中的实际计算方式,直接使用phi作为指数
28 depth_mult = alpha ** phi
29 width_mult = beta ** phi
30
31 # 2. 计算缩放后的深度和宽度
32 # 深度需要向上取整到整数
33 scaled_depth = math.ceil(base_model_params['depth'] * depth_mult)
34
35 # 宽度需要向上取整到最接近8的倍数,以利于硬件计算
36 scaled_width = round(base_model_params['width'] * width_mult)
37 divisor = 8
38 scaled_width = max(divisor, int(scaled_width + divisor / 2) // divisor * divisor)
39
40 # 3. 计算缩放后的分辨率
41 # 分辨率也需要调整到能被特定值整除(通常是网络步幅的倍数)
42 scaled_resolution = round(base_model_params['resolution'] * (gamma ** phi))
43
44 return {
45 'depth': scaled_depth,
46 'width': scaled_width,
47 'resolution': scaled_resolution,
48 'phi': phi
49 }
50
51# --- RegNet Scaling Logic ---
52
53def generate_regnet_params(d, w0, wa, wm, g):
54 """
55 根据 RegNet 的设计原则生成每个 stage 的宽度和深度。
56
57 Args:
58 d (int): 网络 body 的总深度 (block 数量)。
59 w0 (int): 初始宽度。
60 wa (float): 宽度斜率。
61 wm (float): 宽度量化乘数。
62 g (int): Group convolution 的 group width。
63
64 Returns:
65 dict: 包含每个 stage 宽度和深度的参数。
66 """
67 # 1. 计算每个 block 的理想宽度 (线性增长)
68 # u_i = w_0 + w_a * i
69 block_indices = np.arange(d)
70 u_per_block = w0 + wa * block_indices
71
72 # 2. 量化 block 宽度
73 # s_i = round(log(u_i / w_0) / log(w_m))
74 s_per_block = np.round(np.log(u_per_block / w0) / np.log(wm))
75
76 # w_i = w_0 * w_m^s_i
77 w_per_block = w0 * np.power(wm, s_per_block)
78
79 # 3. 确保宽度是 group width 的整数倍
80 # w_i' = round(w_i / g) * g
81 w_per_block = np.round(w_per_block / g) * g
82
83 # 4. 将具有相同宽度的连续 block 分组成 stage
84 stage_widths, stage_depths = np.unique(w_per_block.astype(int), return_counts=True)
85
86 # 5. 计算每个 stage 的 group 数
87 # group_counts = width / group_width
88 stage_groups = stage_widths / g
89
90 return {
91 'stage_widths': stage_widths.tolist(),
92 'stage_depths': stage_depths.tolist(),
93 'stage_groups': stage_groups.tolist()
94 }
95
96if __name__ == '__main__':
97 print("--- EfficientNet Scaling Demo ---")
98 # EfficientNet-B0 的基础参数
99 b0_params = {'width': 32, 'depth': 16, 'resolution': 224} # 这里的width/depth是示例性的,实际是每层的配置
100 # 论文中找到的缩放系数
101 coeffs = {'alpha': 1.2, 'beta': 1.1, 'gamma': 1.15}
102
103 # 缩放到 B1 (phi=1) 和 B2 (phi=2)
104 b1_scaled_params = get_efficientnet_params(1, b0_params, coeffs)
105 b2_scaled_params = get_efficientnet_params(2, b0_params, coeffs)
106
107 print(f"EfficientNet-B0 (phi=0): {b0_params}")
108 print(f"EfficientNet-B1 (phi=1) Scaled Params: {b1_scaled_params}")
109 print(f"EfficientNet-B2 (phi=2) Scaled Params: {b2_scaled_params}")
110
111 print("\n--- RegNet Scaling Demo ---")
112 # RegNetX-400MF 的设计参数
113 # 400MF 表示约 400 Million FLOPS
114 regnet_400mf_params = {
115 'd': 16, # depth
116 'w0': 32, # initial width
117 'wa': 6.95, # width slope
118 'wm': 2.43, # width quantization
119 'g': 16 # group width
120 }
121
122 generated_params = generate_regnet_params(**regnet_400mf_params)
123 print(f"Generated params for a RegNet model (like RegNetX-400MF):")
124 for i in range(len(generated_params['stage_widths'])):
125 print(f" Stage {i+1}: width={generated_params['stage_widths'][i]}, "
126 f"depth={generated_params['stage_depths'][i]}, "
127 f"groups={int(generated_params['stage_groups'][i])}")

工程实践

  • 使用场景:

    • EfficientNet: 由于其出色的效率(在同等 FLOPS 下精度很高),EfficientNet(特别是 B0 到 B4)已成为移动端和边缘设备视觉任务的首选骨干网络。它也是各种计算机视觉任务(分类、检测、分割)一个非常强大的基线模型。
    • RegNet: RegNet 模型(特别是带有 Squeeze-and-Excitation 的 RegNetY)在各种计算量级上都表现出极强的性能,常常在同等 FLOPS 下超过 EfficientNet。由于其结构更规整(例如,没有 MBConv 中的复杂连接),在某些硬件(如 GPU)上,RegNet 的实际吞吐量(images/sec)可能比 FLOPS 相当的 EfficientNet 更高。
  • 超参数选择:

    • EfficientNet: 主要选择是 B0B7 中的哪一个。这是一个直接的精度 vs. 资源权衡。从 B0B1 开始,如果计算预算允许且需要更高精度,再向上扩展。
    • RegNet: 模型族(如 RegNetX-400MF, RegNetY-8GF)的命名直接反映了其大致的 FLOPS,使得选择非常直观。可以直接根据你的 FLOPS 预算来选择模型。RegNetY 通常比 RegNetX 性能更好,因为它加入了 SE 模块。
  • 性能 / 显存 / 吞吐 的权衡:

    • FLOPS vs. 实际速度: FLOPS 是一个理论指标。EfficientNet 使用了大量的深度可分离卷积(Depthwise Separable Convolution),这大大降低了 FLOPS,但在某些硬件上(如老式 GPU 或特定加速器),其内存访问成本(Memory Access Cost, MAC)可能较高,导致实际速度不如结构更简单的 RegNet。
    • 显存: EfficientNet 的高阶模型(如 B6, B7)使用非常高的分辨率,这会消耗大量显存,成为训练和推理的瓶颈。
    • 硬件亲和性: RegNet 的设计(如固定的 group width,简单的 bottleneck block)对现代 GPU 和 AI 加速器通常更友好,更容易实现高吞-吐量。
  • 常见坑和调试技巧:

    • 预处理不匹配: 使用预训练的 EfficientNet/RegNet 模型时,必须使用与原始训练完全相同的图像预处理方法,特别是归一化的均值和标准差,否则性能会大幅下降。
    • 关键组件缺失: 如果从头实现 EfficientNet,不要忘记一些关键组件,如 Swish (SiLU) 激活函数Squeeze-and-Excitation (SE) 模块Stochastic Depth (DropConnect),这些对最终性能至关重要。
    • 缩放规则实现错误: 实现复合缩放时,对深度和宽度的取整规则要特别小心。错误的取整可能导致模型参数与原版有较大差异。

常见误区与边界情况

  • 误区一:复合缩放就是把所有维度都放大

    • 辨析: 关键不在于"都放大",而在于"均衡地"放大。这个均衡的比例(α,β,γ\alpha, \beta, \gamma)是经过实验搜索出来的,并且由单一变量 ϕ\phi 控制。随意地、不成比例地放大三个维度,效果远不如复合缩放。
  • 误区二:RegNet 只是另一个手工设计的网络

    • 辨析: RegNet 的核心贡献是其发现设计原则的方法论,而不是某个具体的网络实例。它展示了如何从一个巨大的、无偏见的设计空间中,通过统计方法自动发现构建高性能网络的通用规则。RegNetX/Y 模型族是这个方法论的产物。
  • 边界情况与面试追问:

    • 追问:EfficientNet 的 ϕ\phi 无限增大会怎样?
      • 回答要点: 理论上模型会无限变大。但实践中,当 ϕ\phi 过大(如远超 B7 对应的 ϕ=6\phi=6),模型会变得极难训练(需要海量数据、超长训练周期、复杂的正则化技巧),且精度收益会急剧饱和(Diminishing Returns)。同时,巨大的分辨率和深度会带来难以承受的显存和计算开销。
    • 追问:EfficientNet 和 RegNet 的设计哲学有何根本不同?
      • 回答要点:
        • 起点不同: EfficientNet 从一个通过神经架构搜索(NAS)找到的优秀个体 (B0) 出发,然后定义如何**“繁殖”这个个体。RegNet 则是研究一个优秀群体的共性,从而定义了创造新个体的“遗传法则”**。
        • 目标不同: EfficientNet 的目标是为一个给定的基线网络找到最优的缩放策略。RegNet 的目标是找到一个普适的、可以生成一系列优秀网络的网络设计空间
    • 追问:如果给你一个固定的 FLOPS 预算,你会选择 EfficientNet 还是 RegNet?
      • 回答要点: 这是一个典型的权衡问题。我会说:
        1. 基准测试为王: 最可靠的方法是在目标硬件上对两者进行基准测试,比较精度和实际吞吐量。
        2. 硬件考量: 如果部署在通用 GPU 上,我会倾向于先尝试 RegNetY,因为它通常有更好的硬件利用率和吞吐量。
        3. 生态与成熟度: EfficientNet 的生态系统(预训练模型、教程)可能更成熟一些,特别是对于移动端部署,有大量优化过的实现。
        4. 结论: 如果追求极致的推理速度(images/sec),RegNetY 可能是更好的起点。如果追求一个稳定、可靠且资源丰富的基线,EfficientNet 是一个非常安全的选择。
相关题目