卷積神經(jīng)網(wǎng)絡(luò)
1. 預(yù)備知識(shí)
1.1 神經(jīng)網(wǎng)絡(luò)中為什么要標(biāo)準(zhǔn)化
- 原因在于神經(jīng)網(wǎng)絡(luò)學(xué)習(xí)過程本質(zhì)就是為了學(xué)習(xí)數(shù)據(jù)分布他膳,一旦訓(xùn)練數(shù)據(jù)與測試數(shù)據(jù)的分布不同响逢,那么網(wǎng)絡(luò)的泛化能力也大大降低;另外一方面矩乐,一旦每批訓(xùn)練數(shù)據(jù)的分布各不相同(batch 梯度下降)龄句,那么網(wǎng)絡(luò)就要在每次迭代都去學(xué)習(xí)適應(yīng)不同的分布回论,這樣將會(huì)大大降低網(wǎng)絡(luò)的訓(xùn)練速度散罕,這也正是為什么我們需要對數(shù)據(jù)都要做一個(gè)歸一化預(yù)處理的原因分歇。
- 對于深度網(wǎng)絡(luò)的訓(xùn)練是一個(gè)復(fù)雜的過程,只要網(wǎng)絡(luò)的前面幾層發(fā)生微小的改變欧漱,那么后面幾層就會(huì)被累積放大下去职抡。一旦網(wǎng)絡(luò)某一層的輸入數(shù)據(jù)的分布發(fā)生改變,那么這一層網(wǎng)絡(luò)就需要去適應(yīng)學(xué)習(xí)這個(gè)新的數(shù)據(jù)分布误甚,所以如果訓(xùn)練過程中缚甩,訓(xùn)練數(shù)據(jù)的分布一直在發(fā)生變化,那么將會(huì)影響網(wǎng)絡(luò)的訓(xùn)練速度窑邦。
1.2 什么是 LRN
- Local Response Normalization的簡稱是LRN擅威,即局部響應(yīng)歸一化層。說到為什么要使用LRN就不得不提到神經(jīng)生物學(xué)中的一個(gè)概念叫做 lateral inhibition(橫向抑制)冈钦,簡單來講就是興奮的神經(jīng)細(xì)胞抑制周圍神經(jīng)細(xì)胞的能力郊丛。應(yīng)用到深度神經(jīng)網(wǎng)絡(luò)中,這種橫向抑制的目的是進(jìn)行局部對比度增強(qiáng)瞧筛,以便將局部特征在下一層得到表達(dá)厉熟。LRN是一個(gè)非訓(xùn)練層,即該層中不存在可訓(xùn)練的參數(shù)较幌。假設(shè)某一個(gè)CNN層的輸出為一個(gè) WxHxC 的張量揍瑟,要對該張量做LRN我們應(yīng)該怎么做呢。假設(shè)我們?nèi)的大小為3乍炉,現(xiàn)在我們要計(jì)算(x, y, i)位置上 LRN 后的值绢片,我們便可以以 (x,y岛琼, i)點(diǎn)位中心底循,在channel維度范圍內(nèi)取一個(gè)1xn大小的網(wǎng)格,此時(shí)我們便可以通過(x, y, i)位置的數(shù)值和其周圍的數(shù)值對(x, y, i)位置進(jìn)行正則化衷恭。
1.3 BN
- BN的基本思想其實(shí)相當(dāng)直觀:因?yàn)樯顚由窠?jīng)網(wǎng)絡(luò)在做非線性變換前的激活輸入值(就是那個(gè)x=WU+B此叠,U是輸入)隨著網(wǎng)絡(luò)深度加深或者在訓(xùn)練過程中,其分布逐漸發(fā)生偏移或者變動(dòng)随珠,之所以訓(xùn)練收斂慢灭袁,一般是整體分布逐漸往非線性函數(shù)的取值區(qū)間的上下限兩端靠近(對于Sigmoid函數(shù)來說,意味著激活輸入值WU+B是大的負(fù)值或正值)窗看,所以這導(dǎo)致反向傳播時(shí)低層神經(jīng)網(wǎng)絡(luò)的梯度消失茸歧,這是訓(xùn)練深層神經(jīng)網(wǎng)絡(luò)收斂越來越慢的本質(zhì)原因,而BN就是通過一定的規(guī)范化手段显沈,把每層神經(jīng)網(wǎng)絡(luò)任意神經(jīng)元這個(gè)輸入值的分布強(qiáng)行拉回到均值為0方差為1的標(biāo)準(zhǔn)正態(tài)分布软瞎,其實(shí)就是把越來越偏的分布強(qiáng)制拉回比較標(biāo)準(zhǔn)的分布逢唤,這樣使得激活輸入值落在非線性函數(shù)對輸入比較敏感的區(qū)域,這樣輸入的小變化就會(huì)導(dǎo)致?lián)p失函數(shù)較大的變化涤浇,意思是這樣讓梯度變大鳖藕,避免梯度消失問題產(chǎn)生,而且梯度變大意味著學(xué)習(xí)收斂速度快只锭,能大大加快訓(xùn)練速度著恩。
1.4
卷積的作用
降維,降低了計(jì)算復(fù)雜度。當(dāng)某個(gè)卷積層輸入的特征數(shù)較多蜻展,對這個(gè)輸入進(jìn)行卷積運(yùn)算將產(chǎn)生巨大的計(jì)算量喉誊;如果對輸入先進(jìn)行降維,減少特征數(shù)后再做卷積計(jì)算量就會(huì)顯著減少纵顾。比如伍茄,一張500 * 500* 100 的圖片,在20個(gè)filter上做1 * 1的卷積,那么結(jié)果的大小為500* 500* 20施逾。
加入非線性,提升網(wǎng)絡(luò)的表達(dá)能力敷矫。比如先進(jìn)行一次普通的卷積(比如3x3),緊跟再進(jìn)行一次1x1的卷積音念,對于某個(gè)像素點(diǎn)來說1x1卷積等效于該像素點(diǎn)在所有特征上進(jìn)行一次全連接的計(jì)算沪饺,無論是第一個(gè)3x3卷積還是新增的1x1卷積,后面都緊跟著激活函數(shù)(比如relu)闷愤。將兩個(gè)卷積串聯(lián)整葡,就能組合出更多的非線性特征。
2. 經(jīng)典卷積神經(jīng)網(wǎng)絡(luò)原理
2.1 LeNet5
-
LeNet5 誕生于 1994 年讥脐,是最早的卷積神經(jīng)網(wǎng)絡(luò)之一遭居,并且推動(dòng)了深度學(xué)習(xí)領(lǐng)域的發(fā)展。LeNet5 的架構(gòu)基于這樣的觀點(diǎn):圖像的特征分布在整張圖像上旬渠,以及帶有可學(xué)習(xí)參數(shù)的卷積是一種用少量參數(shù)在多個(gè)位置上提取相似特征的有效方式俱萍。在那時(shí)候,沒有GPU幫助訓(xùn)練告丢,甚至CPU的速度也很慢枪蘑。因此,能夠保存參數(shù)以及計(jì)算過程是一個(gè)關(guān)鍵進(jìn)展岖免,這和將每個(gè)像素用作一個(gè)大型多層神經(jīng)網(wǎng)絡(luò)的單獨(dú)輸入相反岳颇。LeNet5 闡述了哪些像素不應(yīng)該被使用在第一層,因?yàn)閳D像具有很強(qiáng)的空間相關(guān)性颅湘,而使用圖像中獨(dú)立的像素作為不同的輸入特征則利用不到這些相關(guān)性话侧。LeNet5網(wǎng)絡(luò)結(jié)構(gòu)如下: LeNet5網(wǎng)絡(luò)結(jié)構(gòu)
- LeNet5網(wǎng)絡(luò)結(jié)構(gòu),主要有8層組成:輸入層闯参、C1層瞻鹏、S2層悲立、C3層、S4層新博、C5層薪夕、F6層、輸出層叭披。
- 輸入層(input):這是一個(gè)輸入32* 32的單通道的圖像寥殖。
- C1層:該層為卷積層玩讳。輸入:3232涩蜘,卷積核:6@55,輸出:6@28*28熏纯,步長為1, padding為VALID同诫。
- S2層:該層為下采樣層,也叫池化層樟澜,卷積核:6@22误窖,輸出:6@1414。
- C3層:該層為卷積層秩贰,卷積核:6@55霹俺,輸出:16@1010,步長為1, padding為VALID毒费。
- S4層:該層為池化層丙唧,卷積核:16@22,輸出:16@55觅玻。
- C5層:該層為卷積層想际,卷積核:120@55,輸出:120@11溪厘,步長為1, padding為VALID胡本。
- F6層: 該層為全連接層,采用Sigmoid 函數(shù)進(jìn)行壓縮畸悬,輸出84個(gè)神經(jīng)元侧甫。
- 輸出層:該層由徑向基函數(shù)(RBF)輸出10個(gè)神經(jīng)元。
- LeNet5 網(wǎng)絡(luò)能夠總結(jié)為如下幾點(diǎn):
- 使用卷積蹋宦。參數(shù)較少(局部連接和共享權(quán)重)披粟,在卷積運(yùn)算中,能夠使原信息的特征增強(qiáng)妆档,并且降低噪聲僻爽。
- 下采樣(subsample)。將每個(gè)相鄰區(qū)域的四個(gè)像素求和變?yōu)橐粋€(gè)像素贾惦,殘生一個(gè)縮小1/4的特征映射圖胸梆。
- 多層神經(jīng)網(wǎng)絡(luò)(MLP)作為最后的分類器敦捧,層與層之間的稀疏連接矩陣避免大的計(jì)算成本。
2.2 AlexNet
2012年碰镜,Alex Krizhevsky發(fā)表了Alexet兢卵,它是LeNet的一種更深更寬的版本,并以顯著優(yōu)勢贏得了困難的 ImageNet 競賽绪颖。AlexNet將LeNet的思想擴(kuò)展到了更大的能學(xué)習(xí)到遠(yuǎn)遠(yuǎn)更復(fù)雜的對象與對象層次的神經(jīng)網(wǎng)絡(luò)上秽荤。
-
AlexNet的特點(diǎn),首次在CNN中應(yīng)用ReLU柠横、Dropout和LRN等Trick窃款,同時(shí)使用GPU進(jìn)行運(yùn)算加速。
- (1)成功使用ReLU作為CNN的激活函數(shù)牍氛,并驗(yàn)證其效果在較深的網(wǎng)絡(luò)超過了Sigmoid晨继,成功解決了Sigmoid在網(wǎng)絡(luò)較深時(shí)的梯度彌散問題“峥。基于ReLU的深度卷積網(wǎng)絡(luò)比基于tanh和sigmoid的網(wǎng)絡(luò)訓(xùn)練快數(shù)倍紊扬。使用ReLU后,值域大于等于0,值域是無界的唉擂,而tanh餐屎、sigmoid函數(shù)值域區(qū)間
,值域是有界的玩祟。所以一般在ReLU之后會(huì)做一個(gè)normalization腹缩。雖然ReLU激活函數(shù)在很久之前就被提出了,但是直到AlexNet的出現(xiàn)才將其發(fā)揚(yáng)光大卵凑。
- (2)使用Dropout庆聘。相對于一般如線性模型使用正則的方法來防止模型過擬合,而在神經(jīng)網(wǎng)絡(luò)中Dropout通過修改神經(jīng)網(wǎng)絡(luò)本身結(jié)構(gòu)來實(shí)現(xiàn)勺卢。Dropout雖有單獨(dú)的論文論述伙判,但是AlexNet將其實(shí)用化,通過實(shí)踐證實(shí)了它的效果黑忱。在AlexNet中主要是最后幾個(gè)全連接層使用了Dropout宴抚。
- (3)使用覆蓋最大池化。此前CNN中普遍使用平均池化甫煞,AlexNet全部使用最大池化菇曲,避免平均池化的模糊化效果。并且AlexNet中提出讓步長比池化核的尺寸小抚吠,這樣池化層的輸出之間會(huì)有重疊和覆蓋常潮,提升了特征的豐富性。
- (4)提出了LRN層楷力,對局部神經(jīng)元的活動(dòng)創(chuàng)建競爭機(jī)制喊式,使得其中響應(yīng)比較大的值變得相對更大孵户,并抑制其他反饋較小的神經(jīng)元,增強(qiáng)了模型的泛化能力岔留。
- (5)使用CUDA加速深度卷積網(wǎng)絡(luò)的訓(xùn)練夏哭,利用GPU強(qiáng)大的并行計(jì)算能力,處理神經(jīng)網(wǎng)絡(luò)訓(xùn)練時(shí)大量的矩陣運(yùn)算献联。AlexNet使用了兩塊GTX?580?GPU進(jìn)行訓(xùn)練竖配,單個(gè)GTX?580只有3GB顯存,這限制了可訓(xùn)練的網(wǎng)絡(luò)的最大規(guī)模里逆。因此作者將AlexNet分布在兩個(gè)GPU上进胯,在每個(gè)GPU的顯存中儲(chǔ)存一半的神經(jīng)元的參數(shù)。因?yàn)镚PU之間通信方便运悲,可以互相訪問顯存龄减,而不需要通過主機(jī)內(nèi)存,所以同時(shí)使用多塊GPU也是非常高效的班眯。同時(shí),AlexNet的設(shè)計(jì)讓GPU之間的通信只在網(wǎng)絡(luò)的某些層進(jìn)行烁巫,控制了通信的性能損耗货葬。
- (6)數(shù)據(jù)增強(qiáng)饰迹,隨機(jī)地從
的原始圖像中截取
大小的區(qū)域(以及水平翻轉(zhuǎn)的鏡像),相當(dāng)于增加了
倍的數(shù)據(jù)量。如果沒有數(shù)據(jù)增強(qiáng)酱虎,僅靠原始的數(shù)據(jù)量,參數(shù)眾多的CNN會(huì)陷入過擬合中石洗,使用了數(shù)據(jù)增強(qiáng)后可以大大減輕過擬合缀雳,提升泛化能力。預(yù)測時(shí)渣淳,則取圖片的四個(gè)角加中間共5個(gè)位置脾还,并進(jìn)行左右翻轉(zhuǎn),一共獲得10張圖片入愧,對他們進(jìn)行預(yù)測并對10次結(jié)果求均值鄙漏。同時(shí),AlexNet論文中提到了會(huì)對圖像的RGB數(shù)據(jù)進(jìn)行PCA處理棺蛛,并對主成分做一個(gè)標(biāo)準(zhǔn)差為0.1的高斯擾動(dòng)怔蚌,增加一些噪聲,這個(gè)Trick可以讓錯(cuò)誤率再下降1%旁赊。
- (1)成功使用ReLU作為CNN的激活函數(shù)牍氛,并驗(yàn)證其效果在較深的網(wǎng)絡(luò)超過了Sigmoid晨继,成功解決了Sigmoid在網(wǎng)絡(luò)較深時(shí)的梯度彌散問題“峥。基于ReLU的深度卷積網(wǎng)絡(luò)比基于tanh和sigmoid的網(wǎng)絡(luò)訓(xùn)練快數(shù)倍紊扬。使用ReLU后,值域大于等于0,值域是無界的唉擂,而tanh餐屎、sigmoid函數(shù)值域區(qū)間
-
網(wǎng)絡(luò)結(jié)構(gòu)
AlexNet網(wǎng)絡(luò)結(jié)構(gòu)
2.3 VGG
VGG是Oxford的Visual Geometry Group的組提出的(大家應(yīng)該能看出VGG名字的由來了)桦踊。VGG模型是2014年ILSVRC競賽的第二名,第一名是GoogLeNet终畅。但是VGG模型在多個(gè)遷移學(xué)習(xí)任務(wù)中的表現(xiàn)要優(yōu)于googLeNet籍胯。而且鳄橘,從圖像中提取CNN特征,VGG模型是首選算法芒炼。它的缺點(diǎn)在于瘫怜,參數(shù)量有140M之多,需要更大的存儲(chǔ)空間本刽。它證明了增加網(wǎng)絡(luò)的深度能夠在一定程度上影響網(wǎng)絡(luò)最終的性能鲸湃。VGG有兩種結(jié)構(gòu),分別是VGG16和VGG19子寓,兩者并沒有本質(zhì)上的區(qū)別暗挑,只是網(wǎng)絡(luò)深度不一樣。參考文章
VGG16包含了16個(gè)隱藏層(13個(gè)卷積層和3個(gè)全連接層)斜友,VGG19包含了19個(gè)隱藏層(16個(gè)卷積層和3個(gè)全連接層)炸裆。VGG網(wǎng)絡(luò)的結(jié)構(gòu)非常一致,從頭到尾全部使用的是3x3的卷積和2x2的max pooling鲜屏。
VGG16相比AlexNet的一個(gè)改進(jìn)是采用使用了3個(gè)3x3卷積核來代替7x7卷積核烹看,使用了2個(gè)3x3卷積核來代替5* 5卷積核(AlexNet的9×9或11×11過濾器)。對于給定的感受野(與輸出有關(guān)的輸入圖片的局部大新迨贰)惯殊,采用堆積的小卷積核是優(yōu)于采用大的卷積核,因?yàn)槎鄬臃蔷€性層可以增加網(wǎng)絡(luò)深度來保證學(xué)習(xí)更復(fù)雜的模式也殖,而且代價(jià)還比較型了肌(參數(shù)更少)。
VGGNet則清一色使用3x3卷積忆嗜。因?yàn)榫矸e不僅涉及到計(jì)算量己儒,還影響到感受野。前者關(guān)系到是否方便部署到移動(dòng)端捆毫、是否能滿足實(shí)時(shí)處理闪湾、是否易于訓(xùn)練等,后者關(guān)系到參數(shù)更新冻璃、特征圖的大小响谓、特征是否提取的足夠多、模型的復(fù)雜度和參數(shù)量等等省艳。作者認(rèn)為兩個(gè)3x3的卷積堆疊獲得的感受野大小娘纷,相當(dāng)一個(gè)5x5的卷積;而3個(gè)3x3卷積的堆疊獲取到的感受野相當(dāng)于一個(gè)7x7的卷積跋炕。
feature map維度的整體變化過程是:先將local信息壓縮赖晶,并分?jǐn)偟絚hannel層級(jí),然后無視channel和local,通過fc這個(gè)變換再進(jìn)一步壓縮為稠密的feature map遏插,這樣對于分類器而言有好處也有壞處捂贿,好處是將local信息隱藏于/壓縮到feature map中,壞處是信息壓縮都是有損失的胳嘲,相當(dāng)于local信息被破壞了(分類器沒有考慮到厂僧,其實(shí)對于圖像任務(wù)而言,單張feature map上的local信息還是有用的)了牛。
關(guān)于池化颜屠。特征信息從一開始輸入的224x224x3被變換到7x7x512,從原本較為local的信息逐漸分?jǐn)偟讲煌琧hannel上鹰祸,隨著每次的conv和pool操作打散到channel層級(jí)上甫窟。不難發(fā)現(xiàn),卷積只增加feature map的通道數(shù)蛙婴,而池化只減少feature map的寬高粗井。如今也有不少做法用大stride卷積去替代池化,未來可能沒有池化街图。
關(guān)于全連接浇衬。維度在最后一個(gè)卷積后達(dá)到7x7x512=25088,緊接著壓縮到4096維台夺,可能是作者認(rèn)為這個(gè)過程太急径玖,又接一個(gè)fc4096作為緩沖,同時(shí)兩個(gè)fc4096后的relu又接dropout0.5去過渡這個(gè)過程颤介,因?yàn)樽詈蠹磳⒔o1k-way softmax,所以又接了一個(gè)fc1000去降低softmax的學(xué)習(xí)壓力赞赖。VGG最后三個(gè)全連接層在形式上完全平移AlexNet的最后三層滚朵,超參數(shù)上只有最后一層fc有變化:bias的初始值,由AlexNet的0變?yōu)?.1前域,該層初始化高斯分布的標(biāo)準(zhǔn)差辕近,由AlexNet的0.01變?yōu)?.005。超參數(shù)的變化匿垄,提出者自己的感性理解指導(dǎo)認(rèn)為移宅,以貢獻(xiàn)bias來降低標(biāo)準(zhǔn)差,相當(dāng)于標(biāo)準(zhǔn)差和bias間trade-off椿疗,或許提出者實(shí)驗(yàn)validate發(fā)現(xiàn)這個(gè)值比之前AlexNet設(shè)置的(std=0.01漏峰,bias=0)要更好。
關(guān)于全連接轉(zhuǎn)卷積届榄。作者在測試階段把網(wǎng)絡(luò)中原本的三個(gè)全連接層依次變?yōu)?個(gè)conv7x7浅乔,2個(gè)conv1x1,也就是三個(gè)卷積層。改變之后靖苇,整個(gè)網(wǎng)絡(luò)由于沒有了全連接層席噩,網(wǎng)絡(luò)中間的feature map不會(huì)固定,所以網(wǎng)絡(luò)對任意大小的輸入都可以處理贤壁,因而作者在緊接著的后一句說到: The resulting fully-convolutional net is then applied to the whole (uncropped) image悼枢。
1x1卷積核。VGG在最后的三個(gè)階段都用到了1x1卷積核脾拆,選用1x1卷積核的最直接原因是在維度上繼承全連接馒索,然而作者首先認(rèn)為1x1卷積可以增加決策函數(shù)(decision function,這里的決策函數(shù)就是softmax)的非線性能力假丧,非線性是由激活函數(shù)ReLU決定的双揪,本身1x1卷積則是線性映射,即將輸入的feature map映射到同樣維度的feature map包帚。
同樣stride下渔期,不同卷積核大小的特征圖和卷積參數(shù)差別不大;越大的卷積核計(jì)算量越大渴邦。
-
VGG網(wǎng)絡(luò)結(jié)構(gòu)如下:
VGG網(wǎng)絡(luò)結(jié)構(gòu) -
VGG16 各層的結(jié)構(gòu)和參數(shù)如下:
- C1-1層:卷積層
- 輸入:224 x 224 x 3疯趟;濾波器:64@3 x 3 x 3;輸出:224 x 224 x 64
- C1-2層:卷積層
- 輸入:224 x 224 x 64谋梭;濾波器:64@3 x 3 x 3信峻;輸出:224 x 224 x 64
- P1層:池化層
- 輸入:224 x 224 x 64; 濾波器:64@2 x 2瓮床;輸出:112 x 112 x 64
- C2-1層:卷積層
- 輸入:112 x 112 x 64盹舞; 濾波器:128@3 x 3 x 64;輸出:112 x 112 x 128
- C2-2層:卷積層
- 輸入:112 x 112 x 64隘庄; 濾波器:128@3 x 3 x 64踢步; 輸出:112 x 112 x 128
- P2層:池化層
- 輸入:112 x 112 x 128; 濾波器:128@2 x 2丑掺; 輸出:56 x 56 x 128
- C3-1層:卷積層
- 輸入:56 x 56 x 128获印; 濾波器:256@3 x 3 x 128; 輸出:56 x 56 x 256
- C3-2層:卷積層
- 輸入:56 x 56 x 128街州; 濾波器:256@3 x 3 x 256兼丰; 輸出:56 x 56 x 256
- C3-3層:卷積層
- 輸入:56 x 56 x 256; 濾波器:256@3 x 3 x 256唆缴; 輸出:56 x 56 x 256
- P3層:池化層
- 輸入:56 x 56 x 256鳍征; 濾波器:256@2 x 2; 輸出:28 x 28 x 256
- C4-1層:卷積層
- 輸入:28 x 28 x 256琐谤; 濾波器:512@3 x 3 x 256蟆技; 輸出:28 x 28 x 512
- C4-2層:卷積層
- 輸入:28 x 28 x 512; 濾波器:512@3 x 3 x 256; 輸出:28 x 28 x 512
- C4-3層:卷積層
- 輸入:28 x 28 x 512质礼; 濾波器:512@3 x 3 x 256旺聚; 輸出:28 x 28 x 512
- P4層:池化層
- 輸入:28 x 28 x 512; 濾波器:512@2 x 2眶蕉; 輸出:14 x 14 x 512
- C5-1層:卷積層
- 輸入:14 x 14 x 512砰粹; 濾波器:512@3 x 3 x 512; 輸出:14 x 14 x 512
- C5-2層:卷積層
- 輸入:14 x 14 x 512造挽; 濾波器:512@3 x 3 x 512碱璃; 輸出:14 x 14 x 512
- C5-3層:卷積層
- 輸入:14 x 14 x 512; 濾波器:512@3 x 3 x 512饭入; 輸出:14 x 14 x 512
- P5層:池化層
- 輸入:14 x 14 x 512嵌器; 濾波器:512@2 x 2; 輸出:7 x 7 x 512
- F6層:全連接層谐丢,高斯分布初始化(std=0.005)爽航,bias常數(shù)初始化(0.1)
- 輸入:25088;輸出:4096
- F7層是個(gè)全連接層乾忱,高斯分布初始化(std=0.005)讥珍,bias常數(shù)初始化(0.1)
- 輸入:4096;輸出:4096
- F8層:全連接層窄瘟,高斯分布初始化(std=0.005)衷佃,bias常數(shù)初始化(0.1)
- 輸入:4096;輸出:1000
- 輸出層:
- soft_max
- C1-1層:卷積層
-
VGG優(yōu)點(diǎn)
- 小卷積核蹄葱。將卷積核全部替換為3x3(極少用了1x1)氏义。幾個(gè)小濾波器(3x3)卷積層的組合比一個(gè)大濾波器(5x5或7x7)卷積層好。
- 小池化核图云。相比AlexNet的3x3的池化核觅赊,VGG全部為2x2的池化核;
- 層數(shù)更深,特征圖更寬琼稻。由于卷積核專注于擴(kuò)大通道數(shù)、池化專注于縮小寬和高饶囚,使得模型架構(gòu)上更深更寬的同時(shí)帕翻,計(jì)算量的增加放緩;
- 全連接轉(zhuǎn)卷積萝风。網(wǎng)絡(luò)測試階段將訓(xùn)練階段的三個(gè)全連接替換為三個(gè)卷積嘀掸,測試重用訓(xùn)練時(shí)的參數(shù),使得測試得到的全卷積網(wǎng)絡(luò)因?yàn)闆]有全連接的限制规惰,因而可以接收任意寬或高為的輸入睬塌。
-
VGG缺點(diǎn)
- VGG耗費(fèi)更多計(jì)算資源,其中絕大多數(shù)的參數(shù)都是來自于第一個(gè)全連接層,VGG有3個(gè)全連接層揩晴,使用了更多的參數(shù)導(dǎo)致更多的內(nèi)存占用勋陪。
2.4 GoogLeNet
-
GoogLeNet是2014年Christian Szegedy提出的一種全新的深度學(xué)習(xí)結(jié)構(gòu),在這之前的AlexNet硫兰、VGG等結(jié)構(gòu)都是通過增大網(wǎng)絡(luò)的深度(層數(shù))來獲得更好的訓(xùn)練效果诅愚,但層數(shù)的增加會(huì)帶來很多負(fù)作用,比如overfit劫映、梯度消失违孝、梯度爆炸等。因此設(shè)計(jì)出 GoogLeNet——第一個(gè)Inception架構(gòu)泳赋,inception的提出追求減少深度來提升訓(xùn)練結(jié)果雌桑,能更高效的利用計(jì)算資源,在相同的計(jì)算量下能提取到更多的特征祖今,從而提升訓(xùn)練結(jié)果校坑。模型結(jié)構(gòu)如下,InceptionNet-v1
inception結(jié)構(gòu)的主要貢獻(xiàn)有兩個(gè):一是使用1x1的卷積來進(jìn)行升降維和增加信息衅鹿;二是在多個(gè)尺寸上同時(shí)進(jìn)行卷積再聚合撒踪。
-
多個(gè)尺寸上進(jìn)行卷積再聚合。InceptionNet-v1網(wǎng)絡(luò)結(jié)構(gòu)可以看到對輸入做了4個(gè)分支大渤,分別用不同尺寸的filter進(jìn)行卷積或池化制妄,最后再在特征維度上拼接到一起。這種全新的結(jié)構(gòu)有什么好處呢泵三?Szegedy從多個(gè)角度進(jìn)行了解釋:
解釋1:在直觀感覺上在多個(gè)尺度上同時(shí)進(jìn)行卷積耕捞,能提取到不同尺度的特征。特征更為豐富也意味著最后分類判斷時(shí)更加準(zhǔn)確烫幕。
-
解釋2:利用稀疏矩陣分解成密集矩陣計(jì)算的原理來加快收斂速度俺抽。舉個(gè)例子圖四左側(cè)是個(gè)稀疏矩陣(很多元素都為0,不均勻分布在矩陣中)较曼,和一個(gè)2x2的矩陣進(jìn)行卷積磷斧,需要對稀疏矩陣中的每一個(gè)元素進(jìn)行計(jì)算;如果像右圖那樣把稀疏矩陣分解成2個(gè)子密集矩陣捷犹,再和2x2矩陣進(jìn)行卷積弛饭,稀疏矩陣中0較多的區(qū)域就可以不用計(jì)算,計(jì)算量就大大降低萍歉。這個(gè)原理應(yīng)用到inception上就是要在特征維度上進(jìn)行分解侣颂!傳統(tǒng)的卷積層的輸入數(shù)據(jù)只和一種尺度(比如3x3)的卷積核進(jìn)行卷積,輸出固定維度(比如256個(gè)特征)的數(shù)據(jù)枪孩,所有256個(gè)輸出特征基本上是均勻分布在3x3尺度范圍上憔晒,這可以理解成輸出了一個(gè)稀疏分布的特征集藻肄;而inception模塊在多個(gè)尺度上提取特征(比如1x1,3x3拒担,5x5)嘹屯,輸出的256個(gè)特征就不再是均勻分布,而是相關(guān)性強(qiáng)的特征聚集在一起(比如1x1的的96個(gè)特征聚集在一起澎蛛,3x3的96個(gè)特征聚集在一起抚垄,5x5的64個(gè)特征聚集在一起),這可以理解成多個(gè)密集分布的子特征集谋逻。這樣的特征集中因?yàn)橄嚓P(guān)性較強(qiáng)的特征聚集在了一起呆馁,不相關(guān)的非關(guān)鍵特征就被弱化,同樣是輸出256個(gè)特征毁兆,inception方法輸出的特征“冗余”的信息較少浙滤。用這樣的“純”的特征集層層傳遞最后作為反向計(jì)算的輸入,自然收斂的速度更快气堕。稀疏矩陣分解
解釋3:Hebbin赫布原理纺腊。Hebbin原理是神經(jīng)科學(xué)上的一個(gè)理論,解釋了在學(xué)習(xí)的過程中腦中的神經(jīng)元所發(fā)生的變化茎芭,用一句話概括就是fire togethter, wire together揖膜。赫布認(rèn)為“兩個(gè)神經(jīng)元或者神經(jīng)元系統(tǒng),如果總是同時(shí)興奮梅桩,就會(huì)形成一種‘組合’壹粟,其中一個(gè)神經(jīng)元的興奮會(huì)促進(jìn)另一個(gè)的興奮”。比如狗看到肉會(huì)流口水宿百,反復(fù)刺激后趁仙,腦中識(shí)別肉的神經(jīng)元會(huì)和掌管唾液分泌的神經(jīng)元會(huì)相互促進(jìn),“纏繞”在一起垦页,以后再看到肉就會(huì)更快流出口水雀费。用在inception結(jié)構(gòu)中就是要把相關(guān)性強(qiáng)的特征匯聚到一起。這有點(diǎn)類似上面的解釋2痊焊,把1x1盏袄,3x3,5x5的特征分開薄啥。因?yàn)橛?xùn)練收斂的最終目的就是要提取出獨(dú)立的特征貌矿,所以預(yù)先把相關(guān)性強(qiáng)的特征匯聚,就能起到加速收斂的作用罪佳。
關(guān)于v1,v2,v3,v4版本,請?jiān)L問我以前寫的這篇文章黑低,不確定設(shè)置的是不是隱秘哈哈哈哈哈啊哈
2.5 ResNet
- 殘差網(wǎng)絡(luò)是由來自Microsoft Research的4位學(xué)者提出的卷積神經(jīng)網(wǎng)絡(luò)赘艳,在2015年的ImageNet大規(guī)模視覺識(shí)別競賽(ILSVRC)中獲得了圖像分類和物體識(shí)別的優(yōu)勝酌毡。殘差網(wǎng)絡(luò)的特點(diǎn)是容易優(yōu)化,并且能夠通過增加相當(dāng)?shù)纳疃葋硖岣邷?zhǔn)確率蕾管。其內(nèi)部的殘差塊使用了跳躍連接枷踏,緩解了在深度神經(jīng)網(wǎng)絡(luò)中增加深度帶來的梯度消失問題。
2.5.1 背景
- 我們都知道增加網(wǎng)絡(luò)的寬度和深度可以很好的提高網(wǎng)絡(luò)的性能掰曾,深的網(wǎng)絡(luò)一般都比淺的的網(wǎng)絡(luò)效果好旭蠕,比如說一個(gè)深的網(wǎng)絡(luò)A和一個(gè)淺的網(wǎng)絡(luò)B,那A的性能至少都能跟B一樣旷坦,為什么呢掏熬?因?yàn)榫退阄覀儼袯的網(wǎng)絡(luò)參數(shù)全部遷移到A的前面幾層,而A后面的層只是做一個(gè)等價(jià)的映射秒梅,就達(dá)到了B網(wǎng)絡(luò)的一樣的效果旗芬。在深度學(xué)習(xí)中,網(wǎng)絡(luò)層數(shù)增多一般會(huì)伴著下面幾個(gè)問題捆蜀?
- 梯度彌散或梯度爆炸疮丛。對于原來的網(wǎng)絡(luò),如果簡單地增加深度辆它,會(huì)導(dǎo)致梯度彌散或梯度爆炸誊薄,對于該問題的解決方法是正則化初始化和中間的正則化層(Batch Normalization)。
- 計(jì)算資源的消耗锰茉。通過GPU集群來解決呢蔫。
- 過擬合。過擬合通過采集海量數(shù)據(jù)洞辣,并配合Dropout正則化等方法可以有效避免咐刨;
- 退化問題。網(wǎng)絡(luò)層數(shù)增加扬霜,但是在訓(xùn)練集上的準(zhǔn)確率卻飽和甚至下降了定鸟。這個(gè)不能解釋為overfitting,因?yàn)閛verfit應(yīng)該表現(xiàn)為在訓(xùn)練集上表現(xiàn)更好才對著瓶。退化問題說明了深度網(wǎng)絡(luò)不能很簡單地被很好地優(yōu)化联予。
- 作者通過實(shí)驗(yàn):通過淺層網(wǎng)絡(luò)等同映射構(gòu)造深層模型,結(jié)果深層模型并沒有比淺層網(wǎng)絡(luò)有等同或更低的錯(cuò)誤率材原,推斷退化問題可能是因?yàn)樯顚拥木W(wǎng)絡(luò)并不是那么好訓(xùn)練沸久,也就是求解器很難去利用多層網(wǎng)絡(luò)擬合同等函數(shù)。
2.5.3 網(wǎng)絡(luò)結(jié)構(gòu)
-
下圖展示不同layer的ResNet.
ResNet 不同層數(shù)的網(wǎng)絡(luò)配置
在使用ResNet網(wǎng)絡(luò)結(jié)構(gòu)時(shí)余蟹,發(fā)現(xiàn)層數(shù)不斷加深導(dǎo)致訓(xùn)練集上誤差在增大的現(xiàn)象被消除了卷胯,并且在測試集上的表現(xiàn)也變好了。在2015年ResNet推出不久后威酒,Google就借鑒了ResNet的精髓窑睁,2016年提出了InceptionV4的Inception-ResNet-V2挺峡。在2016年作者有提出了ResNet-V2,V1和V2的主要區(qū)別在于担钮,作者發(fā)現(xiàn)殘差單元的傳播方式中橱赠,前饋和后饋的信息可以直接傳輸,因此將skip connection的非線性激活函數(shù)替換為Identity Mappings(y=x)箫津,同時(shí)v2在每一層都使用了Batch Normalization狭姨,新的殘差單元比以前的泛化性能更強(qiáng)了。有學(xué)者認(rèn)為苏遥,ResNet類似于一個(gè)沒有g(shù)ates的LSTM網(wǎng)絡(luò)饼拍,輸入X傳遞到后面層的過程一直發(fā)生,而不是學(xué)習(xí)出來的暖眼。也有人認(rèn)為ResNet的效果類似于在多層網(wǎng)絡(luò)間的集成(ensemble)惕耕。The Power of Depth for Feedforward Neural Networks證明了加深網(wǎng)絡(luò)比加寬網(wǎng)絡(luò)更有效。 -
通過同樣是34層的卷積神經(jīng)網(wǎng)絡(luò)诫肠,下圖展示VGGNet-19司澎、普通的卷積神經(jīng)網(wǎng)絡(luò)和ResNet的對比,可以看到ResNet和普通神經(jīng)網(wǎng)絡(luò)的最大區(qū)別在于栋豫,ResNet有很多支線連接到后面的層挤安,使得后面的層可以直接學(xué)習(xí)殘差,這種結(jié)構(gòu)被稱為shortcut或skip connections丧鸯。VGGNet-19蛤铜、普通的卷積神經(jīng)網(wǎng)絡(luò)、ResNet
-
ResNet使用了一個(gè)非常深的CNN丛肢,有50,101,152,200層等围肥。能夠訓(xùn)練如此深的網(wǎng)絡(luò)的關(guān)鍵是使用跳過連接(skip connection,也稱為快捷連接),一個(gè)層的輸入信號(hào)也被添加到位于下一層的輸出蜂怎。當(dāng)訓(xùn)練一個(gè)神經(jīng)網(wǎng)絡(luò)時(shí)穆刻,目標(biāo)是使其模擬一個(gè)目標(biāo)函數(shù)h(x)。如果將輸入x添加到網(wǎng)絡(luò)的輸出中(即添加跳過連接)杠步,那么網(wǎng)絡(luò)將被迫模擬
而不是h(x)氢伟。這被稱為殘留學(xué)習(xí)(見下圖,右圖是2016年改進(jìn)后的resnet)。
RESNET短鏈接 -
作者還提出了兩層幽歼、三層的殘差學(xué)習(xí)單元朵锣。兩層的殘差學(xué)習(xí)單元中包含兩個(gè)相同輸出通道數(shù)的
卷積;而三層的殘差單元,第二層使用了
的卷積甸私,第一和第三層使用了NetworkInNetwork 和 InceptionNet中的
卷積诚些,有先降維再升維的操作。此外皇型,如果網(wǎng)絡(luò)的輸入和輸出維度不同泣刹,需要對x做一個(gè)映射助析。再連接到后面的維度。
兩層椅您、三層的ResNet殘差模塊
優(yōu)點(diǎn)
- (1)減少信息丟失。ResNet通過直接將輸入信息短連接到輸出寡键,保護(hù)信息的完整性掀泳。
- (2)簡化學(xué)習(xí)目標(biāo)和難度。整個(gè)網(wǎng)絡(luò)只需要學(xué)習(xí)輸入西轩、輸出差別的那一部分员舵。
tensorflow2 代碼實(shí)現(xiàn)
關(guān)于網(wǎng)絡(luò)的實(shí)現(xiàn),網(wǎng)上有很多代碼藕畔,大家可以根據(jù)不同的框架和編碼習(xí)慣马僻,挑選一篇。
# TensorFlow實(shí)現(xiàn)LeNet
import tensorflow as tf
def inference(inputs):
# input shape: [batch, height, width, 1]
with tf.variable_scope('conv1'):
weights = tf.Variable(tf.truncated_normal([5, 5, 1, 6], stddev=0.1))
biases = tf.Variable(tf.zeros([6]))
conv1 = tf.nn.conv2d(inputs, weights, strides=[1, 1, 1, 1], padding='VALID')
conv1 = tf.nn.relu(tf.nn.bias_add(conv1, biases))
maxpool2 = tf.nn.max_pool(conv1, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1])
with tf.variable_scope('conv3'):
weights = tf.Variable(tf.truncated_normal([5, 5, 6, 16], stddev=0.1))
biases = tf.Variable(tf.zeros([16]))
conv3 = tf.nn.conv2d(maxpool2, weights, strides=[1, 1, 1, 1])
conv3 = tf.nn.relu(tf.nn.bias_add(conv3, biases))
maxpool4 = tf.nn.max_pool(conv3, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1])
with tf.variable_scope('conv5'):
weights = tf.Variable(tf.truncated_normal([5, 5, 6, 16], stddev=0.1))
biases = tf.Variable(tf.zeros([16]))
conv5 = tf.nn.conv2d(maxpool4, weights, strides=[1, 1, 1, 1])
conv5 = tf.nn.relu(tf.nn.bias_add(conv5, biases))
with tf.variable_scope('fc6'):
flat = tf.reshape(conv5, [-1, 120])
weights = tf.Variable(tf.truncated_normal([120, 84], stddev=0.1))
biases = tf.Variable(tf.zeros([84]))
fc6 = tf.nn.matmul(flat, weights) + biases
fc6 = tf.nn.relu(fc6)
with tf.variable_scope('fc7'):
weights = tf.Variable(tf.truncated_normal([84, 10], stddev=0.1))
biases = tf.Variable(tf.zeros([10]))
fc7 = tf.nn.matmul(fc6, weights) + biases
fc7 = tf.nn.softmax(fc7)
return fc7
"""
tf2 搭建ResNet AlexNet 實(shí)戰(zhàn)
"""
import tensorflow as tf
from tensorflow.keras.datasets import fashion_mnist
from tensorflow import data as tfdata
import numpy as np
# 將 GPU 的顯存使用策略設(shè)置為 “僅在需要時(shí)申請顯存空間”注服。
for gpu in tf.config.experimental.list_physical_devices('GPU'):
tf.config.experimental.set_memory_growth(gpu, True)
# 1韭邓、讀取數(shù)據(jù)
'''由于Imagenet數(shù)據(jù)集是一個(gè)比較龐大的數(shù)據(jù)集,且網(wǎng)絡(luò)的輸入為224*224溶弟,所以女淑,我們定義一個(gè)方法,
來讀取數(shù)據(jù)集并將數(shù)據(jù)resize到224*224的大小'''
class Data_load():
def __init__(self):
fashion_mnist = tf.keras.datasets.fashion_mnist
(self.train_images, self.train_labels), (self.test_images, self.test_labels)\
= fashion_mnist.load_data()
# 數(shù)據(jù)維度擴(kuò)充成[n,h,w,c]的模式
self.train_images = np.expand_dims(self.train_images.astype(np.float32) / 255.0, axis=-1)
self.test_images = np.expand_dims(self.test_images.astype(np.float32)/255.0,axis=-1)
# 標(biāo)簽
self.train_labels = self.train_labels.astype(np.int32)
self.test_labels = self.test_labels.astype(np.int32)
# 訓(xùn)練和測試的數(shù)據(jù)個(gè)數(shù)
self.num_train, self.num_test = self.train_images.shape[0], self.test_images.shape[0]
def get_train_batch(self,batch_size):
# 隨機(jī)取batch_size個(gè)索引
index = np.random.randint(0, np.shape(self.train_images)[0], batch_size)
# resize
resized_images = tf.image.resize_with_pad(self.train_images[index], 224, 224 )
return resized_images.numpy(), self.train_labels[index]
def get_test_batch(self,batch_size):
index = np.random.randint(0, np.shape(self.test_images)[0], batch_size)
# resize
resized_images = tf.image.resize_with_pad(self.test_images[index], 224, 224 )
return resized_images.numpy(), self.test_labels[index]
# 2辜御、定義模型
def MyAlexNet():
net=tf.keras.Sequential()
net.add(tf.keras.layers.Conv2D(96,11,(4,4),"same",activation="relu"))
net.add(tf.keras.layers.MaxPool2D(pool_size=3, strides=2))
net.add(tf.keras.layers.Conv2D(filters=256, kernel_size=5, padding='same', activation='relu'))
net.add(tf.keras.layers.MaxPool2D(pool_size=3, strides=2))
net.add(tf.keras.layers.Conv2D(filters=384, kernel_size=3, padding='same', activation='relu'))
net.add(tf.keras.layers.Conv2D(filters=384, kernel_size=3, padding='same', activation='relu'))
net.add(tf.keras.layers.Conv2D(filters=256, kernel_size=3, padding='same', activation='relu'))
net.add(tf.keras.layers.MaxPool2D(pool_size=3, strides=2))
net.add(tf.keras.layers.Flatten())
net.add(tf.keras.layers.Dense(512, activation='relu'))# 為了方便訓(xùn)練 神經(jīng)元個(gè)數(shù)改小鸭你,原來是1024
net.add(tf.keras.layers.Dropout(0.5))
net.add(tf.keras.layers.Dense(256, activation='relu'))# 為了方便訓(xùn)練 神經(jīng)元個(gè)數(shù)改小,原來是1024
net.add(tf.keras.layers.Dropout(0.5))
net.add(tf.keras.layers.Dense(10, activation='sigmoid'))
return net
def train(num_epoches,net):
optimizer = tf.keras.optimizers.SGD(learning_rate=0.01, momentum=0.0, nesterov=False)
net.compile(optimizer=optimizer,
loss='sparse_categorical_crossentropy',
metrics=['accuracy'])
num_iter = data_load.num_train // batch_size
for e in range(num_epoches):
for n in range(num_iter):
x_batch, y_batch = data_load.get_train_batch(batch_size)
test_x_batch, test_y_batch = data_load.get_test_batch(batch_size)
net.fit(x_batch, y_batch,validation_data=(test_x_batch, test_y_batch))
if __name__ == '__main__':
# 加載數(shù)據(jù)
batch_size=64
data_load=Data_load()
x_train_batch,y_train_batch=data_load.get_train_batch(batch_size)
print("x_batch shape:",x_train_batch.shape,"y_batch shape:", y_train_batch.shape)
# 加載網(wǎng)絡(luò)結(jié)構(gòu)
net=MyAlexNet()
X = tf.random.uniform((1,224,224,1))
for layer in net.layers:
X = layer(X)
print(layer.name, 'output shape\t', X.shape)
# 訓(xùn)練
num_epoches = 1
train(num_epoches, net)
"""
tf2 VGG13 實(shí)戰(zhàn)
"""
import tensorflow as tf
from tensorflow.keras import layers, optimizers, datasets, Sequential
import os
os.environ['TF_CPP_MIN_LOG_LEVEL']='2'
tf.random.set_seed(1)
# 定義卷積層
conv_layers = [ # 5 units of conv + max pooling
# unit 1
layers.Conv2D(64, kernel_size=[3, 3], padding="same", activation=tf.nn.relu),
layers.Conv2D(64, kernel_size=[3, 3], padding="same", activation=tf.nn.relu),
layers.MaxPool2D(pool_size=[2, 2], strides=2, padding='same'),
# unit 2
layers.Conv2D(128, kernel_size=[3, 3], padding="same", activation=tf.nn.relu),
layers.Conv2D(128, kernel_size=[3, 3], padding="same", activation=tf.nn.relu),
layers.MaxPool2D(pool_size=[2, 2], strides=2, padding='same'),
# unit 3
layers.Conv2D(256, kernel_size=[3, 3], padding="same", activation=tf.nn.relu),
layers.Conv2D(256, kernel_size=[3, 3], padding="same", activation=tf.nn.relu),
layers.MaxPool2D(pool_size=[2, 2], strides=2, padding='same'),
# unit 4
layers.Conv2D(512, kernel_size=[3, 3], padding="same", activation=tf.nn.relu),
layers.Conv2D(512, kernel_size=[3, 3], padding="same", activation=tf.nn.relu),
layers.MaxPool2D(pool_size=[2, 2], strides=2, padding='same'),
# unit 5
layers.Conv2D(512, kernel_size=[3, 3], padding="same", activation=tf.nn.relu),
layers.Conv2D(512, kernel_size=[3, 3], padding="same", activation=tf.nn.relu),
layers.MaxPool2D(pool_size=[2, 2], strides=2, padding='same')
]
# 定義全連接層
fc_layers = [
layers.Dense(256, activation=tf.nn.relu),
layers.Dense(128, activation=tf.nn.relu),
layers.Dense(100, activation=None),
]
def preprocess(x, y):
# [0~1]
x = tf.cast(x, dtype=tf.float32) / 255.
y = tf.cast(y, dtype=tf.int32)
return x,y
def main():
# [b, 32, 32, 3] => [b, 1, 1, 512]
conv_net = Sequential(conv_layers)
fc_net = Sequential(fc_layers)
conv_net.build(input_shape=[None, 32, 32, 3])
fc_net.build(input_shape=[None, 512])
optimizer = optimizers.Adam(lr=1e-4)
# [1, 2] + [3, 4] => [1, 2, 3, 4]
variables = conv_net.trainable_variables + fc_net.trainable_variables
for epoch in range(2):
for step, (x,y) in enumerate(train_db):
with tf.GradientTape() as tape:
out = conv_net(x) # [b, 32, 32, 3] => [b, 1, 1, 512]
out = tf.reshape(out, [-1, 512]) # flatten, => [b, 512]
logits = fc_net(out) # [b, 512] => [b, 100]
y_onehot = tf.one_hot(y, depth=100) # [b] => [b, 100]
# compute loss
loss = tf.losses.categorical_crossentropy(y_onehot, logits, from_logits=True)
loss = tf.reduce_mean(loss)
grads = tape.gradient(loss, variables)
optimizer.apply_gradients(zip(grads, variables))
if step %100 == 0:
print(epoch, step, 'loss:', float(loss))
total_num = 0
total_correct = 0
for x,y in test_db:
out = conv_net(x)
out = tf.reshape(out, [-1, 512])
logits = fc_net(out)
prob = tf.nn.softmax(logits, axis=1)
pred = tf.argmax(prob, axis=1)
pred = tf.cast(pred, dtype=tf.int32)
correct = tf.cast(tf.equal(pred, y), dtype=tf.int32)
correct = tf.reduce_sum(correct)
total_num += x.shape[0]
total_correct += int(correct)
acc = total_correct / total_num
print(epoch, 'acc:', acc)
if __name__ == '__main__':
(x,y), (x_test, y_test) = datasets.cifar100.load_data()
y = tf.squeeze(y, axis=1)
y_test = tf.squeeze(y_test, axis=1)
print(x.shape, y.shape, x_test.shape, y_test.shape)
train_db = tf.data.Dataset.from_tensor_slices((x,y))
train_db = train_db.shuffle(1000).map(preprocess).batch(128)
test_db = tf.data.Dataset.from_tensor_slices((x_test,y_test))
test_db = test_db.map(preprocess).batch(64)
sample = next(iter(train_db))
print('sample: \n', sample[0].shape, sample[1].shape,
tf.reduce_min(sample[0]), tf.reduce_max(sample[0]))
main()
'''
tf2 搭建ResNet
'''
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers, Sequential
class BasicBlock(layers.Layer):
def __init__(self, filter_num, stride=1):
super(BasicBlock, self).__init__()
self.conv1 = layers.Conv2D(filter_num, (3, 3), strides=stride, padding='same')
self.bn1 = layers.BatchNormalization()
self.relu = layers.Activation('relu')
self.conv2 = layers.Conv2D(filter_num, (3, 3), strides=1, padding='same')
self.bn2 = layers.BatchNormalization()
if stride != 1:
self.downsample = Sequential()
self.downsample.add(layers.Conv2D(filter_num, (1, 1), strides=stride))
else:
self.downsample = lambda x:x
def call(self, inputs, training=None):
# [b, h, w, c]
out = self.conv1(inputs)
out = self.bn1(out)
out = self.relu(out)
out = self.conv2(out)
out = self.bn2(out)
identity = self.downsample(inputs)
output = layers.add([out, identity])
output = tf.nn.relu(output)
return output
# Res Block 模塊擒权。繼承keras.Model或者keras.Layer都可以
class ResNet(keras.Model):
# 第一個(gè)參數(shù)layer_dims:[2, 2, 2, 2] 4個(gè)Res Block袱巨,每個(gè)包含2個(gè)Basic Block
# 第二個(gè)參數(shù)num_classes:我們的全連接輸出,取決于輸出有多少類碳抄。
def __init__(self, layer_dims, num_classes=100): # [2, 2, 2, 2]
super(ResNet, self).__init__()
# 預(yù)處理層愉老;實(shí)現(xiàn)起來比較靈活可以加 MAXPool2D,
# 從頭到尾的順序纳鼎,對多個(gè)網(wǎng)絡(luò)層的線性堆疊俺夕。使用.add()方法將各層添加到模型中
self.stem = Sequential([layers.Conv2D(64, (3, 3), strides=(1, 1), padding='valid'),
layers.BatchNormalization(),
layers.Activation('relu'),
layers.MaxPool2D(pool_size=(2, 2), strides=(1, 1), padding='same')
])
# 創(chuàng)建4個(gè)Res Block;
self.layer1 = self.build_resblock(64, layer_dims[0])
self.layer2 = self.build_resblock(128, layer_dims[1], stride=2)
self.layer3 = self.build_resblock(256, layer_dims[2], stride=2)
self.layer4 = self.build_resblock(512, layer_dims[3], stride=2)
# gap:減少參數(shù)
self.gap = layers.GlobalAveragePooling2D()
self.fc = layers.Dense(num_classes)
def call(self, inputs, training=None):
# 前向運(yùn)算
x = self.stem(inputs)
x = self.layer1(x)
x = self.layer2(x)
x = self.layer3(x)
x = self.layer4(x)
x = self.gap(x) # [b, c]
x = self.fc(x) # [b, 100]
return x
def build_resblock(self, filter_num, blocks, stride=1):
res_blocks = Sequential()
res_blocks.add(BasicBlock(filter_num, stride))
for _ in range(1, blocks):
res_blocks.add(BasicBlock(filter_num, stride=1))
return res_blocks
def resnet18():
return ResNet([2, 2, 2, 2])