有時候,出於某種目的,我們想要自己一步步搭建網絡,不是簡單的調用現成的模型,
- 封裝的太厲害,都快忘記內部結構了
- 自己有一個小點子,想加到網絡結構中,萬一效果好了呢
- 有很多專門的場合,我們有一定的先驗知識,加到網絡中會更好
- 成就感爆棚,即使是實現現有的網絡結構
寫在前面的話
因為也是剛接觸到 深度學習 這塊,並且在項目( 傳統機器視覺項目 )中也遇到了這方面的問題。項目主要是對相似度很高的圖像進行分類,有點像 細粒度 方面的分類。模型採用預訓練模型,接著進行 fine-tune ,但模型一直過擬合,表現形式是:訓練的loss和準確率都很高,驗證集的準確率和loss卻很高,且與訓練集的差距較大,例如訓練精度90%以上,此時的驗證準確率也僅僅70%+。因為,想要自己改一下網絡,增加一些防止過擬合的手段,或者把先驗知識加進去。因此,研究了下如何寫網絡代碼,更深一層理解。
Keras中的層對象,輸入一個張量,輸出一個張量。
層對象有哪些方法
- layer.input
- layer.output
- layer.input_shape
- layer.output_shape
如果該層有多個計算節點,使用下面的方法
- layer.get_input_at(node_index)
- layer.get_output_at(node_index)
- layer.get_input_shape_at(node_index)
- layer.get_output_shape_at(node_index)
寫經典網絡代碼
VGG
這裡我們看下VGG經典網絡結構圖。
優點
- 結構簡潔,整個網絡都使用了同樣大小的卷積核尺寸,且核尺寸較小(3x3)。
- 採用小卷積核比採用大的卷積核更具有優勢,因為多層非線性層可以增加網絡深度來保證學習更複雜的模式,而且參數更少。
缺點
- 耗費更多計算資源,更多的內存占用(140M)。其中絕大多數的參數都是來自於第一個全連接層。
對於上面的缺點,我們當然可以根據自己的需求去修改,有的文章稱這些全連接層即使被去除,對於性能也沒有什麼影響,也可以把全連接層替換成1X1的卷積層,這些都可以試試。
def KerasVGG():
"""
模型採用VGG16 的結構:
1使用固定尺寸的小卷積核 (3x3)
2以2的冪次遞增的卷積核數量 (64, 128, 256)
3兩層卷積搭配一層池化
4全連接層沒有採用 VGG16 龐大的三層結構,避免運算量過大,僅使用 128 個節點的單個FC
5權重初始化採用He Normal
:return:
"""
inputs = Input(shape=(32, 32, 3))
net = inputs
# (32, 32, 3)-->(32, 32, 64)
net = Convolution2D(filters=64, kernel_size=3, strides=1,
padding='same', activation='relu',
kernel_initializer='he_normal')(net)
# (32, 32, 64)-->(32, 32, 64)
net = Convolution2D(filters=64, kernel_size=3, strides=1,
padding='same', activation='relu',
kernel_initializer='he_normal')(net)
# (32, 32, 64)-->(16, 16, 64)
net = MaxPooling2D(pool_size=2, strides=2, padding='valid')(net)
# (16, 16, 64)-->(16, 16, 128)
net = Convolution2D(filters=128, kernel_size=3, strides=1,
padding='same', activation='relu',
kernel_initializer='he_normal')(net)
# (16, 16, 64)-->(16, 16, 128)
net = Convolution2D(filters=128, kernel_size=3, strides=1,
padding='same', activation='relu',
kernel_initializer='he_normal')(net)
# (16, 16, 128)-->(8, 8, 128)
net = MaxPooling2D(pool_size=2, strides=2, padding='valid')(net)
# (8, 8, 128)-->(8, 8, 256)
net = Convolution2D(filters=256, kernel_size=3, strides=1,
padding='same', activation='relu',
kernel_initializer='he_normal')(net)
# (8, 8, 256)-->(8, 8, 256)
net = Convolution2D(filters=256, kernel_size=3, strides=1,
padding='same', activation='relu',
kernel_initializer='he_normal')(net)
# (8, 8, 256)-->(4, 4, 256)
net = MaxPooling2D(pool_size=2, strides=2, padding='valid')(net)
# (4, 4, 256) --> 4*4*256=4096
net = Flatten()(net)
# 4096 --> 128
net = Dense(units=128, activation='relu',
kernel_initializer='he_normal')(net)
# Dropout
net = Dropout(0.5)(net)
# 128 --> 10
net = Dense(units=config.nb_classes, activation='softmax',
kernel_initializer='he_normal')(net)
return inputs, net
如果您想看一下tensorflow版本的vgg16程序,請點這裡。
Inception
inception的詳細結構參見Google的這篇論文: Inception
from keras.layers import Conv2D, MaxPooling2D, Input
input_img = Input(shape=(256, 256, 3))
tower_1 = Conv2D(64, (1, 1), padding='same', activation='relu')(input_img)
tower_1 = Conv2D(64, (3, 3), padding='same', activation='relu')(tower_1)
tower_2 = Conv2D(64, (1, 1), padding='same', activation='relu')(input_img)
tower_2 = Conv2D(64, (5, 5), padding='same', activation='relu')(tower_2)
tower_3 = MaxPooling2D((3, 3), strides=(1, 1), padding='same')(input_img)
tower_3 = Conv2D(64, (1, 1), padding='same', activation='relu')(tower_3)
output = keras.layers.concatenate([tower_1, tower_2, tower_3], axis=1)
這裡可以看出,1X1的卷積其實在很多地方都有用,至於究竟為什麼會這樣,以及這些網絡的背後深層原因,還需要花費更多的時間去理解,去悟,後續再見。