【神经网络】(19) ConvNeXt 代码复现,网络解析,附Tensorflow完整代码
各位同學(xué)好,今天和大家分享一下如何使用 Tensorflow 構(gòu)建 ConvNeXt 卷積神經(jīng)網(wǎng)絡(luò)模型。
論文地址:https://arxiv.org/pdf/2201.03545.pdf
完整代碼在我的Gitee中:https://gitee.com/dgvv4/neural-network-model/tree/master/
21年Transformer頻頻跨界視覺領(lǐng)域,先是在圖像分類上被谷歌ViT突破,后來在目標(biāo)檢測和圖像分割又被微軟Swin Transformer拿下。隨著投身視覺Transformer研究的學(xué)者越來越多,三大任務(wù)榜單皆被Transformer或兩種架構(gòu)結(jié)合的模型占據(jù)頭部,這時(shí)ConvNeXt代表卷積神經(jīng)網(wǎng)絡(luò)站了出來。
1. ConvNeXt Block 模塊
1.1 基本結(jié)構(gòu)
(1)ConvNeXt 使用了分組卷積的思想,和MobileNetV1中的深度卷積(Depthwise Conv)相同。輸入特征圖有多少個(gè)通道數(shù),就有多少個(gè)卷積核,每個(gè)卷積核處理一個(gè)對應(yīng)的通道,每個(gè)卷積核生成一張?zhí)卣鲌D。將所有生產(chǎn)的特征圖在通道維度上堆疊,那么就有輸入特征圖的通道數(shù)和輸出特征圖的通道數(shù)相同。
(2)逆轉(zhuǎn)殘差結(jié)構(gòu),先1*1卷積升維,后1*1卷積降維。借鑒了MobileNetV2的逆轉(zhuǎn)殘差結(jié)構(gòu)。在較小的模型上準(zhǔn)確率由80.5%提升到了80.6%,在較大的模型上準(zhǔn)確率由81.9%提升到82.6%。
可參考我之前的文章:https://blog.csdn.net/dgvv4/article/details/123476899
(3)更少的標(biāo)準(zhǔn)化層,使用 Layer Normalization 代替 Batch Normalization。作者借鑒了Transformer 的結(jié)構(gòu),只保留深度卷積之后的標(biāo)準(zhǔn)化層,替換后準(zhǔn)確率得到小幅提高。
1.2 代碼展示
下面代碼中 gama 是對1*1降維卷積的輸出特征圖的數(shù)據(jù)進(jìn)行縮放。gama 是一個(gè)可學(xué)習(xí)的變量,在網(wǎng)絡(luò)訓(xùn)練過程中,反向傳播優(yōu)化gama的值。
gama 是一個(gè)一維向量,它的元素個(gè)數(shù)和輸出特征圖的通道數(shù)相同。向量中的每個(gè)元素處理一張對應(yīng)的特征圖,某張?zhí)卣鲌D的所有像素值依次和向量的某個(gè)元素相乘,達(dá)到縮放特征圖數(shù)據(jù)的目的。
定義可訓(xùn)練參數(shù) add_weight() 是?Layer類下面的一個(gè)方法,使用之前,先對Layer類實(shí)例化,layers.Layer()
#(2)ConvNeXt Block
def block(inputs, dropout_rate=0.2, layer_scale_init_value=1e-6):'''layer_scale_init_value 縮放比例gama的初始化值'''# 獲取輸入特征圖的通道數(shù)dim = inputs.shape[-1]# 殘差邊residual = inputs# 7*7深度卷積x = layers.DepthwiseConv2D(kernel_size=(7,7), strides=1, padding='same')(inputs)# 標(biāo)準(zhǔn)化x = layers.LayerNormalization()(x)# 1*1標(biāo)準(zhǔn)卷積上升通道數(shù)4倍x = layers.Conv2D(filters=dim*4, kernel_size=(1,1), strides=1, padding='same')(x)# GELU激活函數(shù)x = layers.Activation('gelu')(x)# 1*1標(biāo)準(zhǔn)卷積下降通道數(shù)x = layers.Conv2D(filters=dim, kernel_size=(1,1), strides=1, padding='same')(x)# 創(chuàng)建可學(xué)習(xí)的向量gama,該函數(shù)用于向某一層添加權(quán)重變量,類實(shí)例化layers.Layer()gama = layers.Layer().add_weight(shape=[dim], # 向量個(gè)數(shù)和輸出特征圖通道數(shù)量一致initializer=tf.initializers.Constant(layer_scale_init_value), # 權(quán)重初始化dtype=tf.float32, # 指定數(shù)據(jù)類型trainable=True) # 可訓(xùn)練參數(shù),可通過反向傳播調(diào)整權(quán)重# layer scale 對特征圖的每一個(gè)通道數(shù)據(jù)進(jìn)行縮放,縮放比例gamax = x * gama # [56,56,96]*[96]==>[56,56,96]# Dropout層隨機(jī)殺死神經(jīng)元x = layers.Dropout(rate=dropout_rate)(x)# 殘差連接輸入和輸出x = layers.add([x, residual])return x
2. 主干網(wǎng)絡(luò)
2.1 網(wǎng)絡(luò)結(jié)構(gòu)圖
2.2 設(shè)計(jì)方案
網(wǎng)絡(luò)架構(gòu)。如上圖,在ResNet50網(wǎng)絡(luò)中,res2到res5堆疊block的次數(shù)是(3, 4, 6, 3)比例大概是1:1:2:1,但在Swin Transformer中,比如Swin-T的比例是1:1:3:1,Swin-L的比例是1:1:9:1。很明顯,在Swin Transformer中堆疊block的占比更高。所以作者就將ResNet50中的堆疊次數(shù)由(3, 4, 6, 3)調(diào)整成(3, 3, 9, 3),和Swin-T擁有相似的FLOPs。
下采樣層的設(shè)計(jì)。ResNet網(wǎng)絡(luò)的下采樣都是通過將主分支上3x3的卷積層步距設(shè)置成2,殘差邊上1x1的卷積層的步長設(shè)置成2。但在Swin Transformer中是通過一個(gè)單獨(dú)的Patch Merging實(shí)現(xiàn)的。作者借鑒Swin-T為ConvNext網(wǎng)絡(luò)單獨(dú)使用了一個(gè)下采樣層,由一個(gè)Laryer Normalization 加上一個(gè) kernel_size=2 且 strides=2 的卷積層構(gòu)成。
2.3 完整代碼展示
ConvNeXt使用的全部都是現(xiàn)有的結(jié)構(gòu)和方法,沒有任何結(jié)構(gòu)或者方法的創(chuàng)新。而且代碼也非常的精簡,100多行代碼就能搭建完成
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import Model, layers#(1)輸入圖像經(jīng)過的第一個(gè)卷積塊
def pre_Conv(inputs, out_channel):# 4*4卷積+標(biāo)準(zhǔn)化x = layers.Conv2D(filters=out_channel, # 輸出特征圖的通道數(shù)kernel_size=(4,4),strides=4, # 下采樣padding='same')(inputs)x = layers.LayerNormalization()(x)return x#(2)ConvNeXt Block
def block(inputs, dropout_rate=0.2, layer_scale_init_value=1e-6):'''layer_scale_init_value 縮放比例gama的初始化值'''# 獲取輸入特征圖的通道數(shù)dim = inputs.shape[-1]# 殘差邊residual = inputs# 7*7深度卷積x = layers.DepthwiseConv2D(kernel_size=(7,7), strides=1, padding='same')(inputs)# 標(biāo)準(zhǔn)化x = layers.LayerNormalization()(x)# 1*1標(biāo)準(zhǔn)卷積上升通道數(shù)4倍x = layers.Conv2D(filters=dim*4, kernel_size=(1,1), strides=1, padding='same')(x)# GELU激活函數(shù)x = layers.Activation('gelu')(x)# 1*1標(biāo)準(zhǔn)卷積下降通道數(shù)x = layers.Conv2D(filters=dim, kernel_size=(1,1), strides=1, padding='same')(x)# 創(chuàng)建可學(xué)習(xí)的向量gama,該函數(shù)用于向某一層添加權(quán)重變量,類實(shí)例化layers.Layer()gama = layers.Layer().add_weight(shape=[dim], # 向量個(gè)數(shù)和輸出特征圖通道數(shù)量一致initializer=tf.initializers.Constant(layer_scale_init_value), # 權(quán)重初始化dtype=tf.float32, # 指定數(shù)據(jù)類型trainable=True) # 可訓(xùn)練參數(shù),可通過反向傳播調(diào)整權(quán)重# layer scale 對特征圖的每一個(gè)通道數(shù)據(jù)進(jìn)行縮放,縮放比例gamax = x * gama # [56,56,96]*[96]==>[56,56,96]# Dropout層隨機(jī)殺死神經(jīng)元x = layers.Dropout(rate=dropout_rate)(x)# 殘差連接輸入和輸出x = layers.add([x, residual])return x#(3)下采樣層
def downsampling(inputs, out_channel):# 標(biāo)準(zhǔn)化+2*2卷積下采樣x = layers.LayerNormalization()(inputs)x = layers.Conv2D(filters=out_channel, # 輸出通道數(shù)個(gè)數(shù)kernel_size=(2,2),strides=2, # 下采樣padding='same')(x)return x#(4)卷積塊,一個(gè)下采樣層+多個(gè)block卷積層
def stage(x, num, out_channel, downsampe=True):'''num:重復(fù)執(zhí)行多少次block ; out_channel代表下采樣層輸出通道數(shù)downsampe:判斷是否執(zhí)行下采樣層'''if downsampe is True:x = downsampling(x, out_channel)# 重復(fù)執(zhí)行num次block,每次輸出的通道數(shù)都相同for _ in range(num):x = block(x)return x#(5)主干網(wǎng)絡(luò)
def convnext(input_shape, classes): # 輸入圖像shape和分類類別數(shù)# 構(gòu)造輸入層inputs = keras.Input(shape=input_shape)# [224,224,3]==>[56,56,96]x = pre_Conv(inputs, out_channel=96)# [56,56,96]==>[56,56,96]x = stage(x, num=3, out_channel=96, downsampe=False)# [56,56,96]==>[28,28,192]x = stage(x, num=3, out_channel=192, downsampe=True)# [28,28,192]==>[14,14,384]x = stage(x, num=9, out_channel=384, downsampe=True)# [14,14,384]==>[7,7,768]x = stage(x, num=3, out_channel=768, downsampe=True)# [7,7,768]==>[None,768]x = layers.GlobalAveragePooling2D()(x)x = layers.LayerNormalization()(x)# [None,768]==>[None,classes]logits = layers.Dense(classes)(x) # 不經(jīng)過softmax# 構(gòu)建網(wǎng)絡(luò)model = Model(inputs, logits)return model#(6)接收網(wǎng)絡(luò)模型
if __name__ == '__main__':# 構(gòu)造網(wǎng)絡(luò),傳入輸入圖像的shape,和最終輸出的分類類別數(shù)model = convnext(input_shape=[224,224,3], classes=1000)model.summary() # 查看網(wǎng)絡(luò)結(jié)構(gòu)
網(wǎng)絡(luò)參數(shù)如下
==================================================================================================
Total params: 28,582,504
Trainable params: 28,582,504
Non-trainable params: 0
__________________________________________________________________________________________________
3. 網(wǎng)絡(luò)模型圖
感謝?太陽花的小綠豆?博主的模型圖
總結(jié)
以上是生活随笔為你收集整理的【神经网络】(19) ConvNeXt 代码复现,网络解析,附Tensorflow完整代码的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【目标检测】(6) YOLOV2 目标检
- 下一篇: 【YOLOV4】(7) 特征提取网络代码