Global Average Pooling(简称GAP,全局池化层)技术最早提出是在这篇论文(第3.2节)中,被认为是可以替代全连接层的一种新技术。在keras发布的经典模型中,可以看到不少模型甚至抛弃了全连接层,转而使用GAP,而在支持迁移学习方面,各个模型几乎都支持使用Global Average Pooling和Global Max Pooling(GMP)。 然而,GAP是否真的可以取代全连接层?其背后的原理何在呢?本文来一探究竟。

一、什么是GAP?

  先看看原论文的定义:

  In this paper, we propose another strategy called global average pooling to replace the traditional fully connected layers in CNN. The idea is to generate one feature map for each corresponding category of the classification task in the last mlpconv layer. Instead of adding fully connected layers on top of the feature maps, we take the average of each feature map, and the resulting vector is fed directly into the softmax layer. One advantage of global average pooling over the fully connected layers is that it is more native to the convolution structure by enforcing correspondences between feature maps and categories. Thus the feature maps can be easily interpreted as categories confidence maps. Another advantage is that there is no parameter to optimize in the global average pooling thus overfitting is avoided at this layer. Futhermore, global average pooling sums out the spatial information, thus it is more robust to spatial translations of the input.  

  简单来说,就是在卷积层之后,用GAP替代FC全连接层。有两个有点:一是GAP在特征图与最终的分类间转换更加简单自然;二是不像FC层需要大量训练调优的参数,降低了空间参数会使模型更加健壮,抗过拟合效果更佳。

  我们再用更直观的图像来看GAP的工作原理:

  假设卷积层的最后输出是h × w × d 的三维特征图,具体大小为6 × 6 × 3,经过GAP转换后,变成了大小为 1 × 1 × 3 的输出值,也就是每一层 h × w 会被平均化成一个值。

二、 GAP在Keras中的定义

  GAP的使用一般在卷积层之后,输出层之前:

x = layers.MaxPooling2D((2, 2), strides=(2, 2), name='block5_pool')(x) #卷积层最后一层
x = layers.GlobalAveragePooling2D()(x) #GAP层
prediction = Dense(10, activation='softmax')(x) #输出层

  再看看GAP的代码具体实现:

@tf_export('keras.layers.GlobalAveragePooling2D','keras.layers.GlobalAvgPool2D')
class GlobalAveragePooling2D(GlobalPooling2D):"""Global average pooling operation for spatial data.Arguments:data_format: A string,one of `channels_last` (default) or `channels_first`.The ordering of the dimensions in the inputs.`channels_last` corresponds to inputs with shape`(batch, height, width, channels)` while `channels_first`corresponds to inputs with shape`(batch, channels, height, width)`.It defaults to the `image_data_format` value found in yourKeras config file at `~/.keras/keras.json`.If you never set it, then it will be "channels_last".Input shape:- If `data_format='channels_last'`:4D tensor with shape:`(batch_size, rows, cols, channels)`- If `data_format='channels_first'`:4D tensor with shape:`(batch_size, channels, rows, cols)`Output shape:2D tensor with shape:`(batch_size, channels)`"""def call(self, inputs):if self.data_format == 'channels_last':return backend.mean(inputs, axis=[1, 2])else:return backend.mean(inputs, axis=[2, 3])

  实现很简单,对宽度和高度两个维度的特征数据进行平均化求值。如果是NHWC结构(数量、宽度、高度、通道数),则axis=[1, 2];反之如果是CNHW,则axis=[2, 3]。

三、GAP VS GMP VS FC

  在验证GAP技术可行性前,我们需要准备训练和测试数据集。我在牛津大学网站上找到了17种不同花类的数据集,地址为:http://www.robots.ox.ac.uk/~vgg/data/flowers/17/index.html 。该数据集每种花有80张图片,共计1360张图片,我对花进行了分类处理,抽取了部分数据作为测试数据,这样最终训练和测试数据的数量比为7:1。

  我将数据集上传到我的百度网盘: https://pan.baidu.com/s/1YDA_VOBlJSQEijcCoGC60w ,大家可以下载使用。

  在Keras经典模型中,若支持迁移学习,不但有GAP,还有GMP,而默认是自己组建FC层,一个典型的实现为:

 if include_top:# Classification blockx = layers.Flatten(name='flatten')(x)x = layers.Dense(4096, activation='relu', name='fc1')(x)x = layers.Dense(4096, activation='relu', name='fc2')(x)x = layers.Dense(classes, activation='softmax', name='predictions')(x)else:if pooling == 'avg':x = layers.GlobalAveragePooling2D()(x)elif pooling == 'max':x = layers.GlobalMaxPooling2D()(x)

  本文将在同一数据集条件下,比较GAP、GMP和FC层的优劣,选取测试模型为VGG19和InceptionV3两种模型的迁移学习版本。

  先看看在VGG19模型下,GAP、GMP和FC层在各自迭代50次后,验证准确度和损失度的比对。代码如下:

import keras
from keras.preprocessing.image import ImageDataGenerator
from keras.models import Model
from keras.applications.vgg19 import VGG19from keras.layers import Dense, Flatten
from matplotlib import pyplot as plt
import numpy as np# 为保证公平起见,使用相同的随机种子
np.random.seed(7)
batch_size = 32
# 迭代50次
epochs = 50
# 依照模型规定,图片大小被设定为224
IMAGE_SIZE = 224
# 17种花的分类
NUM_CLASSES = 17
TRAIN_PATH = '/home/yourname/Documents/tensorflow/images/17flowerclasses/train'
TEST_PATH = '/home/yourname/Documents/tensorflow/images/17flowerclasses/test'
FLOWER_CLASSES = ['Bluebell', 'ButterCup', 'ColtsFoot', 'Cowslip', 'Crocus', 'Daffodil', 'Daisy','Dandelion', 'Fritillary', 'Iris', 'LilyValley', 'Pansy', 'Snowdrop', 'Sunflower','Tigerlily', 'tulip', 'WindFlower']def model(mode='fc'):if mode == 'fc':# FC层设定为含有512个参数的隐藏层base_model = VGG19(input_shape=(IMAGE_SIZE, IMAGE_SIZE, 3), include_top=False, pooling='none')x = base_model.outputx = Flatten()(x)x = Dense(512, activation='relu')(x)prediction = Dense(NUM_CLASSES, activation='softmax')(x)elif mode == 'avg':# GAP层通过指定pooling='avg'来设定base_model = VGG19(input_shape=(IMAGE_SIZE, IMAGE_SIZE, 3), include_top=False, pooling='avg')x = base_model.outputprediction = Dense(NUM_CLASSES, activation='softmax')(x)else:# GMP层通过指定pooling='max'来设定base_model = VGG19(input_shape=(IMAGE_SIZE, IMAGE_SIZE, 3), include_top=False, pooling='max')x = base_model.outputprediction = Dense(NUM_CLASSES, activation='softmax')(x)model = Model(input=base_model.input, output=prediction)model.summary()opt = keras.optimizers.rmsprop(lr=0.0001, decay=1e-6)model.compile(loss='categorical_crossentropy',optimizer=opt,metrics=['accuracy'])# 使用数据增强train_datagen = ImageDataGenerator()train_generator = train_datagen.flow_from_directory(directory=TRAIN_PATH,target_size=(IMAGE_SIZE, IMAGE_SIZE),classes=FLOWER_CLASSES)test_datagen = ImageDataGenerator()test_generator = test_datagen.flow_from_directory(directory=TEST_PATH,target_size=(IMAGE_SIZE, IMAGE_SIZE),classes=FLOWER_CLASSES)# 运行模型history = model.fit_generator(train_generator, epochs=epochs, validation_data=test_generator)return historyfc_history = model('fc')
avg_history = model('avg')
max_history = model('max')# 比较多种模型的精确度
plt.plot(fc_history.history['val_acc'])
plt.plot(avg_history.history['val_acc'])
plt.plot(max_history.history['val_acc'])
plt.title('Model accuracy')
plt.ylabel('Validation Accuracy')
plt.xlabel('Epoch')
plt.legend(['FC', 'AVG', 'MAX'], loc='lower right')
plt.grid(True)
plt.show()# 比较多种模型的损失率
plt.plot(fc_history.history['val_loss'])
plt.plot(avg_history.history['val_loss'])
plt.plot(max_history.history['val_loss'])
plt.title('Model loss')
plt.ylabel('Loss')
plt.xlabel('Epoch')
plt.legend(['FC', 'AVG', 'MAX'], loc='upper right')
plt.grid(True)
plt.show()

  各自运行50次迭代后,我们看看准确度比较:

  再看看损失度比较:

  可以看出,首先GMP在本模型中表现太差,不值一提;而FC在前40次迭代时表现尚可,但到了40次后发生了剧烈变化,出现了过拟合现象(运行20次左右时的模型相对较好,但准确率不足70%,模型还是很差);三者中表现最好的是GAP,无论从准确度还是损失率,表现都较为平稳,抗过拟合化效果明显(但最终的准确度70%,模型还是不行)。

  

  我们再转向另一个模型InceptionV3,代码稍加改动如下:

import keras
from keras.preprocessing.image import ImageDataGenerator
from keras.models import Model
from keras.applications.inception_v3 import InceptionV3, preprocess_input
from keras.layers import Dense, Flatten
from matplotlib import pyplot as plt
import numpy as np# 为保证公平起见,使用相同的随机种子
np.random.seed(7)
batch_size = 32
# 迭代50次
epochs = 50
# 依照模型规定,图片大小被设定为224
IMAGE_SIZE = 224
# 17种花的分类
NUM_CLASSES = 17
TRAIN_PATH = '/home/hutao/Documents/tensorflow/images/17flowerclasses/train'
TEST_PATH = '/home/hutao/Documents/tensorflow/images/17flowerclasses/test'
FLOWER_CLASSES = ['Bluebell', 'ButterCup', 'ColtsFoot', 'Cowslip', 'Crocus', 'Daffodil', 'Daisy','Dandelion', 'Fritillary', 'Iris', 'LilyValley', 'Pansy', 'Snowdrop', 'Sunflower','Tigerlily', 'tulip', 'WindFlower']def model(mode='fc'):if mode == 'fc':# FC层设定为含有512个参数的隐藏层base_model = InceptionV3(input_shape=(IMAGE_SIZE, IMAGE_SIZE, 3), include_top=False, pooling='none')x = base_model.outputx = Flatten()(x)x = Dense(512, activation='relu')(x)prediction = Dense(NUM_CLASSES, activation='softmax')(x)elif mode == 'avg':# GAP层通过指定pooling='avg'来设定base_model = InceptionV3(input_shape=(IMAGE_SIZE, IMAGE_SIZE, 3), include_top=False, pooling='avg')x = base_model.outputprediction = Dense(NUM_CLASSES, activation='softmax')(x)else:# GMP层通过指定pooling='max'来设定base_model = InceptionV3(input_shape=(IMAGE_SIZE, IMAGE_SIZE, 3), include_top=False, pooling='max')x = base_model.outputprediction = Dense(NUM_CLASSES, activation='softmax')(x)model = Model(input=base_model.input, output=prediction)model.summary()opt = keras.optimizers.rmsprop(lr=0.0001, decay=1e-6)model.compile(loss='categorical_crossentropy',optimizer=opt,metrics=['accuracy'])# 使用数据增强train_datagen = ImageDataGenerator()train_generator = train_datagen.flow_from_directory(directory=TRAIN_PATH,target_size=(IMAGE_SIZE, IMAGE_SIZE),classes=FLOWER_CLASSES)test_datagen = ImageDataGenerator()test_generator = test_datagen.flow_from_directory(directory=TEST_PATH,target_size=(IMAGE_SIZE, IMAGE_SIZE),classes=FLOWER_CLASSES)# 运行模型history = model.fit_generator(train_generator, epochs=epochs, validation_data=test_generator)return historyfc_history = model('fc')
avg_history = model('avg')
max_history = model('max')# 比较多种模型的精确度
plt.plot(fc_history.history['val_acc'])
plt.plot(avg_history.history['val_acc'])
plt.plot(max_history.history['val_acc'])
plt.title('Model accuracy')
plt.ylabel('Validation Accuracy')
plt.xlabel('Epoch')
plt.legend(['FC', 'AVG', 'MAX'], loc='lower right')
plt.grid(True)
plt.show()# 比较多种模型的损失率
plt.plot(fc_history.history['val_loss'])
plt.plot(avg_history.history['val_loss'])
plt.plot(max_history.history['val_loss'])
plt.title('Model loss')
plt.ylabel('Loss')
plt.xlabel('Epoch')
plt.legend(['FC', 'AVG', 'MAX'], loc='upper right')
plt.grid(True)
plt.show()

  先看看准确度的比较:

  再看看损失度的比较:

  很明显,在InceptionV3模型下,FC、GAP和GMP都表现很好,但可以看出GAP的表现依旧最好,其准确度普遍在90%以上,而另两种的准确度在80~90%之间。

四、结论

  从本实验看出,在数据集有限的情况下,采用经典模型进行迁移学习时,GMP表现不太稳定,FC层由于训练参数过多,更易导致过拟合现象的发生,而GAP则表现稳定,优于FC层。当然具体情况具体分析,我们拿到数据集后,可以在几种方式中多训练测试,以寻求最优解决方案。

转载于:https://www.cnblogs.com/hutao722/p/10008581.html

深度学习基础系列(十)| Global Average Pooling是否可以替代全连接层?相关推荐

  1. 深度学习基础系列| Global Average Pooling是否可以替代全连接层?

    Global Average Pooling(简称GAP,全局池化层)技术最早提出是在这篇论文(第3.2节)中,被认为是可以替代全连接层的一种新技术.在keras发布的经典模型中,可以看到不少模型甚至 ...

  2. 花书+吴恩达深度学习(十二)卷积神经网络 CNN 之全连接层

    目录 0. 前言 1. 全连接层(fully connected layer) 如果这篇文章对你有一点小小的帮助,请给个关注,点个赞喔~我会非常开心的~ 花书+吴恩达深度学习(十)卷积神经网络 CNN ...

  3. 深度学习每层的通道数如何计算_深度学习基础系列(一)| 一文看懂用kersa构建模型的各层含义(掌握输出尺寸和可训练参数数量的计算方法)...

    我们在学习成熟网络模型时,如VGG.Inception.Resnet等,往往面临的第一个问题便是这些模型的各层参数是如何设置的呢?另外,我们如果要设计自己的网路模型时,又该如何设置各层参数呢?如果模型 ...

  4. 深度学习基础系列(十一)| Keras中图像增强技术详解

    https://www.cnblogs.com/hutao722/p/10075150.html 在深度学习中,数据短缺是我们经常面临的一个问题,虽然现在有不少公开数据集,但跟大公司掌握的海量数据集相 ...

  5. 深度学习基础系列(八)| 偏差和方差

    当我们费劲周章不断调参来训练模型时,不可避免地会思考一系列问题,模型好坏的评判标准是什么?改善模型的依据何在?何时停止训练为佳? 要解决上述问题,我们需要引入偏差和方差这两个概念,理解他们很重要,也是 ...

  6. 深度学习基础(十二)—— ReLU vs PReLU

    从算法的命名上来说,PReLU 是对 ReLU 的进一步限制,事实上 PReLU(Parametric Rectified Linear Unit),也即 PReLU 是增加了参数修正的 ReLU. ...

  7. 深度学习基础(十)—— 稀疏编码(二)

    在实际编程时,为了写出准确无误的优化函数代码并能快速又恰到好处地收敛到最优值,可以采用下面的技巧: (1)将输入样本集分成多个小的mini-batches,这样做的好处是每次迭代时输入系统的样本数变少 ...

  8. 动手学深度学习(PyTorch实现)(十)--NiN模型

    NiN模型 1. NiN模型介绍 1.1 NiN模型结构 1.2 NiN结构与VGG结构的对比 2. PyTorch实现 2.1 导入相应的包 2.2 定义NiN block 2.3 全局最大池化层 ...

  9. 【深度学习】(3) 全连接层、激活函数

    各位同学好,今天和大家分享一下tensorflow2.0深度学习中的相关操作.内容有: (1) 全连接层创建: tf.keras.Sequential(),tf.keras.layers.Dense( ...

  10. 局部连接来减参,权值共享肩并肩(深度学习入门系列之十一)

    系列文章: 一入侯门"深"似海,深度学习深几许(深度学习入门系列之一) 人工"碳"索意犹尽,智能"硅"来未可知(深度学习入门系列之二) 神经 ...

最新文章

  1. c++ std::priority_queue优先队列
  2. Java项目:企业人事管理系统(java+SSM+jsp+mysql+maven)
  3. 端口扫描程序nmap使用详解
  4. 如何快速融入团队(三)
  5. 互联网大脑如何产生“梦境“并形成元宇宙
  6. 生命起源之谜:RNA世界假说将迎来终结?
  7. android IntentService生命周期问题
  8. block引用外部变量原理
  9. TensorFlow基于minist数据集实现手写字识别实战的三个模型
  10. 开始复习下sql,常用SQL语句示例(一)
  11. MAC查找JDK的路径
  12. Ceres和g2o的配置和使用
  13. 小括号教学设计导入_如何在教学中凸显出学生的主体地位呢?原来只需要做到这几点即可...
  14. STM32工作笔记0027---理解电阻的串联和并联
  15. 原:PHP内核研究 函数的定义
  16. SCPPO(五):解决MVC中Json传输数据量问题
  17. Random 随机数使用示例
  18. IO复用与select函数
  19. Android studio 如何快速收起代码?
  20. R语言之 as.formula()

热门文章

  1. Java基础之continue与break区别
  2. 史玉柱:创业不是靠忽悠,我的最后四个忠告
  3. 串口通信程序中十六进制格式发送和接收实现
  4. 【python】多进程小结
  5. 机器学习——第一回视频总结
  6. windows通过WHL文件安装python-GDAL等拓展包
  7. 使用flask快速搭建API
  8. mysql命令行进入报错ERROR 2002 (HY000)
  9. PHP开启输出错误信息
  10. jdk lambda表达式的坑