深度学习笔记30_DenseNet算法实战与解析
- 🍨 本文为🔗365天深度学习训练营 中的学习记录博客
- 🍖 原作者:K同学啊 | 接辅导、项目定制
一、我的环境
1.语言环境:Python 3.9
2.编译器:Pycharm
3.深度学习环境:pytorch
二、GPU设置
gpus = tf.config.list_physical_devices("GPU")
print(gpus)
三、数据导入
import pathlib
import warnings
import matplotlib.pyplot as plt
import tensorflow as tf
from keras.layers import Activation
from keras.layers import Dense, Conv2D, MaxPooling2D, ZeroPadding2D, AveragePooling2D, BatchNormalization, \
GlobalAveragePooling2D
from keras.models import Model
warnings.filterwarnings('ignore') # 忽略一些warning内容,无需打印
# 支持中文
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号
data_dir = "data/bird_photos"
data_dir = pathlib.Path(data_dir)
image_count = len(list(data_dir.glob('*/*')))
data_paths = list(data_dir.glob("*"))
classnames = [str(path).split("\\")[2] for path in data_paths]
print(classnames)
num_classes = len(classnames)
print(num_classes)
#['Bananaquit', 'Black Skimmer', 'Black Throated Bushtiti', 'Cockatoo']
四、加载数据
# 加载数据
batch_size = 32
img_height = 224
img_width = 224
"""
关于image_dataset_from_directory()的详细介绍可以参考文章:https://mtyjkh.blog.csdn.net/article/details/117018789
"""
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
plt.figure(figsize=(10, 5)) # 图形的宽为10高为5
plt.suptitle("DataBase")
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")
# 显示第一张
plt.imshow(images[1].numpy().astype("uint8"))
plt.show()
再次检查数据
#数据预处理-再次检查数据
# Image_batch是形状的张量(16, 336, 336, 3)。这是一批形状336x336x3的16张图片(最后一维指的是彩色通道RGB)。
# Label_batch是形状(16,)的张量,这些标签对应16张图片
for image_batch, labels_batch in train_ds:
print(image_batch.shape)
print(labels_batch.shape)
break
数据预处理
AUTOTUNE = tf.data.AUTOTUNE
train_ds = train_ds.cache().shuffle(1000).prefetch(buffer_size=AUTOTUNE)
val_ds = val_ds.cache().prefetch(buffer_size=AUTOTUNE)
六、构建ResNet50V2模型
class DenseLayer(Model):
def __init__(self, bottleneck_size, growth_rate):
super().__init__()
self.filters = growth_rate
self.bottleneck_size = bottleneck_size
self.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 y
# denseblock
class DenseBlock(Model):
def __init__(self, Dense_layers_num, growth_rate): # Dense_layers_num每个denseblock中的denselayer数,growth
super().__init__()
self.Dense_layers_num = Dense_layers_num
self.Dense_layers = []
bottleneck_size = 4 * growth_rate
for 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 y
# transition part
class 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
# DenseNet
class DenseNet(Model):
def __init__(self, block_list=[6, 12, 24, 16], compression_rate=0.5, filters=64):
super().__init__()
growth_rate = 32
self.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 = filters
for 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_rate
self.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算法实战与解析:
DenseNet是CVPR2017年的Best Paper,它脱离了加深网络层数(ResNet)和加宽网络结构(Inception)来提升网络性能的定式思维,从特征的角度考虑,通过特征重用和旁路(Bypass)设置,既大幅度减少了网络的参数量,又在一定程度上缓解了gradient vanishing问题的产生.结合信息流和特征复用的假设,DenseNet当之无愧成为2017年计算机视觉顶会的年度最佳论文。
众所周知,CNN已经成为了深度学习方向最主要的网络结构之一。从一开始的只有五层结构的LeNet, 到后来拥有19层结构的VGG,再到首次跨越100层网络的HighwayNetworks与ResNet, 网络层数的加深成为CNN发展的主要方向之一。
但是随着CNN网络层数的不断增加开始出现梯度消失和模型退化(50层的网络不如20层的网络),批量归一化(BatchNormalization)的广泛使用在一定程度上缓解了梯度消失的问题,而ResNet和Highway Networks通过构造恒等映射设置旁路,进一步减少了梯度消失和模型退化的产生。Fractal Nets通过将不同深度的网络并行化,在获得了深度的同时保证了梯度的传播,随机深度网络通过对网络中一些层进行失活,既证明了ResNet深度的冗余性,又缓解了上述问题的产生。虽然这些不同的网络框架通过不同的实现加深的网络层数,但是他们都包含了相同的核心思想:将feature map进行跨网络层的连接。
DenseNet与ResNet
ResNet
ResNet(Deep Residual Network深度残差网络):通过建立前面层与后面层之间的“短路连接”,这有助于训练过程中梯度的反向传播,从而能训练出更深的CNN网络。
DenseNet
DenseNet采用密集连接机制,即互相连接所有的层,每个层都会与前面所有层在channel维度上连接(concat)在一起,实现特征重用,作为下一层的输入。这样,不但减缓了梯度消失的现象,也使其可以在参数与计算量更少的情况下实现比ResNet更优的性能。
DenseNet网络架构
DenseNet的密集连接方式需要特征图大小保持一致 。 所 以 DenseNet 网络中使用DenseBlock+Transition的结构。
DenseBlock是包含很多层的模块,每个层的特征图大小相同,层与层之间采用密集连接方式。Transition模块是连接两个相邻的DenseBlock,并且通过Pooling使特征图大小降低。
DenseBlock
在DenseBlock中,各个层的特征图大小一致,可以在channel维度上连接。DenseBlock中的非线性组合函数采用的是BN+ReLU+3x3Conv的结构。
这里有一个参数 k kk, 称为增长率, 指的是每一层的额外通道数, , 假如输入层特征图的channel为K0+(L+1)K , 因为每一层都接受前面所有层的特征图,即特征传递方式是直接将前面所有层的特征concat后传到下一层,一般情况下使用较小的K(一般为12),要注意这个K的实际含义就是这层新提取出的特征。
Dense Block采用了激活函数在前、卷积层在后的顺序,即BN-ReLU-Conv的顺序,这种方式也被称为pre-activation。通常的模型relu等激活函数处于卷积conv、批归一化batchnorm之后,即Conv-BN-ReLU,也被称为post-activation。作者证明,如果采用post-activation设计,性能会变差。
BottleNeck层
由于后面层的输入会非常大,DenseBlock内部可以采用Bottleneck层(瓶颈层)来减少计算量,主要是原有的结构中增加1x1 Conv,即BN+ReLU+1x1 Conv+BN+ReLU+3x3 Conv称为DenseNet-B结构。其中1x1 Conv得到4k个特征图它起到的作用是降低特征数量,从而提升计算效率。
每一个Bottleneck输入输出的特征通道数是相同的。这里1×1卷积的作用是固定输出通道数,达到降维的作用,1×1卷积输出的通道数通常是GrowthRate的4倍。当几十个Bottleneck相连接时,concat后的通道数会增加到上千,如果不增加1×1的卷积来降维,后续3×3卷积所需的参数量会急剧增加。
Transition层
Transition层它主要是连接两个相邻的DenseBlock,并且降低特征图大小。Transition层包括一个1x1的卷积和2x2的AvgPooling,结构为BN+ReLU+1x1 Conv+2x2 AvgPooling。
DenseNet的不足在于由于需要进行多次Concatnate操作,数据需要被复制多次,显存容易增加得很快,需要一定的显存优化技术。另外,DenseNet是一种更为特殊的网络,ResNet则相对一般化一些,因此ResNet的应用范围更广泛。
参考:DenseNet
原文地址:https://blog.csdn.net/L1073203482/article/details/145168135
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!