CNN(五):DenseNet+SE-Net实战

news2024/12/29 8:51:38
  •  🍨 本文为🔗365天深度学习训练营中的学习记录博客
  • 🍖 原作者:K同学啊|接辅导、项目定制

     SE-Net(Squeeze-and-Excitation Networks)是ImageNet2017(ImageNet收官赛)的冠军模型,是由WMW团队发布。具有复杂度低,参数少和计算量小的优点。且SENet思路简单,很容易扩展到已有网络结构如Inception和ResNet中。

1 SENet原理

    目前已有很多工作在空间维度上来提升网络的性能,如Inception等,而SENet将关注点放在了特征通道之间的关系上。其具体策略为:通过学习的方式来自动获取到每个特征通道的重要程度,然后依照这个重要程度去提升有用的特征并抑制对当前任务用处不大的特征,这又被叫做“特征标定”策略。

     具体的SE模块如上图所示。给定一个输入x,其特征通道数为{c}',通过一系列卷积等变换F_{tr}后得到一个特征通道数为c的特征。与传统的卷积神经网络不同,我们需要通过下面三个操作来重标定前面得到的特征。

(1)Squeeze:顺着空间维度来进行特征压缩,将一个通道中整个空间(w\times h)特征编码为一个全局特征,这个实数某种程度上具有全局的感受野,并且输出的通道数和输入的特征通道数相等,例如将形状为(1, 32, 32, 10)的feature map压缩成(1, 1, 1, 10)。此操作通常采用global average pooling来实现。

(2)Excitation:得到全局描述特征后,通过Excitation来获取特征通道之间的关系,它是一个类似于循环神经网络中门的机制。

s=F_{ex}(z,W)=\sigma (g(z,W))=\sigma(W_2ReLU(W_1))

    这里采用包含两个全连接层的bottleneck结构,即中间小两头大的结构:其中第一个全连接层起到降维的作用,并通过ReLU激活,第二个全连接层用来将其恢复至原始的维度。进行Excitation操作的最终目的是为每个特征通道生成权重,即学习到各个通道的激活值(sigmoid激活,值在0~1之间)。

(3)Scale:我们将Excitation的输出权重看做是经过特征选择后的每个特征通道的重要性,然后通过乘法逐通道加权到先前的特征上,完成在通道维度上的对原始特征的重标定,从而使得模型对各个通道的特征更具有辨别能力,这类似于attention机制。

2 SE模块应用分析

    SE模块的灵活性在于它可以直接应用现有的网络结构中,以Inception和ResNet为例,我们只需要在Inception模块或Residual模块后添加一个SE模块即可,具体如下图所示,其中方框旁边的维度信息代表该层的输出,r表示Excitation操作中的降维系数。

 3 SE模型效果对比

    SE模块很容易嵌入到其他网络中,为了验证SE模块的作用,在其它流行网络如ResNet和Inception中引入SE模块,测试其在ImageNet上的效果,如下表所示:

     通过表格内容可以得知SE在不同深度的网络的影响。上表分别展示了ResNet-50、ResNet-101、ResNet-152、ResNeXt-50、ResNeXt-101、VGG-16、BN-Inception、Inception-ResNet-v2嵌入SE模型的结果。original一栏为原作者的实验结果,为公平比较,重新进行了实现,即re-implementation的结果。最后一栏的SE-module是指嵌入了SE模块的结果,它的训练参数和第二栏的re-implementation一致。括号中的值是指相对于re-implementation的精度提升幅度。

    由此可知,嵌入了SE的网络在各种深度的网络中都超过了其原始版本的精度,说明无论网络深度如何,SE模块都能够给网络带来性能上的增益。而且,SE-ResNet-50可以达到和ResNet-101差不多的精度,甚至,SE-ResNet-101超过了更深的ResNet-152的精度。

4 代码实现

4.1 开发环境

电脑系统:ubuntu16.04

编译器:Jupter Lab

语言环境:Python 3.7

深度学习环境:tensorflow

4.2 前期准备

4.2.1 设置GPU

import tensorflow as tf
 
gpus = tf.config.list_physical_devices("GPU")
 
if gpus:
    tf.config.experimental.set_memory_growth(gpus[0], True) # 设置GPU显存用量按需使用
    tf.config.set_visible_devices([gpus[0]], "GPU")

4.2.2 导入数据

import matplotlib.pyplot as plt
# 支持中文
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号
 
import os, PIL, pathlib
import numpy as np
 
from tensorflow import keras
from tensorflow.keras import layers,models
 
data_dir = "../data/bird_photos"
data_dir = pathlib.Path(data_dir)
 
image_count = len(list(data_dir.glob('*/*')))
print("图片总数为:", image_count)

4.2.3 加载数据

batch_size = 8
img_height = 224
img_width = 224
 
train_ds = tf.keras.preprocessing.image_dataset_from_directory(
    data_dir,
    validation_split=0.2,
    subset="training",
    seed=123,
    image_size=(img_height, img_width),
    batch_size=batch_size)
 
val_ds = tf.keras.preprocessing.image_dataset_from_directory(
    data_dir,
    validation_split=0.2,
    subset="validation",
    seed=123,
    image_size=(img_height, img_width),
    batch_size=batch_size)
 
class_Names = train_ds.class_names
print("class_Names:",class_Names)

4.2.4 可视化数据

plt.figure(figsize=(10, 5)) # 图形的宽为10,高为5
plt.suptitle("imshow data")
 
for images,labels in train_ds.take(1):
    for i in range(8):
        ax = plt.subplot(2, 4, i+1)
        plt.imshow(images[i].numpy().astype("uint8"))
        plt.title(class_Names[labels[i]])
        plt.axis("off")

4.2.5 检查数据

for image_batch, lables_batch in train_ds:
    print(image_batch.shape)
    print(lables_batch.shape)
    break

4.2.6 配置数据集

AUTOTUNE = tf.data.AUTOTUNE
 
train_ds = train_ds.cache().shuffle(1000).prefetch(buffer_size=AUTOTUNE)
val_ds = val_ds.cache().prefetch(buffer_size=AUTOTUNE)

4.3  SE模块代码实现

    SE模块的网络结果如下图所示,其参数采用DenseNet121中的SE模块参数。这里降维未使用降维率,而直接将其降维为filter_sq(16)。

import tensorflow as ft

def squeeze_excitation_block(inputs, filter_sq):
    squeeze = tf.keras.layers.GlobalAveragePooling2D()(inputs)
        
    excitation = tf.keras.layers.Dense(filter_sq)(squeeze)
    excitation = tf.keras.layers.Activation('relu')(excitation)
    excitation = tf.keras.layers.Dense(inputs.shape[-1])(excitation)
    excitation = tf.keras.layers.Activation('sigmoid')(excitation)
    excitation = tf.keras.layers.Reshape((1, 1, inputs.shape[-1]))(excitation)
        
    scale = inputs * excitation
        
    return scale

4.4 SE模块嵌入到DenseNet代码实现

    各网络结构如下图所示: 

     其相应的代码为:

from tensorflow.keras.models import Model
from tensorflow.keras import layers, backend

def dense_block(x, blocks, name):
    for i in range(blocks):
        x = conv_block(x, 32, name=name+'_block'+str(i+1))
    return x

def conv_block(x, growth_rate, name):
    bn_axis = 3
    
    x1 = layers.BatchNormalization(axis=bn_axis,
                                  epsilon=1.001e-5,
                                  name=name+'_0_bn')(x)
    x1 = layers.Activation('relu', name=name+'_0_relu')(x1)
    x1 = layers.Conv2D(4*growth_rate, 1, use_bias=False, name=name+'_1_conv')(x1)
    
    x1= layers.BatchNormalization(axis=bn_axis,
                                  epsilon=1.001e-5,
                                  name=name+'_1_bn')(x1)
    x1 = layers.Activation('relu', name=name+'_1_relu')(x1)
    x1 = layers.Conv2D(growth_rate, 3, padding='same', use_bias=False, name=name+'_2_conv')(x1)
    
    x = layers.Concatenate(axis=bn_axis, name=name+'_concat')([x, x1])
    return x

def transition_block(x, reduction, name):
    bn_axis = 3
    x = layers.BatchNormalization(axis=bn_axis, epsilon=1.001e-5,
                                 name=name+'_bn')(x)
    x = layers.Activation('relu', name=name+'_relu')(x)
    x = layers.Conv2D(int(backend.int_shape(x)[bn_axis] * reduction), 1,
                     use_bias=False, name=name+'_conv')(x)
    x = layers.AveragePooling2D(2, strides=2, name=name+'_pool')(x)
    return x

def DenseNet(blocks, input_shape=None, classes=4, **kwargs):
    img_input = layers.Input(shape=input_shape)
    bn_axis = 3
    
    # 224, 224, 3 -> 112, 112, 64
    x = layers.ZeroPadding2D(padding=((3,3), (3,3)))(img_input)
    x = layers.Conv2D(64, 7, strides=2, use_bias=False, name='conv1/conv')(x)
    x = layers.BatchNormalization(axis=bn_axis, epsilon=1.001e-5, name='conv1/bn')(x)
    x = layers.Activation('relu', name='conv1/relu')(x)
    
    # 112, 112, 64 -> 56, 56, 64
    x = layers.ZeroPadding2D(padding=((1,1), (1,1)))(img_input)
    x = layers.MaxPooling2D(3, strides=2, name='pool1')(x)
    
    # 56, 56, 64 -> 56, 56, 64+32*block[0]
    # Densenet121  56, 56, 64 -> 56, 56, 64+32*6 == 56, 56, 256
    x = dense_block(x, blocks[0], name='conv2')
    
    # 56, 56, 64+32*block[0] --> 28, 28, 32+16*block[0]
    # Densenet121  56, 56, 256 -> 28, 28, 32+16*6 == 28, 28, 128
    x = transition_block(x, 0.5, name='pool2')
    
    
    # 28, 28, 32+16*block[0] -> 28, 28, 32+16*block[0]+32*block[1]
    # Densenet121  28, 28, 128 -> 28, 28, 128+32*12 == 28, 28, 512
    x = dense_block(x, blocks[1], name='conv3')
    
    # Densenet121  28, 28, 512 -> 14, 14, 256
    x = transition_block(x, 0.5, name='pool3')
    
    
    # Densenet121  14, 14, 256 -> 14, 14, 256+32*block[2] == 14, 14, 1024
    x = dense_block(x, blocks[2], name='conv4')
    
    # Densenet121  14, 14, 1024 -> 7, 7, 512
    x = transition_block(x, 0.5, name='pool4')
    
    
    # Densenet121  7, 7, 512 -> 7, 7, 256+32*block[3] == 7, 7, 1024
    x = dense_block(x, blocks[3], name='conv5')
    
    # 加SE注意力机制
    x = squeeze_excitation_block(x, 16) #Squeeze_excitation_layer(16)(x)
    
    x = layers.BatchNormalization(axis=bn_axis, epsilon=1.001e-5, name='bn')(x)
    x = layers.Activation('relu', name='relu')(x)
    
    x = layers.GlobalAveragePooling2D(name='avg_pool')(x)
    x = layers.Dense(classes, activation='softmax', name='fc4')(x)
    
    inputs = img_input
    
    if blocks == [6, 12, 24, 16]:
        model = Model(inputs, x, name='densenet121')
    elif blocks == [6, 12, 32, 32]:
        model = Model(inputs, x, name='densenet169')
    elif blocks == [6, 12, 48, 32]:
        model = Model(inputs, x, name='densenet201')
    else:
        model = Model(inputs, x, name='densenet')
        
    return model

def DenseNet121(input_shape=[224, 224, 3], classes=4, **kwargs):
    return DenseNet([6, 12, 24, 16], input_shape, classes, **kwargs)

def DenseNet169(input_shape=[224, 224, 3], classes=4, **kwargs):
    return DenseNet([6, 12, 32, 32], input_shape, classes, **kwargs)

def DenseNet121(input_shape=[224, 224, 3], classes=4, **kwargs):
    return DenseNet([6, 12, 48, 32], input_shape, classes, **kwargs)

model = DenseNet121(input_shape=(224,224,3))
model.summary()

     结果显示如下(由于结果内容较多,只展示前后部分内容):

 (中间内容省略)

 4.5 正式训练

# 设置优化器
opt = tf.keras.optimizers.Adam(learning_rate=1e-4)

model.compile(optimizer="adam",
             loss='sparse_categorical_crossentropy',
             metrics=['accuracy'])

epochs = 10

history = model.fit(
                train_ds,
                validation_data=val_ds,
                epochs=epochs)

     结果如下图所示:

 4.6 模型评估

acc = history.history['accuracy']
val_acc = history.history['val_accuracy']

loss = history.history['loss']
val_loss = history.history['val_loss']

epochs_range = range(epochs)

plt.figure(figsize=(12, 4))
plt.subplot(1, 2, 1)
plt.suptitle("DenseNet-SE test")

plt.plot(epochs_range, acc, label='Training Accuracy')
plt.plot(epochs_range, val_acc, label='Validation Accuracy')
plt.legend(loc='lower right')
plt.title('Training and Validation Accuracy')

plt.subplot(1, 2, 2)
plt.plot(epochs_range, loss, label='Training Loss')
plt.plot(epochs_range, val_loss, label='Validation loss')
plt.legend(loc='upper right')
plt.title('Training and Validation loss')
plt.show()

      结果如下图所示:

5 总结

    普通的卷积实际上是对局部区域进行的特征融合,因此其感受野不大,若设计出更多的通道特征来增加这个,不可避免的将导致计算量大大的增加。而SENet网络的创新点在于关注channel之间的关系,希望模型可以自动学习到不同channel特征的重要程度。

    简而言之,在每个channel上将整个特征图浓缩成一个值,即在Squeeze步骤中通过averagepooling的操作计算每个通道的特征,此时每个通道只有一个特征,即size为c;然后在Excitation步骤中,通过降维+ReLU+升维+sigmoid操作,建模出特征通道之间的相互依赖关系,计算出每个特征通道的重要程度,此时size仍为c,c中的每个元素代表着相应通道的重要程度,越重要则越接近1;最后在Scale步骤中,将之前的操作得出的特征图进行scale操作,而scale的权重就是刚刚计算出的Excitation特征(size为c)通过reshape后(size为1*1*c)的矩阵,即对各个通道的特征进行相应的放大或缩小。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ardupilot开发 --- log篇

懂的都懂,你也要懂 log作用记录您的飞行数据; 两种方式或类型: Data flash log ,通常记录在SD卡上,可通过地面站下载; Telemetry logs(Tlogs),地面站通过无线设备进行实…

软件测试(黑皮书)学习一

第一部分 软件测试综述 第一章 软件测试背景 1.1软件缺陷(software bug) 软件失败的术语 故障(fault)失败(failure) 缺点(defect) ------严重、危险异常(anomaly&…

STEP/STP模型文件在线查看【3D CAD】

STEP 文件(正式称为 ISO 10303)是 3D 模型的流行文件格式。 该格式名称中的字母代表“产品数据交换标准”。 这种文件格式是由 ISO 自动化系统和集成技术委员会(称为 TC 184)于 20 世纪 80 年代中期开发的。STEP 格式的创建是为了…

java.lang.Long cannot be cast to java.lang.Integer解决

出错代码:接收泛型指定是Integer 但是在接测试中 频频抛出java.lang.Long cannot be cast to java.lang.Integer异常,debug 发现Map中的参数确实是Long类型的。 解决办法: 使用父类的Number接收,在使用intValue()方法转成int 原因分析&#…

恒运资本:机构持仓比例多少是重仓?

组织投资者关于股票商场的影响越来越大,其持股份额已成为点评一只股票好坏的重要目标之一。但组织持仓份额究竟多少才算是重仓呢?这涉及到许多要素,让我们从多个视点进行剖析。 1.不同组织的界说不同 首要需求注意的是,不同的组织…

OpenCV-Python中的图像处理-图像特征

OpenCV-Python中的图像处理-图像特征 图像特征Harris角点检测亚像素级精度的角点检测Shi-Tomasi角点检测SIFT(Scale-Invariant Feature Transfrom)SURF(Speeded-Up Robust Features)FAST算法BRIEF(Binary Robust Independent Elementary Features)算法ORB (Oriented FAST and R…

网络综合布线实训室建设方案

一、网络综合布线系统概述 网络综合布线系统是为了满足数据通信需求而设计和建立的一套基础设施。它提供了数据传输、信号传输和电力供应的基础结构,支持各种网络设备和终端设备之间的连接。 网络综合布线系统通常包括以下组成部分: 1) 数据…

Element:input输入框远程搜索返回输入建议的代码详解

文章目录 1 原始代码2 代码详解3 添加注释后的代码&#xff1a;4 createStateFilter方法中0代表什么&#xff1f;5 包含即返回建议值 1 原始代码 官网链接&#xff1a;https://element.eleme.cn/#/zh-CN/component/input 官网代码&#xff1a; <template><el-autoc…

Betty核心源码解析(二)--ServerBootstrap启动过程

serverbootstrap用于建立netty服务端&#xff0c;核心逻辑-- 设置线程池-- bossGroup和workGroup设置accept连接handler定义服务器的色弱v儿serversocketchannel实现设置IO读写的业务逻辑相关childHanlder绑定监听端口-- 创建serversocketchannel对象初始化serversocketchanne…

​ Spring Clould 配置中心 - Nacos

视频地址&#xff1a;微服务&#xff08;SpringCloudRabbitMQDockerRedis搜索分布式&#xff09; Nacos配置管理-Nacos实现配置管理&#xff08;P24、P25&#xff09; Nacos除了可以做注册中心&#xff0c;同样可以做配置管理来使用。 当微服务部署的实例越来越多&#xff0c…

java练习4.快速查找

题目: 数组 arr[6,1,3,7,9,8,5,4,2],用快速排序进行升序排序. import java.util.Random;public class recursionDemo {public static void main(String[] args) {/*快速排序:* 第一轮:以0索引为基准数,确定基准数在数组正确的位置,* 比基准数小的放到左边,比基准数大的放在右边…

Python数据分析实战-*和**实现可变多参数的传入或变量的拆解(附源码和实现效果)

实现功能 *和**实现多参数的传入或变量的拆解 实现代码 # 1、实现多参数的传入 def one(a,*b):"""a是一个普通传入参数&#xff0c;*b是一个非关键字星号参数"""print(b) one(1,2,3,4,5,6) 其中&#xff0c;第一个的输入可以理解为&#xff1a…

记一次微信小游戏渗透测试

本文转载于&#xff1a;https://www.freebuf.com/vuls/371936.html 准备工作 因为目标站点只能用微信打开&#xff0c;微信又不能调试看代码。这里推荐可以使用pc端旧版微信3.2.1&#xff0c;具体方法放链接里&#xff1a; https://blog.csdn.net/qq_45863248/article/details/…

(7)(7.3) 自动任务中的相机控制

文章目录 前言 7.3.1 概述 7.3.2 自动任务类型 7.3.3 创建合成图像 前言 本文介绍 ArduPilot 的相机和云台命令&#xff0c;并说明如何在 Mission Planner 中使用这些命令来定义相机勘测任务。这些说明假定已经连接并配置了相机触发器和云台(camera trigger and gimbal hav…

批量爬虫采集大数据的技巧和策略分享

作为一名专业的爬虫程序员&#xff0c;今天主要要和大家分享一些技巧和策略&#xff0c;帮助你在批量爬虫采集大数据时更高效、更顺利。批量爬虫采集大数据可能会遇到一些挑战&#xff0c;但只要我们掌握一些技巧&#xff0c;制定一些有效的策略&#xff0c;我们就能在数据采集…

【设计模式】订单状态流传中的状态机与状态模式

文章目录 1. 前言2.状态模式2.1.订单状态流转案例2.1.1.状态枚举定义2.1.2.状态接口与实现2.1.3.状态机2.1.4.测试 2.2.退款状态的拓展2.2.1.代码拓展2.2.2.测试 2.3.小结 3.总结 1. 前言 状态模式一般是用在对象内部的状态流转场景中&#xff0c;用来实现状态机。 什么是状态…

Lnton羚通关于PyTorch的保存和加载模型基础知识

SAVE AND LOAD THE MODEL (保存和加载模型) PyTorch 模型存储学习到的参数在内部状态字典中&#xff0c;称为 state_dict, 他们的持久化通过 torch.save 方法。 model models.shufflenet_v2_x0_5(pretrainedTrue) torch.save(model, "../../data/ShuffleNetV2_X0.5.pth…

如何在前端实现WebSocket发送和接收TCP消息(多线程模式)

目录 第一步&#xff1a;创建WebSocket连接第二步&#xff1a;监听WebSocket事件第三步&#xff1a;发送消息第四步&#xff1a;后端处理函数说明 当在前端实现WebSocket发送和接收TCP消息时&#xff0c;可以使用以下步骤来实现多线程模式。本文将详细介绍如何在前端实现WebSoc…

DDT数据驱动+Pytest+Allure+自定义代码封装

DDT数据驱动PytestAllure自定义代码封装 CASE --判断运行--单文件去进行运行 CASE --判断 运行–单文件去进行运行 一次性运行多个case——Pytest 1.前置条件&#xff1a; pip install pytest 2.Pytest——脚手架——可以站在它的身上做一系列的事情 3.规则的遵守 4.test_开头…

传感网应用开发实训室建设方案

传感网应用开发实训室概述 物联网是我国战略性新兴产业的重要组成部分&#xff0c;《物联网“十二五”发展规划》圈定了10大领域重点示范工程&#xff0c;第一个关键技术创新工程提出“充分发挥企业主体作用&#xff0c;积极利用高校和研究所实验室的现有研究成果&#xff0c;在…