一. 背景介紹
VERY DEEP CONVOLUTIONAL NETWORKS FOR LARGE-SCALE IMAGE RECOGNITION
是牛津大學(xué)計(jì)算機(jī)視覺實(shí)驗(yàn)室參加2014年ILSVRC(ImageNet Large Scale Visual Recognition Challenge)比賽的網(wǎng)絡(luò)結(jié)構(gòu)。解決ImageNet中的1000類圖像分類和localization。
實(shí)驗(yàn)結(jié)果是VGGNet斬獲了2014年ILSVRC分類第二宴抚,定位第一梢什。(當(dāng)年分類第一是GoogleNet布轿,后續(xù)會(huì)介紹)
Oxford Visual Geometry Group
Robotics Research Group
Paper link
二. Abstract
1.VGGNet 探索的是神經(jīng)網(wǎng)絡(luò)的深度(depth)與其性能之間的關(guān)系哮笆。
VGG通過反復(fù)堆疊3×3的小型卷積核和2×2的最大池化層,VGG成功構(gòu)建了16-19層的卷積神經(jīng)網(wǎng)絡(luò)汰扭。是當(dāng)時(shí)在論文發(fā)表前最深的深度網(wǎng)絡(luò)稠肘。實(shí)際上,VGG在探索深度對神經(jīng)網(wǎng)絡(luò)影響的同時(shí)萝毛,其實(shí)本身廣度也是很深的项阴。那么:
神經(jīng)網(wǎng)絡(luò)的深度和廣度對其本身的影響是什么呢?
- 卷積核的種類對應(yīng)了網(wǎng)絡(luò)的廣度笆包,卷積層數(shù)對應(yīng)了網(wǎng)絡(luò)的深度环揽。這兩者對網(wǎng)絡(luò)的擬合都有影響。但是在現(xiàn)代深度學(xué)習(xí)中庵佣,大家普遍認(rèn)為深度比廣度的影響更加高歉胶。
- 寬度即卷積核的種類個(gè)數(shù),在LeNet那篇文章里我們說了巴粪,權(quán)值共享(每個(gè)神經(jīng)元對應(yīng)一塊局部區(qū)域通今,如果局部區(qū)域是10*10,那么就有100的權(quán)重參數(shù)验毡,但如果我們把每個(gè)神經(jīng)元的權(quán)重參數(shù)設(shè)置為一樣衡创,相當(dāng)于每個(gè)神經(jīng)元用的是同一個(gè)卷積核去卷積圖像,最終兩層間的連接只有 100 個(gè)參數(shù) 晶通!)可以大大減少我們的訓(xùn)練參數(shù),但是由于使用了同一個(gè)卷積核哟玷,最終特征個(gè)數(shù)太少狮辽,效果也不會(huì)好一也,所以一般神經(jīng)網(wǎng)絡(luò)都會(huì)有多個(gè)卷積核,這里說明寬度的增加在一開始對網(wǎng)絡(luò)的性能提升是有效的喉脖。但是椰苟,隨著廣度的增加,對網(wǎng)絡(luò)整體的性能其實(shí)是開始趨于飽和树叽,并且有下降趨勢舆蝴,因?yàn)檫^多的特征(一個(gè)卷積核對應(yīng)發(fā)現(xiàn)一種特征)可能對帶來噪聲的影響。
- 深度即卷積層的個(gè)數(shù)题诵,對網(wǎng)絡(luò)的性能是極其重要的洁仗,ResNet已經(jīng)表明越深的深度網(wǎng)絡(luò)性能也就越好。深度網(wǎng)絡(luò)自然集成了低性锭、中赠潦、高層特征。多層特征可以通過網(wǎng)絡(luò)的堆疊的數(shù)量(深度)來豐富其表達(dá)草冈。挑戰(zhàn)imagenet數(shù)據(jù)集的優(yōu)秀網(wǎng)絡(luò)都是采用較深的模型她奥。網(wǎng)絡(luò)的深度很重要,但是否能夠簡單的通過增加更多的網(wǎng)絡(luò)層次學(xué)習(xí)更好的網(wǎng)絡(luò)怎棱?這個(gè)問題的障礙就是臭名昭著的梯度消失(爆炸)問題哩俭,這從根本上阻礙了深度模型的收斂。
- 增加更多的卷積核可以發(fā)現(xiàn)更多的特征拳恋,但是特征是需要進(jìn)行組合的携茂,只有知道了特征之間的關(guān)系才能夠更好的表達(dá)圖片內(nèi)容,而增加深度就是組合特征的過程诅岩。
2. VGG結(jié)構(gòu)全部都采用較小的卷積核(3×3讳苦,部分1×1)
在VGG出現(xiàn)之前的深度網(wǎng)絡(luò),比如ZFNet或Overfeat普遍都采用了7×7和11×11的卷積核吩谦。VGG通篇全部采用很小的卷積核鸳谜。我們再回顧一下在深度學(xué)習(xí)中卷積核的感受野的作用。
如何選擇卷積核的大惺酵ⅰ咐扭?越大越好還是越小越好?
答案是小而深滑废,單獨(dú)較小的卷積核也是不好的蝗肪,只有堆疊很多小的卷積核,模型的性能才會(huì)提升蠕趁。
- 如上圖所示薛闪,CNN的卷積核對應(yīng)一個(gè)感受野,這使得每一個(gè)神經(jīng)元不需要對全局圖像做感受俺陋,每個(gè)神經(jīng)元只感受局部的圖像區(qū)域豁延,然后在更高層昙篙,將這些感受不同局部的神經(jīng)元綜合起來就可以得到全局信息。這樣做的一個(gè)好處就是可以減少大量訓(xùn)練的參數(shù)诱咏。
-
VGG經(jīng)常出現(xiàn)多個(gè)完全一樣的3×3的卷積核堆疊在一起的情況苔可,這些多個(gè)小型卷積核堆疊的設(shè)計(jì)其實(shí)是非常有效的。如下圖所示袋狞,兩個(gè)3×3的卷積層串聯(lián)相當(dāng)于1個(gè)5×5的卷積層焚辅,即一個(gè)像素會(huì)和周圍5×5的像素產(chǎn)生關(guān)聯(lián),可以說感受野是5×5苟鸯。同時(shí)同蜻,3個(gè)串聯(lián)的3×3卷積層串聯(lián)的效果相當(dāng)于一個(gè)7×7的卷積層。除此之外倔毙,3個(gè)串聯(lián)的3×3的卷積層擁有比一個(gè)7×7更少的參數(shù)量埃仪,只有后者的 (3×3×3) / (7×7) = 55%。最重要的是3個(gè)3×3的卷積層擁有比一個(gè)7×7的卷積層更多的非線性變換(前者可以使用三次ReLu激活陕赃,而后者只有一次)卵蛉。
3.VGG獲得了2014年ILSVRC分類第二,定位第一么库。(當(dāng)年分類第一是GoogleNet傻丝,后續(xù)會(huì)介紹)
三. Architecture
1. vgg模型的輸入是固定的224×224的彩色RGB通道圖像。
2. 輸入做的唯一一個(gè)數(shù)據(jù)預(yù)處理就是各自減去 RGB 3個(gè)通道的均值
3. 使用的是非常小的3×3的卷積核诉儒。
4. 其中一個(gè)結(jié)構(gòu)采用了一些1×1的卷積核葡缰。
1×1的卷積核到底有什么作用呢?
- 1×1的卷積核和正常的濾波器完全是一樣的忱反,只不過它不再感受一個(gè)局部區(qū)域泛释,不考慮像素與像素之間的關(guān)系。1×1的卷積本身就是不同feature channel的線性疊加温算。1×1的卷積最早出現(xiàn)在Network in Network這篇文章中怜校,在Google的inception結(jié)構(gòu)中也采用了大量1×1的卷積。
- NIN論文中解釋1×1的卷積實(shí)現(xiàn)了多個(gè)feature map的結(jié)合注竿,從而整合了不同通道間的信息茄茁。(個(gè)人認(rèn)為這個(gè)作用并不是特點(diǎn),因?yàn)槠渌笮〉木矸e核也可以實(shí)現(xiàn))
-
1×1的卷積可以實(shí)現(xiàn)通道數(shù)量的升維和降維巩割。并且是低成本的特征變換(計(jì)算量比3×3小很多)裙顽。是一個(gè)性價(jià)比很高的聚合操作。怎么理解1×1是性價(jià)比很高的升降通道數(shù)的操作呢宣谈?
(以google inception為例)
原始
新
原始結(jié)構(gòu):
參數(shù):(1×1×192×64) + (3×3×192×128) + (5×5×192×32) = 153600
最終輸出的feature map:64+128+32+192 = 416
加入不同channel的1×1卷積后:
參數(shù):1×1×192×64+(1×1×192×96+3×3×96×128)+(1×1×192×16+5×5×16×32)=15872
最終輸出的feature map: 64+128+32+32=256
所以加入1×1的卷積后愈犹,在降低大量運(yùn)算的前提下,降低了維度蒲祈。
5. 卷積步長是一個(gè)像素
6.采用最大池化層
7. 不是所有卷積層后面都接一個(gè)池化層甘萧。(和之前的網(wǎng)絡(luò)有區(qū)別萝嘁,是反復(fù)堆疊幾個(gè)3×3的卷積)
8. 最大池化是2×2梆掸,步長為2.
9. 最后接了3個(gè)全連接層
10. 前兩個(gè)全連接都是4096扬卷,最后一個(gè)根據(jù)imagenet1000類定為1000個(gè)輸出
11. 分類層是softmax
12. 所有隱層都進(jìn)行了ReLU激活。
13. 只有一個(gè)地方使用了LRN酸钦,并且實(shí)驗(yàn)表明LRN沒有任何用處怪得。
四. ConvNet Configurations
- VGG全部使用了3×3的卷積核和2×2的池化核,通過不斷加深網(wǎng)絡(luò)結(jié)構(gòu)來提升性能卑硫。上圖為VGG各個(gè)級(jí)別的網(wǎng)絡(luò)結(jié)構(gòu)圖徒恋。
- VGG各種級(jí)別的結(jié)構(gòu)都采用了5段卷積,每一段有一個(gè)或多個(gè)卷積層欢伏。同時(shí)每一段的尾部都接著一個(gè)最大池化層來縮小圖片尺寸入挣。每一段內(nèi)的卷積核數(shù)量一致,越靠后的卷積核數(shù)量越多 64-128-256-512-512硝拧。經(jīng)常出現(xiàn)多個(gè)完全一樣的卷積層堆疊在一起的情況径筏。
- A-LRN結(jié)構(gòu)使用了LRN,結(jié)果表明并沒有什么用處障陶。
- C 結(jié)構(gòu)比B多了幾個(gè)1×1的卷積滋恬。在VGG里,1×1的卷積意義主要是線性變換抱究,輸入輸出通道數(shù)量并沒有變化恢氯。沒有發(fā)生降維。所以作者認(rèn)為1×1沒有3×3好鼓寺,大一些的卷積核可以學(xué)到更大的空間特征勋拟。
- A-E 每一級(jí)網(wǎng)絡(luò)逐漸變深,但是參數(shù)并沒有變多很多妈候。這是因?yàn)閰?shù)量主要消耗在最后3個(gè)全連接層敢靡,卷積雖然深但是參數(shù)消耗并不多。但是訓(xùn)練耗時(shí)的仍然是卷積州丹,因其計(jì)算量大醋安。
- D E就是我們經(jīng)常說的VGG-16和VGG-19
五. Training
這個(gè)部分是VGG當(dāng)時(shí)是怎么訓(xùn)練的具體過程,有很多值得借鑒的地方墓毒。
1. 使用mini-batch的梯度下降法吓揪,并且是帶動(dòng)量的。batch_size設(shè)置為256所计,動(dòng)量是0.9柠辞。
2. 前兩個(gè)全連接使用了dropout,值是0.5, 用來緩解過擬合主胧。
3. 學(xué)習(xí)率初始設(shè)置為0.01叭首,衰減系數(shù)為10习勤,每當(dāng)驗(yàn)證集上準(zhǔn)確率不再變好時(shí),會(huì)降低學(xué)習(xí)率焙格。學(xué)習(xí)率一共被衰減3次图毕。總共訓(xùn)練了74個(gè)epoch眷唉,370k個(gè)iteration予颤。
VGG的參數(shù)初始化方式是怎么樣的?
- 上圖中間紅框部分作者介紹了VGG訓(xùn)練時(shí)參數(shù)的初始化方式冬阳,這個(gè)部分比較有意思蛤虐。作者認(rèn)為這么深的網(wǎng)絡(luò)(論文發(fā)表前最深)訓(xùn)練收斂是很困難的,必須借助有效的參數(shù)初始化方式肝陪。
- 作者先訓(xùn)練上面網(wǎng)絡(luò)結(jié)構(gòu)中的A結(jié)構(gòu)驳庭,A收斂之后呢,將A的網(wǎng)絡(luò)權(quán)重保存下來氯窍,再復(fù)用A網(wǎng)絡(luò)的權(quán)重來初始化后面幾個(gè)簡單模型饲常。
- 復(fù)用A的網(wǎng)絡(luò)權(quán)重,只是前四個(gè)卷積層荞驴,以及后三層全連接層不皆,其它的都是隨機(jī)初始化。
- 隨機(jī)初始化熊楼,均值是0霹娄,方差是0.01。bias是0.
六. Image Size
在訓(xùn)練和測試階段鲫骗,VGG都采用了Multi-scale的方式犬耻。
VGG的Multi-Scale方法
- VGG在訓(xùn)練階段使用了Multi-Scale的方法做數(shù)據(jù)增強(qiáng),將原始圖片縮放到不同的尺寸S执泰,然后再隨機(jī)裁剪224×224的圖片枕磁,這樣能增加很多數(shù)據(jù)量,對于防止模型過擬合有很不錯(cuò)的效果术吝。
- 實(shí)驗(yàn)中计济,作者令S在[256, 512]這個(gè)區(qū)間,使用Multi-Scale獲得了多個(gè)版本的數(shù)據(jù)排苍,并將多個(gè)版本的數(shù)據(jù)合在一起訓(xùn)練沦寂。
- 在測試時(shí),也采用了Multi-Scale的方法淘衙,將圖像scale到一個(gè)尺寸Q传藏,并將圖片輸入卷積網(wǎng)絡(luò)計(jì)算,然后再最后一個(gè)卷積層使用滑窗的方式進(jìn)行分類預(yù)測,將不同窗口的分類結(jié)果平均毯侦,再將不同尺寸Q的結(jié)果平均哭靖,得到最后的結(jié)果。這樣可以提高數(shù)據(jù)的利用率和預(yù)測準(zhǔn)確率侈离。
-
下圖是VGG各種不同scale的訓(xùn)練結(jié)果试幽,融合了Multi-Scale的D和E是最好的。
七. Tensorflow實(shí)現(xiàn)簡單的VGG-16的結(jié)構(gòu)
本部分整理自《Tensorflow實(shí)戰(zhàn)》
1. 實(shí)現(xiàn)卷積操作函數(shù)
VGG包含很多卷積霍狰,函數(shù)conv_op創(chuàng)建卷積層抡草,并把本層參數(shù)存入?yún)?shù)列表饰及。這樣可以方便后面VGG結(jié)構(gòu)中多次使用卷積操作蔗坯。
輸入?yún)?shù)
- input_op: 輸入tensor,是一個(gè)4D的tensor燎含,可以理解為image batch宾濒。shape=[batch, in_height, in_width, in_channels],即:[訓(xùn)練時(shí)一個(gè)batch的圖片數(shù)量, 圖片高度, 圖片寬度, 圖像通道數(shù)]
- kh:卷積核的高
- kw:卷積核的寬
- n_out:輸出通道數(shù)屏箍;這三個(gè)參數(shù)恰好定義了一個(gè)卷積核的參數(shù)绘梦,卷積核kernel的參數(shù)即為: shape=[filter_height, filter_width, in_channels, out_channels],即:[卷積核的高度赴魁,卷積核的寬度卸奉,圖像通道數(shù),卷積核個(gè)數(shù)]
- dh:卷積的步長高
- dw:卷積的步長寬颖御。 進(jìn)行卷積操作時(shí)榄棵,我們除了需要輸入的tensor和卷積核參數(shù)外,還需要定義卷積的步長strides潘拱,strides卷積時(shí)在每一維上的步長疹鳄,strides[0]=strides[3]=1。
- p:參數(shù)列表芦岂。將卷積核和bias的參數(shù)寫入p瘪弓,以便后面使用
輸出:
經(jīng)過卷積,和激活函數(shù)的tensor禽最。[batch_size, new_h, new_w, n_out]
def conv_op(input_op, name, kh, kw, n_out, dh, dw, p):
n_in = input_op.get_shape()[-1].value
with tf.name_scope(name) as scope:
kernel = tf.get_variable(scope+'w', shape=[kh, kw, n_in, n_out], dtype = tf.float32,
initializer = tf.contrib.layers.xavier_initializer_conv2d())
conv = tf.nn.conv2d(input_op, kernel, (1, dh, dw, 1), padding = 'SAME')
bias_init_val = tf.constant(0.0, shape = [n_out], dtype = tf.float32)
biases = tf.variable(bias_init_val, trainable = True, name = 'b')
z = tf.nn.bias_add(conv, biases)
activation = tf.nn.relu(z, name = scope)
p += [kernel, biases]
return activation
- 整體過程非常簡單腺怯,建議熟背這一段代碼。首先獲得輸入的tensor川无,即image batch呛占,并且定義name,卷積核的大小舀透,卷積的步長(輸入?yún)?shù))栓票。
- 使用get_shape()獲得輸入tensor的輸入通道數(shù)。
- name_scope將scope內(nèi)生成的variable自動(dòng)命名為name/xxx,用于區(qū)分不同卷積層的組件
- get_variable()定義卷積核的參數(shù)走贪,注意shape和初始化方式
- conv2d對輸入tensor佛猛,使用剛剛的卷積核進(jìn)行卷積操作,注意此處定義strides和padding
- constant定義bias坠狡,注意shape等于輸出通道數(shù)继找。一個(gè)卷積核對應(yīng)一個(gè)輸出通道,對應(yīng)一個(gè)bias逃沿。tf.variable再將其轉(zhuǎn)換成可訓(xùn)練的參數(shù)婴渡。
- 進(jìn)行relu激活
2. 實(shí)現(xiàn)池化操作函數(shù)
輸入?yún)?shù)
- input_op: 輸入tensor
- kh:池化的高
- kw:池化的寬
- dh:池化的步長高
- dw:池化的步長寬。
def maxpool_op(input_op, name, kh, kw, dh, dw):
return tf.nn.max_pool(input_op,
ksize=[1, kh, kw, 1],
strides=[1, dh, dw, 1],
padding='SAME',
name=name)
這部分代碼也很容易理解凯亮,nn.max_pool可以直接使用边臼,需要定義池化的大小ksize,步長strides假消,以及邊界填充方式padding柠并。此部分沒有需要訓(xùn)練的參數(shù)。
3. 定義全連接操作函數(shù)
輸入?yún)?shù)
- input_op: 輸入的tensor
- n_out: 輸出向量長度富拗。 全連接只需要這兩個(gè)參數(shù)
def fc_op(input_op, name, n_out, p):
n_in = input_op.get_shape()[-1].value
with tf.name_scope(name) as scope:
kernel = tf.get_variable(scope+"w", shape=[n_in, n_out], dtype=tf.float32,
initializer=tf.contrib.layers.xavier_initializer())
biases = tf.Variable(tf.constant(0.1, shape=[n_out], dtype=tf.float32), name='b')n
activation = tf.nn.relu_layer(input_op, kernel, biases, name= scope)
p += [kernel, biases]
return activation
此部分代碼也很簡單臼予,全連接層需要訓(xùn)練參數(shù),并且比卷積層更多(卷積層是局部連接)啃沪,同樣獲得輸入圖片tensor的通道數(shù)(向量長度)粘拾,同樣獲得輸入圖片tensor的通道數(shù),注意每個(gè)訓(xùn)練參數(shù)都需要給定初始化值或初始化方式创千。bias利用constant函數(shù)初始化為較小的值0.1,而不是0缰雇, 再做relu非線性變。
4. 根據(jù)論文結(jié)構(gòu)創(chuàng)建VGG16網(wǎng)絡(luò)
input_op是輸入的圖像tensor shape=[batch, in_height, in_width, in_channels]
keep_prob是控制dropout比率的一個(gè)placeholder
def inference_op(input_op,keep_prob):
# 初始化參數(shù)p列表
p = []
VGG16包含6個(gè)部分签餐,前面5段卷積寓涨,最后一段全連接,
每段卷積包含多個(gè)卷積層和pooling層.
下面是第一段卷積,包含2個(gè)卷積層和一個(gè)pooling層,
利用前面定義好的函數(shù)conv_op,mpool_op 創(chuàng)建這些層
# 第一段卷積的第一個(gè)卷積層 卷積核3*3氯檐,共64個(gè)卷積核(輸出通道數(shù))戒良,步長1*1
# input_op:224*224*3 輸出尺寸224*224*64
conv1_1 = conv_op(input_op, name="conv1_1", kh=3, kw=3, n_out=64, dh=1,
dw=1, p=p)
# 第一段卷積的第2個(gè)卷積層 卷積核3*3,共64個(gè)卷積核(輸出通道數(shù))冠摄,步長1*1
# input_op:224*224*64 輸出尺寸224*224*64
conv1_2 = conv_op(conv1_1, name="conv1_2", kh=3, kw=3, n_out=64, dh=1,
dw=1, p=p)
# 第一段卷積的pooling層糯崎,核2*2,步長2*2
# input_op:224*224*64 輸出尺寸112*112*64
pool1 = mpool_op(conv1_2, name="pool1", kh=2, kw=2, dh=2, dw=2)
下面是第2段卷積河泳,包含2個(gè)卷積層和一個(gè)pooling層
# 第2段卷積的第一個(gè)卷積層 卷積核3*3沃呢,共128個(gè)卷積核(輸出通道數(shù)),步長1*1
# input_op:112*112*64 輸出尺寸112*112*128
conv2_1 = conv_op(pool1, name="conv2_1", kh=3, kw=3, n_out=128, dh=1,
dw=1, p=p)
# input_op:112*112*128 輸出尺寸112*112*128
conv2_2 = conv_op(conv2_1, name="conv2_2", kh=3, kw=3, n_out=128, dh=1,
dw=1, p=p)
# input_op:112*112*128 輸出尺寸56*56*128
pool2 = mpool_op(conv2_2, name="pool2", kh=2, kw=2, dh=2, dw=2)
下面是第3段卷積拆挥,包含3個(gè)卷積層和一個(gè)pooling層
# 第3段卷積的第一個(gè)卷積層 卷積核3*3薄霜,共256個(gè)卷積核(輸出通道數(shù))某抓,步長1*1
# input_op:56*56*128 輸出尺寸56*56*256
conv3_1 = conv_op(pool2, name="conv3_1", kh=3, kw=3, n_out=256, dh=1,
dw=1, p=p)
# input_op:56*56*256 輸出尺寸56*56*256
conv3_2 = conv_op(conv3_1, name="conv3_2", kh=3, kw=3, n_out=256, dh=1,
dw=1, p=p)
# input_op:56*56*256 輸出尺寸56*56*256
conv3_3 = conv_op(conv3_2, name="conv3_3", kh=3, kw=3, n_out=256, dh=1,
dw=1, p=p)
# input_op:56*56*256 輸出尺寸28*28*256
pool3 = mpool_op(conv3_3, name="pool3", kh=2, kw=2, dh=2, dw=2)
下面是第4段卷積,包含3個(gè)卷積層和一個(gè)pooling層
# 第3段卷積的第一個(gè)卷積層 卷積核3*3惰瓜,共512個(gè)卷積核(輸出通道數(shù))否副,步長1*1
# input_op:28*28*256 輸出尺寸28*28*512
conv4_1 = conv_op(pool3, name="conv4_1", kh=3, kw=3, n_out=512, dh=1,
dw=1, p=p)
# input_op:28*28*512 輸出尺寸28*28*512
conv4_2 = conv_op(conv4_1, name="conv4_2", kh=3, kw=3, n_out=512, dh=1,
dw=1, p=p)
# input_op:28*28*512 輸出尺寸28*28*512
conv4_3 = conv_op(conv4_2, name="conv4_3", kh=3, kw=3, n_out=512, dh=1,
dw=1, p=p)
# input_op:28*28*512 輸出尺寸14*14*512
pool4 = mpool_op(conv4_3, name="pool4", kh=2, kw=2, dh=2, dw=2)
前面4段卷積發(fā)現(xiàn),VGG16每段卷積都是把圖像面積變?yōu)?/4崎坊,但是通道數(shù)翻倍, 因此圖像tensor的總尺寸縮小一半备禀。
下面是第5段卷積,包含3個(gè)卷積層和一個(gè)pooling層
# 第5段卷積的第一個(gè)卷積層 卷積核3*3奈揍,共512個(gè)卷積核(輸出通道數(shù))曲尸,步長1*1
# input_op:14*14*512 輸出尺寸14*14*512
conv5_1 = conv_op(pool4, name="conv5_1", kh=3, kw=3, n_out=512, dh=1,
dw=1, p=p)
# input_op:14*14*512 輸出尺寸14*14*512
conv5_2 = conv_op(conv5_1, name="conv5_2", kh=3, kw=3, n_out=512, dh=1,
dw=1, p=p)
# input_op:14*14*512 輸出尺寸14*14*512
conv5_3 = conv_op(conv5_2, name="conv5_3", kh=3, kw=3, n_out=512, dh=1,
dw=1, p=p)
# input_op:28*28*512 輸出尺寸7*7*512
pool5 = mpool_op(conv5_3, name="pool5", kh=2, kw=2, dh=2, dw=2)
下面要經(jīng)過全連接,需將第五段卷積網(wǎng)絡(luò)的結(jié)果扁平化, reshape將每張圖片變?yōu)?7512=25088的一維向量
shp = pool5.get_shape()
flattened_shape = shp[1].value * shp[2].value * shp[3].value
# tf.reshape(tensor, shape, name=None) 將tensor變換為參數(shù)shape的形式男翰。
resh1 = tf.reshape(pool5, [-1, flattened_shape], name="resh1")
第一個(gè)全連接層另患,是一個(gè)隱藏節(jié)點(diǎn)數(shù)為4096的全連接層,后面接一個(gè)dropout層奏篙,訓(xùn)練時(shí)保留率為0.5柴淘,預(yù)測時(shí)為1.0
fc6 = fc_op(resh1, name="fc6", n_out=4096, p=p)
fc6_drop = tf.nn.dropout(fc6, keep_prob, name="fc6_drop")
第2個(gè)全連接層,是一個(gè)隱藏節(jié)點(diǎn)數(shù)為4096的全連接層秘通,后面接一個(gè)dropout層,訓(xùn)練時(shí)保留率為0.5敛熬,預(yù)測時(shí)為1.0
fc7 = fc_op(fc6_drop, name="fc7", n_out=4096, p=p)
fc7_drop = tf.nn.dropout(fc7, keep_prob, name="fc7_drop")
最后是一個(gè)1000個(gè)輸出節(jié)點(diǎn)的全連接層肺稀,利用softmax輸出分類概率,argmax輸出概率最大的類別应民。
fc8 = fc_op(fc7_drop, name="fc8", n_out=1000, p=p)
softmax = tf.nn.softmax(fc8)
predictions = tf.argmax(softmax, 1)
return predictions, softmax, fc8, p
個(gè)人原創(chuàng)作品话原,轉(zhuǎn)載需征求本人同意