深度学习100问-9:为什么EfficientNet号称是最好的分类网络?
深度学习100问
Author:louwill
Machine Learning Lab
Model Scaling(模型扩展)一直以来都是提高卷积神经网络效果的重要方法。比如说ResNet可以增加层数从ResNet18扩展到ResNet200,GPipe通过对基线网络的四倍扩展在ImageNet上可以达到84.3%的准确率。本节要介绍的最新网络结构——EfficientNet,就是一种标准化模型扩展的结果。通过下面这张图,我们可以直观的感受一下EfficientNet B0-B7在ImageNet上的效果:对于ImageNet历史上各种网络,EfficientNet可以算是在效果上实现了碾压。
因为效果太好,kaggle上的一众视觉比赛都开始在用。
什么是EfficientNet?
一般我们可以通过调整输入图像大小、网络深度和宽度(即卷积通道数)来实现对卷积网络的扩展。在EfficientNet之前,很多的研究工作都只是针对这三个维度中某一个维度进行调整尝试,限于计算资源和人工调参等因素,很少有研究对这三个维度进行综合调整的。
所以EfficientNet的设想就是能否设计一个标准化的卷积网络扩展方法,既可以实现较高的准确率,也可以充分节省算力资源。因而问题可以描述成如何平衡分辨率、深度和宽度这三个维度,实现卷积网络在准确率和效率上的优化。
EfficientNet给出的解决方案是提出一种模型复合缩放方法(compound scaling method),如下图所示:
图a是一个基线网络,图b、c和d这三个网络分别对该基线网络的宽度、深度和输入分辨率进行扩展,而图d网络就是EfficientNet的主要思想,综合宽度、深度和分辨率对网络进行复合扩展。
一个卷积网络可以定义为如下式子:
其中Fi表述对第i层的卷积运算,Li表述层Fi在第i个阶段被重复了Li次。<Hi,Wi,Ci>表示第i层输入的shape。EfficientNet的设想是一个卷积网络所有的卷积层必须通过相同的比例常数进行统一扩展,所以,一个模型扩展问题可以用数学语言描述为:
其中d、w和r分别表示网络深度、宽度和分辨率。该式表示为在给定计算内存和效率的约束下,如何优化参数d、w和r来实现最好的模型准确率。
EfficientNet的规范化复合调参方法使用了一个复合系数Φ对三个参数进行复合调整:
其中α,β,γ作为常数可以由小型的网格搜索来确定。以上就是EfficientNet的复合扩展方式,但这仅仅是一种模型扩展方法,EfficientNet到底是什么样的一种Net我们还没有说到。换句话说,要实现state of the art的模型效果,光有扩展方式肯定不行,我们必须给这种方式提供一个比较好的基线网络。
EfficientNet使用了MobileNet V2中的MBConv作为模型的主干网络,同时也使用了SENet中的squeeze and excitation方法对网络结构进行了优化。MBConv的主要结构如下图右所示,相较于MobileNet V1,MBConv的设计使得MobileNet V2能够更好利用残差连接带来的效果提升。具体细节可参考MobileNet V2论文。
SENet的squeeze and excitation操作如下图所示,具体可参考SENet论文,这里不做详述。
所以综合MBConv和squeeze and excitation方法的EfficientNet-B0的网络结构如下表所示:
那么对于EfficientNet-B0这样一个基线网络,如何使用复合扩展法对该网络进行扩展呢?这里主要分两步走。首先是将复合系数Φ固定为1,先假设有两倍以上的计算资源可用,然后对α,β,γ进行网络搜索。对于EfficientNet-B0网络,在约束条件为
时,α,β,γ分别取1.2、1.1和1.15时效果最佳。第二步则是固定α,β,γ,通过复合调整公式对基线网络进行扩展得到B1至B7网络。于是就有了本文开头那张图,EfficientNet在ImageNet上的效果碾压过往各种经典网络。EfficientNet-B7在top1准确率上达到了84.4%,在top5准确率上达到了97.1%,同时模型规模要比此前的GPipe小了8.4倍:
基于EfficientNet的迁移学习
普通人来训练和扩展EfficientNet实在太昂贵,一个值得尝试的方法就是迁移学习。下面使用EfficientNet-B0进行猫狗分类的迁移学习训练。
先下载基于keras的EfficientNet迁移学习库:
!git clone https://github.com/Tony607/efficientnet_keras_transfer_learning
%cd efficientnet_keras_transfer_learning/
导入相关模块:
from efficientnet import EfficientNetB0 as Net
from efficientnet import center_crop_and_resize, preprocess_input
# loading pretrained conv base model
conv_base = Net(weights="imagenet", include_top=False, input_shape=input_shape)
搭建基于EfficientNet-B0的迁移学习网络:
from tensorflow.keras import models
from tensorflow.keras import layers
dropout_rate = 0.2
model = models.Sequential()model.add(conv_base)model.add(layers.GlobalMaxPooling2D(name="gap"))
# model.add(layers.Flatten(name="flatten"))
if dropout_rate > 0:
model.add(layers.Dropout(dropout_rate, name="dropout_out"))
# model.add(layers.Dense(256, activation='relu', name="fc1"))
model.add(layers.Dense(2, activation="softmax", name="fc_out"))
冻结卷积层不参与训练:
conv_base.trainable = False
下载猫狗数据:
!wget https://download.microsoft.com/download/3/E/1/3E1C3F21-ECDB-4869-8368-6DEBA77B919F/kagglecatsanddogs_3367a.zip
!unzip -qq kagglecatsanddogs_3367a.zip -d dog_vs_cat
使用keras的ImageDataGenerator模块加载数据并训练:
from tensorflow.keras.preprocessing.image import ImageDataGenerator
train_datagen = ImageDataGenerator(
rescale=1.0 / 255,
rotation_range=40,
width_shift_range=0.2,
height_shift_range=0.2,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True,
fill_mode="nearest",)
# Note that the validation data should not be augmented!test_datagen = ImageDataGenerator(rescale=1.0 / 255)
train_generator = train_datagen.flow_from_directory(
# This is the target directory
train_dir,
# All images will be resized to target height and width.
target_size=(height, width),
batch_size=batch_size,
# Since we use categorical_crossentropy loss, we need categorical labels
class_mode="categorical",)
validation_generator = test_datagen.flow_from_directory(
validation_dir,
target_size=(height, width),
batch_size=batch_size,
class_mode="categorical",)
model.compile(
loss="categorical_crossentropy",
optimizer=optimizers.RMSprop(lr=2e-5),
metrics=["acc"],)
history = model.fit_generator(
train_generator,
steps_per_epoch=NUM_TRAIN // batch_size,
epochs=epochs,
validation_data=validation_generator,
validation_steps=NUM_TEST // batch_size,
verbose=1,
use_multiprocessing=True,
workers=4,)
20个epoch之后验证集准确率达到0.82。训练集和验证集损失以及准确率:
有学术和技术问题的同学可以加我微信进入机器学习实验室读者交流群。加微信后说明来意,最好做个简单的自我介绍,让我有个印象。
参考资料:
往期精彩:
深度学习100问-8:什么是Batch Normalization?
深度学习100问-3:深度学习应掌握哪些Linux开发技术?
一个算法工程师的成长之路
长按二维码.关注机器学习实验室