DAY 13已經回答了一個很重要的問題:目前這套 DQN 訓練程式能不能正常工作?
答案是可以。用來保存過去互動資料的 Replay Buffer 會持續累積經驗,模型權重也確實會更新,訓練過程中的數值沒有立刻出現 NaN 或明顯失控。
但「程式能訓練」和「Agent 已經開始學會 Breakout」是兩件不同的事。
Day 13 只跑了 10,000 個環境步數,一共完成 48 局遊戲。這種規模很適合抓程式錯誤,卻很容易把幾局遊戲的隨機波動誤認成模型真的在進步。
所以 Day 14 的問題很單純:
如果 10K 還不足以判斷模型是否真的在學,那就把實驗拉到 100K;但在跑十倍更久之前,先確認目前的訓練流程沒有把時間浪費掉。
整天的流程可以濃縮成:
10K:確認訓練程式沒壞
↓
要觀察學習,需要更長的 100K
↓
長跑前先整理訓練流程
↓
固定系統設定
↓
只改學習率,跑 100K
↓
看每局分數、數值診斷與真實遊戲畫面
↓
把「看起來有進步」交給 Day 15 正式評估
10K 很適合快速確認幾件事:Replay Buffer 有沒有資料、模型有沒有更新、探索比例有沒有照預定方式下降,以及損失值、Q-value、目標值與梯度是否維持正常。
但它不適合拿來排誰好誰壞。
Breakout 的獎勵不是每一步都有,而且每一局長度不同。假設某個設定在 10K 的平均每局回報是 1.15,另一組是 1.75,這個差距可能只是最後幾局剛好打得比較好,還不能說參數真的造成穩定差異。
因此 Day 14 把兩種用途分開:
這裡的「每局回報(Return)」就是一整局累積起來的獎勵,也可以把它理解成這局到底打得多好。
100K 也不是「一定能學會 Breakout」的門檻。它只是把觀察時間拉長,讓少數幾局的運氣比較不容易主導結論。
這張圖最重要的地方不是「100K 一定要選出勝者」,而是反過來:如果 100K 仍然沒有可靠差異,「目前無法分辨」本身就是合理的實驗結果。
DQN 的卷積神經網路(CNN)確實在 RTX 4060 上計算,但完整的訓練流程不只有神經網路。
每一步大致還要經過:
遊戲環境產生新畫面
↓
模型選擇動作
↓
把這次互動寫進 Replay Buffer
↓
從 Replay Buffer 抽出一批資料
↓
模型計算預測、學習目標、誤差
↓
反向傳播並更新權重
其中遊戲環境和大量 Python 控制流程仍然在 CPU 上執行。
所以:有使用 CUDA,不代表 GPU 就會一直忙。
Day 14 先從最明顯的資料路徑開始檢查:Replay Buffer。
原本的 Replay Buffer 把過去的互動資料存在 CPU 的 NumPy 陣列。模型需要學習時,才從裡面抽一批資料,再送進 GPU。
GPU-resident Replay 的做法則是:讓 Replay Buffer 直接常駐在 GPU 記憶體。
這樣訓練時可以直接在 GPU 內抽樣、取資料,再送進 DQN 更新,不需要每次都把整批訓練資料從 CPU 搬過去。
如果只測「資料已經在 GPU 後,模型更新能有多快」,結果很好:大約可以處理 39K~41K 筆訓練樣本/秒。
但這只是局部測試,不是完整的 Breakout 訓練。
真正的訓練還包含:
所以我又做了一次真正的完整流程比較。固定批次大小 32、每 4 個環境步數更新一次、開始學習的步數、模型與隨機種子都相同,只改 Replay Buffer 放在 CPU 還是 GPU:
| Replay 方式 | 每秒環境步數 | 每秒模型更新次數 | 每秒訓練樣本 | 總耗時 | GPU 平均使用率 |
|---|---|---|---|---|---|
| CPU + 預先配置傳輸緩衝區 | 361.61 | 71.92 | 2,302 | 27.65 s | 23.87% |
| GPU-resident Replay | 338.13 | 67.25 | 2,152 | 29.57 s | 24.63% |
結果很明確:在目前單一 Breakout 環境、批次大小 32 的情況下,GPU Replay 沒有讓完整訓練更快。
原因不是 GPU 裡面抽資料很慢,而是新的互動資料仍然是由 CPU 上的遊戲環境產生。
也就是說,GPU Replay 雖然省掉了「訓練時整批資料從 CPU 搬到 GPU」的成本,卻多了另一件事:每一個環境步數,都要把一小筆新的互動資料寫進 GPU。
這些資料量本身不大,但次數很多。對 GPU 來說,很多次很小的搬運,不一定比少數幾次較大的搬運划算。
因此前面的 39K~41K 並沒有錯,只是它回答的是另一個問題:
局部測試
→ 資料已經在 GPU 時,模型更新可以多快?
完整訓練
→ 真正玩遊戲、收資料、寫 Replay、更新模型時可以多快?
目前的結果告訴我們:GPU 內部的模型更新很快,但整條訓練流程還沒有辦法把這個優勢完全發揮出來。
這也留下了 Day 16 的方向:如果之後要讓 GPU Replay 真正展現優勢,就不能只繼續優化 Replay 本身,而要處理「一次只跑一個遊戲環境、一次只替一個畫面選動作、每次只寫入一筆資料」這些問題。
Replay 的比較沒有直接帶來加速,但測量過程讓另一件事變得很清楚:訓練程式裡有一些工作根本不需要每一步都做。
原本每一步都會頻繁計算診斷資料、立刻把 CSV 寫入磁碟,CPU 執行緒數也沿用比較大的預設值。這些事情不會讓 Agent 多學一筆經驗,卻會讓 CPU、GPU 和 Python 更常互相等待。
在相同訓練設定、相同 10K 隨機種子下,把這些高頻工作減少後:
| 10K 測試 | 調整前 | 調整後 |
|---|---|---|
| 每秒環境步數 | 145.94 | 218.99 |
| 每秒模型更新次數 | 32.85 | 49.29 |
| 總耗時 | 68.52 s | 45.66 s |
| CPU 執行緒數 | 12 | 1 |
| 診斷 / CSV 寫入間隔 | 1 / 1 | 100 / 100 |
兩邊都完成 2,251 次模型更新,目標網路的同步次數也一致,主要訓練數值仍然正常。
每秒環境步數從 145.94 提高到 218.99,大約是 1.50 倍。
這種加速比較乾淨:沒有少做模型更新,也沒有偷偷降低訓練量,只是把不需要每一步做的工作降低頻率。
接著我又測了一個很直覺的想法:既然 GPU 使用率不高,把每次訓練使用的資料量放大,是不是就會更快?
這裡的「批次大小(batch size)」就是模型每次更新時,從 Replay Buffer 拿多少筆資料一起學。
我固定其他主要條件,只比較批次大小 32 / 64 / 128:
| 批次大小 | 每秒環境步數 | 每秒訓練樣本 | GPU 平均使用率 | GPU 記憶體峰值 |
|---|---|---|---|---|
| 32 | 235.74 | 1,698 | 30.13% | 1.76 GiB |
| 64 | 203.32 | 2,929 | 32.22% | 1.89 GiB |
| 128 | 177.36 | 5,110 | 34.88% | 1.97 GiB |
這組批次大小測試是另一輪固定條件的 10K 實驗,所以不要把它的絕對數字直接和上一張 Replay A/B 表橫向比較;這裡只比較同一張表內不同批次大小的相對結果。
結果很有意思:批次 128 的 GPU 使用率最高,每秒也處理最多訓練樣本,但整個遊戲訓練反而最慢。
原因是把批次放大,只是讓「每一次模型更新」做更多工作,卻沒有解決前面真正的等待:目前仍然只有一個遊戲環境,而且模型大部分時間一次只替一個畫面決定動作。
更重要的是,批次大小本身也會改變模型怎麼學,所以不能只因為 GPU 看起來比較忙,就把較大的批次當成更好的 DQN 設定。
這裡真正得到的結論只有一句:
在目前單一遊戲環境的訓練方式下,批次 32 的完整訓練速度最高;批次變大雖然能讓 GPU 一次處理更多資料,卻沒有讓整個 Breakout 訓練更快。
最後,我又固定批次大小 32,測試 1 / 2 / 4 個 PyTorch CPU 執行緒,每秒環境步數分別是 290.08 / 306.79 / 304.15,因此後續採用 2 個執行緒。
到這裡就停止改系統設定。
因為接下來如果一邊改學習率,一邊又換 Replay、批次大小、CPU 執行緒數,最後曲線有差異時,就不知道到底是哪一項造成的。
現在才回到真正的強化學習問題。
學習率(learning rate)控制的是:每次模型根據誤差調整權重時,要走多大一步。
太小可能學得很慢;太大則可能讓模型的價值估計變得不穩定。
這次一次只改學習率:
| 設定 | 學習率 | 其他主要條件 |
|---|---|---|
| 基準 | 1e-4 |
GPU Replay、批次32、隨機種子42、探索規則、目標網路同步、獎勵裁切、FP32 固定 |
| 較低 | 5e-5 |
同上 |
| 較高 | 2e-4 |
同上 |
這裡三組都固定使用 GPU Replay,不是因為它已經證明比 CPU Replay 更快,而是因為這一輪要固定同一條資料路徑,才能把「學習率」留成主要變因。
三組先跑 10K 做健康檢查,全部正常後才進 100K。
100K 期間也不只保存最後結果,而是保留中途模型與紀錄,讓 25K、50K、75K、100K 都可以回頭查看。
統計方式也先固定:
三組都完整跑到 100,000 個環境步數:
| 設定 | 完成回合數 | 最近 20 局平均 | 中位數 | 最佳 20 局移動平均 | 近期變化 | 每秒環境步數 | 總耗時 |
|---|---|---|---|---|---|---|---|
基準 1e-4 |
364 | 5.15 | 5.50 | 5.15 | -0.30 | 358.47 | 278.96 s |
較低 5e-5 |
389 | 1.80 | 2.00 | 2.90 | -0.20 | 387.92 | 257.78 s |
較高 2e-4 |
308 | 9.15 | 8.00 | 9.15 | -0.10 | 390.60 | 256.02 s |
在這一次隨機種子 42 的實驗裡,2e-4 的後段表現明顯高於另外兩組。
這比 10K 時的結果有資訊得多,因為現在看到的是較長一段學習過程,而不是少數幾局的短期平均。
但仍然只能得到這個結論:
在目前這組條件下,
2e-4是值得下一階段繼續驗證的候選設定。
它還不能被叫作「最佳學習率」,因為現在只有一個訓練隨機種子。
圖中的淡色點是每一局實際得到的回報,粗線則是 20 回合移動平均。
這張圖最重要的不是「2e-4 贏了」,而是:跑到 100K 之後,三條學習曲線終於開始出現值得追蹤的差異。
只看每局分數還不夠。
DQN 內部還有幾個重要數值:
如果學習真的開始數值失控,常見情況會是:價值估計越來越誇張、誤差越拉越大、損失值與梯度也一起持續升高,最後甚至出現無效數值。
這次 100K 沒有看到這條完整的失控鏈。
三組的 Q-value 與目標值確實都上升:
| 設定 | Q 平均:25K → 100K | 目標值平均:25K → 100K |
|---|---|---|
基準 1e-4 |
0.751 → 1.364 | 0.731 → 1.373 |
較低 5e-5 |
0.555 → 1.155 | 0.553 → 1.193 |
較高 2e-4 |
0.513 → 1.722 | 0.499 → 1.755 |
較高學習率那組的價值估計上升得最快,但「Q-value 變大」本身不能直接當成錯誤,也不能直接當成學得比較好。
到 100K 附近,三組平均絕對 TD 誤差約為:
0.054
0.033
0.044
較高學習率那組全程最大的絕對 TD 誤差約為 1.18,主要數值都仍然是正常有限值。
因此目前比較合理的判讀是:2e-4 讓價值估計變化得更快,值得繼續監看,但現在還沒有足夠證據說它已經數值不穩定。
另外,這組設定的 epsilon 在前 10K 就下降到 0.05,後面 90K 大多維持低探索比例。這不是這次比較的變因;如果之後要研究探索速度,應該另外做只改 epsilon 下降速度的實驗。
曲線和診斷數據都是數字,但它們看不到擋板到底有沒有開始對球做出比較合理的反應。
所以 Day 14 另外保存不同訓練階段的模型,使用同一套方式重新跑 Breakout,再把畫面錄成 GIF。
為了讓 1K、10K、50K、100K 之間可以比較,錄影時固定:
epsilon = 0,也就是完全不加入隨機探索,只使用模型當下認為最好的動作。結果是:
0
0
4
7
這些都只是同一個隨機種子、單一回合的結果,所以不能拿來取代 Day 15 的正式評估。
但它們可以回答另一件事:不同訓練階段的模型,實際在畫面上做出了什麼行為?
1K 與 10K 還看不到穩定得分;50K 已經可以看到磚塊出現缺口;100K 則清掉更多磚塊,並在 363 個評估步數後結束。
這些 GIF 是「行為證據」,不是正式成績。
看起來比較會玩,不代表它在不同隨機種子、不同回合下都比較強。
看到 100K 的分數還不算高,很容易直接猜:是不是 CNN 或中間的隱藏層不夠大?
目前還不能這樣判斷。
如果 50K 到 100K 的分數仍然往上走,第一個合理動作通常是繼續觀察更長的訓練,而不是立刻把模型放大。
只有當:
訓練跑得更久
↓
分數長時間停在差不多的位置
↓
訓練數值又都正常
↓
不同合理參數設定也都卡在相近低分
才比較有理由懷疑模型容量不足。
如果未來真的要測模型大小,也應該一次只改一個地方,例如把隱藏層大小比較成 256 / 512 / 1024,而不是一次把整個 CNN 和全連接層全部放大。
Day 14 現在看到的是:100K 比 10K 更明顯地出現了學習跡象,而不是模型容量已經撞牆。
走到這裡,前面的系統測試、100K 學習曲線、數值診斷與 GIF,其實可以收斂成兩個答案。
10K 只能證明訓練程式沒壞;拉到 100K 後,三種學習率才開始拉開。
在這一次隨機種子 42 下,2e-4 的後段分數較高,而且 Q-value、TD 誤差與梯度沒有形成明顯的數值失控鏈。
所以它可以成為下一階段的候選設定,但還不能被叫作最佳設定。
Day 15 會把模型固定下來,用固定的評估隨機種子、完全不探索的策略與 Atari 原始分數,正式比較 Random Policy(隨機策略)和 DQN。
GPU Replay 在「資料已經位於 GPU」的局部測試裡很快,但完整訓練沒有因此變快;把批次放大也只是讓 GPU 更忙,沒有提高整個遊戲訓練的速度。
真正值得處理的方向已經變得很具體:
一次只跑一個遊戲環境
+ 一次只替一個畫面選動作
+ 每次只寫入一筆 GPU Replay
所以 GPU Replay 不會因為這次完整比較沒有贏就被丟掉。
Day 16 會進一步改成:
多個遊戲環境並行
↓
一次替多個畫面決定動作
↓
一次寫入多筆 GPU Replay
也就是多環境並行(Vectorized Environments)搭配批次化的 GPU 訓練流程。
Day 14 最後真正學到的不是「哪個 GPU 設定最快」,也不是「哪個學習率一定最好」,而是先把兩個問題分開:
系統跑得快,不代表模型學得好;模型看起來在進步,也不能只靠一次實驗就宣布成功。