神經網絡詳解及技巧


前言

筆者一直在ipad上做手寫筆記,最近突然想把筆記搬到博客上來,也就有了下面這些。因為本是給自己看的筆記,所以內容很簡陋,只是提了一些要點。隨緣更新。

正文

step1 建立一個神經網絡模型

一個常見的神經網絡——完全連接前饋神經網絡
  • 全連接:layer和layer之間兩兩連接
  • 前饋傳遞方向由后向前,任意兩層之間沒有反饋
  • 深度:許多隱含層

\(\sigma(\begin{bmatrix}1&-2 \\-1&1\end{bmatrix}\begin{bmatrix}1 \\-1\end{bmatrix}+\begin{bmatrix}1 \\0\end{bmatrix})=\begin{bmatrix}0.98 \\0.12\end{bmatrix}\) 如此一層一層傳遞下去。

最普通的激活函數\(\sigma(z)\)為sigmoid函數

\[f(z)=\frac{1}{1+e^{-z}} \]

其圖像為:

當然,現在已經很少使用sigmoid函數做激活函數了。

本質

通過隱含層來代替原來的特征工程,這樣最后一個隱含層輸出的就是一組新的特征,然后通過一個多分類器(可以是\(solfmax\)函數)得到最后的輸出\(y\)

舉例:手寫識別

step2 模型評估

對於神經網絡,我們采用交叉熵來對 \(y\)\(\hat y\) 的損失進行計算。(后期我將在生成模型和判別模型中對他進行詳細的描述)

step3 最佳模型——梯度下降

\(backpropation\)(反向傳播,也就是所謂的\(BP\))在神經網絡中是一種有效的方式計算\(\frac{\partial{L}}{\partial w}\)的方式,我們可以利用很多框架進行計算,如:TensorFlow,Pytorch。

反向傳播(\(BP\))

\(L(\theta)\)是總體損失函數,\(l^n(\theta)\)是單個樣本產生的誤差。

計算\(L(\theta)= \sum_{n=0}^{N}l^n(\theta)\),只需要計算\(\frac{\partial L(\theta)}{\partial w}=\sum_{n=1}^{N}\frac{\partial L(\theta)}{\partial w}\)

我們取出一個神經元進行分析

易得:

\[\frac{\partial l}{\partial w}=\frac{\partial z}{\partial w}\frac{\partial l}{\partial z} \]

Forward Pass \(\frac{\partial z}{\partial w}\):

這里我可以很輕松看出\(\frac{\partial z}{\partial w}\)為上一隱含層輸出的值。

Backward Pass \(\frac{\partial l}{\partial z}\):

\[\frac{\partial l}{\partial z}=\frac{\partial a}{\partial z}\frac{\partial l}{\partial a} = \frac{\partial a}{\partial z}[\frac{\partial z'}{\partial a}\frac{\partial l}{\partial z}+\frac{\partial z''}{\partial a}\frac{\partial l}{\partial z''}] = \sigma'(z)[w_3 \frac{\partial l}{\partial z'} + w_4 \frac{\partial l}{\partial z''}] \]

這時候我們會覺得每計算一次梯度相當麻煩,每個參數的梯度都需要層層往后計算,計算量大到無法想象。實際上進行Backward Pass和向前傳播的計算量差不多,我們只需將我們的思維逆轉一下,從最后一層往前計算,也能計算出所有參數的梯度,這時的計算量是線性的,這就是 \(BP\) 的思想(個人為很類似於算法中的動態規划)。

利用keras建立神經網絡

建立神經網絡的過程,別人以為你在搞什么特別深奧高大上的東西,其實你只是在搭積木一樣一層一層疊隱含層而已=。=

import keras
from keras.models import Sequential
from keras.layers import Dense

model = Sequential()    # 建立一個模型
# 搭建網絡
'''@param
    Dense: Fully connect layer
    input_dim: 輸入層
    units: 神經元
    activation: 激活函數
'''
model.add(Dense(input_dim=10, units=500, activation='sigmoid'))   # 建立一個神經網絡
# 再加一個隱含層
model.add(Dense(units=500, activation='sigmoid'))
# 輸出層
model.add(Dense(units=10, activation='softmax'))    # 輸出向量長度為10,激活函數為softmax
# loss function
model.compile(loss='categotial_crossentropy',  # 損失函數:交叉熵
              optimizer='adam',  # 優化器(都是梯度下降)
              metrics=['accuracy']  # 指標
              )
# batch_size: 將訓練集隨機分為分為幾個batch,每次計算隨機的一個
# 所有batch都計算一次,一個epoch結束
model.fit(x_train, y_train, batch_size=100, epochs=20)

# case1: 測試集正確率
score = model.evaluate(x_test, y_test)
print('Total loss on Test Set:', score[0])
print('Accuracy of Testing Set:', score[1])
# case 2:模型預測
result = model.predict(x_test)

深度學習的技巧

在test上如何改進:

新的激活函數

sigmoid缺點——梯度消失:

有時神經網絡層數越深,結果越差,原因可能是梯度消失,比較靠近input的幾層梯度很小,靠近output的幾層梯度較大,在前幾層還未怎么更新參數時,后幾層已經收斂。因為每經過一個sigmoid,\(\Delta w\)的影響就會被消弱。

ReLU:
  • 當 input > 0 時,output = input
  • 當 input < 0 時,output = 0

在input < 0 時,相當於該節點被移除,整個網絡就是 a thinner linear network,如果時線性的話,梯度不會遞減。
你可能會說這個線性模型如何處理那些復雜的非線性模型,畢竟不是所有問題都和線性一樣美好,你要注意了我們這是deep learning,關鍵在於這個“deep”,這是一個有着數層幾千個神經元的網絡,它們疊加的效果就是一個非線性的模型,是一個很復雜的function。對於ReLU activation function的神經網絡,只是在小范圍內是線性的,在總體上還是非線性的。

好處:

  1. 比sigmoid處理起來快
  2. 無窮多的sigmoid疊加起來的結果(不同的bias)
  3. 可以處理梯度消失

變種:

  • Leaky ReLU

  • Parametric ReLU

Maxout —— 讓network自動學習的激活函數

方法:

  1. 先將輸入分組,如2個一組或3個一組
  2. 再從每一組中選擇最大的一個

下圖為一個簡單的示例

原理:
其實上面介紹的ReLU為一個特殊的Maxout,理論上Maxout可以擬合任何激活函數
比如下面這個ReLU可以由如此的Maxout得到

選擇不同的\(w\)\(b\)可以做到

你可能會問這樣不就有的節點訓練不到了嗎?因為有些節點的權值為0等於從網絡中去除了。其實只是部分數據上此節點為0,但是我們有大量數據,總有數據可以訓練到這個節點。所以 \(Maxout\) 需要比 \(ReLU\) 更大的數據量才能訓練好這個網絡。

更新學習速率

RMSProp

屬於之前在線性模型中提到的\(Adagrad\)算法的變形

\[w^1 \longleftarrow w^0 - \frac{\eta}{\sigma^0}g^0 \qquad \sigma^0=g^0 \]

\[w^2 \longleftarrow w^1 - \frac{\eta}{\sigma^1}g^1 \qquad \sigma^1=\sqrt{\alpha(\sigma^0)^2+(1-\alpha)(g^1)^2} \]

\[w^3 \longleftarrow w^2 - \frac{\eta}{\sigma^2}g^2 \qquad \sigma^2=\sqrt{\alpha(\sigma^1)^2+(1-\alpha)(g^2)^2} \]

\[...... \]

\[w^{t+1} \longleftarrow w^t - \frac{\eta}{\sigma^t}g^t \qquad \sigma^t=\sqrt{\alpha(\sigma^{t-1})^2+(1-\alpha)(g^t)^2} \]

一個固定的learning rate除以一個\(\sigma\)(在第一個時間點,\(\sigma\)就是第一個算出來GD的值),在第二個時間點,你算出來一個\(g^1\)\(\sigma^2\)(你可以去手動調一個\(\alpha\)值,把\(\alpha\)值調整的小一點,說明你傾向於相信新的gradient告訴你的這個error surface的平滑或者陡峭的程度)。

Momentum

參考了物理世界慣性的概念,遇到一個小山坡時可以通過慣性翻過,從而越過局部最優點。

在算法中只參考上一次的速度,因為上一次的速度已經包含了之前所有的速度。
步驟:

  1. 選擇一個初始位置 \(\theta^0\) 和初始速度 \(v^0=0\)
  2. 計算在 \(\theta^0\) 處的梯度\(\Delta L(\theta^0)\)
  3. \(v^1=\lambda v^0-\eta\Delta L(\theta^0)\)
  4. \(\theta^0= \theta^1+v^1\)
  5. 如此往復
Adam

\(RMSProp\)\(Momentum\)的結合。有興趣的朋友直接看圖吧。

在這里插入圖片描述

在train上改進(過擬合)

Early Stopping

我們需要的是測試集錯誤最小,而不是訓練集,如果測試集的Loss上升,則需要立刻停止訓練,但是如果將測試集加入訓練則會導致測試結果不客觀。這時我們可以引入驗證集來解決。當訓練時驗證集Loss上升則停止訓練。

Regularization

和線性模型一樣,我們需要在原來的\(loss function\)加入正則化,讓得到的結果更加平滑。
常見的有\(L_1-norm\)(一次式)和\(L_2-norm\)(二次式)

Dropout
如何訓練

在訓練時的時候,每一次參數更新之前,對network里面的每個神經元(包括輸入層),做采樣(sampling)。 每個神經元會有p%的可能性會被丟掉,跟着的 \(w\) 也會被丟掉。

解釋

你在訓練時,加上dropout,你會看到在訓練集上結果會變得有點差(因為某些神經元不見了),但是dropout真正做的事就是讓你測試集越做越好。

假設有\(m\)個神經元,就可以訓練 \(2^m\) 個神經網絡結構,每個網絡的偏差雖然很大,但是最后平均下來還是很准的(這個又要回到我們在線性模型中說的 \(varience\)\(bias\) 問題)。

dropout其實是用了模型融合(model essemble)的思想,訓練了很多模型最后加權得到最終的結果。

在testing上注意兩件事情:
  • 第一件事情就是在testing上不做dropout。
  • 在dropout的時候,假設dropout rate在training是p%,all weights都要乘以\((1-p\%)\)

關於為什么要乘\((1-p\%)\),舉一個簡單的例子:

總結

到此神經網絡已經介紹得差不多了,你可能會說,就這,就這?其實神經網絡也不是什么深奧的東西,本質上就是一個有着數千個參數的模型,和最簡單的線性模型一樣,也是通過最常規的方法——梯度下降求解。當然,其中也涉及了一些挺玄學(只可意會,不可言傳,當然也是我的數學功底不夠,無法准確描述)的方法。

上一篇:機器學習筆記(1)——線性回歸
下一篇:可能會講一講CNN或者神經網絡中神經元的來源——logistic回歸(未開始寫=。=)

(如果覺得有用請點個贊吧)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM