【Pytorch 深度學習筆記】機器如何學習
【Pytorch 深度學習筆記】機器如何學習
哈囉大家好我是 LukeTseng,感謝您點進本篇筆記,該篇筆記主要配合讀本 《Deep Learning with pytorch》 進行學習,另外透過網路資料作為輔助。本系列筆記是我本人奠基深度學習基礎知識的開始,若文章有誤煩請各位指正,謝謝!
本篇為 《Deep Learning with pytorch》 這本書第五章 The mechanics of learning 的相關筆記。
學習僅是參數估計(Learning is just parameter estimation)
在書中的章節 5.2,所想表達的是:所謂學習,在 ML / DL 中,從機制上看就是在參數估計,用資料去把模型裡未知的參數調到最合理,讓模型對新資料也能預測得準。
在書中把學習描述成一個反覆迭代的流程。
給模型一批輸入資料、模型用目前參數算出輸出(Forward)、再把輸出跟正確答案(Ground Truth)相比得到誤差,接著用誤差去決定參數該往哪個方向更新(Backward),重複到誤差夠小為止。
在這邊最重要的觀念是模型不是靠人手寫規則學會,而是靠調參數讓輸入輸出關係越來越符合資料。

Source:“Deep Learning with PyTorch” P. 107 Figure 5.2
- 基準真相(Ground Truth):希望模型產出的正確答案。
- 權重(Weights):模型中的參數,初始值通常是隨機的。
- 前向傳播(Forward Propagation):將數據輸入模型,根據當前的權重產生輸出。
- 誤差評估(Error Evaluation):就是損失函數(Loss Function),透過比較模型的預算輸出與 Ground Truth,計算出一個誤差衡量值。
- 反向傳播(Backward Propagation):計算誤差對權重的導數(梯度),了解如果權重改變一個單位,誤差會如何變化。
- 權重更新:朝著減少誤差的方向調整權重,並重複此過程直到誤差降至可接受的水準。
例子:校正溫度計
假設你有一個好看但沒標記單位指針式溫度計。
為了解決單位的問題,所以就來建立一個模型,將溫度計的讀數轉換為大家所熟悉的攝氏溫度。
那這模型裡面要做什麼事情?透過記錄一組讀數(未知單位 )與相對應的攝氏溫度( ),來估計兩者之間的轉換參數。
這也是監督式學習最典型的資料形式,(輸入, 正確輸出) 的配對。
資料預處理
收集好相關資料後,來將這些資料轉換成 tensor(Code Source:p1ch5
/1_parameter_estimation.ipynb):
1 | t_c = [0.5, 14.0, 15.0, 28.0, 11.0, 8.0, 3.0, -4.0, 6.0, 13.0, 21.0] |
這些數據包含了噪點(Noise),可能是讀數誤差或設備本身的不精確。
資料視覺化
如果進一步將資料視覺化,採用 matplotlib 做繪圖的話,讀數與攝氏溫度看起來會有呈現線性趨勢的樣子:
1 | %matplotlib inline |

模型選擇
藉由剛才觀察到的線性趨勢,假設最簡單的模型是: $$t_c = w \times t_u + b$$
- (weight, 權重):表示縮放比例。
- (bias, 偏差值):表示常數的偏移量。
在此的學習任務就是要根據現有資料對參數 跟 來做估計這件事。如果能找到一組參數,使得輸入 得到的預測溫度與實際測量的 非常接近,那就完成了擬合(Fitting)或學習。
接著就可以定義模型了:
1 | def model(t_u, w, b): |
當中 跟 都是 0 維 tensor,即為純量的意思。
雖然這兩個是純量,但與 tensor 做相乘或相加的時候,PyTorch 會自動用廣播機制來產生 tensor 的結果。
減少損失是我們所想的(Less loss is what we want)
損失函數(Loss Function)
有時也稱為 Cost Function(成本函數)。
Loss Function 是一個將模型表現量化為單一數字的函數,學習過程的目標就是最小化這個數值。
基本上就是告訴訓練模型的人,這個模型的表現有多好,如果值越大表示模型表現差(預測與目標差距很大),越小表示越好(與目標差距小)。
計算方式:通常是模型產出的預測值( )與期望的目標值( ,Ground Truth)之間的差。
數學運算式的選擇:絕對值 vs. 平方
為了保證損失值不論在預測過高或過低時都是正數,書中提出兩種比較常看到的選擇(接續溫度計的例子來舉例):
- 絕對差( ):這是比較簡單且直接的方式,但在預測等於目標的地方(最小值處),其導數(斜率)是未定義的。
- 平方差( ):書中所採用的方式。
為何選擇平方差
- 導數性質:平方差在接近最小值時表現更為平滑,在當 時,其導數為 0,有助於數值的優化。
- 懲罰離群值(Outliers):讓模型產生許多「微小的錯誤」比產生少數幾個「巨大的錯誤」要好,因此平方運算會放大那些錯的離譜的結果,這正是所樂見的。
- 凸函數(Convex):對線性模型來說,這兩種 Loss Function 都是凸的,因此有明確的單一最小值,可以很容易就找到。
補充:所謂凸函數指的是二階導數 的情況,根據二階導數判斷法,這種情況會產生極小值。
接下來看圖可能會更好理解:

Source:“Deep Learning with PyTorch” P. 110 Figure 5.4
左邊是絕對差的部分,右邊是平方差。
絕對值函數( )在 x = 0 的地方導數不存在,這會有個問題,就是不能做 gradient descent,因為梯度的原理就是求導數,然後剛好那邊不存在,也就沒有辦法計算梯度。
而平方差函數( ) 恰好相反,在 x = 0 附近曲線平滑,可微分(導數 = 0)。
繼續溫度計例子:定義 loss function
先前有將模型定義好,接著就來定義一個 Loss Function,書中採取的方式是均方誤差(Mean Square Error, MSE) 的方式。
1 | def loss_fn(t_p, t_c): |
(t_p - t_c)**2 會對 tensor 中的每個元素計算差異並平方,產生一個與輸入相同形狀的新 tensor。
最後呼叫 .mean() 方法,將 tensor 中所有元素的平方差取平均值,最終輸出一個單一的純量損失值。
由於 loss function 的輸出是要一個純量值,因而這邊用 .mean() 求純量值,而非直接 return squared_diffs,因為這東西是個向量,看不出模型的好壞程度。
初始化參數及檢查損失值
將 權重設定為 1.0,偏差值 設定為 0.0。
接下來呼叫模型 model(t_u, w, b),而這時候的模型僅僅只是把輸入的資料原封不動的做輸出( )
1 | w = torch.ones(()) |
Output:
1 | tensor([35.7000, 55.9000, 58.2000, 81.9000, 56.3000, 48.9000, 33.9000, |
而在這個步驟就已經算是前向傳播的部分了。將數據輸入模型,根據當前的權重產生輸出,就是前向傳播的定義。
接下來做檢查損失值的動作:
1 | loss = loss_fn(t_p, t_c) |
Output:
1 | tensor(1763.8846) |
可以發現得出的損失值非常大,表示當前的參數跟實際值的差異極大,因此需要透過後續的優化過程來降低這個數值。
廣播機制(Broadcastings)的補充
廣播機制會從最後一個維度(末尾維度)開始向後比對。
以下是匹配 tensor 元素的規則:
- 對於由後往前數的每個索引維度,如果其中一個運算元在該維度上的大小為 1,則 PyTorch 會將該維度上的單一元素與另一個 tensor 在該維度上的每個元素進行配對。
- 如果兩個 tensor 的大小都大於 1,則它們必須相同,並使用自然比對。
- 如果一個 tensor 的索引維度比另一個 tensor 多,則另一個 tensor 的所有元素都會用來符合這些維度上的每個元素。
書中用一個示意圖來表示廣播機制的運作原理:

Source:“Deep Learning with PyTorch” P. 112
另外書中也舉了這樣的例子:
1 | x = torch.ones(()) |
Output:
1 | shapes: x: torch.Size([]), y: torch.Size([3, 1]) |
形狀為 ()(純量,零維張量)與形狀為 (3, 1) 的張量相乘後,形狀的結果會是 (3, 1)。
這個結果主要遵循上面的第三條規則。
接下來 (3, 1) 跟 (1, 3) 做相乘,得到的形狀會是 (3, 3)。這部分遵循上面第一條規則,兩個 tensor 在該維度的長度完全一致、其中一個維度長度為 1。如果一個張量的維度比另一個少,則會自動將缺失的維度視為長度 1 並進行擴展,這也是為什麼結果會是 (3, 3) 的原因。
沿著梯度下降(Down along the gradient)
在上一節有提及到如何優化來降低 loss function 的產生值,那這個優化的方法就叫做梯度下降(Gradient Descent)。
書中提供了轉鈕作為梯度下降的比喻。
想像面前有一台機器,上面有兩個標示為 (權重)和 (偏差值)的轉鈕,螢幕上顯示著損失值(Loss)。
目標是要把這個 Loss 降到最低,那因為不知道轉扭會對損失這件事情會有多大的影響,所以會微調轉扭,觀察 Loss 是增加還是減少,從而決定旋轉方向。
當接近最小值(損失變化變慢)時,會縮小調整幅度,避免錯過最低點或讓損失再次攀升。
總之,Gradient Descent 是在計算損失函數相對於每個參數的變化率,並朝著損失減少的方向修改參數的方法。

Source:“Deep Learning with PyTorch” P. 113 Figure 5.5
數值估計變化率(求導數)
為了知道如何轉動轉鈕(調整參數 和 ),得要知道微調參數後損失會如何變化。在這邊書中的做法是給參數加一個極小值 delta ,觀察損失在附近的變化。
1 | delta = 0.1 |
這段程式碼在計算當 改變時,Loss Function 的變化幅度。
其實就是在求導數啦,這一段可以寫成這樣的數學公式: $$f’(w) \approx \frac{f(w + \delta) - f(w - \delta)}{2\delta}$$
這種叫做中心差分,就是前向差分 + 後向差分,可以想像往左一步也往右一步的感覺。
前向差分(導數定義): $$f’(w) \approx \frac{f(w + \delta) - f(w)}{\delta}$$
後向差分: $$f’(w) \approx \frac{f(w) - f(w - \delta)}{\delta}$$
中心差分在步長一樣小的情況下,近似通常會更準,因此書中採用中心差分的方式計算 Loss 值在附近的變化。
確定調整方向與幅度
得到變化率後,要決定調整參數的方向:
- 方向選擇:
- 變化率是正的,代表增加 會讓損失增加,所以要減少 。
- 變化率是負的,則要增加 以最小化損失。
- 比例調整:更新的幅度應與損失的變化率成比例,這樣對損失影響較大的參數會得到較大的調整。
學習率(Learning Rate)
在實際更新參數時,不能直接看變化率調整,因為變化率在遠處可能會與當前的 完全不同,不然怎麼叫做變化率嘛。因此需要一個較小的(可能 0.001)縮放因子(scaling factor)乘上這個變化率,在機器學習中稱為學習率(learning_rate:用 eta 表示)。
簡言之,Learning Rate 是要讓模型每一步要跨多大步的設定,專門控制它更新權重時的步伐大小。如果步伐跨的很大,則有可能會錯失掉最小值的點,在最小值附近亂跳或產生震盪,反之跨的太小,那這個模型不知道是要跑掉民國幾年才跑得完。
在這邊撰寫程式碼:
1 | learning_rate = 1e-2 |
等號前面的 ,代表著下一個 ;後面的 則為當前的 。
後面的負號是為了要讓 做增加或減少的事情,例如得到的導數 loss_rate_of_change_w < 0,這時候 是要做增加,因此前面加上一個負號讓式子變正的。
然後同樣的邏輯也適用於 :
1 | loss_rate_of_change_b = \ |
經過反覆迭代這些數值評估,且假設 learning rate 選得夠小,最後肯定收斂到參數的最優值,使得最後估計出的 loss 最小。
由數值估計轉為「分析法」(Analytical)
原本的數值估計法是給 增加一個微小的 值,並觀察 Loss 的變化率來估計梯度。但這種方法有兩個主要問題:
- 擴展性差:當模型有數百萬個參數時,對每個參數重複執行模型來探測行為是非常低效的行為。
- 精準度受限於 delta:擾動的大小(如 0.1)會影響估計的準確性。如果 Loss 變化太快,這種方法無法給出最正確的下降方向。
分析法(Analytical)則透過數學公式直接求導,相當於讓這個擾動量趨近於無窮小。
那分析法怎麼做呢?利用 chain rule(連鎖律),在此之前先對損失函數對參數的變化拆解為兩個部分:
- Loss 對其輸入(即模型輸出 )的變化率。
- 模型輸出 對參數(如 )的變化率。
公式表示為:$$\frac{dLoss}{dw}=\frac{dLoss}{dt_p} \cdot \frac{dt_p}{dw}$$
這邊所對應的是書上這條:d loss_fn / d w = (d loss_fn / d t_p) * (d t_p / d w)
第一部分:Loss 對模型輸出的導數
先前的 Loss 定義為均方誤差 MSE:
1 | def loss_fn(t_p, t_c): |
然後對某一個 求偏導,得到其導函數為:
1 | def dloss_fn(t_p, t_c): |
原本 loss_fn 的公式是 $$loss = \frac{1}{N}\sum^N_{i=1}(t_{p,i} - t_{c,i})^2$$
當中 是樣本數,除以 就是取平均值。
對 求偏導後得到: $$\frac{\partial loss}{\partial t_{p,i}} = \frac{1}{N} \cdot 2(t_{p,i} - t_{c,i})$$
t_p.size(0) 就等同於那個 。
第二部分:模型輸出對參數的導數
模型是一個線性函數,長這樣:
對 和 分別求導:
- 對 求導:只有 項與 相關,結果為 。
- 對 求導: 的係數是 1.0,結果為 1.0。
寫成程式碼則會是:
1 | def model(t_u, w, b): |
1 | def dmodel_dw(t_u, w, b): |
1 | def dmodel_db(t_u, w, b): |
定義完整的梯度函數
將上述部分合起來,就可以得到 grad_fn 了。
這個函數會計算所有資料點上的偏導數,並做加總(廣播機制的逆向過程),最後回傳一個包含所有參數梯度的 tensor。
1 | def grad_fn(t_u, t_c, t_p, w, b): |
將這個函數寫成數學式子就會是:

Source:“Deep Learning with PyTorch” P. 116 Figure 5.7
透過迭代去擬合模型(Iterating to fit the model)
在這邊會結合先前學到的損失函數與梯度計算做整合,再透過迭代讓模型真正學習。
什麼是訓練迴圈(Training Loop)?
當有了模型、損失函數和梯度函數後,還需要一個自動化的過程來不斷更新參數,直到損失降到最低為止,這就是訓練迴圈存在的意義。
這個過程包含以下兩個要素:
- Epoch(訓練週期):機器學習術語,把資料集(data set)裡面的資料全部訓練過一次就是一個 Epoch。
- 終止條件:迭代通常會持續到預設的 Epoch 結束,或是參數 與 不再發生顯著變化為止。
完整的 training_loop 程式碼:
1 | def training_loop(n_epochs, learning_rate, params, t_u, t_c): |
- 前向傳播(
model(t_u, w, b)):將輸入數據t_u丟進模型,得到預測值t_p。 - 損失函數(
loss_fn(t_p, t_c)):比較預測值與實際觀測值t_c的差距。 - 反向傳播(
grad_fn(t_u, t_c, t_p, w, b)):計算損失函數對參數的偏導數(梯度),找出讓損失下降的方向。 - 更新參數(gradient descent):沿著梯度的反方向移動一小步(由
learning_rate決定步長)。
overtraining : learning rate 設得太大
書中作者用 learning_rate = 1e-2 也就是 0.01 下去跑 100 Epoch 的時候,結果發現 Loss 不減反增,變成超大的 inf。
如下程式碼及其輸出結果:
1 | training_loop( |
Output:
1 | Epoch 1, Loss 1763.884644 |
原因是過度訓練(overtraining) or 發散的問題,就是參數的更新幅度太大,導致模型在最小值的兩側劇烈擺動,每一次更新都越過了最小值並跳得更遠。
如圖:

Source:“Deep Learning with PyTorch” P. 118 Figure 5.8
解決方法就是把 learning rate 再調小一點,調成 learning_rate = 1e-4。
調整完後再執行一次:
1 | training_loop( |
Output:
1 | Epoch 1, Loss 1763.884644 |
在這裡有個問題,就是雖然 Loss 穩定了,但是訓練到 Epoch 100 的時候,梯度還是很大,幾乎沒什麼降,就表示離最優解還很遠。
參數規模不一致
書中的作者觀察梯度發現了問題,就是權重 的梯度比偏差值 的梯度大了約 50 倍。
We can see that the first-epoch gradient for the weight is about 50 times larger than the gradient for the bias.
也表示說 跟 處於不同的空間尺度(scale)。
如果學習率大到足以讓 更新,就會讓 不穩定(爆炸);如果學習率小到能讓 穩定,對 來說就太小了,更新幾乎停滯。
輸入正規化(Normalizing inputs)
為了解決「學習率太高會導致損失值 inf」、「太低則會讓訓練極其緩慢甚至停滯」的問題,因此需要對輸入做正規化(Normalizing)的動作。
具體來說怎麼做呢?就是要把輸入資料的範圍大致落在 -1.0 到 1.0 之間。
在溫度計例子中,原始資料 t_u 的數值較大,於是乘以 0.1,得到了正規化後的資料 t_un:
1 | t_un = 0.1 * t_u |
使用正規化後的資料 t_un 後,再執行一次 training_loop,把 learning rate 設回先前會導致不穩的 1e−2。
結果發現:
- 穩定性大幅提升:不像之前 learning rate 調高的時候,會出現
inf的狀況。 - 梯度趨於平衡
最後再用這個正規化後的資料,執行 5000 輪的 Epoch 時,損失值最終降到了約 2.927。得到的權重 w 為 5.3671,偏差值 b 為 -17.3012。
雖說這種規模的問題可能不用用到正規化的方式去優化,也可以在那邊手動調參數,但日後訓練龐大的神經網路,那些參數量是大的可怕,總不可能一個一個手動調,因此需要用到正規化的技巧來優化參數。
總整理
學習就是在做參數估計
在 ML / DL 中,「學習」本質上不是學規則,而是用資料反覆調整模型參數,讓輸入與輸出關係最符合資料分佈。
整個學習流程可理解成這樣:
Forward → 計算 Loss → Backward → 更新參數 → 重複前面步驟
模型本身只是函數形式,真正讓模型變聰明的是參數(weights, bias)的調整。
損失函數(Loss Function):把「好壞」變成可視化的數字
為什麼需要 Loss Function?因為電腦沒有辦法去理解一個模型的好壞,只能最小化一個數值。
為何選擇平方差(MSE)?
相較於絕對差:
- 可微:在最小值處導數為 0,適合梯度下降法。
- 平滑:數值穩定,利於優化。
- 懲罰離群值:鼓勵整體誤差變小。
- 凸函數(對於線性模型來說):保證只有一個全域最小值。
梯度下降(gradient descent):沿著最陡的下降方向走
梯度下降就是在算出「參數變一點點,Loss 會變多少」,然後往 Loss 下降的方向移動。
做到梯度下降基本上有三個要點:
- 梯度(Gradient):方向。
- 學習率(Learning Rate):步伐大小。
- 反覆迭代:逐步逼近最小值。
數值法 vs. 分析法
數值估計(差分法):
- 原理直觀
- 不具擴展性
- 精度受 delta 影響
分析法(Chain Rule):
- 直接算偏導
- 高效、精準
- 是 Backpropagation 的數學基礎
訓練迴圈(Training Loop):學習自動化
一個完整訓練流程固定包含:
- Forward
- 計算 Loss
- Backward(算梯度)
- 更新參數
learning rate 陷阱
- 太大 → 發散、Loss 爆炸(inf)
- 太小 → 幾乎不動,訓練停滯
輸入正規化(Normalization)
目的:讓所有特徵落在相近的數值尺度
效果:
- 梯度平衡
- 可用較大的 learning rate
- 收斂更穩定、更快
專有名詞對照表
| 中文名詞 | 英文名詞 | 說明 |
|---|---|---|
| 學習 | Learning | 用資料估計參數,使模型表現變好 |
| 參數估計 | Parameter Estimation | 尋找最佳參數值的過程 |
| 模型 | Model | 描述輸入與輸出關係的函數 |
| 參數 | Parameters | 模型中可被學習的數值 |
| 權重 | Weight | 控制輸入影響力大小 |
| 偏差值 | Bias | 用來平移輸出的常數 |
| 擬合 | Fitting | 模型成功貼近資料 |
| 監督式學習 | Supervised Learning | 使用 (輸入, 正確輸出) 訓練 |
| 資料集 | Dataset | 用於訓練或測試的資料 |
| 輸入資料 | Input Data | 餵給模型的數值 |
| 正確答案 | Ground Truth | 希望模型輸出的真實值 |
| 噪聲 | Noise | 資料中的誤差或干擾 |
| 廣播機制 | Broadcasting | 自動擴展張量形狀以運算 |
| 前向傳播 | Forward Propagation / Forward Pass | 用目前參數計算輸出 |
| 模型輸出 | Prediction / Output | 模型預測結果 |
| 誤差評估 | Error Evaluation | 衡量預測與正解差距 |
| 反向傳播 | Backpropagation | 計算誤差對參數的影響 |
| 梯度 | Gradient | Loss 對參數的變化率 |
| 損失函數 | Loss Function | 將模型表現轉為數字 |
| 成本函數 | Cost Function | Loss Function 的別稱 |
| 損失值 | Loss | 模型好壞的量化指標 |
| 均方誤差 | Mean Squared Error (MSE) | 常用回歸損失函數 |
| 絕對差 | Absolute Error | 使用差值絕對值 |
| 平方差 | Squared Error | 使用差值平方 |
| 離群值 | Outlier | 與其他資料差距極大的點 |
| 凸函數 | Convex Function | 只有單一全域最小值 |
| 導數 | Derivative | 函數的變化率 |
| 偏導數 | Partial Derivative | 對單一變數求導 |
| 變化率 | Rate of Change | 輸入改變造成輸出變化 |
| 中心差分 | Central Difference | 數值估計導數方法 |
| 前向差分 | Forward Difference | 往前估計導數 |
| 後向差分 | Backward Difference | 往後估計導數 |
| 分析法 | Analytical Method | 直接用公式算導數 |
| 梯度下降 | Gradient Descent | 沿梯度反方向降低 Loss |
| 最小值 | Minimum | Loss 最低的位置 |
| 收斂 | Convergence | 參數趨於穩定 |
| 發散 | Divergence | 參數失控、不穩定 |
| 學習率 | Learning Rate | 控制更新步伐大小 |
| 縮放因子 | Scaling Factor | 縮小梯度的倍率 |
| 訓練 | Training | 調整參數的過程 |
| 訓練迴圈 | Training Loop | 自動化訓練流程 |
| 訓練週期 | Epoch | 全資料訓練一次 |
| 過度訓練 | Overtraining | 步伐過大導致發散 |
| 正規化 | Normalization | 將數值縮放到合理範圍 |


