news 2026/10/16 2:48:14

EfficientNet模型详解:复合缩放原理与B0到B7选型实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
EfficientNet模型详解:复合缩放原理与B0到B7选型实战指南

做深度学习这几年,我越来越觉得“模型精度”不是一个孤立的指标,它背后永远是算力、显存、响应时间、开发周期这些现实成本的加权。EfficientNet 这个名字,凡是搞过图像分类的应该都不陌生,它当年最大的贡献不是在某一个榜单上把 Top-1 推高了多少,而是把“如何在有限算力预算内拿到更好的精度”这件事系统化、工程化了。这篇文章会用做项目踩过坑的视角,把 EfficientNet 的模块设计、复合缩放原理、B0 到 B7 的选型方式,以及真正跑训练和部署时容易遇到的问题都梳理一遍,适合打算把它用进实际业务的同学。

1. 复合缩放为什么是 EfficientNet 的灵魂

1.1 效率这件事:FLOPs、参数量、延迟到底谁最重要

讨论模型效率之前,先要把三个概念掰开:FLOPs、参数量、延迟。FLOPs 是理论计算量,表示模型做一次前向要多少次浮点运算;参数量表示模型占多少存储。这两个指标最容易获取,论文里到处都是,但实际部署时真正卡脖子的是延迟和峰值内存。

延迟取决于算力、内存带宽、算子和硬件调度。同样一个模型,在不同硬件上的表现差异可能超过一倍,甚至更多。有些网络在 GPU 上跑得飞快,一到某些移动端芯片上就原形毕露,原因往往是深度可分离卷积在这些平台上缺少底层优化,看似计算量小,实际kernel调度开销巨大。所以选 EfficientNet 的哪个版本,不能只看 FLOPs 表,得结合自己目标设备的实测速度来定。

我自己的判断标准是这样的:如果做移动端或边缘设备,延迟和内存占用优先级最高;如果做服务端离线推理,FLOPs 反而是次要的,吞吐量和显存利用率更关键;如果做学术对比或刷点,精度永远是第一位。EfficientNet 的巧妙之处在于,它用一套统一的方法约束了参数量和 FLOPs 的增长,让选择模型变成了选择算力预算的问题。

1.2 单独调宽度、深度、分辨率都有瓶颈

在 EfficientNet 之前,改进卷积网络的主流思路是加深或者加宽。ResNet 系列靠增加层数拿到了红利,但网络深到一定程度后,梯度回传愈发困难,单纯堆层数会撞上收益递减的墙。加宽度,也就是增加每层通道数,能更快让模型“看到更多特征”,但很多实验证明,当通道数膨胀到一定规模后,增益会快速下降,同样存在饱和问题。

提高输入分辨率其实是最直觉的提升手段,因为图片更清楚,模型能看到更细的纹理。代价则非常直接:分辨率是平方关系,图像边长增大一倍,计算量直接翻四倍。在算力有限的前提下,盲目提高分辨率会很快吃掉所有预算。

更关键的是,这三个维度不是独立的。深层网络需要更宽的特征通道来表达更丰富的信息,宽网络也需要足够的分辨率来保留细节,分辨率提高后又需要更深的结构去提炼高级语义。单独调任何一个维度,都会让网络在某个方面成为瓶颈。这就是为什么以往那种只加深、只加宽或者只提分辨率的做法,很快遇到天花板的原因。

1.3 复合缩放的数学约束是怎么推出来的

EfficientNet 提出用一组系数同时缩放宽度、深度和分辨率。假设初始网络深度为 d、宽度为 w、分辨率为 r,复合缩放后分别变成 d^φ、w^φ、r^φ。其中深度按 α 的 φ 次方缩放,宽度按 β 的 φ 次方缩放,分辨率按 γ 的 φ 次方缩放,公式写作:

  • 深度 d = α^φ
  • 宽度 w = β^φ
  • 分辨率 r = γ^φ

约束条件为 α、β、γ 都不小于 1,且 α·β²·γ² ≈ 2。

为什么是平方项?原因在于计算量对不同维度的敏感程度不一样:深度对 FLOPs 的影响是线性的,宽度和分辨率则是平方关系。卷积层的计算量大约是输出通道数乘输入通道数乘以卷积核大小和空间分辨率。宽度增加一倍,所有卷积层的输入输出通道同时增加,所以计算量变成原来四倍;分辨率增加一倍,空间尺寸变成原来四倍,计算量也变成四倍;深度增加一倍,计算量线性增加一倍。

为了让总体 FLOPs 按 2^φ 的倍数增长,就需要把每个维度的增长指数压制到使总和满足 2^φ。用网格搜索在基线网络上找一组可行解,作者给出的经验值是 α 约为 1.2、β 约为 1.1、γ 约为 1.15。B0 是未缩放的基线模型,B1 到 B7 就是依次增加 φ 得到的结果。这种方法确保模型变大时,每个维度都在同步提升,而不是让某一块被拖后腿。

2. 三个关键结构部件:MBConv、SE、Swish

2.1 MBConv 模块里的倒残差和深度卷积

EfficientNet 的基线网络由一系列 MBConv 模块堆叠而成。MBConv 全称是 Mobile Inverted Bottleneck Convolution,是从 MobileNetV2 那块引入的结构,但 EfficientNet 对它的使用更系统。

拆开看一个 MBConv 模块,流程大概是这样:先做一个 1×1 卷积做通道扩张,通常把通道数放大六倍;然后接一个 3×3 或 5×5 的深度卷积,也就是每个通道单独做空间卷积;接着是一个 SE 注意力模块;最后用 1×1 卷积把通道数压缩回原来的规模。如果输入输出形状一致,就加一条残差捷径;如果 stride 为 2,则跳过残差连接并对空间尺寸下采样。

这个结构的设计动机很明确:深度卷积的计算量比普通卷积小得多,因为它不跨通道。普通卷积是每个输出通道要对所有输入通道做加权求和,深度卷积则完全没有跨通道交互,计算量约为普通卷积的九分之一。倒残差的含义是先压缩再扩张再压缩,中间的深度卷积在高维空间里做特征变换,两头用低维通道控制整体计算量。这种设计和经典残差块的先降维再升维正好相反,所以叫倒残差。

实际使用中,B0 到 B7 的 MBConv 配置并不完全一样。越靠后的阶段会用更大的 expansion 系数和更大的卷积核,同时通道数也随缩放系数增长。如果自己重新实现,最需要注意的就是 stride 为 2 时残差连接的处理,以及 SE 模块在两边的分支形状是否匹配。

2.2 SE 注意力为什么会提升准确率

SE 模块全称 Squeeze-and-Excitation,它做的事情可以概括成一句话:让网络在通道维度上学会“注意该注意的”。具体操作是,对特征图做全局平均池化,把每层通道压缩成一个数值;然后经过两个全连接层,中间经过一次非线性激活,最后用 sigmoid 输出一组 0 到 1 之间的权重,把原来特征图的每个通道按权重重新标定。

这个设计初看很朴素,但它抓住了卷积网络的一个弱点:默认情况下每个通道对输出的贡献是一样的,不管这个通道捕获的特征是否有用。SE 相当于是给每个通道装了一个可学习的阀门,模型训练时自动学会哪些通道重要、哪些通道可以不要。它的计算开销很小,全连接层把通道数先压到原来的四分之一或八分之一,只增加少量参数和 FLOPs,但带来的精度提升通常非常可观。

在 EfficientNet 里,SE 模块被放在深度卷积之后、最后那个 1×1 投影卷积之前。这个位置很讲究:深度卷积已经把空间信息提取了一轮,SE 根据全局信息调整通道权重,再做通道混合,信息流更顺畅。如果你把 SE 放到模块最前面或最后面,效果都会打折扣,这是我在实验中试过的一个细节。

2.3 Swish 激活函数带来的一点点甜头

EfficientNet 的另一个细节是激活函数用了 Swish。Swish 的公式是 x 乘以 sigmoid(x),它跟 ReLU 最大的区别是:ReLU 在输入为负时硬截断为 0,Swish 在输入为负时仍保留一个很小的负梯度;ReLU 曲线是不光滑的折线,Swish 是光滑且非单调的曲线。

光滑和非单调的特性,理论上能让梯度在负区间也保持流动,对深层网络的训练更友好。很多实验发现,在相同条件下把 ReLU 换成 Swish,精度会小幅提升,而且很少出现因为激活函数导致的训练动荡。代价是 sigmoid 的计算量略高,但在现代框架中,Swish 被融合成了 SiLU,在算子层面几乎不增加额外开销。

如果你要复现 EfficientNet,推荐直接使用框架里已经实现的 SiLU,不需要手动拼一个 Swish。手动实现除了慢,还容易因为数值稳定性问题在低精度训练时出现 NaN。

3. B0 到 B7:模型家族参数分析与选型思路

3.1 官方模型参数速查

下表列出 EfficientNet B0 到 B7 的主要公开参数和 ImageNet 上的 Top-1 精度。注意这些数字是在特定训练策略和数据增强下得到的,实际复现会有小幅浮动。

模型输入分辨率参数量理论FLOPsImageNet Top-1
B02245.3M0.39B77.1%
B12407.8M0.70B79.1%
B22609.2M1.0B80.1%
B330012M1.8B81.6%
B438019M4.2B82.9%
B545630M9.9B83.6%
B652843M19B84.0%
B760066M37B84.3%

这里有个观察很有意思:B5 到 B7 的参数量和 FLOPs 都在大幅增长,但精度只涨了不到一个点。这说明靠无脑提高分辨率和网络宽度来刷精度,收益会越来越小,这恰恰也印证了复合缩放思路本身的边界。实际业务里,B4 以上的版本往往只在精度要求极高的场景出现,大部分项目集中在 B0 到 B4 之间。

3.2 边缘端、服务端、高精度任务各自怎么选

选型第一步不是看精度表,而是定算力预算。边缘端设备如果只允许几百兆 FLOPs 的内存和延迟预算,B0 或 B1 是合理选择;它们的参数量只有 5M 到 8M,量化到 INT8 后体积很小,可以跑在低功耗芯片上。

服务端推理如果瓶颈是 GPU 显存,B4 到 B5 更合适,因为它们在 19M 到 30M 参数量下就能达到 83% 以上的精度。如果追求极致精度且不在乎推理时长,B7 是官方常规版本里最强的一个。但实际项目中,B7 的 600 输入分辨率对数据预处理和显存占用压力很大,通常需要混合精度和模型并行才能舒服地跑起来。

还有一类场景是实验室刷分。如果只看榜单数字,直接上 B7 甚至更激进的变体会显得很能打,但这背后大量训练资源被牺牲掉,容易得不偿失。我通常建议先摸清自己的显存是否装得下对应分辨率的 batch,再决定上哪档。一个稳妥的操作是,先用 B0 跑通全流程,再慢慢往上换更大的版本,这样调试成本最低。

3.3 当 Backbone 用在检测和分割里时的注意事项

EfficientNet 不仅在分类任务里发光,作为检测和分割的主干网络也很常见。把它用作 Backbone 时,有几个点必须想清楚。

第一个是下采样节奏。EfficientNet 的每个 stage 输出不同分辨率的特征图,拿到检测框架里做 FPN 时,通常要选 P3、P4、P5 这几层。不同缩放的 stage 结构和通道数不一样,需要和 FPN 的通道映射对齐,这在自定义实现里特别容易出错。

第二个是输入分辨率。检测任务里 Backbone 的输入分辨率不等于分类任务的标准分辨率。很多检测框架会用更大的输入,比如 800×1333,这时如果直接把分类预训练权重拿过来,某些 stage 的池化层或 padding 方式可能出现对齐问题。EfficientNet 没有全局池化,所以对输入尺寸的容错性整体还行,但下采样过程对奇数尺寸不友好,输入尺寸最好都能被 32 整除。

第三个是训练成本。EfficientNet 系列在检测任务里收敛速度往往比 ResNet 慢,因为深度可分离卷积和 SE 模块带来更复杂的梯度流。一般建议加载 ImageNet 预训练权重,同时把训练轮数适当拉长。如果数据量很小,从零训练 EfficientNet 主干经常会吃亏,不如先用 ResNet 练个基线,再考虑切换。

4. 实战:EfficientNet 图像分类的完整复现

4.1 数据准备与环境配置

假设手头是一个十类的小型图像数据集,每类几千张图。这种规模下,我不会从零训练 EfficientNet,而是直接加载预训练权重做迁移学习,这也是绝大多数业务场景的真实做法。

环境方面,PyTorch 是目前最顺手的框架,因为它生态里既有官方实现,又有大量预训练权重,直接调用就行。显存 8G 以下建议用 B0,8G 到 16G 可以跑 B2,16G 以上再考虑更大版本。如果显存紧张,最优先做的是开启混合精度训练,这一项能直接省掉将近一半显存,而且对精度几乎没有损伤。

数据处理部分主要做几件事:图片解码后缩放到训练分辨率,随机裁剪、随机翻转、颜色抖动,最后归一化。EfficientNet 论文里还用了 RandAugment 一类的自动增强策略,对小数据集很有帮助,因为它相当于免费扩大了数据分布。代码实现上直接用现成的图像增强库就能完成。

4.2 模型加载、微调训练的实现示例

加载模型时,可以直接从 torchvision 里导入 EfficientNet B0,替换最后的分类头。核心代码长这样:

import torch import torch.nn as nn from torchvision import models # 加载预训练权重 model = models.efficientnet_b0(weights=models.EfficientNet_B0_Weights.DEFAULT) # 替换分类层,适配自己的类别数 num_classes = 10 in_features = model.classifier[1].in_features model.classifier[1] = nn.Linear(in_features, num_classes)

替换分类头时要注意,EfficientNet 的分类器前还有一层 Dropout,通常默认是 0.2,小数据集可以保留或适当调低。如果数据集只有几百张,我建议前几十个 epoch 冻结主干,只训练分类头,因为这时候骨干网络的大梯度很容易破坏预训练特征。等分类头收敛后,再解冻主干,用较小的学习率全部微调几个 epoch。

训练时使用交叉熵损失,优化器选 AdamW,初始学习率设置在 1e-3 到 3e-3 之间,weight decay 给到 1e-4 左右。batch size 按显存能装下的最大程度来设,一般 32 到 128 之间。学习率用余弦退火调度,配合 warmup,可以在刚开始训练时避免大幅度震荡。

4.3 训练细节:学习率、EMA、正则化

训练 EfficientNet 和训练 ResNet 最大的区别在于,它对正则化更敏感。因为深度可分离卷积的参数量相对少,SE 模块又引入了注意力机制,模型在中小数据集上更容易过拟合。我踩过的一个典型坑是:不开正则化时训练集精度很高,验证集精度在二十个 epoch 后开始下滑,明显是过拟合。

解决办法有三个方向。第一是把数据增强力度拉大,尤其是随机裁剪比例和翻转概率,RandAugment 也可以上,它的强度系数从小到大调,观察验证集变化。第二是把 dropout 概率提高,尤其是在分类头前面的那层,0.3 到 0.4 都算合理范围,对于特别小的数据集可以加到 0.5。第三是用 EMA 指数移动平均,维护一份参数的历史均值,验证时使用这份平均参数,通常能稳定提升两三个点的精度。

EMA 的另一个好处是让训练过程更平滑。我在多个数据集上对比过,开了 EMA 的模型在训练后期几乎没有振荡,验证曲线更稳定,这对最终挑选 checkpoint 很有帮助。实现起来也不复杂,只需要在每个 step 结束后按动量系数更新 shadow 参数,等训练结束再从中取值做验证即可,很多训练框架都有现成接口。

4.4 推理阶段的优化与导出

训练完的模型要落地,还需要经过转换和优化。最快的转换路径是导出 ONNX,再用推理引擎的优化后端加快速度。导出前要注意输入尺寸固定,因为 ONNX 对动态尺寸支持虽有,但会引入额外算子,影响性能。如果业务要求多分辨率输入,建议分别导出几个静态尺寸的模型,或者直接选择所有场景中最大的分辨率。

如果目标是边缘设备,INT8 量化几乎是绕不开的一步。量化方式有两种:训练后量化和量化感知训练。数据分布和原始 ImageNet 差异大时,训练后量化容易掉点,这时推荐做量化感知训练,也就是在微调阶段模拟低精度误差,让模型学着自己适应量化噪声。这一步通常能挽回大部分精度损失。

还有一个容易被忽略的是 BatchNorm 折叠。ONNX 导出时,框架一般会自动把 BatchNorm 折叠进前面的卷积层。如果发现导出的模型在推理引擎里速度变慢,可以先检查是否做了折叠,再检查深度卷积算子是否被映射到高效内核。不同推理后端的算子支持差异挺大,换一个后端可能速度就完全不同,这些都需要提前做 benchmark。

5. 实战中的坑与排查技巧

5.1 复现精度对不上的各种原因

我自己复现 EfficientNet 时,最常见的困惑就是为什么论文里数据那么漂亮,自己训出来就少了一两个点。这里有一个非常现实的原因:ImageNet 的训练是一套极其复杂的流程,包含几百轮的训练、多种增强策略、EMA 和精细调参。业务数据上的微调与从头训练的精度没有直接可比性,不要被绝对数字绑架。

如果你确实在小数据集上发现模型不收敛,首先检查输入分辨率。EfficientNet 的预训练权重对应某个固定分辨率,微调时如果擅自改成别的分辨率,精度会受影响。相对安全的做法是沿用预训练时对应的分辨率,或者至少保证统一裁剪比例,训练和验证都用同样的预处理逻辑。

另外一个常踩的坑是标签不平衡。EfficientNet 在分类头上没有做任何特殊处理,如果类别分布差异很大,模型会倾向预测多数类。这时候需要在损失函数里加类别权重,或者用 Focal Loss 替代交叉熵。单纯加大学习率并不能解决这个问题。

5.2 显存不足和训练不收敛的解法

显存不够是最常见的硬性问题。优先开启混合精度训练,这一步能省一半显存,代价几乎为零。如果还不够,可以缩减 batch size 并配合梯度累积,用多个小 batch 的梯度累加来模拟大 batch 的效果,模型质量不会明显下降,只是训练时间会变长。

还有一个技巧是把 EfficientNet 的输入分辨率做阶梯式提升。先在小分辨率下跑通全流程,当时不炸,再逐步提高分辨率。这个思路跟论文里的渐进式训练类似,实测既能省显存,又能稳定训练过程,尤其适合那些数据分布不太规整的项目。

训练不收敛时,不要急着调模型结构,先看两件事:预训练权重的加载是否成功,以及学习率是否过大。B0 的参数量虽小,但 SE 模块会让梯度分布不太均匀,学习率稍大就容易震荡。用 AdamW 时可以从 1e-3 起步调低到 3e-4 试试,有时候只是这一个参数就把问题解决了。

5.3 这些场景下别执着于 EfficientNet

EfficientNet 不是万能的。有些场景下它的收益其实不明显,甚至还会拖后腿。

第一种是超低延迟场景。深度可分离卷积在部分硬件上可能没有优化,实际耗时反而大于普通卷积。这时候要么换一个为硬件深度优化的精简模型,要么选择把 MBConv 替换成普通卷积的变体,后者并不罕见,比如后来的 Fused-MBConv 就是因为这个原因被提出的。

第二种是超大 batch 分布式训练场景。EfficientNet 的参数量小,但训练内存开销并不低,因为深度卷积中间特征图很大,batch 增大后,通信和显存压力反而比同精度的其他结构高。这种情况建议先用 ResNet 或更简结构跑通,再考虑上 EfficientNet。

第三种是数据量特别大的场景。如果你有上千万张图且计算资源充足,EfficientNet 的复合缩放收益会被更通用的训练策略覆盖,很多后续工作也证明了这一点。不用为了“更先进”而强行使用它,模型只是工具,目标是精度和成本的最优解。

6. 从 EfficientNet 到 EfficientNetV2:设计语言的演进

6.1 EfficientNetV2 改了什么

EfficientNetV2 是在原版基础上对训练效率和结构细节做了进一步优化的版本,它最重要的改动有两个。

第一个改动是引入 Fused-MBConv。Fused-MBConv 把原版里的 1×1 扩张卷积和 3×3 深度卷积合并成一个普通的 3×3 卷积。这个改动看起来是“倒退”,但它解决了深度可分离卷积在 GPU 和移动端上运算效率不高的问题。普通卷积在大多数硬件上有现成的高度优化实现,数据复用也好得多,所以实际延迟反而更低。

第二个改动是渐进式训练策略。EfficientNetV2 在训练过程中动态调整输入分辨率、数据增强强度和网络正则化强度。刚开始用小分辨率和弱增强,让模型快速学习数据分布;后期逐步加大分辨率和增强强度,模拟从粗到精的学习过程。这个策略能在更少训练轮数内达到同等精度,是一个工程价值很高的思路。

6.2 这套设计思想对后来模型的影响

从 EfficientNet 流行起来之后,复合缩放和 SE 模块几乎成了新一代高效网络的标准配料。许多后续模型都会在缩放策略上做文章,要么把搜索空间改得更贴合硬件性能,要么把复合缩放扩展到宽度、深度、分辨率之外的维度,比如核大小、注意力头数等。这种“把缩放变成一个可优化问题”的思路,影响力超出了 EfficientNet 本身的成绩。

同时,EfficientNet 也让大家重新意识到 NAS 的价值。它证明在一个受限但设计良好的搜索空间里,结构搜索得到的网络可以比人工设计网络更均衡。后来很多这类工作都会引用这个结论,把搜索结果用于实际部署场景。

6.3 我对这套模型的一些个人经验与使用心得

项目里用过 EfficientNet 一年之后,我最大的体会是“效率”不是只看推理一次要多少 FLOPs,而是整个数据链路从训练到部署的性价比。EfficientNet 最让我佩服的不是某一个结构细节,而是它把模型设计变成了一个有约束的优化问题,让后来做模型选型的人有了一个明确预算表。

如果现在启动一个新项目,我会这样用 EfficientNet:数据量小、算力有限时直接用 B0 或 B1 做迁移学习,把验证集结果跑出来再说;数据量大、显卡多时,把 B4 当作默认主力版本来对比;对目标和极端尺寸有要求时,考虑用更大版本的家族,同时配合 EMA 和量化感知训练保精度。EfficientNet 未必在每个任务里都是最优的,但它一定是那个能让你快速建立 baseline、心里有底气的选择。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/16 2:44:43

流媒体下载实操:跑通 N_m3u8DL-RE 从点播到直播

流媒体下载实操:跑通 N_m3u8DL-RE 从点播到直播 【免费下载链接】N_m3u8DL-RE Cross-Platform, modern and powerful stream downloader for MPD/M3U8/ISM. English/简体中文/繁體中文. 项目地址: https://gitcode.com/GitHub_Trending/nm3/N_m3u8DL-RE N_m…

作者头像 李华
网站建设 2026/10/16 2:44:13

Visual Studio 2022 安装教程:从下载到配置,新手避坑完整指南

如果你第一次接触 Visual Studio,大概率会在安装这一步被绕晕:下载器只有几兆,点开之后却要下载好几个 GB,各种“工作负载”“单个组件”看得一头雾水,装完了又不知道选哪个项目模板。我从第一次装到帮别人排查安装问题…

作者头像 李华
网站建设 2026/10/16 2:44:07

安卓离线唐诗三百首应用开发:从数据设计到背诵模式全解析

1. 项目概述与核心动机很多人第一次听到“安卓 唐诗三百首”这个组合时,第一反应往往是“这不就是个本地数据库加列表页面吗?”说实话,三年前我也是这么想的。直到我真正接手了一个类似的项目需求——某个文化类创业团队想做一个面向中小学生…

作者头像 李华