iT邦幫忙

2026 iThome 鐵人賽

DAY 22
0
AI Engineering

從零訓練到瀏覽器部署:30 天打造 Atari Breakout 強化學習 AI系列 第 22

Day 22|模型訓練完了,接下來呢?把 PyTorch 模型帶進 ONNX

  • 分享至 

  • xImage
  •  

Day 21,我們終於把長時間訓練告一段落,留下了最後要用的 Dueling Double DQN。

前面二十多天一直在想一件事:怎麼讓 Agent 學會玩 Breakout?

從今天開始,問題要換了。

接下來我要做的是一個網頁:左邊讓人自己玩 Breakout,右邊則讓訓練好的 RL Agent 同時玩。也就是說,這個模型不能永遠只待在 Python 和 PyTorch 裡,它最後得走進瀏覽器。

但這時就會遇到一個很實際的問題:

PyTorch 訓練出來的模型,要怎麼交給瀏覽器使用?

這就是 Day 22 的主題。

今天不做網頁,也不做 WebGPU。我先處理中間最重要的一步:把 PyTorch 模型匯出成 ONNX。


ONNX 是什麼?為什麼不能直接用 .pt

前面訓練模型時,我們一直使用 PyTorch,所以模型自然也存成 PyTorch 熟悉的格式,例如 .pt

這在 Python 裡完全沒問題,但到了別的環境就不一定了。

可以先把它想成:

格式 比較像什麼 適合在哪裡用
PyTorch .pt PyTorch 自己最熟悉的模型存檔 Python / PyTorch
ONNX .onnx 比較通用的神經網路描述格式 ONNX Runtime、瀏覽器等其他環境

ONNX 全名是 Open Neural Network Exchange

它不是一個新的 AI 模型,也不會讓模型突然變強。它做的事情比較像是:把原本只在 PyTorch 裡很好使用的神經網路,整理成其他執行環境也比較容易讀懂的格式。

所以今天不是重新訓練模型,也不是換演算法。

權重沒有重學,Dueling Double DQN 也沒有變成別的東西。只是把同一個已經訓練好的模型,換成比較適合接下來部署的格式。


但匯出之前,得先講清楚「模型到底看什麼」

這件事比副檔名本身更重要。

我們的 Agent 並不是直接把一張原始 Breakout 彩色畫面丟進神經網路。

前面的環境會先把畫面整理過:

  • 轉成灰階;
  • 縮成 84 × 84
  • 保留最近 4 張畫面,讓模型看得出球正在往哪裡移動。

所以模型真正看到的,其實是最近 4 張處理過的畫面。

程式裡會把它表示成:

4 張畫面 × 84 × 84

接著再把原本 0~255 的像素值縮到 0~1,才送進神經網路。

為什麼這件事要特別講?

因為如果 PyTorch 版本把畫面縮到 0~1,但未來瀏覽器版本卻直接把 0~255 丟進去,模型雖然一樣可以「跑」,結果卻可能完全不同。

同樣地,如果四張畫面的排列方式搞錯,也可能不會立刻報錯,但 Agent 的判斷就會歪掉。

所以部署模型時,不能只搬權重,連模型原本是怎麼吃資料的,也要一起保持一致。


一張圖看懂:從遊戲畫面到 Agent 的動作

Day 22:一張 Breakout 畫面如何一路變成 Agent 的下一個動作

這張圖可以從左往右看。

前兩步是在整理畫面,第三步才是真正的神經網路。

模型拿到最近 4 張畫面之後,會輸出 4 個數字。這 4 個數字就是前面一直提過的 Q-value:可以先把它理解成「模型覺得每個動作現在有多值得做」。

四個動作分別是:

  • 不動;
  • 發球;
  • 往右;
  • 往左。

例如某一瞬間模型算出:

不動   1.2
發球   0.4
往右   3.8
往左   2.1

那它就會選 3.8 最高的「往右」。

這也解釋了為什麼最後匯出的 ONNX 模型,不需要自己處理整個 Atari 遊戲。

ONNX 的工作很單純:

收到整理好的畫面 → 算出四個 Q-value。

遊戲怎麼跑、畫面怎麼整理、最後怎麼把動作送回遊戲,仍然由外面的程式負責。

這樣反而比較好理解,也比較好除錯。


實際把 PyTorch 模型轉成 ONNX

概念講完,真正匯出時做的事情其實不複雜。

我先載入 Day 21 留下來的最後模型,把它切到推論模式,然後準備一筆符合模型輸入格式的資料,交給 PyTorch 的 ONNX exporter。

大致上就是:

Day 21 的 PyTorch 模型
        ↓
告訴 exporter 模型的輸入長什麼樣
        ↓
匯出
        ↓
model.onnx

最後得到的 ONNX 模型,輸入仍然是 4 張 84 × 84 的畫面,輸出仍然是 4 個 Q-value。

換句話說:

格式換了,但模型做的事情沒有換。

這才是我們要的結果。


產生 .onnx 檔案,就代表成功了嗎?

還不能這樣說。

如果 exporter 沒有報錯,只能證明「檔案有寫出來」。檔案存在,不代表之後真的能正確使用。

所以匯出後,我至少先做兩層檢查。

第一層是用 ONNX 自己的 checker 檢查模型檔案,確認模型結構是合法的,輸入和輸出也符合預期。

這可以幫我們抓到像是模型檔損壞、輸入輸出定義有問題之類的錯誤。

但它有一個很重要的限制:

它只能告訴我們「這個 ONNX 檔案看起來是合法的」,不能告訴我們「ONNX 算出來一定和 PyTorch 一模一樣」。

所以我還另外保留了一批固定的 Breakout 遊戲狀態,並先記下原本 PyTorch 模型對這些狀態算出的 Q-value 和動作。

這些資料今天先留著,明天會派上用場。


為什麼 Day 22 還不能說「部署成功」?

因為今天其實只完成了:

PyTorch 模型
↓
ONNX 模型

我們還沒有真的拿 ONNX Runtime 去執行它。

這兩件事差很多。

ONNX 是模型檔案的格式;ONNX Runtime 才是真正負責把這個模型跑起來的程式。

所以目前我們知道的是:模型已經成功匯出,而且檔案結構沒有明顯問題。

但我們還不知道:

同一張 Breakout 畫面丟進 PyTorch 和 ONNX Runtime,兩邊最後會不會算出一樣的 Q-value、選到一樣的動作?

這正是下一篇 Day 23 要驗證的事情。


Day 22 做完之後,我得到什麼?

今天真正重要的成果,不只是多了一個 model.onnx

更重要的是,我開始把模型從「只能在訓練程式裡使用的東西」,變成「準備交給其他環境使用的東西」。

到這裡可以確認三件事:

  1. Day 21 的最終模型已經成功匯出成 ONNX。
  2. 模型需要的輸入,以及四個輸出代表的動作,都已經固定下來。
  3. 我們已經留下原本 PyTorch 的對照結果,下一步可以拿來檢查 ONNX Runtime 有沒有跑偏。

而今天刻意還沒有做的,是瀏覽器、WebGPU、Cloudflare 或 Human vs RL 畫面。

這些都還太早。

在把模型真正放進網頁之前,我想先回答一個更基本的問題:

換了執行模型的方式之後,它還是不是原本那個 Agent?

Day 23,就來驗證這件事。


上一篇
Day 21|訓練越久越好嗎?跑到 5M 後,我最後反而選了 2.5M 模型
下一篇
Day 23|換成 ONNX Runtime 之後,它還是同一個 Agent 嗎?
系列文
從零訓練到瀏覽器部署:30 天打造 Atari Breakout 強化學習 AI27
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言