Day 40 模型部署與系列回顧
引言
今天是這個系列的最後一天。我們會把前面 39 天學到的東西收尾:先用 torch.onnx.export 把模型匯出成 ONNX,再用 Streamlit 做一個簡單的展示介面,最後為整個系列做一個完整的回顧,並談談接下來可以怎麼繼續往前走。
這一篇的重點不只是「把程式碼跑起來」,而是理解「模型從訓練到被使用」這中間還需要哪些環節。當你能把模型變成一個能被同事、客戶或自己 demo 的成果,整個深度學習的學習歷程才真正完整。
匯出成 ONNX:跨框架的橋樑
ONNX(Open Neural Network Exchange)是一種開放的神經網路交換格式,把模型從 PyTorch 帶到其他框架或硬體時特別有用。常見情境:用 PyTorch 訓練、用 onnxruntime 在不同平台推論、或把模型送進 ONNX Runtime + TensorRT 做加速。
import torch
from src.model import build_model
# 載入最佳權重
model = build_model("resnet18", num_classes=10)
state = torch.load("checkpoints/best.pt", map_location="cpu")
model.load_state_dict(state)
model.eval()
# 準備一個虛擬輸入(batch=1, 3 通道, 32x32)
dummy = torch.randn(1, 3, 32, 32)
# 匯出成 ONNX(opset_version=17 是當時穩定的寫法)
torch.onnx.export(
model,
dummy,
"checkpoints/svhn_resnet18.onnx",
input_names=["input"],
output_names=["logits"],
opset_version=17,
dynamic_axes={
"input": {0: "batch"},
"logits": {0: "batch"},
},
)
print("已匯出到 checkpoints/svhn_resnet18.onnx")
幾個關鍵參數值得說明:第一,input_names 與 output_names 會寫進 ONNX 模型,之後用其他框架載入時就能用對應的名字取得輸入與輸出;第二,dynamic_axes 讓 batch 維度變成動態,這樣模型可以接受任意大小的 batch,不用每次重新匯出;第三,opset_version=17 是當時穩定的版本,舊版本可能不支援某些新算子,新版本則可能有相容性問題,選 17 通常是最安全的選擇。
用 onnxruntime 驗證匯出結果
匯出後,務必用 onnxruntime 載入跑一次,確認輸出和 PyTorch 原版一致。這個步驟很常被忽略,但它能抓出很多「PyTorch 跑得好好的,匯出後數值卻不對」的問題。
import numpy as np
import onnxruntime as ort
# 用 PyTorch 推論
torch_out = model(dummy).detach().cpu().numpy()
# 用 ONNX Runtime 推論
session = ort.InferenceSession("checkpoints/svhn_resnet18.onnx")
onnx_out = session.run(None, {"input": dummy.numpy()})[0]
# 比對兩個輸出
print("PyTorch 預測類別:", int(torch_out.argmax()))
print("ONNX 預測類別 :", int(onnx_out.argmax()))
print("最大差異 :", float(np.abs(torch_out - onnx_out).max()))
正常情況下,最大差異應該在 1e-5 以下,這代表 ONNX 模型與原始 PyTorch 模型在數值上幾乎完全一致。如果差異偏大,可能是匯出時某個算子被轉換成低精度的近似,這時就要檢查模型中是否有不常見的自訂層。
用 Streamlit 做展示介面
Streamlit 是 Python 生態裡最受歡迎的快速展示工具之一,只要幾十行程式碼就能做出一個可以讓使用者上傳圖片、看到預測結果的網頁。下面的範例會把剛剛匯出的 ONNX 模型接上來,做一個簡單的 SVHN 數字辨識 demo。
import streamlit as st
import numpy as np
import onnxruntime as ort
from PIL import Image
# 載入 ONNX 模型(全域變數,整個 app 共用一份)
@st.cache_resource
def load_model(path: str = "checkpoints/svhn_resnet18.onnx"):
return ort.InferenceSession(path)
session = load_model()
st.title("SVHN 門牌數字辨識 Demo")
st.write("上傳一張 32x32 的門牌號碼圖片,看模型預測的數字。")
uploaded = st.file_uploader("選擇圖片...", type=["png", "jpg", "jpeg"])
if uploaded is not None:
img = Image.open(uploaded).convert("RGB").resize((32, 32))
arr = np.asarray(img).astype(np.float32) / 255.0
arr = (arr - np.array([0.4377, 0.4438, 0.4728])) / np.array([0.1980, 0.2010, 0.1970])
arr = arr.transpose(2, 0, 1)[None, :] # (1, 3, 32, 32)
logits = session.run(None, {"input": arr})[0][0]
probs = np.exp(logits) / np.exp(logits).sum()
pred = int(probs.argmax())
st.image(img, caption=f"預測數字:{pred}(信心 {probs[pred]:.2%})", use_column_width=True)
啟動方式很簡單:在終端機執行 streamlit run app.py,瀏覽器就會自動打開 http://localhost:8501 看到介面。整支程式不到 30 行就有上傳、預測、信心分數顯示等基本功能,這正是 Streamlit 的魅力。@st.cache_resource 確保 ONNX 模型只載入一次,重新整理頁面不會重複讀檔。實務上也可以用 st.progress、st.metric、st.columns 等元件把結果展示得更精緻,加上 st.sidebar 放說明文字,整個介面就會顯得相當完整。對沒有前端經驗的 Python 開發者來說,Streamlit 幾乎是最低成本的展示解法。
部署選項與後續考量
把 Streamlit app 跑起來只是第一步,要讓別人也能用,還需要考慮部署。常見選項包括:
- Streamlit Community Cloud:免費託管,把程式碼 push 到 GitHub 就能自動部署,適合 demo 與小型專案。
- Docker + 雲端服務:把 Streamlit 包進 Docker image,部署到 AWS、Azure、GCP 或自家機房,彈性高但維運成本也較高。
- 整合到既有後端:把 ONNX 模型用 FastAPI 包成 REST API,讓前端或行動裝置呼叫。這種方式最適合正式產品。
不論選哪種方式,有幾件事值得提前準備:把 requirements 寫成 requirements.txt、把模型權重放進同一個 repo 或物件儲存、加上基本的輸入檢查與錯誤處理、為關鍵流程加上 log。這些工作看起來瑣碎,卻是模型從「能跑」變成「能用」的關鍵。
系列回顧:40 天學到了什麼?
從 Day 1 的 Python 環境設定,到 Day 40 的模型部署,我們走完了一條完整的「Python → 資料 → 數學 → PyTorch → 深度學習專案」學習路徑。回顧整個系列,可以分成七個階段:
- 第一階段:Python 基礎(Day 3–14):運算子、條件判斷、迴圈、函式、字串、容器、檔案、模組與物件導向。
- 第二階段:資料處理(Day 15–19):NumPy 運算、Pandas 清理與合併。
- 第三階段:資料視覺化(Day 20–22):Matplotlib、Seaborn、完整的 EDA 流程。
- 第四階段:數學基礎(Day 23–25):線性代數、微積分與梯度、機率與統計。
- 第五階段:PyTorch 入門(Day 26–31):張量、自動微分、資料載入、第一個神經網路、訓練迴圈、影像分類實作。
- 第六階段:經典模型(Day 32–36):CNN、遷移學習、RNN 與 LSTM,並各做了一個實作專案。
- 第七階段:專案實戰(Day 37–40):從資料、模型、評估一路到部署,把整個深度學習專案走完一遍。
這條路徑的設計精神是:先把 Python 與資料處理的基礎打穩,再進入深度學習的理論與實作,最後用一個端到端的專案把所有環節串起來。完成這個系列後,你應該已經具備獨立完成一個中小型深度學習專案的能力——從資料探索、模型訓練,到評估與部署。
值得再次強調的是,深度學習專案的成功並不全靠模型。資料清理、任務定義、評估設計、部署細節這些「看不見的工作」,往往才是真正決定成果能不能上線的關鍵。整個系列裡,每一篇都在提醒這件事:沒有 Day 11 的檔案讀寫,就沒有 Day 28 的 DataLoader;沒有 Day 24 的梯度觀念,就沒有 Day 30 的訓練迴圈;沒有 Day 39 的評估分析,就沒有 Day 40 的部署信心。把這些觀念內化,之後不論遇到什麼樣的任務,都能快速判斷該把心力放在哪裡。
結語
這一篇同時也是整個 40 天系列的收尾。回頭看,從 Python 的基本語法一路走到模型部署,我們其實只用了最少的數學與框架 API,就把深度學習的核心流程(資料 → 模型 → 評估 → 部署)走了一遍。接下來的路很長,但地基已經打好了。
下一步可以怎麼走?
40 天只是一個起點。深度學習的世界還有很多值得探索的方向,依個人興趣可以選擇不同的延伸路線:
- 自然語言處理進階:認識 Transformer 與注意力機制,學會使用 Hugging Face 的預訓練模型(BERT、GPT 等),進到生成式 AI 的世界。
- 電腦視覺進階:認識物件偵測(YOLO、Faster R-CNN)、語意分割(U-Net、Mask R-CNN)等任務,並動手做影像生成(VAE、GAN、Diffusion Model)。
- 模型最佳化與 MLOps:學習模型量化、剪枝、知識蒸餾等加速技巧,並把模型部署到行動裝置或邊緣裝置;同時培養 MLflow、DVC、Airflow 等 MLOps 工具的使用習慣。
- 參加社群與實作:Kaggle 競賽、Hugging Face Spaces、GitHub 開源貢獻,都是把所學變成實戰的好地方。
不論選擇哪條路線,今天所建立的 Python 與 PyTorch 基礎都會持續派上用場。最重要的是保持動手做的習慣:閱讀官方資源、讀程式、跑模型、修正錯誤,每一輪迭代都會讓自己更接近能獨立解決真實問題的能力。這個系列到這裡告一段落,但你的深度學習旅程才剛開始。
感謝你陪著這個系列走完 40 天。當你下次打開 Jupyter notebook 開始寫模型時,希望這系列累積的觀念與程式碼能成為你最好的參考書。祝你在深度學習的路上,做出自己真正想做的東西。
留言
張貼留言