深度学习训练营_第J3周_DenseNet算法实战与解析
- 🍨 本文为🔗365天深度学习训练营 中的学习记录博客
- 🍖 原作者:K同学啊|接辅导、项目定制
本周主要研究DenseNet与ResNet的区别,并将其改进思路在YOLOv5网络进行应用,测试改进效果。
首先是用Tensorflow复现代码
class DenseLayer(Model):def __init__(self,bottleneck_size,growth_rate):super().__init__()self.filters=growth_rateself.bottleneck_size=bottleneck_sizeself.b1=BatchNormalization()self.a1=Activation('relu')self.c1=Conv2D(filters=self.bottleneck_size,kernel_size=(1,1),strides=1)self.b2=BatchNormalization()self.a2=Activation('relu')self.c2=Conv2D(filters=32,kernel_size=(3,3),strides=1,padding='same')def call(self,*x):x=tf.concat(x,2)x=self.b1(x)x=self.a1(x)x=self.c1(x)x=self.b2(x)x=self.a2(x)y=self.c2(x) return yclass DenseBlock(Model):def __init__(self,Dense_layers_num,growth_rate):#Dense_layers_num每个denseblock中的denselayer数,growthsuper().__init__()self.Dense_layers_num=Dense_layers_numself.Dense_layers=[]bottleneck_size=4*growth_ratefor i in range(Dense_layers_num):layer=DenseLayer(bottleneck_size,growth_rate)self.Dense_layers.append(layer)def call(self,input):x=[input]for layer in self.Dense_layers:output=layer(*x)x.append(output)y=tf.concat(x,2)return yclass Transition(Model):def __init__(self,filters):super().__init__()self.b=BatchNormalization()self.a=Activation('relu')self.c=Conv2D(filters=filters,kernel_size=(1,1),strides=1)self.p=AveragePooling2D(pool_size=(2,2),strides=2)def call(self,x):x=self.b(x)x=self.a(x)x=self.c(x)y=self.p(x)return y class DenseNet(Model):def __init__(self,block_list=[6,12,24,16],compression_rate=0.5,filters=64):super().__init__()growth_rate=32self.padding=ZeroPadding2D(((1,2),(1,2)))self.c1=Conv2D(filters=filters,kernel_size=(7,7),strides=2,padding='valid')self.b1=BatchNormalization()self.a1=Activation('relu')self.p1=MaxPooling2D(pool_size=(3,3),strides=2,padding='same')self.blocks=tf.keras.models.Sequential()input_channel=filtersfor i,layers_in_block in enumerate(block_list):if i<3 :self.blocks.add(DenseBlock(layers_in_block,growth_rate))block_out_channels=input_channel+layers_in_block*growth_rateself.blocks.add(Transition(filters=block_out_channels*0.5))if i==3:self.blocks.add(DenseBlock(Dense_layers_num=layers_in_block,growth_rate=growth_rate))self.p2=GlobalAveragePooling2D()self.d2=Dense(1000,activation='softmax') def call(self,x):x=self.padding(x)x=self.c1(x)x=self.b1(x)x=self.a1(x)x=self.p1(x)x=self.blocks(x)x=self.p2(x)y=self.d2(x)return y
model=DenseNet()
DenseNet网络大体由多个Dense模块组成,模块之间用Transition层连接。
Transition层包含一个1x1卷积层+一个池化层
Transition层作用详解:
DenseNet整体结构:
可以看到相对于ResNet,DenseNet每个Dense模块都是一种“全连接”的状态,不过仔细想一下,转化到网络结构中其实不是这样表示的,换句话说,代码里并不需要把各个层都向后连接到后面每一层。实际上只需要一种如下图的实现方式:(下图仅为简化结构图,DenseNet的模型中为了简化计算,将Dense模块内设计为BottleNeck结构)
右侧一路concat下去就实现了理论图上每层都向下连接到所有后面层的操作。
另外注意到,这里用到了ResNetv2的先BN+ReLU,再卷积的这种“预激活”策略。
下面进行在YOLOv5网络上的改进测试,由于上周进行了ResNetv2的预激活模块的该井测试,结果显示效果有所退化,所以此次改进不完全依照DenseNet,只借鉴其稠密连接的策略,而卷积模块Conv仍使用传统的先卷积再BN+ReLU的顺序。
实际的模型与上图有一定偏差,因为要涉及到concat试保证w,h尺寸一致的问题,具体backbone如下图:
下面是网络模型配置文件
# YOLOv5 🚀 by Ultralytics, GPL-3.0 license# Parameters
nc: 80 # number of classes
depth_multiple: 1.0 # model depth multiple
width_multiple: 1.0 # layer channel multiple
anchors:- [10,13, 16,30, 33,23] # P3/8 大物体- [30,61, 62,45, 59,119] # P4/16 中物体- [116,90, 156,198, 373,326] # P5/32 小物体# YOLOv5 v6.0 backbone
backbone:# [from, number, module, args] 【输入层号(-1表示上一层),本层有几个BottleNeck,本层类型,本层需要的参数】[[-1, 1, Conv, [32, 6, 2, 2]], # 0[-1, 1, nn.MaxPool2d, [2, 2]], # 1[0, 1, Conv, [32, 3, 2]], # 2[[1, 2], 1, Concat, [1]], # 3[-1, 3, C3, [64]], # 4[[3, 4], 1, Concat, [1]], # 5[-1, 1, Conv, [128, 3, 2]], # 6[5, 1, nn.MaxPool2d, [2, 2]], # 7[[6, 7], 1, Concat, [1]], # 8[-1, 6, C3, [256]], # 9[[8, 9], 1, Concat, [1]], # 10[-1, 1, Conv, [512, 3, 2]], # 11[10, 1, nn.MaxPool2d, [2, 2]], # 12[[11, 12], 1, Concat, [1]], # 13[-1, 9, C3, [1024]], # 14[-1, 1, Conv, [512, 1, 1]], # 15[[13, 14], 1, Concat, [1]], # 16[-1, 1, Conv, [1024, 3, 2]], # 17[-1, 3, C3, [1024]], # 18[-1, 1, SPPF, [1024, 5]], # 19]
#注:backbone和head中的bottleneck是两种结构
# YOLOv5 v6.0 head
head:[[-1, 1, Conv, [512, 1, 1]], # 20[-1, 1, nn.Upsample, [None, 2, 'nearest']], # 21[[-1, 15], 1, Concat, [1]], # 22[-1, 3, C3, [512, False]], # 23[-1, 1, Conv, [256, 1, 1]], # 24[-1, 1, nn.Upsample, [None, 2, 'nearest']], # 25[[-1, 9], 1, Concat, [1]], # 26[-1, 3, C3, [256, False]], # 27[-1, 1, Conv, [256, 3, 2]], # 28[[-1, 24], 1, Concat, [1]], # 29[-1, 3, C3, [512, False]], # 30[-1, 1, Conv, [512, 3, 2]], # 31[[-1, 20], 1, Concat, [1]], # 32[-1, 3, C3, [1024, False]], # 33[[27, 30, 33], 1, Detect, [nc, anchors]], # 34]
很遗憾,效果不如原版