画像の詳細なアテンション メカニズム (SEBlock | ECABlock | CBAM)

現在、チャネル注意メカニズムと空間注意メカニズムという2 つの主な注意メカニズムがあります

I.はじめに

画像が入力されると、ニューラル ネットワークが画像の特徴を抽出し、各レイヤーには異なるサイズの特徴マップがあることがわかっています。図 1 に示すように、VGG ネットワークが画像の特徴を抽出するときの特徴マップのサイズの変化を示しています。

ここに画像の説明を挿入

図 1 VGG ネットワーク機能の構造図

このうち、特徴マップの一般的な行列形状は[ C , H , W ] {[C,H,W]}です。[ C H W ] (図 1 の数字は[ H , W , C ] {[H,W,C]}[ H C ]形式)。モデルがトレーニングされているときは [ B , C , H , W ] {[B,C,H,W]}[ B C H W ]このうち、Bはバッチサイズ(バッチサイズ)、Cはチャネル(チャネル数)、Hは特徴マップの高さ(高さ)、Wは特徴マップの重み(幅)で表されます。

質問: 特徴マップの次元が[ B , C , H , W ] {[B,C,H,W]} であるのはなぜですか[ B C H 他の寸法形式の代わりにW ] を使用しますか?

回答: pytorch が画像を処理する場合、読み込み画像処理は[ C , H , W ] {[C,H,W]}です。[ C H W ]形式では、トレーニング中にバッチ サイズが追加される場合、複数の特徴マップが存在し、バッチ サイズは最初の次元に配置されます。これは当然[ B , C , H , W ] {[B,C,H, W] }[ B C H W ]これがpytorchの処理方法です

ネットワークが画像特徴層を抽出する場合、畳み込み層間にチャネル アテンション メカニズムと空間アテンション メカニズムを追加することで、ネットワークの画像抽出能力を強化できます。コードを記述するときは、特徴マップ間のアテンション メカニズムを考慮して、コード入力が[ B , C , H , W ] {[B,C,H,W]} となるようにします。[ B C H W ]特徴マップ、出力は依然として[ B , C , H , W ] {[B,C,H,W]}[ B C H W ]次元の特徴マップ。3 つの論文を見て、これら 2 つの注意メカニズムがどのように機能するかを見てみましょう。


2. SENet - チャネル アテンション メカニズム

1. 論文の紹介

論文のタイトル: スクイーズアンド励起ネットワーク

論文リンク: https://arxiv.org/pdf/1709.01507.pdf

ペーパーコード: https://github.com/hujie-frank/SENet

SEブロック構造図:

ここに画像の説明を挿入

図2 SEBlock構造図

要約: 畳み込みニューラル ネットワーク (CNN) の中心的な構成要素は畳み込み演算子です。これにより、ネットワークは、各層の局所的な受容野内で空間情報とチャネル情報の両方を融合することにより、有益な特徴を構築できます。広範囲にわたる先行研究では、この関係の空間コンポーネントが調査され、特徴階層全体にわたる空間エンコーディングの品質を向上させることによって CNN の表現力を強化することを目指してきました。この研究では、代わりにチャネルの関係に焦点を当て、チャネル間の相互依存性を明示的にモデル化することでチャネルごとの特徴応答を適応的に再調整する、「スクイーズアンド励起」(SE) ブロックと呼ぶ新しいアーキテクチャ ユニットを提案します。これらのブロックを積み重ねて、さまざまなデータセット間で非常に効果的に一般化する SENet アーキテクチャを形成できることを示します。さらに、SE ブロックがわずかな追加の計算コストで既存の最先端の CNN のパフォーマンスに大幅な向上をもたらすことを実証します。スクイーズアンドエキサイテーション ネットワークは、ILSVRC 2017 の分類提出の基礎を形成し、1 位を獲得し、トップ 5 の誤差を 2.251% に削減し、2016 年の受賞作品を相対的に約 25% 上回りました。モデルとコードは https://github.com/hujie-frank/SENet で入手できます。

概要のハイライト:

畳み込みニューラル ネットワーク (CNN) のコア コンポーネントは畳み込み演算子です。これにより、ネットワークは、各層の局所的な受容野で空間情報とチャネル情報を融合することにより、有益な特徴を構築できます。これまでの多くの研究では、この関係の空間コンポーネントが調査され、特徴階層における空間エンコーディングの品質を向上させることで CNN を強化することが試みられてきました。この研究では、チャネルごとの関係に焦点を当て、チャネル特性応答を自動的に適応的に再調整する SE モジュールと呼ばれる新しいアーキテクチャ ユニットを提案します。これらのモジュールを積み重ねて SENet ネットワーク構造を形成し、複数のデータセット上で非常に効果的に一般化できます。

SEBlock の革新ポイント:

  1. SEBlock は各チャネルに重みを与えるため、異なるチャネルが結果に異なる力を与えるようになります。
  2. この SE モジュールは、現在主流のニューラル ネットワークに簡単に追加できます。

2. アルゴリズムの解釈

図 3 は、次のようなチャネル アテンション メカニズムの 4 つのステップを示しています。

ここに画像の説明を挿入

図 3 SEBlock モジュールの分析
  1. 単一の画像から開始して、画像の特徴が抽出されます。現在の特徴レイヤー U の特徴マップの次元は[ C , H , W ] {[C,H,W]}です。[ C H W ]

  2. 特徴マップの[ H , W ] {[H,W]}[ H W ]平均プーリングまたは最大プーリングの次元、プーリング後の特徴マップのサイズは[ C , H , W ] {[C,H,W]}[ C H W ] ->[ C , 1 , 1 ] {[C,1,1]}[ C 1 [ C , 1 , 1] {[C,1,1]}[ C 1 各チャネル C には、それに対応する番号があることがわかります。図4は、ステップ(2)の具体的な動作に対応する。

ここに画像の説明を挿入

図4 平均プーリング(最大プーリング)動作、各チャネルの重みを取得、各チャネルの重みを取得
  1. [ C , 1 , 1 ] {[C,1,1]}の場合[ C 1 1 ]特徴は、各チャネル自体から抽出された重みとして理解できます。重みは、特徴抽出に対する各チャネルの影響を表します。グローバル プールされたベクトルが MLP ネットワークを通過した後、その意味は、各チャネルを取得することです図5は、ステップ(3)の具体的な動作に対応する。

ここに画像の説明を挿入

図 5 チャネル重みの生成
  1. 上記の手順では、各チャネルの重み C [ C , 1 , 1 ] {[C,1,1]}を取得します。[ C 1 1 ]、特徴マップ U[ C , H , W ] {[C,H,W]}[ C H W ]、つまり、各チャネルにはそれ自体の重みが乗算されます重みが大きいとそれに応じてチャネル特徴マップの値が大きくなり、最終的な出力への影響も大きくなり、重みが小さいとチャネル特徴マップの値が小さくなることが分かります最終的な出力への影響も大きくなり、小さくなります図6は、ステップ(4)の具体的な動作に対応する。

ここに画像の説明を挿入

図 6 チャネル アテンション - 各チャネルに異なる重みを乗算

チャネル アテンション ネットワークの詳細は、元の論文に記載されており、ここでは図 7 に示します。

ここに画像の説明を挿入

図 7 SEBlock 実装前 (左) と後 (右) の比較

注: この論文の比較実験により、rr効果はrが 16 のときに最もよくなります。したがって、一般的なデフォルトはr = 16 r=16 です。r=1 6ですが、チャンネル数が少ない場合は自分で調整する必要があります

3. Pytorch コードの実装

import torch
import torch.nn as nn


class SEBlock(nn.Module):
    def __init__(self, mode, channels, ratio):
        super(SEBlock, self).__init__()
        self.avg_pooling = nn.AdaptiveAvgPool2d(1)
        self.max_pooling = nn.AdaptiveMaxPool2d(1)
        if mode == "max":
            self.global_pooling = self.max_pooling
        elif mode == "avg":
            self.global_pooling = self.avg_pooling
        self.fc_layers = nn.Sequential(
            nn.Linear(in_features = channels, out_features = channels // ratio, bias = False),
            nn.ReLU(),
            nn.Linear(in_features = channels // ratio, out_features = channels, bias = False),
        )
		self.sigmoid = nn.Sigmoid()
     
    
    def forward(self, x):
        b, c, _, _ = x.shape
        v = self.global_pooling(x).view(b, c)
        v = self.fc_layers(v).view(b, c, 1, 1)
        v = self.sigmoid(v)
        return x * v

if __name__ == "__main__":
    model = SEBlock("max", 54, 9)
    feature_maps = torch.randn((8, 54, 32, 32))
    model(feature_maps)

4. 個人的な理解

チャネル アテンション メカニズムが効果的である理由: 特徴マップから画像特徴を抽出するプロセスでは、一部の特徴レイヤーの効果がより高く、一部の特徴レイヤーの効果が低くなるのは避けられません。したがって、チャネル自体によって抽出された重みが特徴マップに適用され、チャネルの重みが特徴マップから抽出された特徴に基づいて適応的に与えられるようになり、より大きな効果を持つ特徴マップがより大きな影響を与えるようになります。結果について。したがって、最終的な結果では、通常の畳み込み層よりも効果的に特徴を抽出できます。


3. ECANet - チャネル アテンション メカニズム (SENet では 1 次元の畳み込みが MLP に置き換わります)

1. 論文の紹介

論文名: ECA-Net: 深層畳み込みニューラル ネットワークの効率的なチャネル アテンション

論文リンク: https://arxiv.org/pdf/1910.03151.pdf

ペーパーコード: https://github.com/BangguWu/ECANet

ECABlock の主な構造図

ここに画像の説明を挿入

図 8 有効チャネル アテンション (ECA) モジュールの概略図

要約: 最近、チャネル アテンション メカニズムがディープ畳み込みニューラル ネットワーク (CNN) のパフォーマンス向上に大きな可能性をもたらすことが実証されました。ただし、既存の手法のほとんどは、より優れたパフォーマンスを実現するために、より洗練されたアテンション モジュールの開発に特化しているため、必然的にモデルの複雑さが増加します。パフォーマンスと複雑さのトレードオフのパラドックスを克服するために、この論文では、明らかなパフォーマンスの向上をもたらしながら、少数のパラメーターのみを必要とする効率的なチャネル アテンション (ECA) モジュールを提案します。SENet のチャネル アテンション モジュールを分析することで、チャネル アテンションの学習には次元の削減を回避することが重要であり、適切なクロスチャネル インタラクションによってモデルの複雑さを大幅に軽減しながらパフォーマンスを維持できることが経験的に示されました。したがって、次元削減を行わず、1D 畳み込みを介して効率的に実装できるローカル クロスチャネル インタラクション戦略を提案します。さらに、1Dコンボリューションのカーネルサイズを適応的に選択し、ローカルクロスチャネルインタラクションのカバレッジを決定する方法を開発します。提案された ECA モジュールは効率的かつ効果的です。たとえば、ResNet50 のバックボーンに対するモジュールのパラメータと計算は、それぞれ 80 対 24.37M、4.7e-4 GFLOP 対 3.86 GFLOP であり、パフォーマンスの向上は 2% 以上です。トップ1の精度という点で。私たちは、ResNets と MobileNetV2 のバックボーンを使用して、画像分類、オブジェクト検出、インスタンス セグメンテーションに関する ECA モジュールを広範囲に評価しています。実験結果は、私たちのモジュールがより効率的でありながら、対応するモジュールに対して有利なパフォーマンスを示していることを示しています。

概要のハイライト:

近年、チャネル アテンション メカニズムは、ディープ畳み込みニューラル ネットワーク (CNN) のパフォーマンスを向上させる上で大きな可能性を示しています。ただし、既存の手法のほとんどは、パフォーマンスを向上させるためにより複雑なアテンション モジュールの開発に焦点を当てているため、必然的にモデルの複雑さが増加します。パフォーマンスと複雑さの間のトレードオフを克服するために、この論文では、少数のパラメータのみを使用して大幅なパフォーマンス向上をもたらす効率的なチャネル アテンション (ECA) モジュールを提案します。SENet のチャネル アテンション モジュールを分析することで、チャネル アテンションを学習するには次元削減を回避することが重要であり、適切なクロスチャネル インタラクションによりパフォーマンスを維持しながらモデルの複雑さを大幅に軽減できることが経験的に示されています。したがって、次元削減を行わず、1D 畳み込みによって効率的に実装できるローカル クロスチャネル インタラクション戦略を提案します。

ECABlock のイノベーション

  1. SEBlock のステップ (3) では、MLP モジュール (FC->ReLU>FC->Sigmoid) が 1 次元の畳み込み形式に変換され、パラメータ計算の量が効果的に削減されます (CNN ネットワークでは、多くの場合、連結層はパラメータが膨大なため、全連結層を1次元の畳み込み形式に変更します)

  2. 1 次元畳み込みの効果は、完全に接続されていないことです。各畳み込みプロセスは一部のチャネルでのみ機能します。つまり、完全に接続された層のようなフルチャネル インタラクションではなく、適切なクロスチャネル インタラクションが実現されます。

2. 論文の解釈

平均プーリングによって取得された、指定された集約特徴[ C , 1 , 1 ] {[C,1,1]}[ C 1 [1 ] に示すように、ECA モジュールはカーネル サイズ k で 1 次元の畳み込みを実行することによってチャネル重みを生成します。ここで、k はチャネル次元 C のマッピングによって適応的に決定されます。

図の SEBlock との違いは、SEBlock のステップ (3) のみで、全結合層を 1 次元の畳み込みに置き換えます。1 次元の畳み込みカーネルのサイズは、チャネル C の数によって適応的に決定されます。

コンボリューション カーネルのサイズを適応的に決定する式: k = ∣ log 2 C + b γ ∣ od {k=|\cfrac{log_2{C}+b}{\gamma}|_{odd}}k=cログ_ _2C+bああ_ _
ここで、k はコンボリューション カーネルのサイズを表し、C はチャネル数を表します。∣ ∣ odd {| |_{odd}}ああ_ _k は奇数のみを取ることができることを示します、γ {\gamma}ガンマb{b}論文ではbを 2 と 1 に設定して、チャネル数 C とコンボリューション カーネルのサイズの比率を変更しています。

(チャネル C が畳み込みカーネルのサイズを適応的に決定することを理解する方法: チャネル数が多い場合、畳み込みカーネル k を少し大きくする必要があります。チャネル数が少ない場合、畳み込みカーネル k をいくつかのチャネル間で完全に Fusion インタラクションできるように、わずかに小さい)

3. Pytorch コードの実装

import math
import torch
import torch.nn as nn


class ECABlock(nn.Module):
    def __init__(self, channels, gamma = 2, b = 1):
        super(ECABlock, self).__init__()
        kernel_size = int(abs((math.log(channels, 2) + b) / gamma))
        kernel_size = kernel_size if kernel_size % 2 else kernel_size + 1
        self.avg_pool = nn.AdaptiveAvgPool2d(1)
        self.conv = nn.Conv1d(1, 1, kernel_size = kernel_size, padding = (kernel_size - 1) // 2, bias = False)
        self.sigmoid = nn.Sigmoid()

    def forward(self, x):
        v = self.avg_pool(x)
        v = self.conv(v.squeeze(-1).transpose(-1, -2)).transpose(-1, -2).unsqueeze(-1)
        v = self.sigmoid(v)
        return x * v


if __name__ == "__main__":
    features_maps = torch.randn((8, 54, 32, 32))
    model = ECABlock(54, gamma = 2, b = 1)
    model(features_maps)

2 つの論文のコード実装を比較すると、MLP が一次元の畳み込みに置き換えられただけであることがわかります。

# SEBlock 采用全连接层方式
def forward(self, x):
    b, c, _, _ = x.shape
    v = self.global_pooling(x).view(b, c)
    v = self.fc_layers(v).view(b, c, 1, 1)
    v = self.sigmoid(v)
    return x * v

# ECABlock 采用一维卷积方式
def forward(self, x):
	v = self.avg_pool(x)
	v = self.conv(v.squeeze(-1).transpose(-1, -2)).transpose(-1, -2).unsqueeze(-1)
	v = self.sigmoid(v)
	return x * v

4. 個人的な理解

ECABlock 自体には大きな内容の変更はなく、完全に接続されたレイヤーを置き換えてデータ量を削減するだけです (加算よりも減算の方が優れている場合があります)。


4. CBAMBlock - チャネル アテンション メカニズムと空間アテンション メカニズムの混合使用 (SEBlock または ECABlock の後に空間アテンション メカニズムが続く)

1. 論文の紹介

論文名: CBAM: 畳み込みブロック アテンション モジュール

論文リンク: https://arxiv.org/pdf/1807.06521v2.pdf

ペーパーコード: https: //github.com/luuuyi/CBAM.PyTorch(再現版)

CBAMBブロック構造図

ここに画像の説明を挿入

図9 CBAM(畳み込みブロックアテンションモジュール)のモジュール構造

要約: 我々は、フィードフォワード畳み込みニューラル ネットワーク用のシンプルかつ効果的なアテンション モジュールである畳み込みブロック アテンション モジュール (CBAM) を提案します。中間の特徴マップが与えられると、私たちのモジュールはチャネルと空間という 2 つの異なる次元に沿ってアテンション マップを順番に推論し、適応的な特徴改善のためにアテンション マップが入力特徴マップに乗算されます。CBAM は軽量で汎用的なモジュールであるため、オーバーヘッドを無視してあらゆる CNN アーキテクチャにシームレスに統合でき、ベース CNN とともにエンドツーエンドでトレーニングできます。弊社では、ImageNet-1K、MS COCO 検出、VOC 2007 検出データセットに関する広範な実験を通じて CBAM を検証しています。私たちの実験では、さまざまなモデルで分類と検出のパフォーマンスが一貫して向上していることが示されており、CBAM の幅広い適用可能性が実証されています。コードとモデルは公開されます。

概要のハイライト:

我々は、フィードフォワード畳み込みニューラル ネットワーク用のシンプルかつ効果的なアテンション モジュールである畳み込みブロック アテンション モジュール (CBAM) を提案します。中間の特徴マップが与えられると、私たちのモジュールは 2 つの独立したアテンション メカニズム、チャネル アテンションと空間アテンションを採用し、アテンション メカニズムによって得られた重みを入力特徴マップと乗算して、適応的な特徴改善を実現しますCBAM は軽量の汎用モジュールであるため、オーバーヘッドを無視して任意の CNN アーキテクチャにシームレスに統合でき、ベース CNN と一緒にエンドツーエンドでトレーニングできます。当社では、ImageNet-1K、MS COCO 検出、VOC 2007 検出データセットに関する広範な実験を通じて CBAM を検証しています。私たちの実験では、さまざまなモデルが分類と検出の両方のパフォーマンスで一貫した改善を達成し、CBAM の幅広い適用可能性を実証していることが示されています。コードとモデルは公開されます。

CBAMの革新

  1. SENet または ECANet に基づいて、チャネル アテンション モジュールの後に空間アテンション モジュールが接続され、チャネル アテンションと空間アテンションの二重メカニズムを実現します。
  2. SENet または ECANet の選択は主に、チャネル アテンションの接続が MLP か 1 次元畳み込みかによって決まります。
  3. アテンション モジュールは、単一の最大プーリングまたは平均プーリングを使用しなくなり、最大プーリングと平均プーリングの追加またはスタックを使用しますチャネル アテンション モジュールは加算を使用し、空間アテンション モジュールはスタッキングを使用します。

2. 論文の解釈

上記 2 つの論文では、チャネル アテンション方式のフル コネクション (SENet) またはコンボリューション (ECANet) 実装が実現されていますが、上記論文との最大の違いは、空間アテンション機構が追加されていることです。

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

ここに画像の説明を挿入

図10 チャネルアテンションモジュールの実装方法
  1. 特徴マップはそれぞれ MaxPool と AvgPool を通過して、2 つの[ C , 1 , 1 ] {[C,1,1]}を形成します。[ C 1 1 ]重みベクトル
  2. 2 つの重みベクトルは同じ MLP ネットワーク (同じネットワークであるため、ネットワーク パラメーターを共有する MLP とみなすこともできます) を通過し、各チャネルの重みにマッピングされます。
  3. マッピングされた重みを追加し、その後にシグモイド出力を追加します。
  4. 結果のチャネル重み[ C , 1 , 1 ] {[C,1,1]}[ C 1 1 ]と元の特徴マップ[ C , H , W ] {[C,H,W]}[ C H W ]チャネルで乗算

全体としては、基本的に SENet と同じですが、単一平均プーリング方式が最大プーリング方式と平均プーリング方式に同時に変更される点が異なりますその後、MLPを少し修正して1次元畳み込みに変更すると、ECANetの変形版になります

2) 空間注意メカニズム

ここに画像の説明を挿入

図11 空間注意モジュールの実装方法
  1. 特徴マップはそれぞれ MaxPool と AvgPool を通過して、2 つの[ 1 , H , W ] {[1,H,W]} を形成します。[ 1 H W ]重みベクトル、つまりチャネルごとの最大プーリングと平均プーリング。[ C , H , W ] {[C,H,W]}からのチャネル数[ C H W ]は[ 1 , H , W ] {[1,H,W]}になります[ 1 H W ]、同じ特徴点のすべてのチャネルをプールします。
  2. 取得した 2 つの特徴マップを積み重ねて[ 2 , H , W ] {[2,H,W]} を形成します。[ 2 H W ]特徴マップの空間重み
  3. 畳み込み層の後、特徴マップの次元は[ 2 , H , W ] {[2,H,W]}になります。[ 2 H W ]は[ 1 , H , W ] {[1,H,W]}になります[ 1 H W],这 [ 1 , H , W ] {[1,H,W]} [ 1 H W ]の特徴マップは特徴マップ上の各点の重要度を表し、値が大きいほど重要であることを示します。
  4. 結果の空間重み[ 1 , H , W ] {[1,H,W]}[ 1 H W ]と元の特徴マップ[ C , H , W ] {[C,H,W]}[ C H W ]を乗算します。つまり、特徴マップ上の[ H , W ] {[H,W]}[ H W ]各ポイントに重みが割り当てられます

サイズは[ H , W ] {[H,W]}として確認できます。[ H W ]特徴マップ、各点( x , y ) 、 x ∈ ( 0 , H ) 、 y ∈ ( 0 , W ) {(x,y),x\in(0,H),y\in (0 、W)}( x ,y ) バツ( 0 ,H ) y( 0 ,W )には、特徴マップ内の点の重要性を表す C 値があり、元の画像は領域の重要性を示す受容野を通じて推定されます。注目すべき場所(値が大きい場所ほど注目されやすい)にネットワークを適応的に集中させる必要があり、空間注目の仕組みが生まれました。

3. Pytorch コードの実装

チャネル アテンション メカニズム - 完全接続レイヤー バージョン

import math
import torch
import torch.nn as nn


class Channel_Attention_Module_FC(nn.Module):
    def __init__(self, channels, ratio):
        super(Channel_Attention_Module_FC, self).__init__()
        self.avg_pooling = nn.AdaptiveAvgPool2d(1)
        self.max_pooling = nn.AdaptiveMaxPool2d(1)
        self.fc_layers = nn.Sequential(
            nn.Linear(in_features = channels, out_features = channels // ratio, bias = False),
            nn.ReLU(),
            nn.Linear(in_features = channels // ratio, out_features = channels, bias = False)
        )
        self.sigmoid = nn.Sigmoid()

    def forward(self, x):
        b, c, h, w = x.shape
        avg_x = self.avg_pooling(x).view(b, c)
        max_x = self.max_pooling(x).view(b, c)
        v = self.fc_layers(avg_x) + self.fc_layers(max_x)
        v = self.sigmoid(v).view(b, c, 1, 1)
        return x * v

チャネル アテンション メカニズム - 1D 畳み込みバージョン

class Channel_Attention_Module_Conv(nn.Module):
    def __init__(self, channels, gamma = 2, b = 1):
        super(Channel_Attention_Module_Conv, self).__init__()
        kernel_size = int(abs((math.log(channels, 2) + b) / gamma))
        kernel_size = kernel_size if kernel_size % 2 else kernel_size + 1
        self.avg_pooling = nn.AdaptiveAvgPool2d(1)
        self.max_pooling = nn.AdaptiveMaxPool2d(1)
        self.conv = nn.Conv1d(1, 1, kernel_size = kernel_size, padding = (kernel_size - 1) // 2, bias = False)
        self.sigmoid = nn.Sigmoid()

    def forward(self, x):
        avg_x = self.avg_pooling(x)
        max_x = self.max_pooling(x)
        avg_out = self.conv(avg_x.squeeze(-1).transpose(-1, -2)).transpose(-1, -2).unsqueeze(-1)
        max_out = self.conv(max_x.squeeze(-1).transpose(-1, -2)).transpose(-1, -2).unsqueeze(-1)
        v = self.sigmoid(avg_out + max_out)
        return x * v

空間注意メカニズム

class Spatial_Attention_Module(nn.Module):
    def __init__(self, k: int):
        super(Spatial_Attention_Module, self).__init__()
        self.avg_pooling = torch.mean
        self.max_pooling = torch.max
        # In order to keep the size of the front and rear images consistent
        # with calculate, k = 1 + 2p, k denote kernel_size, and p denote padding number
        # so, when p = 1 -> k = 3; p = 2 -> k = 5; p = 3 -> k = 7, it works. when p = 4 -> k = 9, it is too big to use in network
        assert k in [3, 5, 7], "kernel size = 1 + 2 * padding, so kernel size must be 3, 5, 7"
        self.conv = nn.Conv2d(2, 1, kernel_size = (k, k), stride = (1, 1), padding = ((k - 1) // 2, (k - 1) // 2),
                              bias = False)
        self.sigmoid = nn.Sigmoid()

    def forward(self, x):
        # compress the C channel to 1 and keep the dimensions
        avg_x = self.avg_pooling(x, dim = 1, keepdim = True)
        max_x, _ = self.max_pooling(x, dim = 1, keepdim = True)
        v = self.conv(torch.cat((max_x, avg_x), dim = 1))
        v = self.sigmoid(v)
        return x * v

CBAM モジュール (空間注意とチャネル注意の組み合わせ)

class CBAMBlock(nn.Module):
    def __init__(self, channel_attention_mode: str, spatial_attention_kernel_size: int, channels: int = None,
                 ratio: int = None, gamma: int = None, b: int = None):
        super(CBAMBlock, self).__init__()
        if channel_attention_mode == "FC":
            assert channels != None and ratio != None and channel_attention_mode == "FC", \
                "FC channel attention block need feature maps' channels, ratio"
            self.channel_attention_block = Channel_Attention_Module_FC(channels = channels, ratio = ratio)
        elif channel_attention_mode == "Conv":
            assert channels != None and gamma != None and b != None and channel_attention_mode == "Conv", \
                "Conv channel attention block need feature maps' channels, gamma, b"
            self.channel_attention_block = Channel_Attention_Module_Conv(channels = channels, gamma = gamma, b = b)
        else:
            assert channel_attention_mode in ["FC", "Conv"], \
                "channel attention block must be 'FC' or 'Conv'"
        self.spatial_attention_block = Spatial_Attention_Module(k = spatial_attention_kernel_size)

    def forward(self, x):
        x = self.channel_attention_block(x)
        x = self.spatial_attention_block(x)
        return x

if __name__ == "__main__":
    feature_maps = torch.randn((8, 54, 32, 32))
    model = CBAMBlock("FC", 5, channels = 54, ratio = 9)
    model(feature_maps)
    model = CBAMBlock("Conv", 5, channels = 54, gamma = 2, b = 1)
    model(feature_maps)

4. 個人的な理解

空間アテンション メカニズムとチャネル アテンション メカニズムは同じ効果を持ち、どちらも重みを抽出し、元の特徴マップに作用しますが、一方が[ H , W ] {[H,W]}にある点が異なります。[ H W ]次元、1 つは[C] {[C]}にあります[ C ]次元では、このような方法は計算量をあまり増やさずにいくつかのポイントを上げることができ、これは良いトリックです。
必要なのは注意力だけです!

おすすめ

転載: blog.csdn.net/weixin_43913124/article/details/123113339