基于EfficientNet-B0构建更高阶EfficientNet模型的技术咨询
构建EfficientNet B1-B7的详细指南
首先修正你提供的EfficientNet-B0代码中的关键错误,这些问题会导致模型结构偏离官方标准,也会影响后续扩展:
基础模块修正
1. Squeeze-Excitation层修正
原代码中第二层卷积输入通道写错,且无需手动reshape(AdaptiveAvgPool2d输出可直接传入卷积):
class squeeze_excitation(nn.Module): def __init__(self, in_c, reduction_ratio=4): super().__init__() self.layers = nn.Sequential( nn.AdaptiveAvgPool2d(1), nn.Conv2d(in_c, in_c // reduction_ratio, kernel_size=1), nn.SiLU(inplace=True), # 官方用Swish,PyTorch 1.7+可用SiLU直接替代 nn.Conv2d(in_c // reduction_ratio, in_c, kernel_size=1), nn.Sigmoid(), ) def forward(self, x): return x * self.layers(x)
2. MBConv模块添加残差连接
官方MBConv包含shortcut连接,仅当输入输出通道、分辨率匹配时启用:
class MBConv(nn.Module): def __init__(self, in_c, out_c, kernel_size, stride, expand_ratio=6, reduction_ratio=4): super().__init__() self.use_residual = (in_c == out_c) and (stride == 1) expanded_c = in_c * expand_ratio self.layers = nn.Sequential( # 1x1扩张卷积(仅当expand_ratio>1时生效) nn.Conv2d(in_c, expanded_c, kernel_size=1, bias=False) if expand_ratio !=1 else nn.Identity(), nn.BatchNorm2d(expanded_c) if expand_ratio !=1 else nn.Identity(), nn.SiLU(inplace=True) if expand_ratio !=1 else nn.Identity(), # 深度可分离卷积 nn.Conv2d(expanded_c, expanded_c, kernel_size=kernel_size, stride=stride, padding=kernel_size//2, groups=expanded_c, bias=False), nn.BatchNorm2d(expanded_c), nn.SiLU(inplace=True), # SE层 squeeze_excitation(expanded_c, reduction_ratio), # 1x1投影卷积 nn.Conv2d(expanded_c, out_c, kernel_size=1, bias=False), nn.BatchNorm2d(out_c), ) def forward(self, x): if self.use_residual: return x + self.layers(x) return self.layers(x)
扩展到B1-B7的核心规则
官方EfficientNet通过复合缩放同时调整三个维度:
- 宽度系数
w:缩放所有卷积层的通道数,公式w = 1.2^Φ - 深度系数
d:缩放每个MBConv阶段的重复次数,公式d = 1.1^Φ - 分辨率系数
r:缩放输入图像尺寸,公式r = 1.15^Φ
各模型对应的参数如下:
| 模型 | Φ值 | 宽度系数w | 深度系数d | 输入分辨率 |
|---|---|---|---|---|
| B0 | 0 | 1.0 | 1.0 | 224x224 |
| B1 | 0.5 | 1.2 | 1.1 | 240x240 |
| B2 | 1 | 1.4 | 1.2 | 260x260 |
| B3 | 2 | 1.8 | 1.4 | 300x300 |
| B4 | 3 | 2.2 | 1.8 | 380x380 |
| B5 | 4 | 2.6 | 2.2 | 456x456 |
| B6 | 5 | 3.0 | 2.6 | 528x528 |
| B7 | 6 | 3.4 | 3.1 | 600x600 |
缩放细节
- 通道数缩放:将B0的通道数乘以
w后,取最接近的8的倍数(官方硬件优化方式) - 重复次数缩放:将B0每个阶段的MBConv重复次数乘以
d后向上取整 - 分辨率:直接使用对应模型的输入尺寸
可扩展的EfficientNet实现
基于B0的基础配置,编写通用类支持所有B系列模型:
import torch import torch.nn as nn def round_channels(channels, width_coeff, divisor=8): """缩放通道数并取8的倍数""" scaled = channels * width_coeff return max(divisor, int(scaled + divisor/2) // divisor * divisor) def round_repeats(repeats, depth_coeff): """缩放重复次数并向上取整""" return int(torch.ceil(repeats * depth_coeff)) class EfficientNet(nn.Module): def __init__(self, width_coeff=1.0, depth_coeff=1.0, resolution=224, num_classes=1000): super().__init__() # B0基础配置:(expand_ratio, in_c, out_c, kernel_size, stride, repeats) base_config = [ (1, 32, 16, 3, 1, 1), (6, 16, 24, 3, 2, 2), (6, 24, 40, 5, 2, 2), (6, 40, 80, 3, 2, 3), (6, 80, 112, 5, 1, 3), (6, 112, 192, 5, 2, 4), (6, 192, 320, 3, 1, 1), ] # 初始卷积层 first_c = round_channels(32, width_coeff) self.features = nn.Sequential( nn.Conv2d(3, first_c, kernel_size=3, stride=2, padding=1, bias=False), nn.BatchNorm2d(first_c), nn.SiLU(inplace=True), ) # 构建MBConv阶段 for exp_ratio, in_c, out_c, k, s, reps in base_config: scaled_in = round_channels(in_c, width_coeff) scaled_out = round_channels(out_c, width_coeff) scaled_reps = round_repeats(reps, depth_coeff) for i in range(scaled_reps): # 仅第一个块用指定stride,后续用stride=1 stride = s if i ==0 else 1 self.features.append( MBConv(scaled_in if i==0 else scaled_out, scaled_out, k, stride, exp_ratio) ) # 顶部卷积和分类器 last_c = round_channels(1280, width_coeff) self.features.append( nn.Sequential( nn.Conv2d(round_channels(320, width_coeff), last_c, kernel_size=1, bias=False), nn.BatchNorm2d(last_c), nn.SiLU(inplace=True), nn.AdaptiveAvgPool2d(1), ) ) self.classifier = nn.Linear(last_c, num_classes) def forward(self, x): x = self.features(x) x = x.flatten(1) return self.classifier(x) # 快速实例化各模型 def EfficientNetB0(num_classes=1000): return EfficientNet(1.0, 1.0, 224, num_classes) def EfficientNetB1(num_classes=1000): return EfficientNet(1.2, 1.1, 240, num_classes) def EfficientNetB2(num_classes=1000): return EfficientNet(1.4, 1.2, 260, num_classes) def EfficientNetB3(num_classes=1000): return EfficientNet(1.8, 1.4, 300, num_classes) def EfficientNetB4(num_classes=1000): return EfficientNet(2.2, 1.8, 380, num_classes) def EfficientNetB5(num_classes=1000): return EfficientNet(2.6, 2.2, 456, num_classes) def EfficientNetB6(num_classes=1000): return EfficientNet(3.0, 2.6, 528, num_classes) def EfficientNetB7(num_classes=1000): return EfficientNet(3.4, 3.1, 600, num_classes)
关键注意事项
- SE层是官方EfficientNet的核心组件,所有MBConv模块必须包含
- 残差连接仅在输入输出通道、分辨率匹配时启用,对模型性能至关重要
- 通道数取8的倍数是官方为适配硬件加速的优化,建议保留
- 训练时需对应调整输入图像分辨率,比如B7需要输入600x600的图像
内容的提问来源于stack exchange,提问作者h4kr
相关产品推荐
相关产品推荐

