【Pytorch 深度學習筆記】模型驗證與過擬合以及 Autograd 的細節
【Pytorch 深度學習筆記】模型驗證與過擬合以及 Autograd 的細節
哈囉大家好我是 LukeTseng,感謝您點進本篇筆記,該篇筆記主要配合讀本 《Deep Learning with pytorch》 進行學習,另外透過網路資料作為輔助。本系列筆記是我本人奠基深度學習基礎知識的開始,若文章有誤煩請各位指正,謝謝!
本篇為 《Deep Learning with pytorch》 這本書 5.5.3 Training, validation, and overfitting 及 5.5.4 Autograd nits and switching it off 的相關筆記。
模型驗證(validation)
在訓練模型時,最重要的目標是讓模型不僅能記住所給定的訓練資料,還能對沒見過的資料泛化(Generalization)。而最壞的情況是遇到過擬合。
:::info
泛化(Generalization)指的是模型在訓練時從資料中學到「可重複使用的規律」,因此面對沒看過的新資料(同類型、但不是訓練集裡的樣本)也能做出合理的預測,而不是只有把訓練資料背起來而已。
:::
因此需要有兩套系統來分別對模型訓練及驗證:
- 訓練集(Training set):用來讓模型擬合(fit)參數的資料。
- 驗證集(Validation set):不參與訓練,僅用於評估模型在處理新資料時表現如何。
過擬合(overfitting)
什麼是過擬合(overfitting)?就是模型對於訓練資料擬合(fit)的太好了,在訓練集上都表現得非常好,那個曲線幾乎做到完全貼合、貼緊,但換到沒見過的新資料(如驗證集)時表現變差,因為模型記住了訓練資料的細節甚至雜訊,而不是學到可泛化的規律。
過擬合並非模型不夠強,而是模型太適應訓練集,結果對新資料預測不準,因此過擬合本質上是泛化能力不好,也就是離開訓練資料分佈後就失效。
所以如果發現模型在訓練集上的準確度極高,但在驗證集上的損失卻很高(或不降反升),就是過擬合的徵兆。
像是以下這兩張示例圖,表示過擬合的極端例子:

Source:“Deep Learning with PyTorch” P.133 Figure 5.13
如何解決過擬合問題?
- 增加資料量:獲取更多資料以涵蓋更多變化。
- 簡化模型:減小模型的規模(減少參數數量)。雖會讓模型對訓練集的擬合不如複雜模型完美,但在資料點間的表現會更穩定。
- 正則化(Regularization):在損失函數(Loss Function)中加入懲罰項,讓模型參數變化更平滑。
- 加入雜訊(Noise):在輸入中人為加入擾動,強迫模型學習更本質的特徵。
- 平衡策略:先增加模型規模直到能擬合資料,再縮減規模直到停止過擬合。
評估損失的標準(也是判斷過擬合的標準)
在訓練迴圈(Training Loop)中,要觀察兩個數值:
- 訓練損失(Training Loss):模型是否能擬合訓練集。如果訓練損失不下降,通常表示模型太簡單、資料量不足。
- 驗證損失(Validation Loss):模型是否能泛化到新資料。如果跟訓練損失開始分岔,就表示模型正在過擬合。
拆分資料集(Spliting a dataset)
在這邊拆分是要拆成前面說的「訓練集(training set)」跟「驗證集(Validation set)」。
拆成這樣就可以去判斷一個模型是否遇到 overfitting 的問題。
使用 torch.randperm 來隨機排列索引,然後將 dataset 分為兩部分(Code Source):
1 | n_samples = t_u.shape[0] |
Output:
1 | (tensor([9, 6, 5, 8, 4, 7, 0, 1, 3]), tensor([ 2, 10])) |
在獲得了 index 的 tensor 後,可利用這些 tensor 從資料的 tensor 來去建立 training set 跟 validation set。
1 | train_t_u = t_u[train_indices] |
接著修改 training_loop,在每個 Epoch 中,分別計算「訓練損失」(Training Loss)和「驗證損失」(Validation Loss),但要注意只在訓練損失(Training Loss)上呼叫 .backward() 來更新模型參數。
1 | def training_loop(n_epochs, optimizer, params, train_t_u, val_t_u, |
採用 SGD,正規化參數再跑訓練:
1 | params = torch.tensor([1.0, 0.0], requires_grad=True) |
Output:
1 | Epoch 1, Training loss 66.5811, Validation loss 142.3890 |
從結果看來,模型在訓練集與驗證集上的 loss 都持續下降,代表是真的有在學習,而且目前看起來沒有明顯 overfitting 的跡象。
雖說 Validation loss 明顯比 Training loss 高,但也不至於說高出一個量級,是在可接受的範圍內。
作者也說這樣的評估其實並不公平,因為驗證集是很小的,對於評估驗證損失的意義只能到某種程度,而且模型預期會在 training set 表現更好,因為參數就是被 training set 形塑出來的。因此 validation loss 比 training loss 高本身不意外,重點是它是否一樣跟 training loss 一起往下走、且差距是否合理接近。
一些過擬合的情境
- A:訓練和驗證損失都沒有下降,表示模型沒學到東西。
- B:訓練損失下降,驗證損失卻上升,即為過擬合。
- C:兩者同步下降,是最理想的情況。
- D:兩者趨勢相似但數值有差距,這表示過擬合在可控範圍內。

Source:“Deep Learning with PyTorch” P.136 Figure 5.14
小結
可以把訓練集(Training set)想像成是課本裡面的範例,而所謂驗證集(Validation set)就是實戰考題。
如果一個學生只是把練習題的答案背下來,在寫練習題時都全對(訓練損失極低),但一碰到沒看過的考題就 G 了(驗證損失極高),就表示這學生已經過擬合了。
Autograd 的細節
Autograd 會不會搞混?
在之前的訓練迴圈中,於同一個 Epoch 裡做了兩件事:
- 把訓練資料
train_t_u丟進模型,算出train_loss。 - 把驗證資料
val_t_u丟進模型,算出val_loss。
然後只對 train_loss 呼叫了 .backward()。
疑惑的點在於,模型被執行了兩次(一次訓練、一次驗證),是否會讓 Autograd 搞混?而在當呼叫 .backward() 時,他會不會不小心把驗證集的資料也算進去,影響到參數的更新?
答案是不會。
原因在於 PyTorch 的運作機制:獨立的計算圖(Computation Graph)。
- 當用
train_t_u跑模型時,PyTorch 建立了一個計算圖,連接了train_t_u->model->train_loss。 - 當用
val_t_u跑模型時,PyTorch 又建立了另一個獨立的計算圖,連接了val_t_u->model->val_loss。
這兩張圖唯一的交集是共享了模型的參數(parameters)。
當只對 train_loss 呼叫 .backward() 時,Autograd 只會沿著訓練的圖往回跑,計算參數相對於訓練損失的梯度,不會理驗證的圖。
但是有一個風險,書中作者提醒,如果不小心對 val_loss 也呼叫了 .backward(),那麼梯度就會累加,然後模型會變成整個資料集(訓練集 + 驗證集)在學習,這就會破壞驗證集要作為獨立測試的意義了。
以下的示例圖就是在說明上面這些東東:

Source:“Deep Learning with PyTorch” P.137 Figure 5.15
為什麼要關閉 Autograd?
既然都不會在 val_loss 上呼叫 .backward(),那麼 PyTorch 為了「跟蹤計算過程」而建立驗證計算圖的行為,其實是在浪費資源。
如果試著去優化,建立計算圖還是會消耗額外的速度和記憶體,特別是當模型擁有數百萬個參數時。
解決方法就是用先前有用過的 torch.no_grad。
1 | # 以下是原本的寫法,會建立不必要的圖 |
另一種方式是用 torch.set_grad_enabled(is_train)。
如果要根據 bool(像是是否處於訓練模式)來動態控制,可使用這個方法。
1 | def calc_forward(t_u, t_c, is_train): |
總整理
為什麼需要模型驗證(Validation)
模型訓練的真正目標不是把訓練資料「背起來」,而是學到可泛化的規律(Generalization),能正確預測沒看過的新資料。
因此,資料必須分成兩部分:
- 訓練集(Training set):用來調整模型參數。
- 驗證集(Validation set):不參與訓練,只用來檢查模型的泛化能力。
驗證集的存在,就是用來檢測過擬合。
什麼是過擬合(Overfitting)
過擬合指的是「模型在訓練集表現極好,但在驗證集或新資料上表現明顯變差的現象」。
原因不是模型不夠強,而是太貼合訓練資料的細節與雜訊,導致離開訓練資料分佈就失效。
典型徵兆是:
- 訓練損失持續下降
- 驗證損失卻停止下降甚至上升
表示模型的泛化能力不足。
常見的過擬合解法
- 增加資料量:獲取更多資料以涵蓋更多變化。
- 簡化模型:減小模型的規模(減少參數數量)。雖會讓模型對訓練集的擬合不如複雜模型完美,但在資料點間的表現會更穩定。
- 正則化(Regularization):在損失函數(Loss Function)中加入懲罰項,讓模型參數變化更平滑。
- 加入雜訊(Noise):在輸入中人為加入擾動,強迫模型學習更本質的特徵。
- 平衡策略:先增加模型規模直到能擬合資料,再縮減規模直到停止過擬合。
如何用 Loss 判斷模型狀態
在訓練過程中需同時觀察兩個數值:
- Training Loss
- 反映模型是否能學會訓練資料。
- 不下降通常代表模型太簡單或資料不足。
- Validation Loss
- 反映模型的泛化能力。
- 與 Training Loss 分岔是過擬合的警訊。
理想的情況是兩者同步下降,且差距合理。
資料集拆分與驗證流程
實務上會先隨機打亂資料,再切出一部分作為驗證集(如 80% / 20% 的比例做拆分)。
訓練迴圈中主要就分兩部分:
- 訓練資料:計算 loss → backward → 更新參數
- 驗證資料:只 forward、只觀察,不更新參數
只要 Validation loss 跟著 Training loss 一起下降,通常表示模型真的在學習,而不是死背資料。
幾種典型學習情境
- A:訓練、驗證 loss 都不降 → 模型沒學到東西
- B:訓練降、驗證升 → 明顯過擬合
- C:兩者同步下降 → 理想狀態
- D:趨勢相同但驗證略高 → 可接受的輕微過擬合
Autograd 與驗證的關係
在同個 Epoch 中如何處理 Autograd 跟驗證:
- 訓練資料與驗證資料會各自建立獨立的計算圖(Computation Graph)
- 只對 Training loss 呼叫
.backward() - Autograd 不會把驗證資料算進梯度
但若誤對 val_loss.backward(),梯度就會被污染,驗證集失去意義。
為什麼驗證時要關閉 Autograd
驗證階段不需要梯度,但 PyTorch 預設仍會建立計算圖,造成:
- 額外的記憶體消耗
- 不必要的計算成本
解法:
- 使用
torch.no_grad() - 或用
torch.set_grad_enabled(is_train)動態控制
能有效提升效能,特別是在大型模型中。


