Aufmerksamkeitsmechanismen in der Computer Vision: CBAM
Aufmerksamkeit – „Bemerkung von jemandem oder etwas gemacht“
Einführung
Computer Vision ist ein Bereich der künstlichen Intelligenz, der darauf abzielt, die visuelle Wahrnehmung des Menschen nachzuahmen. Die Einführung von Aufmerksamkeitsmechanismen hat zu erheblichen Fortschritten in der Computer Vision geführt.
Voraussetzungen
Kenntnisse der Python-Syntax und der Programmierkonzepte sind für das Verständnis der in diesem Artikel vorgestellten Codefragmente unerlässlich. Darüber hinaus ist die Kenntnis der Deep-Learning-Grundlagen und der PyTorch-Bibliothek hilfreich, um die in den folgenden Abschnitten behandelten Konzepte zu verstehen.
Bevor wir uns weiter mit der Verwendung von Aufmerksamkeitsmechanismen in der Computer Vision befassen, werfen wir zunächst einen Blick auf ihre Inspiration: das menschliche Sehen.
Visuelle Wahrnehmung im menschlichen Auge
Im obigen Diagramm verläuft das Licht vom interessierenden Objekt (in diesem Fall ein Blatt) zur Makula, der primären Funktionsregion der Netzhautregion des Auges. Was passiert jedoch, wenn wir mehrere Objekte im Sichtfeld haben?
„Achtung“ auf einer Kaffeetasse in mehreren Objektinstanzen (Bildnachweis – ICML 2019)
„Achtung“ auf einem Papierstapel in mehreren Objektinstanzen (Bildnachweis – ICML 2019)
Ähnlich wie wir uns auf ein einzelnes Objekt konzentrieren, wenn sich in unserem Sichtfeld eine Reihe unterschiedlicher Objekte befinden, verwendet der Aufmerksamkeitsmechanismus in unserem visuellen Wahrnehmungssystem eine ausgeklügelte Gruppe von Filtern, um einen Unschärfeeffekt zu erzeugen (ähnlich dem von „Bokeh“). in der digitalen Fotografie). Dadurch wird die Umgebung unscharf, wenn das interessierende Objekt scharfgestellt ist.
Das Konzept der Aufmerksamkeit im Kontext neuronaler Netze und Deep Learning wurde 2014 in einem wegweisenden Artikel mit dem Titel „Neural Machine Translation by Jointly Learning to Align and Translate“ von Dzmitry Bahdanau, Kyunghyun Cho und Yoshua Bengio vorgestellt. Aufmerksamkeit, die Aufmerksamkeit populär machte, wurde im NeurIPS 2017-Artikel von Google Brain mit dem Titel „Attention Is All You Need“ vorgestellt.
Transformatorarchitektur, Scaled-Dot-Product-Attention und Multi-Head-Attention. (Bildnachweise)
In diesem Artikel wurde der Aufmerksamkeitsmechanismus anhand von drei Hauptkomponenten berechnet: Abfrage, Schlüssel und Wert. Diese Idee wurde erstmals im Paper „Self Attention Generative Adversarial Networks“ (im Volksmund auch unter der Abkürzung SAGAN bekannt) auf Computer Vision übertragen. In diesem Artikel wurde das Selbstaufmerksamkeitsmodul vorgestellt, wie im folgenden Diagramm dargestellt (f(x), g(x) und h(x) stellen Abfrage, Schlüssel bzw. Wert dar):
Selbstaufmerksamkeitsmodul von SAGAN
In diesem Beitrag besprechen wir eine andere Form des Aufmerksamkeitsmechanismus in der Computer Vision, bekannt als Convolutional Block Attention Module (CBAM).
Faltungsblock-Aufmerksamkeitsmodul (CBAM)
Obwohl das Convolutional Block Attention Module (CBAM) im ECCV-Artikel von 2018 mit dem Titel „CBAM: Convolutional Block Attention Module“ populär gemacht wurde, wurde das allgemeine Konzept im Artikel von 2016 mit dem Titel „SCA-CNN: Spatial and Channel-wise Attention in Convolutional Networks“ vorgestellt für Bildunterschriften“. SCA-CNN demonstrierte das Potenzial der Nutzung mehrschichtiger Aufmerksamkeit: räumliche Aufmerksamkeit und Kanalaufmerksamkeit kombiniert, die beiden Bausteine von CBAM bei der Bildunterschrift. Das CBAM-Papier war das erste, das die breite Anwendbarkeit des Moduls, insbesondere für Bildklassifizierungs- und Objekterkennungsaufgaben, erfolgreich demonstrierte.
Bevor wir uns mit den Details von CBAM befassen, werfen wir einen Blick auf die allgemeine Struktur des Moduls:
Layout des Faltungsblock-Aufmerksamkeitsmoduls
CBAM enthält zwei aufeinanderfolgende Untermodule namens Channel Attention Module (CAM) und Spatial Attention Module (SAM), die in dieser bestimmten Reihenfolge angewendet werden. Die Autoren des Papiers weisen darauf hin, dass CBAM bei jedem Faltungsblock in tiefen Netzwerken angewendet wird, um nachfolgende „verfeinerte Feature-Maps“ aus den „Eingabe-Zwischen-Feature-Maps“ zu erhalten.
Was sind räumliche Aufmerksamkeit und Kanalaufmerksamkeit?
Die Wörter Spatial und Channel sind wahrscheinlich die am häufigsten verwendeten Begriffe in der Computer Vision, insbesondere wenn es um Faltungsschichten geht. In jeder Faltungsschicht ist die Eingabe ein Tensor und die nachfolgende Ausgabe ein Tensor. Dieser Tensor wird durch dreidimensionale Metriken charakterisiert: h für die Höhe jeder Feature-Map; w für die Breite jeder Feature-Map; und c für die Gesamtzahl der Kanäle, die Gesamtzahl der Feature-Maps oder die Tiefe des Tensors. Aus diesem Grund bezeichnen wir die Abmessungen der Eingabe normalerweise als (c × h × w). Die Feature-Maps sind im Wesentlichen jedes Segment des unten gezeigten würfelförmigen Tensors, d. h. die Feature-Maps sind übereinander gestapelt. Der c-Dimensionswert wird durch die Gesamtzahl der in dieser Ebene verwendeten Faltungsfilter bestimmt. Bevor wir uns mit den Besonderheiten der räumlichen Aufmerksamkeit und der Kanalaufmerksamkeit befassen, wollen wir zunächst herausfinden, was diese Begriffe bedeuten und warum sie so wichtig sind.
Was versteht man unter räumlicher Aufmerksamkeit?
Spatial bezieht sich auf den Domänenraum, der in jeder Feature-Map eingeschlossen ist. Räumliche Aufmerksamkeit stellt den Aufmerksamkeitsmechanismus/die Aufmerksamkeitsmaske auf der Feature-Map oder einen einzelnen Querschnittsausschnitt des Tensors dar. Im Bild unten ist das interessierende Objekt beispielsweise ein Vogel, sodass die räumliche Aufmerksamkeit eine Maske erzeugt, die die Merkmale, die diesen Vogel definieren, hervorhebt. Indem wir die Feature-Maps mithilfe räumlicher Aufmerksamkeit verfeinern, verbessern wir die Eingabe in die nachfolgenden Faltungsschichten, was somit die Leistung des Modells verbessert.
Feature-Maps dargestellt als Tensor (Bildnachweis)
Was ist dann Kanalaufmerksamkeit und brauchen wir sie überhaupt?
Wie oben besprochen, sind Kanäle im Wesentlichen die in einem Tensor gestapelten Feature-Maps, wobei jeder Querschnittsausschnitt im Grunde eine Feature-Map der Dimension (h x w) ist. Normalerweise lernen die trainierbaren Gewichte, aus denen die Filter bestehen, in Faltungsschichten im Allgemeinen kleine Werte (nahe Null), sodass wir ähnliche Feature-Maps beobachten, von denen viele wie Kopien voneinander erscheinen. Diese Beobachtung war ein Hauptgrund für das CVPR 2020-Papier mit dem Titel „GhostNet: More Features from Cheap Operations“. Obwohl sie ähnlich aussehen, sind diese Filter äußerst nützlich beim Erlernen verschiedener Arten von Funktionen. Während einige speziell für das Erlernen horizontaler und vertikaler Kanten gedacht sind, sind andere allgemeiner und erlernen eine bestimmte Textur im Bild. Die Kanalaufmerksamkeit stellt im Wesentlichen eine Gewichtung für jeden Kanal bereit und wertet somit die einzelnen Kanäle auf, die am meisten zum Lernen beitragen, und steigert somit die Gesamtleistung des Modells.
Warum beides verwenden, ist nicht eines ausreichend?
Nun, technisch gesehen ja und nein; Die Autoren bieten in ihrer Code-Implementierung die Möglichkeit, nur die Kanalaufmerksamkeit zu verwenden und die räumliche Aufmerksamkeit auszuschalten. Um optimale Ergebnisse zu erzielen, wird jedoch empfohlen, beide zu verwenden. Laienhaft ausgedrückt sagt die Kanalaufmerksamkeit aus, welche Feature-Map für das Lernen wichtig ist und sie verbessert oder, wie die Autoren sagen, „verfeinert“. Unterdessen vermittelt die räumliche Aufmerksamkeit was in der Feature-Map unbedingt gelernt werden muss. Die Kombination beider verbessert die Feature Maps erheblich und rechtfertigt somit die deutliche Verbesserung der Modellleistung.
Räumliches Aufmerksamkeitsmodul (SAM)
Räumliches Aufmerksamkeitsmodul (SAM)
Das räumliche Aufmerksamkeitsmodul (SAM) besteht aus einer dreifachen sequentiellen Operation. Der erste Teil davon wird Kanalpool genannt, in dem der Eingabetensor der Dimensionen (c × h × w) in zwei Kanäle zerlegt wird , d. h. (2 × h × w), wobei jeder der beiden Kanäle das maximale Pooling und das durchschnittliche Pooling über die Kanäle hinweg darstellt. Dies dient als Eingabe für die Faltungsschicht, die eine 1-Kanal-Feature-Map ausgibt, d. h. die Dimension der Ausgabe beträgt (1 × h × w). Somit handelt es sich bei dieser Faltungsschicht um eine Faltung, die die räumliche Dimension beibehält, und verwendet Polsterung, um dasselbe zu erreichen. Im Code folgt auf die Faltung eine Batch-Norm-Schicht, um die Ausgabe der Faltung zu normalisieren und zu skalieren. Allerdings haben die Autoren auch eine Option zur Verwendung der ReLU-Aktivierungsfunktion nach der Faltungsschicht bereitgestellt, diese verwendet jedoch standardmäßig nur Faltung + Batch-Norm. Die Ausgabe wird dann an eine Sigmoid-Aktivierungsschicht weitergeleitet. Sigmoid ist eine probabilistische Aktivierung und ordnet alle Werte einem Bereich zwischen 0 und 1 zu. Diese räumliche Aufmerksamkeitsmaske wird dann mithilfe eines einfachen elementweisen Produkts auf alle Feature-Maps im Eingabetensor angewendet.
Die Autoren validierten verschiedene Ansätze zur Berechnung der räumlichen Aufmerksamkeit mithilfe von SAM in der ImageNet-Klassifizierungsaufgabe. Die Ergebnisse des Papiers sind unten dargestellt:
Architecture | Parameters (in millions) | GFLOPs | Top-1 Error (%) | Top-5 Error (%) |
---|---|---|---|---|
Vanilla ResNet-50 | 25.56 | 3.86 | 24.56 | 7.5 |
ResNet-50 + CBAM1 | 28.09 | 3.862 | 22.8 | 6.52 |
ResNet-50 + CBAM (1 x 1 conv, k = 3)2 | 28.10 | 3.869 | 22.96 | 6.64 |
ResNet-50 + CBAM (1 x 1 conv, k = 7)2 | 28.10 | 3.869 | 22.9 | 6.47 |
ResNet-50 + CBAM (AvgPool + MaxPool, k = 3)2 | 28.09 | 3.863 | 22.68 | 6.41 |
ResNet-50 + CBAM (AvgPool + MaxPool, k = 7)2 | 28.09 | 3.864 | 22.66 | 6.31 |
1 – CBAM repräsentiert hier nur das Channel Attention Module (CAM). Das Raumaufmerksamkeitsmodul (SAM) wurde ausgeschaltet.
2 – CBAM repräsentiert hier sowohl CAM als auch SAM. Die Angaben in den Klammern zeigen die Art und Weise der Berechnung des Kanalpools und der Kernelgröße, die für die Faltungsschicht in SAM verwendet werden.
PyTorch-Code-Implementierung der räumlichen Aufmerksamkeitskomponenten:
import torch
import torch.nn as nn
class BasicConv(nn.Module):
def __init__(self, in_planes, out_planes, kernel_size, stride=1, padding=0, dilation=1, groups=1, relu=True, bn=True, bias=False):
super(BasicConv, self).__init__()
self.out_channels = out_planes
self.conv = nn.Conv2d(in_planes, out_planes, kernel_size=kernel_size, stride=stride, padding=padding, dilation=dilation, groups=groups, bias=bias)
self.bn = nn.BatchNorm2d(out_planes,eps=1e-5, momentum=0.01, affine=True) if bn else None
self.relu = nn.ReLU() if relu else None
def forward(self, x):
x = self.conv(x)
if self.bn is not None:
x = self.bn(x)
if self.relu is not None:
x = self.relu(x)
return x
class ChannelPool(nn.Module):
def forward(self, x):
return torch.cat( (torch.max(x,1)[0].unsqueeze(1), torch.mean(x,1).unsqueeze(1)), dim=1 )
class SpatialGate(nn.Module):
def __init__(self):
super(SpatialGate, self).__init__()
kernel_size = 7
self.compress = ChannelPool()
self.spatial = BasicConv(2, 1, kernel_size, stride=1, padding=(kernel_size-1) // 2, relu=False)
def forward(self, x):
x_compress = self.compress(x)
x_out = self.spatial(x_compress)
scale = F.sigmoid(x_out) # broadcasting
return x * scale
SAM PyTorch-Code
Kanalaufmerksamkeitsmodul (CAM)
Kanalaufmerksamkeitsmodul (CAM)
Das Kanalaufmerksamkeitsmodul (CAM) ist eine weitere sequentielle Operation, aber etwas komplexer als das räumliche Aufmerksamkeitsmodul (SAM). Auf den ersten Blick ähnelt CAM der Squeeze Excite (SE)-Schicht. SE wurde erstmals im CVPR/TPAMI-Papier von 2018 mit dem Titel „Squeeze-and-Excitation Networks“ vorgeschlagen. Um es kurz zusammenzufassen: So sieht der SE-Block aus:
_SE-Block
Um einen SE-Block präziser zu interpretieren, zeigt das folgende Diagramm des SE-Blocks aus dem CVPR-2020-Papier mit dem Titel „ECA-Net: Efficient Channel Attention for Deep Convolutional Neural Networks“ die klare Ähnlichkeit zwischen einem SE-Block und das CAM im CBAM (Hinweis: Wir werden ECANet in einem kommenden Beitrag dieser Serie behandeln).
_SE-Block vereinfacht
Was ist also der Unterschied zwischen Squeeze Excitation und Channel Attention Module?
Bevor wir darauf antworten, werfen wir einen kurzen Blick auf das SE-Modul. Das SE-Modul verfügt über die folgenden Komponenten: Global Average Pooling (GAP) und ein mehrschichtiges Perzeptron-Netzwerk (MLP), das durch Reduktionsverhältnis (r) und Sigmoidaktivierung abgebildet wird. Die Eingabe in den SE-Block ist im Wesentlichen ein Tensor der Dimension (c × h × w). GAP ist im Wesentlichen eine durchschnittliche Pooling-Operation, bei der jede Feature-Map auf ein einzelnes Pixel reduziert wird, sodass jeder Kanal nun in eine räumliche Dimension (1 × 1) zerlegt wird. Somit ist die Ausgabedimension des GAP im Grunde ein eindimensionaler Vektor der Länge c, der als (c × 1 × 1) dargestellt werden kann. Dieser Vektor wird dann als Eingabe an das Multi-Layer-Perceptron-Netzwerk (MLP) übergeben, das einen Engpass aufweist, dessen Breite oder Anzahl der Neuronen durch das Reduktionsverhältnis (r) bestimmt wird. Je höher das Reduktionsverhältnis, desto geringer ist die Anzahl der Neuronen im Engpass und umgekehrt. Der Ausgabevektor dieses MLP wird dann an eine Sigmoid-Aktivierungsschicht weitergeleitet, die dann die Werte im Vektor im Bereich von 0 und 1 abbildet.
Das Kanalaufmerksamkeitsmodul (CAM) ähnelt mit einer kleinen Modifikation der Squeeze-Erregungsschicht. Anstatt die Feature-Maps durch Global Average Pooling (GAP) auf ein einzelnes Pixel zu reduzieren, wird der Eingabetensor in zwei aufeinanderfolgende Dimensionsvektoren (c × 1 × 1) zerlegt. Einer dieser Vektoren wird von GAP generiert, während der andere Vektor von Global Max Pooling (GMP) generiert wird. Durchschnittliches Pooling wird hauptsächlich zum Aggregieren räumlicher Informationen verwendet, während maximales Pooling viel umfassendere Kontextinformationen in Form von Kanten des Objekts innerhalb des Bildes bewahrt, was zu einer feineren Kanalaufmerksamkeit führt. Einfach ausgedrückt hat das durchschnittliche Pooling einen glättenden Effekt, während das maximale Pooling einen viel schärferen Effekt hat, aber die natürlichen Kanten der Objekte präziser beibehält. Die Autoren bestätigen dies in ihren Experimenten, in denen sie zeigen, dass die Verwendung von GAP und GMP bessere Ergebnisse liefert als die alleinige Verwendung von GAP, wie im Fall von Squeeze-Anregungsnetzwerken.
Die folgende Tabelle zeigt einige Ergebnisse zur ImageNet-Klassifizierung aus dem Papier zur Validierung der Verwendung von GAP + GMP:
Architecture | Parameters (in millions) | GFLOPs | Top-1 Error (%) | Top-5 Error (%) |
---|---|---|---|---|
Vanilla ResNet-50 | 25.56 | 3.86 | 24.56 | 7.5 |
SE-ResNet-50 (GAP) | 25.92 | 3.94 | 23.14 | 6.7 |
ResNet-50 + CBAM1 (GMP) | 25.92 | 3.94 | 23.2 | 6.83 |
ResNet-50 + CBAM1 (GMP + GAP) | 25.92 | 4.02 | 22.8 | 6.52 |
1 – CBAM repräsentiert hier nur das Channel Attention Module (CAM). Das räumliche Aufmerksamkeitsmodul (SAM) wurde ausgeschaltet.
Die beiden von GAP und GMP erzeugten Vektoren werden nacheinander an das MLP-Netzwerk gesendet, dessen Ausgangsvektoren anschließend elementweise summiert werden. Zu beachten ist, dass beide Eingaben die gleichen Gewichtungen für das MLP verwenden und die ReLU-Aktivierungsfunktion aufgrund ihrer Nichtlinearität als bevorzugte Wahl verwendet wird.
Der resultierende Vektor wird nach der Summierung dann an die Sigmoid-Aktivierungsschicht weitergeleitet, die die Kanalgewichte generiert (die einfach elementweise entsprechend jeder entsprechenden Kanal-/Feature-Map im Eingabetensor multipliziert werden).
Hier ist die Implementierung des Channel Attention Module (CAM) in PyTorch:
import torch
import torch.nn as nn
def logsumexp_2d(tensor):
tensor_flatten = tensor.view(tensor.size(0), tensor.size(1), -1)
s, _ = torch.max(tensor_flatten, dim=2, keepdim=True)
outputs = s + (tensor_flatten - s).exp().sum(dim=2, keepdim=True).log()
return outputs
class Flatten(nn.Module):
def forward(self, x):
return x.view(x.size(0), -1)
class ChannelGate(nn.Module):
def __init__(self, gate_channels, reduction_ratio=16, pool_types=['avg', 'max']):
super(ChannelGate, self).__init__()
self.gate_channels = gate_channels
self.mlp = nn.Sequential(
Flatten(),
nn.Linear(gate_channels, gate_channels // reduction_ratio),
nn.ReLU(),
nn.Linear(gate_channels // reduction_ratio, gate_channels)
)
self.pool_types = pool_types
def forward(self, x):
channel_att_sum = None
for pool_type in self.pool_types:
if pool_type=='avg':
avg_pool = F.avg_pool2d( x, (x.size(2), x.size(3)), stride=(x.size(2), x.size(3)))
channel_att_raw = self.mlp( avg_pool )
elif pool_type=='max':
max_pool = F.max_pool2d( x, (x.size(2), x.size(3)), stride=(x.size(2), x.size(3)))
channel_att_raw = self.mlp( max_pool )
elif pool_type=='lp':
lp_pool = F.lp_pool2d( x, 2, (x.size(2), x.size(3)), stride=(x.size(2), x.size(3)))
channel_att_raw = self.mlp( lp_pool )
elif pool_type=='lse':
# LSE pool only
lse_pool = logsumexp_2d(x)
channel_att_raw = self.mlp( lse_pool )
if channel_att_sum is None:
channel_att_sum = channel_att_raw
else:
channel_att_sum = channel_att_sum + channel_att_raw
scale = F.sigmoid( channel_att_sum ).unsqueeze(2).unsqueeze(3).expand_as(x)
return x * scale
Kanalaufmerksamkeitsmodul (CAM) in PyTorch
Die Autoren bieten zwar die Möglichkeit, Power Average Pooling (LP-Pool) und Logarithmic Summed Exponential (LSE) zu verwenden, die im obigen Codeausschnitt gezeigt werden, die Ergebnisse, die bei deren Verwendung erzielt werden, werden in dem Artikel jedoch nicht erörtert. Für das Papier ist das Verkleinerungsverhältnis (r) auf einen Standardwert von 16 eingestellt.
Abschluss
CBAM wird als Schicht in jedem Faltungsblock eines CNN-Modells angewendet. Es nimmt einen Tensor auf, der die Feature-Maps aus der vorherigen Faltungsschicht enthält, und verfeinert ihn zunächst durch Anwenden der Kanalaufmerksamkeit mithilfe von CAM. Anschließend wird dieser verfeinerte Tensor an SAM übergeben, wo die räumliche Aufmerksamkeit angewendet wird, was zu den verfeinerten Feature-Maps führt. Der vollständige Code für die CBAM-Ebene in PyTorch ist unten aufgeführt.
import torch
import math
import torch.nn as nn
class BasicConv(nn.Module):
def __init__(self, in_planes, out_planes, kernel_size, stride=1, padding=0, dilation=1, groups=1, relu=True, bn=True, bias=False):
super(BasicConv, self).__init__()
self.out_channels = out_planes
self.conv = nn.Conv2d(in_planes, out_planes, kernel_size=kernel_size, stride=stride, padding=padding, dilation=dilation, groups=groups, bias=bias)
self.bn = nn.BatchNorm2d(out_planes,eps=1e-5, momentum=0.01, affine=True) if bn else None
self.relu = nn.ReLU() if relu else None
def forward(self, x):
x = self.conv(x)
if self.bn is not None:
x = self.bn(x)
if self.relu is not None:
x = self.relu(x)
return x
class Flatten(nn.Module):
def forward(self, x):
return x.view(x.size(0), -1)
class ChannelGate(nn.Module):
def __init__(self, gate_channels, reduction_ratio=16, pool_types=['avg', 'max']):
super(ChannelGate, self).__init__()
self.gate_channels = gate_channels
self.mlp = nn.Sequential(
Flatten(),
nn.Linear(gate_channels, gate_channels // reduction_ratio),
nn.ReLU(),
nn.Linear(gate_channels // reduction_ratio, gate_channels)
)
self.pool_types = pool_types
def forward(self, x):
channel_att_sum = None
for pool_type in self.pool_types:
if pool_type=='avg':
avg_pool = F.avg_pool2d( x, (x.size(2), x.size(3)), stride=(x.size(2), x.size(3)))
channel_att_raw = self.mlp( avg_pool )
elif pool_type=='max':
max_pool = F.max_pool2d( x, (x.size(2), x.size(3)), stride=(x.size(2), x.size(3)))
channel_att_raw = self.mlp( max_pool )
elif pool_type=='lp':
lp_pool = F.lp_pool2d( x, 2, (x.size(2), x.size(3)), stride=(x.size(2), x.size(3)))
channel_att_raw = self.mlp( lp_pool )
elif pool_type=='lse':
# LSE pool only
lse_pool = logsumexp_2d(x)
channel_att_raw = self.mlp( lse_pool )
if channel_att_sum is None:
channel_att_sum = channel_att_raw
else:
channel_att_sum = channel_att_sum + channel_att_raw
scale = F.sigmoid( channel_att_sum ).unsqueeze(2).unsqueeze(3).expand_as(x)
return x * scale
def logsumexp_2d(tensor):
tensor_flatten = tensor.view(tensor.size(0), tensor.size(1), -1)
s, _ = torch.max(tensor_flatten, dim=2, keepdim=True)
outputs = s + (tensor_flatten - s).exp().sum(dim=2, keepdim=True).log()
return outputs
class ChannelPool(nn.Module):
def forward(self, x):
return torch.cat( (torch.max(x,1)[0].unsqueeze(1), torch.mean(x,1).unsqueeze(1)), dim=1 )
class SpatialGate(nn.Module):
def __init__(self):
super(SpatialGate, self).__init__()
kernel_size = 7
self.compress = ChannelPool()
self.spatial = BasicConv(2, 1, kernel_size, stride=1, padding=(kernel_size-1) // 2, relu=False)
def forward(self, x):
x_compress = self.compress(x)
x_out = self.spatial(x_compress)
scale = F.sigmoid(x_out) # broadcasting
return x * scale
class CBAM(nn.Module):
def __init__(self, gate_channels, reduction_ratio=16, pool_types=['avg', 'max'], no_spatial=False):
super(CBAM, self).__init__()
self.ChannelGate = ChannelGate(gate_channels, reduction_ratio, pool_types)
self.no_spatial=no_spatial
if not no_spatial:
self.SpatialGate = SpatialGate()
def forward(self, x):
x_out = self.ChannelGate(x)
if not self.no_spatial:
x_out = self.SpatialGate(x_out)
return x_out
Ablationsstudie und Ergebnisse
Die Autoren validieren CBAM anhand einer Vielzahl von Experimenten, die von der ImageNet-Klassifizierung bis zur Objekterkennung in MS-COCO- und PASCAL-VOC-Datensätzen reichen. Die Autoren stellen außerdem eine Ablationsstudie vor, in der sie ihre Idee der Anwendung von CAM und SAM in dieser Reihenfolge innerhalb von CBAM im Vergleich zu den Ergebnissen bei Nichtanwendung von SAM verteidigen.
Architecture | Top-1 Error (%) | Top-5 Error (%) |
---|---|---|
Vanilla ResNet-50 | 24.56 | 7.5 |
SE-ResNet-50 | 23.14 | 6.7Res |
Net-50 + CBAM (CAM -> SAM) | 22.66 | 6.31 |
ResNet-50 + CBAM (SAM -> CAM) | 22.78 | 6.42 |
ResNet-50 + CBAM (SAM and CAM in parallel) | 22.95 | 6.59 |
Abschließend sind hier die Ergebnisse von CBAM zur ImageNet-Klassifizierung für einige der in der Arbeit vorgestellten Modelle:
Architecture | Parameters (in millions) | GFLOPs | Top-1 Error (%) | Top-5 Error (%) |
---|---|---|---|---|
Vanilla ResNet-101 | 44.55 | 7.57 | 23.38 | 6.88 |
SE-ResNet-101 | 49.33 | 7.575 | 22.35 | 6.19 |
ResNet-101 + CBAM | 49.33 | 7.581 | 21.51 | 5.69 |
Vanilla WideResNet18 (widen=2.0) | 45.62 | 6.696 | 25.63 | 8.20 |
WideResNet18 (widen=2.0) + SE | 45.97 | 6.696 | 24.93 | 7.65 |
WideResNet18 (widen=2.0) + CBAM | 45.97 | 6.697 | 24.84 | 7.63 |
Vanilla ResNeXt50(32x4d) | 25.03 | 3.768 | 22.85 | 6.48 |
ResNeXt50 (32x4d) + SE | 27.56 | 3.771 | 21.91 | 6.04 |
ResNeXt50 (32x4d) + CBAM | 27.56 | 3.774 | 21.92 | 5.91 |
Vanilla MobileNet | 4.23 | 0.569 | 31.39 | 11.51 |
MobileNet + SE | 5.07 | 0.570 | 29.97 | 10.63 |
MobileNet + CBAM | 5.07 | 0.576 | 29.01 | 9.99 |
Und einige der bemerkenswerten Ergebnisse der Objekterkennung, wie im Artikel gezeigt:
MS-COCO
Backbone | Detector | mAP@.5 | mAP@.75 | mAP@[.5, .95] |
---|---|---|---|---|
Vanilla ResNet101 | Faster-RCNN | 48.4 | 30.7 | 29.1 |
ResNet101 + CBAM | Faster-RCNN | 50.5 | 32.6 | 30.8 |
PASCAL VOC
Backbone | Detector | mAP@.5 | Parameters (in millions) |
---|---|---|---|
VGG16 | SSD | 77.8 | 26.5 |
VGG16 | StairNet | 78.9 | 32.0 |
VGG16 | StairNet + SE | 79.1 | 32.1 |
VGG16 | StairNet + CBAM | 79.3 | 32.1 |
Wie oben gezeigt, übertrifft CBAM die Vanilla-Modelle und ihre nachfolgenden Squeeze-Anregungsversionen in komplexen Netzwerken bei anspruchsvollen Datensätzen wie ImageNet und MS-COCO deutlich.
Die Autoren gehen sogar noch einen Schritt weiter, um zusätzliche Einblicke in die Auswirkung von CBAM auf die Modellleistung zu liefern, indem sie die Ergebnisse der Gradientengewichteten Klassenaktivierungskarten (Grad-CAM) auf einigen Beispielbildern von ImageNet visualisieren und sie mit denen des SE-Modells vergleichen das Basis-Vanilla-Modell. Grad-CAM nutzt grundsätzlich den Gradienten der letzten Faltungsschicht, um eine grobe Lokalisierung der wichtigen Bereiche innerhalb des Bildes bereitzustellen, wodurch das Modell eine Vorhersage für diese bestimmte Zielbezeichnung liefern kann. Beispielsweise sollte Grad-CAM für ein Bild eines Hundes grundsätzlich den Bereich im Bild hervorheben, in dem sich der Hund befindet, da dieser Teil des Bildes für das Modell wichtig (oder der auslösende Grund) ist, ihn als zu klassifizieren (d. h. zu erkennen). ein Hund.
Einige der Ergebnisse des CBAM-Papiers sind unten aufgeführt:
GradCAM-Ergebnisse für CBAM
Die P-Werte unten zeigen den Klassenkonfidenzwert, und wie gezeigt, liefert CBAM im Vergleich zu seinem SE-Pendant und Vanilla deutlich bessere Grad-CAM-Ergebnisse. Als Sahnehäubchen führten die Autoren eine Umfrage durch, bei der den Benutzern die Grad-CAM-Ergebnisse für Vanilla-, SE- und CBAM-Modelle einer Stichprobe gemischter Bilder zusammen mit ihren tatsächlichen Bezeichnungen angezeigt wurden, und wurden gebeten, darüber abzustimmen, welche Grad- Die CAM-Ausgabe hebt den Kontextbereich besser hervor, der diese Bezeichnung darstellt. Basierend auf der Umfrage gewann CBAM die Umfrage mit deutlichem Vorsprung.
Vielen Dank fürs Lesen.
Referenzen
- Attention in Deep Learning, Alex Smola und Aston Zhang, Amazon Web Services (AWS), ICML 2019
- Aufmerksamkeit ist alles, was Sie brauchen, Google Brain, NIPS 2017
- Generative gegnerische Netzwerke mit Selbstaufmerksamkeit
- Selbstaufmerksamkeit in Computer Vision
- Aufmerksamkeit? Aufmerksamkeit!
- CBAM: Convolutional Block Attention Module, ECCV 2018
- SCA-CNN: Räumliche und kanalweise Aufmerksamkeit in Faltungsnetzwerken für Bildunterschriften
- Squeeze-and-Excitation Networks, CVPR/TPAMI 2018