# 機器學習 >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) :::