Mise en œuvre du mécanisme d'attention CBAM

1. Introduction à CBAM

Papier: CBAM: Convolutional Block Attention
Module
propose un module d'attention simple mais efficace CBAM, étant donné une carte de caractéristiques intermédiaire, déduire le poids d'attention le long des deux dimensions de l'espace et du canal tour à tour, puis le multiplier avec la carte de caractéristiques d'origine Apportez des ajustements adaptatifs aux fonctionnalités. Étant donné que CBAM est un module polyvalent léger, il peut être intégré de manière transparente dans n'importe quelle architecture CNN avec une surcharge supplémentaire négligeable, et il peut être formé de bout en bout avec le CNN de base. Après avoir intégré CBAM dans différents modèles sur différents ensembles de données de classification et de détection, les performances du modèle ont été constamment améliorées, démontrant sa large applicabilité.
Le CBAM peut être divisé en deux parties: le module d'attention de canal et le module d'attention spatiale, comme illustré dans la figure ci-dessous.
Insérez la description de l'image ici

  • Module d'attention du canal: faites attention aux caractéristiques qui sont significatives.
    Entrez une carte de caractéristiques F comme H W C (en fait, il peut y avoir un lot, à savoir NHWC). Tout d'abord, effectuez une mise en commun maximale de l'espace global et une mise en commun moyenne pour obtenir deux Un descripteur 1 1 C. Ensuite, envoyez-les à MLP (contenant une couche cachée), le nombre de neurones dans la première couche est C / r et le nombre de neurones dans la deuxième couche est C. Ce réseau de neurones est partagé (je ne sais pas s'il est partagé entre deux descripteurs ou deux couches de réseaux de neurones, qui devraient être ces derniers). Ensuite, appliquez une addition élément par élément aux deux vecteurs de sortie et obtenez le coefficient de poids Mc via la fonction sigmoïde. Enfin, multipliez Mc avec la fonction originale F pour obtenir une nouvelle fonction F '
  • Module d'attention spatiale: faites attention à l'endroit où les caractéristiques sont significatives.
    Entrez la carte des caractéristiques F'as H W C (peut également avoir un lot). Premièrement, deux descripteurs de H W 1 sont obtenus par le regroupement moyen des canaux et le regroupement maximal . Et épissé par canal. Ensuite, grâce à une couche convolutive 7 * 7 et une fonction d'activation sigmoïde, le coefficient de poids Ms est obtenu. Enfin, multipliez Ms et F pour obtenir la fonction d'attention finale.

2. Mise en œuvre du code

Après avoir lu le processus ci-dessus, il est facile de comprendre l'implémentation du code. Voici deux copies du code. L'un consiste à faire référence au code du blogueur , et l'autre est Github: kobiso / CBAM-tensorflow .
Code un:

def combined_static_and_dynamic_shape(tensor):
    """Returns a list containing static and dynamic values for the dimensions.  Returns a list of static 
    and dynamic values for shape dimensions. This is  useful to preserve static shapes when available in reshape operation.  
    Args:    tensor: A tensor of any type.  
    Returns:    A list of size tensor.shape.ndims containing integers or a scalar tensor.  """
    static_tensor_shape = tensor.shape.as_list()
    dynamic_tensor_shape = tf.shape(tensor)
    combined_shape = []
    for index, dim in enumerate(static_tensor_shape):
        if dim is not None:
            combined_shape.append(dim)
        else:
            combined_shape.append(dynamic_tensor_shape[index])
    return combined_shape


def convolutional_block_attention_module(feature_map, index, reduction_ratio = 0.5):
    """CBAM:convolutional block attention module
    Args:
        feature_map:input feature map
        index:the index of the module
        reduction_ratio:output units number of first MLP layer:reduction_ratio * feature map
    Return:
        feature map with channel and spatial attention"""

    with tf.variable_scope("cbam_%s" % (index)):
        feature_map_shape = combined_static_and_dynamic_shape(feature_map)
        # channel attention module
        channel_avg_weights = tf.nn.avg_pool(value=feature_map,
                                             ksize=[1, feature_map_shape[1], feature_map_shape[2], 1],
                                             strides=[1, 1, 1, 1],
                                             padding='VALID')  # global average pool
        channel_max_weights = tf.nn.max_pool(value=feature_map,
                                             ksize=[1, feature_map_shape[1], feature_map_shape[2], 1],
                                             strides=[1, 1, 1, 1],
                                             padding='VALID')
        channel_avg_reshape = tf.reshape(channel_avg_weights,
                                         [feature_map_shape[0], 1, feature_map_shape[3]])
        channel_max_reshape = tf.reshape(channel_max_weights,
                                         [feature_map_shape[0], 1, feature_map_shape[3]])
        channel_w_reshape = tf.concat([channel_avg_reshape, channel_max_reshape], axis=1)

        fc_1 = tf.layers.dense(inputs=channel_w_reshape,
                               units=feature_map_shape[3] * reduction_ratio,
                               name="fc_1",
                               activation=tf.nn.relu)
        fc_2 = tf.layers.dense(inputs=fc_1,
                               units=feature_map_shape[3],
                               name="fc_2",
                               activation=None)
        channel_attention = tf.reduce_sum(fc_2, axis=1, name="channel_attention_sum")
        channel_attention = tf.nn.sigmoid(channel_attention)
        channel_attention = tf.reshape(channel_attention,
                                       shape=[feature_map_shape[0], 1, 1, feature_map_shape[3]])
        feature_map_with_channel_attention = tf.multiply(feature_map, channel_attention)
        # saptial attention module
        # 通道平均池化,格式NWHC
        channel_wise_avg_pooling = tf.reduce_mean(feature_map_with_channel_attention, axis=3)
        channel_wise_avg_pooling = tf.reshape(channel_wise_avg_pooling,
                                              shape=[feature_map_shape[0], feature_map_shape[1],
                                                     feature_map_shape[2], 1]) # shape=[batch, H, W, 1]
        # 通道最大池化
        channel_wise_max_pooling = tf.reduce_max(feature_map_with_channel_attention, axis=3)
        channel_wise_max_pooling = tf.reshape(channel_wise_max_pooling,
                                              shape=[feature_map_shape[0], feature_map_shape[1],
                                                     feature_map_shape[2], 1])
        # 按通道拼接
        channel_wise_pooling = tf.concat([channel_wise_avg_pooling, channel_wise_max_pooling], axis=3)
        spatial_attention = slim.conv2d(channel_wise_pooling, 1, [7, 7],
                                        padding='SAME',
                                        activation_fn=tf.nn.sigmoid,
                                        scope="spatial_attention_conv")
        feature_map_with_attention = tf.multiply(feature_map_with_channel_attention, spatial_attention)
        return feature_map_with_attention

Lorsque ce code est implémenté dans le module d'attention de canal, les deux descripteurs 1 1 C sont d'abord épissés puis entrés dans le MLP, et les poids des deux couches MLP ne sont pas partagés, ce qui semble un peu problématique. En raison de la limite de mots, un autre code sera mis sur le prochain blog.
J'ai mis ce module dans l'un de mes réseaux de neurones à quatre couches pour la classification MNIST manuscrite, mais je ne pense pas que la précision ait été beaucoup améliorée, et j'ai l'impression intuitivement qu'elle fonctionne plus lentement. Dans l'article, en insérant CBAM dans certains grands réseaux, je pense que l'amélioration des performances n'est pas particulièrement importante. . .

Je suppose que tu aimes

Origine blog.csdn.net/qq_43265072/article/details/106057548
conseillé
Classement