大部分介紹神經網路的文章中概念性的東西太多,而且夾雜著很多數學公式,讀起來讓人頭疼,尤其沒什麼基礎的人完全get不到作者想要表達的思想。本篇文章嘗試零公式(但有少量數學知識)說清楚什麼是神經網路,並且舉例來說明神經網路能幹什麼。另外一些文章喜歡舉“根據歷史交易資料預測房子價值”或者“根據歷史資料來預測未來幾天是否下雨”的例子來引入“機器學習/深度學習/神經網路/監督學習”的主題,並介紹他們的作用,這種例子的樣本(輸入X輸出Y)都是數值,數字到數字的對映,簡單易懂,但是現實應用中還有很多場景並非如此,比如本文後面舉的“影像分類”例子,輸入是圖片並不是簡單的數值輸入。
卷積神經網路的輸出
我們平時討論的機器學習/深度學習/神經網路大部分時候說的是“監督學習”範疇,監督學習應用最為廣泛,也是神經網路發揮巨大作用的領域,因此,本文所有內容都是基於監督學習。從帶有標籤的樣本資料中學習“經驗”,最後將經驗作用於樣本之外的資料,得到預測結果,這就是監督學習。監督學習主要解決兩大類問題:
分類很好理解,就是根據輸入特徵,預測與之對應的分類,輸出是離散數值,比如明天是否下雨(下雨/不下雨)、簡訊是否是垃圾簡訊(是/否)、圖片中包含的動物是貓、狗還是猴子(貓/狗/猴子)等等。分類模型的輸出一般是N維向量(N為分類數),每個向量值代表屬於此分類的機率。
圖1 分類任務
如上圖,根據樣本資料(黃色圓形、藍色正方形、綠色稜形),監督學習可以確定兩條邊界線,對於任何樣本之外的資料(圖中灰色正方形),可以預測它所屬分類為B,對應的預測輸出可以是[0.04, 0.90, 0.06],代表屬於A類的機率為0.04,屬於B類的機率為0.90,屬於C類的機率為0.06,屬於B類的機率最大,因此我們可以認為它的分類為B。
請注意圖中用來劃分型別區域的兩條虛線,同類樣本並沒有完全按照虛線分割開來,有黃色的圓形被劃分到B類中,也有藍色的正方形被劃分到A類中。這種情況之所以出現,是因為監督學習得到的經驗應該具備一定程度的泛化能力,所以允許學習過程中出現一定的誤差,這樣的學習才是有效的。
與分類相反,迴歸主要解決一些輸出為具體數值的問題,比如明天的氣溫(20、21、30等)、明天股票開盤價(100、102、200等)。迴歸模型的輸出一般是具體數值(包含向量,向量中包含每個具體的數值)。
圖2 迴歸任務
如上圖,根據樣本資料(圖中藍色正方形,平面座標系點),監督學習可以確定一條直線y=1.5x+1,對於任何樣本之外的輸入(Xn),可以預測對應的輸出Y為1.5*Xn+1。
請注意透過監督學習得到的直線y=1.5*x+1,事實上並不是每個樣本都剛好落在該條直線上,大部分分佈在直線周圍。原因跟上面提到的一樣,監督學習過程允許出現一定的誤差,這樣才是有效的學習。
不管是分類還是迴歸問題,監督學習都是從樣本資料中學習經驗,然後將經驗應用到樣本之外的資料。那麼這個經驗具體指什麼?學習的本質是什麼呢?
以上面迴歸問題為例,我們得到直線y=1.5*x+1的過程如下:
(1)確定樣本資料呈直線分佈(近似直線分佈);
(2)設定一個目標函式:y=w*x+b;
(3)調整w和b的值,使樣本資料點儘可能近地分佈在直線周圍(可以使用最小二乘法);
(4)得到最優的w和b的值。
以上是4步完成學習的過程,這個也是最簡單的監督學習過程。至於其中“如何確定樣本呈直線分佈”、“如何判斷目標函式為y=w*x+b”以及“如何去調整w和b的值,可以使樣本資料點儘可能近的分佈在直線周圍”這些問題,後面一一介紹。
我們經常聽到的深度學習中模型訓練,其實指的就是學習的過程,最後輸出的模型中主要包含的就是w和b的值,換句話說,訓練的過程,主要是確定w和b的值,可以稱這些引數為“經驗”。
監督學習的過程就是找出X->Y的對映關係,這裡的輸入X可以稱之為“特徵”,特徵可以是多維的,實際上X大多數情況都是多維向量,類似[1, 1.002, 0.2, ...],輸出Y稱為“預測值”,預測值也可以是多維的,類似[0.90, 0.08, 0.02],比如前面提到的分類問題中,輸出Y為多維向量,每個向量值代表預測對應分類的機率大小。
全連線神經網路由許許多多的“神經元”連線而成,每個神經元可以接收多個輸入,產生一個輸出,類似前面提到的X->Y的對映,如果輸入是多維的,格式就是[x1, x2, ..., xn]->Y(對於單個神經元來講,輸出都是一個數值)。多個神經元相互連線起來,就形成了神經網路,神經元的輸入可以是來自其他多個神經元的輸出,該神經元的輸出又可以作為其他神經元的輸入(的一部分)。下圖為一個神經元的結構:
圖3 神經元結構
如上圖所示,一個神經元接收[x1, x2, ..., xn]作為輸入,對於每個輸入Xi,都會乘以一個權重Wi,將乘積結果相加再經過函式f作用後,產生輸出Y。多個神經元相互連線之後,得到神經網路:
圖4 全連線神經網路
如上圖,多個神經元相互連線起來組成全連線神經網路(圖中只包含w引數,省略了b),圖中神經網路一共包含3層(Layer1,Layer2和Layer3),上一層每個神經元的輸出全部作為後一層每個神經元的輸入,這種網路叫“全連線神經網路”(顧名思義,全連線的意思)。圖中黃色部分就是兩個完整的神經元結構,第一個神經元有三個輸入(x1,x2和x3),分別乘以對應的權重w31,w32和w33,第二個神經元有四個輸入(分別來自於Layer1層中的4個輸出)。該神經網路可以接受一個3維向量作為輸入(格式為[x1, x2, x3]),從左往右計算,最後輸出一個2維向量(格式為[y1, y2])。對應它學習到的經驗,可以用來處理符合如下對映關係的“分類”或者“迴歸”問題:
圖5 神經網路的對映關係
全連線神經網路是結構最簡單的神經網路,相鄰兩層之間的神經元每個之間都有連線,因為結構最簡單,因此通常以它作為入口來介紹其他結構更復雜的網路。注意,大部分神經網路並不是每個神經元都有連線關係,而且有些並不是嚴格遵從“資料從左往右移動”這種順序。
對於單個神經元的計算過程而言,是非常簡單的,分三步:
(1)計算每個輸入引數Xi和對應權重Wi的乘積;
(2)將乘積加起來,再加上一個偏移值b;
(3)最後將函式f作用在(2)中的結果上,得到神經元的輸出。
但是對於神經網路這種包含大量的神經元而言,如何可以更加方便、程式碼中更簡潔地去實現呢?答案是使用矩陣,線性代數搞忘記的同學也不要緊,這裡僅僅是利用了“矩陣相乘”和“矩陣相加”的規則。
矩陣相加
矩陣相加要求兩個矩陣維度相同,矩陣中對應的數字直接相加即可,生成一個新的矩陣,維度跟之前一樣:
圖6 矩陣加法
矩陣相乘
矩陣相乘要求第一個矩陣包含的列數和第二個矩陣包含的行數相同,M*N的矩陣乘以N*T的矩陣,得到M*T的一個新矩陣:
圖7 矩陣乘法
第一個矩陣A的第一行每個元素與第二個矩陣B的第一列各個元素相乘然後加起來,作為結果矩陣C中的第一行第一列,第一個矩陣A的第一行每個元素與第二個矩陣B的第二列各個元素相乘然後加起來,作為結果矩陣C中的第一行第二列,以此類推。上圖中3*3的矩陣乘以3*2的矩陣,得到一個3*2的新矩陣。如果將上圖7中A矩陣換成神經網路中的引數W(W11,W12,W22...),將B矩陣換成輸入X特徵(X1, X2, X3...),那麼全連線神經網路中每一層(可以包含多個神經元)的計算過程可以用矩陣表示成:
圖8 矩陣在神經網路中的用法
如上圖,使用矩陣我們可以批次操作。對於圖4中第一層(Layer1)所有神經元的計算過程,可以透過圖8一次性計算完成。圖中W矩陣先和X矩陣相乘,再加上偏移值B矩陣,得到一箇中間結果(也是一個矩陣),然後再將中間結果傳給函式f,輸出另外一個新矩陣Y,那麼這個Y就是神經網路第一層Layer1的輸出,它會作為下一層Layer2的輸入,後面以此類推。注意,函式f接受一個矩陣為引數,並作用於矩陣中每個元素,返回一個維度一樣的新矩陣,後面會提到。可以看到,之前需要計算4次f(w*x+b),現在只需要一次就可以了。
透過前面的介紹,可以得知,神經網路的訓練過程就是找到最合適的W矩陣(多個)和最合適的b矩陣(多個)使得神經網路的輸出與真實值(標籤)最接近,這個過程也叫做模型訓練或者調參(當然模型訓練遠不止這樣,還有其他諸如超引數的確定)。
即使輸入是高維向量,經過簡單的W*X+b這樣處理之後,輸出和輸入仍然呈線性關係。但是現實場景中大部分待解決的問題都不是線性模型,因此我們需要在輸入和輸出之間增加一個非線性變換,也就是前面多次提到的f函式(又稱為啟用函式)。由於各種原因(這裡涉及到神經網路具體的訓練過程,反向傳播計算權重值,暫不過多解釋),常見可用的啟用函式並不多,這裡舉兩個函式為例:
Sigmoid函式
Sigmoid函式能將任意實數對映到(0, 1)之間,具體函式影像如下:
圖9 Sigmoid函式影像
上圖中Sigmoid函式將任意輸入對映到(0, 1)之間的值,因此Sigmoid函式又經常被稱為邏輯函式,常用於二分類預測問題,假設有兩個分類A和B,對於任何輸入特徵X,Sigmoid返回值越趨近於1,那麼預測分類為A,反之則為B。
ReLu函式
ReLu函式很簡單,返回值為max(x, 0),具體函式影像為:
圖10 ReLu函式影像
上圖中ReLu函式將任意輸入的負數轉換為0,其他輸入原樣輸出。ReLu函式是目前深度學習中應用最多的啟用函式,具體原因這裡不做解釋。這裡需要說一下,深度學習/神經網路中有些東西並沒有非常充足的理論依據,完全靠前人經驗總結而來,比如這裡的ReLu函式看似簡單為什麼在大部分場合下效果最好,或者神經網路訓練中神經元到底如何組織準確性最高等等問題。
經過前面的介紹不難得出,神經網路可以解決複雜對映關係的“分類”問題。將特徵輸入到神經網路,經過一系列計算得到輸出。下圖舉一個形象的例子來說明神經網路如何解決分類問題:
圖11 預測白酒品牌
上圖顯示一個和全連線神經網路同樣結構的管道網狀結構,從上到下有多個閥門可以調節控制液體走向(圖中①),經過事先多次樣本液體訓練(使用不同品牌、不同酒精度、不同子型號的白酒),我們將閥門調節到最佳狀態。隨後將一杯白酒從最頂部倒入網狀結構,最後經過管道所有液體會分別流進三個玻璃杯中(圖中③)。如果我們將一杯五糧液倒入管道,理論情況所有的液體應該完全流進第一個玻璃杯中(圖中左側),但是實際上由於神經網路具備泛化能力,對於任何輸入(包括訓練樣本),大部分時候不會跟正確結果100%一致,最終只會保證第一個玻璃杯中的液體最多(比如佔85%),其餘兩個玻璃杯同樣存在少量液體(圖中右側)。
那麼現在有個問題,神經網路最後輸出的是數值(或多維向量,向量包含具體數值),結果是如何體現“分類”的概念呢?本文最開始講到過,分類問題最後都是透過機率來體現,某個分類的機率最高,那麼就屬於該分類,下圖顯示如何將數值轉換成機率:
圖12 數值到機率轉換
如上圖所示,對於2分類問題,我們通常使用前面提到的Sigmoid函式將其轉換成(0,1)之間的機率值,然後再根據機率值劃分類別。對於N分類(N也可以為2),我們要使用另外一個函式Softmax,該函式接受一個向量作為引數,返回一個新向量,維度跟輸入一致,新向量的每個值均分佈在在(0, 1)之前,並且所有機率之和為1。注意該函式作用在整個向量上,向量中的每個值之間相互有影響,感興趣的同學上網查一下公式。
影像分類又稱為影像識別,給定一張圖,要求輸出圖中包含的目標型別,比如我們常見的“微軟識花”、“識別貓還是狗”等等,這是計算機視覺中最典型的“分類”問題。影像分類是其他諸如“目標檢測”、“目標分割”的基礎。
數字影像本質上是一個多維矩陣,常見的RGB影像可以看作是3個二維矩陣,矩陣中每個值表示對應顏色通道上的值(0~255),還有其他比如灰度圖,可以看作是是1個二維矩陣,矩陣中每個值表示顏色的畫素值(0~255)。
圖13 彩色數字影像矩陣
如上圖所示,一張RGB全綵數字圖片大小為180*200,對應3個矩陣,大小都是180*200,矩陣中的數值範圍都在0~255。對於單通道灰度圖而言,對應1個矩陣,大小也是180*200:
圖14 灰度圖矩陣
前面已經講到如何使用全連線神經網路解決“分類”的問題,影像分類同樣屬於分類問題,因此也可以使用神經網路的方式解決,唯一的區別是前面提到的都是數值特徵輸入[x1, x2, x3, ...],那麼對於影像而言,該將什麼輸入給神經網路呢?答案是影像矩陣,影像矩陣中包含數值,將一個M*N的二維矩陣展開後,得到一個M*N維向量,將該向量輸入神經網路,經過神經網路計算,輸出各個分類機率。下面以“手寫數字影像識別”為例,介紹全連線神經網路如何做影像分類。手寫數字影像識別是深度學習中的一個HelloWorld級的任務,大部分教程均以此為例子講解影像識別,下圖為手寫數字圖片:
圖15 手寫數字影像
上圖顯示4張手寫數字圖片,分別為“5”、“0”、“4”、“1”,每張圖片大小為28*28,即長寬都為28畫素,圖片都是灰度影像,也就是說每張圖片對應1個28*28維矩陣,將該矩陣展開得到一個28*28維向量,直接輸入到全連線神經網路中。從0到9一共10個分類,因此神經網路的輸出是一個10維向量。
圖16 手寫數字圖片識別全過程
如上圖所示,原始輸入圖片大小為28*28,將其展開成[784*1]的特徵X傳入神經網路。神經網路一共包含兩層,第一層W矩陣大小為[1000*784],W*X之後得到大小為[1000*1]的輸出,該輸出作為第二層的輸入X,第二層W矩陣大小為[10*1000],W*X之後得到大小為[10*1]的輸出,該輸出(經過Softmax作用後)即為數字0~9的機率。
注意上面定義的神經網路結構中,只包含兩層(圖中藍色和綠色,黃色部分不算),第一層的W矩陣尺寸為[1000*784],這裡的1000是隨意設定的,可以是500甚至2000,它和神經元數量保持一致。第二層的W矩陣尺寸為[10*1000],這裡的1000跟前面一樣,這裡的10是分類數,因為一共10個分類,所以為10,如果100分類,這裡是100。神經網路的層數和每層包含的神經元個數都可以調整,這個過程就是我們常說的“修改網路結構”。
透過上面的方式做手寫數字圖片識別的準確性可能不高(我沒有試驗過),即使已經很高了它也不是一種非常好的方式,這種方式也許對於手寫數字圖片識別的任務很有效,但是對於其他圖片比如貓、狗識別仍然很有效嗎?答案是否定的,原因很簡單:直接將整張圖片的資料完全輸入到神經網路中,包含的特徵太複雜,或者噪音太多,這種現象可能在手寫數字這種簡單的圖片中有效,一旦換成複雜的圖片後可能就不行了。那麼針對一般影像分類的任務,在將資料傳到神經網路進行分類之前,我們還需要做什麼呢?
影像特徵在計算機視覺中是一個非常非常重要的概念,它在一定程度上可以當作圖片的特定標識,每張圖片都包含一些人眼看不到的特徵。關於影像特徵的介紹,大家可以參考我之前的一篇部落格:https://www.cnblogs.com/xiaozhi_5638/p/11512260.html
在使用神經網路對圖片進行分類之前,我們需要先提取影像特徵,然後再將提取到的特徵輸入到全連線神經網路中進行分類,因此解決影像分類問題的正確神經網路結構應該是這樣的:
圖17 包含特徵提取的神經網路
如上圖所示,在全連線神經網路之前增加了一個模組,該模組也是神經網路的一部分,同樣由許許多多的神經元組成,但是可能不再是全連線這種結構了,它可以自動提取圖片特徵,然後將特徵輸入到後面的全連線網路進行分類,我們通常把這裡的全連線網路稱為“分類器”(是不是似曾相識?)。這樣一來,全連線網路的輸入特徵大小不再是[784*1]了(圖中黃色部分),而應該根據前面的輸出來定。
圖17中這種由全連線神經網路(分類器)和特徵提取部分組合起來的神經網路有一個專有名詞,叫“卷積神經網路”,之所以叫“卷積”,因為在提取特徵的時候使用了卷積操作,具體後面介紹。
卷積神經網路中包含一個特徵提取的結構,該結構主要負責對原始輸入資料(比如影像,注意還可以是其他東西)進行特徵提取、抽象化、降維等操作,它主要包括以下幾個內容:
卷積層主要負責特徵提取,它使用一個卷積核(一個小型矩陣)以從左到右、從上到下的順序依次作用於原始輸入矩陣,然後生成一個(或多個)新矩陣,這些新矩陣我們稱之為feature maps。具體操作過程如下圖:
圖18 卷積操作過程
如上圖所示,圖中綠色部分為原始輸入矩陣,黃色矩陣為卷積核(一個3*3的矩陣),經過卷積操作後生成一個新的矩陣(粉色),該矩陣稱為feature map。卷積核可以有多個,每個卷積核不同,同一個輸入矩陣經過不同的卷積核處理之後會得到不同的feature map。因此在卷積層中,存在多個卷積核處理之後就會生成多個feature maps,這些feature map各不相同,每個都代表一定的特徵。
如果原始輸入矩陣是一張圖片,經過卷積核處理之後,生成的多個feature maps雖然仍然是矩陣的形式,但是不能再把它們當作圖片來對待。下圖顯示一張圖片經過兩個不同的卷積核處理之後生成的兩個feature maps,我們用工具將這兩個feature maps以圖片的形式顯示出來:
圖19 feature map的視覺化
如上圖所示,一張原始圖片經過一次卷積處理之後,生成的feature map以圖片的方式顯示出來之後似乎還是可以人眼識別出來。但是,如果經過多次卷積處理之後,那麼最終的feature map就無法人眼識別了。上圖還可以看出,不同的卷積核處理同一張輸入圖片後,生成的feature map之間有差別。
這裡再次強調,雖然經過卷積操作得到的feature maps仍然可以以圖片的形式顯示出來,但是它不在是我們通常理解中的“圖片”了。雖然人眼看不再有任何意義,但是對於計算機來講,意義非常重大。卷積層可以存在多個,一個卷積層後面可以緊跟另外一個卷積層,前一層的輸出是下一層的輸入。卷積層中的一些引數,比如卷積核矩陣中的具體數值,都需要透過訓練得到,這個道理跟前面提到的W和b引數一樣,也是需要透過訓練去擬合。
和前面講全連線神經網路一樣,經過卷積層處理之後生成的feature maps仍然需要進行非線性轉換,這裡的方式跟前面一樣,使用常見的啟用函式,比如ReLu函式作用在feature map上的效果如下圖:
圖20 對feature map做非線性變換
如上圖,feature map經過啟用函式處理之後,得到另外一個矩陣,我們稱之為 Rectified feature map。根據前面介紹ReLu的內容,我們可以得知,該啟用函式(max(0, x))將原feature map矩陣中的所有負數全部變成了0。
只有卷積操作和啟用處理還是不夠,因為到目前為止,(Rectified) feature maps包含的特徵資料還是太大,為了讓模型具備一定的泛化能力,我們需要對feature maps進行降維,這個過程稱之為池化:
圖21 最大池化操作
如上圖,池化層在原始feature maps上進行操作,還是按照“從左往右從上到下”的順序,選擇一個子矩陣(圖中圓圈部分2*2,類似前面的卷積核),選取該子矩陣範圍內最大的值作為新矩陣中的值,依次處理後最後組成一個全新矩陣,這個全新矩陣尺寸比原來的小。除了取最大值外,還有取平均值和求和的做法,但是經過前人實踐證明,取最大值(最大池化)效果最好。
經過池化層處理之後的feature maps仍然可以以圖片的方式顯示出來,還是和前面一樣,人眼已經分不清是啥了,但是對於計算機來講意義重大。
圖22 池化操作
如上圖所示,一張feature map經過兩種方式池化,取最大值和求和,分別得到不同的新矩陣,然後將新矩陣以圖片的方式顯示出來,可以看到差別還是非常大(雖然人眼已經分不清內容)。
通常情況下,卷積層後面不需要都緊跟一個池化層,可以經過多個卷積層之後再加一個池化層,也就是說,卷積和池化可以不按照1:1的比例進行組合。卷積神經網路中特徵提取部分就是使用卷積層、啟用處理、池化層等組合而成,可以根據需要修改相應網路層的數量(通常所說的“調整網路結構”)。最後一個池化層輸出的結果就是我們提取得到的影像特徵,比如最後一個池化層輸出T個矩陣(feature maps),每個大小為M*N,那麼將其展開後得到一個T*M*N維向量,那麼這個向量就是影像特徵。到這裡應該就很清楚了,我們如果將這個特徵向量傳到一個“分類器”中,透過分類器就可以得到最終的分類結果,分類器可以使用前面講到的全連線神經網路。
其實看到這裡的同學,如果前面的內容都看懂了,這塊就不難了。影像特徵已經得到了,直接將它輸入到全連線神經網路中去,就可以得到最終分類結果。下圖顯示將一個手寫數字圖片傳入卷積神經網路中的過程,先分別經過兩個卷積層和兩個池化層(交叉相連而成,圖中忽略了啟用處理等其他操作),然後將最後一個池化層的輸出先展開再作為全連線網路的輸入,經過兩個全連線層,最終得到一個10*1的輸出結果。
圖23 卷積神經網路的計算過程
關於卷積神經網路的配圖均來自:https://ujjwalkarn.me/2016/08/11/intuitive-explanation-convnets/
一些深度學習框架會幫我們去做模型訓練的具體工作,比如上面提到的w和b的確定,找出最合適的w和b儘量使預測值與真實值之間的誤差最小。下面舉個例子,使用tensorflow來最佳化 loss=4*(w-1)^2這個函式,找到最合適的w使loss最小:
圖24 loss=4*(w-1)^2函式的影像
如上圖所示,我們學過的數學知識告訴我們,w等於1時loss最小,這個過程可以透過求導得出(導數等於0的時候)。那麼使用tensorflow來幫我們確定w該是怎樣呢?下面是使用tensorflow來最佳化該函式,確定最有w的值:
1.4
1.0799999
1.016
1.0032
1.00064
我們可以看到,經過5次尋找,我們得到最優的w為1.00064,已經非常接近1了。這個過程其實就是深度學習框架訓練模型的簡單版本。
注意:
(1)本篇文章沒有涉及到具體模型訓練的原理,也就是求W和b矩陣的具體過程,因為該過程比較複雜而且涉及到很多數學公式,讀者只需要知道:模型訓練的本質就是使用大量帶有標籤的樣本資料找到相對比較合適的W和b矩陣,之後這些矩陣引數可以作用於樣本之外的資料。
(2)深度學習很多做法缺乏實際理論依據,大部分還是靠經驗,比如到底多少層合適,到底用什麼啟用函式效果更好,很多時候針對不同的資料集(或者問題)可能有不同的答案。
(3)除了名字相同外,深度學習中的神經網路跟人腦神經網路工作原理沒有關係,之前以為有關係,所以取了一個類似的名字,後來科學家發現好像沒什麼關係,因為人腦太複雜。