---
# System prepended metadata

title: 機器學習
tags: [note]

---

# 機器學習
>BY y1w3n
>隨手記版本

定義問題->蒐集資料->資料分析->處理資料集->訓練模型->推論和預測
## 名詞
* ==監督式學習== 
  * 透過特徵跟目標變數(正確答案)來學習
  * KNN
      * 分類器
      * 資料放進去之後找離它最近的K筆資料，結果就會推測在被選中的K筆資料中找多的(分類)或平均(回歸)
  * 決策樹
      * 一棵可用於決策分類的樹形分類器
  * 回歸 ： 預測有意義的數值
      * for監督式學習
      * 有意義的數值(例：鞋子尺寸23.5cm、24cm…)
      * 沒意義的數值(例：性別男視為0、女視為1)
  * 分類
      * ![image](https://hackmd.io/_uploads/r1G_QeDXke.png)


* ==非監督式學習==
  * **不**提供目標變數(正確答案)
  * 所以就要去分析輸入的資料再去解釋分析出的結果
  * 方法：降維(例如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 過程
![image](https://hackmd.io/_uploads/ByFLmz__0.png)
* 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()
```
![image](https://hackmd.io/_uploads/S1imzyFuC.png)

---

```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()
```
![image](https://hackmd.io/_uploads/SksYdktu0.png)
:::
* 階躍函數跟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]
```
![image](https://hackmd.io/_uploads/BJEyylK_0.png)
:::

* 運算
    * 權重的符號
    ![image](https://hackmd.io/_uploads/H1m2XZtdC.png)
    ![image](https://hackmd.io/_uploads/B1y3I-Y_A.png)
[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 這樣修的
    ![image](https://hackmd.io/_uploads/ByayAWtu0.png)
    分子和分母上都乘上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函數->用機率推理結果
    ![image](https://hackmd.io/_uploads/BJ3rxGtOA.png)
    * 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)
    * 最常被用在回歸上的損失函數
    ![image](https://hackmd.io/_uploads/S1NwRGYdC.png)
    * 求預測值與真實值之間距離的平方總和
    ```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)
* 交叉熵誤差
    ![image](https://hackmd.io/_uploads/rkurZQY_C.png)
    * 分類問題常用的損失函數
    * 正解標籤越大，交叉熵誤差算出來越接近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方向上的變化率（偏導數）。公式是：
![image](https://hackmd.io/_uploads/ByKbJnJK0.png)
**∂f/∂x: 在y固定的情況下f的變化量除以x的變化量就叫做f對於x的偏導數** (這個符號∂念:partial)
![image](https://hackmd.io/_uploads/Syzwl59t0.png)

使用偏導數在計算梯度的時候就像是在算每個點的斜率，我們要的就是用梯度找這個範圍的最小值

導數跟偏導數差別:
導數單變量函數、單一方向變化
偏導數看多個方向上的變化
:::
[梯度下降法colab](https://colab.research.google.com/drive/1qEwW2WdXAKM8mVjtRiD52dILROlqW-B3?authuser=2#scrollTo=4r8QQ_eZkuCE&line=24&uniqifier=1)
* 算出梯度後，在梯度下降法中，帶入公式:
![image](https://hackmd.io/_uploads/SymBBpJtR.png)
    - θ是參數向量。
    - α 是學習率。
    - \( ∇θ 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)
* 簡單但費時
![image](https://hackmd.io/_uploads/r1R4TmzFC.png)
* 每經過一個epoch(當所有數據都被使用過時)，就記錄當下數據跟精確度(損失函數)

---

### 誤差反向傳播法
* 被算在上面的第2步: 記算梯度
#### 計算圖
* 表示神經網路計算過程的結構化圖形
![image](https://hackmd.io/_uploads/rycIKSbYA.png)
* 正向傳播: 左到右計算(輸入-> 輸出)
* 反向傳播: 從右到左，基於鍊式法則後向前計算導數
* 局部計算: 只需要計算跟自己有關的內容不用考慮全局
* 計算圖的反向傳播：沿著與正方向相反的方向，乘上局部導數
#### 鍊式法則
* 複合函數: 多個函數組成
![image](https://hackmd.io/_uploads/HkR9hr-FA.png)
* 複合函數導數=構成這個複合函數的函數們的**導數乘積**(在上面的圖中就是左邊兩個函數的導數相乘=右邊函數的導數)

### 相關技巧
#### 參數更新
* 隨機梯度下降法(SGD)在某些時候效率低，我們可以優化它的下降路徑
![image](https://hackmd.io/_uploads/r1GmLtjY0.png)
* Momentum
    * 動量優化法
    * 在0~1之間(通常設0.9)
* AdaGrad
* Adam
:::spoiler 圖
![image](https://hackmd.io/_uploads/ryH76KsKC.png)
![image](https://hackmd.io/_uploads/HkNBpFoKA.png)
![image](https://hackmd.io/_uploads/BJv8pFsFR.png)
![image](https://hackmd.io/_uploads/ryfv6KoY0.png)
:::



