# 機器學習基本概念簡介(機器學習 2021)筆記 [TOC] 機器學習:讓機器幫我們找一個函式來解決問題。 (⇔ 傳統:我們寫出函式叫機器執行) 用來解決「解決問題的函式」會複雜到人寫不出來的問題。例如: * 語音辨識 * 影像辨識 * ⋯⋯ ![](https://hackmd.io/_uploads/SJQbYnKXh.png) <small>https://youtu.be/Ye018rCVvOo?t=0</small> ## 基本步驟 以預測 YouTube 頻道觀看人數為例。 ### 1. 選擇 Model - 寫出帶有未知參數的函式 ![](https://hackmd.io/_uploads/rJSeO3Fm2.png) <small>https://youtu.be/Ye018rCVvOo?t=650</small> * 猜測:未來觀看人數跟過去觀看人數有關。 * 好的猜測還自於對問題的了解 (domain knowledge)。 簡單的例子:未來某天的觀看人數 $y$,是過去某天的觀看人數 $x_1$ 乘上 $w$ 再加上 $b$,可以寫出一個 function:$y = b + wx_1$。 * Model 就是帶有未知參數的 funciton。 * Feature ($x_1$) 是 function 裡已知的東西。 * Weight ($w$)、bias ($b$) 是 unknown paramter。 * Weight (權重) 是會跟 feature 做相乘的。 * Bias (偏置) 是不會跟 feature 做相乘的,是加上去的。 ### 2. 定義 Loss:一個用來評估 model 輸出的優劣的 function * Loss ($L$) 是一個 function,輸入是 model 裡的參數 ($b$、$w$),輸出是一個代表模型表現的數值。 ![](https://hackmd.io/_uploads/rkhnRhtX3.png) <small>https://youtu.be/Ye018rCVvOo?t=911</small> 舉例: 假設我們想計算 $L(0.5k, 1)$:也就是 $b$ 帶 $0.5k$,$w$ 帶 $1$ 的話,model 的表現有多好。 $$ y = 0.5k + 1x_1 $$ 拿出訓練資料(過去真實的資料)來帶入,假設 2017/1/1 的觀看次數是 4.8K,帶入模型:$0.5k + 1 \times 5.3k$ 得到 $y = 5.3k$;但真實的資料顯示 1/2 的觀看次數是 4.9k ($\hat{y}$),因此,預測出的結果與真實差距是 $e_1 = |y - \hat{y}| = 0.4k$。 * 正確、真實的數值 ($\hat{y}$) 叫做 label。 ![](https://hackmd.io/_uploads/ryeBZTtm2.png) <small style="text-align: center;">https://youtu.be/Ye018rCVvOo?t=1232</small> 不過訓練資料不只有一筆,我們可以跑過過去一年的所有資料,得到許多 $e$,然後取平均: $$ L = \frac{1}{N} \sum\limits_{n} e_n $$ $L$ 越小,代表這組參數越好。 > $e$ 的計算不只有稱作 mean absolute error (MAE) 的 $|y - \hat{y}|$ 一種,還有像是稱作 mean square error (MSE) 的 $(y - \hat{y})^2$,適用於 $y$ 和 $\hat{y}$ 是機率分佈的 Cross-entrophy。 #### Error Surface(等高線圖) 我們窮舉所有 $w$ 和 $b$,為每個組合都算一個 Loss,可以獲得一個等高線圖。越紅代表 Loss 越大,越藍代表 Loss 越小。 ![](https://hackmd.io/_uploads/r1pf4ptQh.png) <small>https://youtu.be/Ye018rCVvOo?t=1369</small> 畫出來的等高線圖就叫 Error Surface。 ### 3. Optimization - 解一個最佳化問題 我們要找一組 $w$ 和 $b$,讓 $L$ 最小。 ![](https://hackmd.io/_uploads/B1S-8TYQ2.png) <small>https://youtu.be/Ye018rCVvOo?t=1506</small> 把最好的一組 $w$ 和 $b$ 定義為 $w^*, b^*$。 其中一個 optimization 的辦法是 Gradient Descent。 為了方便解釋 Gradient Descent,我們先假設未知參數只有一個 $w$,先不管 $b$。現在只有一個參數,我們可以得到一個曲線圖,也是一個二維的 Error Surface。 步驟: ![](https://hackmd.io/_uploads/r1dyJCtQ3.png) <small>https://youtu.be/Ye018rCVvOo?t=1554</small> 1. 隨機選擇一個初始的點 $w^0$。(在某些情況下會有比隨機選擇還更好的做法,現在先不管這件事) 2. 計算 $w^0$ 在曲線上的切線斜率 (藍色虛線),也就是微分 $\frac{\partial{L}}{\partial{W}} |w = w^0$。以此判斷下一個 $w$ 要取在哪才會再好一點。 * 想像有一個人站在 $w^0$ 點上,左右環視一下 (就是算微分),就知道哪邊比較低,就往哪邊跨出一步。 * 要跨多大步?有兩個考量: 1. 斜率有多大?斜率大,就跨大一點。 2. 在訓練模型時,我們自己決定的一個*超參數* (*hyperparamter*):learning rate ($\eta$)。Learing rate 越大,跨的就越大。 > 在機器學習中,需要自己設定的參數會叫做 hyperparamters,來跟「機器會自己找出來的參數」paramters (例如 $w$、$b$) 做區別。 3. 把 $w$ 移動一步,變成 $w^1$。 4. 重複步驟 2 跟 3,反覆更新 $w$,直到⋯⋯ * 失去耐心了:在訓練時通常會設定更新次數的上限,這個上限也是個*超參數* (*hyperparamter*)。 * 微分值是 0:理想狀況,表示走到平地了。 但以上步驟可能有個問題,我們最終可能會走到由隨機初始位置不斷往下走所能達到的最低點 (local minima),但永遠走不到整個 error surface 真正的最低點 (global minima)。 ![](https://hackmd.io/_uploads/SyMIyCKXh.png) <small>https://youtu.be/Ye018rCVvOo?t=2060</small> 雖然在教科書上,Gradient Descent 的 local minima 問題是個大問題,但在實務上卻是個假議題,有其他更麻煩的問題。 #### 有兩個參數,怎麼使用 Gradient Descent? 其實跟一個參數沒什麼不同。 ![](https://hackmd.io/_uploads/B1uHbAK72.png) <small>https://youtu.be/Ye018rCVvOo?t=2188</small> > 做機器學習不需要會算微分,在 PyTorch 只要 call 一個 function 就好了。 ![](https://hackmd.io/_uploads/HJlJGAtQn.png) <small>https://youtu.be/Ye018rCVvOo?t=2313</small> ### 基本步驟:小結 ![](https://hackmd.io/_uploads/B1ub70Fmn.png) <small>https://youtu.be/Ye018rCVvOo?t=2430</small> 但以上這個模型在真實的預測上,表現沒有很好。 ## 進一步改善 ### 依照對問題的理解來更新模型 ![](https://hackmd.io/_uploads/rk0KS0t72.png) <small>https://youtu.be/Ye018rCVvOo?t=2594</small> 觀察真實數據後,我們發現觀看人數有每七天一個循環的週期,因此,我們應該要把前七天的觀看人次都列入考量。 我們寫了一個新的模型,把過去七天的資料 ($x_j$) 分別乘上不同的 weights ($w_j$),通通加起來之後再加上一個 bias ($b$): $$ y = b + \sum\limits_{j=1}^{7} w_j x_j $$ 確實有了進步 (對真實預測資料的 loss $L'$ 從 $0.58k$ 降到 $0.49k$)。 ![](https://hackmd.io/_uploads/BJFyLRKX2.png) <small>https://youtu.be/Ye018rCVvOo?t=2768</small> 如果更進一步的提升 input 的天數,例如到 28 天,模型的表現還可以再進步一點 ($L' = 0.46k$)。但是,再提高到 56 天,就卡住沒有再進步了 ($L'$ 仍然是 $0.46k$)。 ### 更強的 Model 回顧我們模型的基本邏輯,$y = b + wx_1$,會發現無論如何改參數,它能做到的還是非常有限,$x_1$ 對 $y$ 永遠是線性的關係 (藍線)。這樣的 model 稱為 linear model。 ![](https://hackmd.io/_uploads/rkPyq0tmn.png) <small>https://youtu.be/bHcJCp2Fyxs?t=0</small> 但也許現實中,當前一天的觀看人數 $x_1$ 小於某個數值時,$y$ 與 $x_1$ 是成正比,而當 $x_1$ 超過某個數值時,$y$ 與 $x_1$ 反而會成反比 (紅線),物極必反。 原先用的 model 不管如何調整 $w$ 和 $b$ 也永遠造不出紅色的那條線。顯然 linear model 有很大的限制。這種來自於 model 的限制叫做 Model Bias。 > Model Bias 和參數 $b$ 的 bias 是不同的概念! 要如何找一個有可能表達出紅線的 model 呢? 也就是,要如何寫一個,有辦法可以變成紅線的,帶有未知參數的 function? 紅色曲線的 function 可以由一群長得像這樣 (固定格式):<img height="20" src="https://hackmd.io/_uploads/rypc30tmh.png"> 的不同藍色 function 相加之後組合出來。 ![](https://hackmd.io/_uploads/SyF4T0tX2.png) <small>https://youtu.be/bHcJCp2Fyxs?t=146</small> 不管是怎樣的 piecewise linear curve (由很多直線線段組合出來的 curve ) 都可以用這個方式組合出來,只是越複雜 (區段越多) 的 curve 就需要越多的 <img height="20" src="https://hackmd.io/_uploads/rypc30tmh.png"> 才能組出來。 ![](https://hackmd.io/_uploads/Sk9gACt72.png) 就算不是 piecewise linear curve,也可以用 piecewise linear curve 來逼近它。 ![](https://hackmd.io/_uploads/B1OzC0tm2.png) 因此,我們可以用一堆 <img height="20" src="https://hackmd.io/_uploads/rypc30tmh.png"> 的組合,來逼近任何可能的 function。 要如何定義 <img height="20" src="https://hackmd.io/_uploads/rypc30tmh.png">?可以用一個叫做 Sigmoid 的 function 來逼近它。 ![](https://hackmd.io/_uploads/Hydqkk5Xn.png) <small>https://youtu.be/bHcJCp2Fyxs?t=508</small> 總之就是有 $w$、$b$ 和 constant $c$ 三個參數可以調整的 function。當 $x_1$ 非常大,$y$ 會收斂於 $c$,而當 $x_1$ 非常小,$y$ 會收斂於 $0$。 這個 function 可以簡單記為: $$ y = c\ sigmoid(b + wx_1) $$ > 而那個原本的、轉折是硬的 function <img height="20" src="https://hackmd.io/_uploads/rypc30tmh.png">,則叫做 Hard Sigmoid。 $w$、$b$ 和 $c$ 對 Sigmoid Function 的影響: ![](https://hackmd.io/_uploads/SyS8byqX3.png) <small>https://youtu.be/bHcJCp2Fyxs?t=692</small> * $w$ 控制斜度。 * $b$ 控制左右移動。 * $c$ 控制高度。 如此我們就能做出不同的 Sigmoid Function,再用各種 Sigmoid Function 疊加出近似各種 piecewise linear curve,去逼近各式各樣的 function。 ![](https://hackmd.io/_uploads/B1PPfk9m3.png) <small>https://youtu.be/bHcJCp2Fyxs?t=761</small> $$ y = b + \sum\limits_{i}c_i\ sigmoid(b_i + w_i x_1) $$ 這就是一個非常有彈性的、有未知參數的 function。 #### Non-linear Model ![](https://hackmd.io/_uploads/ryMLIJ573.png) <small>https://youtu.be/bHcJCp2Fyxs?t=913</small> 我們最初的 model 是: $$ y = b + w x_1 $$ 利用 sigmoid,我們可以有一個更有彈性的 model: $$ y = b + \sum\limits_{i}c_i\ sigmoid(b_i + w_i x_1) $$ > $i$ 代表可以用多少個 sigmoid function 來組合。 而在上一小段,我們也曾把最初的 model,$y = b + w x_1$,改成支援多個 feature (例如可以用前 $j$ 天的資料來做預測) 的版本: $$ y = b + \sum\limits_{j} w_j x_j $$ > $j$ 代表可以輸入多少個 feature。 而要把它擴展更有彈性的 sigmoid 版本,我們可以把 $b + \sum\limits_{j} w_j x_j$「套進」sigmoid 版的 function 裡: $$ y = b + \sum\limits_{i}c_i\ sigmoid\left(b_i + \sum\limits_{j} w_{ij} x_j\right) $$ 畫個圖來解釋一個實際的例子:如果我們取前三天的觀看人數來預測下一天的觀看人數 ($j: 1, 2, 3$),並且使用 3 個 sigmoid function ($i: 1, 2, 3$)。 首先,單獨看一個藍色的部分,也就是當 $i = 1$ 時、第一個 $sigmoid$ 的括號裡做的事: ![](https://hackmd.io/_uploads/B1akF1cmn.png) <small>https://youtu.be/bHcJCp2Fyxs?t=1039</small> 就是把三個 feature,$x_1$、$x_2$、$x_3$ 分別乘上一個 weight ($w_{i1}$、$w_{i2}$) 之後,加總起來,再加上一個 bias $bi$。 > * $w_{ij}$ 是指在第 $i$ 個 $sigmoid$ 裡,乘給第 $j$ 個 feature 的 weight。 > * $b_{i}$ 就是在第 $i$ 個 $sigmoid$ 裡,feture 都乘上 weight 之後相加再加上的 bias。 以此類推⋯⋯ ![](https://hackmd.io/_uploads/r1hZok5X2.png) 為了簡化,我們把第 $i$ 個 `sigmoid` 裡的東西 (也就是藍框裡的式子) 表示為 $r_i$。分別有 $r_1$、$r_2$ 和 $r_3$。 > 再來為了要把 $x_j$ 和 $r_i$ 的關係用比較簡化的方法來表示,我們可以用線性代數中,矩陣相乘 (向量) 的表示法: > > ![](https://hackmd.io/_uploads/SJognJqmn.png) <small>https://youtu.be/bHcJCp2Fyxs?t=1194</small> > > $$ > r = b + Wx > $$ > > 向量 $x$ 乘上矩陣 $w$ 加上 $b$ 得到向量 $r$。這樣就可以省下寫一堆 $x$、一堆 $+$ ⋯⋯ 的時間。 在式子 $y = b + \sum\limits_{i}c_i\ sigmoid\left(b_i + \sum\limits_{j} w_{ij} x_j\right)$ 中,接下來,就是讓 $r_1$、$r_2$ 和 $r_3$ 分別通過 $sigmoid$: ![](https://hackmd.io/_uploads/SJmZAy9Q3.png) <small>https://youtu.be/bHcJCp2Fyxs?t=1342</small> > 這裡也有一個簡潔的表示法: > > $$ > a = \sigma(r) > $$ > > 代表 $r$ 這個向量裡的東西分別通過 $sigmoid$,然後得到 $a$ 這個向量。 最後,就是把三個 $sigmoid$ 出來的結果,分別乘上 $c_i$ 之後加總,再加上一個 $b$ 之後得到 $y$。 ![](https://hackmd.io/_uploads/Hkrskgcmh.png) <small>https://youtu.be/bHcJCp2Fyxs?t=1406</small> > 向量的表示法: > > <img height="300" src="https://hackmd.io/_uploads/rJZ1bg9Xh.png" /> > > $$ > y = b + c^T a > $$ > > 代表 $r$ 這個向量裡的東西分別通過 $sigmoid$,乘上轉置(transpose,也就是讓原本直的 $c$ 躺成平的)後的 $c$ ,然後得到 $a$ 這個向量。 總上,我們的「比較有彈性」的 model: $$ y = b + \sum\limits_{i}c_i\ sigmoid\left(b_i + \sum\limits_{j} w_{ij} x_j\right) $$ 畫成圖可以表示為: ![](https://hackmd.io/_uploads/SkstGe97h.png) <small>https://youtu.be/bHcJCp2Fyxs?t=1448</small> > 而用線性代數的方式可以記為: > > * ${\bf r} = {\bf b} + W{\bf x}$ > * ${\bf a} = \sigma({\bf r})$ > * $y = b + {\bf c}^T {\bf a}$ > > 合併後: > > $$ > y = b + {\bf c}^T \sigma({\bf b} + W{\bf x}) > $$ > > * $\bf x$ 是所有 feature 的向量,共有 $j$ 個項目。 > * $W$ 是有 $i \times j$ 個項目 (有多少個 sigmoid $\times$ 有多少個 feature) 的矩陣。 > * $\bf b$ 是放在每個 sigmoid 的 bias 的向量,有 $i$ 個項目。 > * $\bf c$ 對每個 sigmoid 出來的東西要乘上的常數的向量,有 $i$ 個項目。 > * $b$ 是最後加上去的 bias。 > > 其中 $W$、$\bf b$、$\bf c$、$b$ 四個是未知參數,也就是訓練模型時要找出的東西。 > ![](https://hackmd.io/_uploads/ry1F8xcX2.png) > <small>https://youtu.be/bHcJCp2Fyxs?t=1533</small> > 我們可以把這個四個東西的內容通通合併成一個向量,然後統稱它為 $\theta$。 這邊的 $sigmoid$ 除了 $sigmoid$ 之外,其實還有不同的 function 可以選擇。而像 $sigmoid$ 這樣的 function,就稱為 Activation Function。 > Q&A > > Q1:可不可以暴力搜尋所有的參數來找到最好的? > A1:在只有兩個參數 $w$ 和 $b$ 的時候可能可以,但很快的參數就會變得非常的多,不可能用暴力搜尋的,因此就需要用 Gradient Descent 之類的方法來找。 > > Q2:Sigmoid 的數目? > A2:Sigmoid 的數目是自己決定的,sigmoid 的數量越多,越能組出更複雜的 function。要有多少 sigmoid 又是另一個 hyperparamter。 > > Q2:可以用 hard sigmoid 嗎? > A2:可以,但寫出來的 function 會比較複雜,所以這邊先用 sigmoid 來介紹。 ### Loss 的定義是一樣的 只是現在我們的參數太多了,通通寫出來太累了,所以就用 $L(\theta)$ 來表示。$\theta$ 代表所有未知的參數。 ![](https://hackmd.io/_uploads/HyD8tlc7h.png) <small>https://youtu.be/bHcJCp2Fyxs?t=1852</small> ### Optimization 也是一樣的 還是可以用 Gradient Descent。只是有一堆東西要微分,全部微分後得到一個很大的向量,而這個向量 $\bf g$ 就叫做 gradient。 ![](https://hackmd.io/_uploads/BkIVjxcX3.png) <small>https://youtu.be/bHcJCp2Fyxs?t=1937</small> 常看到的表示方式是: $$ {\bf g} = \nabla L(\theta^0) $$ $\nabla$ 的意思是把所有 $\theta$ 通通拿去對 $L$ 做微分。$\theta^0$ 的 "$^0$" 表示 算微分的位置在 $\theta = \theta^0$ 的地方。 更新參數的方法也是一樣的,只是從更新兩個參數變成更新一堆參數。 ![](https://hackmd.io/_uploads/r1uKjx9Qn.png) 也是一樣,重複更新很多次直到不想再做為止。 ![](https://hackmd.io/_uploads/rJpTsxq73.png) #### 實作細節:分 Batch 實務上,我們不會一次把所有資料一次通通丟進去算 loss 和 gradient。 ![](https://hackmd.io/_uploads/H1pSTgcmh.png) <small>https://youtu.be/bHcJCp2Fyxs?t=2208</small> 我們會把 data 分成好幾個 batch (隨機分組就好),每次用一個 batch 算 loss $L^n$,算出 gradient 來更新參數。 * 每次更新參數,叫一次 update。 * 把所有 batch 都看過一次,叫一次 epoch。 舉例: ![](https://hackmd.io/_uploads/r10jpe5Q3.png) <small>https://youtu.be/bHcJCp2Fyxs?t=2356</small> ## 更多模型的變形 ### Hard Sigmoid Hard Sigmoid 是兩個 Rectified Linear Unit (ReLU,一條水平的線走到一個地方突然有個轉折) 的加總。 ![](https://hackmd.io/_uploads/r1ru0xcQn.png) <small>https://youtu.be/bHcJCp2Fyxs?t=2463</small> 要用 Hard Sigmoid,可以把 activation function 換成 $max$,然後把原本的 $i$ 變兩倍,因為兩個 ReLU 才能組出一個 hard sigmoid。 ![](https://hackmd.io/_uploads/H1w-y-57n.png) 實際上 ReLU 似乎比較好用?之後會介紹。 ### 疊更多層 把經過一層 activation function 的 $a$ 當作 feature $x$ 再丟進另一層 activation function。 ![](https://hackmd.io/_uploads/rJyZlZ973.png) <small>https://youtu.be/bHcJCp2Fyxs?t=2713</small> 參數又更多了。 要過幾層?又是另一個 hyperparamter。 ## Neural Network & Deep Learning 這樣的機器學習模型要叫什麼名字? 1. 有很多 sigmoid 或 ReLU 串再一起,就像神經元一樣,很多神經元串在一起,就叫神經網路。 ![](https://hackmd.io/_uploads/BksVZWqmn.png) <small>https://youtu.be/bHcJCp2Fyxs?t=2953</small> * 但在 80-90 年代已經把這個名字玩爛了。 2. 有很多層 neuron,每一層 neuron 叫一個 layer (hidden layer),有很多 hidden layer 就叫做 deep,於是就叫做 Deep Learning。 ![](https://hackmd.io/_uploads/Hy4IzWqmh.png) <small>https://youtu.be/bHcJCp2Fyxs?t=3051</small> 人們就開始把 layer 越疊越多、越疊越深⋯⋯錯誤率也在降低,從 16.4% 到 7.3%、6.7% ⋯⋯。 ![](https://hackmd.io/_uploads/rJ7RzW97n.png) <small>https://youtu.be/bHcJCp2Fyxs?t=3083</small> 「比 101 還要多層」 ### 「Deep」的意義何在?為什麼要疊很多層? 通通攤成一層不好嗎?是不是因為可以叫「deep」比較厲害,攤開變「fat」就不潮了? ![](https://hackmd.io/_uploads/BJAIX-5m2.png) <small>https://youtu.be/bHcJCp2Fyxs?t=3133</small> 之後會講。 ### Overfitting 指「在訓練資料上表現良好,但對沒看過的真實資料反而表現變差」。 ![](https://hackmd.io/_uploads/ryuEEW9Q2.png) <small>https://youtu.be/bHcJCp2Fyxs?t=3221</small> ## 延伸 * 更有效率的算 gradient 的方法:Backpropagation https://www.youtube.com/watch?v=ibJpTrp5mcE