ResNet50,keras實現 ,轉了那麼久,發現,額,還是最初的程式碼寫的好
阿新 • • 發佈:2021-01-04
技術標籤: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