目錄
- 前言
- 一、CNN解決了什么問題?
- 二、CNN網(wǎng)絡(luò)的結(jié)構(gòu)
- 2.1 卷積層 - 提取特征
- 卷積運算
- 權(quán)重共享
- 稀疏連接
- 總結(jié):標(biāo)準(zhǔn)的卷積操作
- 卷積的意義
- 1x1卷積的重大意義
- 2.2 激活函數(shù)
- 2.3 池化層(下采樣) - 數(shù)據(jù)降維,避免過擬合
- 2.4 全連接層 - 分類,輸出結(jié)果
- 三、Pytorch實現(xiàn)LeNet網(wǎng)絡(luò)
- 3.1 模型定義
- 3.2 模型訓(xùn)練(使用GPU訓(xùn)練)
- 3.3 訓(xùn)練和評估模型
前言
在學(xué)計算機視覺的這段時間里整理了不少的筆記,想著就把這些筆記再重新整理出來,然后寫成Blog和大家一起分享。目前的計劃如下(以下網(wǎng)絡(luò)全部使用Pytorch搭建):
專題一:計算機視覺基礎(chǔ)
- 介紹CNN網(wǎng)絡(luò)(計算機視覺的基礎(chǔ))
- 淺談VGG網(wǎng)絡(luò),介紹ResNet網(wǎng)絡(luò)(網(wǎng)絡(luò)特點是越來越深)
- 介紹GoogLeNet網(wǎng)絡(luò)(網(wǎng)絡(luò)特點是越來越寬)
- 介紹DenseNet網(wǎng)絡(luò)(一個看似十分NB但是卻實際上用得不多的網(wǎng)絡(luò))
- 整理期間還會分享一些自己正在參加的比賽的Baseline
專題二:GAN網(wǎng)絡(luò)
- 搭建普通的GAN網(wǎng)絡(luò)
- 卷積GAN
- 條件GAN
- 模式崩潰的問題及網(wǎng)絡(luò)優(yōu)化
以上會有相關(guān)代碼實踐,代碼是基于Pytorch框架。話不多說,我們先進(jìn)行專題一的第一部分介紹,卷積神經(jīng)網(wǎng)絡(luò)。
一、CNN解決了什么問題?
在CNN出現(xiàn)之前,對于圖像的處理一直都是一個很大的問題,一方面因為圖像處理的數(shù)據(jù)量太大,比如一張512 x 512的灰度圖,它的輸入?yún)?shù)就已經(jīng)達(dá)到了252144個,更別說1024x1024x3之類的彩色圖,這也導(dǎo)致了它的處理成本十分昂貴且效率極低。另一方面,圖像在數(shù)字化的過程中很難保證原有的特征,這也導(dǎo)致了圖像處理的準(zhǔn)確率不高。
而CNN網(wǎng)絡(luò)能夠很好的解決以上兩個問題。對于第一個問題,CNN網(wǎng)絡(luò)它能夠很好的將復(fù)雜的問題簡單化,將大量的參數(shù)降維成少量的參數(shù)再做處理。也就是說,在大部分的場景下,我們使用降維不會影響結(jié)果。比如在日常生活中,我們用一張1024x1024x3表示鳥的彩色圖和一張100x100x3表示鳥的彩色圖,我們基本上都能夠用肉眼辨別出這是一只鳥而不是一只狗。這也是卷積神經(jīng)網(wǎng)絡(luò)在圖像分類里的一個重要應(yīng)用?!緋s:卷積神經(jīng)網(wǎng)絡(luò)可以對一張圖片進(jìn)行是貓還是狗進(jìn)行分類。如果一張圖片里有貓有狗有雞,我們要分別識別出每個區(qū)域的動物,并用顏色分割出來,基礎(chǔ)的CNN網(wǎng)絡(luò)還能夠使用嗎?這是一個拓展思考,有興趣的讀者可以看一下FCN網(wǎng)絡(luò)。對比之下能夠更好的理解CNN網(wǎng)絡(luò)】
對于第二個問題,CNN網(wǎng)絡(luò)利用了類似視覺的方式保留了圖像的特征,當(dāng)圖像做翻轉(zhuǎn)、旋轉(zhuǎn)或者變換位置的時候,它也能有效的識別出來是類似的圖像。
以上兩個問題的解決,都是由于CNN網(wǎng)絡(luò)具有以下優(yōu)勢和特點:局部區(qū)域連接、權(quán)值共享。在解釋這三個特點之前,我們先看一下CNN網(wǎng)絡(luò)的三大主要結(jié)構(gòu),卷積層、池化層(或者叫做匯聚層)、全連接層。
二、CNN網(wǎng)絡(luò)的結(jié)構(gòu)
2.1 卷積層 - 提取特征
卷積運算
了解卷積層,我們先要了解一下卷積運算。別看它叫做卷積,但是和嚴(yán)格意義上的數(shù)學(xué)卷積是不同的。深度學(xué)習(xí)所謂的卷積運算是互相關(guān)( cross-correlation )運算?!緦嶋H上,經(jīng)過數(shù)學(xué)證明發(fā)現(xiàn)無論用嚴(yán)格卷積或互相關(guān)運算,卷積層的輸出不會受太大影響。而互相關(guān)運算比嚴(yán)格卷積運算簡潔的多,所以我們一般都采用互相關(guān)運算來替代嚴(yán)格意義上的卷積運算?!课覀円远S數(shù)據(jù)為例(高 x 寬),不考慮圖像的通道個數(shù),假設(shè)輸入的高度為3、寬度為3的二維張量,卷積核的高度和寬度都是2,而卷積核窗口的形狀由內(nèi)核的高度和寬度決定:
二維的互相關(guān)運算。陰影部分是第一個輸出元素,以及用于計算這個輸出的輸入和核張量元素: 0 x 0 + 1x1 + 3x2 +4x3 = 19。由此可見互相關(guān)運算就是一個乘積求和的過程。在二維互相關(guān)運算中,卷積窗口從輸入張量的左上角開始,從左到右、從上到下滑動。這里我們設(shè)置步長為1,即每次跨越一個距離。當(dāng)卷積窗口滑到新一個位置時,包含在該窗口中的部分張量與卷積核張量進(jìn)行按元素相乘,得到的張量再求和得到一個單一的標(biāo)量值,由此我們得到了這一位置的輸出張量值。 【我們這里頻繁提到了張量,張量(Tensor)是一個多維數(shù)組,它是標(biāo)量、向量、矩陣的高維拓展。】
看了上面的內(nèi)容,可能有讀者開始思考,如果這個輸入矩陣是 4x4,卷積核是3x3,步長為1的時候,在向右移動時,我們發(fā)現(xiàn)如果要和卷積核進(jìn)行卷積操作,左便的輸入矩陣還缺少了一列;在行的方向也是如此。如果我們忽略邊緣的像素,我們可能就丟失了邊緣的細(xì)節(jié)。那么這種情況下我們?nèi)绾翁幚砟??這時我們可以進(jìn)行填充操作(padding),在用pytorch實現(xiàn)時我們可以在Convd函數(shù)中對padding進(jìn)行設(shè)置,這里我們可以設(shè)置padding=1,就能夠在行和列上向外擴充一圈。【ps:實際上當(dāng)處理比較大的圖片,且任務(wù)是分類任務(wù)時,我們可以不用進(jìn)行padding。因為對于大部分的圖像分類任務(wù),邊緣的細(xì)節(jié)是是無關(guān)緊要的,且邊緣的像素點相比于總的像素來講,占比是很小的,對于整個圖像分類的任務(wù)結(jié)果影響不大】
對于卷積操作,我們有一個統(tǒng)一的計算公式。且學(xué)會相關(guān)的計算對于了解感受野和網(wǎng)絡(luò)的搭建至關(guān)重要。學(xué)會相關(guān)的計算,我們在搭建自己的網(wǎng)絡(luò)或者復(fù)現(xiàn)別人的網(wǎng)絡(luò),才能夠確定好填充padding、步長stride以及卷積核kernel size的參數(shù)大小。一般這里有一個統(tǒng)一的公式:
假設(shè)圖像的尺寸是 input x input,卷積核的大小是kernel,填充值為padding,步長為stride,卷積后輸出的尺寸為output x output,則卷積后,尺寸的計算公式為:
ps:如果輸入的圖像尺寸是 mxn類型的,可以通過裁剪 or 插值轉(zhuǎn)換成 mxm or nxn類型的圖像;或者分別計算圖像的高和寬大小也可以,公式都是類似的。
權(quán)重共享
我們在前面有提到過CNN網(wǎng)絡(luò)的一個特性是權(quán)重共享(share weights)也正是體現(xiàn)在通道處理的過程中。一般的神經(jīng)網(wǎng)絡(luò)層與層之間的連接是,每個神經(jīng)元與上一層的全部神經(jīng)元連接,這些連接線的權(quán)重獨立于其他的神經(jīng)元,所以假設(shè)上一層是m個神經(jīng)元,當(dāng)前層是n個神經(jīng)元,那么共有mxn個連接,也就有mxn個權(quán)重。權(quán)重矩陣是mxn的形式。那么CNN是如何呢?權(quán)重共享是什么意思?我們引入下面這樣一張圖來幫助我們理解:
我們先說明上圖中各個模塊的矩陣格式及關(guān)系:
- 輸入矩陣格式:(樣本數(shù),圖像高度,圖像寬度,圖像通道數(shù))
- 輸出矩陣格式:(樣本數(shù),圖像高度、圖像寬度、圖像通道數(shù))
- 卷積核的格式:(卷積核高度、卷積核寬度、輸入通道數(shù)、輸出通道數(shù))
- 卷積核的輸入通道數(shù)(in depth)由輸入矩陣的通道數(shù)所決定。(紅色標(biāo)注)
- 輸出矩陣的通道數(shù)(out depth)由卷積核的輸出通道所決定。(綠色標(biāo)注)
- 輸出矩陣的高度和寬度,由輸入矩陣、卷積核大小、步長、填充共同決定,具體計算公式見上文。
當(dāng)輸入一張大小為8x8x3的彩色圖時,我們已經(jīng)提前設(shè)計好了卷積核后的輸出通道為5,即卷積核的個數(shù)為5【即五個偏置,一個卷積核一個偏置】(通道數(shù)的設(shè)計一般是實驗后得到的較優(yōu)結(jié)果)。每個卷積核去和輸入圖像在通道上一一對應(yīng)進(jìn)行卷積操作(即互相關(guān)操作,除非刻意強調(diào),這里所說的卷積都是互相關(guān),步長為1,填充為0),得到了3個6x6的feature map。然后再將三個6x6的Feature map按照Eletwise相加進(jìn)行通道融合得到最終的feature map,大小為6x6(也就是將得到的三個矩陣逐元素相加,之后所有元素再加上該矩陣的偏置值,得到新的6x6矩陣)。權(quán)重共享也是體現(xiàn)在這個過程中。我們單獨提取出第一個卷積核,當(dāng)它的第一個通道(3x3)與輸入圖像的第一個通道(8x8)進(jìn)行卷積操作時,按照普通的神經(jīng)網(wǎng)絡(luò)連接方式其權(quán)重矩陣是9 x 81。但是這里我們要注意,我們在窗口滑動進(jìn)行卷積的操作權(quán)重是確定的,都是以輸入圖像的第一個通道為模板,卷積核的第一個通道3x3矩陣為權(quán)重值,然后得到卷積結(jié)果。這個過程中權(quán)重矩陣就是3x3,且多次應(yīng)用于每次計算中。權(quán)重的個數(shù)有9x81減少到3x3,極大的減少了參數(shù)的數(shù)量。綜合起來,對于第一個卷積核來講,它的權(quán)重矩陣就是3x3x3+1,整個卷積過程的權(quán)重大小為3x3x3x5+5,而不是8x8x3x3x3x3x5。權(quán)重共享大大減少了模型的訓(xùn)練參數(shù)。權(quán)重共享意味著當(dāng)前隱藏層中的所有神經(jīng)元都在檢測圖像不同位置處的同一個特征,即檢測特征相同。因此也將輸入層到隱藏層的這種映射稱為特征映射。由上我們可以理解,從某種意義上來說,通道就是某種意義上的特征圖。輸出的同一張?zhí)卣鲌D上的所有元素共享一個卷積核,即共享一個權(quán)重。通道中某一處(特征圖上某一個神經(jīng)元)數(shù)值的大小就是當(dāng)前位置對當(dāng)前特征強弱的反應(yīng)。而為什么在CNN網(wǎng)絡(luò)中我們會增加通道數(shù)目,其實就是在增加通道的過程中區(qū)學(xué)習(xí)圖像的多個不同特征。
稀疏連接
通過上面對于卷積的過程以及權(quán)重共享的解釋,我們能夠總結(jié)出CNN的另一個特征。有心的讀者其實能夠自己總結(jié)出來。我們在上面提到過,對于普通的神經(jīng)網(wǎng)絡(luò),隱藏層和輸入層之間的神經(jīng)元是采用全連接的方式。然而CNN網(wǎng)絡(luò)并不是如此。它的在局部區(qū)域創(chuàng)建連接,即稀疏連接。比如,對于一張輸入的單通道的8x8圖片,我們用3x3的卷積核和他進(jìn)行卷積,卷積核中的每個元素的值是和8x8矩陣中選取了3x3的矩陣做卷積運算,然后通過滑動窗口的方式,卷積核中的每個元素(也就是神經(jīng)元)只與上一層的所有神經(jīng)元中的9個進(jìn)行連接。相比于神經(jīng)元之間的全連接方式,稀疏連接極大程度上的減少了參數(shù)的數(shù)量,同時也一定程度上避免了模型的過擬合。這種算法的靈感是來自動物視覺的皮層結(jié)構(gòu),其指的是動物視覺的神經(jīng)元在感知外界物體的過程中起作用的只有一部分神經(jīng)元。在計算機視覺中,像素之間的相關(guān)性與像素之間的距離同樣相關(guān),距離較近的像素間相關(guān)性強,距離較遠(yuǎn)則相關(guān)性比較弱,由此可見局部相關(guān)性理論也適用于計算機視覺的圖像處理。因此,局部感知(稀疏連接)采用部分神經(jīng)元接受圖像信息,再通過綜合全部的圖像信息達(dá)到增強圖像信息的目的。(至于我們?yōu)槭裁船F(xiàn)在經(jīng)常采用3x3卷積核,因為實驗結(jié)果告訴我們,3x3卷積核常常能達(dá)到更好的實驗效果。)
總結(jié):標(biāo)準(zhǔn)的卷積操作
在進(jìn)行上面的解釋后,相信大家已經(jīng)對于什么是卷積,卷積的兩點特點:稀疏鏈接和權(quán)重共享已經(jīng)有了了解。下面我們來總結(jié)一般意義的標(biāo)準(zhǔn)的卷積操作:當(dāng)輸入的feature map數(shù)量(即輸入的通道數(shù))是N,卷積層filter(卷積核)個數(shù)是M時,則M個filter中,每一個filter都有N個channel,都要分別和輸入的N個通道做卷積,得到N個特征圖,然后將這N個feature map按Eletwise相加(即:進(jìn)行通道融合),再加上該filter對應(yīng)的偏置(一個filter對應(yīng)一個共享偏置),作為該卷積核所得的特征圖。同理,對其他M-1個filter也進(jìn)行以上操作。所以最終該層的輸出為M個feature map(即:輸出channel等于filter的個數(shù))??梢?,輸出的同一張?zhí)卣鲌D上的所有元素共享同一個卷積核,即共享一個權(quán)重。不同特征圖對應(yīng)的卷積核不同。
卷積的意義
如果用圖像處理上的專業(yè)術(shù)語,我們可以將卷積叫做銳化。卷積其實是想要強調(diào)某些特征,然后將特征強化后提取出來,不同卷積核關(guān)注圖片上不同的特征,比如有的更關(guān)注邊緣而有的更關(guān)注中心地帶等。當(dāng)完成幾個卷積層后(卷積 + 激活函數(shù) + 池化)【后面講解激活函數(shù)和池化】,如圖所示:
在CNN中,我們就是通過不斷的改變卷積核矩陣的值來關(guān)注不同的細(xì)節(jié),提取不同的特征。也就是說,在我們初始化卷積核的矩陣值(即權(quán)重參數(shù))后,我們通過梯度下降不斷降低loss來獲得最好的權(quán)重參數(shù),整個過程都是自動調(diào)整的。
1x1卷積的重大意義
按照道理講,我是不該這么快就引入1x1卷積的,不過考慮到它在各種網(wǎng)絡(luò)中應(yīng)用的重要性并且也不難理解,就在這里提前和大家解釋一下1x1卷積是什么,作用是什么。
如果有讀者在理解完上文所提到的卷積后,可能會發(fā)出疑問:卷積的本質(zhì)不是有效的提取相鄰像素間的相關(guān)特征嗎?1x1卷積核的每個通道和上一層的通道進(jìn)行卷積操作的時候不是沒法識別相鄰元素了嗎?
其實不然,1x1卷積層的確是在高度和寬度的維度上失去了識別相鄰元素間的相互作用的能力,并且通過1x1卷積核后的輸出結(jié)果的高和寬與上一層的高和寬是相同的。但是,通道數(shù)目可能是不同的。而1x1卷積的唯一計算也正是發(fā)生在通道上。它通過改變1x1卷積核的數(shù)量,實現(xiàn)了多通道的線性疊加,使得不同的feature map進(jìn)行線性疊加。我們通過下圖來更好的認(rèn)識1x1卷積的作用:
上圖使用了2個通道的1x1卷積核與3個通道的3x3輸入矩陣進(jìn)行卷積操作。由圖可知,這里的輸入和輸出具有相同的高度和寬度,輸出的每個元素都是從輸入圖像的同一位置的元素的線性組合。我們可以將1x1卷積層看做是每個像素位置應(yīng)用的全連接層。同時,我們發(fā)現(xiàn)輸出結(jié)果的通道數(shù)目發(fā)生了改變,這也是1x1卷積的一個重要應(yīng)用。可以對輸出通道進(jìn)行升維或者降維,并且不改變圖像尺寸的大小,有利于跨通道的信息交流的內(nèi)涵。降維之后我們可以使得參數(shù)數(shù)量變得更少,訓(xùn)練更快,內(nèi)存占用也會更少。如果是在GPU上訓(xùn)練,顯存就更加珍貴了。
卷積網(wǎng)絡(luò)的一個非常重要的應(yīng)用就是ResNet網(wǎng)絡(luò),而ResNet網(wǎng)絡(luò)結(jié)構(gòu),已經(jīng)應(yīng)用于各種大型網(wǎng)絡(luò)中,可以說是隨處可見。這里先貼個ResNet中應(yīng)用了1x1卷積的殘差塊,后面會有一篇文章來解讀ResNet網(wǎng)絡(luò):ResNet paper download
2.2 激活函數(shù)
由前述可知,在CNN中,卷積操作只是加權(quán)求和的線性操作。若神經(jīng)網(wǎng)絡(luò)中只用卷積層,那么無論有多少層,輸出都是輸入的線性組合,網(wǎng)絡(luò)的表達(dá)能力有限,無法學(xué)習(xí)到非線性函數(shù)。因此CNN引入激活函數(shù),激活函數(shù)是個非線性函數(shù),常用于卷積層和全連接層輸出的每個神經(jīng)元,給神經(jīng)元引入了非線性因素,使網(wǎng)絡(luò)的表達(dá)能力更強,幾乎可以逼近任意函數(shù),這樣的神經(jīng)網(wǎng)絡(luò)就可應(yīng)用到眾多的非線性模型中,我們可以用公式來定義隱藏層的每個神經(jīng)元輸出公式:
其中,\(b_l\)是該感知與連接的共享偏置,Wl是個nxn的共享權(quán)重矩陣,代表在輸入層的nxn的矩形區(qū)域的特征值。
當(dāng)激活函數(shù)作用于卷積層的輸出時:
這里的σ是神經(jīng)元的激勵函數(shù),可以是Sigmoid、tanh、ReLU等函數(shù)。
2.3 池化層(下采樣) - 數(shù)據(jù)降維,避免過擬合
在CNN中,池化層通常在卷積或者激勵函數(shù)的后面,池化的方式有兩種,全最大池化或者平均池化,池化主要有三個作用:
- 一是降低卷積層對目標(biāo)位置的敏感度,即實現(xiàn)局部平移不變性,當(dāng)輸入有一定的平移時,經(jīng)過池化后輸出不會發(fā)生改變。CNN通過引入池化,使得其特征提取不會因為目標(biāo)位置變化而受到較大的影響。
- 二是降低對空間降采樣表示的敏感性
- 三是能夠?qū)ζ溥M(jìn)行降維壓縮,以加快運算速度 ,防止過擬合。
與卷積層類似的是,池化層運算符有一個固定的窗口組成,該窗口也是根據(jù)步幅大小在輸入的所有區(qū)域上滑動,為固定的形狀窗口遍歷每個位置計算一個輸出。
輸出的張量高度為2,寬度為2,這四個元素為每個池化窗口中的最大值(stride=1,padding=0):
但是,不同于卷積層中的輸入與卷積核之間的互相關(guān)計算,池化層不包含參數(shù)。池化層的運算符是確定性的,我們通常計算池化窗口中所有元素的最大值或平均值(些操作分別被稱為最大池化層和平均池化層),而不是像卷積層那樣將各通道的輸入在互相關(guān)操作后進(jìn)行eletwise特征融合,這也意味著池化層的輸出通道數(shù)和輸入通道數(shù)目是相同的。池化操作的計算一般形式為,設(shè)輸入圖像尺寸為WxHxC,寬x高x深度,卷積核的尺寸為FxF,S:步長,則池化后圖像的大小為:
2.4 全連接層 - 分類,輸出結(jié)果
我們剛給講了卷積層、池化層和激活函數(shù),這些在全連接層之前層的作用都是將原始數(shù)據(jù)映射到隱層特征空間來提取特征,而全連接層的作用就是將學(xué)習(xí)到的特征表示映射到樣本的標(biāo)記空間。換句話說,就是把特征正和島一起(高度提純特征),方便交給最后的分類器或者回歸。
我們也可以把全連接層的過程看做一個卷積過程,例如某個網(wǎng)絡(luò)在經(jīng)過卷積、ReLU激活后得到3x3x5的輸出,然后經(jīng)過全連接層轉(zhuǎn)換成1x4096的形式:
從上圖我們可以看出,我們用一個3x3x5的filter去卷積激活函數(shù)的輸出,得到的結(jié)果是全連接層的一個神經(jīng)元,因為我們有4096個神經(jīng)元,我們實際上就是用一個3x3x5x4096的卷積層去卷積激活函數(shù)的輸出【不帶偏置】。因此全連接層中的每個神經(jīng)元都可以看成一個不帶偏置加權(quán)平均的多項式,我們可以簡單寫成。
這一步卷積還有一個非常重要的作用,就是把分布式特征representation映射到樣本標(biāo)記空間,簡單說就是把特征整合到一起,輸出為一個值,這樣可以大大減少特征位置對分類帶來的影響。
從上面的圖,我們可以看出,貓在不同的位置,輸出的特征值相同,但是位置不同;對于電腦來說,特征值相同,但是特征值位置不同,那分類結(jié)果可能是不一樣的。此時全連接層的作用就是,在展平后忽略其空間結(jié)構(gòu)特性,不管它在哪兒,只要它有這個貓,那么就能判別它是貓。這也說明了它是一個跟全局圖像的問題有關(guān)的問題(例如:圖像是否包含一只貓呢)。這也說明了全連接層的結(jié)構(gòu)不適合用于在方位上找patter的任務(wù),例如分割任務(wù)(后面的FCN就是將全連接層改成了卷積層)。不過全連接層有一個很大的缺點,就是參數(shù)過于多。所以后面的像ResNet網(wǎng)絡(luò)、GoogLeNet都已經(jīng)采用全局平均池化取代全連接層來融合學(xué)到的特征。另外,參數(shù)多了也引發(fā)了另外一個問題,模型復(fù)雜度提升,學(xué)習(xí)能力太好容易造成過擬合。
三、Pytorch實現(xiàn)LeNet網(wǎng)絡(luò)
LeNet模型是最早發(fā)布的卷積神經(jīng)網(wǎng)絡(luò)之一,它是由ATT貝爾實驗室的研究院Yann LeCun在1989年提出的,目的是識別圖像中的手寫數(shù)字,發(fā)表了第一篇通過反向傳播成功訓(xùn)練卷積神經(jīng)網(wǎng)絡(luò)的研究。我們現(xiàn)在通過pytorch來實現(xiàn):LeNet - Paper Download 。
LeNet它雖然很小,但是包含了深度學(xué)習(xí)的基本模塊。我們先對LeNet結(jié)構(gòu)進(jìn)行一個具體的分析,這也是我們搭建任何一個神經(jīng)網(wǎng)絡(luò)之前要提前知道的:
每個卷積塊中的基本單元是一個卷積層、一個Sigmod激活函數(shù)和平均池化層。(雖然ReLU函數(shù)和最大池化層很有效,但是當(dāng)時還沒有出現(xiàn))。每個卷積層使用5x5卷積核和一個Sigmoid激活函數(shù)。第一個卷積層有6個輸出通道,第二個卷積層有16個輸出通道。每個2x2赤化操作通過空間下采樣將維數(shù)減少4倍。卷積的輸出形狀由(批量大小,通道數(shù),高度,寬度)決定。LeNet中有三個全連接層,分別有120、84、10個輸出,因為我們在執(zhí)行手寫數(shù)字的分類任務(wù)(一共有0-9共10個數(shù)字),所以輸出層的10維對應(yīng)于最后輸出的結(jié)果的數(shù)量。
把上面的模型簡化一下,網(wǎng)絡(luò)結(jié)構(gòu)大概就是這個樣子:
3.1 模型定義
import torch
from torch import nn
from d2l import torch as d2l
class Reshape(torch.nn.Module):
def forward(self, x):
# 通過view函數(shù)把圖像展成標(biāo)準(zhǔn)的Tensor接收格式,即(樣本數(shù)量,通道數(shù),高,寬)
return x.view(-1, 1, 28, 28)
net = torch.nn.Sequential(
Reshape(),
# 第一個卷積塊,這里用到了padding=2
nn.Conv2d(1, 6, kernel_size=5, padding=2),
nn.Sigmoid(),
nn.AvgPool2d(kernel_size=2, stride=2),
# 第二個卷積塊
nn.Conv2d(6, 16, kernel_size=5),
nn.Sigmoid(),
nn.AvgPool2d(kernel_size=2, stride=2),
# 稠密塊(三個全連接層)
nn.Flatten(),
nn.Linear(16 * 5 * 5, 120), nn.Sigmoid(),
nn.Linear(120, 84), nn.Sigmoid(),
nn.Linear(84, 10))
X = torch.rand(size=(1, 1, 28, 28), dtype=torch.float32)
for layer in net:
X = layer(X)
print(layer.__class__.__name__,'output shape: \t',X.shape)
輸出結(jié)果為:
3.2 模型訓(xùn)練(使用GPU訓(xùn)練)
我們用LeNet在Fashion-MNIST數(shù)據(jù)集上測試模型表現(xiàn)結(jié)果:
batch_size = 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size=batch_size)
def evaluate_accuracy_gpu(net, data_iter, device=None): #@save
"""使用GPU計算模型在數(shù)據(jù)集上的精度。"""
if isinstance(net, torch.nn.Module):
net.eval() # 設(shè)置為評估模式
if not device:
device = next(iter(net.parameters())).device
# 正確預(yù)測的數(shù)量,總預(yù)測的數(shù)量
metric = d2l.Accumulator(2)
for X, y in data_iter:
if isinstance(X, list):
X = [x.to(device) for x in X]
else:
X = X.to(device)
y = y.to(device)
metric.add(d2l.accuracy(net(X), y), y.numel())
return metric[0] / metric[1]
#@save
def train_ch6(net, train_iter, test_iter, num_epochs, lr, device):
"""用GPU訓(xùn)練模型。"""
def init_weights(m):
if type(m) == nn.Linear or type(m) == nn.Conv2d:
nn.init.xavier_uniform_(m.weight)
net.apply(init_weights)
print('training on', device)
net.to(device)
optimizer = torch.optim.SGD(net.parameters(), lr=lr)
loss = nn.CrossEntropyLoss()
animator = d2l.Animator(xlabel='epoch', xlim=[1, num_epochs],
legend=['train loss', 'train acc', 'test acc'])
timer, num_batches = d2l.Timer(), len(train_iter)
for epoch in range(num_epochs):
# 訓(xùn)練損失之和,訓(xùn)練準(zhǔn)確率之和,范例數(shù)
metric = d2l.Accumulator(3)
net.train()
for i, (X, y) in enumerate(train_iter):
timer.start()
optimizer.zero_grad()
X, y = X.to(device), y.to(device)
y_hat = net(X)
l = loss(y_hat, y)
l.backward()
optimizer.step()
with torch.no_grad():
metric.add(l * X.shape[0], d2l.accuracy(y_hat, y), X.shape[0])
timer.stop()
train_l = metric[0] / metric[2]
train_acc = metric[1] / metric[2]
if (i + 1) % (num_batches // 5) == 0 or i == num_batches - 1:
animator.add(epoch + (i + 1) / num_batches,
(train_l, train_acc, None))
test_acc = evaluate_accuracy_gpu(net, test_iter)
animator.add(epoch + 1, (None, None, test_acc))
print(f'loss {train_l:.3f}, train acc {train_acc:.3f}, '
f'test acc {test_acc:.3f}')
print(f'{metric[2] * num_epochs / timer.sum():.1f} examples/sec '
f'on {str(device)}')
3.3 訓(xùn)練和評估模型
lr, num_epochs = 0.9, 10
train_ch6(net, train_iter, test_iter, num_epochs, lr, d2l.try_gpu())
這個是我的訓(xùn)練結(jié)果,大概就醬紫,結(jié)束了結(jié)束了,累死我了。
到此這篇關(guān)于一文帶你了解CNN(卷積神經(jīng)網(wǎng)絡(luò))的文章就介紹到這了,更多相關(guān)CNN(卷積神經(jīng)網(wǎng)絡(luò))內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
您可能感興趣的文章:- Numpy實現(xiàn)卷積神經(jīng)網(wǎng)絡(luò)(CNN)的示例
- 使用卷積神經(jīng)網(wǎng)絡(luò)(CNN)做人臉識別的示例代碼
- PyTorch上實現(xiàn)卷積神經(jīng)網(wǎng)絡(luò)CNN的方法
- TensorFlow深度學(xué)習(xí)之卷積神經(jīng)網(wǎng)絡(luò)CNN
- TensorFlow實現(xiàn)卷積神經(jīng)網(wǎng)絡(luò)CNN