changed a year ago
Linked with GitHub

參考資料

udacity介紹
udacity pytorch
coursera


活化函數

sigmoid

\[ sigmoid(x) = \frac{1}{1 + e^{-x}} \]

def sigmoid(x): return 1 / (1 + np.exp(-1*x))

relu

\[ relu(x) = \begin{cases} 0, & n < 0 \\ x, & n >= 0 \end{cases} \]

def relu(x): return np.maximum(0,x)

hyperbolic tangent

\[ tanh(x) = \frac{e ^ x - e ^ {-x}}{e ^ x + e ^ {-x}} \]

np.tanh(x)

多類別分類函數

softmax

def softmax(x): c = np.max(x) exp = np.exp(x - c) return exp / np.sum(exp)

損失函數

參考資料

交叉熵Cross Entropy(CE)

模型預測機率

red_point blue_point
point1 0.6 0.4
point2 0.4 0.6
point3 0.9 0.1
point4 0.5 0.5

one hot 標籤

red_point blue_point
point1 0 1
point2 1 0
point3 0 1
point4 0 1

兩個nunpy_array相乘

red_point blue_point
point1 0 0.4
point2 0.4 0
point3 0 0.1
point4 0 0.5

把他們log之後

red_point blue_point
point1 0 -0.91629073
point2 -0.91629073 0
point3 0 -2.30258509
point4 0 -0.69314718

相加乘負號

error = 4.828313737302301

import numpy as np def one_hot(y): scale = (y.size, y.max() + 1) one_hot_label = np.zeros(scale) one_hot_label[np.arange(y.size), y] = 1 return one_hot_label def error_fuction(predict, label): label_probalbility = np.log(predict)*label print(label_probalbility) return np.sum(-label_probalbility) label = np.array([1,0,1,1]) one_hot_label = one_hot(label) #print(label) #print(one_hot_label) predict = np.array([[0.6,0.4], [0.4,0.6], [0.9,0.1], [0.5,0.5]]) error = error_fuction(predict, one_hot_label) print(error)

均方誤差Mean Squared Error(MSE)

\[ MSE = \frac{1}{n}\sum^n_{i=1}(x_i-y_i)^2 \]

\[ x_i是模型預測值 \]

\[ y_i是目標值 \]

關於損失函數選擇

機率預測選擇CE

由於CE使用了log函數,在預測錯得越離譜時,它的loss就越趨近無限大
至於MSE,即使預測結果跟正確答案完全反白,它的loss值最多就是1
這明顯會影響到訓練效率

數值預測選擇MSE

由於數值不像機率會在\([0,1]\),它可以是\((-\infty,\infty)\)
把負的東西丟給CE會壞掉,因此這個情況會使用MSE
此外,由於數值區間不再是\([0,1]\),在錯得很離譜的情況下,MSE也能給出很大的懲罰


gradient descent

對損失函數(E)進行偏微分,找到現在所在位置的斜率,將權重(W)減去學習率x該值,得到新的權重(W')

\[ w_j'=w_j - \alpha\frac{\partial}{\partial w_j}E \]

偏微分的實作

  1. \(解析微分\)
    針對不同的函式,由人工先計算出導數,再使用此導數對求出偏微分值。其優點是可以節省電腦計算資源,缺點是求導數有點麻煩。
  2. \(數值微分\)
    \(\lim_{\epsilon \to 0}\frac{f(x + \epsilon) - f(x - \epsilon)}{2 \epsilon}\)
    \(\epsilon\)帶入很小的數字(如:0.001),這能算出微分的近似值

反向傳播

別人寫得比較好

符號定義

  • \(Z^{[n]}\)是第n層linear的輸出
  • \(W^{[n]}\)是第n層linear的weight
  • \(b^{[n]}\)是第n層linear的bias
  • \(g\)是活化函數
  • \(L\)是損失函數
  • \(*\)是dot
  • \(\cdot\)是矩陣相乘
  • \(\alpha\)是learning rate

各層的偏導數

\[ dZ^{[output]} = L'(Z^{[output]}) \]

\[ dZ^{[n]} = W^{[n+1]T}dZ^{[n+1]} * g'(Z^{[n]}) \]

\[ dW^{[n]} = dZ^{[n]}\cdot g(Z^{[n-1]})^T \]

\[ db^{[n]} = dZ^{[n]} \]

梯度下降

\(W = W - \alpha dW\)
\(b = b - \alpha db\)

chain rule

假設有一個3層的神經網路,那麼,w1對損失函數的微分值(\(\frac{\partial E}{\partial w1}\))就會是:
\[ \frac{\partial E}{\partial w1} = \frac{\partial E}{\partial w3}*\frac{\partial w3}{\partial w2}*\frac{\partial w2}{\partial w1} \]
這樣子我就能用前面的反向傳播算好的微分數值(斜率),秒算現在需要算的微分數值,然後再把它拿去更新權重了


為甚麼需要非線性activation fuction

\[ w^{[2]}*(w^{[1]}*x + b^{[1]}) + b^{[2]} \\=w^{[2]}*w^{[1]}*x + w^{[2]}*b^{[1]} + b^{[2]} =w'x + b' \]

這樣下來,用w1,w2跟用一個w'根本沒差,這樣就做不出更複雜的模型


變異和偏差

去除偏差

  1. 換更多層的神經網路
  2. 增加訓練時間
  3. 換個網路架構

去除變異

  1. 增加訓練資料
  2. 做正規化
  3. 換個神經網路架構

防止過擬和

L正規化

參考
莫煩

目標:避免過度擬和

運作概念

\(loss = L(y_{predict}, y) - \lambda\sum_i |w_i|^x\)

  1. 在計算loss的時候要考量\(\lambda\sum_i |w_i|^x\),這能讓參數不要太大,有效避免過度擬和

  2. \(\lambda\)是可調整的參數,代表限制的程度

  3. x代表他會是Lx正規優化,\(- \sum_i |w_i|^2\)就是L2,\(- \sum_i |w_i|^1\)就是L1

  4. 如圖,最小的loss會在白色的焦點上
    image

  5. 如圖,用L1正規化可能有好幾個最小loss的點,這容易造成不穩定,因此L2較為常用,但在CNN裡面L1較為常用
    image

Andrew Ng 提供的另一種解釋

假設我們使用tanh函數當激勵函數,他數值在接近0的時候會趨近於線性,而L2正規化能讓\(w\)趨近於0,又\(z = wx + b\),所以最終會讓神經網路變簡單,減少過度擬和問題

Dropout正規化

運作方式

  1. 隨機決定是否在訓練中去除某個點的影響->刪除他們的輸出
  2. 最後把整層的輸出除掉"這層每個點的存活率",為了就是讓輸出期望值不變(翻轉dropout)

可行原因

  1. 不能過度依賴任何點的輸出,都有可能變成0

電腦視覺常用(因為超容易過度擬和)

資料增強(Data Augmentation)

  1. 將圖片進行反轉、增加干擾
  2. 雖然不如多一個真正全新的圖片,但成本很低

早期停止(early stop)

  1. 把train set和devalop set的loss曲線畫出
  2. 在devalop set的loss停止下降開始上升時,終止訓練
  3. Udacity教我邊訓練邊存devalop set的loss最小的模型

訓練效率優化

標準化

作法

\[ \frac{y - \mu}{\sigma} \]

原因

標準化能讓損失函數的圖形更趨近圓形(在2D情況),這能讓模型快速找到損失函數的最小值。
image

小批次訓練

批次大小由1~訓練資料數量(m)

  • batch = 1 -> loss快速改變但不穩定
  • batch = m -> loss穩定下降但很慢
  • batch = (1, m) -> 介於兩者之間

批次設定準則

  • 訓練資料總數<2000 -> batch = m
  • batch = \(2^x,x=6,7,8,9\)(建議設這個大小)
  • 必須塞得進GPU ram

梯度爆炸/消失

梯度爆炸

原因

參考
在一個非常深的神經網路中,每一層的梯度都比1大一些,在進行反向傳播時,總梯度會指數型增加,最後數字變超級大

解決方法

  1. 用Relu替代Sigmoid
  2. 逐層貪婪預訓練
  3. gradient clip讓梯度(偏導數)的值強制限制在某個範圍

梯度消失

在一個非常深的神經網路中,每一層的梯度都比1小一些,在進行反向傳播時,總梯度會指數型減小,最後數字會趨近0

優化方法

設置合理的初始權重

用ReLU

w_i = np.random.randn(in_features, out_features) * np.sqrt(2 / in_features) #n_i_1代表第i - 1個權重有多少參數

其他活化函數

w_i = np.random.randn(in_features, out_features) * np.sqrt(1 / in_features) #n_i_1代表第i - 1個權重有多少參數

優化器

指數加權平均

\(V_t=\beta V_{t-1}-(1-\beta)\theta_t\)

  • 相當於\(V_t\)=前\(\frac{1}{1-\beta}\)項的加權平均
  • \(\beta\)大曲線越平滑

滑窗平均不好嗎

  • 太浪費記憶體

偏差校正

image

  • 若從0開始計算,起始值會非常小
  • \(\frac{原本算出的值}{1-\beta^t}\)

帶動量的梯度下降

  • 原本的gradient descent為了要避免震盪,lr不能太大
    但這導致學得很慢
  • 可以藉由先前的指數加權平均,讓學習的過程帶有"動量"
  • \(\beta\)可以想成球的質量,越大越難以藉由後面的加速度改變方向
    image

實作

\(V_{dW}=\beta V_{dw}+(1-\beta)dW\)
\(V_{db}=\beta V_{db}+(1-\beta)db\)
\(W = W - \alpha V_{dW}\)
\(b = b - \alpha V_{db}\)

  • \(\alpha\)是lr

RMSprop

\(S_{dw}=\beta_2 S_{dw}+(1-\beta_2)dW^2\)
\(S_{db}=\beta_2 S_{db}+(1-\beta_2)db^2\)
\(W=W-\alpha \frac{dw}{\sqrt{S_{dw}}+\epsilon}\)
\(b=b-\alpha \frac{db}{\sqrt{S_{db}}+\epsilon}\)

  • \(\alpha\)是lr
  • \(\epsilon\)是一個很小的值,確保在運算過程中不會因為數值太小出現錯誤

Adam

動量和RMSprop的合體
\(V_{dW}=\beta_1 V_{dw}+(1-\beta_1)dW\)
\(V_{db}=\beta_1 V_{db}+(1-\beta_1)db\)
\(S_{dw}=\beta_2 S_{dw}+(1-\beta_2)dW^2\)
\(S_{db}=\beta_2 S_{db}+(1-\beta_2)db^2\)

\(W=W-\alpha \frac{V_{dW}}{\sqrt{S_{dw}}+\epsilon}\)
\(b=b-\alpha \frac{V_{db}}{\sqrt{S_{db}}+\epsilon}\)

推薦參數設置

  • lr:看情況
  • \(\beta_1\):0.9
  • \(\beta_2\):0.999
  • \(\epsilon\):\(10^{-8}\)

學習率衰減

一般

\(\alpha=\frac{1}{1+decayRate\cdot epochNumber}\alpha_0\)

指數

\(\alpha = 0.95^{epochnum}\cdot \alpha_0\)

方根衰減

\(\alpha = \frac{k}{\sqrt{epochnum}}\cdot \alpha_0\)
or
\(\alpha = \frac{k}{\sqrt{t}}\cdot \alpha_0\)

離散衰減

一個階段一個\(\alpha\)

優化:Broadcasting in Python

import numpy as np A = np.array([[56.0, 0.0, 4.4, 68.0], [1.2, 104.0, 52.0, 8.0], [1.8, 135.0, 99.0, 0.9]]) cal = A.sum(axis = 0)#縱行相加 print(cal) percentage = A / cal.reshape(1, 4) print(percentage*100)

numpy 技巧

優化:使用向量

python的numpy的內建函式會直接使用平行用算,因此,把要算的東西換成向量就能使用np.dot()做矩陣乘法,經實測速度比使用for迴圈快了幾百倍。

import numpy as np import time a = np.random.rand(1000000) b = np.random.rand(1000000) for_loop = 0 dot = 0 start = time.time() for i in range(1000000): for_loop += a[i]*b[i] end = time.time() print("ans:",for_loop) print("for loop:",(end - start)*1000,"ms") start = time.time() dot = np.dot(a,b) end = time.time() print("ans:",dot) print("for loop:",(end - start)*1000,"ms") """ ans: 249872.83356724805 for loop: 177.97136306762695 ms ans: 249872.83356725107 for loop: 3.999948501586914 ms """ print("==============================") c = np.random.rand(1000000) for_loop = np.zeros(c.shape) dot = 0 start = time.time() for i in range(1000000): for_loop[i] = np.exp(c[i]) end = time.time() print("ans:",for_loop) print("for loop:",(end - start)*1000,"ms") start = time.time() dot = np.exp(c) end = time.time() print("ans:",dot) print("for loop:",(end - start)*1000,"ms") """ ans: [1.73980356 2.20203383 1.49559244 ... 1.75642864 2.60338631 1.22761082] for loop: 501.096248626709 ms ans: [1.73980356 2.20203383 1.49559244 ... 1.75642864 2.60338631 1.22761082] for loop: 4.000425338745117 ms """ #嘗試寫迴圈之前都試試看能不能用內建函式 #np.log(vector) #np.abs(vector) #np.maximun(vector,0)#元素小於0就換成0 #vector**2 #and........

去除一個資料在微分時使用的for loop

讓所有資料能一次算

Select a repo