課程來源:人工智能實踐:Tensorflow筆記2
文章目錄
- 前言
- 1、卷積神經網絡的基本步驟
- 1、卷積神經網絡計算convolution
- 2、感受野以及卷積核的選取
- 3、全零填充Padding
- 4、tf描述卷積層
- 5、批標準化(BN操作)
- 6、池化Pooling
- 7、舍棄Dropout
- 8、卷積神經網絡搭建以及參數分析
- 2、經典卷積網絡講解
- 1、LeNet
- 2、AlexNet
- 3、VGGNet
- 4、InceptionNet
- 5、ResNet
- 6、經典卷積網絡總結
- 總結
前言
本講目標:講解卷積神經網絡的基本步驟以及分析比較經典的網絡架構,希望對你有所幫助
經典的5個論文的下載鏈接:
鏈接:https://pan.baidu.com/s/1rIH1nh28ON6DKM6T9HPXbQ
提取碼:kbd8
1、卷積神經網絡的基本步驟
1、卷積神經網絡計算convolution
卷積概念:
卷積的概念:卷積可以認為是一種有效提取圖像特征的方法。一般會用一個正方形的
卷積核,按指定步長,在輸入特征圖上滑動,遍歷輸入特征圖中的每個像素點。每一個步長,
卷積核會與輸入特征圖出現重合區域,重合區域對應元素相乘、求和再加上偏置項得到輸出
特征的一個像素點
卷積注意點:
輸入特征圖的深度,決定了卷積核的深度
當前層的卷積核個數,決定了當前層的輸出特征圖深度
如果覺得某一層的特征提取能力不足,可以在這一層多用幾個卷積核
卷積:使用立體卷積核實現了參數的空間共享
執行卷積計算時,卷積核里的參數是固定的,反向傳播時會更新參數
示例:
2、感受野以及卷積核的選取
這個之前有思考過,見下面鏈接:
為什么兩層33卷積核效果比1層55卷積核效果要好?
3、全零填充Padding
在 Tensorflow 框架中,用參數 padding = ‘SAME’或 padding = ‘VALID’表示是否進行全
零填充,其對輸出特征尺寸大小的影響如下:
對于 5×5×1 的圖像來說,
當 padding = ‘SAME’時,輸出圖像邊長為 5;當 padding = ‘VALID’時,輸出圖像邊長為 3。
4、tf描述卷積層
tf.keras.layers.Conv2D(
filters=卷積核個數,
kernel_size=卷積核尺寸、#正方形寫核長整數,或(核高h,核寬w)
strides=滑動步長,#橫縱向相同寫步長整數,或(縱向步長h,橫向步長w),默認1
padding ="same" or "valid", #使用全零填充是"same",不使用是"valid"(默認)
activation ="relu" or "sigmoid" or"train" or"softmax",#如果有BN此處不填
input_shape =(高,寬,通道數) #輸入特征維度,可省略)
調用方法如下:
代碼解釋:
1、使用6個(5,5)的卷積核卷積,不全零填充,使用sigmoid作為激活函數
2、使用(2,2)的池化核,步長為2,選用最大池化
3、使用Flatten將輸出拉直成一維數組
4、使用10個神經元構成的全連接,激活函數使用softmax
model=tf.keras.models.Sequential([Conv2D(6,5,padding='valid',activation='sigmoid'),MaxPool2D(2,2),#或者這樣調用#Conv2D(6,(5,5),padding='valid',activation='sigmoid'),#MaxPool2D(2,(2,2),#也可以這樣調用#Conv2D(filters=6,kenrnel_size=(5,5),padding='valid',activation='sigmoid'),#MaxPool2D(pool_size=(2,2),strides=2),Flatten(),Dense(10,activation='softmax')
])
在利用 Tensorflow 框架構建卷積網絡時,一般會利用 BatchNormalization
函數來構建 BN 層,進行批歸一化操作,所以在 Conv2D 函數中經常不寫 BN
5、批標準化(BN操作)
神經網絡對0附近的數據更敏感
但是隨著網絡層數的增加,特征數據會出現偏離0均值的情況
使用標準化,將偏移的數據重新拉回
批標準化:是對一個batch的數據做標準化處理,常用在卷積操作和激活操作之間
第K個,batch張輸出特征圖:
BN操作,將原本偏移的特征數據,重新拉回到0均值,使進入激活函數的數據分布在激活函數線性區,使得輸入數據的微小變化,更明顯地體現到輸出上,提升了激活函數對輸入數據的區分力。
但是這種簡單的特征數據標準化,使特征數據完全滿足標準正太分布,集中在激活函數的線性區域,使激活函數喪失了非線性性質,因此在BN操作中為每個卷積核引入兩個可訓練參數。
反向傳播時,縮放因子和偏移因子會與其他待訓練參數一同唄訓練優化。使標準正太分布后的特征數據,通過縮放因子和偏移因子,優化了特征數據分布的寬窄和偏移量。保證了網絡的非線性表達力。
BN層位于卷積層與激活層之間
tf提供了BN操作的函數BatchNormalization()
model=tf.keras.models.Sequential([Conv2D(filters=6,kernel_size=(5,5),padding='same'),#卷積層BatchNormalization(), #BN層Activation('relu'), #激活層MaxPool2D(pool_size=(2,2),strides=2,padding='same'),#池化層Dropout(0.2), #dropout層
])
6、池化Pooling
池化用于減少特征數據量
最大值池化可以提取圖片紋理,均值池化可以保留背景特征
如果用2 * 2的池化核對輸入圖片進行步長為2的池化操作,輸出圖片將變為輸入圖片的四分之一大小
tf描述池化操作:
tf.keras.layers.MaxPool2D(
pool_size=池化核尺寸,
strides=池化步長,
padding='valid'or'same'#same全零填充,valid不全零填充、
)
tf.keras.layers.AveragePooling2D(
pool_size=池化核尺寸,
strides=池化步長,
padding='valid'or'same'#same全零填充,valid不全零填充
)
調用例子:
model=tf.keras.models.Sequential([Conv2D(filters=6,kernel_size=(5,5),padding='same'),#卷積層BatchNormalization(), #BN層Activation('relu'), #激活層MaxPool2D(pool_size=(2,2),strides=2,padding='same'),#池化層Dropout(0.2), #dropout層
])
7、舍棄Dropout
為了緩解神經網絡過擬合,在神經網絡訓練中常常把隱藏層的部分神經元按照一定比例從神經網絡中臨時舍棄,
在使用神經網絡時,再把所有神經元恢復到神經網絡中。
Dropout函數:
tf.keras.layers.Dropout(舍棄的概率)
8、卷積神經網絡搭建以及參數分析
卷積神經網絡:借助卷積核提取特征后,送入全連接網絡
卷積神經網絡的主要模塊:
卷積(conv)
批標準化(BN)
激活(Activation)
池化(Pooling)
卷積是什么:卷積就是特征提取器,就是CBAPD
在這里我們仍然套用網絡八股的構架:
1、import引入tensorflow及keras、numpy等所需模塊
2、讀取數據集
3、搭建所需的網絡結構,當網絡結構比較簡單時,可以利用keras模塊中的tf.keras.Sequential來搭建順序網絡模型;但是當網絡不再是簡單的順序結構,而是有其他特殊的結構出現(如ResNet中的跳連結構),急需要利用class來定義自己的網絡結構
4、對搭建好的網絡進行編譯(compile),通常在這一步指定所采用的優化器(Adam、SGD、RMSdrop)以及損失函數(交叉熵函數、均方差函數等)
5、將數據輸入編譯好的網絡來訓練(model.fit),在這一步中指定訓練輪數epochs以及batch_size等信息。由于參數量和計算量一般都比較大,訓練所需的時間也會比較長,這一步中通常會加入斷點續訓以及模型參數保存
6、將神經網絡模型具體信息打印出來(model.summary),包括網絡結構、網絡各層參數
這里還要講一下flatten函數:
Flatten層用來將輸入“壓平”,即把多維的輸入一維化,常用在從卷積層到全連接層的過渡。Flatten不影響batch的大小。
接下來我們使用類搭建5x5的卷積核,6個,池化核2x2,步長2的網絡結構:
#使用類搭建網絡結構,CBAPD
class Baseline(Model):def _init_(self):super(Baseline,self)._init_()self.c1 =Conv2D(filters=6,kernel_size=(5,5),padding='same')self.b1 =BatchNormalization()self.a1 =Activaction('relu')self.p1 =MaxPool2D(pool_size=(2,2),strides=2,padding ='same')self.d1 =Dropout(0.2)self.flatten =Flatten()self.f1 =Dense(128,activation='relu')self.d2 =Dropout(0.2)self.f2 =Dense(10,activation='softmax')
#call函數調用init函數中搭建好的每層網絡結構
def call(self,x):x=self.c1(x)x=self.b1(x)x=self.a1(x)x=self.p1(x)x=self.d1(x)x=self.flatten(x)x=self.f1(x)x=self.d2(x)y=self.f2(x)return y
#從輸入到輸出過一次前向傳播,返回推理結果
我們來分析一下參數:
打開weights文件:
baseline/conv2d/kernel:0
第一層網絡 5x5x3的卷積核 一共6個
(5, 5, 3, 6)=>450
baseline/conv2d/bias:0
每個卷積核的偏置項b
(6,)=>6
baseline/batch_normalization/gamma:0
BN操作中的縮放因子γ,每個卷積核一個γ
(6,)=>6
baseline/batch_normalization/beta:0
BN操作中的偏移因子γ,每個卷積核一個γ
(6,)=>6
baseline/dense/kernel:0
第一層全連接網絡
(1536, 128)=>196608
baseline/dense/bias:0
第一層全連接網絡128個偏置b
(128,)=>128
baseline/dense_1/kernel:0
第二層全連接網絡
(128, 10)=>1280
baseline/dense_1/bias:0
第二層全連接網絡10個偏置b
(10,)=>10
總共:450+6+6+6+196608+128+1280+10=198494
有了這些參數就可以復現出神經網絡的前向傳播實現應用
可以發現,神經網絡的網絡參數十分多,多大十幾萬甚至更多(當網絡復雜層數增加),并且可以發現絕大部分參數都集中在全連接層上,卷積層的參數占比較小。然而卷積核的參數卻是非常重要的(因為卷積是特征提取器,特征的參數才是圖片識別的重點)。所以減少全連接網絡的參數或許會是一個不錯的網絡優化方法。
至此,基本的神經網絡的搭建方法已經講解完畢。接下來會逐一講解LeNet、AlexNet、VGGNet、InceptionNet 和 ResNet的特點,并且用基于tensorflow的代碼復現出網絡架構。
2、經典卷積網絡講解
1、LeNet
網絡結構:
tf復現模型:
這里對原模型進行調整,輸入圖片大小修改為32 * 32 * 3,用來適應數據集cifar10,并且將大的卷積核用小卷積核來替代
class LeNet5(Model):def __init__(self):super(LeNet5,self).__init__()self.c1=Conv2D(filters=6,kernel_size=(5,5),padding='valid',input_shape=(32,32,3),activation='sigmoid')self.p1=MaxPool2D(pool_size=(2,2),strides=2)self.c2=Conv2D(filters=16,kernel_size=(5,5),padding='valid',activation='sigmoid')self.p2=MaxPool2D(pool_size=(2,2),strides=2)self.flatten =Flatten()self.f1 =Dense(120,activation='sigmoid')self.f1 =Dense(84,activation='sigmoid')self.f1 =Dense(10,activation='softmax')def call(self, x):x = self.c1(x)x = self.p1(x)x = self.c2(x)x = self.p2(x)x = self.flatten(x)x = self.f1(x)x = self.f2(x)y = self.f3(x)return ymodel = LeNet5()
優點:共享卷積核,減少網絡參數.
如何理解卷積神經網絡中的權值共享?
2、AlexNet
網絡結構:
當時由于顯存不足,訓練分成兩部分完成。這里對原模型進行調整,輸入圖片大小修改為32 * 32 * 3,用來適應數據集cifar10,并且將大的卷積核用小卷積核來替代。
class AlexNet8(Model):def __init__(self):super(AlexNet8,self).__init__()self.c1=Conv2D(filters=96,kernel_size=(3,3))self.b1=BatchNormalization()self.a1=Activation('relu')self.p1=MaxPool2D(pool_size=(3,3),strides=2)self.c2=Conv2D(filters=256,kernel_size=(3,3))self.b2=BatchNormalization()self.a2=Activation('relu')self.p2=MaxPool2D(pool_size=(3,3),strides=2)self.c3=Conv2D(filters=384,kernel_size=(3,3,padding='same',activation='relu')self.c4=Conv2D(filters=384,kernel_size=(3,3,padding='same',activation='relu')self.c5=Conv2D(filters=256,kernel_size=(3,3,padding='same',activation='relu')self.p3=MaxPool2D(pool_size=(3,3),strides=2)self.flatten =Flatten()self.f1 =Dense(2048,activation='relu')self.d1 =Dropout(0.5)self.f1 =Dense(2048,activation='relu')self.d1 =Dropout(0.5)self.f1 =Dense(10,activation='softmax')
def call(self, x):x = self.c1(x)x = self.b1(x)x = self.a1(x)x = self.p1(x)x = self.c2(x)x = self.b2(x)x = self.a2(x)x = self.p2(x)x = self.c3(x)x = self.c4(x)x = self.c5(x)x = self.p3(x)x = self.flatten(x)x = self.f1(x)x = self.d1(x)x = self.f2(x)x = self.d2(x)y = self.f3(x)return ymodel = AlexNet8()
優點:激活函數使用 Relu,提升訓練速度;Dropout 防止過擬合
3、VGGNet
網絡結構:
為適應 cifar10 數據集,將輸入圖像尺寸由 224 * 244 * 3 調整為 32 * 32 * 3
class VGG16(Model):def __init__(self):super(VGG16, self).__init__()self.c1 = Conv2D(filters=64, kernel_size=(3, 3), padding='same') # 卷積層1self.b1 = BatchNormalization() # BN層1self.a1 = Activation('relu') # 激活層1self.c2 = Conv2D(filters=64, kernel_size=(3, 3), padding='same', )self.b2 = BatchNormalization() # BN層1self.a2 = Activation('relu') # 激活層1self.p1 = MaxPool2D(pool_size=(2, 2), strides=2, padding='same')self.d1 = Dropout(0.2) # dropout層self.c3 = Conv2D(filters=128, kernel_size=(3, 3), padding='same')self.b3 = BatchNormalization() # BN層1self.a3 = Activation('relu') # 激活層1self.c4 = Conv2D(filters=128, kernel_size=(3, 3), padding='same')self.b4 = BatchNormalization() # BN層1self.a4 = Activation('relu') # 激活層1self.p2 = MaxPool2D(pool_size=(2, 2), strides=2, padding='same')self.d2 = Dropout(0.2) # dropout層self.c5 = Conv2D(filters=256, kernel_size=(3, 3), padding='same')self.b5 = BatchNormalization() # BN層1self.a5 = Activation('relu') # 激活層1self.c6 = Conv2D(filters=256, kernel_size=(3, 3), padding='same')self.b6 = BatchNormalization() # BN層1self.a6 = Activation('relu') # 激活層1self.c7 = Conv2D(filters=256, kernel_size=(3, 3), padding='same')self.b7 = BatchNormalization()self.a7 = Activation('relu')self.p3 = MaxPool2D(pool_size=(2, 2), strides=2, padding='same')self.d3 = Dropout(0.2)self.c8 = Conv2D(filters=512, kernel_size=(3, 3), padding='same')self.b8 = BatchNormalization() # BN層1self.a8 = Activation('relu') # 激活層1self.c9 = Conv2D(filters=512, kernel_size=(3, 3), padding='same')self.b9 = BatchNormalization() # BN層1self.a9 = Activation('relu') # 激活層1self.c10 = Conv2D(filters=512, kernel_size=(3, 3), padding='same')self.b10 = BatchNormalization()self.a10 = Activation('relu')self.p4 = MaxPool2D(pool_size=(2, 2), strides=2, padding='same')self.d4 = Dropout(0.2)self.c11 = Conv2D(filters=512, kernel_size=(3, 3), padding='same')self.b11 = BatchNormalization() # BN層1self.a11 = Activation('relu') # 激活層1self.c12 = Conv2D(filters=512, kernel_size=(3, 3), padding='same')self.b12 = BatchNormalization() # BN層1self.a12 = Activation('relu') # 激活層1self.c13 = Conv2D(filters=512, kernel_size=(3, 3), padding='same')self.b13 = BatchNormalization()self.a13 = Activation('relu')self.p5 = MaxPool2D(pool_size=(2, 2), strides=2, padding='same')self.d5 = Dropout(0.2)self.flatten = Flatten()self.f1 = Dense(512, activation='relu')self.d6 = Dropout(0.2)self.f2 = Dense(512, activation='relu')self.d7 = Dropout(0.2)self.f3 = Dense(10, activation='softmax')def call(self, x):x = self.c1(x)x = self.b1(x)x = self.a1(x)x = self.c2(x)x = self.b2(x)x = self.a2(x)x = self.p1(x)x = self.d1(x)x = self.c3(x)x = self.b3(x)x = self.a3(x)x = self.c4(x)x = self.b4(x)x = self.a4(x)x = self.p2(x)x = self.d2(x)x = self.c5(x)x = self.b5(x)x = self.a5(x)x = self.c6(x)x = self.b6(x)x = self.a6(x)x = self.c7(x)x = self.b7(x)x = self.a7(x)x = self.p3(x)x = self.d3(x)x = self.c8(x)x = self.b8(x)x = self.a8(x)x = self.c9(x)x = self.b9(x)x = self.a9(x)x = self.c10(x)x = self.b10(x)x = self.a10(x)x = self.p4(x)x = self.d4(x)x = self.c11(x)x = self.b11(x)x = self.a11(x)x = self.c12(x)x = self.b12(x)x = self.a12(x)x = self.c13(x)x = self.b13(x)x = self.a13(x)x = self.p5(x)x = self.d5(x)x = self.flatten(x)x = self.f1(x)x = self.d6(x)x = self.f2(x)x = self.d7(x)y = self.f3(x)return ymodel = VGG16()
總體來看,VGGNet的結構是相當規整的,它繼承了 AlexNet中的Relu激活函數、Dropout操作等有效的方法,同時采用了單一尺寸的 3 * 3 小卷積核,形成了規整的 C(Convolution,卷積)、B(Batch normalization)、A(Activation,激活)、P(Pooling,池化)、D(Dropout)結構,這一典型結構在卷積神經網絡中的應用是非常廣的
優點:小卷積核減少參數的同時,提高識別準確率;網絡結構規整,適合并行加速。
4、InceptionNet
優點:一層內使用不同尺寸的卷積核,提升感知力(通過 padding 實現輸出特征面積一致);使用 1 * 1 卷積核,改變輸出特征 channel 數(減少網絡參數)。
基本單元:
class ConvBNRelu(Model):def __init__(self,ch,kernelsz=3,strides=1,padding='same'):super(ConvBNRelu,self).__init__()self.model=tf.keras.models.Sequential([Conv2D(ch,kernelsz,strides=strides,padding=padding),BatchNormalization(),Activation('relu')])def call(self,x,training=None):x=self.model(x,training=training)return x
#ch 特征圖的通道數,即卷積核個數
class InceptionBlk(Model):def __init__(self,ch,strides=1):super(InceptionBlk,self).__init__()self.ch=chself.strides=stridesself.c1=ConvBNRelu(ch,kernelsz=1,strides=strides)self.c2_1=ConvBNRelu(ch,kernelsz=1,strides=strides)self.c2_2=ConvBNRelu(ch,kernelsz=3,strides=1)self.c3_1=ConvBNRelu(ch,kernelsz=1,strides=strides)self.c3_2=ConvBNRelu(ch,kernelsz=5,strides=1)self.p4_1=MaxPool2D(3,strides=1,padding='same')self.c4_2=ConvBNRelu(ch,kernelsz=1,strides=strides)def call(self,x):x1=self.c1(x)x2_1=self.c2_1(x)x2_2=self.c2_2(x2_1)x3_1=self.c3_1(x)x3_2=self.c3_2(c3_1)x4_1=self.p4_1(x)x4_2=self.c4_2(x4_1)#concat along axis=channelx=tf.concat([x1,x2_2,x3_2,x4_2],axis=3)return x
其實基本單元一開始是長這樣的,通過不同尺寸的卷積層和池化層的橫向組合(卷積和池化后的尺寸相同,便于疊加)來拓寬網絡深度,增強網絡對尺寸的適應性。但由于卷積核都是在上一層的輸出上直接計算的,導致參數變多以及運算變得復雜,所以加入1 * 1卷積核,減少特征厚度。
以5 * 5的卷積運算為例說明這個
問題。假設網絡上一層的輸出為 100 * 100 * 128(H * W * C),通過 32 * 5 * 5(32 個大小
為 5 * 5 的卷積核)的卷積層(步長為 1、全零填充)后,輸出為 100 * 100 * 32,卷積層的
參數量為 32 * 5 * 5 * 128 = 102400;如果先通過 32 * 1 * 1 的卷積層(輸出為 100 * 100 * 32),
再通過 32 * 5 * 5 的卷積層,輸出仍為 100 * 100 * 32,但卷積層的參數量變為 32 * 1 * 1 * 128
- 32 * 5 * 5 * 32 = 29696,僅為原參數量的 30 %左右,這就是小卷積核的降維作用。
由基本模塊組成網絡構架如下:
代碼描述:
class Inception10(Model):def __init__(self,num_blocks,num_classes,init_ch=16,**kwargs):super(Inception10,self).__init__(**kwargs)self.in_channels=init_chself.out_channels=init_chself.num_blocks=num_blocksself.init_ch=init_chself.c1=ConvBNRelu(init_ch)self.blocks=tf.keras.models.Sequential()for block_id in range(num_blocks):for layer_id in range (2):if layer_id==0:block=InceptionBlk(self.out_channels,strides=2)else:block=InceptionBlk(self.out_channels,strides=1)self.blocks.add(block)#self.out_channels*=2self.p1 = GlobalAveragePooling2D()self.f1 = Dense(num_classes,activation='softmax')def call(self,x):x=self.c1(x)x=self.blocks(x)x=self.p1(x)y=self.f1(x)return y
model=Inception10(num_blocks=2,num_classes=10)
參數num_blocks代表InceptionNet的Block數,每個Block由兩個基本單元構成,每經過一個
Block,特征圖尺寸變為1/2,通道數變為原來的兩倍; num_classes代表分類數
init_ch代表初始通道數,代表InceptionNet基本單元的初始卷積核個數
InceptionNet采用"全局平均池化+全連接層",VGGNet(有三層全連接層)
平均池化:在特征圖上以窗口的形式滑動,取窗口內的平均值為采樣值
全局平均池化:直接針對特征圖取平均值,每一個特征圖輸出一個值,通過這種方式,每個特征圖都與分類概率直接聯系起來替代了全連接層的功能,并且不會產生額外的訓練參數,減少了過擬合的可能,但會導致網絡收斂速度變慢。
InceptionNet采用多尺寸卷積再聚合的方式拓寬了網絡結構,并通過 1 * 1卷積運算來減少參數量。
5、ResNet
優點:層間殘差跳連,引入前方信息,減少梯度消失,使神經網絡層數變深成為可能。
已知:對于一個深度比較合適的網絡來說,繼續增加層數反而會導致訓練錯誤率的提升:
ResNet核心思路為:對一個準確率達到飽和的淺層網絡,在它后面加幾個恒等映射層(即 y = x,輸出等于輸入),增加網絡深度的同時不增加誤差。這使得神經網絡的層數可以超越之前的約束,提高準確率。
這種殘差結構的示意圖如下:
注意,這里的相加與 InceptionNet 中的相加是有本質區別的,Inception 中的相加是沿深度方向疊加,像“千層蛋糕”一樣,對層數進行疊加;ResNet 中的相加則是特征圖對應元素的數值相加。
代碼描述:
class ResnetBlock(Model):def __init__(self,filters,strides=1,residual_path=False):super(ResnetBlock,self).__init__()self.filters = filtersself.strides = stridesself.residual_path = residual_pathself.c1 = Conv2D(filters,(3,3),strides=strides,padding='same',use_bias=False)self.b1 = BatchNormalization()self.a1 = Activation('relu')self.c2 = Conv2D(filters,(3,3),strides=1,padding='same',use_bias=False)self.b2 = BatchNormalization()#residual_path為Ture時,對輸入進行下采樣,用1x1的卷積核卷積,保證x和F(x)維度相同if residual_path:self.down_c1 = Conv2D(filters,(1,1),strides=strides,padding='same',use_bias=False)self.down_b1 = BatchNormalization()self.a2=Activation('relu')def call(self,inputs):residual = inputs #residual等于輸入值本身,即residual=x#將輸入通過卷積、BN層,激活層,計算F(X)x=self.c1(inputs)x=self.b1(x)x=self.a1(x)x=self.c2(x)y=self.b2(x)if self.residual_path:residual=self.down_c1(inputs)residual=self.down_b1(residual)out = self.a2(y+residual) #最后輸出兩部分的和,F(x)+x或者F(x)+Wx,再過激活函數return out
ResNet18網絡結構以及其利用tf構建模型示意:
![]() | ![]() |
class ResNet(Model):def __init__(self,block_list,initial_filters=64): #block_list表示每個block有幾個卷積層super(ResNet,self).__init__()self.num_blocks =len(block_list)self.block_list =block_listself.out_filters = initial_filtersself.c1 = Conv2D(self.out_filters,(3,3),strides=1,padding='same',use_bias=False,kernel_initialize='he_normal')self.b1 = tf.keras.layers.BatchNormalization()self.a1 = Activation('relu')self.blocks = tf.keras.models.Sequential()#構建ResNet網絡結構for block_id in range(len(block_list)): #第幾個resnet_blockfor layer_id in range(block_list(block_list[block_id])): #第幾個卷積層if block_id!=0 and layer_id==0 : #對除第一個block以外的每個block的輸入進行下采樣,對于一個樣值序列間隔幾個樣值取樣一次,這樣得到新序列就是原序列的下采樣block = ResnetBlock(self.out_filters,strides=2,residual_path = True)else:block = ResnetBlock(self.out_filters,residual_path=False)self.blocks.add(block) #將構建好的block加入resnetself.out_filters *=2 #下一個block的卷積核個數是上一個block的2倍self.p1 = tf.keras.layers.GlobalAveragePooling2D()self.f1 = tf.keras.layers.Dense(10)def call(self,inputs):x=self.c1(inputs)x=self.b1(x)x=self,a1(x)x=self.blocks(x)x=self.p1(x)y=self.f1(x) #由于使用了GAP,所以不需要dropout操作(不再是黑箱操作了)return y
三層殘差單元用于構建更深的網絡:
6、經典卷積網絡總結
總結
課程鏈接:MOOC人工智能實踐:TensorFlow筆記2
參考以及知識點補充鏈接:
卷積神經網絡—AlexNet、VGG、GoogleNet、ResNet論文解讀
CNN淺析和歷年ImageNet冠軍模型解析