# 機器學習
>BY y1w3n
>隨手記版本
定義問題->蒐集資料->資料分析->處理資料集->訓練模型->推論和預測
## 名詞
* ==監督式學習==
* 透過特徵跟目標變數(正確答案)來學習
* KNN
* 分類器
* 資料放進去之後找離它最近的K筆資料,結果就會推測在被選中的K筆資料中找多的(分類)或平均(回歸)
* 決策樹
* 一棵可用於決策分類的樹形分類器
* 回歸 : 預測有意義的數值
* for監督式學習
* 有意義的數值(例:鞋子尺寸23.5cm、24cm…)
* 沒意義的數值(例:性別男視為0、女視為1)
* 分類
* 
* ==非監督式學習==
* **不**提供目標變數(正確答案)
* 所以就要去分析輸入的資料再去解釋分析出的結果
* 方法:降維(例如PCA)、分群(例:k-means )
* 降維:以最少特徵去分析資料的方法
* k-means(平均分群演算法)
1. 設定分群數目
2. 隨機挑選質心
3. 算距離
4. 分群資料(看哪些資料被分到哪個質心)
5. 重找質心(換到每群中間)
6. 重複3~5 一直到質心不再移動
* 強化學習
*
## 深度學習入門
[一些colab連結](https://colab.research.google.com/drive/1qEwW2WdXAKM8mVjtRiD52dILROlqW-B3?authuser=2&hl=zh-tw#scrollTo=WBPaKCarDk0r)
### 感知器神經網路演算法
* 依照人類大腦特性提出
* 感知器
* 接收刺激(input)
* 神經元計算權重
* if超過閾值θ(界限)-> 輸出1 (0訊號不流/1流)
* if 傳遞訊號 = 神經元被激活
p.s.神經元只傳遞訊號
* 主要運用在二元分類
* 將訊息傳遞給其他神經元作為輸入
```python=
# 反及閘(NAND)感知器
import numpy as np
def AND(x1, x2):
x = np.array([x1, x2]) # 輸入
w = np.array([0.5, 0.5])#權重
theta = -0.7
tmp = np.sum(w*x) + theta
if tmp <= 0:
return 0
else:
return 1
```
* 互斥或閘(XOR)在沒有隱藏層運作時不能表示
* 透過組合輸入NAND、OR後再傳遞到AND輸出可達成
* 就叫**多層感知器**
* 也就是說多層感知器==能表示非線性空間==||(非線性空間包含一次以上多項式函數,圖形不是直線)||
:::spoiler 舉例
x1、x2 : 第0層
反及閘(NAND)、或閘(OR)輸出 : 第1層
及閘(AND)輸出 : 第2層
| x1 | x2 | NAND| OR | AND |XOR(對照)|
|----|----|-----|----|-----|--------|
| 0 | 0 | 1 | 0 | 0 | 0 |
| 0 | 1 | 1 | 1 | 0 | 1 |
| 1 | 0 | 1 | 1 | 0 | 1 |
| 1 | 1 | 0 | 1 | 1 | 0 |
:::
### 神經網路
* 輸入層->隱藏層(中間層)->輸出層
* **激活函數h(x)** : 將輸入信號轉成輸出信號,必須是非線性函數,這樣疊加層才能發揮優勢(輸出層的激活函數用σ()表示)
* 神經網路中是**sigmoid函數**作為激活函數
* **階躍函數** : 激活函數以閾值為界,一旦輸入超過閾值,就切換輸出
:::spoiler 過程

* a、y : 節點
:::
:::spoiler 階躍函數圖形、sigmoid函數圖形
```python=
#階躍函數圖形程式碼
import numpy as np
import matplotlib.pylab as plt
def step_function(x):
return np.array(x > 0)
x = np.arange(-5.0, 5.0, 0.1)
y = step_function(x)
plt.plot(x, y)
plt.ylim(-0.1, 1.1) # 指定y軸的範圍
plt.show()
```

---
```python=
# 階躍函數圖形+ sigmoid函數圖形程式碼
import numpy as np
import matplotlib.pylab as plt
import seaborn as sns
def step_function(x):
return np.array(x > 0, dtype=int)
def sigmoid(x):
return 1 / (1 + np.exp(-x))
X = np.arange(-5.0, 5.0, 0.1)
y_step_function = step_function(X)
y_sigmoid = sigmoid(X)
plt.figure(figsize=(8, 6))
sns.lineplot(x=X, y=y_step_function, label='step_function')
sns.lineplot(x=X, y=y_sigmoid, label='sigmoid')
plt.ylim(-0.1, 1.1)
plt.legend()
plt.show()
```

:::
* 階躍函數跟sigmoid函數比較
* 階躍函數只能return 0或1
* 感知器中神經源之間流動是二元信號(0或1)
* 神經網路是流動連續數值的信號
* 輸出信號都在0~1之間
* 都是非線性函數
* ReLU(Rectified Linear Unit)函數
* 神經網路最近主要使用的函數
* 輸出大於0就輸出該值
* 小於等於0就輸出0
:::spoiler ReLU函數圖形
```python=
#ReLU函數圖形
def relu(x):
return np.maximum(0, x) #maximum: 從輸入中選較大的值輸出
x = np.arange(-5.0, 5.0, 0.1)
print(relu(x))
y=relu(x)
plt.plot(x, y)
plt.ylim(-1, 5) # 指定y軸的範圍
plt.show()
```
```
#print(relu(x))
[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.1 0.2 0.3
0.4 0.5 0.6 0.7 0.8 0.9 1. 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 2. 2.1
2.2 2.3 2.4 2.5 2.6 2.7 2.8 2.9 3. 3.1 3.2 3.3 3.4 3.5 3.6 3.7 3.8 3.9
4. 4.1 4.2 4.3 4.4 4.5 4.6 4.7 4.8 4.9]
```

:::
* 運算
* 權重的符號


[colab](https://colab.research.google.com/drive/1qEwW2WdXAKM8mVjtRiD52dILROlqW-B3?authuser=2#scrollTo=5NWMewF4gJ1P&line=4&uniqifier=1)
```python=
X = np.array([1.0, 0.5])
W1 = np.array([[0.1, 0.3, 0.5], [0.2, 0.4, 0.6]])
B1 = np.array([0.1, 0.2, 0.3])
print(W1.shape) # (2, 3)
print(X.shape) # (2,)
print(B1.shape) # (3,)
A1 = np.dot(X, W1) + B1 # np.dot : 一次計算出結果
print(A1)
#經過sigmoid激活函數
Z1=sigmoid(A1)
print(Z1)
```
[三層神經網路程式](https://colab.research.google.com/drive/1qEwW2WdXAKM8mVjtRiD52dILROlqW-B3?authuser=2#scrollTo=WG-XG0WOjDOs&line=1&uniqifier=1)
* 輸出層
* 回歸問題用恆等函數(依原樣輸出)
* 分類問題用softmax函數(但容易有超級大ㄉ值所以用下面的方法修)
:::spoiler 這樣修的

分子和分母上都乘上C這個任意的常數(因為同時對分母和 分子乘以相同的常數,所以計算結果不變)。然後,把這個C 移到指數函數(exp)中,記為logC。最後,把logC替換為 另一個符號C'
C通常是取輸入訊號的最大值
:::
```python=
#softmax函數
def softmax(a):
c = np.max(a)
exp_a = np.exp(a-c) # exp(x)是表示ex的指數函數(e是納皮爾常數2.7182...)
sum_exp_a = np.sum(exp_a)
y = exp_a / sum_exp_a
return y
```
* softmax函數輸出介於0~1且輸出總和為1
* softmax函數也可以解釋為==機率==
:::spoiler 以手寫數字識別為例
* 分10類->輸出層神經元數量10個
* 分類->softmax函數->用機率推理結果

* 2的輸出值會最大
:::
* 數據處理
* 正規化 : 把資料限定到某個範圍內
* 預處理 : 對神經網路的輸入數據進行某種既定的轉換
* 數據白化 : 將資料整體的分佈形狀均勻化的方法
### 神經網路的學習
* 從**數據**中學習
* 提取特徵量(人類想的)
* 圖像的特徵量通常表示為向量的形式
* 但深度學習在訓練過程不會有人為介入
* 數據分為**訓練數據**和**測試數據**
* 泛化能力: 處理未被觀察過的數據的能力
* 過擬合: 無法處理新數據
* one-hot label: 將類別數據轉換為二進制向量表示
#### 損失函數
[參考連結](https://chih-sheng-huang821.medium.com/%E6%A9%9F%E5%99%A8-%E6%B7%B1%E5%BA%A6%E5%AD%B8%E7%BF%92-%E5%9F%BA%E7%A4%8E%E4%BB%8B%E7%B4%B9-%E6%90%8D%E5%A4%B1%E5%87%BD%E6%95%B8-loss-function-2dcac5ebb6cb)
* 損失就是「實際值和預測值的殘差」
* 可以用任意函數
* 越接近0越好
* 一般用MSE(均方誤差)跟交叉熵誤差
* ==表示性能不好的程度==
* 均方誤差(Mean-Square Error)
* 最常被用在回歸上的損失函數

* 求預測值與真實值之間距離的平方總和
```python=
def mean_squared_error(y, t):
return 0.5 * np.sum((y-t)**2)
```
[均方誤差舉例colab](https://colab.research.google.com/drive/1qEwW2WdXAKM8mVjtRiD52dILROlqW-B3?authuser=2#scrollTo=TGBChgU75EmJ&line=11&uniqifier=1)
* 交叉熵誤差

* 分類問題常用的損失函數
* 正解標籤越大,交叉熵誤差算出來越接近0
```python=
def cross_entropy_error(y, t):
delta = 1e-7 #防止負無限大發生
return -np.sum(t * np.log(y + delta))
```
[交叉熵誤差舉例colab](https://colab.research.google.com/drive/1qEwW2WdXAKM8mVjtRiD52dILROlqW-B3?authuser=2#scrollTo=F3G1dUL879Dk&line=12&uniqifier=1)
* mini-batch學習 : 從一堆數據中選一批據進行學習
#### 梯度
[偏導數跟梯度動畫圖](https://www.youtube.com/watch?v=Dhou27Ergkk)
* 目的: 尋找梯度為0的地方,找到的就是最小值或極小值(鞍點)
* 梯度: 損失函數衡量模型預測與真實值之間的誤差,而梯度是用來最小化損失函數的工具。也就是說要==找最優參數組合==(權重跟偏置)
* 導數: 某個函數在某一特定點的瞬間變化率
* 導數計算方式:對於一般的多項式 \( ax^n \),其導數是 \( anx^(n-1) \)。
:::spoiler 梯度
梯度可以用比較簡單的方式理解成「多變數函數的斜率」。假設你爬山的時候有一張地圖,上面標出了高度變化的等高線。梯度就像是告訴你在某個位置,哪個方向是上坡最陡、上升最快的方向。
想像一下你在一個山坡上,你想知道哪個方向爬得最快,梯度就告訴你哪個方向坡最陡,並且告訴你這個方向的陡峭程度。
在數學上,假設有一個二維的高度函數 f(x,y),梯度是由函數在x和 y 方向上的斜率(即偏導數)組成的向量∇𝑓(𝑥,𝑦),它由兩部分組成:x 方向上的變化率(偏導數)和在y方向上的變化率(偏導數)。公式是:

**∂f/∂x: 在y固定的情況下f的變化量除以x的變化量就叫做f對於x的偏導數** (這個符號∂念:partial)

使用偏導數在計算梯度的時候就像是在算每個點的斜率,我們要的就是用梯度找這個範圍的最小值
導數跟偏導數差別:
導數單變量函數、單一方向變化
偏導數看多個方向上的變化
:::
[梯度下降法colab](https://colab.research.google.com/drive/1qEwW2WdXAKM8mVjtRiD52dILROlqW-B3?authuser=2#scrollTo=4r8QQ_eZkuCE&line=24&uniqifier=1)
* 算出梯度後,在梯度下降法中,帶入公式:

- θ是參數向量。
- α 是學習率。
- \( ∇θ J(θ))是損失函數(J(θ))對參數θ的梯度。
* 學習率
* 一開始我們要人工設學習率,通常是0.01或0.01
* 概念: 就好比要找的一個「好位置」,如果學習率太大的話容易跳過那個「好位置」,太小的話雖然準確但會非常慢甚至陷在局部最小值
* 什麼情況要更新學習率?
* 調小
* 損失在某段時間內沒有減少時
* 損失上下波動很大也就是不穩定時
* 調大
* 當損失函數下降得太慢時
* 陷入局部最小值時
* 帶入上面的公式我會得到新的參數,然後再去計算損失函數跟梯度,就這樣重複
#### 神經網路學習完整步驟
* 目的: 找到合適的參數,也就是合適的偏置跟權重
---
1. mini-batch
* 從一堆數據中隨機選一批據進行學習,目標是縮小mini-batch損失函數的值
2. 計算梯度
* 為了減少mini-batch的損失函數的值,需要求出各個權重參數的梯度。
* 梯度顯示了在當前 mini-batch 中,損失函數減少最快的方向。
3. 更新參數
* 算出梯度後帶入公式更新參數(參數更新是對我當前mini-batch的數據)
4. 重複以上步驟
---
* 這裡是用mini-batch做梯度下降所以也叫隨機梯度下降法(Stochastic Gradient Descent, SGD)
* 簡單但費時

* 每經過一個epoch(當所有數據都被使用過時),就記錄當下數據跟精確度(損失函數)
---
### 誤差反向傳播法
* 被算在上面的第2步: 記算梯度
#### 計算圖
* 表示神經網路計算過程的結構化圖形

* 正向傳播: 左到右計算(輸入-> 輸出)
* 反向傳播: 從右到左,基於鍊式法則後向前計算導數
* 局部計算: 只需要計算跟自己有關的內容不用考慮全局
* 計算圖的反向傳播:沿著與正方向相反的方向,乘上局部導數
#### 鍊式法則
* 複合函數: 多個函數組成

* 複合函數導數=構成這個複合函數的函數們的**導數乘積**(在上面的圖中就是左邊兩個函數的導數相乘=右邊函數的導數)
### 相關技巧
#### 參數更新
* 隨機梯度下降法(SGD)在某些時候效率低,我們可以優化它的下降路徑

* Momentum
* 動量優化法
* 在0~1之間(通常設0.9)
* AdaGrad
* Adam
:::spoiler 圖




:::