You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于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输入分辨率
B001.01.0224x224
B10.51.21.1240x240
B211.41.2260x260
B321.81.4300x300
B432.21.8380x380
B542.62.2456x456
B653.02.6528x528
B763.43.1600x600

缩放细节

  1. 通道数缩放:将B0的通道数乘以w后,取最接近的8的倍数(官方硬件优化方式)
  2. 重复次数缩放:将B0每个阶段的MBConv重复次数乘以d后向上取整
  3. 分辨率:直接使用对应模型的输入尺寸

可扩展的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 14:18:10