1. 程式人生 > 其它 >ResNet50,keras實現 ,轉了那麼久,發現,額,還是最初的程式碼寫的好

ResNet50,keras實現 ,轉了那麼久,發現,額,還是最初的程式碼寫的好

技術標籤:keras系列

1.恆等塊(Identity block)

skip connection 為2層

在這裡插入圖片描述

跳連線為三層

在這裡插入圖片描述

對應的封裝程式

def identity_block(X, f, filters, stage, block):
    """
    實現圖3的恆等塊
    
    引數:
        X - 輸入的tensor型別的資料,維度為( m, n_H_prev, n_W_prev, n_H_prev )
        f - 整數,指定主路徑中間的CONV視窗的維度
        filters - 整數列表,定義了主路徑每層的卷積層的過濾器數量
        stage - 整數,根據每層的位置來命名每一層,與block引數一起使用。
        block - 字串,據每層的位置來命名每一層,與stage引數一起使用。
        
    返回:
        X - 恆等塊的輸出,tensor型別,維度為(n_H, n_W, n_C)
    
    """
#定義命名規則 conv_name_base = "res" + str(stage) + block + "_branch" bn_name_base = "bn" + str(stage) + block + "_branch" #獲取過濾器 F1, F2, F3 = filters #儲存輸入資料,將會用於為主路徑新增捷徑 X_shortcut = X #主路徑的第一部分 ##卷積層 X =
Conv2D(filters=F1, kernel_size=(1,1), strides=(1,1) ,padding="valid", name=conv_name_base+"2a", kernel_initializer=glorot_uniform(seed=0))(X) ##歸一化 X = BatchNormalization(axis=3,name=bn_name_base+"2a")(X) ##使用ReLU啟用函式 X = Activation("relu"
)(X) #主路徑的第二部分 ##卷積層 X = Conv2D(filters=F2, kernel_size=(f,f),strides=(1,1), padding="same", name=conv_name_base+"2b", kernel_initializer=glorot_uniform(seed=0))(X) ##歸一化 X = BatchNormalization(axis=3,name=bn_name_base+"2b")(X) ##使用ReLU啟用函式 X = Activation("relu")(X) #主路徑的第三部分 ##卷積層 X = Conv2D(filters=F3, kernel_size=(1,1), strides=(1,1), padding="valid", name=conv_name_base+"2c", kernel_initializer=glorot_uniform(seed=0))(X) ##歸一化 X = BatchNormalization(axis=3,name=bn_name_base+"2c")(X) ##沒有ReLU啟用函式 #最後一步: ##將捷徑與輸入加在一起 X = Add()([X,X_shortcut]) ##使用ReLU啟用函式 X = Activation("relu")(X) return X

2. 卷積塊

殘差網路的卷積塊是另一種型別的殘差塊,它適用於輸入輸出的維度不一致的情況,它不同於上面的恆等塊,與之區別在於,捷徑中有一個CONV2D層,如下圖

在這裡插入圖片描述

def convolutional_block(X, f, filters, stage, block, s=2):
    """
    實現圖5的卷積塊
    
    引數:
        X - 輸入的tensor型別的變數,維度為( m, n_H_prev, n_W_prev, n_C_prev)
        f - 整數,指定主路徑中間的CONV視窗的維度
        filters - 整數列表,定義了主路徑每層的卷積層的過濾器數量
        stage - 整數,根據每層的位置來命名每一層,與block引數一起使用。
        block - 字串,據每層的位置來命名每一層,與stage引數一起使用。
        s - 整數,指定要使用的步幅
    
    返回:
        X - 卷積塊的輸出,tensor型別,維度為(n_H, n_W, n_C)
    """
    
    #定義命名規則
    conv_name_base = "res" + str(stage) + block + "_branch"
    bn_name_base   = "bn"  + str(stage) + block + "_branch"
    
    #獲取過濾器數量
    F1, F2, F3 = filters
    
    #儲存輸入資料
    X_shortcut = X
    
    #主路徑
    ##主路徑第一部分
    X = Conv2D(filters=F1, kernel_size=(1,1), strides=(s,s), padding="valid",
               name=conv_name_base+"2a", kernel_initializer=glorot_uniform(seed=0))(X)
    X = BatchNormalization(axis=3,name=bn_name_base+"2a")(X)
    X = Activation("relu")(X)
    
    ##主路徑第二部分
    X = Conv2D(filters=F2, kernel_size=(f,f), strides=(1,1), padding="same",
               name=conv_name_base+"2b", kernel_initializer=glorot_uniform(seed=0))(X)
    X = BatchNormalization(axis=3,name=bn_name_base+"2b")(X)
    X = Activation("relu")(X)
    
    ##主路徑第三部分
    X = Conv2D(filters=F3, kernel_size=(1,1), strides=(1,1), padding="valid",
               name=conv_name_base+"2c", kernel_initializer=glorot_uniform(seed=0))(X)
    X = BatchNormalization(axis=3,name=bn_name_base+"2c")(X)
    
    #捷徑
    X_shortcut = Conv2D(filters=F3, kernel_size=(1,1), strides=(s,s), padding="valid",
               name=conv_name_base+"1", kernel_initializer=glorot_uniform(seed=0))(X_shortcut)
    X_shortcut = BatchNormalization(axis=3,name=bn_name_base+"1")(X_shortcut)
    
    #最後一步
    X = Add()([X,X_shortcut])
    X = Activation("relu")(X)
    
    return X

3.搭建ResNet50

這個圖的話,關注第一個

第一個是零填充,好像也可以不用哦,就設定為padding = ‘same’模式就行
但是可能是為了後面的封裝方便,所有的函式裡寫的都是’valid’,其實就不用改了

在這裡插入圖片描述

程式如下:

def ResNet50(input_shape=(64,64,3),classes=6):
    """
    實現ResNet50
    CONV2D -> BATCHNORM -> RELU -> MAXPOOL -> CONVBLOCK -> IDBLOCK*2 -> CONVBLOCK -> IDBLOCK*3
    -> CONVBLOCK -> IDBLOCK*5 -> CONVBLOCK -> IDBLOCK*2 -> AVGPOOL -> TOPLAYER
    
    引數:
        input_shape - 影象資料集的維度
        classes - 整數,分類數
        
    返回:
        model - Keras框架的模型
        
    """
    
    #定義tensor型別的輸入資料
    X_input = Input(input_shape)
    
    #0填充
    X = ZeroPadding2D((3,3))(X_input)
    
    #stage1
    X = Conv2D(filters=64, kernel_size=(7,7), strides=(2,2), name="conv1",
               kernel_initializer=glorot_uniform(seed=0))(X)
    X = BatchNormalization(axis=3, name="bn_conv1")(X)
    X = Activation("relu")(X)
    X = MaxPooling2D(pool_size=(3,3), strides=(2,2))(X)
    
    #stage2
    X = convolutional_block(X, f=3, filters=[64,64,256], stage=2, block="a", s=1)
    X = identity_block(X, f=3, filters=[64,64,256], stage=2, block="b")
    X = identity_block(X, f=3, filters=[64,64,256], stage=2, block="c")
    
    #stage3
    X = convolutional_block(X, f=3, filters=[128,128,512], stage=3, block="a", s=2)
    X = identity_block(X, f=3, filters=[128,128,512], stage=3, block="b")
    X = identity_block(X, f=3, filters=[128,128,512], stage=3, block="c")
    X = identity_block(X, f=3, filters=[128,128,512], stage=3, block="d")
    
    #stage4
    X = convolutional_block(X, f=3, filters=[256,256,1024], stage=4, block="a", s=2)
    X = identity_block(X, f=3, filters=[256,256,1024], stage=4, block="b")
    X = identity_block(X, f=3, filters=[256,256,1024], stage=4, block="c")
    X = identity_block(X, f=3, filters=[256,256,1024], stage=4, block="d")
    X = identity_block(X, f=3, filters=[256,256,1024], stage=4, block="e")
    X = identity_block(X, f=3, filters=[256,256,1024], stage=4, block="f")
    
    #stage5
    X = convolutional_block(X, f=3, filters=[512,512,2048], stage=5, block="a", s=2)
    X = identity_block(X, f=3, filters=[512,512,2048], stage=5, block="b")
    X = identity_block(X, f=3, filters=[512,512,2048], stage=5, block="c")
    
    #均值池化層
    X = AveragePooling2D(pool_size=(2,2),padding="same")(X)
    
    #輸出層
    X = Flatten()(X)
    X = Dense(classes, activation="softmax", name="fc"+str(classes),
              kernel_initializer=glorot_uniform(seed=0))(X)
    
    
    #建立模型
    model = Model(inputs=X_input, outputs=X, name="ResNet50")
    
    return model

參考來源