经典卷积网络进阶--ResNet详解
一.ResNet概述
resnet在2015名聲大噪,微軟公司提出了一種新的網絡結構---殘差網絡(resnet)。殘差模塊結構圖如下圖1,圖中曲線連接方式(X identity)稱為近道連接,這種連接方式直接跳過了權重層;經過權重層的連接方式(F(X))與近道連接(X identity)構成了殘差模塊
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 圖1
殘差網絡是由一系列殘差塊組成的(1式)。一個殘差塊可以用表示為:
?
殘差塊分成兩部分直接映射部分和殘差部分。h(x1)??是直接映射,反應在圖1中是左邊的曲線;?
F(X1,w1) 是殘差部分,一般由兩個或者三個卷積操作構成,即1式中右側包含卷積的部分。
殘差網絡結構允許網絡盡可能加深,較為常見的是:ResNet50和ResNet101。具體結構如下
從表中可以看出,所有ResNet網絡主要被分為5個部分。
殘差塊可以大致分成2種,一種有bottleneck結構,即下圖右中的1*1,3*3,1*1 的卷積層,用于先降維再升維,主要出于降低計算復雜度的現實考慮,稱之為“bottleneck block”,另一種沒有bottleneck結構,如下圖左所示,稱之為“basic block”。即下圖左中的basic block由2個3×3和3×3卷積層構成。
近道連接(shortcut)也分為兩種(如下圖是以兩個3*3的卷積核的殘差塊進行劃分恒等塊和卷積塊的),一種是近道連接中有卷積模塊,另一種是近道連接無卷積模塊,殘差塊根據近道連接是否有卷積模塊可以分為卷積塊(convolutional block)和恒等快(identity block)。為什么在近道連接中加入卷積塊?原因在于如果近道連接所連接的兩組數據的通道(channel)個數不同,則可以在近道連接中加入1*1卷積模塊對通道個數進行調整。
在本篇文章中主要分享ResNet50,在ResNet50中,為了減少參數計算量,會使用1*1的卷積核對輸入數據進行降維,再進行卷積運算,當輸出時同樣使用1*1的卷積核使數據維度恢復到輸入時的維度。
該ResNet50殘差模塊結構圖如下:輸入數據256維,在第一層1*1的卷積層中降維到64維,再經過3*3的卷積后,最后由1*1的卷積層將其恢復到256維
二.ResNet50實現MNIST分類
基于keras框架
代碼如下:
from keras.models import Model from keras.layers import Input,Dense,Dropout,Flatten,MaxPooling2D,Conv2D,AveragePooling2D,Activation,BatchNormalization,\ZeroPadding2D,Add from keras.initializers import glorot_uniform from keras.datasets import mnist from keras.utils import np_utils from matplotlib import pyplot as plt import numpy as np#數據集預處理 (X_train,Y_train),(X_test,Y_test)=mnist.load_data() X_test1=X_test Y_test1=Y_test #處理特征數據 X_train=X_train.reshape(-1,28,28,1).astype("float32")/255.0 X_test=X_test.reshape(-1,28,28,1).astype("float32")/255.0 # 處理標簽 Y_train=np_utils.to_categorical(Y_train,10) Y_test=np_utils.to_categorical(Y_test,10) print(X_train.shape) print(Y_train.shape) #搭建恒等快 #X代表輸入數據,f代表該恒等快的第二個卷積的大小,因為第一個和第三個卷積核大小都是1*1,stage代表第幾個卷積核,block代表卷積核的名字 def identity_block(X,f,filters,stage,block):#命名cov_name="res"+str(stage)+block+"branch"bn_name="bn"+str(stage)+block+"branch"F1,F2,F3=filters #該恒等快的各個卷積核的大小X_TEMP=X #保存輸入數據,為近道連接做準備#定義第一層卷積核X=Conv2D(filters=F1,kernel_size=(1,1),strides=1,padding="valid",name=cov_name+"2a",kernel_initializer=glorot_uniform(seed=0))(X)X=BatchNormalization(axis=3,name=bn_name+"2a")(X)X=Activation("relu")(X)#定義第二層卷積核X=Conv2D(filters=F2,kernel_size=(f,f),strides=1,padding="same",activation="relu",name=cov_name+"2b",kernel_initializer=glorot_uniform(seed=0))(X)X=BatchNormalization(axis=3,name=bn_name+"2b")(X)#定義第三層卷積核X=Conv2D(filters=F3,kernel_size=(1,1),strides=1,padding="valid",activation="relu",name=cov_name+"2c",kernel_initializer=glorot_uniform(seed=0))(X)X=BatchNormalization(axis=3,name=bn_name+"2c")(X) #歸一化#將近道連接與經過權重的輸出加起來X=Add()([X,X_TEMP])#經過激活函數輸出值X=Activation("relu")(X)return X#搭建卷積塊 def cov_block(X,f,filters,stage,block,s=2):#卷積塊命名cov_name="res"+str(stage)+block+"branch"bn_name="bn"+str(stage)+block+"branch"X_TEMP=XF1,F2,F3=filters#搭建固定的卷積模塊#搭建第一層的卷積核,步長為sX=Conv2D(filters=F1,kernel_size=(1,1),strides=s,activation="relu",name=cov_name+"2a",kernel_initializer=glorot_uniform(seed=0))(X)X=BatchNormalization(axis=3,name=bn_name+"2a")(X)#搭建第二層卷積核X=Conv2D(filters=F2,kernel_size=(f,f),strides=1,padding="same",activation="relu",name=cov_name+"2b",kernel_initializer=glorot_uniform(seed=0))(X)X=BatchNormalization(axis=3,name=bn_name+"2b")(X)#搭建第三層卷積核X=Conv2D(filters=F3,kernel_size=(1,1),strides=1,name=cov_name+"2c",kernel_initializer=glorot_uniform(seed=0))(X)X=BatchNormalization(axis=3,name=bn_name+"2c")(X)#搭建近道連接的卷積核,加入卷積層和歸一化層X_TEMP=Conv2D(filters=F3,kernel_size=(1,1),strides=(s,s),name=cov_name+"1",kernel_initializer=glorot_uniform(seed=0))(X_TEMP)X_TEMP=BatchNormalization(axis=3,name=bn_name+"1")(X_TEMP) #歸一化層#將近道連接與經過卷積的輸出加在一起X=Add()([X,X_TEMP])#激活層X=Activation("relu")(X)return X #利用恒等快和卷積塊搭建resnet50網絡結構 def resnet():X_input=Input(shape=(28,28,1)) #輸入X=ZeroPadding2D((3,3))(X_input) #填充0#搭建stage1:卷積層(卷積層,歸一化層,激活層,池化層)X=Conv2D(filters=64,kernel_size=(7,7),strides=2,activation="relu",name="cov1")(X)X=BatchNormalization(axis=3,name="bn_cov1")(X)X=MaxPooling2D(pool_size=(3,3),strides=2)(X)#搭建stage2:一個卷積塊和兩個恒等快,卷積塊中卷積核大小均為3*3,卷積核個數分別為64,64,256,恒等快的卷積核大小均為3*3,卷積核大小為3*3#卷積核個數分別為64,64,256X=cov_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:一個卷積塊和3個恒等快,卷積塊的卷積核的大小為3*3,卷積核的個數128,128,512;恒等塊的卷積核大小為3*3,卷積核的個數為#128,128,512X=cov_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:一個卷積塊和5個恒等塊,卷積塊中卷積核的大小為3*3,卷積核個數為256,256,1024,恒等快卷積核的大小為3*3,卷積核個數分別為256#256,1024X=cov_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:一個卷積塊與兩個恒等快,卷積塊中卷積核大小為3*3,卷積核個數為512,512,2048,恒等快中卷積核的大小為3*3,卷積核的個數分別為512#512,2048X=cov_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),name="avg_pool",strides=1,padding="same")(X)#搭建平坦層X=Flatten()(X)#輸出層X=Dense(units=10,activation="softmax",name="fc")(X)#調用model函數,定義所搭建的網絡模型model=Model(inputs=X_input,outputs=X,name="resnet50")return model model=resnet() #模型編譯 model.compile(loss="categorical_crossentropy",optimizer="adam",metrics=["accuracy"] ) model.summary() #模型訓練 n_epoch=4 batch_size=128 def run_resnet():training=model.fit(X_train,Y_train,epochs=n_epoch,batch_size=batch_size,validation_split=0.25,verbose=1)test=model.evaluate(X_train,Y_train,verbose=1)return training,test training,test=run_resnet() print("誤差:",test[0]) print("準確率:",test[1])def show_history(training_history,train,validation):plt.plot(training.history[train],linstyle="-",color="b")plt.plot(training.history[validation],linstyle="--",color="r")plt.title("Training history")plt.xlabel("epoch")plt.ylabel("accuracy")plt.legend(["train","validation"],loc="lower right")plt.show() show_history(training,"accuracy","val-accuracy")def show_history1(training_history,train,validation):plt.plot(training.history[train],linstyle="-",color="b")plt.plot(training.history[validation],linstyle="--",color="r")plt.title("Training history")plt.xlabel("epoch")plt.ylabel("loss")plt.legend(["train","validation"],loc="upper right")plt.show() show_history1(training,"loss","val-loss")prediction=model.predict(X_test) def image_show(image):fig=plt.gcf()fig.set_size_inches(2,2)fig.imshow(image,cmap="binary")plt.show() def result(i):image_show(X_test1[i])print("真實值:",Y_test1[i])print("預測值:",np.argmax(prediction[i])) result(1) result(0)?
總結
以上是生活随笔為你收集整理的经典卷积网络进阶--ResNet详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 经典卷积网络进阶--GoolgleNet
- 下一篇: 迁移学习---inceptionV3