用 Pyodide 解鎖前端AI超能力
https://github.com/pyodide/pyodide
UpdatedMarch 24, 2026•3 min read
JJhihHao Wu**近期研究重點包含 AI Agent 的供應鏈攻擊、PII 偵測模型評估,以及醫療 AI 在臨床流程中的安全落地。
在這裡,我分享深度技術實測報告(如 NVIDIA NeMo, WildGuard)與職場技術成長心得,致力於在 AI 浪潮中打造具備資安韌性的解決方案。**Part of seriesAI 工具與模型評測
On this page
用 Pyodide 解鎖前端AI超能力實作:用鐵達尼號資料集訓練AI用Pyodide可以跑LLM/SLM嗎?瀏覽器裡跑Python,會不會有資安問題?
用 Pyodide 解鎖前端AI超能力
https://github.com/pyodide/pyodide
Pyodide是一個讓你能在瀏覽器中執行Python 程式碼的專案,背後的技術核心是WebAssembly,能讓瀏覽器執行接近原生速度程式碼的技術,像NumPy、Pandas、Scikit-learn這些我們平常常用的重量級套件,全部搬到了瀏覽器裡。
這樣有什麼好處?
零安裝、零設定:使用者不需要在自己的電腦上安裝Python或任何函式庫,想像一下,醫師只要打開一個網頁,就能使用最新的AI診斷工具,而不用去煩惱背後複雜的環境設定。
資料隱私與安全:所有運算都在使用者的瀏覽器中完成,數據完全不需要上傳到伺服器,對於處理高度敏感的數據來說,解決了隱私洩漏的風險。
離線運行:一旦網頁和必要的資料載入完成,即使斷網,應用程式依然可以繼續運作。
降低伺服器成本:傳統的AI應用,大量的運算都在後端伺服器上進行,特別是GPU的成本,如果可以把運算壓力部分轉移到使用者端,就可以大幅降低了後端伺服器的負擔與成本。
過去要開始一個AI的案子,光是協調伺服器規格、處理防火牆、確認網路,就大概去了半條命,而Pyodide提供新的的架構,好像可以讓我們想想看未來是不是能夠有不同的架構應用在AI產品的開發流程中。
實作:用鐵達尼號資料集訓練AI
首先,你需要一個基本的HTML檔案結構來載入Pyodide並執行我們的Python腳本。
<!DOCTYPE html>
<html>
<head>
<title>Pyodide AI in Browser</title>
<script src="https://cdn.jsdelivr.net/pyodide/v0.25.1/full/pyodide.js"></script>
</head>
<body>
<h2>在瀏覽器中用Pyodide訓練鐵達尼號生存預測模型</h2>
<p>請打開瀏覽器的開發者工具(F12)來看Python腳本的輸出結果。</p>
<div id="output"></div>
<script type="text/python">
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score
import micropip
# 由於瀏覽器環境的限制,我們需要從一個URL來載入資料
url = 'https://raw.githubusercontent.com/datasciencedojo/datasets/master/titanic.csv'
print("--- 1. 載入資料 ---")
# 直接用pandas從URL讀取CSV檔
df = pd.read_csv(url)
print("資料載入成功,前五筆資料:")
print(df.head())
print("\n--- 2. 資料前處理 ---")
# 選擇我們要用的特徵欄位
features = ['Pclass', 'Sex', 'Age', 'SibSp', 'Parch', 'Fare']
target = 'Survived'
df_processed = df[features + [target]].copy()
# 處理缺失值:用中位數填補年齡
df_processed['Age'] = df_processed['Age'].fillna(df_processed['Age'].median())
# 將性別轉換為數值 (0: male, 1: female)
df_processed['Sex'] = df_processed['Sex'].map({'male': 0, 'female': 1})
# 再次檢查是否有缺失值
print("缺失值處理後:")
print(df_processed.isnull().sum())
print("\n--- 3. 切分訓練集與測試集 ---")
X = df_processed[features]
y = df_processed[target]
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
print(f"訓練集大小: {X_train.shape}")
print(f"測試集大小: {X_test.shape}")
print("\n--- 4. 訓練隨機森林模型 ---")
# 建立模型
model = RandomForestClassifier(n_estimators=100, random_state=42)
# 訓練模型
model.fit(X_train, y_train)
print("模型訓練完成!")
print("\n--- 5. 評估模型 ---")
# 進行預測
y_pred = model.predict(X_test)
# 計算準確率
accuracy = accuracy_score(y_test, y_pred)
print(f"模型在測試集上的準確率: {accuracy:.4f}")
# 將結果顯示在網頁上
from pyodide.ffi import to_js
from js import document
output_div = document.getElementById("output")
output_div.innerHTML += f"<p><b>模型訓練完成!</b></p>"
output_div.innerHTML += f"<p>測試集準確率: <b>{accuracy:.4f}</b></p>"
</script>
<script>
async function main() {
let pyodide = await loadPyodide();
console.log("Pyodide is ready.");
// 執行 <script type="text/python"> 中的程式碼
await pyodide.runPythonAsync(document.querySelector('script[type="text/python"]').textContent);
}
main();
</script>
</body>
</html>
這段程式碼做了什麼?
載入Pyodide:透過CDN載入了Pyodide。
安裝與載入套件:在
<script type="text/python">標籤中,像在本地端一樣import pandas, numpy, sklearn等套件。讀取資料:因為在瀏覽器中沒有本機檔案系統的概念,我們直接用Pandas從一個公開的URL讀取鐵達尼號的CSV資料。
資料前處理:我們進行了標準的資料清理步驟,包括選擇特徵、用中位數填補年齡的缺失值,以及將性別(字串)轉換為數值,大部分的步驟都跟你在Jupyter Notebook裡做的一模模一樣樣。
模型訓練:使用Scikit-learn中的
RandomForestClassifier(隨機森林分類器)。評估與輸出:訓練完成後,我們在測試集上進行預測,並計算準確率。最後,透過
pyodide.ffi這個橋樑,我們能將Python中計算出的結果(準確率),直接傳遞給JavaScript,並將結果顯示在畫面上。
用Pyodide可以跑LLM/SLM嗎?
理論上可以在Pyodide裡安裝PyTorch或TensorFlow.js來跑模型,但試了幾個小模型的組合,效能都不是太好XD
主要原因我猜應該是多了幾層導致的,這邊我們改由Hugging Face所出的Transformers.js,由Hugging Face的團隊維護,可以讓你用極其簡單的方式,在瀏覽器裡直接載入並運行Hugging Face Hub上成千上萬個預訓練好的模型。
底層同樣是WebAssembly(使用ONNX Runtime Web),所以我們前面談到的所有優點,它都具備。
我們來實作一個現在利用LLM在醫療院所常見的案例「AI病歷摘要助理」,用一個小模型幫他快速生成摘要。
<!DOCTYPE html>
<html>
<head>
<title>Browser-based LLM Demo</title>
<style>
body { font-family: sans-serif; padding: 2em; }
#container { max-width: 800px; margin: auto; }
textarea { width: 100%; height: 150px; }
pre { background-color: #f0f0f0; padding: 1em; white-space: pre-wrap; word-wrap: break-word; }
</style>
</head>
<body>
<div id="container">
<h2>AI病歷摘要助理</h2>
<p>在下面的文字框中輸入或貼上病歷紀錄,然後點擊按鈕。AI模型將在您的瀏覽器中運行,並生成摘要,您的資料完全不會被上傳。</p>
<p id="status">狀態:點擊按鈕開始載入模型...</p>
<textarea id="inputText">
Patient presents with a two-week history of a persistent dry cough and progressive shortness of breath, especially on exertion. Patient denies fever, chills, or night sweats. Past medical history is significant for hypertension, managed with Lisinopril. Patient is a non-smoker. On examination, mild crackles were noted at the lung bases. Oxygen saturation is 94% on room air. Plan to order a chest X-ray and basic blood work.
</textarea>
<br><br>
<button id="summarizeBtn">生成摘要</button>
<h3>模型生成的摘要:</h3>
<pre id="outputText">...等待生成...</pre>
</div>
<script type="module" src="https://cdn.jsdelivr.net/npm/@xenova/transformers@2.17.1"></script>
<script>
// 設定讓 Transformers.js 不會從 Hugging Face 下載重複的模型檔案
// 這在開發時很有用
const status = document.getElementById('status');
const inputText = document.getElementById('inputText');
const outputText = document.getElementById('outputText');
const summarizeBtn = document.getElementById('summarizeBtn');
let summarizer = null; // 用來存放我們的模型
summarizeBtn.addEventListener('click', async () => {
if (!summarizer) {
status.textContent = '狀態:正在從Hugging Face載入摘要模型 (只需一次)...';
// 這是神奇之處:直接從Hugging Face Hub載入一個預訓練好的摘要模型
// Xenova/distilbart-cnn-6-6 是一個輕量且效果不錯的模型
const { pipeline } = await import('https://cdn.jsdelivr.net/npm/@xenova/transformers@2.17.1');
summarizer = await pipeline('summarization', 'Xenova/distilbart-cnn-6-6', {
progress_callback: (progress) => {
status.textContent = `狀態:模型載入中... ${Math.round(progress.progress)}%`;
}
});
status.textContent = '狀態:模型已就緒!';
}
outputText.textContent = '...AI思考中...';
// 使用載入好的模型來執行摘要任務
const summary = await summarizer(inputText.value, {
max_length: 50, // 摘要的最大長度
min_length: 20 // 摘要的最小長度
});
outputText.textContent = summary[0].summary_text;
});
</script>
</body>
</html>
好玩XD 且生成速度還滿快的。
醫師可以在完全離線的狀態下,使用這個工具來輔助他的日常工作,而不用擔心任何資料外洩的風險。同樣的模式,可以用在情緒分析(分析醫病對話的語氣)、問題回答(建立一個基於標準作業流程的問答機器人)各種NLP任務上,不過這個token數量還有待加強。
隨著Web GPU技術的普及和更多輕量化模型的出現,我們在瀏覽器裡能做的事情,應該會越來越超乎想像,也會重新定義我們開發AI應用思考脈絡。
瀏覽器裡跑Python,會不會有資安問題?
先說結論,在現代瀏覽器中,執行Pyodide的風險,遠低於你把資料上傳到一個不知名的雲端伺服器來的安全,主要是因為瀏覽器大多提供了沙盒(Sandbox)機制。
所有從網頁來的程式碼,不管是JavaScript還是透過WebAssembly執行的Python(也就是Pyodide),都被關在這個隔離室裡。它們可以使用的資源、可以存取的資料,都受到極其嚴格的控管。
大多數的沙盒都有下面這些限制:
無法存取本地檔案系統:在沙箱裡的Python程式碼,絕對無法跑去讀取你電腦的個人檔案、桌面上的文件或任何瀏覽器以外的資料。在前面的範例能讀取資料,是因為我們指定了一個公開的網路URL,再透過瀏覽器允許的網路請求(
fetch)功能去做的,而不是本地端的檔案存取。受限的網路通訊:沙箱裡的程式碼的所有的對外網路請求,都必須遵守瀏覽器的「同源政策(Same-Origin Policy)」,這是一個基礎且強大的安全屏障,防止惡意網站竊取你在其他網站的登入資訊。
與主機作業系統隔離:無法直接呼叫作業系統的底層API,想加密你的硬碟勒索比特幣?想打開你的攝影機?這些敏感操作的權限,都由瀏覽器掌控,並且需要使用者授權同意。
當然也不是完全沒有資安風險,最大的風險在於「阻斷服務攻擊(Denial of Service)」。惡意Python腳本可能會設計一個無限迴圈或消耗大量記憶體的計算,讓你當前的瀏覽器分頁卡死,不過目前的瀏覽器也大多丟這種惡億偵測,相對來說還是安全的,未來可以想見這樣的應用應該會冒出滿多的。


Top comments (0)