densenet详解_resnet详解

densenet详解_resnet详解本文主要介绍近几年效果比较好的DenseNet网络并代码实现。

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE稳定放心使用

简介

从2012年AlexNet大展身手以来,卷积神经网络经历了(LeNet、)AlexNet、ZFNet、VGGNet、GoogLeNet(借鉴Network in Network)、ResNet、DenseNet的大致发展路线。其实,自从ResNet提出之后,ResNet的变种网络层出不穷,各有特点,性能都略有提高。

在这种情况下,DenseNet可以说是“继往开来”也不为过,作为2017年CVPR最佳论文(认真研读这篇论文,绝对会感觉心潮澎湃),DenseNet思想上部分借鉴了“前辈”ResNet,但是采用的确实完全不同的结构,结构上并不复杂却十分有效,在CIFAR数据集上全面超越了ResNet。

本项目着重实现使用Keras搭建DenseNet的网络结构,同时,利用其在数据集上进行效果评测。

  • 论文标题

    Densely Connected Convolutional Networks

  • 论文地址

    https://arxiv.org/abs/1608.06993

  • 论文源码

    https://github.com/pytorch/vision/blob/master/torchvision/models/densenet.py

网络说明

设计背景

以往的卷积神经网络提高效果的方法,要么更深(如ResNet,解决了深层网络出现的梯度消失问题),要么更宽(如GoogLeNet的Inception结构),而DenseNet的作者从feature着手,通过对feature的极致利用来达到更好的效果同时减少参数

为了解决梯度消失(vanishing-gradient)问题,很多论文及结构被提出如ResNet、Highway Networks、Stochastic depth等,尽管网络结构有所差异,但是都不难改变一个核心思路从靠前的层到靠后的层之间创建直通路线如之前提到的ResNet中的shortcut。DenseNet的作者延续这个思路,提出在保证网络中层与层之间最大程度的信息传输的前提下,直接将所有层连接起来。

结构设计

dense block

为了实现上述的所有层的信息传递,提出下面这张dense block结构。在之前的卷积神经网络中,如果有L层就会有L个连接,但是在DenseNet中,L层会有 L × ( L + 1 ) ÷ 2 L \times (L+1) \div 2 L×(L+1)÷2个连接,这种连接方式称为全连接。在下图中, x 0 x_0 x0是输入的feature map,层 H 1 H_1 H1的输入就是 x 0 x_0 x0,层 H 2 H_2 H2的输入则是 x 0 x_0 x0 x 1 x_1 x1

在这里插入图片描述

DenseNet的网络很窄参数很少,这很大程度上得益于dense block的设计,在dense block中每个卷积层输出的feature map数量很少(基本上小于100)。同时这种连接方式使得特征和梯度的传递更加有效,网络更加容易训练,主要是因为梯度消失的主要原因就是输入信息以及梯度信息在很多层之间传递导致的(这和前馈运算以及反馈运算的计算机制有关),采用dense block连接则相当于每一层和input及loss直接相连,减轻梯度消失的发生。

同时,这种dense connection有正则化的效果,对于过拟合有一定的抑制作用。

网络结构

流程示意

在这里插入图片描述

结构说明

有趣的是,这篇论文全文只出现了两个公式却让人明明白白理解的论文的核心要义(比起很多大段都是公式罗列的灌水文章确实好太多了)。

x ℓ = H ℓ ( x ℓ − 1 ) + x ℓ − 1 \mathbf{x}_{\ell}=H_{\ell}\left(\mathbf{x}_{\ell-1}\right)+\mathbf{x}_{\ell-1} x=H(x1)+x1

上式借鉴ResNet思路,第l层的输出是l-1层的输出加上对l-1层输出的非线性变换(激活)。

x ℓ = H ℓ ( [ x 0 , x 1 , … , x ℓ − 1 ] ) \mathbf{x}_{\ell}=H_{\ell}\left(\left[\mathbf{x}_{0}, \mathbf{x}_{1}, \ldots, \mathbf{x}_{\ell-1}\right]\right) x=H([x0,x1,,x1])

上式由DenseNet提出,对0到l-1层的输出做concatenation(通道合并,类似Inception),H表示BN、Relu和3*3卷积。

作者将整个DenseNet理解为多个dense block以及其他层的组合,这样可以确保每个dense block的size统一方便concate。

在这里插入图片描述

可以看到,作者主要提出了四种网络架构分别是DenseNet121、DenseNet169、DenseNet201以及DenseNet264,其中k表示growth rate,表示每个dense block每层输出的feature map个数,为了避免过宽的网络,设计均使用较小的k如32(事实证明,小的k具有更好的效果)。而且,由于后面的各层均可以得到前面各层的输出,所以最后concat得到的feature map通道数不小。

同时,每个dense block的3*3卷积前都加了一个1*1卷积,即为bottleneck layer,以求减少feature map数量,降维的同时融合了各通道的特征。此外,为了进一步压缩参数量,每两个dense block之间增加了1*1卷积操作,它和池化层配合构成了Transition layer,经过该层默认channel减半。DenseNet-C表示增加了Transition layer,DenseNet-BC则表示既有bottleneck又有Transition layer。bottleneck主要是为了减少通道数进行降维,减少计算量;Transition layer主要目的也是对feature map进行降维,减少通道数。

网络效果

在这里插入图片描述

DenseNet-BC的网络参数和相同深度的DenseNet相比确实减少了很多,参数减少可以节省内存,减少过拟合现象的发生。总的来说,有力冲击ResNet的地位。

网络优点

  • 减轻了梯度消失(vanishing-gradient)
  • 大大加强了feature的传递
  • 更加深入利用了feature
  • 一定程度上减少了参数数量

代码实现

由于DenseNet最基础的DenseNet121都高达121层且代码封装度很高,对该模型进行了高度封装。

实际使用各个深度学习框架已经封装了DenseNet的几种主要网络结构(DenseNet121等),使用很方便,不建议自己搭建(尤其对于DenseNet这样很深的网络)。

网络构建对照结构表即可,这是复现论文网络结构的主要依据。默认参数实现的是带bottleneck的DenseNet121,其他结构调整函数的参数即可,参考代码

from keras.models import Model
from keras.layers import BatchNormalization, Conv2D, Activation, Dropout, AveragePooling2D, concatenate, GlobalAveragePooling2D, MaxPooling2D, Dense, Input
from keras.regularizers import l2
import keras.backend as K
def Conv_Block(input_tensor, filters, bottleneck=False, dropout_rate=None, weight_decay=1e-4):
""" 封装卷积层 :param input_tensor: 输入张量 :param filters: 卷积核数目 :param bottleneck: 是否使用bottleneck :param dropout_rate: dropout比率 :param weight_decay: 权重衰减率 :return: """
concat_axis = 1 if K.image_data_format() == 'channel_first' else -1  # 确定格式
x = BatchNormalization(axis=concat_axis, epsilon=1.1e-5)(input_tensor)
x = Activation('relu')(x)
if bottleneck:
# 使用bottleneck进行降维
inter_channel = filters * 4
x = Conv2D(inter_channel, (1, 1),
kernel_initializer='he_normal',
padding='same', use_bias=False,
kernel_regularizer=l2(weight_decay))(x)
x = BatchNormalization(axis=concat_axis, epsilon=1.1e-5)(x)
x = Activation('relu')(x)
x = Conv2D(filters, (3, 3), kernel_initializer='he_normal', padding='same', use_bias=False)(x)
if dropout_rate:
x = Dropout(dropout_rate)(x)
return x
def Transition_Block(input_tensor, filters, compression_rate, weight_decay=1e-4):
""" 封装Translation layer :param input_tensor: 输入张量 :param filters: 卷积核数目 :param compression_rate: 压缩率 :param weight_decay: 权重衰减率 :return: """
concat_axis = 1 if K.image_data_format() == 'channel_first' else -1  # 确定格式
x = BatchNormalization(axis=concat_axis, epsilon=1.1e-5)(input_tensor)
x = Activation('relu')(x)
x = Conv2D(int(filters * compression_rate), (1, 1),
kernel_initializer='he_normal',
padding='same',
use_bias=False,
kernel_regularizer=l2(weight_decay))(x)
x = AveragePooling2D((2, 2), strides=(2, 2))(x)
return x
def Dense_Block(x, nb_layers, filters, growth_rate, bottleneck=False, dropout_rate=None, weight_decay=1e-4, grow_nb_filters=True, return_concat_list=False):
""" 实现核心的dense block :param x: 张量 :param nb_layers: 模型添加的conv_block数目 :param filters: 卷积核数目 :param growth_rate: growth rate :param bottleneck: 是否加入bottleneck :param dropout_rate: dropout比率 :param weight_decay: 权重衰减 :param grow_nb_filters: 是否允许核数目增长 :param return_concat_list: 是否返回feature map 的list :return: """
concat_axis = 1 if K.image_data_format() == 'channels_first' else -1
x_list = [x]
for i in range(nb_layers):
cb = Conv_Block(x, growth_rate, bottleneck, dropout_rate, weight_decay)
x_list.append(cb)
x = concatenate([x, cb], axis=concat_axis)
if grow_nb_filters:
filters += growth_rate
if return_concat_list:
return x, filters, x_list
else:
return x, filters
def DenseNet(n_classes=1000, input_shape=(224, 224, 3), include_top=True, nb_dense_block=4, growth_rate=32, nb_filter=64,
nb_layers_per_block=[6, 12, 24, 16], bottleneck=True, reduction=0.5, dropout_rate=0.0, weight_decay=1e-4,
subsample_initial_block=True):
concat_axis = 1 if K.image_data_format() == 'channel_first' else -1
final_nb_layer = nb_layers_per_block[-1]
nb_layers = nb_layers_per_block[:-1]
compression = 1.0 - reduction
if subsample_initial_block:
initial_kernel = (7, 7)
initial_strides = (2, 2)
else:
initial_kernel = (3, 3)
initial_strides = (1, 1)
input_tensor = Input(shape=input_shape)
x = Conv2D(nb_filter, initial_kernel, kernel_initializer='he_normal', padding='same',
strides=initial_strides, use_bias=False, kernel_regularizer=l2(weight_decay))(input_tensor)
if subsample_initial_block:
x = BatchNormalization(axis=concat_axis, epsilon=1.1e-5)(x)
x = Activation('relu')(x)
x = MaxPooling2D((3, 3), strides=(2, 2), padding='same')(x)
for block_index in range(nb_dense_block - 1):
x, nb_filter = Dense_Block(x, nb_layers[block_index], nb_filter, growth_rate, bottleneck=bottleneck,
dropout_rate=dropout_rate, weight_decay=weight_decay)
x = Transition_Block(x, nb_filter, compression_rate=compression, weight_decay=weight_decay)
nb_filter = int(nb_filter * compression)
x, nb_filter = Dense_Block(x, final_nb_layer, nb_filter, growth_rate, bottleneck=bottleneck,
dropout_rate=dropout_rate, weight_decay=weight_decay)
x = BatchNormalization(axis=concat_axis, epsilon=1.1e-5)(x)
x = Activation('relu')(x)
x = GlobalAveragePooling2D()(x)
if include_top:
x = Dense(n_classes, activation='softmax')(x)
model = Model(input_tensor, x, name='densenet121')
return model

模型训练

数据集使用Caltech101数据集,比较性能,不进行数据增广(注意删除干扰项)。Batch大小指定为32,使用BN训练技巧,二次封装Conv2D。损失函数使用经典分类的交叉熵损失函数,优化函数使用Adam,激活函数使用Relu。(这都是比较流行的选择)

具体训练结果见文末Github仓库根目录notebook文件。

损失图像

在这里插入图片描述
准确率图像

在这里插入图片描述

可以对比之前的ResNet,显然,同一个数据集上同样的超参数设置,DenseNet的收敛速度快了很多(较快达到饱和准确率且这是诸多结构网络所能达到的最高验证集准确率),但是这样的快速收敛的代价就是内存的巨大消耗。

补充说明

DenseNet最伟大之处在于其核心思想为建立不同层之间的连接关系,充分利用feature,减轻梯度消失问题。同时配合以bottleneck和transition layer以降维减参。本项目源码开源于我的Github,欢迎Star或者Fork。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/187928.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • github开源可视化_可视化拖拽项目管理 github

    github开源可视化_可视化拖拽项目管理 githubGIT下载:国内太慢甚至下载不了的解决方法。在淘宝的镜像网站上下载: https://npm.taobao.org/mirrors/git-for-windows/gitconfig

  • python取整符号_python 取整「建议收藏」

    广告关闭腾讯云11.11云上盛惠,精选热门产品助力上云,云服务器首年88元起,买的越多返的越多,最高返5000元!(1)向下取整向下取整很简单,直接使用int()函数即可,如下代码(python2.7.5idle)a=3.75int(a)3(2)四舍五入第二种就是对数字进行四舍五入,具体的看下面的代码:a=3.25;b=3.75round(a);round(b)3.0…

  • Android游戏引擎选择[通俗易懂]

    Android游戏引擎选择[通俗易懂]Android游戏引擎选择今天在博客园看到一篇关于android游戏引擎的选择文章,特转载以便日后了解1.Ronkon(网站地址:http://www.ronkonandroid.com)如果不是想帮助作者解决一大堆兼容性问题的话还是不要使用这个引擎,我在上面浪费了1天半,就只是为了把实例程序跑起来。开始还以为是我水平菜,结果一堆人没跑起来,和我一样都是黑屏。虽然它文档做得好但我还是放弃了,本来Android平台兼容性就是老大难,在来个半吊子的引擎我可没本事搞定。2.AndEngine(网站地址:ht

  • Flutter一步实现Toast 弹窗(fluttertoast 库)

    Flutter一步实现Toast 弹窗(fluttertoast 库)Flutter一步实现Toast弹窗(fluttertoast库)在Flutter中,想要实现一个Toast弹窗效果,最简单、最直接的方法就是使用fluttertoast库来实现。官网地址:https://pub.flutter-io.cn/packages/fluttertoast如何使用?导入依赖#addthislinetoyourdependenciesfluttertoast:^7.1.6在使用的地方,导入类import’package:f

  • linux根据进程号查看进程位置信息_shell获取进程的进程号

    linux根据进程号查看进程位置信息_shell获取进程的进程号1)说明:Linux的所有进程都保存在/proc/目录下,保存形式为:/proc/进程号。进入到进程号目录后,里面有一个cwd链接文件即指向的进程的的目录。2)操作:A:确定进程号。如:4874;B:查找进程执行的文件。psaux|grep4874C:确定进程所在的目录。ll/proc/4874;技术交流CleverCode是一名架构师,技术交…

    2022年10月31日
  • 把字符串转换成float类型_c++如何将string类型转换成int类型

    把字符串转换成float类型_c++如何将string类型转换成int类型在学习数据结构经常碰到各种数据间转换的例程,就想c的库里面有没有现有的函数,用的时候直接调用就好,一查果然有:整型转字符串itoa();字符串转整型atoi();用的时候需要添加头文件#include<stdlib.h>。itoa();功能:将整形(int)转换为字符串(char),十进制转任意进制,以字符形式输出(转换后的结果以字符形式输出)使用方式:首先要申明头文件stdlib.hchar*_itoa(intvalue,char*string,intradix).

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号