深度學習之影像分類(十五)EfficientNetV1 網路結構
目錄
- 深度學習之影像分類(十五)EfficientNetV1 網路結構
- 1. 前言
- 2. 寬度,深度以及解析度
- 3. EfficientNetV1 網路結構
- 4. 代碼
本節學習 EfficientNetV1 網路結構,學習視頻源于 Bilibili,參考博客太陽花的小綠豆: EfficientNet網路詳解.

1. 前言
EfficientNetV1 是由Google團隊在 2019 年提出的,其原始論文為 EfficientNet: Rethinking Model Scaling for Convolutional Neural Networks,所提出的 EfficientNet-B7 在 Imagenet top-1 上達到了當年最高準確率 84.3%,與之前準確率最高的 GPipe 相比,引數量僅僅為其 1/8.4,推理速度提升了 6.1 倍,本文的核心在于同時探索輸入解析度、網路深度、寬度的影響,下圖給出了當時 EfficientNet 以及其他網路的 Top-1 準確率和引數量,我們可以發現,EfficientNet 不僅在引數數量上比很多主流模型小之外,準確率也更高,

下圖給出了模型的結構圖,圖 a 是傳統的卷積神經網路,圖 b 在 a 的基礎上增大了網路的寬度(即特征矩陣的channel個數),圖 c 在 a 的基礎上增加了網路的深度(層的個數),圖 d 在 a 的基礎上增大了輸入解析度(即輸入高和寬,這會使得之后所有特征矩陣的高和寬都會相應增加),圖 e 即本文考慮的同時增加網路的寬度、深度、以及網路的輸入解析度,

為了提升網路的準確率,一般會考慮增加網路的寬度、深度以及輸入影像的解析度,那么這三個因素是如何影響網路性能的呢?愿論文的部分敘述如下,
增加網路的深度能夠得到更佳豐富、復雜的特征(高級語意),并且能很好的應用到其他任務中去,但是網路的深度過深會面臨梯度消失,訓練困難等問題,The intuition is that deeper ConvNet can capture richer and more complex features, and generalize well on new tasks. However, deeper networks are also more difficult to train due to the vanishing gradient problem.
增加網路的寬度能夠獲得更高細粒度的特征,并且也更容易訓練,但是對于寬度很大,深度較淺額度網路往往很難學習到更深層次的特征,例如我就只有一個
3
×
3
3 \times 3
3×3 的卷積,但是輸出通道為 10000,也沒辦法得到更為抽象的高級語意,wider networks tend to be able to capture more fine-grained features and are easier to train. However, extremely wide but shallow networks tend to have difficulties in capturing higher level features.
增加輸入網路的影像解析度,能夠潛在獲得更高細粒度的特征模版,影像解析度越高能看到的細節就越多,能提升分辨能力,但是對于非常高的輸入解析度,準確率的增益也會減少,且大解析度影像會增加網路的計算量(注意不是引數量),With higher resolution input images, ConvNets can potentially capture more fine-grained patterns. but the accuracy gain diminishes for very high resolutions.
從實驗結果可見,分別增加三者在準確率達到 80% 之后就近乎趨于飽和了,我們看,同時調整三者當準確率達到 80% 之后并沒有達到飽和,甚至還有更大的提升,這就說明了同時考慮網路的寬度、深度以及輸入影像的解析度,我們可以得到更好的結果,此外我們能看到,當 FLOPs 相同的時候,即理論計算量相同的時候,同時增加這三者,得到的效果也會更好(這其實也就是同時小幅度增加這三個,比單單增加其中一個增加得非常狠要好),那究竟該如何同時去增加這三者呢?

作者還做了實驗,即采用不同網路深度 d 和輸入解析度 r 的組合,不斷調整網路的寬度 w 得到了如下結果,可以分析出在相同的 FLOPs 的情況下,同時增加網路深度 d 和輸入解析度 r 效果最好,

2. 寬度,深度以及解析度
為什么 ImageNet 訓練測驗影像要設定為 224?有人說可能是為了和前人對比網路本身的性能要控制變數吧,那第一個跑 ImageNet 的人為什么用 224 呢?可能別人就會說,工程經驗,在本文中,EfficientNetV1 使用 NAS(Neural Architecture Search)技術來搜索網路的影像輸入解析度 r,網路的深度 depth 以及 channel 的寬度 width 三個引數的合理化配置,在之前的一些論文中,基本都是通過改變上述3個引數中的一個來提升網路的性能,而這篇論文就是同時來探索這三個引數的影響,
為了探究三者與網路性能之間的關系,作者先在論文中對整個網路的運算進行抽象:
N
(
d
,
w
,
r
)
=
?
i
=
1
…
s
F
i
L
i
(
X
?
H
i
,
W
i
,
C
i
?
)
N(d, w, r)=\bigodot_{i=1 \ldots s} F_{i}^{L_{i}}\left(X_{\left\langle H_{i}, W_{i}, C_{i}\right\rangle}\right)
N(d,w,r)=i=1…s??FiLi??(X?Hi?,Wi?,Ci???)
其中:
- ? i = 1 … s \bigodot_{i=1 \ldots s} ?i=1…s? 表示連乘運算
- F i F_i Fi? 表示一個運算操作,例如后面講的 MBConv, F i L i F_i^{L_i} FiLi?? 表示在 Stage i 中 F i F_i Fi? 運算被重復執行
- X X X 表示輸入 Stage 的特征矩陣
- ? H i , W i , C i ? \left\langle H_{i}, W_{i}, C_{i}\right\rangle ?Hi?,Wi?,Ci?? 表示特征矩陣 X X X 的高度,寬度,以及通道數
隨后,為了探究 r,d 和 w 這三個因子對最終準確率的影響,則將 r,d 和 w 加入到公式中,我們可以得到抽象化后的優化問題(在指定資源限制下):
Our target is to maximize the model accuracy for any given resource constraints, which can be formulated as an optimization problem:

其中:
d用來縮放網路的深度 L ^ i \widehat{L}_i L i?r用來縮放特征矩陣的 H ^ i , W ^ i \widehat{H}_i, \widehat{W}_i H i?,W i?w用來縮放特征矩陣的 C ^ i \widehat{C}_i C i?target_memory為記憶體限制target_flops為 FLOPs 限制
需要指出的是:
- FLOPs 與
d的關系是:當d翻倍,FLOPs 也翻倍, - FLOPs 與
w的關系是:當w翻倍,FLOPs 會翻 4 倍,因為卷積層的 FLOPs 約等于 f e a t u r e w × f e a t u r e h × f e a t u r e c × k e r n e l w × k e r n e l h × k e r n e l n u m b e r feature_w \times feature_h \times feature_c \times kernel_w \times kernel_h \times kernel_{number} featurew?×featureh?×featurec?×kernelw?×kernelh?×kernelnumber?,假設輸入輸出特征矩陣的高寬不變,當 width 翻倍,輸入特征矩陣的 channels (即 f e a r u r e c fearure_c fearurec?)和輸出特征矩陣的 channels(即卷積核的個數, k e r n e l n u m b e r kernel_{number} kernelnumber?)都會翻倍,所以 FLOPs 會翻 4 倍 - FLOPs與
r的關系是:當r翻倍,FLOPs 會翻4倍,和上面類似,因為特征矩陣的寬度 f e a t u r e w feature_w featurew? 和特征矩陣的高度 f e a t u r e h feature_h featureh? 都會翻倍,所以 FLOPs 會翻 4 倍
所以總的 FLOPs 倍率可以用近似用
(
α
×
β
2
×
γ
2
)
?
(\alpha \times \beta^2 \times \gamma^2)^{\phi}
(α×β2×γ2)? 來表示,當限制
α
×
β
2
×
γ
2
≈
2
\alpha \times \beta^2 \times \gamma^2 \approx 2
α×β2×γ2≈2 時,對于任意一個
?
\phi
? 而言 FLOPs 相當增加了
2
?
2^{\phi}
2? 倍,為此,作者提出了一個混合縮放方法 ( compound scaling method) ,在這個方法中使用了一個混合因子
?
\phi
? 去統一的縮放 r,d 和 w 這三個因子,具體的計算公式如下:

接下來作者在基準網路 EfficientNetB-0(下一小節會講)上使用 NAS 來搜索 α , β , γ \alpha, \beta, \gamma α,β,γ 這三個引數:
- (step1)首先固定 ? = 1 \phi=1 ?=1,并基于上面給出的公式 (2) 和 (3) 進行搜索,作者發現對于 EfficientNetB-0 最佳引數為 α = 1.2 , β = 1.1 , γ = 1.15 \alpha = 1.2 , \beta = 1.1 , \gamma = 1.15 α=1.2,β=1.1,γ=1.15 ,此時 α × β 2 × γ 2 ≈ 1.9203 \alpha \times \beta^2 \times \gamma^2 \approx 1.9203 α×β2×γ2≈1.9203
- (step2)固定 α = 1.2 , β = 1.1 , γ = 1.15 \alpha = 1.2 , \beta = 1.1 , \gamma = 1.15 α=1.2,β=1.1,γ=1.15 ,在 EfficientNetB-0 的基礎上使用不同的 ? \phi ? 分別得到 EfficientNetB-1至 EfficientNetB-7,當 ? \phi ? = 1 的時候,由公式 (3) 可以得到三者相對于 EfficientNetB-0 的倍率為 d = 1.2 , w = 1.1 , r = 1.15 d = 1.2 , w = 1.1 , r = 1.15 d=1.2,w=1.1,r=1.15 ,對應的就是 EfficientNetB-2,EfficientNetB-1 則是對應于當 ? \phi ? = 0.5 的情況,
原論文指出:對于不同的基準網路搜索出的 α , β , γ \alpha, \beta, \gamma α,β,γ 這三個引數不定相同,作者也說了,如果直接在大模型上去搜索 α , β , γ \alpha, \beta, \gamma α,β,γ 這三個引數可能獲得更好的結果,但是在較大的模型中搜索成本太大,所以這篇文章就在比較小的 EfficientNetB-0 模型上進行搜索的,Google 這種大廠都說計算量大,那就是真的大…
Notably, it is possible to achieve even better performance by searching for α, β, γ directly around a large model, but the search cost becomes prohibitively more expensive on larger models. Our method solves this issue by only doing search once on the small baseline network (step 1), and then use the same scaling coefficients for all other models (step 2).
3. EfficientNetV1 網路結構
EfficientNet-B0 baseline 網路的結構配置如下圖所示,這個網路也是作者通過網路搜索技術得到的,后續講的 EfficientNet-B1 到 B7 都是在這個網路的基礎上進行簡單調整的,在 B0 中一共分為 9 個 stage,表中的卷積層后默認都跟有 BN 以及 Swish 激活函式,stage 1 就是一個 3 × 3 3 \times 3 3×3 的卷積層,對于 stage 2 到 stage 8 就是在重復堆疊 MBConv,stage 9 由三部分組成,首先是一個 1 × 1 1 \times 1 1×1 的卷積,然后是平均池化,最后是一個全連接層,表中 Resolution 是每個輸入特征矩陣的高度和寬度,Channels 是輸出特征矩陣的通道數,Layers 則是將 Operator 重復多少次,stride 引數則是僅針對每個 stage 的第一個 block,之后的 stride 都是 1,

緊接著我們來看看 MBConv 模塊,論文中說了他和 MobileNetV3 使用的 block 是一樣的(那其實單看網路結構沒什么contribution,貢獻主要在第二節的研究以及推廣的model上),對于主分支而言,首先是一個 1 × 1 1 \times 1 1×1 卷積用于升維,其輸出特征矩陣通道是輸入 channel 的 n 倍,緊接著通過一個 DW 卷積(這里 DW 卷積的卷積核可能是 3 × 3 3 \times 3 3×3 或者 5 × 5 5 \times 5 5×5,在上面那個表格中有的,stride 可能等于 1 也可能等于 2),然后通過一個 SE 模塊,使用注意力機制調整特征矩陣,然后再通過 1 × 1 1 \times 1 1×1 卷積進行降維,注意這里只有 BN,沒有 swish 激活函式(其實就是對應線性激活函式),僅當輸入 MBConv 結構的特征矩陣與輸出的特征矩陣 shape 存在時才有 short-cut 連接,并且在原始碼中只有使用到 short-cut 連接的 MBConv 模塊才有 Dropout 層,同樣的,在 stage2 的第一個 MBConv 中,第一個 1 × 1 1 \times 1 1×1 卷積沒有升維,所以在實作中沒有這個卷積層,

SE 模塊在之前 MobileNetV3 的講解中講過了,這里再回顧一下,首先對輸入矩陣的每個 channel 進行平均池化,然后經過兩個全連接層,第一個是 Swish 激活函式,第二個是 Sigmoid 激活函式,第一個全連接層的節點個數等于輸入 MBConv 模塊的特征矩陣 channel 個數的 1/4(在 MobileNetV3 中是輸入 SE 模塊的特征矩陣 channel 的 1/4,都是原始碼中的細節),第二個全連接層的節點個數等于輸入 SE 模塊的特征矩陣 channel 個數,第二個全連接層的輸出其實就是輸入 SE 模塊的特征矩陣每個 channel 的注意力權重,值越大越重要,越越小越不重要,將權重按通道乘回原特征矩陣就可以了,

接下來我們看看 EfficientNet-B1 到 B7 是怎么構建出來的,下表分別給出了針對網路寬度,深度的倍率因子,以及對于輸入尺寸上的變化,注意,dropout_connect_rate 就是對應上述講的 MBConv 中的 Dropout 結構的隨機失活比率,dropout_rate 對應的是最后一個全連接層前 Dropout 的隨機失活比率,

性能對比能發現,EfficientNet 就是準確率最高,引數量最小,理論上計算量最低,然而使用中發現,EfficientNet 非常占 GPU 的顯存,因為他們輸入影像解析度特別大,速度直接給 FLOPs 就有點耍流氓了,真實運行速度和 FLOPs 不是直接正比的,ShuffleNetV2 就指出,這樣做是萬萬不行滴,

4. 代碼
EfficientNetV1 實作代碼如下所示,代碼出處:
import math
import copy
from functools import partial
from collections import OrderedDict
from typing import Optional, Callable
import torch
import torch.nn as nn
from torch import Tensor
from torch.nn import functional as F
def _make_divisible(ch, divisor=8, min_ch=None):
"""
This function is taken from the original tf repo.
It ensures that all layers have a channel number that is divisible by 8
It can be seen here:
https://github.com/tensorflow/models/blob/master/research/slim/nets/mobilenet/mobilenet.py
"""
if min_ch is None:
min_ch = divisor
new_ch = max(min_ch, int(ch + divisor / 2) // divisor * divisor)
# Make sure that round down does not go down by more than 10%.
if new_ch < 0.9 * ch:
new_ch += divisor
return new_ch
def drop_path(x, drop_prob: float = 0., training: bool = False):
"""
Drop paths (Stochastic Depth) per sample (when applied in main path of residual blocks).
"Deep Networks with Stochastic Depth", https://arxiv.org/pdf/1603.09382.pdf
This function is taken from the rwightman.
It can be seen here:
https://github.com/rwightman/pytorch-image-models/blob/master/timm/models/layers/drop.py#L140
"""
if drop_prob == 0. or not training:
return x
keep_prob = 1 - drop_prob
shape = (x.shape[0],) + (1,) * (x.ndim - 1) # work with diff dim tensors, not just 2D ConvNets
random_tensor = keep_prob + torch.rand(shape, dtype=x.dtype, device=x.device)
random_tensor.floor_() # binarize
output = x.div(keep_prob) * random_tensor
return output
class DropPath(nn.Module):
"""
Drop paths (Stochastic Depth) per sample (when applied in main path of residual blocks).
"Deep Networks with Stochastic Depth", https://arxiv.org/pdf/1603.09382.pdf
"""
def __init__(self, drop_prob=None):
super(DropPath, self).__init__()
self.drop_prob = drop_prob
def forward(self, x):
return drop_path(x, self.drop_prob, self.training)
class ConvBNActivation(nn.Sequential):
def __init__(self,
in_planes: int,
out_planes: int,
kernel_size: int = 3,
stride: int = 1,
groups: int = 1,
norm_layer: Optional[Callable[..., nn.Module]] = None,
activation_layer: Optional[Callable[..., nn.Module]] = None):
padding = (kernel_size - 1) // 2
if norm_layer is None:
norm_layer = nn.BatchNorm2d
if activation_layer is None:
activation_layer = nn.SiLU # alias Swish (torch>=1.7)
super(ConvBNActivation, self).__init__(nn.Conv2d(in_channels=in_planes,
out_channels=out_planes,
kernel_size=kernel_size,
stride=stride,
padding=padding,
groups=groups,
bias=False),
norm_layer(out_planes),
activation_layer())
class SqueezeExcitation(nn.Module):
def __init__(self,
input_c: int, # block input channel
expand_c: int, # block expand channel
squeeze_factor: int = 4):
super(SqueezeExcitation, self).__init__()
squeeze_c = input_c // squeeze_factor
self.fc1 = nn.Conv2d(expand_c, squeeze_c, 1)
self.ac1 = nn.SiLU() # alias Swish
self.fc2 = nn.Conv2d(squeeze_c, expand_c, 1)
self.ac2 = nn.Sigmoid()
def forward(self, x: Tensor) -> Tensor:
scale = F.adaptive_avg_pool2d(x, output_size=(1, 1))
scale = self.fc1(scale)
scale = self.ac1(scale)
scale = self.fc2(scale)
scale = self.ac2(scale)
return scale * x
class InvertedResidualConfig:
# kernel_size, in_channel, out_channel, exp_ratio, strides, use_SE, drop_connect_rate
def __init__(self,
kernel: int, # 3 or 5
input_c: int,
out_c: int,
expanded_ratio: int, # 1 or 6
stride: int, # 1 or 2
use_se: bool, # True
drop_rate: float,
index: str, # 1a, 2a, 2b, ...
width_coefficient: float):
self.input_c = self.adjust_channels(input_c, width_coefficient)
self.kernel = kernel
self.expanded_c = self.input_c * expanded_ratio
self.out_c = self.adjust_channels(out_c, width_coefficient)
self.use_se = use_se
self.stride = stride
self.drop_rate = drop_rate
self.index = index
@staticmethod
def adjust_channels(channels: int, width_coefficient: float):
return _make_divisible(channels * width_coefficient, 8)
class InvertedResidual(nn.Module):
def __init__(self,
cnf: InvertedResidualConfig,
norm_layer: Callable[..., nn.Module]):
super(InvertedResidual, self).__init__()
if cnf.stride not in [1, 2]:
raise ValueError("illegal stride value.")
self.use_res_connect = (cnf.stride == 1 and cnf.input_c == cnf.out_c)
layers = OrderedDict()
activation_layer = nn.SiLU # alias Swish
# expand
if cnf.expanded_c != cnf.input_c:
layers.update({"expand_conv": ConvBNActivation(cnf.input_c,
cnf.expanded_c,
kernel_size=1,
norm_layer=norm_layer,
activation_layer=activation_layer)})
# depthwise
layers.update({"dwconv": ConvBNActivation(cnf.expanded_c,
cnf.expanded_c,
kernel_size=cnf.kernel,
stride=cnf.stride,
groups=cnf.expanded_c,
norm_layer=norm_layer,
activation_layer=activation_layer)})
if cnf.use_se:
layers.update({"se": SqueezeExcitation(cnf.input_c,
cnf.expanded_c)})
# project
layers.update({"project_conv": ConvBNActivation(cnf.expanded_c,
cnf.out_c,
kernel_size=1,
norm_layer=norm_layer,
activation_layer=nn.Identity)})
self.block = nn.Sequential(layers)
self.out_channels = cnf.out_c
self.is_strided = cnf.stride > 1
# 只有在使用shortcut連接時才使用dropout層
if self.use_res_connect and cnf.drop_rate > 0:
self.dropout = DropPath(cnf.drop_rate)
else:
self.dropout = nn.Identity()
def forward(self, x: Tensor) -> Tensor:
result = self.block(x)
result = self.dropout(result)
if self.use_res_connect:
result += x
return result
class EfficientNet(nn.Module):
def __init__(self,
width_coefficient: float,
depth_coefficient: float,
num_classes: int = 1000,
dropout_rate: float = 0.2,
drop_connect_rate: float = 0.2,
block: Optional[Callable[..., nn.Module]] = None,
norm_layer: Optional[Callable[..., nn.Module]] = None
):
super(EfficientNet, self).__init__()
# kernel_size, in_channel, out_channel, exp_ratio, strides, use_SE, drop_connect_rate, repeats
default_cnf = [[3, 32, 16, 1, 1, True, drop_connect_rate, 1],
[3, 16, 24, 6, 2, True, drop_connect_rate, 2],
[5, 24, 40, 6, 2, True, drop_connect_rate, 2],
[3, 40, 80, 6, 2, True, drop_connect_rate, 3],
[5, 80, 112, 6, 1, True, drop_connect_rate, 3],
[5, 112, 192, 6, 2, True, drop_connect_rate, 4],
[3, 192, 320, 6, 1, True, drop_connect_rate, 1]]
def round_repeats(repeats):
"""Round number of repeats based on depth multiplier."""
return int(math.ceil(depth_coefficient * repeats))
if block is None:
block = InvertedResidual
if norm_layer is None:
norm_layer = partial(nn.BatchNorm2d, eps=1e-3, momentum=0.1)
adjust_channels = partial(InvertedResidualConfig.adjust_channels,
width_coefficient=width_coefficient)
# build inverted_residual_setting
bneck_conf = partial(InvertedResidualConfig,
width_coefficient=width_coefficient)
b = 0
num_blocks = float(sum(round_repeats(i[-1]) for i in default_cnf))
inverted_residual_setting = []
for stage, args in enumerate(default_cnf):
cnf = copy.copy(args)
for i in range(round_repeats(cnf.pop(-1))):
if i > 0:
# strides equal 1 except first cnf
cnf[-3] = 1 # strides
cnf[1] = cnf[2] # input_channel equal output_channel
cnf[-1] = args[-2] * b / num_blocks # update dropout ratio
index = str(stage + 1) + chr(i + 97) # 1a, 2a, 2b, ...
inverted_residual_setting.append(bneck_conf(*cnf, index))
b += 1
# create layers
layers = OrderedDict()
# first conv
layers.update({"stem_conv": ConvBNActivation(in_planes=3,
out_planes=adjust_channels(32),
kernel_size=3,
stride=2,
norm_layer=norm_layer)})
# building inverted residual blocks
for cnf in inverted_residual_setting:
layers.update({cnf.index: block(cnf, norm_layer)})
# build top
last_conv_input_c = inverted_residual_setting[-1].out_c
last_conv_output_c = adjust_channels(1280)
layers.update({"top": ConvBNActivation(in_planes=last_conv_input_c,
out_planes=last_conv_output_c,
kernel_size=1,
norm_layer=norm_layer)})
self.features = nn.Sequential(layers)
self.avgpool = nn.AdaptiveAvgPool2d(1)
classifier = []
if dropout_rate > 0:
classifier.append(nn.Dropout(p=dropout_rate, inplace=True))
classifier.append(nn.Linear(last_conv_output_c, num_classes))
self.classifier = nn.Sequential(*classifier)
# initial weights
for m in self.modules():
if isinstance(m, nn.Conv2d):
nn.init.kaiming_normal_(m.weight, mode="fan_out")
if m.bias is not None:
nn.init.zeros_(m.bias)
elif isinstance(m, nn.BatchNorm2d):
nn.init.ones_(m.weight)
nn.init.zeros_(m.bias)
elif isinstance(m, nn.Linear):
nn.init.normal_(m.weight, 0, 0.01)
nn.init.zeros_(m.bias)
def _forward_impl(self, x: Tensor) -> Tensor:
x = self.features(x)
x = self.avgpool(x)
x = torch.flatten(x, 1)
x = self.classifier(x)
return x
def forward(self, x: Tensor) -> Tensor:
return self._forward_impl(x)
def efficientnet_b0(num_classes=1000):
# input image size 224x224
return EfficientNet(width_coefficient=1.0,
depth_coefficient=1.0,
dropout_rate=0.2,
num_classes=num_classes)
def efficientnet_b1(num_classes=1000):
# input image size 240x240
return EfficientNet(width_coefficient=1.0,
depth_coefficient=1.1,
dropout_rate=0.2,
num_classes=num_classes)
def efficientnet_b2(num_classes=1000):
# input image size 260x260
return EfficientNet(width_coefficient=1.1,
depth_coefficient=1.2,
dropout_rate=0.3,
num_classes=num_classes)
def efficientnet_b3(num_classes=1000):
# input image size 300x300
return EfficientNet(width_coefficient=1.2,
depth_coefficient=1.4,
dropout_rate=0.3,
num_classes=num_classes)
def efficientnet_b4(num_classes=1000):
# input image size 380x380
return EfficientNet(width_coefficient=1.4,
depth_coefficient=1.8,
dropout_rate=0.4,
num_classes=num_classes)
def efficientnet_b5(num_classes=1000):
# input image size 456x456
return EfficientNet(width_coefficient=1.6,
depth_coefficient=2.2,
dropout_rate=0.4,
num_classes=num_classes)
def efficientnet_b6(num_classes=1000):
# input image size 528x528
return EfficientNet(width_coefficient=1.8,
depth_coefficient=2.6,
dropout_rate=0.5,
num_classes=num_classes)
def efficientnet_b7(num_classes=1000):
# input image size 600x600
return EfficientNet(width_coefficient=2.0,
depth_coefficient=3.1,
dropout_rate=0.5,
num_classes=num_classes)
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/297937.html
標籤:其他
