歡迎你們前往騰訊雲社區,獲取更多騰訊海量技術實踐乾貨哦~php
做者介紹:高成才,騰訊Android開發工程師,2016.4月校招加入騰訊,主要負責企鵝電競推流SDK、企鵝電競APP的功能開發和技術優化工做。本文發表於QQ會員技術團隊的專欄python
本文主要是對CS231n課程學習筆記的提煉,添加了一些Deep Learning Book和Tensorflow 實戰,以及Caffe框架的知識。linux
卷積網絡是一種專門用來處理具備相似網格結構的數據的神經網絡。它常規神經網絡很是類似:它們都是由神經元組成,神經元中有具備學習能力的權重和誤差。每一個神經元都獲得一些輸入數據,進行內積運算後再進行激活函數運算。整個網絡依舊是一個可導的評分函數:該函數的輸入是原始的圖像像素,輸出是不一樣類別的評分。在最後一層(每每是全鏈接層),網絡依舊有一個損失函數(好比SVM或Softmax),而且在神經網絡中咱們實現的各類技巧和要點依舊適用於卷積神經網絡。git
卷積神經網絡的結構基於一個假設,即輸入數據是圖像,基於該假設,咱們就向結構中添加了一些特有的性質。這些特有屬性使得前向傳播函數實現起來更高效,而且大幅度下降了網絡中參數的數量。常規神經網絡的神經網絡的輸入是一個向量,而後在一系列的隱層中對它作變換。每一個隱層都是由若干的神經元組成,每一個神經元都與前一層中的全部神經元鏈接。可是在一個隱層中,神經元相互獨立不進行任何鏈接。github
常規神經網絡對於大尺寸圖像效果不盡人意。在CIFAR-10中,圖像的尺寸是32x32x3(寬高均爲32像素,3個顏色通道),所以,對應的的常規神經網絡的第一個隱層中,每個單獨的全鏈接神經元就有32x32x3=3072個權重。這個數量看起來還能夠接受,可是很顯然這個全鏈接的結構不適用於更大尺寸的圖像。舉例說來,一個尺寸爲200x200x3的圖像,會讓神經元包含200x200x3=120,000個權重值。而網絡中確定不止一個神經元,那麼參數的量就會快速增長!顯而易見,這種全鏈接方式效率低下,大量的參數也很快會致使網絡過擬合。算法
卷積神經網絡針對輸入所有是圖像的狀況,將結構調整得更加合理,得到了不小的優點。與常規神經網絡不一樣,卷積神經網絡的各層中的神經元是3維排列的:寬度、高度和深度(這裏的深度指的是激活數據體的第三個維度,而不是整個網絡的深度,整個網絡的深度指的是網絡的層數)。咱們將看到,層中的神經元將只與前一層中的一小塊區域鏈接,而不是採起全鏈接方式。以下圖爲常規神經網絡和卷積神經網絡示意圖:ubuntu
一個簡單的卷積神經網絡是由各類層按照順序排列組成,網絡中的每一個層使用一個能夠微分的函數將激活數據從一個層傳遞到另外一個層。卷積神經網絡主要由三種類型的層構成:卷積層,池化(Pooling)層和全鏈接層(全鏈接層和常規神經網絡中的同樣)。經過將這些層疊加起來,就能夠構建一個完整的卷積神經網絡,結構以下圖所示:bash
卷積層的參數是由一些可學習的濾波器集合構成的。每一個濾波器在空間上(寬度和高度)都比較小,可是深度和輸入數據一致。 在前向傳播的時候,讓每一個濾波器都在輸入數據的寬度和高度上滑動(更精確地說是卷積),而後計算整個濾波器和輸入數據任一處的內積。當濾波器沿着輸入數據的寬度和高度滑事後,會生成一個2維的激活圖(activation map),激活圖給出了在每一個空間位置處濾波器的反應。直觀地來講,網絡會讓濾波器學習到當它看到某些類型的視覺特徵時就激活,具體的視覺特徵多是某些方位上的邊界,或者在第一層上某些顏色的斑點,甚至能夠是網絡更高層上的蜂巢狀或者車輪狀圖案。在每一個卷積層上,咱們會有一整個集合的濾波器(好比12個),每一個都會生成一個不一樣的二維激活圖。將這些激活映射在深度方向上層疊起來就生成了輸出數據,以下圖左所示,32*32的圖像通過多個濾波器輸出數據:服務器
(1) 局部鏈接網絡
局部鏈接會大大減小網絡的參數。在處理圖像這樣的高維度輸入時,讓每一個神經元都與前一層中的全部神經元進行全鏈接是不現實的。相反,咱們讓每一個神經元只與輸入數據的一個局部區域鏈接。該鏈接的空間大小叫作神經元的感覺野(receptive field),它的尺寸是一個超參數(其實就是濾波器的空間尺寸)。在深度方向上,這個鏈接的大小老是和輸入量的深度相等。須要再次強調的是,咱們對待空間維度(寬和高)與深度維度是不一樣的:鏈接在空間(寬高)上是局部的,可是在深度上老是和輸入數據的深度一致。
(2) 空間排列
上文講解了卷積層中每一個神經元與輸入數據體之間的鏈接方式,可是還沒有討論輸出數據體中神經元的數量,以及它們的排列方式。共有3個超參數控制着輸出數據體的尺寸:深度(depth),步長(stride)和零填充(zero-padding)。下面是對它們的討論:
它和使用的濾波器的數量一致,每一個濾波器會在輸入數據中尋找一些不一樣的東西。舉例來講,若是第一個卷積層的輸入是原始圖像,那麼在深度維度上的不一樣神經元將可能被不一樣方向的邊界,或者是顏色斑點激活。咱們將這些沿着深度方向排列、感覺野相同的神經元集合稱爲深度列(depthcolumn)。以下圖所示卷積層共有6個濾波器,輸出的數據深度也爲6。
在滑動濾波器的時候,必須指定步長。當步長爲1,濾波器每次移動1個像素。當步長爲2(或者不經常使用的3,或者更多,這些在實際中不多使用),濾波器滑動時每次移動2個像素。這個操做會讓輸出數據體在空間上變小。以下圖所示,移動的步長爲1,輸入的數據尺寸6 * 6,而輸出的數據尺寸爲4 * 4。
卷積層濾波器輸出會減小數據的尺寸,以下圖,32 * 32 * 3的輸入通過一個5 * 5 * 3的濾波器時輸出28 * 28 * 1尺寸的數據。
而零填充有一個良好性質,便可以控制輸出數據體的空間尺寸(最經常使用的是用來保持輸入數據體在空間上的尺寸,這樣輸入和輸出的寬高都相等)。將輸入數據體用0在邊緣處進行填充是很方便的,這個零填充(zero-padding)的尺寸是一個超參數。以下圖所示經過零填充保持了輸入輸出數據寬高的一致。
卷積層輸出計算公式:
假設輸入數據體的尺寸爲:
卷積層4個超參數爲:
則輸出數據體的尺寸爲:
其中:
對這些超參數,常見的設置是F=3,S=1,P=1。好比輸入是7x7,濾波器是3x3,步長爲1,填充爲0,那麼就能獲得一個5x5的輸出。若是步長爲2,輸出就是3x3。
(3) 參數共享
在卷積層中使用參數共享是用來控制參數的數量。每一個濾波器與上一層局部鏈接,同時每一個濾波器的全部局部鏈接都使用一樣的參數,此舉會一樣大大減小網絡的參數。
這裏做一個合理的假設:若是一個特徵在計算某個空間位置(x,y)的時候有用,那麼它在計算另外一個不一樣位置(x2,y2)的時候也有用。基於這個假設,能夠顯著地減小參數數量。換言之,就是將深度維度上一個單獨的2維切片看作深度切片(depth slice),好比一個數據體尺寸爲55x55x96的就有96個深度切片,每一個尺寸爲55x55。在每一個深度切片上的神經元都使用一樣的權重和誤差。在反向傳播的時候,都要計算每一個神經元對它的權重的梯度,可是須要把同一個深度切片上的全部神經元對權重的梯度累加,這樣就獲得了對共享權重的梯度。這樣,每一個切片只更新一個權重集。
在一個深度切片中的全部權重都使用同一個權重向量,那麼卷積層的前向傳播在每一個深度切片中能夠看作是在計算神經元權重和輸入數據體的卷積(這就是「卷積層」名字由來)。這也是爲何老是將這些權重集合稱爲濾波器(filter)(或卷積核(kernel)),由於它們和輸入進行了卷積。下圖動態的顯示了卷積的過程:
一般,在連續的卷積層之間會週期性地插入一個池化層。它的做用是逐漸下降數據體的空間尺寸,這樣的話就能減小網絡中參數的數量,使得計算資源耗費變少,也能有效控制過擬合。池化層使用Max操做,對輸入數據體的每個深度切片獨立進行操做,改變它的空間尺寸。最多見的形式是池化層使用尺寸2x2的濾波器,以步長爲2來對每一個深度切片進行降採樣,將其中75%的激活信息都丟掉。每一個Max操做是從4個數字中取最大值(也就是在深度切片中某個2x2的區域)。深度保持不變。
池化層的計算公式:
輸入數據體尺寸:
池化層有兩個超參數:
輸出數據體尺寸:
其中:
由於對輸入進行的是固定函數計算,因此沒有引入參數。在池化層中不多使用零填充。
在實踐中,最大池化層一般只有兩種形式:一種是F=3, S=2,另外一個更經常使用的是F=2, S=2。對更大感覺野進行池化須要的池化尺寸也更大,並且每每對網絡有破壞性。
普通池化(General Pooling):除了最大池化,池化單元還可使用其餘的函數,好比平均池化(average pooling)或L-2範式池化(L2-norm pooling)。平均池化歷史上比較經常使用,可是如今已經不多使用了。由於實踐證實,最大池化的效果比平均池化要好。池化層的示意圖以下圖所示:
反向傳播:回顧一下反向傳播的內容,其中函數的反向傳播能夠簡單理解爲將梯度只沿最大的數回傳。所以,在向前傳播通過匯聚層的時候,一般會把池中最大元素的索引記錄下來(有時這個也叫做道岔(switches)),這樣在反向傳播的時候梯度的路由就很高效。
在卷積神經網絡的結構中,提出了不少不一樣類型的歸一化層,有時候是爲了實如今生物大腦中觀測到的抑制機制。可是這些層漸漸都再也不流行,由於實踐證實它們的效果即便存在,也是極其有限的。
在全鏈接層中,神經元對於前一層中的全部激活數據是所有鏈接的,這個常規神經網絡中同樣。它們的激活能夠先用矩陣乘法,再加上誤差。
這是第一個成功的卷積神經網絡應用,是Yann LeCun在上世紀90年代實現,被用於手寫字體的識別,也是學習神經網絡的「Hello World」。網絡結構圖以下圖所示:
C1層:卷積層,這層包含6個特徵卷積核,卷積核的大小爲5 * 5,而後能夠獲得6個特徵圖,每一個特徵圖的大小爲32-5+1=28。
S2層:這是下采樣層,使用最大池化進行下采樣,池化的尺寸爲2x2,所以咱們能夠獲得6個14x14的特徵圖。
C3層:卷積層,這層包含16個特徵卷積核,卷積核的大小依然爲5x5,所以能夠獲得16個特徵圖,每一個特徵圖大小爲14-5+1=10。
S4層:下采樣層,依然是2x2的最大池化下采樣,結果獲得16個5x5的特徵圖。
C5層:卷積層,這層使用120個5x5的卷積核,最後輸出120個1x1的特徵圖。
以後就是全鏈接層,而後進行分類。
Alexnet的意義重大,他證實了CNN在複雜模型下的有效性,使得神經網絡在計算機視覺領域大放異彩。 它由Alex Krizhevsky,Ilya Sutskever和Geoff Hinton實現。AlexNet在2012年的ImageNet ILSVRC 競賽中奪冠,性能遠遠超出第二名(16%的top5錯誤率,第二名是26%的top5錯誤率)。這個網絡的結構和LeNet很是相似,可是更深更大,而且使用了層疊的卷積層來獲取特徵(以前一般是隻用一個卷積層而且在其後立刻跟着一個匯聚層)。結構圖以下圖所示:
每層的詳細信息以下圖所示:
AlexNet輸入尺寸爲227x227x3。
第一個卷基層由96個大小爲11x11的濾波器,步長爲4。輸出尺寸爲(227 -11)/4 +1 = 55,深度爲64。大概有11x11x3x643萬5千個參數。
第二層池化的尺寸爲3x3,步長爲2,因此輸出的尺寸爲(55-3)/2+1=27。
這是第一次使用ReLU,至少第一次將ReLU發揚光大。
使用如今已經不怎麼使用的數據規範化。
使用了不少數據加強。
Matthew Zeiler和Rob Fergus發明的網絡在ILSVRC 2013比賽中奪冠,它被稱爲 ZFNet(Zeiler & Fergus Net的簡稱)。它經過修改結構中的超參數來實現對AlexNet的改良,具體說來就是增長了中間卷積層的尺寸,讓第一層的步長和濾波器尺寸更小。他們基於試驗作了一些參數的調整。結構圖以下圖所示:
VGGNet並無瘋狂的架構選擇,沒有在如何設定濾波器的個數,尺寸大小上作很是多的工做。整個VGG網絡只用33卷積核,滑動步長2和22池化窗口,滑動步長2。整個過程就維持了這樣的參數設定。VGG的關鍵點在於這個操做你重複了多少層,最後一樣的這組參數設定的網絡結構重複了16層。選擇這個層的緣由多是他們發現這樣有最好的表現。對於每張圖片須要200M內存,全部的參數加起來,最終總的參數量會達到1.4億。
爲何使用33濾波器?33是最小的可以捕獲上下左右和中心的感覺野,多個33的卷積層比一個更大尺寸濾波器卷積層有更多的非線性。在步長爲1的狀況下, 兩個33的濾波器的最大感覺野區域是55, 三個33的濾波器的最大感覺野區域是77, 能夠替代更大的濾波器尺寸多個33的卷積層比一個大尺寸的filter有更少的參數,假設卷積層的輸入和輸出的特徵圖大小相同爲10,那麼含有3個33的濾波器的卷積層參數個數3(331010)=2700, 由於三個33的filter能夠當作是一個77的filter分解而來的(中間層有非線性的分解), 可是1個77的卷積層參數爲771010=4900 11濾波器做 用是在不影響輸入輸出維數的狀況下,對輸入線進行線性形變,而後經過Relu進行非線性處理,增長網絡的非線性表達能力。
VGGNet很差的一點是它耗費更多計算資源,而且使用了更多的參數,結構圖以下所示:
VGGNet性能不錯,可是有大量的參數。通常來講,提高網絡性能最直接的辦法就是增長網絡深度和寬度,這也就意味着巨量的參數。可是,巨量參數容易產生過擬合也會大大增長計算量。
通常文章認爲解決上述兩個缺點的根本方法是將全鏈接甚至通常的卷積都轉化爲稀疏鏈接。一方面現實生物神經系統的鏈接也是稀疏的,另外一方面有文獻代表:對於大規模稀疏的神經網絡,能夠經過分析激活值的統計特性和對高度相關的輸出進行聚類來逐層構建出一個最優網絡。「稀疏鏈接結構」的理解是這樣的,用盡量的「小」、「分散」的可堆疊的網絡結構,去學習複雜的分類任務,Inception之因此能提升網絡精度,可能就是歸功於它擁有多個不一樣尺度的kernels,每個尺度的kernel會學習不一樣的特徵,把這些不一樣kernels學習到的特徵匯聚給下一層,可以更好的實現全方位的深度學習。
普通的Inception結構以下圖所示:
降維實現的Inception以下圖所示,將256維降到64維,減小了參數量:
爲何VGG網絡的參數那麼多?就是由於它在最後有兩個4096的全連層。Szegedy吸收了教訓,爲了壓縮GoogLeNet的網絡參數,他把全連層取消了。GoogLeNet完整結構以下圖所示:
由何愷明和同事完成的殘差網絡,他們不只僅在2015年的ImageNet上獲勝,同時還贏得了至關多的比賽,幾乎全部重要比賽的第一名。在深度網絡優化中存在一個著名的障礙是:梯度消失和梯度爆炸。這個障礙能夠經過合理的初始化和一些其餘技術來解決,可是隨着網絡的深度增長,準確度飽和並迅速減小,這一現象稱爲degradation,而且普遍的存在於深層網絡中,說明不是全部的系統都很容易被優化。
對於plainnet 若是你單純的提升網絡層數,將沒有什麼用處。如上圖,在cifar-10上,實線是測試集上的錯誤率,虛線是訓練集上的錯誤率。咱們看到層數更深的網絡錯誤率反而更高,這不科學,按道理來講,層數更深的網絡容量更大,那是由於咱們在優化參數上作的不夠好,無法選擇更優的參數。而殘差網絡模型的訓練錯誤率和測試錯誤率都隨着網絡深度的增長在持續的改進。訓練ResNet須要2-3周8個GPU訓練。
何愷明提出了深度殘差學習的概念來解決這一問題。首先咱們假設咱們要求的映射是H(x),經過上面的觀察咱們意識到直接求得H(x)並不那麼容易,因此咱們轉而去求H(x)的殘差形式F(x)=H(x)-x,假設求F(x)的過程比H(x)要簡單,這樣,經過F(x)+x咱們就能夠達到咱們的目標,簡單來講就是上面這幅圖,咱們將這個結構稱之爲一個residual block。 相信不少人都會對第二個假設有疑惑,也就是爲何F(x)比H(x)更容易求得,關於這一點,論文中也沒有明確解釋。可是根據後的實驗結果確實能夠獲得這一個結論。
在ResNet中有這些有趣的跳躍鏈接,如上圖所示。在殘差網絡的反向傳播中,梯度除了流經這些權值向後傳播,還有這些跳躍鏈接,這些跳躍鏈接是加法處理,能夠分散梯度,讓梯度流向以前的一部分,所以你能夠訓練出離圖像很近的一些特徵。
經過下圖ImageNet上神經網絡算法的深度和錯誤率統計,咱們能夠看到,神經網路層數越來約深,同時錯誤率也愈來愈低。
深度學習框架不少,咱們這裏只介紹兩個用的比較多的框架:
Tensorflow:TensorFlow 是一個使用數據流圖(data flow graphs)進行數值計算的開源軟件庫, 一個用於機器智能的開源軟件庫。TensorFlow擁有產品級的高質量代碼,有Google強大的開發、維護能力的加持,總體架構設計也很是優秀。 Google做爲巨頭公司有比高校或者我的開發者多得多的資源投入到TensorFlow的研發,能夠預見,TensorFlow將來的發展將會是飛速的,可能會把大學或者我的維護的深度學習框架遠遠甩在身後。TensorFlow是相對高階的機器學習庫,用戶能夠方便地用它設計神經網絡結構,而沒必要爲了追求高效率的實現親自寫C++或CUDA代碼。 TensorFlow也有內置的TF.Learn和TF.Slim等上層組件能夠幫助快速地設計新網絡 TensorFlow的另一個重要特色是它靈活的移植性,能夠將同一份代碼幾乎不通過修改就輕鬆地部署到有任意數量CPU或GPU的PC、服務器或者移動設備上。 除了支持常見的網絡結構卷積神經網絡(Convolutional Neural Network,CNN)、循環神經網絡(Recurent Neural Network,RNN)外,TensorFlow還支持深度強化學習乃至其餘計算密集的科學計算(如偏微分方程求解等)。
Caffe: Caffe是一個被普遍使用的開源深度學習框架,在Tensorflow出現以前一直是深度學習領域Github star最多的項目。Caffe的主要優點爲:1.容易上手,網絡結構都是以配置文件形式定義,不須要用代碼設計網絡。訓練速度快,組件模塊化,能夠方便的拓展到新的模型和學習任務上。可是Caffe最開始設計時的目標只針對於圖像,沒有考慮文本、語音或者時間序列的數據,所以Caffe對卷積神經網絡的支持很是好,可是對於時間序列RNN,LSTM等支持的不是特別充分。
Tensorflow支持在window、linux、mac上面運行,我搭建的環境使用的是Ubuntu16.04 64位。Tensorflow須要依賴python環境,這裏默認使用python3.5做爲python的基礎版本。推薦使用Anaconda做爲Python環境,由於能夠避免大量的兼容性問題。
Anaconda是Python的一個科學計算髮行版,內置了數百個Python常用會使用的庫,其中可能有一些仍是Tensorflow的依賴庫。Anaconda的Python版本要和Tensorflow版本一致,否則會存在問題。我這裏下載的是Anaconda3-4.2.0-Linux-x86_64.sh。執行:
bash Anaconda3-4.2.0-Linux-x86_64.sh
安裝Anconda,安裝完成後,程序會提示咱們是否把Anaconda3的binary路徑加入到.bashrc,這裏建議添加,這樣之後python命令就會自動使用Anaconda Python3.5的環境了。
Tensowflow分爲cpu版本和gpu版本,若是你的電腦上有NVIDIA顯卡的話,建議裝GPU版本,他會加快你訓練的速度。cpu版本安裝比較簡單,這裏就不在贅述,主要講下gpu版本的安裝。首先使用:
lspci | grep -i nvidia
查看nvidia顯卡的型號,而後去developer.nvidia.com/cuda-gpus查看你的顯卡是否支持cuda,只有支持cuda的gpu才能安裝tensorflow的gpu版本。個人電腦是GeForce 940M 支持cuda。
(1) 安裝CUDA和cuDNN
首先在nvidia官網上下載對應的cuda版本。這邊下載是cuda_8.0.61_375.26_linux.run。這裏下載會比較慢,建議使用迅雷下載。在安裝前須要暫停NVIDIA的驅動X server,首先使用ctrl+alt+f2接入ubuntu的命令界面,若是進不去,有些電腦須要使用fn+ctrl+alt+f2而後執行
sudo /etc/init.d/lightdm stop
暫停X Server。而後執行以下命令安裝:
chmod u+x cuda_8.0.61_375.26_linux.runsudo ./cuda_8.0.61_375.26_linux.run
先按q跳過開頭的license,接着輸入accept接受協議,而後按y鍵選擇安裝驅動程序,在以後的選擇中,咱們選擇不安裝OpenGL,不然可能出如今登陸界面循環登陸的問題。而後按n鍵選擇不安裝samples。
接下來安裝cuDNN,cuDNN是NVIDIA推出的深度學習中的CNN和RNN的高度優化的實現,底層使用了不少先進的技術和接口,所以比其餘GPU上的神經網絡庫性能高很多。首先從官網上下載cuDNN,這一步須要先註冊NVIDIA的賬號,並等待審覈。執行
cd /usr/local sudo tar -xzvf ~/Downloads/cudnn-8.0-linux-x64-v6.0.tgz
就完能夠成了cuDNN的安裝。
(2) 安裝Tensorflow
在github上下載對應的版本,這裏下載的是tensorflow_gpu-1.2.1-cp35-cp35m-
linux_x86_64.whl。而後執行以下命令便可完成安裝。
pip install tensorflow_gpu-1.2.1-cp35-cp35m-linux_x86_64.whl
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)print(mnist.train.images.shape, mnist.train.labels.shape)print(mnist.test.images.shape, mnist.test.labels.shape)print(mnist.validation.images.shape, mnist.validation.labels.shape)
sess = tf.InteractiveSession()
x = tf.placeholder(tf.float32, [None, 784])
W = tf.Variable(tf.zeros([784, 10]))
b = tf.Variable(tf.zeros([10]))
y = tf.nn.softmax(tf.matmul(x, W) + b)
y_ = tf.placeholder(tf.float32, [None, 10])
cross_entropy = tf.reduce_mean(-tf.reduce_sum(y_ * tf.log(y), reduction_indices=[1]))
train_step = tf.train.GradientDescentOptimizer(0.5).minimize(cross_entropy)
tf.global_variables_initializer().run()
for i in range(1000):
batch_xs, batch_ys = mnist.train.next_batch(1000)
train_step.run({x: batch_xs, y_: batch_ys})
correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))print(accu
複製代碼
from tensorflow.examples.tutorials.mnist import input_dataimport tensorflow as tf
mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)
sess = tf.InteractiveSession()def weight_variable(shape):
initial = tf.truncated_normal(shape, stddev=0.1) return tf.Variable(initial)def bias_variable(shape):
initial = tf.constant(0.1, shape=shape) return tf.Variable(initial)def conv2d(x, W):
return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')def max_pool_2x2(x):
return tf.nn.max_pool(x, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
x = tf.placeholder(tf.float32, [None, 784])
y_ = tf.placeholder(tf.float32, [None, 10])
x_image = tf.reshape(x, [-1, 28, 28, 1])
W_conv1 = weight_variable([5, 5, 1, 32])
b_conv1 = bias_variable([32])
h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1)
h_pool1 = max_pool_2x2(h_conv1)
W_conv2 = weight_variable([5, 5, 32, 64])
b_conv2 = bias_variable([64])
h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2)
h_pool2 = max_pool_2x2(h_conv2)
W_fc1 = weight_variable([7 * 7 * 64, 1024])
b_fc1 = bias_variable([1024])
h_pool2_flat = tf.reshape(h_pool2, [-1, 7 * 7 * 64])
h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1)
keep_prob = tf.placeholder(tf.float32)
h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)
W_fc2 = weight_variable([1024, 10])
b_fc2 = bias_variable([10])
y_conv = tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)
cross_entropy = tf.reduce_mean(-tf.reduce_sum(y_ * tf.log(y_conv), reduction_indices=[1]))
train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)
correct_prediction = tf.equal(tf.argmax(y_conv, 1), tf.argmax(y_, 1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
tf.global_variables_initializer().run()for i in range(10000):
batch = mnist.train.next_batch(50) if i % 100 == 0:
train_accuracy = accuracy.eval(feed_dict={x: batch[0], y_: batch[1], keep_prob: 1.0})
print("step %d, training accuracy %g" % (i, train_accuracy))
train_step.run(feed_dict={x: batch[0], y_: batch[1], keep_prob: 0.5})
print("test accuracy %g" % accuracy.eval(feed_dict={x: mnist.test.images,
y_: mnist.test.labels, keep_prob: 1.0}))
複製代碼
在企鵝電競的業務場景中,須要識別主播當前在王者榮耀中使用的英雄。這裏初次嘗試使用深度學習的方法來分類,使用的框架是Caffe。
首先是準備訓練數據,咱們須要根據業務場景設計須要分類的特徵。咱們能夠選擇整張遊戲畫面、遊戲中英雄畫面或者技能鍵做爲分類特徵,以下圖所示。使用整張遊戲畫面時,圖片包含較多無關元素,可以標識英雄的特徵區域較小。使用英雄截圖能夠做爲明顯特徵,可是因爲每一個英雄存在多套皮膚,準備數據比較困難,以及會對識別準確率有影響。因此使用技能鍵區域做爲分類特徵是較好的選擇。
在設計分類特徵是須要特徵儘量清晰,而且不一樣種類之間的特徵差異大。
收集數據並標註是繁瑣可是很重要的任務,同一個模型在不一樣的數據訓練下可能效果會差異很大。這裏是在Youtube針對不一樣的英雄分別下載一段15分鐘左右的視頻,而後使用以下ffmpeg命令截取技能鍵區域的圖片並將其尺寸壓縮。
ffmpeg -i videos/1/test.mp4 -r 1 -vf "crop=380:340:885:352,scale=224:224" images/1/test_%4d.png
數據集圖片文件夾以下圖所示。須要人肉篩選其中不包含技能鍵的噪聲圖片,並將其放入0文件夾中。0文件夾做爲背景分類,對應於沒法識別英雄的分類。
這裏每一個分類大概包含1000-2000張圖片。數據集固然越大越好,而且覆蓋儘量多的場景,增長模型的泛化能力。
數據集生成好了以後,須要將其轉換爲Caffe可以識別的格式。首先使用下圖左側的python代碼生成訓練集和測試集所包含的圖片,生成文件以下圖右側所示,包含圖片路徑和分類。其中測試集爲全部圖片的五分之一。
而後將圖片轉換爲LMDB格式的數據,Caffe提供了工具幫助咱們轉換。處理腳本以下圖所示。
上文介紹的六種模型爲經常使用的圖片分類模型,這裏咱們選擇GoogLeNet做爲模型網絡。在Caffe工程的models文件夾中有這些網絡模型,查看caffe/models/bvlc_googlenet文件夾,其中文件以下圖:
solver.prototxt文件夾中定義了訓練模型所用到的參數,其中參數的具體含義以下圖所示:
train_val.prototxt爲GoogLeNet網絡的結構的具體定義,網絡文件不少,這裏介紹下獨立的網絡層結構。首先是數據層,其參數以下圖所示:
train_val.prototxt爲GoogLeNet網絡的結構的具體定義,網絡文件不少,這裏介紹下獨立的網絡層結構。首先是數據層,其參數以下圖所示:
卷積層、ReLU層和池化層結構以下圖所示:
LRN層的結構以下圖所示:
Dropout層和全鏈接層以下圖所示:
deploy文件爲部署時使用的網絡結構,其大部份內容與train_val.prototxt文件類似,去掉了一些測試層的內容。
將數據和模型準備好了以後,就能夠進行模型的訓練了。這裏調用以下代碼開始訓練:
caffe train -solver solver.prototxt
能夠添加-snapshot 參數能夠接着上次訓練的模型訓練,訓練日誌以下圖所示:
當模型收斂,或者準確率達到咱們要求以後,能夠中止訓練。
GoogLeNet自己是比較大的網絡,咱們能夠根據本身的需求裁剪網絡。 GoogLeNet中的LRN層影響不大能夠去掉,或者刪除一些卷積層,下降網絡的層數。
當咱們的數據量比較小的時候,訓練完整的網絡參數比較容易過擬合。這時咱們能夠經過只訓練網絡的某幾層來解決這個問題。 首先修改train_val.prototxt中的全鏈接層的名稱,即loss1/classifier,loss2/classifier,loss3/classifier這三層的名稱,以及他們的輸出分類個數,默認是1000,須要改爲咱們本身的種類總數。這樣咱們再加載訓練好的model時,這三層的參數纔會從新初始化。而後將全部其餘層的lr_mult該爲0,這樣其餘層的參數不會改變,使用預先訓練好的參數。 下載bvlc_googlenet.caffemodel,這是谷歌在ImageNet上訓練出來的參數。而後調用caffe train -solver solver.prototxt -weights bvlc_googlenet.caffemodel便可訓練。
局部極小值問題:可行的trick是開始階段batchsize調小。
Loss爆炸:學習率調小。
Loss始終不收斂:要懷疑數據集或label是否有問題。
過擬合:數據加強,正則化、Dropout、BatchNormalization以及early stopping等策略。
權值初始化:通常xavier或gaussian。
Finetune: 使用GoogLeNet或VGG,能夠finetune已有模型。
一、本文主要整理自CS231n課程筆記,
二、Yoshua Bengio 《Deep Learning Book》 炸裂推薦,很是值得一看。
三、周志華《機器學習》 適合機器學習入門。
四、《Tensorflow 實戰》Tensorflow實踐不錯。
五、斯坦福大學深度學習教程 。
李濤 編輯
此文已由做者受權騰訊雲技術社區發佈,轉載請註明原文出處