DenseNet网络结构详解及代码复现

article/2025/9/14 1:21:52

1. DenseNet论文详解

Abstract:

如果在靠近输入和靠近输出层之间包含更短的连接,那么卷积神经网络可以很大程度上更深,更准确和高效地进行训练。根据这一结果,我们提出了DenseNet(密集卷积网络): 对于每一层,所有前一层地特征图作为输入,而这一层地特征图用作所有后续层地输入。优势有:缓解了梯度消失问题,加强了特征传播,鼓励特征复用,并很大程度上减小了参数的数量。

1.1. Introduction

随着卷积神经网络的不断加深,出现了一个问题——关于输入或梯度在经过很多层到达网络的终点(或起点)时,它可能会消失。对于此问题的解决方法:

  1. ResNet和Highway Networks通过identity connection将信号从一层绕传到另一层
  2. Stochastic depth在训练期间随机掉落层来缩短ResNet,以允许更好的信息和梯度流动
  3. FractalNets重复组合多个具有不同数量卷积块的平行层序列,以获得较大的标称深度,同时在网络中保持许多短路径

这些方法都有一个共同点:创建了从早期层到后期层的短路径

为了确保网络中各层之间最大的信息流,我们将所有层直接连接到彼此。为了保持前馈性质,每一层都从所有前面的层获得额外的输入,并将自己的特性映射传递给所有后续层。与resnet相比,我们从来没有在将特性传递到一个层之前通过累加来组合它们,相反,我们通过特征连接来组合它们。我们将此方法称为DenseNet
在这里插入图片描述

DenseNet的优势:

  • 改进了信息和梯度的流动,使得易于训练
  • 密集连接具有正则化效益,减少了训练集规模较小的过拟合问题

1.2. DenseNets

1.2.1 Compare nets

  • Traditional nets: x l = H l ( x l − 1 ) x_l=H_l(x_{l-1}) xl=Hl(xl1)
  • ResNets x l = H l ( x l − 1 ) + x l − 1 x_l=H_l(x_{l-1}) + x_{l-1} xl=Hl(xl1)+xl1
  • DenseNets x l = H l ( [ x 0 , x 1 , … , x l − 1 ] ) x_l=H_l([x_0,x_1,\dots,x_{l-1}]) xl=Hl([x0,x1,,xl1])

参数说明:

  • x 0 x_0 x0:卷积网络得输入的单个图像
  • 此网络有 L L L层,每一层实现一个非线性变换 H l H_l Hl,其中 l l l表示第 l l l
  • x l x_l xl:第 l l l层的输出

1.2.2 Architecture of net

在这里插入图片描述
在这里插入图片描述

Composite function:定义为3个连续操作的复合函数

  • BN:batch normalization
  • ReLU:rectified linear unit
  • Conv:convolution

Pooling layers

feature-map的大小发生改变时, x l = H l ( [ x 0 , x 1 , … , x l − 1 ] ) x_l=H_l([x_0,x_1,\dots,x_{l-1}]) xl=Hl([x0,x1,,xl1])中的拼接操作是不可行的,需要通过下采样层来改变feature-map的大小,下采样层的组成为:

  • BN层
  • 1x1卷积层
  • 平均层化层

Growth rate

如果每个函数 H l H_l Hl产生 k k kfeature-maps,那么可以得出第 l l l层有 k 0 + k ⋅ ( l − 1 ) k_0+k \cdot (l-1) k0+k(l1)个输入feature-maps,其中 k 0 k_0 k0是输入层的通道数,我们将k作为网络的growth rate。从表1,可知growth rate32

Bottleneck layers

虽然每一层只产生 k k kfeature-maps,但是由于输入很多,我们需要在每一次3x3卷积之前引入一个1x1卷积以减少feature-maps的数量,从而减小参数并提高效率,在实验中,我们发现这种设计对DenseNet很有效,我们让每个1x1卷积产生 4 k 4k 4k个特征图,这样我们的函数 H l H_l Hl为:

  • BN
  • ReLU
  • Conv(1x1)
  • BN
  • ReLU
  • Conv(3x3)

Compression
为了进一步提高模型紧凑型,我们可以减少transition layer输出的特征图数量。如果dense block包含m个特征图,我们让dense block后的transition latyer生成 ⌊ θ m ⌋ \lfloor \theta m\rfloor θm个特征图,其中 θ \theta θ为压缩因子,在我们的实验中设置 θ = 0.5 \theta=0.5 θ=0.5

Implementation Details

  1. 在进入第一个dense block时,对输入图像进行16(或为growth rate两倍)输出通道的卷积
  2. 对于3x3卷积,输入的四周用一个像素填充0,以保证feature-map的大小不变
  3. 使用1x1卷积,然后使用2x2平均池化作为两个dense block的过渡层
  4. 在最后一个dense block的末端,执行一个全局平均池化,然后加一个softmax分类器

2. 基于Pytorch代码复现

2.1 模型搭建

import torch.nn as nn
import torch
from torchvision import modelsclass _DenseLayer(nn.Module):def __init__(self, num_input_features, growth_rate, bn_size, drop_rate=0):super(_DenseLayer, self).__init__()self.drop_rate = drop_rateself.dense_layer = nn.Sequential(nn.BatchNorm2d(num_input_features),nn.ReLU(inplace=True),nn.Conv2d(in_channels=num_input_features, out_channels=bn_size * growth_rate, kernel_size=1, stride=1, padding=0, bias=False),nn.BatchNorm2d(bn_size * growth_rate),nn.ReLU(inplace=True),nn.Conv2d(in_channels=bn_size * growth_rate, out_channels=growth_rate, kernel_size=3, stride=1, padding=1, bias=False))self.dropout = nn.Dropout(p=self.drop_rate)def forward(self, x):y = self.dense_layer(x)if self.drop_rate > 0:y = self.dropout(y)return torch.cat([x, y], dim=1)class _DenseBlock(nn.Module):def __init__(self, num_layers, num_input_features, bn_size, growth_rate, drop_rate=0):super(_DenseBlock, self).__init__()layers = []for i in range(num_layers):layers.append(_DenseLayer(num_input_features + i * growth_rate, growth_rate, bn_size, drop_rate))self.layers = nn.Sequential(*layers)def forward(self, x):return self.layers(x)class _TransitionLayer(nn.Module):def __init__(self, num_input_features, num_output_features):super(_TransitionLayer, self).__init__()self.transition_layer = nn.Sequential(nn.BatchNorm2d(num_input_features),nn.ReLU(inplace=True),nn.Conv2d(in_channels=num_input_features, out_channels=num_output_features, kernel_size=1, stride=1, padding=0, bias=False),nn.AvgPool2d(kernel_size=2, stride=2))def forward(self, x):return self.transition_layer(x)class DenseNet(nn.Module):def __init__(self, num_init_features=64, growth_rate=32, blocks=(6, 12, 24, 16), bn_size=4, drop_rate=0, num_classes=1000):super(DenseNet, self).__init__()self.features = nn.Sequential(nn.Conv2d(in_channels=3, out_channels=num_init_features, kernel_size=7, stride=2, padding=3, bias=False),nn.BatchNorm2d(num_init_features),nn.ReLU(inplace=True),nn.MaxPool2d(kernel_size=3, stride=2, padding=1))num_features = num_init_featuresself.layer1 = _DenseBlock(num_layers=blocks[0], num_input_features=num_features, growth_rate=growth_rate, bn_size=bn_size, drop_rate=drop_rate)num_features = num_features + blocks[0] * growth_rateself.transtion1 = _TransitionLayer(num_input_features=num_features, num_output_features=num_features // 2)num_features = num_features // 2self.layer2 = _DenseBlock(num_layers=blocks[1], num_input_features=num_features, growth_rate=growth_rate, bn_size=bn_size, drop_rate=drop_rate)num_features = num_features + blocks[1] * growth_rateself.transtion2 = _TransitionLayer(num_input_features=num_features, num_output_features=num_features // 2)num_features = num_features // 2self.layer3 = _DenseBlock(num_layers=blocks[2], num_input_features=num_features, growth_rate=growth_rate, bn_size=bn_size, drop_rate=drop_rate)num_features = num_features + blocks[2] * growth_rateself.transtion3 = _TransitionLayer(num_input_features=num_features, num_output_features=num_features // 2)num_features = num_features // 2self.layer4 = _DenseBlock(num_layers=blocks[3], num_input_features=num_features, growth_rate=growth_rate, bn_size=bn_size, drop_rate=drop_rate)num_features = num_features + blocks[3] * growth_rateself.avgpool = nn.AdaptiveAvgPool2d((1, 1))self.fc = nn.Linear(num_features, num_classes)def forward(self, x):x = self.features(x)x = self.layer1(x)x = self.transtion1(x)x = self.layer2(x)x = self.transtion2(x)x = self.layer3(x)x = self.transtion3(x)x = self.layer4(x)x = self.avgpool(x)x = torch.flatten(x, start_dim=1)x = self.fc(x)return xdef DenseNet121(num_classes):return DenseNet(blocks=(6, 12, 24, 16), num_classes=num_classes)def DenseNet169(num_classes):return DenseNet(blocks=(6, 12, 32, 32), num_classes=num_classes)def DenseNet201(num_classes):return DenseNet(blocks=(6, 12, 48, 32), num_classes=num_classes)def DenseNet264(num_classes):return DenseNet(blocks=(6, 12, 64, 48), num_classes=num_classes)def read_densenet121():device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')model = models.densenet121(pretrained=True)model.to(device)print(model)def get_densenet121(flag, num_classes):if flag:net = models.densenet121(pretrained=True)num_input = net.classifier.in_featuresnet.classifier = nn.Linear(num_input, num_classes)else:net = DenseNet121(num_classes)return net

2.2 训练结果如下

  1. 训练数据集与验证集大小以及训练参数
Using 3306 images for training, 364 images for validation
Using cuda GeForce RTX 2060 device for training
lr: 0.0001
batch_size: 16
  1. 使用自己定义的网络训练结果
[epoch 1/10] train_loss: 1.209 val_acc: 0.626
[epoch 2/10] train_loss: 1.035 val_acc: 0.588
[epoch 3/10] train_loss: 0.980 val_acc: 0.679
[epoch 4/10] train_loss: 0.902 val_acc: 0.670
[epoch 5/10] train_loss: 0.838 val_acc: 0.698
[epoch 6/10] train_loss: 0.805 val_acc: 0.712
[epoch 7/10] train_loss: 0.825 val_acc: 0.717
[epoch 8/10] train_loss: 0.765 val_acc: 0.742
[epoch 9/10] train_loss: 0.759 val_acc: 0.755
[epoch 10/10] train_loss: 0.732 val_acc: 0.687
Best acc: 0.755
Finished Training
Train 耗时为:440.4s
  1. 使用预训练模型参数训练结果
[epoch 1/10] train_loss: 0.505 val_acc: 0.909
[epoch 2/10] train_loss: 0.306 val_acc: 0.931
[epoch 3/10] train_loss: 0.240 val_acc: 0.920
[epoch 4/10] train_loss: 0.209 val_acc: 0.898
[epoch 5/10] train_loss: 0.191 val_acc: 0.931
[epoch 6/10] train_loss: 0.198 val_acc: 0.918
[epoch 7/10] train_loss: 0.152 val_acc: 0.940
[epoch 8/10] train_loss: 0.157 val_acc: 0.929
[epoch 9/10] train_loss: 0.143 val_acc: 0.940
[epoch 10/10] train_loss: 0.150 val_acc: 0.923
Best acc: 0.940
Finished Training
Train 耗时为:478.8s

上一篇:ResNet
下一篇:Mobile Net 系列
完整代码


http://chatgpt.dhexx.cn/article/pZ1TorHd.shtml

相关文章

设计模式六大原则(6)开闭原则(Open Close Principle)

开闭原则(Open Close Principle) 定义:一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。 问题由来:在软件的生命周期内,因为变化、升级和维护等原因需要对软件原有代码进行修改时,可…

Linux C/C++编程: 文件操作open/close、fopen与freopen/fclose

open是linux下的底层系统调用函数,fopen与freopen c/c下的标准I/O库函数,带输入/输出缓冲。 linxu下的fopen是open的封装函数,fopen最终还是要调用底层的系统调用open。 所以在linux下如果需要对设备进行明确的控制,那最好使用底…

Linux之open()、close()函数

目录 open函数 函数介绍 头文件及函数原型 参数 close函数 函数介绍 头文件函数原型 open()、close()函数使用 open函数 函数介绍 在Linux中open()函数用来打开或创建一个文件,当打开文件失败时返回值为-1;成功则返回作为文件描述符(一个非负的…

Linux李哥私房菜——open、close和fd

open() 头文件&#xff1a;#include<fcntl.h>//在centos6.0中只要此头文件就可以#include<sys/types.h>#incldue<sys/stat.h> 功能&#xff1a;打开和创建文件&#xff08;建立一个文件描述符&#xff0c;其他的函数可以通过文件描述符对指定文件进行读取与…

详解C中的系统调用open/close/read/write

文章目录 open() and close()read() and write()实操:代码示例1 将in.txt文件中的内容写入到out.txt文件中&#xff08;一个一个字符写入&#xff09;2 将in.txt文件中的内容写入到out.txt文件中&#xff08;数组写入&#xff09; 先谈谈open/close/read/write与fopen/fclose/f…

JavaScript中window对象及open和close使用

Window对象 是一个顶级对象&#xff0c;不是任何对象的属性&#xff0c;所以可以不写window.xxx而直接使用内部的属性和方法。 实际上&#xff0c;在web前端开发时&#xff0c;所有的全局变量都自动成为window对象的属性 Window对象的属性 Screen History Location Navigat…

Python基础(十三)——文件操作(open函数、close函数)

本文以Python3以上为学习基础。 目录 1、 使用文件操作第一原则 2、open函数 2.1、文件打开模式 2.1.1、只读模式打开文件——只读&#xff08;r&#xff09; 2.1.2、读写模式打开文件——读写模式&#xff08;r&#xff09; ​ 2.1.3、写模式打开文件——写模式&#…

layer中的open与close

关于layer中的open方法与close方法 open方法open函数的定义open函数里面optionsopen函数中返回的值 close方法如何使用close方法关于layer中的一些发现 写在最后的话 open方法 open函数用来创建一个弹出层。 open函数的定义 形式为&#xff1a;layer.open(options) 例如&…

open函数详解与close函数详解

open() 头文件&#xff1a;#include<fcntl.h>//在centos6.0中只要此头文件就可以#include<sys/types.h>#incldue<sys/stat.h> 功能&#xff1a;打开和创建文件&#xff08;建立一个文件描述符&#xff0c;其他的函数可以通过文件描述符对指定文件进行读取与…

open和close函数

open函数&#xff1a;打开或创建文件 系统调用open可以用来打开普通文件、块设备文件、字符设备文件、链接文件和管道文件&#xff0c;但只能用来创建普通文件&#xff0c;创建特殊文件需要使用特定的函数。 头文件&#xff1a; #include <sys/types.h> #include <…

linux close 头文件,Linux open close read write lseek函数的使用

我们经常需要在Linux中进行文件操作,今天我就来分享下文件操作用到的一些函数 1 open 所需头文件: 函数原型:int open(const char *pathname,flags,int perms) pathname:被打开的文件名,可包含路径 flag :文件打开的方式,参数可以通过“|” 组合构成,但前3 个参数不能互…

linux的open close函数

目录 opencloseopen 参数说明代码 解析报错不用怕,我提供解决思路 前言 开始进入学linux的第一个阶段 第一阶段的 Linux的系统函数 第一节 先讲 open close 函数 open 怎么在liunx查看呢 我们利用下面的命令 linux自带的工具 man 手册 man 1 是普通shell 的命令 比如 ls ma…

简述Java序列化的几种方式

目录 JDK原生的序列化 字符串获取字节流 Protobuf Protostuff Thrift kryo hessian fst JSON字符串序列化 Jackson Gson FastJson 序列化和反序列化在网络传输过程中需要做的事情。 序列化 就是得的 字节流&#xff0c;反序列化就是得的对象。 下面梳理Java编程需要…

java序列化之writeObject 和readObject

什么是序列化和反序列化&#xff1f; 序列化&#xff1a;将对象转化为字节的过程称为序列化过程。 反序列化&#xff1a;将字节转化为对象的过程称为反序列化。 序列化主要应用于网络传输和数据存储的场景。在java中&#xff0c;只有类实现了java.io.serializable接口&#x…

java序列化总结

目录 对象序列化是什么 为什么需要序列化与反序列化 序列化及反序列化相关知识 Java 序列化中如果有些字段不想进行序列化&#xff0c;怎么办&#xff1f; Java序列化接口 java.io.Serializable 使用序列化和serialVersionUID进行类重构 Java外部化接口 java.io.Externa…

java序列化接口Serializable

Serializable接口说明 类的可序列化性通过实现(implements) java.io.Serializable可序列化接口。 没有实现这个接口的类不会将其任何状态序列化或反序列化。 可序列化类的所有子类型本身可序列化。 序列化接口没有方法或字段只用于识别可序列化的语义。 为了允许序列化不可序…

Java序列化之serialVersionUID

Java序列化之serialVersionUID 今天讲一讲Java对象中的serialVersionUID&#xff0c;先从序列化讲起。 什么是序列化 序列化&#xff0c;简单的说&#xff0c;就是将一个对象转化&#xff08;编码&#xff09;成可以传输的输出流&#xff08;字节流&#xff09;。而反序列化…

一文了解Java序列化与反序列化

目录 序列化示例有父类的对象序列化Serializable和Externalizable区别序列化和反序列化实现serialVersionUID不一致有什么问题1、先注释掉反序列化代码, 执行序列化代码, 然后User类新增一个属性sex2、再注释掉序列化代码执行反序列化代码3、指定serialVersionUID 序列化字段修…

一篇搞懂java序列化Serializable

序列化 (Serialization)将对象的状态信息转换为可以存储或传输的形式的过程。 一、序列化和反序列化的概念 把对象转换为字节序列的过程称为对象的序列化。 把字节序列恢复为对象的过程称为对象的反序列化。 对象的序列化主要有两种用途&#xff1a; 1&#xff09; 把对象…

Java序列化与数据传输

1&#xff09;什么是序列化 ① 序列化&#xff1a;Java 提供了一种对象序列化的机制&#xff0c;该机制中&#xff0c;一个对象可以被表示为一个字节序列&#xff0c;该字节序列包括该对象的数据、有关对象的类型的信息和存储在对象中数据的类型。 堆内存中的java对象数据&…