Ⅰ CNN網路簡介
卷積神經網路簡介(Convolutional Neural Networks,簡稱CNN)
卷積神經網路是近年發展起來,並引起廣泛重視的一種高效識別方法。20世紀60年代,Hubel和Wiesel在研究貓腦皮層中用於局部敏感和方向選擇的神經元時發現其獨特的網路結構可以有效地降低反饋神經網路的復雜性,繼而提出了卷積神經網路(Convolutional
Neural
Networks-簡稱CNN)。現在,CNN已經成為眾多科學領域的研究熱點之一,特別是在模式分類領域,由於該網路避免了對圖像的復雜前期預處理,可以直接輸入原始圖像,因而得到了更為廣泛的應用。
K.Fukushima在1980年提出的新識別機是卷積神經網路的第一個實現網路。隨後,更多的科研工作者對該網路進行了改進。其中,具有代表性的研究成果是Alexander和Taylor提出的「改進認知機」,該方法綜合了各種改進方法的優點並避免了耗時的誤差反向傳播。
一般地,CNN的基本結構包括兩層,其一為特徵提取層,每個神經元的輸入與前一層的局部接受域相連,並提取該局部的特徵。一旦該局部特徵被提取後,它與其它特徵間的位置關系也隨之確定下來;其二是特徵映射層,網路的每個計算層由多個特徵映射組成,每個特徵映射是一個平面,平面上所有神經元的權值相等。特徵映射結構採用影響函數核小的sigmoid函數作為卷積網路的激活函數,使得特徵映射具有位移不變性。此外,由於一個映射面上的神經元共享權值,因而減少了網路自由參數的個數。卷積神經網路中的每一個卷積層都緊跟著一個用來求局部平均與二次提取的計算層,這種特有的兩次特徵提取結構減小了特徵解析度。
CNN主要用來識別位移、縮放及其他形式扭曲不變性的二維圖形。由於CNN的特徵檢測層通過訓練數據進行學習,所以在使用CNN時,避免了顯示的特徵抽取,而隱式地從訓練數據中進行學習;再者由於同一特徵映射面上的神經元權值相同,所以網路可以並行學習,這也是卷積網路相對於神經元彼此相連網路的一大優勢。卷積神經網路以其局部權值共享的特殊結構在語音識別和圖像處理方面有著獨特的優越性,其布局更接近於實際的生物神經網路,權值共享降低了網路的復雜性,特別是多維輸入向量的圖像可以直接輸入網路這一特點避免了特徵提取和分類過程中數據重建的復雜度。
1. 神經網路
首先介紹神經網路,這一步的詳細可以參考資源1。簡要介紹下。神經網路的每個單元如下:
其對應的公式如下:
其中,該單元也可以被稱作是Logistic回歸模型。當將多個單元組合起來並具有分層結構時,就形成了神經網路模型。下圖展示了一個具有一個隱含層的神經網路。
其對應的公式如下:
比較類似的,可以拓展到有2,3,4,5,…個隱含層。
神經網路的訓練方法也同Logistic類似,不過由於其多層性,還需要利用鏈式求導法則對隱含層的節點進行求導,即梯度下降+鏈式求導法則,專業名稱為反向傳播。關於訓練演算法,本文暫不涉及。
2 卷積神經網路
在圖像處理中,往往把圖像表示為像素的向量,比如一個1000×1000的圖像,可以表示為一個1000000的向量。在上一節中提到的神經網路中,如果隱含層數目與輸入層一樣,即也是1000000時,那麼輸入層到隱含層的參數數據為1000000×1000000=10^12,這樣就太多了,基本沒法訓練。所以圖像處理要想練成神經網路大法,必先減少參數加快速度。就跟辟邪劍譜似的,普通人練得很挫,一旦自宮後內力變強劍法變快,就變的很牛了。
2.1 局部感知
卷積神經網路有兩種神器可以降低參數數目,第一種神器叫做局部感知野。一般認為人對外界的認知是從局部到全局的,而圖像的空間聯系也是局部的像素聯系較為緊密,而距離較遠的像素相關性則較弱。因而,每個神經元其實沒有必要對全局圖像進行感知,只需要對局部進行感知,然後在更高層將局部的信息綜合起來就得到了全局的信息。網路部分連通的思想,也是受啟發於生物學裡面的視覺系統結構。視覺皮層的神經元就是局部接受信息的(即這些神經元只響應某些特定區域的刺激)。如下圖所示:左圖為全連接,右圖為局部連接。
在上右圖中,假如每個神經元只和10×10個像素值相連,那麼權值數據為1000000×100個參數,減少為原來的千分之一。而那10×10個像素值對應的10×10個參數,其實就相當於卷積操作。
2.2 參數共享
但其實這樣的話參數仍然過多,那麼就啟動第二級神器,即權值共享。在上面的局部連接中,每個神經元都對應100個參數,一共1000000個神經元,如果這1000000個神經元的100個參數都是相等的,那麼參數數目就變為100了。
怎麼理解權值共享呢?我們可以這100個參數(也就是卷積操作)看成是提取特徵的方式,該方式與位置無關。這其中隱含的原理則是:圖像的一部分的統計特性與其他部分是一樣的。這也意味著我們在這一部分學習的特徵也能用在另一部分上,所以對於這個圖像上的所有位置,我們都能使用同樣的學習特徵。
更直觀一些,當從一個大尺寸圖像中隨機選取一小塊,比如說 8×8 作為樣本,並且從這個小塊樣本中學習到了一些特徵,這時我們可以把從這個
8×8 樣本中學習到的特徵作為探測器,應用到這個圖像的任意地方中去。特別是,我們可以用從 8×8
樣本中所學習到的特徵跟原本的大尺寸圖像作卷積,從而對這個大尺寸圖像上的任一位置獲得一個不同特徵的激活值。
如下圖所示,展示了一個33的卷積核在55的圖像上做卷積的過程。每個卷積都是一種特徵提取方式,就像一個篩子,將圖像中符合條件(激活值越大越符合條件)的部分篩選出來。
2.3 多卷積核
上面所述只有100個參數時,表明只有1個100*100的卷積核,顯然,特徵提取是不充分的,我們可以添加多個卷積核,比如32個卷積核,可以學習32種特徵。在有多個卷積核時,如下圖所示:
上圖右,不同顏色表明不同的卷積核。每個卷積核都會將圖像生成為另一幅圖像。比如兩個卷積核就可以將生成兩幅圖像,這兩幅圖像可以看做是一張圖像的不同的通道。如下圖所示,下圖有個小錯誤,即將w1改為w0,w2改為w1即可。下文中仍以w1和w2稱呼它們。
下圖展示了在四個通道上的卷積操作,有兩個卷積核,生成兩個通道。其中需要注意的是,四個通道上每個通道對應一個卷積核,先將w2忽略,只看w1,那麼在w1的某位置(i,j)處的值,是由四個通道上(i,j)處的卷積結果相加然後再取激活函數值得到的。
所以,在上圖由4個通道卷積得到2個通道的過程中,參數的數目為4×2×2×2個,其中4表示4個通道,第一個2表示生成2個通道,最後的2×2表示卷積核大小。
2.4 Down-pooling
在通過卷積獲得了特徵 (features)
之後,下一步我們希望利用這些特徵去做分類。理論上講,人們可以用所有提取得到的特徵去訓練分類器,例如 softmax
分類器,但這樣做面臨計算量的挑戰。例如:對於一個 96X96
像素的圖像,假設我們已經學習得到了400個定義在8X8輸入上的特徵,每一個特徵和圖像卷積都會得到一個 (96 − 8 + 1) × (96 − 8+ 1) = 7921 維的卷積特徵,由於有 400 個特徵,所以每個樣例 (example) 都會得到一個 892 × 400 =3,168,400 維的卷積特徵向量。學習一個擁有超過 3 百萬特徵輸入的分類器十分不便,並且容易出現過擬合 (over-fitting)。
為了解決這個問題,首先回憶一下,我們之所以決定使用卷積後的特徵是因為圖像具有一種「靜態性」的屬性,這也就意味著在一個圖像區域有用的特徵極有可能在另一個區域同樣適用。因此,為了描述大的圖像,一個很自然的想法就是對不同位置的特徵進行聚合統計,例如,人們可以計算圖像一個區域上的某個特定特徵的平均值(或最大值)。這些概要統計特徵不僅具有低得多的維度 (相比使用所有提取得到的特徵),同時還會改善結果(不容易過擬合)。這種聚合的操作就叫做池(pooling),有時也稱為平均池化或者最大池化 (取決於計算池化的方法)。
至此,卷積神經網路的基本結構和原理已經闡述完畢。
2.5 多層卷積
在實際應用中,往往使用多層卷積,然後再使用全連接層進行訓練,多層卷積的目的是一層卷積學到的特徵往往是局部的,層數越高,學到的特徵就越全局化。
3 ImageNet-2010網路結構
ImageNetLSVRC是一個圖片分類的比賽,其訓練集包括127W+張圖片,驗證集有5W張圖片,測試集有15W張圖片。本文截取2010年AlexKrizhevsky的CNN結構進行說明,該結構在2010年取得冠軍,top-5錯誤率為15.3%。值得一提的是,在今年的ImageNetLSVRC比賽中,取得冠軍的GoogNet已經達到了top-5錯誤率6.67%。可見,深度學習的提升空間還很巨大。
下圖即為Alex的CNN結構圖。需要注意的是,該模型採用了2-GPU並行結構,即第1、2、4、5卷積層都是將模型參數分為2部分進行訓練的。在這里,更進一步,並行結構分為數據並行與模型並行。數據並行是指在不同的GPU上,模型結構相同,但將訓練數據進行切分,分別訓練得到不同的模型,然後再將模型進行融合。而模型並行則是,將若干層的模型參數進行切分,不同的GPU上使用相同的數據進行訓練,得到的結果直接連接作為下一層的輸入。
上圖模型的基本參數為:
輸入:224×224大小的圖片,3通道
第一層卷積:5×5大小的卷積核96個,每個GPU上48個。
第一層max-pooling:2×2的核。
第二層卷積:3×3卷積核256個,每個GPU上128個。
第二層max-pooling:2×2的核。
第三層卷積:與上一層是全連接,3*3的卷積核384個。分到兩個GPU上個192個。
第四層卷積:3×3的卷積核384個,兩個GPU各192個。該層與上一層連接沒有經過pooling層。
第五層卷積:3×3的卷積核256個,兩個GPU上個128個。
第五層max-pooling:2×2的核。
第一層全連接:4096維,將第五層max-pooling的輸出連接成為一個一維向量,作為該層的輸入。
第二層全連接:4096維
Softmax層:輸出為1000,輸出的每一維都是圖片屬於該類別的概率。
4 DeepID網路結構
DeepID網路結構是香港中文大學的Sun
Yi開發出來用來學習人臉特徵的卷積神經網路。每張輸入的人臉被表示為160維的向量,學習到的向量經過其他模型進行分類,在人臉驗證試驗上得到了97.45%的正確率,更進一步的,原作者改進了CNN,又得到了99.15%的正確率。
如下圖所示,該結構與ImageNet的具體參數類似,所以只解釋一下不同的部分吧。
上圖中的結構,在最後只有一層全連接層,然後就是softmax層了。論文中就是以該全連接層作為圖像的表示。在全連接層,以第四層卷積和第三層max-pooling的輸出作為全連接層的輸入,這樣可以學習到局部的和全局的特徵。
Ⅱ 如何用PyTorch實現遞歸神經網路
從 Siri 到谷歌翻譯,深度神經網路已經在機器理解自然語言方面取得了巨大突破。這些模型大多數將語言視為單調的單詞或字元序列,並使用一種稱為循環神經網路(recurrent neural network/RNN)的模型來處理該序列。但是許多語言學家認為語言最好被理解為具有樹形結構的層次化片語,一種被稱為遞歸神經網路(recursive neural network)的深度學習模型考慮到了這種結構,這方面已經有大量的研究。雖然這些模型非常難以實現且效率很低,但是一個全新的深度學習框架 PyTorch 能使它們和其它復雜的自然語言處理模型變得更加容易。
雖然遞歸神經網路很好地顯示了 PyTorch 的靈活性,但它也廣泛支持其它的各種深度學習框架,特別的是,它能夠對計算機視覺(computer vision)計算提供強大的支撐。PyTorch 是 Facebook AI Research 和其它幾個實驗室的開發人員的成果,該框架結合了 Torch7 高效靈活的 GPU 加速後端庫與直觀的 Python 前端,它的特點是快速成形、代碼可讀和支持最廣泛的深度學習模型。
開始 SPINN
鏈接中的文章(https://github.com/jekbradbury/examples/tree/spinn/snli)詳細介紹了一個遞歸神經網路的 PyTorch 實現,它具有一個循環跟蹤器(recurrent tracker)和 TreeLSTM 節點,也稱為 SPINN——SPINN 是深度學習模型用於自然語言處理的一個例子,它很難通過許多流行的框架構建。這里的模型實現部分運用了批處理(batch),所以它可以利用 GPU 加速,使得運行速度明顯快於不使用批處理的版本。
SPINN 的意思是堆棧增強的解析器-解釋器神經網路(Stack-augmented Parser-Interpreter Neural Network),由 Bowman 等人於 2016 年作為解決自然語言推理任務的一種方法引入,該論文中使用了斯坦福大學的 SNLI 數據集。
該任務是將語句對分為三類:假設語句 1 是一幅看不見的圖像的准確標題,那麼語句 2(a)肯定(b)可能還是(c)絕對不是一個准確的標題?(這些類分別被稱為蘊含(entailment)、中立(neutral)和矛盾(contradiction))。例如,假設一句話是「兩只狗正跑過一片場地」,蘊含可能會使這個語句對變成「戶外的動物」,中立可能會使這個語句對變成「一些小狗正在跑並試圖抓住一根棍子」,矛盾能會使這個語句對變成「寵物正坐在沙發上」。
特別地,研究 SPINN 的初始目標是在確定語句的關系之前將每個句子編碼(encoding)成固定長度的向量表示(也有其它方式,例如注意模型(attention model)中將每個句子的每個部分用一種柔焦(soft focus)的方法相互比較)。
數據集是用句法解析樹(syntactic parse tree)方法由機器生成的,句法解析樹將每個句子中的單詞分組成具有獨立意義的短語和子句,每個短語由兩個詞或子短語組成。許多語言學家認為,人類通過如上面所說的樹的分層方式來組合詞意並理解語言,所以用相同的方式嘗試構建一個神經網路是值得的。下面的例子是數據集中的一個句子,其解析樹由嵌套括弧表示:
( ( The church ) ( ( has ( cracks ( in ( the ceiling ) ) ) ) . ) )
這個句子進行編碼的一種方式是使用含有解析樹的神經網路構建一個神經網路層 Rece,這個神經網路層能夠組合詞語對(用詞嵌入(word embedding)表示,如 GloVe)、 和/或短語,然後遞歸地應用此層(函數),將最後一個 Rece 產生的結果作為句子的編碼:
X = Rece(「the」, 「ceiling」)
Y = Rece(「in」, X)
... etc.
但是,如果我希望網路以更類似人類的方式工作,從左到右閱讀並保留句子的語境,同時仍然使用解析樹組合短語?或者,如果我想訓練一個網路來構建自己的解析樹,讓解析樹根據它看到的單詞讀取句子?這是一個同樣的但方式略有不同的解析樹的寫法:
The church ) has cracks in the ceiling ) ) ) ) . ) )
或者用第 3 種方式表示,如下:
WORDS: The church has cracks in the ceiling .
PARSES: S S R S S S S S R R R R S R R
我所做的只是刪除開括弧,然後用「S」標記「shift」,並用「R」替換閉括弧用於「rece」。但是現在可以從左到右讀取信息作為一組指令來操作一個堆棧(stack)和一個類似堆棧的緩沖區(buffer),能得到與上述遞歸方法完全相同的結果:
1. 將單詞放入緩沖區。
2. 從緩沖區的前部彈出「The」,將其推送(push)到堆棧上層,緊接著是「church」。
3. 彈出前 2 個堆棧值,應用於 Rece,然後將結果推送回堆棧。
4. 從緩沖區彈出「has」,然後推送到堆棧,然後是「cracks」,然後是「in」,然後是「the」,然後是「ceiling」。
5. 重復四次:彈出 2 個堆棧值,應用於 Rece,然後推送結果。
6. 從緩沖區彈出「.」,然後推送到堆棧上層。
7. 重復兩次:彈出 2 個堆棧值,應用於 Rece,然後推送結果。
8. 彈出剩餘的堆棧值,並將其作為句子編碼返回。
我還想保留句子的語境,以便在對句子的後半部分應用 Rece 層時考慮系統已經讀取的句子部分的信息。所以我將用一個三參數函數替換雙參數的 Rece 函數,該函數的輸入值為一個左子句、一個右子句和當前句的上下文狀態。該狀態由神經網路的第二層(稱為循環跟蹤器(Tracker)的單元)創建。Tracker 在給定當前句子上下文狀態、緩沖區中的頂部條目 b 和堆棧中前兩個條目 s1\s2 時,在堆棧操作的每個步驟(即,讀取每個單詞或閉括弧)後生成一個新狀態:
context[t+1] = Tracker(context[t], b, s1, s2)
容易設想用你最喜歡的編程語言來編寫代碼做這些事情。對於要處理的每個句子,它將從緩沖區載入下一個單詞,運行跟蹤器,檢查是否將單詞推送入堆棧或執行 Rece 函數,執行該操作;然後重復,直到對整個句子完成處理。通過對單個句子的應用,該過程構成了一個大而復雜的深度神經網路,通過堆棧操作的方式一遍又一遍地應用它的兩個可訓練層。但是,如果你熟悉 TensorFlow 或 Theano 等傳統的深度學習框架,就知道它們很難實現這樣的動態過程。你值得花點時間回顧一下,探索為什麼 PyTorch 能有所不同。
圖論
圖 1:一個函數的圖結構表示
深度神經網路本質上是有大量參數的復雜函數。深度學習的目的是通過計算以損失函數(loss)度量的偏導數(梯度)來優化這些參數。如果函數表示為計算圖結構(圖 1),則向後遍歷該圖可實現這些梯度的計算,而無需冗餘工作。每個現代深度學習框架都是基於此反向傳播(backpropagation)的概念,因此每個框架都需要一個表示計算圖的方式。
在許多流行的框架中,包括 TensorFlow、Theano 和 Keras 以及 Torch7 的 nngraph 庫,計算圖是一個提前構建的靜態對象。該圖是用像數學表達式的代碼定義的,但其變數實際上是尚未保存任何數值的佔位符(placeholder)。圖中的佔位符變數被編譯進函數,然後可以在訓練集的批處理上重復運行該函數來產生輸出和梯度值。
這種靜態計算圖(static computation graph)方法對於固定結構的卷積神經網路效果很好。但是在許多其它應用中,有用的做法是令神經網路的圖結構根據數據而有所不同。在自然語言處理中,研究人員通常希望通過每個時間步驟中輸入的單詞來展開(確定)循環神經網路。上述 SPINN 模型中的堆棧操作很大程度上依賴於控制流程(如 for 和 if 語句)來定義特定句子的計算圖結構。在更復雜的情況下,你可能需要構建結構依賴於模型自身的子網路輸出的模型。
這些想法中的一些(雖然不是全部)可以被生搬硬套到靜態圖系統中,但幾乎總是以降低透明度和增加代碼的困惑度為代價。該框架必須在其計算圖中添加特殊的節點,這些節點代表如循環和條件的編程原語(programming primitive),而用戶必須學習和使用這些節點,而不僅僅是編程代碼語言中的 for 和 if 語句。這是因為程序員使用的任何控制流程語句將僅運行一次,當構建圖時程序員需要硬編碼(hard coding)單個計算路徑。
例如,通過詞向量(從初始狀態 h0 開始)運行循環神經網路單元(rnn_unit)需要 TensorFlow 中的特殊控制流節點 tf.while_loop。需要一個額外的特殊節點來獲取運行時的詞長度,因為在運行代碼時它只是一個佔位符。
# TensorFlow
# (this code runs once, ring model initialization)
# 「words」 is not a real list (it』s a placeholder variable) so
# I can』t use 「len」
cond = lambda i, h: i < tf.shape(words)[0]
cell = lambda i, h: rnn_unit(words[i], h)
i = 0
_, h = tf.while_loop(cond, cell, (i, h0))
基於動態計算圖(dynamic computation graph)的方法與之前的方法有根本性不同,它有幾十年的學術研究歷史,其中包括了哈佛的 Kayak、自動微分庫(autograd)以及以研究為中心的框架 Chainer和 DyNet。在這樣的框架(也稱為運行時定義(define-by-run))中,計算圖在運行時被建立和重建,使用相同的代碼為前向通過(forward pass)執行計算,同時也為反向傳播(backpropagation)建立所需的數據結構。這種方法能產生更直接的代碼,因為控制流程的編寫可以使用標準的 for 和 if。它還使調試更容易,因為運行時斷點(run-time breakpoint)或堆棧跟蹤(stack trace)將追蹤到實際編寫的代碼,而不是執行引擎中的編譯函數。可以在動態框架中使用簡單的 Python 的 for 循環來實現有相同變數長度的循環神經網路。
# PyTorch (also works in Chainer)
# (this code runs on every forward pass of the model)
# 「words」 is a Python list with actual values in it
h = h0
for word in words:
h = rnn_unit(word, h)
PyTorch 是第一個 define-by-run 的深度學習框架,它與靜態圖框架(如 TensorFlow)的功能和性能相匹配,使其能很好地適合從標准卷積神經網路(convolutional network)到最瘋狂的強化學習(reinforcement learning)等思想。所以讓我們來看看 SPINN 的實現。
代碼
在開始構建網路之前,我需要設置一個數據載入器(data loader)。通過深度學習,模型可以通過數據樣本的批處理進行操作,通過並行化(parallelism)加快訓練,並在每一步都有一個更平滑的梯度變化。我想在這里可以做到這一點(稍後我將解釋上述堆棧操作過程如何進行批處理)。以下 Python 代碼使用內置於 PyTorch 的文本庫的系統來載入數據,它可以通過連接相似長度的數據樣本自動生成批處理。運行此代碼之後,train_iter、dev_iter 和 test_itercontain 循環遍歷訓練集、驗證集和測試集分塊 SNLI 的批處理。
from torchtext import data, datasets
TEXT = datasets.snli.ParsedTextField(lower=True)
TRANSITIONS = datasets.snli.ShiftReceField()
LABELS = data.Field(sequential=False)train, dev, test = datasets.SNLI.splits(
TEXT, TRANSITIONS, LABELS, wv_type='glove.42B')TEXT.build_vocab(train, dev, test)
train_iter, dev_iter, test_iter = data.BucketIterator.splits(
(train, dev, test), batch_size=64)
你可以在 train.py中找到設置訓練循環和准確性(accuracy)測量的其餘代碼。讓我們繼續。如上所述,SPINN 編碼器包含參數化的 Rece 層和可選的循環跟蹤器來跟蹤句子上下文,以便在每次網路讀取單詞或應用 Rece 時更新隱藏狀態;以下代碼代表的是,創建一個 SPINN 只是意味著創建這兩個子模塊(我們將很快看到它們的代碼),並將它們放在一個容器中以供稍後使用。
import torchfrom torch import nn
# subclass the Mole class from PyTorch』s neural network package
class SPINN(nn.Mole):
def __init__(self, config):
super(SPINN, self).__init__()
self.config = config self.rece = Rece(config.d_hidden, config.d_tracker)
if config.d_tracker is not None:
self.tracker = Tracker(config.d_hidden, config.d_tracker)
當創建模型時,SPINN.__init__ 被調用了一次;它分配和初始化參數,但不執行任何神經網路操作或構建任何類型的計算圖。在每個新的批處理數據上運行的代碼由 SPINN.forward 方法定義,它是用戶實現的方法中用於定義模型向前過程的標准 PyTorch 名稱。上面描述的是堆棧操作演算法的一個有效實現,即在一般 Python 中,在一批緩沖區和堆棧上運行,每一個例子都對應一個緩沖區和堆棧。我使用轉移矩陣(transition)包含的「shift」和「rece」操作集合進行迭代,運行 Tracker(如果存在),並遍歷批處理中的每個樣本來應用「shift」操作(如果請求),或將其添加到需要「rece」操作的樣本列表中。然後在該列表中的所有樣本上運行 Rece 層,並將結果推送回到它們各自的堆棧。
def forward(self, buffers, transitions):
# The input comes in as a single tensor of word embeddings;
# I need it to be a list of stacks, one for each example in
# the batch, that we can pop from independently. The words in
# each example have already been reversed, so that they can
# be read from left to right by popping from the end of each
# list; they have also been prefixed with a null value.
buffers = [list(torch.split(b.squeeze(1), 1, 0))
for b in torch.split(buffers, 1, 1)]
# we also need two null values at the bottom of each stack,
# so we can from the nulls in the input; these nulls
# are all needed so that the tracker can run even if the
# buffer or stack is empty
stacks = [[buf[0], buf[0]] for buf in buffers]
if hasattr(self, 'tracker'):
self.tracker.reset_state()
for trans_batch in transitions:
if hasattr(self, 'tracker'):
# I described the Tracker earlier as taking 4
# arguments (context_t, b, s1, s2), but here I
# provide the stack contents as a single argument
# while storing the context inside the Tracker
# object itself.
tracker_states, _ = self.tracker(buffers, stacks)
else:
tracker_states = itertools.repeat(None)
lefts, rights, trackings = [], [], []
batch = zip(trans_batch, buffers, stacks, tracker_states)
for transition, buf, stack, tracking in batch:
if transition == SHIFT:
stack.append(buf.pop())
elif transition == REDUCE:
rights.append(stack.pop())
lefts.append(stack.pop())
trackings.append(tracking)
if rights:
reced = iter(self.rece(lefts, rights, trackings))
for transition, stack in zip(trans_batch, stacks):
if transition == REDUCE:
stack.append(next(reced))
return [stack.pop() for stack in stacks]
在調用 self.tracker 或 self.rece 時分別運行 Tracker 或 Rece 子模塊的向前方法,該方法需要在樣本列表上應用前向操作。在主函數的向前方法中,在不同的樣本上進行獨立的操作是有意義的,即為批處理中每個樣本提供分離的緩沖區和堆棧,因為所有受益於批處理執行的重度使用數學和需要 GPU 加速的操作都在 Tracker 和 Rece 中進行。為了更干凈地編寫這些函數,我將使用一些 helper(稍後將定義)將這些樣本列表轉化成批處理張量(tensor),反之亦然。
我希望 Rece 模塊自動批處理其參數以加速計算,然後解批處理(unbatch)它們,以便可以單獨推送和彈出。用於將每對左、右子短語表達組合成父短語(parent phrase)的實際組合函數是 TreeLSTM,它是普通循環神經網路單元 LSTM 的變型。該組合函數要求每個子短語的狀態實際上由兩個張量組成,一個隱藏狀態 h 和一個存儲單元(memory cell)狀態 c,而函數是使用在子短語的隱藏狀態操作的兩個線性層(nn.Linear)和將線性層的結果與子短語的存儲單元狀態相結合的非線性組合函數 tree_lstm。在 SPINN 中,這種方式通過添加在 Tracker 的隱藏狀態下運行的第 3 個線性層進行擴展。
圖 2:TreeLSTM 組合函數增加了第 3 個輸入(x,在這種情況下為 Tracker 狀態)。在下面所示的 PyTorch 實現中,5 組的三種線性變換(由藍色、黑色和紅色箭頭的三元組表示)組合為三個 nn.Linear 模塊,而 tree_lstm 函數執行位於框內的所有計算。圖來自 Chen et al. (2016)。
Ⅲ 卷積層在神經網路中如何運算
卷積神經網路(Convolutional Neural Networks, CNN)的核心是進行卷積運算操作。在實際應用中往往採用多層網路結構,因此又被稱為深度卷積神經網路。本文將從單個卷積的計算出發,帶大家掌握卷積層在神經網路中的運算方法。
2.1 單個卷積的計算
要想了解卷積層在神經網路中的計算過程,我們首先需要了解單個「卷積」是如何運作的。
想必大家在學習CNN的過程中都見過下圖( 出處在此 ,這上面有各種各樣的卷積gif圖):
input_shape=(5,5),kernelsize=(3,3),padding=『same』,stride=1,output_shape=(5,5)
在此圖中:
在此次計算中:
Ps: 在實際應用中,每一個輸出的特徵圖還會配備一個偏置s,在上圖中無表示。
2.2 卷積層在神經網路中的運算
了解完單個卷積是如何計算的之後,我們就可以從神經網路的角度來看『卷積層』的運算過程了。下圖展示的是輸入三通圖像(8*8*3)經一層卷積結構,輸出兩通特徵圖(8*8*2)的計算過程:
卷積參數:input_shape=(8,8,3),kernelsize=(3,3),padding=『same』,stride=1,output_shape=(8,8,2)
在此圖中:
在此次卷積層的運算中:
首先我們來關注一下輸入和輸出,他倆的尺度都是(8*8),而輸入是3通道,輸出是2通道(深度學習中不管幹啥一定要先看輸入輸出,對一層是這樣,對整個模型也是這樣)。
其次就准備進入我們最熟悉的卷積核計算了,可是在此之前我們得知道,這個運算過程中到底發生了幾次卷積核計算呢?有的朋友可能要說,卷積的一大特性就是『權值共享』,有幾通輸出就有幾個卷積核,每個卷積核把輸入特徵圖從頭掃到尾。然而這個其實是不對的!
實際上,在卷積核計算數量問題上,應該是「 有幾通道的輸出就有幾套卷積核,每套內的卷積核數量與輸入通道數相等 」,就像我在上圖中所畫的:
至此,這一個卷積層的運算就全部完成了。
2.3 「可訓練參數」驗證
畢竟空口無憑,下面我來通過「 可訓練參數 」的數量,來為大家驗證一下卷積層是不是按我說的這么運算的。大家應該知道,一個卷積層內的「可訓練參數」,其實就是指的卷積核里的那些值,以及要加的偏置量,那麼如果按照前面描述的計算方法來看,一個卷積層內的「可訓練參數有多少呢」?我們可知:
由此可得到:
那麼按理說可訓練參數量應為:
讓我們用keras的summary()來驗證一下:
很棒!
記住,普通卷積層的可訓練參數量為:
Ps: 還有一個衡量模型大小、復雜度的量叫做「理論計算量FLOPs」(floating point operations)。它通常只考慮Conv、FC等參數層的乘、加操作的數量,並且「純加」操作也會被忽略(例如bias)。卷積層運算中的FLOPs計算公式為:
Ps: 這里還要為大家明確一個「感受野」的概念,簡單來講就是卷積神經網路中的某一層特徵圖上的一個點,對應到原圖上可以關聯到多少個點,我們用一張圖來解釋一下:
上圖展示的是一個3層一維卷積,kernel_size=3,我們可以看到:頂層左一的像素與底層左起7個像素值有關,這時候就代表它的感受野有7。我們可以顯而易見的得出以下兩個結論:
這個感受野在後續的卷積的拆分講解中還要用到。
Ⅳ cnn卷積神經網路用什麼語言來寫pascial
200+
這個是hinton matlab代碼的C++改寫版. convnetjs - Star,SAE,首選的肯定是LIBSVM這個庫;RBM#47. DeepLearn Toolbox - Star,包括了CNN;C++SVM方面,Java。
2。
下面主要一些DeepLearning的GitHub項目吧;SdA#47:2200+
實現了卷積神經網路,還實現了Rasmussen的共軛梯度Conjugate Gradient演算法,DBN,C/CRBM/CDBN#47:Python。
3,CAE等主流模型,實現的模型有DBN#47,可以用來做分類,語言是Python;LR等,從演算法與實現上都比較全:800+
實現了深度學習網路. rbm-mnist - Star,應該是應用最廣的機器學習庫了,強化學習等. Deep Learning(yusugomo) - Star,Scala:1000+
Matlab實現中最熱的庫存,提供了5種語言的實現。
5;dA#47:500+
這是同名書的配套代碼。
4. Neural-Networks-And-Deep-Learning - Star!
1,回歸
Ⅳ 一文看懂四種基本的神經網路架構
原文鏈接:
http://blackblog.tech/2018/02/23/Eight-Neural-Network/
更多干貨就在我的個人博客 http://blackblog.tech 歡迎關注
剛剛入門神經網路,往往會對眾多的神經網路架構感到困惑,神經網路看起來復雜多樣,但是這么多架構無非也就是三類,前饋神經網路,循環網路,對稱連接網路,本文將介紹四種常見的神經網路,分別是CNN,RNN,DBN,GAN。通過這四種基本的神經網路架構,我們來對神經網路進行一定的了解。
神經網路是機器學習中的一種模型,是一種模仿動物神經網路行為特徵,進行分布式並行信息處理的演算法數學模型。這種網路依靠系統的復雜程度,通過調整內部大量節點之間相互連接的關系,從而達到處理信息的目的。
一般來說,神經網路的架構可以分為三類:
前饋神經網路:
這是實際應用中最常見的神經網路類型。第一層是輸入,最後一層是輸出。如果有多個隱藏層,我們稱之為「深度」神經網路。他們計算出一系列改變樣本相似性的變換。各層神經元的活動是前一層活動的非線性函數。
循環網路:
循環網路在他們的連接圖中定向了循環,這意味著你可以按照箭頭回到你開始的地方。他們可以有復雜的動態,使其很難訓練。他們更具有生物真實性。
循環網路的目的使用來處理序列數據。在傳統的神經網路模型中,是從輸入層到隱含層再到輸出層,層與層之間是全連接的,每層之間的節點是無連接的。但是這種普通的神經網路對於很多問題卻無能無力。例如,你要預測句子的下一個單詞是什麼,一般需要用到前面的單詞,因為一個句子中前後單詞並不是獨立的。
循環神經網路,即一個序列當前的輸出與前面的輸出也有關。具體的表現形式為網路會對前面的信息進行記憶並應用於當前輸出的計算中,即隱藏層之間的節點不再無連接而是有連接的,並且隱藏層的輸入不僅包括輸入層的輸出還包括上一時刻隱藏層的輸出。
對稱連接網路:
對稱連接網路有點像循環網路,但是單元之間的連接是對稱的(它們在兩個方向上權重相同)。比起循環網路,對稱連接網路更容易分析。這個網路中有更多的限制,因為它們遵守能量函數定律。沒有隱藏單元的對稱連接網路被稱為「Hopfield 網路」。有隱藏單元的對稱連接的網路被稱為玻爾茲曼機。
其實之前的帖子講過一些關於感知機的內容,這里再復述一下。
首先還是這張圖
這是一個M-P神經元
一個神經元有n個輸入,每一個輸入對應一個權值w,神經元內會對輸入與權重做乘法後求和,求和的結果與偏置做差,最終將結果放入激活函數中,由激活函數給出最後的輸出,輸出往往是二進制的,0 狀態代表抑制,1 狀態代表激活。
可以把感知機看作是 n 維實例空間中的超平面決策面,對於超平面一側的樣本,感知器輸出 1,對於另一側的實例輸出 0,這個決策超平面方程是 w⋅x=0。 那些可以被某一個超平面分割的正反樣例集合稱為線性可分(linearly separable)樣例集合,它們就可以使用圖中的感知機表示。
與、或、非問題都是線性可分的問題,使用一個有兩輸入的感知機能容易地表示,而異或並不是一個線性可分的問題,所以使用單層感知機是不行的,這時候就要使用多層感知機來解決疑惑問題了。
如果我們要訓練一個感知機,應該怎麼辦呢?
我們會從隨機的權值開始,反復地應用這個感知機到每個訓練樣例,只要它誤分類樣例就修改感知機的權值。重復這個過程,直到感知機正確分類所有的樣例。每一步根據感知機訓練法則來修改權值,也就是修改與輸入 xi 對應的權 wi,法則如下:
這里 t 是當前訓練樣例的目標輸出,o 是感知機的輸出,η 是一個正的常數稱為學習速率。學習速率的作用是緩和每一步調整權的程度,它通常被設為一個小的數值(例如 0.1),而且有時會使其隨著權調整次數的增加而衰減。
多層感知機,或者說是多層神經網路無非就是在輸入層與輸出層之間加了多個隱藏層而已,後續的CNN,DBN等神經網路只不過是將重新設計了每一層的類型。感知機可以說是神經網路的基礎,後續更為復雜的神經網路都離不開最簡單的感知機的模型,
談到機器學習,我們往往還會跟上一個詞語,叫做模式識別,但是真實環境中的模式識別往往會出現各種問題。比如:
圖像分割:真實場景中總是摻雜著其它物體。很難判斷哪些部分屬於同一個對象。對象的某些部分可以隱藏在其他對象的後面。
物體光照:像素的強度被光照強烈影響。
圖像變形:物體可以以各種非仿射方式變形。例如,手寫也可以有一個大的圓圈或只是一個尖頭。
情景支持:物體所屬類別通常由它們的使用方式來定義。例如,椅子是為了讓人們坐在上面而設計的,因此它們具有各種各樣的物理形狀。
卷積神經網路與普通神經網路的區別在於,卷積神經網路包含了一個由卷積層和子采樣層構成的特徵抽取器。在卷積神經網路的卷積層中,一個神經元只與部分鄰層神經元連接。在CNN的一個卷積層中,通常包含若干個特徵平面(featureMap),每個特徵平面由一些矩形排列的的神經元組成,同一特徵平面的神經元共享權值,這里共享的權值就是卷積核。卷積核一般以隨機小數矩陣的形式初始化,在網路的訓練過程中卷積核將學習得到合理的權值。共享權值(卷積核)帶來的直接好處是減少網路各層之間的連接,同時又降低了過擬合的風險。子采樣也叫做池化(pooling),通常有均值子采樣(mean pooling)和最大值子采樣(max pooling)兩種形式。子采樣可以看作一種特殊的卷積過程。卷積和子采樣大大簡化了模型復雜度,減少了模型的參數。
卷積神經網路由三部分構成。第一部分是輸入層。第二部分由n個卷積層和池化層的組合組成。第三部分由一個全連結的多層感知機分類器構成。
這里舉AlexNet為例:
·輸入:224×224大小的圖片,3通道
·第一層卷積:11×11大小的卷積核96個,每個GPU上48個。
·第一層max-pooling:2×2的核。
·第二層卷積:5×5卷積核256個,每個GPU上128個。
·第二層max-pooling:2×2的核。
·第三層卷積:與上一層是全連接,3*3的卷積核384個。分到兩個GPU上個192個。
·第四層卷積:3×3的卷積核384個,兩個GPU各192個。該層與上一層連接沒有經過pooling層。
·第五層卷積:3×3的卷積核256個,兩個GPU上個128個。
·第五層max-pooling:2×2的核。
·第一層全連接:4096維,將第五層max-pooling的輸出連接成為一個一維向量,作為該層的輸入。
·第二層全連接:4096維
·Softmax層:輸出為1000,輸出的每一維都是圖片屬於該類別的概率。
卷積神經網路在模式識別領域有著重要應用,當然這里只是對卷積神經網路做了最簡單的講解,卷積神經網路中仍然有很多知識,比如局部感受野,權值共享,多卷積核等內容,後續有機會再進行講解。
傳統的神經網路對於很多問題難以處理,比如你要預測句子的下一個單詞是什麼,一般需要用到前面的單詞,因為一個句子中前後單詞並不是獨立的。RNN之所以稱為循環神經網路,即一個序列當前的輸出與前面的輸出也有關。具體的表現形式為網路會對前面的信息進行記憶並應用於當前輸出的計算中,即隱藏層之間的節點不再無連接而是有連接的,並且隱藏層的輸入不僅包括輸入層的輸出還包括上一時刻隱藏層的輸出。理論上,RNN能夠對任何長度的序列數據進行處理。
這是一個簡單的RNN的結構,可以看到隱藏層自己是可以跟自己進行連接的。
那麼RNN為什麼隱藏層能夠看到上一刻的隱藏層的輸出呢,其實我們把這個網路展開來開就很清晰了。
從上面的公式我們可以看出,循環層和全連接層的區別就是循環層多了一個權重矩陣 W。
如果反復把式2帶入到式1,我們將得到:
在講DBN之前,我們需要對DBN的基本組成單位有一定的了解,那就是RBM,受限玻爾茲曼機。
首先什麼是玻爾茲曼機?
[圖片上傳失敗...(image-d36b31-1519636788074)]
如圖所示為一個玻爾茲曼機,其藍色節點為隱層,白色節點為輸入層。
玻爾茲曼機和遞歸神經網路相比,區別體現在以下幾點:
1、遞歸神經網路本質是學習一個函數,因此有輸入和輸出層的概念,而玻爾茲曼機的用處在於學習一組數據的「內在表示」,因此其沒有輸出層的概念。
2、遞歸神經網路各節點鏈接為有向環,而玻爾茲曼機各節點連接成無向完全圖。
而受限玻爾茲曼機是什麼呢?
最簡單的來說就是加入了限制,這個限制就是將完全圖變成了二分圖。即由一個顯層和一個隱層構成,顯層與隱層的神經元之間為雙向全連接。
h表示隱藏層,v表示顯層
在RBM中,任意兩個相連的神經元之間有一個權值w表示其連接強度,每個神經元自身有一個偏置系數b(對顯層神經元)和c(對隱層神經元)來表示其自身權重。
具體的公式推導在這里就不展示了
DBN是一個概率生成模型,與傳統的判別模型的神經網路相對,生成模型是建立一個觀察數據和標簽之間的聯合分布,對P(Observation|Label)和 P(Label|Observation)都做了評估,而判別模型僅僅而已評估了後者,也就是P(Label|Observation)。
DBN由多個限制玻爾茲曼機(Restricted Boltzmann Machines)層組成,一個典型的神經網路類型如圖所示。這些網路被「限制」為一個可視層和一個隱層,層間存在連接,但層內的單元間不存在連接。隱層單元被訓練去捕捉在可視層表現出來的高階數據的相關性。
生成對抗網路其實在之前的帖子中做過講解,這里在說明一下。
生成對抗網路的目標在於生成,我們傳統的網路結構往往都是判別模型,即判斷一個樣本的真實性。而生成模型能夠根據所提供的樣本生成類似的新樣本,注意這些樣本是由計算機學習而來的。
GAN一般由兩個網路組成,生成模型網路,判別模型網路。
生成模型 G 捕捉樣本數據的分布,用服從某一分布(均勻分布,高斯分布等)的雜訊 z 生成一個類似真實訓練數據的樣本,追求效果是越像真實樣本越好;判別模型 D 是一個二分類器,估計一個樣本來自於訓練數據(而非生成數據)的概率,如果樣本來自於真實的訓練數據,D 輸出大概率,否則,D 輸出小概率。
舉個例子:生成網路 G 好比假幣製造團伙,專門製造假幣,判別網路 D 好比警察,專門檢測使用的貨幣是真幣還是假幣,G 的目標是想方設法生成和真幣一樣的貨幣,使得 D 判別不出來,D 的目標是想方設法檢測出來 G 生成的假幣。
傳統的判別網路:
生成對抗網路:
下面展示一個cDCGAN的例子(前面帖子中寫過的)
生成網路
判別網路
最終結果,使用MNIST作為初始樣本,通過學習後生成的數字,可以看到學習的效果還是不錯的。
本文非常簡單的介紹了四種神經網路的架構,CNN,RNN,DBN,GAN。當然也僅僅是簡單的介紹,並沒有深層次講解其內涵。這四種神經網路的架構十分常見,應用也十分廣泛。當然關於神經網路的知識,不可能幾篇帖子就講解完,這里知識講解一些基礎知識,幫助大家快速入(zhuang)門(bi)。後面的帖子將對深度自動編碼器,Hopfield 網路長短期記憶網路(LSTM)進行講解。