通过实验验证GAP、GMP、FC性能表现

深度学习


文章目录

  • 深度学习
  • 一、背景
  • 二、什么是GAP
  • 三、GAP在Keras中的定义
  • 四、GAP VS GMP VS FC
  • 五、结论


一、背景

Global Average Pooling(简称GAP,全局池化层)技术最早提出是可以替代全连接层的一种新技术。在keras发布的经典模型中,可以看到不少模型甚至抛弃了全连接层,转而使用GAP,而在支持迁移学习方面,各个模型几乎都支持使用Global Average Pooling和Global Max Pooling(GMP)。 然而,GAP是否真的可以取代全连接层?核心的技术原理是什么?

二、什么是GAP

简单来说,就是在卷积层之后,用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 your
          Keras 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种不同花类的数据集,
地址为:https://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 block
	x = 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.output
        x = 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.output
        prediction = 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.output
        prediction = 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 history

fc_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.output
        x = 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.output
        prediction = 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.output
        prediction = 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 history

fc_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层。当然具体情况具体分析,我们拿到数据集后,可以在几种方式中多训练测试,以寻求最优解决方案。

参考链接
一文搞懂池化层!Pooling详解(网络下采样篇)
全局平均池化能否完美代替全连接
池化层pooling与采样

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/420954.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Cy8c42(2.软件串口使用)

1.前言 原本早上想开始用可编程逻辑模块来做RS寄存器的,但是RS寄存器要两个输入,板载只有一个按键,那就先看看串口吧,把串口作为一个按键用。 2.初始化 芯片内部一共有两种串口 (1)软件串口&#xff08…

企业数字化转型的第一步:由被动多云向主动多云转变

随着经济环境、市场形势、技术发展、用户需求等诸多因素的变化,数字化转型为企业进一步提升效率和竞争力、提供更加丰富的个性化产品和服务、进行业务场景创新、探寻新的增长机会和运营模式提供了崭新的途径。越来越多的企业意识到,数字化转型已不是企业…

【嵌入式——QT】数值输入和显示组件

数值输入和显示组件 QSlider:滑动条,通过滑动来设置数值;QScrollBar:卷滚条,与QSlider类似,还可以用于卷滚区域;QProgressBar:进度条,一般用于显示任务进度,…

【Leetcode每日一题】二分查找 - 在排序数组中查找元素的第一个和最后一个位置(难度⭐⭐)(18)

1. 题目解析 Leetcode链接:34. 在排序数组中查找元素的第一个和最后一个位置 这个问题的理解其实相当简单,只需看一下示例,基本就能明白其含义了。 核心在于找到给定目标值所在的数组下标区间,设计一个O(logn)的算法。 2. 算法原…

集成测试之我的初步学习与总结

基本概念 将软件集成起来后进行测试。 集成测试又叫子系统测试、组装测试、部件测试等。集成测试主要是针对软件高层设计进行测试,一般来说是以模块和子系统为单位进行测试。 集成测试包含的层次 模块内的集成,主要是测试模块内各个接口间的交互集成…

【C++】用文件流的put和get成员函数读写文件

题目 编写一个mycopy程序,实现文件复制的功能。用法是在控制台输入: mycooy 源文件名 目标文件名 参数介绍 m a i n main main 函数的参数有两个,一个int类型参数和一个指针数组。 a r g c argc argc 表示参数的个数。参数为void时 a r g …

Github上最值得学习的10个Android开源项目,安卓面试题

1.Java语言进阶与Android相关技术核 Android应用是由Java语言进行开发的,SDK也是由Java语言编写,对于Android来说,只要SDK没有用Kotlin重写,那么Java语言是都需要学习的。而且Android APK的后台服务器程序大概率是Java语言构建&a…

一【初识EMC】

在作为硬件行业相关从业者,经常接触到EMC相关问题,下面来简单介绍下EMC相关方面的知识 文章目录 前言一、生活中的EMC现象?二、EMC是什么三、EMC的三要素四、EMI与EMS的评估方式1.RE2.CE3.HAR4.FLICKER5.Rs6.CS7.ESD8.EFT9.DIP10.PMS11.surge…

内置kpi接口短视频解析html源码

内置kpi接口短视频解析html源码,复制代码即可解析视频并 去水印 源码免费下载地址专业知识分享社区-专业知识笔记免费分享 (chaobiji.cn)

flutter旋转动画,Android彻底组件化方案实践方法

Android基础 & 常用 针对Android基础&常用知识,我认为对于初级开发者来说,按照优先级最主要的知识点主要包括:四大组件、布局使用、多线程 & 动画;具体介绍如下: 2. Android进阶 针对Android进阶知识&am…

Java通过Semaphore控制同一时间只有3个线程运行

怎么控制同一时间只有3个线程运行? 直接上代码 import java.util.Date; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Semaphore;public class SemaphoreThreadPoolExample {private static…

精酿啤酒:从原料到成品的质量控制流程

质量控制是啤酒酿造过程中重要的一环,它涉及到从原料选择到成品生产的每一个环节。Fendi Club啤酒对其质量控制流程有着严格的要求,以确保产品的品质和一致性。 Fendi Club啤酒对原料的选择进行严格把关。他们选用上好、新鲜的麦芽、水和酵母等原料&…

MySql安全加固:可信IP地址访问控制 设置密码复杂度

MySql安全加固:可信IP地址访问控制 & 设置密码复杂度 1.1 可信IP地址访问控制1.2 设置密码复杂度 💖The Begin💖点点关注,收藏不迷路💖 1.1 可信IP地址访问控制 当您在创建用户时使用’%作为主机部分,…

zookeeper启动报错

启动zookeeper报错 从报错中可以看到 Invalid config, exiting abnormally 意思是:配置无效,异常退出 在往上看是没有zoo.cof这个配置文件 2024-02-27 14:47:03,285 [myid:] - ERROR [main:o.a.z.s.q.QuorumPeerMain99] - Invalid config, exiting…

怎样进入powershell状态?怎样获取powershell的help信息?

进入powershell的方法之一:在dos命令窗口运行powershell命令,系统输出版权信息,命令行提示符前面出现PS 标志。说明系统进入powerrshell状态(如下图)。 获取powershell的help信息方法: 在powershell命令行…

android适配器adapter,Android程序员架构之路该如何继续学习

便于开发的插件、工具和第三方开源库 1.GsonFormat 使用方法:快捷键AltS也可以使用AltInsert选择GsonFormat,作用:速将json字符串转换成一个Java Bean,免去我们根据json字符串手写对应Java Bean的过程。 2.ButterKnife Zelezny …

Windows批处理:bat文件学习

目录 第一章、快速了解Windows批处理1.1)Windows批处理相关概念介绍1.1.1)批处理的起源1.1.2)bat文件介绍 1.2)Demo1.2.1)创建文件添加命令1.2.2)bat脚本中的命令解释 第二章、实例2.1)点击bat文…

Java集合容器面试题

Java集合容器面试题 前言1、同步容器与并发容器之间的关系?2、阻塞队列的作用?3、阻塞队列了解多少?4、BlockingQueue接口中的一些方法?5、Java中的集合类有几种?6、数组和集合的区别?7、ArrayList、LinkLi…

【二分】二分模板+二分题目

一、朴素二分 . - 力扣(LeetCode). - 备战技术面试?力扣提供海量技术面试资源,帮助你高效提升编程技能,轻松拿下世界 IT 名企 Dream Offer。https://leetcode.cn/problems/binary-search/description/ int left 0, right nums.…

Windows Docker 部署 Jenkins

一、简介 今天介绍一下在 Windows Docker 中部署 Jenkins 软件。在 Windows Docker 中,分为两种情况 Linux 容器和 Windows 容器。Linux 容器是通常大多数使用的方式,Windows 容器用于 CI/CD 依赖 Windows 环境的情况。 二、Linux 容器 Linux 容器内部…