注意力机制的两种模块SEblock 和 CBAM模块

1.SENet模块
def SE_moudle(input_xs,reduction_ratio = 16.):
    shape = input_xs.get_shape().as_list()
    se_module = tf.reduce_mean(input_xs,[1,2])
    #第一个Dense:shape[-1]/reduction_ratio:即把input_channel再除以reduction_ratio,使channel下降到指定维度数
    se_module = tf.keras.layers.Dense(shape[-1]/reduction_ratio,activation=tf.nn.relu)(se_module)
    #第二个Dense:重新回升到与input_channel相同的原始维度数
    se_module = tf.keras.layers.Dense(shape[-1], activation=tf.nn.relu)(se_module)
    se_module = tf.nn.sigmoid(se_module)
    se_module = tf.reshape(se_module,[-1,1,1,shape[-1]])
    out_ys = tf.multiply(input_xs,se_module)
    return out_ys
2.加载SENet模块的Resnet

 

def identity_block(input_xs, out_dim, with_shortcut_conv_BN=False):
    if with_shortcut_conv_BN:
        pass
    else:
        #返回与input的形状和内容均相同的张量,即shortcut等同于input_xs
        shortcut = tf.identity(input_xs)
    #input输入的channel数
    input_channel = input_xs.get_shape().as_list()[-1]
    #如果输入的channel数不等于输出的channel数的话
    if input_channel != out_dim:
        #求输出的channel数减去输入的channel数的绝对值,作为pad填充值
        pad_shape = tf.abs(out_dim - input_channel)
        #name="padding"表示给该填充操作赋予名称为"padding"。使用了默认参数mode='CONSTANT'和constant_values=0,表示填充默认值0。
        #第二个参数为paddings填充的形状:即分别的批量维度、高、宽的维度上都不作填充,在channel维度上填充pad_shape//2的数量。
        shortcut = tf.pad(shortcut, [[0, 0], [0, 0], [0, 0], [pad_shape // 2, pad_shape // 2]], name="padding")
    #残差卷积块中的3个Conv2D卷积的卷积核大小分别为1x1、3x3、1x1
    conv = tf.keras.layers.Conv2D(filters=out_dim // 4, kernel_size=1, padding="SAME", activation=tf.nn.relu)(input_xs)
    conv = tf.keras.layers.BatchNormalization()(conv)
    conv = tf.keras.layers.Conv2D(filters=out_dim // 4, kernel_size=3, padding="SAME", activation=tf.nn.relu)(conv)
    conv = tf.keras.layers.BatchNormalization()(conv)
    conv = tf.keras.layers.Conv2D(filters=out_dim // 4, kernel_size=1, padding="SAME", activation=tf.nn.relu)(conv)
    conv = tf.keras.layers.BatchNormalization()(conv)
    #下面开始加载SENet模块
    #返回的为[批量维度、高、宽、channel维度]
    shape = conv.get_shape().as_list()
    #默认参数为keepdims=False的话,不会再保留运算所在的维度。设置keepdims=True的话,会保留运算所在的维度为1。
    #[批量维度、高、宽、channel维度]经过reduce_mean后转换为[批量维度、channel维度]
    se_module = tf.reduce_mean(conv, [1, 2])
    #第一个Dense:shape[-1]/reduction_ratio:即把input_channel再除以reduction_ratio,使channel下降到指定维度数
    se_module = tf.keras.layers.Dense(shape[-1] / 16, activation=tf.nn.relu)(se_module)
    #第二个Dense:重新回升到与input_channel相同的原始维度数
    se_module = tf.keras.layers.Dense(shape[-1], activation=tf.nn.relu)(se_module)
    se_module = tf.nn.sigmoid(se_module)
    #把[批量维度、channel维度]重新转换为[批量维度、高、宽、channel维度],即[批量维度、1、1、channel维度]
    se_module = tf.reshape(se_module, [-1, 1, 1, shape[-1]])
    #multiply元素乘法:SENet模块输出值se_module 和 残差卷积输出conv(即SENet模块输入值conv)
    se_module = tf.multiply(conv, se_module)
    #残差连接:对残差的原始输入shortcut(即input_xs) 与 SENet模块输出值se_module 进行求和
    output_ys = tf.add(shortcut, se_module)
    output_ys = tf.nn.relu(output_ys)
    return output_ys

3. CBAM模块

def cbam_module(input_xs, reduction_ratio=0.5):
    #分别获取批量大小、通道数,通道数作为隐藏层的神经元数量
    batch_size, hidden_num = input_xs.get_shape().as_list()[0], input_xs.get_shape().as_list()[3]
    ### 第一步:Channel Attention 模块 ###
    #1.默认参数为keepdims=False的话,不会再保留运算所在的维度。设置keepdims=True的话,会保留运算所在的维度为1。
    #  连续两次的reduce_max/reduce_mean,实际都是先将[批量维度、高、宽、channel维度]转换为[批量维度、1、宽、channel维度],
    #  再转换为[批量维度、1、1、channel维度]。
    #2.首先对Channel Attention 模块的输入数据分别进行全局池化(reduce_max)和平均池化(reduce_mean)两种操作。
    maxpool_channel = tf.reduce_max(tf.reduce_max(input_xs, axis=1, keepdims=True), axis=2, keepdims=True)
    avgpool_channel = tf.reduce_mean(tf.reduce_mean(input_xs, axis=1, keepdims=True), axis=2, keepdims=True)
    maxpool_channel = tf.keras.layers.Flatten()(maxpool_channel)
    avgpool_channel = tf.keras.layers.Flatten()(avgpool_channel)
    #使用2个连续的全连接层对全局池化(reduce_max)后的特征进行提取。
    #reduction_ratio为0.5,即第1个全连接层的神经元数量为输入数据的输入数据的通道数的一半,然后第2个全连接层神经元数量重新恢复为输入数据的通道数
    mlp_1_max = tf.keras.layers.Dense(units=int(hidden_num * reduction_ratio), activation=tf.nn.relu)(maxpool_channel)
    mlp_2_max = tf.keras.layers.Dense(units=hidden_num)(mlp_1_max)
    #[批量维度、1、1、channel维度],此处的隐藏层的神经元数量hidden_num等于输入数据的通道数
    mlp_2_max = tf.reshape(mlp_2_max, [-1, 1, 1, hidden_num])
    #使用2个连续的全连接层对平均池化(reduce_mean)后的特征进行提取。
    #reduction_ratio为0.5,即第1个全连接层的神经元数量为输入数据的输入数据的通道数的一半,然后第2个全连接层神经元数量重新恢复为输入数据的通道数
    mlp_1_avg = tf.keras.layers.Dense(units=int(hidden_num * reduction_ratio), activation=tf.nn.relu)(avgpool_channel)
    mlp_2_avg = tf.keras.layers.Dense(units=hidden_num, activation=tf.nn.relu)(mlp_1_avg)
    #[批量维度、1、1、channel维度],此处的隐藏层的神经元数量hidden_num等于输入数据的通道数
    mlp_2_avg = tf.reshape(mlp_2_avg, [-1, 1, 1, hidden_num])
    #把“对全局池化(reduce_max)提取后的”特征和“对平均池化(reduce_mean)提取后的”特征进行求和,然后通过sigmoid激活归一化到0到1之间
    channel_attention = tf.nn.sigmoid(mlp_2_max + mlp_2_avg)
    #把“通过sigmoid激活归一化的”值和Channel Attention 模块的输入数据进行内积计算,其最终计算结果值作为后面的Spatial Attention 模块的输入
    channel_refined_feature = input_xs * channel_attention

    ### 第二步:Spatial Attention 模块 ###
    #1.首先把Channel Attention 模块的输出作为Spatial Attention 模块的输入,然后对输入数据分别进行全局池化(reduce_max)和平均池化(reduce_mean)两种操作。
    #2.默认参数为keepdims=False的话,不会再保留运算所在的维度。设置keepdims=True的话,会保留运算所在的维度为1。
    #3.全局池化(reduce_max):把[批量维度、高、宽、channel维度]转换为[批量维度、高、宽、1]
    maxpool_spatial = tf.reduce_max(channel_refined_feature, axis=3, keepdims=True)
    #平均池化(reduce_mean):把[批量维度、高、宽、channel维度]转换为[批量维度、高、宽、1]
    avgpool_spatial = tf.reduce_mean(channel_refined_feature, axis=3, keepdims=True)
    #把全局池化(reduce_max)和平均池化(reduce_mean)后的数据在channel维度上进行合并,最终转换为[批量维度、高、宽、2]
    max_avg_pool_spatial = tf.concat([maxpool_spatial, avgpool_spatial], axis=3)
    #目的是将数据的维度降维为1,以便后面的sigmoid激活归一化计算
    conv_layer = tf.keras.layers.Conv2D(filters=1, kernel_size=(3, 3), padding="same", activation=None)(max_avg_pool_spatial)
    #通过sigmoid激活归一化到0到1之间
    spatial_attention = tf.nn.sigmoid(conv_layer)
    #将Spatial Attention 模块的输出channel_refined_feature 和Spatial Attention 模块的输出spatial_attention 进行内积计算,
    #其最终计算结果值作为CBAM注意力机制模块的输出值。
    refined_feature = channel_refined_feature * spatial_attention
    #将CBAM注意力机制模块的输入值input_xs和输出值refined_feature进行求和得出最终的结果
    output_layer = refined_feature + input_xs
    return output_layer

4.加载了CBAM模块的Resnet

def identity_block(input_xs, out_dim, with_shortcut_conv_BN=False):
    layer_depth=20 #层的深度
    if with_shortcut_conv_BN:
        pass
    else:
        #返回与input的形状和内容均相同的张量,即shortcut等同于input_xs
        shortcut = tf.identity(input_xs)
    #input输入的channel数
    input_channel = input_xs.get_shape().as_list()[-1]
    #如果输入的channel数不等于输出的channel数的话
    if input_channel != out_dim:
        #求输出的channel数减去输入的channel数的绝对值,作为pad填充值
        pad_shape = tf.abs(out_dim - input_channel)
        #name="padding"表示给该填充操作赋予名称为"padding"。使用了默认参数mode='CONSTANT'和constant_values=0,表示填充默认值0。
        #第二个参数为paddings填充的形状:即分别的批量维度、高、宽的维度上都不作填充,在channel维度上填充pad_shape//2的数量。
        shortcut = tf.pad(shortcut, [[0, 0], [0, 0], [0, 0], [pad_shape // 2, pad_shape // 2]], name="padding")
    #残差卷积块中的3个Conv2D卷积的卷积核大小分别为1x1、3x3、1x1
    conv = tf.keras.layers.Conv2D(filters=out_dim // 4, kernel_size=1, padding="SAME", activation=tf.nn.relu)(input_xs)
    conv = tf.keras.layers.BatchNormalization()(conv)
    conv = tf.keras.layers.Conv2D(filters=out_dim // 4, kernel_size=3, padding="SAME", activation=tf.nn.relu)(conv)
    conv = tf.keras.layers.BatchNormalization()(conv)
    conv = tf.keras.layers.Conv2D(filters=out_dim // 4, kernel_size=1, padding="SAME", activation=tf.nn.relu)(conv)
    conv = tf.keras.layers.BatchNormalization()(conv)
    conv = tf.layers.conv2d(conv, out_dim, [1, 1], strides=[1, 1], kernel_initializer=tf.variance_scaling_initializer,
                  bias_initializer=tf.zeros_initializer, name="conv{}_2_1x1".format(str(layer_depth)))
    conv = tf.layers.batch_normalization(conv)
    # ResNet中加载的CBAM模块
    conv = cbam_module(conv)
    #残差连接:对残差的原始输入shortcut(即input_xs) 与 CBAM模块输出值conv进行求和
    output_ys = shortcut + conv
    output_ys = tf.nn.relu(output_ys)
    return output_ys

 

你可能感兴趣的:(深度学习理解篇)