みなさん、こんにちは。今日は、注意メカニズムと 深さ方向に分離可能な畳み込みを使用して、YOLOV4のPANet機能ピラミッドを最適化する方法を紹介します。このブログを読む前に、以下を読むことをお勧めします。
YOLOV4バックボーンネットワーク:https ://blog.csdn.net/dgvv4/article/details/123818580
混合ドメイン注意メカニズム:https ://blog.csdn.net/dgvv4/article/details/123888724
軽量ネットワーク:https ://blog.csdn.net/dgvv4/article/details/123476899
1.PANetの原則
さまざまな深さでネットワークによって抽出された機能マップには、機能に焦点を合わせる傾向が異なります。高レベルの特徴マップは、形状やサイズなどのオブジェクトの全体的な特性にさらに注意を払い、低レベルの特徴マップは、テクスチャパターンなどのオブジェクトの詳細な特徴に注意を払います。低レベルの特徴マップは、検出されるオブジェクトをより適切に見つけることができます。
ニューラルネットワークの順方向計算パスが長すぎるため、通常は数十のレイヤーがありますが、畳み込みごとに特徴情報が希釈されるため、特徴情報が下から上に流れるのに役立ちません。
したがって、PANetは、ボトムアップパスアグリゲーションモジュールPANをトップダウンFPNネットワーク構造に追加します。これにより、トップダウンパスを減らしながら、低レベルの特徴マップの情報を高レベルの特徴マップに送信します。高レベルの特徴マップから低レベルの特徴マップへの情報の流れを通過する必要がある畳み込み層。
2.CBAMアテンションメカニズム
CBAMの注意メカニズムについては、前回の記事で詳しく説明しました。ご不明な点がございましたら、上記のリンクに対応するブログ投稿をご覧ください。ここに簡単なレビューがあります。
CBAMモジュールは、畳み込み層出力の結果を入力特徴マップとして使用し、最初にチャネル注意モジュールを通過して重み付けされた結果を取得し、次に空間注意モジュールを通過してから、前のステップでチャネル注意モジュールを処理します。次に、中間特徴マップに重みが付けられ、最後に注意分布の重みが入力特徴マップと乗算されて、適応特徴の最適化が実現されます。
与えられた入力画像に対して、CBAMモジュールのチャネル注意部分は「何」が意味するかに注意を払いますが、空間注意部分は「どこ」がより有益な部分にもっと注意を払います。特徴抽出作業を実行するネットワーク。
コード表示:
#(1)CBAM注意力机制
# 通道注意力
def channel_attenstion(inputs, ratio=0.25):
'''ratio代表第一个全连接层下降通道数的倍数'''
channel = inputs.shape[-1] # 获取输入特征图的通道数
# 分别对输出特征图进行全局最大池化和全局平均池化
# [h,w,c]==>[None,c]
x_max = layers.GlobalMaxPooling2D()(inputs)
x_avg = layers.GlobalAveragePooling2D()(inputs)
# [None,c]==>[1,1,c]
x_max = layers.Reshape([1,1,-1])(x_max) # -1代表自动寻找通道维度的大小
x_avg = layers.Reshape([1,1,-1])(x_avg) # 也可以用变量channel代替-1
# 第一个全连接层通道数下降1/4, [1,1,c]==>[1,1,c//4]
x_max = layers.Dense(channel*ratio)(x_max)
x_avg = layers.Dense(channel*ratio)(x_avg)
# relu激活函数
x_max = layers.Activation('relu')(x_max)
x_avg = layers.Activation('relu')(x_avg)
# 第二个全连接层上升通道数, [1,1,c//4]==>[1,1,c]
x_max = layers.Dense(channel)(x_max)
x_avg = layers.Dense(channel)(x_avg)
# 结果在相叠加 [1,1,c]+[1,1,c]==>[1,1,c]
x = layers.Add()([x_max, x_avg])
# 经过sigmoid归一化权重
x = tf.nn.sigmoid(x)
# 输入特征图和权重向量相乘,给每个通道赋予权重
x = layers.Multiply()([inputs, x]) # [h,w,c]*[1,1,c]==>[h,w,c]
return x
# 空间注意力机制
def spatial_attention(inputs):
# 在通道维度上做最大池化和平均池化[b,h,w,c]==>[b,h,w,1]
# keepdims=Fale那么[b,h,w,c]==>[b,h,w]
x_max = tf.reduce_max(inputs, axis=3, keepdims=True) # 在通道维度求最大值
x_avg = tf.reduce_mean(inputs, axis=3, keepdims=True) # axis也可以为-1
# 在通道维度上堆叠[b,h,w,2]
x = layers.concatenate([x_max, x_avg])
# 1*1卷积调整通道[b,h,w,1]
x = layers.Conv2D(filters=1, kernel_size=(1,1), strides=1, padding='same')(x)
# sigmoid函数权重归一化
x = tf.nn.sigmoid(x)
# 输入特征图和权重相乘
x = layers.Multiply()([inputs, x])
return x
# CBAM注意力
def CBAM_attention(inputs):
# 先经过通道注意力再经过空间注意力
x = channel_attenstion(inputs)
x = spatial_attention(x)
return x
3.深さ方向に分離可能な畳み込み
通常の畳み込みは、すべてのチャネル、入力特徴マップのチャネル数を処理する畳み込みカーネルであり、畳み込みカーネルには複数のチャネルがあり、1つの畳み込みカーネルが特徴マップを生成します。
深さ分離可能な畳み込みは、深さ畳み込み+ポイントごとの畳み込みとして理解できます。
深さ方向の畳み込みは、長さと幅の方向の空間情報のみを扱います。点ごとの畳み込みは、クロスチャネル方向の情報のみを扱います。パラメータ数を大幅に削減し、計算効率を向上させることができます。
深さのたたみ込み:たたみ込みカーネルは1つのチャネルのみを処理します。つまり、各たたみ込みカーネルは、対応する独自のチャネルのみを処理します。入力特徴マップのチャネルと同じ数の畳み込みカーネルがあります。各畳み込みカーネルによって処理される機能マップは、一緒にスタックされます。入力フィーチャマップと出力フィーチャマップのチャネル数は同じです。
長さ方向と幅方向の情報のみを処理すると、クロスチャネル情報が失われるため、クロスチャネル情報を補足するには、ポイントごとの畳み込みが必要です。
ポイントバイポイント畳み込み: 1x1畳み込みを使用して、クロスチャネル次元を処理します。1x1畳み込みカーネルがいくつの機能マップを生成するか。
コード表示:
#(2)# 深度可分离卷积+BN+relu
def conv_block(inputs, filters, kernel_size, strides):
# 深度卷积
x = layers.DepthwiseConv2D(kernel_size, strides,
padding='same', use_bias=False)(inputs) # 有BN不要偏置
# 逐点卷积
x = layers.Conv2D(filters, kernel_size=(1,1), strides=1,
padding='same', use_bias=False)(x)
x = layers.BatchNormalization()(x)
x = layers.Activation('relu')(x)
return x
#(3)5次卷积操作提取特征减少参数量
def five_conv(x, filters):
x = conv_block(x, filters, (1,1), strides=1)
x = conv_block(x, filters*2, (3,3), strides=1)
x = conv_block(x, filters, (1,1), strides=1)
x = conv_block(x, filters*2, (3,3), strides=1)
x = conv_block(x, filters, (1,1), strides=1)
return x
4.改善されたPANetピラミッド
PANetピラミッドは、CBAMアテンションメカニズムと深さ方向に分離可能な畳み込みで最適化されています。
(1)すべての標準畳み込みブロックを深さ方向に分離可能な畳み込みブロックに置き換えます。
(2)アップサンプルのアップサンプリングとダウンサンプルのダウンサンプリングの後、出力特徴マップでCBAMアテンションメカニズムを使用します。
深さ方向に分離可能な畳み込みは、モデルの計算量とパラメーター量を大幅に減らすことができます。
CBAMは軽量の汎用モジュールであるため、このモジュールのオーバーヘッドを無視して、任意のCNNアーキテクチャにシームレスに統合でき、基礎となる畳み込みニューラルネットワークと一緒にエンドツーエンドでトレーニングできます。実験によると、CBAMモジュールはさまざまなモデルで特定の役割を果たすことができ、分類と検出のパフォーマンスの点でモデルが改善されています。
バックボーンネットワークコードは次のとおりです。p5はSPPモジュールに続く3番目の有効なフィーチャレイヤーであり、feat1とfeat2はそれぞれネットワークによって出力される1番目と2番目の有効なフィーチャレイヤーです。
#(4)主干网络
def panet(feat1, feat2, p5):
# 对网络的有效输出特征层采用CBAM注意力
feat1 = CBAM_attention(feat1)
feat2 = CBAM_attention(feat2)
p5 = CBAM_attention(p5)
#(1)
# 对spp结构的输出进行卷积和上采样
# [13,13,512]==>[13,13,256]==>[26,26,256]
p5_upsample = conv_block(p5, filters=256, kernel_size=(1,1), strides=1)
p5_upsample = layers.UpSampling2D(size=(2,2))(p5_upsample)
# 对feat2特征层卷积后再与p5_upsample堆叠
# [26,26,512]==>[26,26,256]==>[26,26,512]
p4 = conv_block(feat2, filters=256, kernel_size=(1,1), strides=1)
# 上采样后使用注意力机制
p4 = CBAM_attention(p4)
# 通道维度上堆叠
p4 = layers.concatenate([p4, p5_upsample])
# 堆叠后进行5次卷积[26,26,512]==>[26,26,256]
p4 = five_conv(p4, filters=256)
#(2)
# 对p4卷积上采样
# [26,26,256]==>[26,26,512]==>[52,52,512]
p4_upsample = conv_block(p4, filters=128, kernel_size=(1,1), strides=1)
p4_upsample = layers.UpSampling2D(size=(2,2))(p4_upsample)
# feat1层卷积后与p4_upsample堆叠
# [52,52,256]==>[52,52,128]==>[52,52,256]
p3 = conv_block(feat1, filters=128, kernel_size=(1,1), strides=1)
# 上采样后使用注意力机制
p3 = CBAM_attention(p3)
# 通道维度上堆叠
p3 = layers.concatenate([p3, p4_upsample])
# 堆叠后进行5次卷积[52,52,256]==>[52,52,128]
p3 = five_conv(p3, filters=128)
# 存放第一个特征层的输出
p3_output = p3
#(3)
# p3卷积下采样和p4堆叠
# [52,52,128]==>[26,26,256]==>[26,26,512]
p3_downsample = conv_block(p3, filters=256, kernel_size=(3,3), strides=2)
# 下采样后使用注意力机制
p3_downsample = CBAM_attention(p3_downsample)
# 通道维度上堆叠
p4 = layers.concatenate([p3_downsample, p4])
# 堆叠后的结果进行5次卷积[26,26,512]==>[26,26,256]
p4 = five_conv(p4, filters=256)
# 存放第二个有效特征层的输出
p4_output = p4
#(4)
# p4卷积下采样和p5堆叠
# [26,26,256]==>[13,13,512]==>[13,13,1024]
p4_downsample = conv_block(p4, filters=512, kernel_size=(3,3), strides=2)
# 下采样后使用注意力机制
p4_downsample = CBAM_attention(p4_downsample)
# 通道维度上堆叠
p5 = layers.concatenate([p4_downsample, p5])
# 堆叠后进行5次卷积[13,13,1024]==>[13,13,512]
p5 = five_conv(p5, filters=512)
# 存放第三个有效特征层的输出
p5_output = p5
# 返回输出层结果
return p3_output, p4_output, p5_output
ネットワークアーキテクチャを見ると、パラメータの数は2,000万を超えるものから600万に減少しています。
if __name__ == '__main__':
# 构造输入
feat1 = keras.Input(shape=[52,52,256])
feat2 = keras.Input(shape=[26,26,512])
p5 = keras.Input(shape=[13,13,1024])
# 返回panet结果
p3_output, p4_output, p5_output = panet(feat1, feat2, p5)
inputs = [feat1, feat2, p5]
outputs = [p3_output, p4_output, p5_output]
# 构建网络
model = keras.Model(inputs, outputs)
model.summary()
Total params: 6,846,037
Trainable params: 6,826,837
Non-trainable params: 19,200