• 可堆叠的残差注意力模块用于图像分类(Residual Attention Network for Image Classification——代码复现与解读)


    1.模型介绍

            该模型设计的思想就是利用attention机制,在普通ResNet网络中,增加侧分支,侧分支通过一系列的卷积和池化操作,逐渐提取高层特征并增大模型的感受野,前面已经说过高层特征的激活对应位置能够反映attention的区域,然后再对这种具有attention特征的feature map进行上采样,使其大小回到原始feature map的大小,就将attention对应到原始图片的每一个位置上,这个feature map叫做 attention map,与原来的feature map 进行element-wise product的操作,相当于一个权重器,增强有意义的特征,抑制无意义的信息。

    论文中模型的结构如下图所示。

            最上面红色箭头标记的流程,就是一个普通的残差网络,(这个分支其实为主干分支可以加传统的 resnet,ResNetXt,Inception 网络等)。然后在残差块的部分位置,加入另外的分支(即为灰色部分),构成一个整体的Attention Module,下面对Attention Module做具体分析。

     

    一个Attention Module分为两个分支,右边的分支就是普通的卷积网络,即主干分支,叫做Trunk Branch。左边的分支是为了得到一个掩码mask,该掩码的作用是得到输入特征x的attention map,所以叫做Mask Branch,这个Mask Branch包含down sample和up sample的过程,目的是为了保证和右边分支的输出大小一致。

    得到Attention map的mask以后,一个比较naive的方法就是直接用mask和主干分支进行一个element-wise product的操作,即M(x) * T(x),来对特征做一次权重操作。但是这样导致的问题就是:

    M(x)的掩码是通过最后的sigmoid函数得到的,M(x)值在[0, 1]之间,连续多个Module模块直接相乘的话会导致feature map的值越来越小,同时也有可能打破原有网络的特性,使得网络的性能降低

    于是就有了如下的改进:Attention Residual Learning

    前面已经说了直接进行element-wise product操作会使得性能降低,那么比较好的方式就借鉴ResNet恒等映射的方法:

    其中M(x)为Soft Mask Branch的输出,F(x)为Trunk Branch的输出,那么当M(x)=0时,该层的输入就等于F(x),因此该层的效果不可能比原始的F(x)差,这一点也借鉴了ResNet中恒等映射的思想,同时这样的加法,也使得Trunk Branch输出的feature map中显著的特征更加显著,增加了特征的判别性。此外, attention residual learning 既能很好地保留原始特征的特性,又能使原始特征具有绕过soft Mask Branch分支的能力,从而直接前馈(forward)到最顶层来削弱 mask 分支的特征筛选能力。经过这种残差结构的堆叠,能够很容易的将模型的深度达到很深的层次,具有非常好的性能。

    2.代码实现

     注意力模块:

    1. def attention_block(input, input_channels=None, output_channels=None, encoder_depth=1):
    2. p = 1
    3. t = 2
    4. r = 1
    5. if input_channels is None:
    6. input_channels = input.get_shape()[-1].value
    7. if output_channels is None:
    8. output_channels = input_channels
    9. # First Residual Block
    10. for i in range(p):
    11. input = residual_block(input)
    12. # Trunc Branch
    13. output_trunk = input
    14. for i in range(t):
    15. output_trunk = residual_block(output_trunk)
    16. # Soft Mask Branch
    17. ## encoder
    18. ### first down sampling
    19. output_soft_mask = MaxPool2D(padding='same')(input) # 32x32
    20. for i in range(r):
    21. output_soft_mask = residual_block(output_soft_mask)
    22. skip_connections = []
    23. for i in range(encoder_depth - 1):
    24. ## skip connections
    25. output_skip_connection = residual_block(output_soft_mask)
    26. skip_connections.append(output_skip_connection)
    27. # print ('skip shape:', output_skip_connection.get_shape())
    28. ## down sampling
    29. output_soft_mask = MaxPool2D(padding='same')(output_soft_mask)
    30. for _ in range(r):
    31. output_soft_mask = residual_block(output_soft_mask)
    32. ## decoder
    33. skip_connections = list(reversed(skip_connections))
    34. for i in range(encoder_depth - 1):
    35. ## upsampling
    36. for _ in range(r):
    37. output_soft_mask = residual_block(output_soft_mask)
    38. output_soft_mask = UpSampling2D()(output_soft_mask)
    39. ## skip connections
    40. output_soft_mask = Add()([output_soft_mask, skip_connections[i]])
    41. ### last upsampling
    42. for i in range(r):
    43. output_soft_mask = residual_block(output_soft_mask)
    44. output_soft_mask = UpSampling2D()(output_soft_mask)
    45. ## Output
    46. output_soft_mask = Conv2D(input_channels, (1, 1))(output_soft_mask)
    47. output_soft_mask = Conv2D(input_channels, (1, 1))(output_soft_mask)
    48. output_soft_mask = Activation('sigmoid')(output_soft_mask)
    49. # Attention: (1 + output_soft_mask) * output_trunk
    50. output = Lambda(lambda x: x + 1)(output_soft_mask)
    51. output = Multiply()([output, output_trunk]) #
    52. # Last Residual Block
    53. for i in range(p):
    54. output = residual_block(output)
    55. return output

    整个浅层的模型结构:

    1. def AttentionResNet10(shape=(32, 32, 3), n_channels=32, n_classes=10):
    2. input_ = Input(shape=shape)
    3. x = Conv2D(n_channels, (5, 5), padding='same')(input_)
    4. x = BatchNormalization()(x)
    5. x = Activation('relu')(x)
    6. x = MaxPool2D(pool_size=(2, 2))(x) # 16x16
    7. x = residual_block(x, input_channels=32, output_channels=128)
    8. x = attention_block(x, encoder_depth=2)
    9. x = residual_block(x, input_channels=128, output_channels=256, stride=2) # 8x8
    10. x = attention_block(x, encoder_depth=1)
    11. x = residual_block(x, input_channels=256, output_channels=512, stride=2) # 4x4
    12. x = attention_block(x, encoder_depth=1)
    13. x = residual_block(x, input_channels=512, output_channels=1024)
    14. x = residual_block(x, input_channels=1024, output_channels=1024)
    15. x = residual_block(x, input_channels=1024, output_channels=1024)
    16. x = AveragePooling2D(pool_size=(4, 4), strides=(1, 1))(x) # 1x1
    17. x = Flatten()(x)
    18. output = Dense(n_classes, activation='softmax')(x)
    19. model = Model(input_, output)
    20. return model

    模型调用函数:在这里调用封装好的CIFAR10图形识别数据,CIFAR10数据集共有60000张彩色图像,这些图像式32*32*3,分为10个类,每个类6000张。

    1. import keras
    2. from IPython.display import SVG
    3. from keras.utils.vis_utils import model_to_dot
    4. from keras.datasets import cifar10
    5. from keras.utils import to_categorical
    6. from keras.preprocessing.image import ImageDataGenerator
    7. from keras.callbacks import ReduceLROnPlateau, EarlyStopping
    8. from .models import AttentionResNet
    9. # 加载数据集
    10. (x_train, y_train), (x_test, y_test) = cifar10.load_data()
    11. y_train = to_categorical(y_train)
    12. y_test = to_categorical(y_test)
    13. # define generators for training and validation data
    14. train_datagen = ImageDataGenerator(
    15. featurewise_center=True,
    16. featurewise_std_normalization=True,
    17. rotation_range=20,
    18. width_shift_range=0.2,
    19. height_shift_range=0.2,
    20. zoom_range=0.2,
    21. horizontal_flip=True)
    22. val_datagen = ImageDataGenerator(
    23. featurewise_center=True,
    24. featurewise_std_normalization=True)
    25. # 计算特征归一化所需的函数
    26. # (std, mean, and principal components if ZCA whitening is applied)
    27. train_datagen.fit(x_train)
    28. val_datagen.fit(x_train)
    29. # build a model
    30. model = AttentionResNet(n_classes=10)
    31. # define loss, metrics, optimizer
    32. model.compile(keras.optimizers.Adam(lr=0.0001), loss='categorical_crossentropy', metrics=['accuracy'])
    33. # fits the model on batches with real-time data augmentation
    34. batch_size = 32
    35. model.fit_generator(train_datagen.flow(x_train, y_train, batch_size=batch_size),
    36. steps_per_epoch=len(x_train)//batch_size, epochs=200,
    37. validation_data=val_datagen.flow(x_test, y_test, batch_size=batch_size),
    38. validation_steps=len(x_test)//batch_size,
    39. callbacks=callbacks, initial_epoch=0)

    全部代码链接:

    https://download.csdn.net/download/weixin_40651515/86309657

    参考资料链接:

    1.https://zhuanlan.zhihu.com/p/36838135

    2.https://arxiv.org/pdf/1704.06904.pdf

  • 相关阅读:
    删除排序链表中的重复元素
    如何将两台Mac显示器设置为单个屏幕配置
    druid keepAlive 导致数据库连接数飙升
    C语言字符串比较详解:strcmp和strncmp函数的使用方法和规则
    通关GO语言13 参数传递:值、引用及指针之间的区别?
    Java如何实现消费数据隔离?
    smallWhiteDot Tech Suppor
    Linux串口信息查询
    (附源码)python飞机票销售系统 毕业设计 141432
    面向OLAP的列式存储DBMS-10-[ClickHouse]的常用数组操作
  • 原文地址:https://blog.csdn.net/weixin_40651515/article/details/126141396