卷積神經(jīng)網(wǎng)絡(luò)-LeNet5、AlexNet妖异、VGGNet惋戏、GoogleNet、ResNet原理及tensorflow2實(shí)現(xiàn)

卷積神經(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 1*1卷積的作用

  • 降維,降低了計(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ū)間[0,1],值域是有界的玩祟。所以一般在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ī)地從256*256的原始圖像中截取224*224大小的區(qū)域(以及水平翻轉(zhuǎn)的鏡像),相當(dāng)于增加了2*(256-224)^2=2048倍的數(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%旁赊。
  • 網(wǎng)絡(luò)結(jié)構(gòu)


    AlexNet網(wǎng)絡(luò)結(jié)構(gòu)
AlexNet網(wǎng)絡(luò)詳細(xì)結(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
  • 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ò)將被迫模擬f(x)=h(x)-x而不是h(x)氢伟。這被稱為殘留學(xué)習(xí)(見下圖,右圖是2016年改進(jìn)后的resnet)。

    RESNET短鏈接

  • 作者還提出了兩層幽歼、三層的殘差學(xué)習(xí)單元朵锣。兩層的殘差學(xué)習(xí)單元中包含兩個(gè)相同輸出通道數(shù)的3*3卷積;而三層的殘差單元,第二層使用了3*3的卷積甸私,第一和第三層使用了NetworkInNetwork 和 InceptionNet中的1*1卷積诚些,有先降維再升維的操作。此外皇型,如果網(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])

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末贱鄙,一起剝皮案震驚了整個(gè)濱河市劝贸,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌逗宁,老刑警劉巖映九,帶你破解...
    沈念sama閱讀 218,941評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異瞎颗,居然都是意外死亡件甥,警方通過查閱死者的電腦和手機(jī)捌议,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來引有,“玉大人瓣颅,你說我怎么就攤上這事∑┱” “怎么了宫补?”我有些...
    開封第一講書人閱讀 165,345評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長曾我。 經(jīng)常有香客問我粉怕,道長,這世上最難降的妖魔是什么抒巢? 我笑而不...
    開封第一講書人閱讀 58,851評(píng)論 1 295
  • 正文 為了忘掉前任贫贝,我火速辦了婚禮,結(jié)果婚禮上蛉谜,老公的妹妹穿的比我還像新娘稚晚。我一直安慰自己,他們只是感情好悦陋,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,868評(píng)論 6 392
  • 文/花漫 我一把揭開白布蜈彼。 她就那樣靜靜地躺著,像睡著了一般俺驶。 火紅的嫁衣襯著肌膚如雪幸逆。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,688評(píng)論 1 305
  • 那天暮现,我揣著相機(jī)與錄音还绘,去河邊找鬼。 笑死栖袋,一個(gè)胖子當(dāng)著我的面吹牛拍顷,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播塘幅,決...
    沈念sama閱讀 40,414評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼昔案,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了电媳?” 一聲冷哼從身側(cè)響起踏揣,我...
    開封第一講書人閱讀 39,319評(píng)論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎匾乓,沒想到半個(gè)月后捞稿,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,775評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年娱局,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了彰亥。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,096評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡衰齐,死狀恐怖任斋,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情耻涛,我是刑警寧澤仁卷,帶...
    沈念sama閱讀 35,789評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站犬第,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏芒帕。R本人自食惡果不足惜歉嗓,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,437評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望背蟆。 院中可真熱鬧鉴分,春花似錦、人聲如沸带膀。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽垛叨。三九已至伦糯,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間嗽元,已是汗流浹背敛纲。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評(píng)論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留剂癌,地道東北人淤翔。 一個(gè)月前我還...
    沈念sama閱讀 48,308評(píng)論 3 372
  • 正文 我出身青樓,卻偏偏與公主長得像佩谷,于是被迫代替她去往敵國和親旁壮。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,037評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容