[ディープラーニング](9)CNNの混合ドメインアテンションメカニズム(DANet、CBAM)、Tensorflowコードを完備

みなさん、こんにちは。今日は、Tensorflowを使用してDANetCBAMハイブリッドドメインアテンションメカニズムモデルを構築する方法を紹介します。前回の記事では、CNNのチャネルアテンションメカニズムSENetとECANetを紹介しました。興味がある場合は、 https ://blog.csdn.net/dgvv4/article/details/123572065をご覧ください。


1.アテンションメカニズムの紹介

アテンションメカニズムは本質的にリソース割り当てメカニズムであり、アテンションターゲットの重要度に応じてリソース割り当て方法を変更できるため、リソースはアテンションオブジェクトに対してより傾いています畳み込みニューラルネットワークでは、アテンションメカニズムによって割り当てられるリソースは重みパラメーターです。モデルトレーニングプロセスでは、アテンションオブジェクトにより多くの重みパラメータを割り当てると、アテンションオブジェクトの特徴抽出能力を向上させることができますターゲット検出タスクに注意メカニズムを追加すると、モデルの表現能力が向上し、無効なターゲットの妨害が効果的に減少し、対象のターゲットの検出効果が向上し、モデルの全体的な検出精度が向上します。


2.CBAMアテンションメカニズム

2.1メソッドの紹介

CBAMアテンションメカニズムは、チャネルアテンションメカニズム(チャネル)と空間アテンションメカニズム(空間)で構成されます。

CNAMアテンションメカニズムの利点:

(1)高度な軽量化: CBAMモジュールは、多数の畳み込み構造、少数のプーリング層、および機能融合操作を備えていません。この構造は、畳み込み乗算によって生じる多くの計算を回避し、モジュールの複雑さを低くします。計算量が少ない実験によると、軽量モデルにCBAMモジュールを追加すると、安定したパフォーマンスの向上がもたらされます。計算の複雑さがわずかに増加するのと比較して、CBAMの導入は非常に費用対効果が高くなります。

(2)強力な汎用性:その構造特性は、CBAMの強力な汎用性と高い移植性を決定します。これは主に2つの側面に反映されます。一方で、プーリング操作に基づくCBAMモジュールは、畳み込み操作の後に直接埋め込むことができます。つまり、このモジュールは、VGGなどの従来のニューラルネットワークだけでなく、ResNet50、MobileNetV3などのショートカット接続に基づく残差構造を含むネットワークにも追加できます。一方、 CBAMは、ターゲットの検出と分類の両方のタスク適しています。さまざまなデータ特性を持つデータセットを使用すると、検出または分類の精度でパフォーマンスを向上させることができます。

(3)良い効果:畳み込みニューラルネットワークに基づく従来の注意メカニズムは、チャネルドメインの分析に重点を置いており、特徴マップチャネル間の関係の考慮に限定されています。チャネルと空間の2つのスコープから始めて、CBAMは、チャネルから空間への順次注意構造を実現するために、空間的注意とチャネル注意という2つの分析次元を導入します。空間的注意により、ニューラルネットワークは、分類において決定的な役割を果たす画像内のピクセル領域により多くの注意を向け、無関係な領域を無視することができます。チャネル注意は、特徴マップチャネルの分布関係を処理するために使用され、同時に、2次元の注意分布が強化されます。注意メカニズムにより、モデルのパフォーマンスを向上させることができます。


2.2ネットワーク構造

(1)チャネルアテンションメカニズム

CBAMのチャネルアテンションメカニズムモジュールのフローチャートは次のとおりです。まず、入力特徴マップはそれぞれグローバル最大プーリングとグローバル平均プーリングの対象となり、特徴マップは2つの次元に基づいて圧縮され、異なる次元の2つの特徴記述が取得されます。プールされた特徴マップは多層パーセプトロンネットワークを共有します。このネットワークは、最初に1 * 1の畳み込みによって減少し、次に1*1の畳み込みによって増加します。2つの特徴マップをlayers.add()でスタックし、シグモイド活性化関数を使用して特徴マップの各チャネルの重みを正規化します正規化された重みと入力特徴マップを乗算します。

コード表示

#(1)通道注意力
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

(2)空間的注意メカニズム

CBAMの空間アテンションメカニズムモジュールは次のとおりです。チャネルアテンションメカニズムの出力フィーチャマップは、空間ドメインで処理されますまず、フィーチャマップは、チャネルディメンションに基づいてそれぞれ最大プーリングと平均プーリングの対象となり2つの出力フィーチャマップはチャネルディメンションlayers.concatenate()にスタックされます次に、1 * 1畳み込みを使用してチャネル数を調整し、最後にシグモイド関数を使用して重みを正規化します正規化された重みと入力特徴度を乗算します。

コード表示

#(2)空间注意力机制
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

(3)全体的なプロセス

CBAMの全体的なフローチャートは次のとおりです。入力特徴マップは、チャネルアテンションメカニズムを通過し、重みと入力特徴マップを乗算してから空間アテンションメカニズムに送信し、正規化された重みと空間アテンションメカニズムの入力特徴マップを乗算して最終的なものを取得します。特徴マップ。

完全なコード表示

import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers, Model

#(1)通道注意力
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

#(2)空间注意力机制
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

#(3)CBAM注意力
def CBAM_attention(inputs):

    # 先经过通道注意力再经过空间注意力
    x = channel_attenstion(inputs)
    x = spatial_attention(x)
    return x

#(4)构建模型结构
if __name__ == '__main__':
    
    # 构建输入层
    inputs = keras.Input(shape=[26,26,512])
    # CBAM注意力机制
    x = CBAM_attention(inputs)
    # 构建模型
    model = Model(inputs, x)
    # 查看模型结构
    model.summary()

パラメータは次のとおりです。

Total params: 263,427
Trainable params: 263,427
Non-trainable params: 0

3.DANetアテンションメカニズム

DANetアテンションメカニズムは、ポジションアテンションメカニズム(position)とチャネルアテンションメカニズム(channel)で構成されています。

ロケーションアテンションメカニズムは、任意の2つのロケーションでのフィーチャマップの空間依存関係をキャプチャする役割を果たし、同様のフィーチャは距離に関係なく相互に関連付けられます。チャネルアテンションメカニズムは、相互依存性を持つチャネルマップを選択的に強調するために、すべてのチャネルマップ間で関連する機能を統合する役割を果たします


 3.1位置注意メカニズム

ロケーションアテンションメカニズムのフローチャートは次のとおりです。

(1)入力特徴マップA(C×H×W)は、最初に3つの畳み込み層を介して3つの特徴マップB、C、Dを取得し、次にB、C、DをC×Nに再形成します。ここで、N=H×Wです。

(2)次に、再形成された特徴マップBの転置(NxC)に再形成された特徴マップC(CxN)行列tf.multul()を乗算し、正規化された重みS(N×N)を取得します。

(3)次に、再形成された特徴マップD(CxN)と重みSの転置(NxN)の間で行列乗算tf.multul( )を実行し、スケール係数αを乗算してから、元の形状に再形成します。ここで、αは0を初期化し、徐々に大きな重みを取得することを学びます

(4)最後に、layers.add()を入力特徴マップAと重ね合わせて、最終出力Eを取得します。

コード表示

# 位置注意力
def position_attention(inputs):
    # 定义可训练变量,反向传播可更新
    gama = tf.Variable(tf.ones(1))  # 初始化1

    # 获取输入特征图的shape
    b, h, w, c = inputs.shape

    # 深度可分离卷积[b,h,w,c]==>[b,h,w,c//8]
    x1 = layers.SeparableConv2D(filters=c//8, kernel_size=(1,1), strides=1, padding='same')(inputs)
    # 调整维度排序[b,h,w,c//8]==>[b,c//8,h,w]
    x1_trans = tf.transpose(x1, perm=[0,3,1,2])
    # 重塑特征图尺寸[b,c//8,h,w]==>[b,c//8,h*w]
    x1_trans_reshape = tf.reshape(x1_trans, shape=[-1,c//8,h*w])
    # 调整维度排序[b,c//8,h*w]==>[b,h*w,c//8]
    x1_trans_reshape_trans = tf.transpose(x1_trans_reshape, perm=[0,2,1])
    # 矩阵相乘
    x1_mutmul = x1_trans_reshape_trans @ x1_trans_reshape
    # 经过softmax归一化权重
    x1_mutmul = tf.nn.softmax(x1_mutmul)

    # 深度可分离卷积[b,h,w,c]==>[b,h,w,c]
    x2 = layers.SeparableConv2D(filters=c, kernel_size=(1,1), strides=1, padding='same')(inputs)
    # 调整维度排序[b,h,w,c]==>[b,c,h,w]
    x2_trans = tf.transpose(x2, perm=[0,3,1,2])
    # 重塑尺寸[b,c,h,w]==>[b,c,h*w]
    x2_trans_reshape = tf.reshape(x2_trans, shape=[-1,c,h*w])

    # 调整x1_mutmul的轴,和x2矩阵相乘
    x1_mutmul_trans = tf.transpose(x1_mutmul, perm=[0,2,1])
    x2_mutmul = x2_trans_reshape @ x1_mutmul_trans

    # 重塑尺寸[b,c,h*w]==>[b,c,h,w]
    x2_mutmul = tf.reshape(x2_mutmul, shape=[-1,c,h,w])
    # 轴变换[b,c,h,w]==>[b,h,w,c]
    x2_mutmul = tf.transpose(x2_mutmul, perm=[0,2,3,1])
    # 结果乘以可训练变量
    x2_mutmul = x2_mutmul * gama

    # 输入和输出叠加
    x = layers.add([x2_mutmul, inputs])
    return x

3.2チャネルアテンションモジュール

チャネルアテンションモジュールのフローチャートは次のとおりです。

(1)特徴マップAの形状変更(CxN)と形状変更および転置(NxC)をそれぞれ行います。

(2)得られた2つの特徴マップ行列tf.multul()を乗算し、softmaxを介して正規化された重みX(C×C)を取得します。

(3)次に、重みXの転置(CxC)と再形成された特徴マップA(CxN )に対して行列乗算tf.multul()を実行し、スケール係数βを乗算して、元の形状に再形成します。ここで、βは0に初期化され、徐々に大きな重みを取得することを学習します

(4)最後に、入力特徴マップAと重ね合わせて、最終的な出力特徴マップEを取得します。

コード表示

# 通道注意力
def channel_attention(inputs):
    # 定义可训练变量,反向传播可更新
    gama = tf.Variable(tf.ones(1))  # 初始化1

    # 获取输入特征图的shape
    b, h, w, c = inputs.shape

    # 重新排序维度[b,h,w,c]==>[b,c,h,w]
    x = tf.transpose(inputs, perm=[0,3,1,2])  # perm代表重新排序的轴
    # 重塑特征图尺寸[b,c,h,w]==>[b,c,h*w]
    x_reshape = tf.reshape(x, shape=[-1,c,h*w])

    # 重新排序维度[b,c,h*w]==>[b,h*w,c]
    x_reshape_trans = tf.transpose(x_reshape, perm=[0,2,1])  # 指定需要交换的轴
    # 矩阵相乘
    x_mutmul = x_reshape_trans @ x_reshape
    # 经过softmax归一化权重
    x_mutmul = tf.nn.softmax(x_mutmul)

    # reshape后的特征图与归一化权重矩阵相乘[b,x,h*w]
    x = x_reshape @ x_mutmul
    # 重塑形状[b,c,h*w]==>[b,c,h,w]
    x = tf.reshape(x, shape=[-1,c,h,w])
    # 重新排序维度[b,c,h,w]==>[b,h,w,c]
    x = tf.transpose(x, perm=[0,2,3,1])
    # 结果乘以可训练变量
    x = x * gama

    # 输入和输出特征图叠加
    x = layers.add([x, inputs])

    return x

3.3全体的なプロセス

DANetの全体的なフローチャートは次のとおりです。入力画像は、それぞれ位置注意メカニズムとチャネル注意メカニズムを通過し、出力特徴マップをlayers.add()と重ね合わせて、出力特徴マップを取得します。

完全なコード表示

import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers, Model

#(1)通道注意力
def channel_attention(inputs):
    # 定义可训练变量,反向传播可更新
    gama = tf.Variable(tf.ones(1))  # 初始化1

    # 获取输入特征图的shape
    b, h, w, c = inputs.shape

    # 重新排序维度[b,h,w,c]==>[b,c,h,w]
    x = tf.transpose(inputs, perm=[0,3,1,2])  # perm代表重新排序的轴
    # 重塑特征图尺寸[b,c,h,w]==>[b,c,h*w]
    x_reshape = tf.reshape(x, shape=[-1,c,h*w])

    # 重新排序维度[b,c,h*w]==>[b,h*w,c]
    x_reshape_trans = tf.transpose(x_reshape, perm=[0,2,1])  # 指定需要交换的轴
    # 矩阵相乘
    x_mutmul = x_reshape_trans @ x_reshape
    # 经过softmax归一化权重
    x_mutmul = tf.nn.softmax(x_mutmul)

    # reshape后的特征图与归一化权重矩阵相乘[b,x,h*w]
    x = x_reshape @ x_mutmul
    # 重塑形状[b,c,h*w]==>[b,c,h,w]
    x = tf.reshape(x, shape=[-1,c,h,w])
    # 重新排序维度[b,c,h,w]==>[b,h,w,c]
    x = tf.transpose(x, perm=[0,2,3,1])
    # 结果乘以可训练变量
    x = x * gama

    # 输入和输出特征图叠加
    x = layers.add([x, inputs])

    return x

#(2)位置注意力
def position_attention(inputs):
    # 定义可训练变量,反向传播可更新
    gama = tf.Variable(tf.ones(1))  # 初始化1

    # 获取输入特征图的shape
    b, h, w, c = inputs.shape

    # 深度可分离卷积[b,h,w,c]==>[b,h,w,c//8]
    x1 = layers.SeparableConv2D(filters=c//8, kernel_size=(1,1), strides=1, padding='same')(inputs)
    # 调整维度排序[b,h,w,c//8]==>[b,c//8,h,w]
    x1_trans = tf.transpose(x1, perm=[0,3,1,2])
    # 重塑特征图尺寸[b,c//8,h,w]==>[b,c//8,h*w]
    x1_trans_reshape = tf.reshape(x1_trans, shape=[-1,c//8,h*w])
    # 调整维度排序[b,c//8,h*w]==>[b,h*w,c//8]
    x1_trans_reshape_trans = tf.transpose(x1_trans_reshape, perm=[0,2,1])
    # 矩阵相乘
    x1_mutmul = x1_trans_reshape_trans @ x1_trans_reshape
    # 经过softmax归一化权重
    x1_mutmul = tf.nn.softmax(x1_mutmul)

    # 深度可分离卷积[b,h,w,c]==>[b,h,w,c]
    x2 = layers.SeparableConv2D(filters=c, kernel_size=(1,1), strides=1, padding='same')(inputs)
    # 调整维度排序[b,h,w,c]==>[b,c,h,w]
    x2_trans = tf.transpose(x2, perm=[0,3,1,2])
    # 重塑尺寸[b,c,h,w]==>[b,c,h*w]
    x2_trans_reshape = tf.reshape(x2_trans, shape=[-1,c,h*w])

    # 调整x1_mutmul的轴,和x2矩阵相乘
    x1_mutmul_trans = tf.transpose(x1_mutmul, perm=[0,2,1])
    x2_mutmul = x2_trans_reshape @ x1_mutmul_trans

    # 重塑尺寸[b,c,h*w]==>[b,c,h,w]
    x2_mutmul = tf.reshape(x2_mutmul, shape=[-1,c,h,w])
    # 轴变换[b,c,h,w]==>[b,h,w,c]
    x2_mutmul = tf.transpose(x2_mutmul, perm=[0,2,3,1])
    # 结果乘以可训练变量
    x2_mutmul = x2_mutmul * gama

    # 输入和输出叠加
    x = layers.add([x2_mutmul, inputs])
    return x

#(3)DANet网络架构
def danet(inputs):

    # 输入分为两个分支
    x1 = channel_attention(inputs)  # 通道注意力
    x2 = position_attention(inputs)  # 位置注意力

    # 叠加两个注意力的结果
    x = layers.add([x1,x2])
    return x

# 构建网络
if __name__ == '__main__':

    # 构造输入层
    inputs = keras.Input(shape=[26,26,512])
    # 经过DANet注意力机制返回结果
    outputs = danet(inputs)

    # 构造模型
    model = Model(inputs, outputs)
    # 查看模型结构
    model.summary()

ネットワークパラメータを表示する

Total params: 296,512
Trainable params: 296,512
Non-trainable params: 0

おすすめ

転載: blog.csdn.net/dgvv4/article/details/123888724