# 參考資料
[udacity介紹](https://haosquare.com/udacity-deep-learning-pytorch/)
[udacity pytorch](https://learn.udacity.com/my-programs?tab=Currently%2520Learning)
[coursera](https://www.coursera.org/specializations/deep-learning)
---
# 活化函數
## sigmoid
$$
sigmoid(x) = \frac{1}{1 + e^{-x}}
$$
```python=
def sigmoid(x):
return 1 / (1 + np.exp(-1*x))
```
## relu
$$
relu(x) =
\begin{cases}
0, & n < 0 \\
x, & n >= 0
\end{cases}
$$
```python=
def relu(x):
return np.maximum(0,x)
```
## hyperbolic tangent
$$
tanh(x) = \frac{e ^ x - e ^ {-x}}{e ^ x + e ^ {-x}}
$$
```python=
np.tanh(x)
```
---
# 多類別分類函數
## softmax
```python=
def softmax(x):
c = np.max(x)
exp = np.exp(x - c)
return exp / np.sum(exp)
```
---
# 損失函數
[參考資料](https://medium.com/jarvis-toward-intelligence/%E6%AF%94%E8%BC%83-cross-entropy-%E8%88%87-mean-squared-error-8bebc0255f5)
## 交叉熵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
```python=
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),這能算出微分的近似值
---
# 反向傳播
[別人寫得比較好](https://hackmd.io/@kk6333/HJqZGce1s?utm_source=preview-mode&utm_medium=rec)
## 符號定義
- $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正規化
[參考](https://hackmd.io/@kk6333/BkIDyLikj)
[莫煩](https://www.youtube.com/watch?v=TmzzQoO8mr4)
### 目標:避免過度擬和
### 運作概念
$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會在白色的焦點上

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

### 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情況),這能讓模型快速找到損失函數的最小值。

# 小批次訓練
## 批次大小由1~訓練資料數量(m)
- batch = 1 -> loss快速改變但不穩定
- batch = m -> loss穩定下降但很慢
- batch = (1, m) -> 介於兩者之間
## 批次設定準則
- 訓練資料總數<2000 -> batch = m
- batch = $2^x,x=6,7,8,9$(建議設這個大小)
- 必須塞得進GPU ram
## 梯度爆炸/消失
### 梯度爆炸
#### 原因
[參考](https://zhuanlan.zhihu.com/p/112904260)
在一個非常深的神經網路中,每一層的梯度都比1大一些,在進行反向傳播時,總梯度會指數型增加,最後數字變超級大
#### 解決方法
1. 用Relu替代Sigmoid
2. 逐層貪婪預訓練
3. gradient clip讓梯度(偏導數)的值強制限制在某個範圍
4.
### 梯度消失
在一個非常深的神經網路中,每一層的梯度都比1小一些,在進行反向傳播時,總梯度會指數型減小,最後數字會趨近0
### 優化方法
設置合理的初始權重
#### 用ReLU
```python=
w_i = np.random.randn(in_features, out_features) * np.sqrt(2 / in_features)
#n_i_1代表第i - 1個權重有多少參數
```
#### 其他活化函數
```python=
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$大曲線越平滑
### 滑窗平均不好嗎
- 太浪費記憶體
### 偏差校正

- 若從0開始計算,起始值會非常小
- $\frac{原本算出的值}{1-\beta^t}$
## 帶動量的梯度下降
- 原本的gradient descent為了要避免震盪,lr不能太大
但這導致學得很慢
- 可以藉由先前的指數加權平均,讓學習的過程帶有"動量"
- $\beta$可以想成球的質量,越大越難以藉由後面的加速度改變方向

### 實作
$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
```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迴圈快了幾百倍。
```python=
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

## 讓所有資料能一次算
