tensorflow2.0——ShortCut結構、BottleNeck結構與ResNet50網絡


一、ShortCut結構

  ResNet神經網絡中有一種ShortCut Connection網絡結構,主要用的是跳遠連接的方式來解決深層神經網絡退化的問題,在跳遠連接的后需要對輸入與激活前的值進行相加,激活前的值y可能與輸入值的shape相同(稱為identity block),也可能不相同(稱為cov block),所以有ResNet有兩種方式,當shape不相同時,用1*1的卷積操作來處理,一般來說1*1的卷積對神經網絡特征的學習作用不大,通常用來做shape的調整。

  如下圖,輸入是265通道的圖,虛線表示shape不同的ResNet操作,實線表示shape相同的ResNet操作。

  

 

 

二、ShortCut實現

   用cifar10數據集對的shape進行實現,跳遠結構也比較簡單,和上一節的圖示一樣只是跳了兩層卷積,如何實現維度不同的的跳遠連接操作呢,可以通過對比ResNet的輸入與卷積之后的輸出的shape即可,如下代碼:

    # 判斷輸入和輸出是否有相同的channel,不相同則用1*1卷積進行操作
    if block_input.shape[-1] == cov_output.shape[-1]:
        block_out = block_input + cov_output
        return block_out
    else:
        block_out = block_input + Covunit(cov_output, cov_output.shape[-1], [1 * 1])    # 當維度不相同時用1*1的卷積處理
        return block_out

代碼示例:

import tensorflow as tf


# 創建卷積結構單元的函數
def Covunit(unit_input, filters, kernel_size, activation=None):
    '''
    :param unit_input: 卷積核輸入,tensor
    :param filters: 卷積核個數
    :param kernel_size: 卷積核大小
    :param activation: 激活函數,若為0則不激活
    :return:卷積后輸出的tensor
    '''

    unit_cov = tf.keras.layers.Conv2D(filters, kernel_size, padding='same')(unit_input)  # 卷積,不改變長於寬
    unit_bn = tf.keras.layers.BatchNormalization()(unit_cov)  # 批標准化

    if activation:
        unit_act = tf.keras.layers.Activation(activation)(unit_bn)
    else:
        unit_act = unit_bn

    return unit_act


# ResNet函數,網絡結構中可能有多個這樣的結構塊,所以需要如此操作
def ShortCut(block_input, filters=None, activations=None):
    '''
    :param block_input: ResNet結構的輸入
    :param filters: 該ResNet結構每個卷積層的filters個數
    :param filters: 該ResNet結構每個卷積層的激活函數,最后一層不需要激活函數
    :return: Inceptionblock結構輸出的tensor
    '''

    # 如果有多個卷積操作,則依次構建卷積操作
    if filters:
        layer_input = block_input  # 定義卷積層的輸入,初始為block_input
        for filters, activation in zip(filters, activations):
            layer_input = Covunit(unit_input=layer_input, filters=filters, kernel_size=[3, 3],
                                  activation=activations)
        cov_output = layer_input  # 卷積層的輸出
    else:
        cov_output = block_input  # 無卷積層的輸出

    # 判斷輸入和輸出是否有相同的channel,不相同則用1*1卷積進行操作
    if block_input.shape[-1] == cov_output.shape[-1]:
        block_out = block_input + cov_output
        return block_out
    else:
        block_out = block_input + Covunit(cov_output, cov_output.shape[-1], [1 * 1])  # 當維度不相同時用1*1的卷積處理
        return block_out

 

三、BottleNeck結構

  普通的shortcut(如下左圖)參數的計算量比較大,這個時候可以使用bottleneck瓶頸結構(如下右圖)來減少參數量,這種替換有點類似於用兩個3*3卷積替換一個5*5的卷積的情況。

 

 

 

四、ResNet50網絡

  ResNet50網絡是一個非常典型的利用瓶頸結構進行深度網絡構建的,其結構如下圖:

 

  這里有如下注意事項:

  1. 在進行layername=COV1前需要對輸入進行3*3padding stride 2的零填充操作。
  2. 每一個stage的第一個瓶頸層應為cov block,用來做參數結構調整,其余瓶頸層應為identity block。如cov2_x有3個瓶頸層,第一個瓶頸層應該是cov block,剩余的兩個位你identity block。

 

五、ResNet50網絡的實現

  考慮到存在兩種不同的跳遠連接,所以這里定義cov_block與identity_block兩個函數。

代碼示例

 

from tensorflow.keras import layers


def cov_block(input_tensor, filters, kernel_size=(3, 3), strides=(1, 1), name=None):
    '''
    resnet網絡中,跳遠連接后,input與output的通道數不同的瓶頸卷積塊
    input_tensor:輸入tensor
    filters:瓶頸結構各個卷積的通道數,如[64,64,265]
    '''

    filter1, filter2, filter3 = filters  # 瓶頸結構個各個卷積層的卷積核數

    # 瓶頸結構1*1卷積層
    x = layers.Conv2D(filter1, (1, 1), strides=strides, name=name + 'bot_cov1')(input_tensor)
    x = layers.BatchNormalization(name=name + 'bottle_cov1_bn')(x)
    x = layers.Activation('relu')(x)

    # 瓶頸結構3*3卷積層
    x = layers.Conv2D(filter2, kernel_size, strides=strides, name=name + 'bot_cov2')(x)
    x = layers.BatchNormalization(name=name + 'bottle_cov2_bn')(x)
    x = layers.Activation('relu')(x)

    # 瓶頸結構1*1卷積層
    x = layers.Conv2D(filter3, (1, 1), strides=strides, name=name + 'bot_cov3')(x)
    x = layers.BatchNormalization(name=name + 'bottle_cov3_bn')(x)

    # 跳遠結構,調整input與output相同的shape
    shortcut = layers.Conv2D(filter3, (1, 1), strides, 1, name=name + 'bot_shortcut')(input_tensor)
    shortcut = layers.BatchNormalization(name=name + 'bottle_cov3_bn')(shortcut)

    # 輸出
    x = layers.add([x, shortcut])
    x = layers.Activation('relu')(x)

    return x


def identity_block(input_tensor, filters, kernel_size=(3, 3), strides=(1, 1), name=None):
    '''
    resnet網絡中,跳遠連接后,input與output的通道數相同的瓶頸卷積塊
    input_tensor:輸入tensor
    filters:瓶頸結構各個卷積的通道數,如[64,64,265]
    '''

    filter1, filter2, filter3 = filters  # 瓶頸結構個各個卷積層的卷積核數

    # 瓶頸結構1*1卷積層
    x = layers.Conv2D(filter1, (1, 1), strides=strides, name=name + 'bot_cov1')(input_tensor)
    x = layers.BatchNormalization(name=name + 'bottle_cov1_bn')(x)
    x = layers.Activation('relu')(x)

    # 瓶頸結構3*3卷積層
    x = layers.Conv2D(filter2, kernel_size, strides=strides, name=name + 'bot_cov2')(x)
    x = layers.BatchNormalization(name=name + 'bottle_cov2_bn')(x)
    x = layers.Activation('relu')(x)

    # 瓶頸結構1*1卷積層
    x = layers.Conv2D(filter3, (1, 1), strides=strides, name=name + 'bot_cov3')(x)
    x = layers.BatchNormalization(name=name + 'bottle_cov3_bn')(x)

    # 輸出,輸入與輸出shape相同,輸入在上一次卷積激活錢已經標准化,所以這里不需要再次標准化,直接相加
    x = layers.add([x, input_tensor])
    x = layers.Activation('relu')(x)

    return x


def resnet50(input):
    # resnet50-cov1: 3*3 padding, 7*7,64, stride 2
    x = layers.ZeroPadding2D((3, 3))(input)
    x = layers.Conv2D(64, (7, 7), strides=(2, 2), name='resnet_cov1')(x)
    x = layers.BatchNormalization(name='resnet_cov1_bn')(x)
    x = layers.Activation('relu')(x)

    # 3*3 maxpooling, stride 2
    x = layers.MaxPooling2D((3, 3), (2, 2))(x)

    # resnet50-cov2
    x = cov_block(input_tensor=x, filters=[64, 64, 256], kernel_size=(3, 3), strides=(1, 1), name='resnet_cov2_bot1')
    x = identity_block(input_tensor=x, filters=[64, 64, 256], kernel_size=(3, 3), strides=(1, 1),
                       name='resnet_cov2_bot2')
    x = identity_block(input_tensor=x, filters=[64, 64, 256], kernel_size=(3, 3), strides=(1, 1),
                       name='resnet_cov2_bot3')

    # resnet50-cov3
    x = cov_block(x, [128, 128, 512], (3, 3), (1, 1), 'resnet_cov3_bot1')
    x = identity_block(x, [128, 128, 512], (3, 3), (1, 1), 'resnet_cov3_bot2')
    x = identity_block(x, [128, 128, 512], (3, 3), (1, 1), 'resnet_cov3_bot3')
    x = identity_block(x, [128, 128, 512], (3, 3), (1, 1), 'resnet_cov3_bot4')

    # resnet50-cov4
    x = cov_block(x, [256, 256, 1024], (3, 3), (1, 1), 'resnet_cov4_bot1')
    x = identity_block(x, [256, 256, 1024], (3, 3), (1, 1), 'resnet_cov4_bot2')
    x = identity_block(x, [256, 256, 1024], (3, 3), (1, 1), 'resnet_cov4_bot3')
    x = identity_block(x, [256, 256, 1024], (3, 3), (1, 1), 'resnet_cov4_bot4')
    x = identity_block(x, [256, 256, 1024], (3, 3), (1, 1), 'resnet_cov4_bot5')
    x = identity_block(x, [256, 256, 1024], (3, 3), (1, 1), 'resnet_cov4_bot6')

    # resnet50-cov5
    x = cov_block(x, [512, 512, 2048], (3, 3), (1, 1), 'resnet_cov5_bot1')
    x = identity_block(x, [512, 512, 2048], (3, 3), (1, 1), 'resnet_cov5_bot2')
    x = identity_block(x, [512, 512, 2048], (3, 3), (1, 1), 'resnet_cov5_bot3')

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM