文章目錄
- 前言
- 一、什么是DenseNet?
- 二、keras 復現
- 1.Conv Block
- 2.Dense Block
- 3.Transition Block
- 總結
前言
據說DenseNet 是優于ResNet的網路結構,有著引數少,性能優越的特點并且實作思路很簡單,
表面上好像是跨連接,實際上是concatenate 特征圖,
一、什么是DenseNet?
先來看一張圖
了解到這些之后,相信聰明的小伙伴已經意識到這個一個問題,想如此堆疊特征層必然會引來特征層通道數過多的問題,同時特征層的大小要和輸入時的一致,這樣勢必會倒是網路的引數過多,于是作者引入了transition_block,如下圖紅色方框中所示:

transition_block 包含一個1x1的卷積,用來調整通道數,和一個 2x2 的池化來縮小特征層的大小,這樣每個Dense Block中進行計算時 特征保持不變,直到進入下一個Dense Block, 這就是Dense Block的實作思路了,
二、keras 復現
1.Conv Block
該部分是用于Dense Block 中進行卷積運算的塊,每個引數的含義 已經寫在代碼的注釋中,
代碼如下(示例):
def conv_block(x,stage,branch,nb_filter,dropout_rate = None):
# 引數
# x: input tensor #輸入張量
# stage: index for dense block #第幾個dense_block
# branch: layer index within each dense block #dense_block 中各層的索引
# nb_filter: number of filters #卷積核的個數
# dropout_rate: dropout rate #dropout 引數
eps = 1.1e-5
bn_axis =3
conv_name_base = 'conv' + str(stage) + '_' + str(branch)
relu_name_base = 'relu' + str(stage) + '_' + str(branch)
inter_channels = 4 * nb_filter
# 1x1 的卷積用于調整輸入tensor 的通道數
x = BatchNormalization(epsilon=eps,axis=bn_axis,name=conv_name_base+'_x1_bn')(x)
x = Activation('relu',name=relu_name_base+'_x1')(x)
x = Conv2D(inter_channels,1,1,name=conv_name_base+'_x1',use_bias=False)(x)
print("x in conv: ",x.shape)
if dropout_rate:
x = Dropout(dropout_rate)(x)
# 3x3 的卷積
x = BatchNormalization(epsilon=eps,axis=bn_axis,name=conv_name_base+'_x2_bn')(x)
x = Activation('relu',name=relu_name_base+'_x2')(x)
x = ZeroPadding2D((1, 1), name=conv_name_base+'_x2_zeropadding')(x)
x = Conv2D(nb_filter, 3, 1, name=conv_name_base+'_x2', use_bias=False)(x)
print("x in conv2: ",x.shape)
if dropout_rate:
x = Dropout(dropout_rate)(x)
return x
2.Dense Block
通過定義好的conv_block 去宣告 Dense Block,
def dense_block(x, stage, nb_layers, nb_filter, growth_rate, dropout_rate=None,grow_nb_filters=True):
# Build a dense_block where the output of each conv_block is fed to subsequent ones
# # Arguments
# x: input tensor
# stage: index for dense block
# nb_layers: the number of layers of conv_block to append to the model. 添加卷積塊的個數
# nb_filter: number of filters 卷積核的個數
# growth_rate: growth rate 增長率
# dropout_rate: dropout rate dropout
# grow_nb_filters: flag to decide to allow number of filters to grow 是否允許卷積核的個數變多
concat_feat = x
for i in range(nb_layers):
branch = i + 1
x = conv_block(concat_feat,stage,branch,growth_rate,dropout_rate)
concat_feat = layers.Concatenate(axis=3,name='concat_'+str(stage)+'_'+str(branch))([concat_feat,x])
if grow_nb_filters:
nb_filter += growth_rate
return concat_feat, nb_filter
growth rate 用于判斷下一個 Dense Block 中卷積 的初始個數 ,作者的設定,為的是能夠控制網路深層的卷積核個數,
3.Transition Block
用于縮小特征層,
def transition_block(x,stage,nb_filter,compression=1.0,dropout_rate = None):
# dense block 中 concat 特征層太多了 通過這個來縮小一下
# x: input tensor #輸入張量
# stage: index for dense block #第幾個trainsition_block
# nb_filter: number of filters #卷積核的個數
# compression: calculated as 1 - reduction. Reduces the number of feature maps in the transition block. # 減少特征圖的比例
# dropout_rate: dropout rate #dropout 引數
eps = 1.1e-5
bn_axis =3
conv_name_base = 'conv' + str(stage) + '_blk'
relu_name_base = 'relu' + str(stage) + '_blk'
pool_name_base = 'pool' + str(stage)
x = BatchNormalization(epsilon=eps, axis=bn_axis, name=conv_name_base+'_bn')(x)
x = Activation('relu', name=relu_name_base)(x)
x = Conv2D(int(nb_filter * compression), 1, 1, name=conv_name_base, use_bias=False)(x)
if dropout_rate:
x = Dropout(dropout_rate)(x)
print(x.shape)
x = AveragePooling2D((2, 2), strides=(2, 2), name=pool_name_base)(x)
return x
以DenseNet-121 為例(169 和201 的引數已注釋在代碼中了):
完整代碼:
from keras import layers
from keras.models import Model
from keras.layers import Input, merge, ZeroPadding2D
from keras.layers import Dense, Dropout, Activation
from keras.layers import Conv2D
from keras.layers import AveragePooling2D, GlobalAveragePooling2D, MaxPooling2D
from keras.layers import BatchNormalization
import keras.backend as K
from keras.layers.convolutional import Convolution2D
import tensorflow as tf
def conv_block(x,stage,branch,nb_filter,dropout_rate = None):
# 引數
# x: input tensor #輸入張量
# stage: index for dense block #第幾個dense_block
# branch: layer index within each dense block #dense_block 中各層的索引
# nb_filter: number of filters #卷積核的個數
# dropout_rate: dropout rate #dropout 引數
eps = 1.1e-5
bn_axis =3
conv_name_base = 'conv' + str(stage) + '_' + str(branch)
relu_name_base = 'relu' + str(stage) + '_' + str(branch)
inter_channels = 4 * nb_filter
# 1x1 的卷積用于調整輸入tensor 的通道數
x = BatchNormalization(epsilon=eps,axis=bn_axis,name=conv_name_base+'_x1_bn')(x)
x = Activation('relu',name=relu_name_base+'_x1')(x)
x = Conv2D(inter_channels,1,1,name=conv_name_base+'_x1',use_bias=False)(x)
print("x in conv: ",x.shape)
if dropout_rate:
x = Dropout(dropout_rate)(x)
# 3x3 的卷積
x = BatchNormalization(epsilon=eps,axis=bn_axis,name=conv_name_base+'_x2_bn')(x)
x = Activation('relu',name=relu_name_base+'_x2')(x)
x = ZeroPadding2D((1, 1), name=conv_name_base+'_x2_zeropadding')(x)
x = Conv2D(nb_filter, 3, 1, name=conv_name_base+'_x2', use_bias=False)(x)
print("x in conv2: ",x.shape)
if dropout_rate:
x = Dropout(dropout_rate)(x)
return x
def transition_block(x,stage,nb_filter,compression=1.0,dropout_rate = None):
# dense block 中 concat 特征層太多了 通過這個來縮小一下
# x: input tensor #輸入張量
# stage: index for dense block #第幾個trainsition_block
# nb_filter: number of filters #卷積核的個數
# compression: calculated as 1 - reduction. Reduces the number of feature maps in the transition block. # 減少特征圖的比例
# dropout_rate: dropout rate #dropout 引數
eps = 1.1e-5
bn_axis =3
conv_name_base = 'conv' + str(stage) + '_blk'
relu_name_base = 'relu' + str(stage) + '_blk'
pool_name_base = 'pool' + str(stage)
x = BatchNormalization(epsilon=eps, axis=bn_axis, name=conv_name_base+'_bn')(x)
x = Activation('relu', name=relu_name_base)(x)
x = Conv2D(int(nb_filter * compression), 1, 1, name=conv_name_base, use_bias=False)(x)
if dropout_rate:
x = Dropout(dropout_rate)(x)
print(x.shape)
x = AveragePooling2D((2, 2), strides=(2, 2), name=pool_name_base)(x)
return x
def dense_block(x, stage, nb_layers, nb_filter, growth_rate, dropout_rate=None,grow_nb_filters=True):
# Build a dense_block where the output of each conv_block is fed to subsequent ones
# # Arguments
# x: input tensor
# stage: index for dense block
# nb_layers: the number of layers of conv_block to append to the model. 添加卷積塊的個數
# nb_filter: number of filters 卷積核的個數
# growth_rate: growth rate 增長率
# dropout_rate: dropout rate dropout
# grow_nb_filters: flag to decide to allow number of filters to grow 是否允許卷積核的個數變多
concat_feat = x
for i in range(nb_layers):
branch = i + 1
x = conv_block(concat_feat,stage,branch,growth_rate,dropout_rate)
concat_feat = layers.Concatenate(axis=3,name='concat_'+str(stage)+'_'+str(branch))([concat_feat,x])
if grow_nb_filters:
nb_filter += growth_rate
return concat_feat, nb_filter
def DenseNet(nb_dense_block=4, growth_rate=32, nb_filter=64, reduction=0.0, dropout_rate=0.0, weight_decay=1e-4, classes=1000, weights_path=None):
'''Instantiate the DenseNet 121 architecture,
# Arguments
nb_dense_block: number of dense blocks to add to end
growth_rate: number of filters to add per dense block
nb_filter: initial number of filters
reduction: reduction factor of transition blocks.
dropout_rate: dropout rate
weight_decay: weight decay factor
classes: optional number of classes to classify images
weights_path: path to pre-trained weights
# Returns
A Keras model instance.
'''
eps = 1.1e-5
# compute compression factor
compression = 1.0 - reduction
# Handle Dimension Ordering for different backends
global concat_axis
concat_axis =3
nb_filter = 64
nb_layers = [6,12,24,16] # For DenseNet-121
#nb_layers = [6, 12, 32, 32] # For DenseNet-169
#nb_layers = [6, 12, 48, 32] # For DenseNet-201
img_input = Input(shape=(224, 224, 3), name='data')
# Initial convolution
x = ZeroPadding2D((3, 3), name='conv1_zeropadding')(img_input)
x = Conv2D(nb_filter, 3, 1, name='conv1', use_bias=False)(x)
x = BatchNormalization(epsilon=eps, axis=concat_axis, name='conv1_bn')(x)
x = Activation('relu', name='relu1')(x)
x = ZeroPadding2D((1, 1), name='pool1_zeropadding')(x)
x = MaxPooling2D((3, 3), strides=(2, 2), name='pool1')(x)
print("first",x.shape)
# Add dense blocks
for block_idx in range(nb_dense_block - 1):
stage = block_idx+2
x, nb_filter = dense_block(x, stage, nb_layers[block_idx], nb_filter, growth_rate, dropout_rate=dropout_rate)
# Add transition_block
x = transition_block(x, stage, nb_filter, compression=compression, dropout_rate=dropout_rate)
nb_filter = int(nb_filter * compression)
final_stage = stage + 1
x, nb_filter = dense_block(x, final_stage, nb_layers[-1], nb_filter, growth_rate, dropout_rate=dropout_rate)
x = BatchNormalization(epsilon=eps, axis=concat_axis, name='conv'+str(final_stage)+'_blk_bn')(x)
x = Activation('relu', name='relu'+str(final_stage)+'_blk')(x)
x = GlobalAveragePooling2D(name='pool'+str(final_stage))(x)
x = Dense(classes, name='fc6')(x)
x = Activation('softmax', name='prob')(x)
model = Model(img_input, x, name='densenet')
if weights_path is not None:
model.load_weights(weights_path)
return model
if __name__ == '__main__':
img = tf.random.uniform([1,3, 256, 256])
x = transition_block(img,1,32)
print(x.shape)
model = DenseNet()
model.summary()
總結
DenseNet 這個技巧用來改 其他網路結構應該還不錯,把它復現一下,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/335330.html
標籤:其他
上一篇:OpenCV 錯誤:無法打開攝像頭(打開攝像頭卡機)
下一篇:cNN入門教程
