全連接神經網絡(DNN)是最朴素的神經網絡,它的網絡參數最多,計算量最大。
網絡結構
DNN的結構不固定,一般神經網絡包括輸入層、隱藏層和輸出層,一個DNN結構只有一個輸入層,一個輸出層,輸入層和輸出層之間的都是隱藏層。每一層神經網絡有若干神經元(下圖中藍色圓圈),層與層之間神經元相互連接,層內神經元互不連接,而且下一層神經元連接上一層所有的神經元。
隱藏層比較多(>2)的神經網絡叫做深度神經網絡(DNN的網絡層數不包括輸入層),深度神經網絡的表達力比淺層網絡更強,一個僅有一個隱含層的神經網絡就能擬合任何一個函數,但是它需要很多很多的神經元。
優點:由於DNN幾乎可以擬合任何函數,所以DNN的非線性擬合能力非常強。往往深而窄的網絡要更節約資源。
缺點:DNN不太容易訓練,需要大量的數據,很多技巧才能訓練好一個深層網絡。
感知器
DNN也可以叫做多層感知器(MLP),DNN的網絡結構太復雜,神經元數量太多,為了方便講解我們設計一個最簡單的DNN網絡結構--感知機,
DNN中的神經元由五部分組成:
- 輸入:一個感知器可以接收多個輸入$(x_1,x_2,...,x_n|x_i\in R)$
- 權重:每一個輸入都有一個權重$w_i \in R$
- 偏置項:$b \in R$,就是上圖中的$w_0$
- 激活函數:也叫做非線性單元,神經網絡的激活函數有很多,我有一篇博客專門介紹了激活函數。
- 輸出:$y=f(w*x+b)$
神經網絡的訓練
神經網絡的復雜之處在於他的組成結構太復雜,神經元太多,為了方便大家理解,我們設計一個最簡單的神經網絡
這是一個只有兩層的神經網絡,假定輸入$x$,我們規定隱層h和輸出層o這兩層都是$z=wx+b$和$f(z)=\frac{1}{1+e^{-z}}$的組合,一旦輸入樣本x和標簽y之后,模型就開始訓練了。那么我們的問題就變成了求隱層的w、b和輸出層的w、b四個參數的過程。
訓練的目的是神經網絡的輸出和真實數據的輸出"一樣",但是在"一樣"之前,模型輸出和真實數據都是存在一定的差異,我們把這個"差異"作這樣的一個參數$e$代表誤差的意思,那么模型輸出加上誤差之后就等於真實標簽了,作:$y=wx+b+e$
當我們有n對$x$和$y$那么就有n個誤差$e$,我們試着把n個誤差$e$都加起來表示一個誤差總量,為了不讓殘差正負抵消我們取平方或者取絕對值,本文取平方。這種誤差我們稱為“殘差”,也就是模型的輸出的結果和真實結果之間的差值。損失函數Loss還有一種稱呼叫做“代價函數Cost”,殘差表達式如下:
$$Loss=\sum_{i=1}^{n}e_i^2=\sum_{i=1}^{n}(y_i-(wx_i+b))^2$$
現在我們要做的就是找到一個比較好的w和b,使得整個Loss盡可能的小,越小說明我們訓練出來的模型越好。
反向傳播算法(BP)
BP算法主要有以下三個步驟 :
- 前向計算每個神經元的輸出值;
- 反向計算每個神經元的誤差項$e$值;
- 最后用隨機梯度下降算法迭代更新權重w和b。
我們把損失函數展開如下圖所示,他的圖形到底長什么樣子呢?到底該怎么求他的最小值呢?OK,為了方便讀者理解,我把Loss函數給你們畫出來。
$$Loss=\sum_{i=1}^{n}(x_i^2w^2+b^2+2x_iwb-2y_ib-2x_iy_iw+y_i^2)=Aw^2+Bb^2+Cwb+Db+Dw+Eb+F$$
我們初始化一個$w_o$和$b_0$,帶到Loss里面去,這個點($w_o,b_o,Loss_o$)會出現在碗壁的某個位置,而我們的目標位置是碗底,那就慢慢的一點一點的往底部挪吧。
$$x_{n+1}=x_n-\eta \frac{df(x)}{dx}$$
上式為梯度下降算法的公式,其中$\frac{df(x)}{dx}$為梯度,$\eta$是學習率,也就是每次挪動的步長,$\eta$大每次迭代的腳步就大,$\eta$小每次迭代的腳步就小,我們只有取到合適的$\eta$才能盡可能的接近最小值而不會因為步子太大越過了最小值。到后面每次移動的水平距離是在逐步減小的,原因就是因為整個函數圓乎乎的底部斜率在降低,不明白那就吃個栗子:
如圖所示,當$x_n=3$時,$-\eta\frac{df(x)}{dx}$為負數,更新后$x_{n+1}$會減小;當$x_n=-3$時,$-\eta\frac{df(x)}{dx}$為正數,更新后$x_{n+1}$還是會減小。這總函數其實就是凸函數。滿足$f(\frac{x_i+x_2}{2})=\frac{f(x_i)+f(x_2)}{2}$都是凸函數。沿着梯度的方向是下降最快的。
我們初始化$(w_0,b_0,Loss_o)$后下一步就水到渠成了,
$$w_1=w_o-\eta \frac{\partial Loss}{\partial w},b_1=b_o-\eta \frac{\partial Loss}{\partial b}$$
有了梯度和學習率$\eta$乘積之后,當這個點逐漸接近“碗底”的時候,偏導也隨之下降,移動步伐也會慢慢變小,收斂會更為平緩,不會輕易出現“步子太大”而越過最低的情況。一輪一輪迭代,但損失值的變化趨於平穩時,模型的差不多就訓練完成了。
梯度下降算法
我們用$$w_{new}=w_{old}-\eta\frac{\partial Loss}{\partial w}$$講以下梯度下降算法,零基礎的讀者可以仔細觀看,有基礎的請忽視梯度下降算法,我們定義y為真實值,$\hat{y}$為預測值
$$\frac{\partial Loss}{\partial w}=\frac{\partial}{\partial\mathrm{w}}\frac{1}{2}\sum_{i=1}^{n}(y-\hat{y})^2=\frac{1}{2}\sum_{i=1}^{n}\frac{\partial}{\partial\mathrm{w}}(y-\hat{y})^2$$
y是與$w$無關的參數,而$\hat{y}=wx+b$,下面我們用復合函數求導法
$$\frac{\partial Loss}{\partial\mathrm{w}}=\frac{\partial Loss}{\partial \hat{y}}
\frac{\partial \hat{y}}{\partial w}$$
分別計算上式等號右邊的兩個偏導數
$$\frac{\partial Loss}{\partial\hat{y}}=\frac{\partial}{\partial \hat{y}}(y^2-2y\hat{y}+\hat{y}^2)=-2y+2\hat{y}$$
$$\frac{\partial \hat{y}}{\partial\mathrm{w}}=\frac{\partial}{\partial\mathrm{w}}(wx+b)=x$$
代入$\frac{\partial Loss}{\partial w}$,求得
$$\frac{\partial Loss}{\partial\mathrm{w}}=\frac{1}{2}\sum_{i=1}^{n}\frac{\partial}{\partial\mathrm{w}}(y-\hat{y})^2=\frac{1}{2}\sum_{i=1}^{n}2(-y+\hat{y})\mathrm{x}=-\sum_{i=1}^{n}(y-\hat{y})\mathrm{x}$$
有了上面的式字,我們就能寫出訓練線性單元的代碼
$$\begin{bmatrix} w_0 \\ w_1 \\ w_2 \\ ... \\ w_m \\ \end{bmatrix}_{new}= \begin{bmatrix} w_0 \\ w_1 \\ w_2 \\ ... \\ w_m \\ \end{bmatrix}_{old}+\eta\sum_{i=1}^{n}(y-\hat{y}) \begin{bmatrix} x_0 \\ x_1\\ x_2\\ ... \\ x_m\\ \end{bmatrix}$$
哈哈哈,是不是發現我剛才講的明明是線性回歸模型的訓練,和大家想知道的神經網絡的訓練有毛線關系呀!你們說的沒錯,就是有一毛錢的關系,嘿嘿[笑臉]!
這個網絡用函數表達式寫的話如下所示:
第一層(隱藏層) $\begin{matrix}z_h=w_nx+b_n,&y_h=\frac{1}{1+e^{-z_h}}\end{matrix}$
第二層(輸出層) $\begin{matrix}z_o=w_oy_h+b_o,&y_o=\frac{1}{1+e^{-z_o}}\end{matrix}$
接下來的工作就是把$w_h、b_h、w_o、b_o$參數利用梯度下降算法求出來,把損失函數降低到最小,那么我們的模型就訓練出來呢。
第一步:准備樣本,每一個樣本$x_i$對應標簽$y_i$。
第二步:清洗數據,清洗數據的目的是為了幫助網絡更高效、更准確地做好分類。
第三步:開始訓練,
$$Loss=\sum_{i=1}^{n}(y_{oi}-y_i)^2$$
我們用這四個表達式,來更新參數。
$$(w_h)^n=(w_h)^{n-1}-\eta \frac{\partial Loss}{\partial w_h}$$
$$(b_h)^n=(b_h)^{n-1}-\eta \frac{\partial Loss}{\partial b_h}$$
$$(w_o)^n=(w_o)^{n-1}-\eta \frac{\partial Loss}{\partial w_o}$$
$$(b_o)^n=(b_o)^{n-1}-\eta \frac{\partial Loss}{\partial b_o}$$
問題來了,$\frac{\partial Loss}{\partial w_h}$、$\frac{\partial Loss}{\partial b_h}$、$\frac{\partial Loss}{\partial w_o}$、$\frac{\partial Loss}{\partial b_o}$這4個值怎么求呢?
$$Loss=\sum_{i=1}^{n}(y_{oi}-y_i)^2\Rightarrow Loss=\frac{1}{2}\sum_{i=1}^{n}(y_{oi}-y_i)^2$$
配一個$\frac{1}{2}$出來,為了后面方便化簡。
$$\frac{\partial Loss}{\partial w_h}=\frac{\partial \sum_{i=1}^{n}\frac{1}{2}(y_{oi}-y_i)^2}{\partial w_o}=\frac{\partial \sum_{i=1}^{n}y_{oi}}{w_o}=\sum_{i=1}^{n}\frac{\partial y_{oi}}{\partial z_o}·\frac{z_o}{w_o}=\sum_{i=1}^{n}\frac{\partial y_{oi}}{\partial z_o}·\frac{z_o}{y_h}·\frac{\partial y_h}{\partial z_h}·\frac{\partial z_h}{\partial w_h}$$
其他三個參數,和上面類似,這是一種“鏈乘型”求導方式。我們的網絡兩層就4個連乘,如果是10層,那么就是20個連乘。但一層網絡的其中一個節點連接着下一層的其他節點時,那么這個節點上的系數的偏導就會通過多個路徑傳播過去,從而形成“嵌套型關系”。
DropOut
DropOut是深度學習中常用的方法,主要是為了克服過擬合的現象。全連接網絡極高的VC維,使得它的記憶能力非常強,甚至把一下無關緊要的細枝末節都記住,一來使得網絡的參數過多過大,二來這樣訓練出來的模型容易過擬合。
DropOut:是指在在一輪訓練階段臨時關閉一部分網絡節點。讓這些關閉的節點相當去去掉。如下圖所示去掉虛線圓和虛線,原則上是去掉的神經元是隨機的。
python代碼實現MNIST手寫數字識別
MNIST的段代碼在TensorFlow官網的Github上面也是有的,地址:https://github.com/tensorflow/tensorflow,文件目錄在:tensorflow/tensorflow/examples/tutorials/mnist
然后我有一篇博客專門講解了如何用tensorlfow和keras框架搭建DNN CNN RNN神經網絡實現MNIST手寫數字識別模型,地址鏈接