导语:模型跑在浏览器与边缘的答案
AI 推理不一定都在云端 GPU 集群。隐私敏感(人脸/语音/文档)、离线可用、低延迟(无网络往返)场景,越来越需要模型在浏览器、边缘节点或用户设备上直接运行。WASM 给出了关键答案:一套编译产物(ONNX Runtime 编译到 WASM)同时跑在浏览器、Cloudflare 边缘、移动端,代码零重写,且天然沙箱安全。
本文系统讲 WASM AI 推理:先拆解「为什么 WASM 适合推理」,再深入 ONNX Runtime Web 三后端架构(WASM/SIMD/WebGPU),覆盖模型转换与量化、WASI-NN 提案、边缘部署、浏览器实时应用与性能基准,最后给选型矩阵。
前置:/wasm-introduction-architecture/(WASM 基础)、/wasm-simd-high-performance/(SIMD)、/wasm-edge-computing-cdn/(边缘部署)、/wasm-javascript-interop/(JS 互操作)。
目录
- 1. 为什么用 WASM 跑推理
- 2. ONNX Runtime Web 架构:三后端
- 3. 模型准备:转换与量化
- 4. 浏览器端推理实战
- 5. WASI-NN:服务器端的推理提案
- 6. 边缘推理部署
- 7. 实时应用场景
- 8. 性能基准与优化
- 9. 选型矩阵
- 10. 速查表
- 延伸阅读
1. 为什么用 WASM 跑推理
1.1 WASM 推理的优势
| 优势 | 说明 |
|---|---|
| 一套代码多端跑 | 同一模块在浏览器/边缘/桌面运行 |
| 隐私 | 数据不出设备/不出区域 |
| 离线 | 断网仍可用 |
| 低延迟 | 无网络往返,本地执行 |
| 沙箱安全 | 多租户边缘节点隔离 |
| 免安装 | 浏览器即运行时 |
1.2 与原生/云端的对比
云端 GPU:延迟高(网络)、成本高、数据出域
原生 SDK:需按平台分别编译分发、安装
WASM :一套 .wasm + 少量 JS 胶水 → 处处运行
1.3 适用边界
适合:小到中型模型(<100MB)、推理而非训练、
延迟/隐私敏感、需跨平台分发
不适合:大模型训练、超大 LLM(百GB 参数,需流式/分片时另议)
一句话总结:WASM 推理 = 一套代码 + 隐私 + 离线 + 低延迟 + 沙箱——适合中小模型推理与跨平台分发,避开云端往返与平台碎片化。
2. ONNX Runtime Web 架构:三后端
2.1 ORT Web 整体架构
ONNX Runtime Web 分层:
JS API(onnxruntime-web)→ 调度器 → 后端执行器
后端(按可用性与性能选择):
1. WebAssembly(CPU 通用,最兼容)
2. WebAssembly + SIMD(CPU 向量化,显著提速)
3. WebGPU(GPU 加速,需浏览器支持)
4. WASM + threads(多线程推理,需 COOP/COEP)
2.2 选择后端的执行器
// 用 WASM + SIMD 后端
import * as ort from 'onnxruntime-web';
const session = await ort.InferenceSession.create('./model.onnx', {
executionProviders: ['wasm'],
graphOptimizationLevel: 'all', // 图优化
enableCpuMemArena: true,
enableMemPattern: true,
});
// SIMD 自动启用:wasm + simd 变体按 capability 选择
2.3 SIMD / threads / WebGPU 逐级加速
执行层加速阶梯:
WASM :基准(x1)
+ SIMD :向量化,2-4x(大部分推理算子受益)
+ threads :多核并行,可再 2-4x(需 SharedArrayBuffer)
+ WebGPU :GPU 并行,进一步提速(矩阵类算子)
内存限制也关键:ORT Web 支持 onnx 权重外部数据文件
一句话总结:ORT Web 是「后端可插拔」架构——WASM→+SIMD→+threads→+WebGPU 逐级提速,JS API 不变,按浏览器能力自动选择最佳执行器。
3. 模型准备:转换与量化
3.1 转换到 ONNX
# PyTorch → ONNX
import torch
model.eval()
dummy = torch.randn(1, 3, 224, 224)
torch.onnx.export(model, dummy, 'model.onnx',
input_names=['input'], output_names=['output'],
opset_version=17, dynamic_axes={'input': {0: 'batch'}})
3.2 量化:让模型适合 WASM
量化是 WASM 推理的关键优化:
FP32 → FP16 / INT8:体积减半至 1/4,速度提升
WASM CPU 上 INT8 算子往往比 FP32 快且省内存
onnxruntime 量化:
python -m onnxruntime.quantization
QuantizationMode.IntegerOps / QDQ
动态量化:激活仍 FP32,仅权重 INT8(无精度损失显著)
3.3 体积与内存优化
1. 权重外部化:model.onnx + model.onnx.data(拆分大权重)
2. 裁剪/蒸馏:在训练阶段减小模型
3. 图优化:graphOptimizationLevel=all 移除冗余算子
4. 删除 metadata 与调试信息
目标:<10MB 的模型适合浏览器秒级加载
一句话总结:模型准备三步——torch.onnx.export 转 ONNX、量化到 INT8/FP16 减体积提速、权重外部化与图优化控内存——量化往往是 WASM 上推理提速的最大杠杆。
4. 浏览器端推理实战
4.1 完整推理流程
<script type="module">
import * as ort from 'onnxruntime-web';
// 1. 加载模型
const session = await ort.InferenceSession.create(
'./model.onnx',
{ executionProviders: ['wasm'], graphOptimizationLevel: 'all' },
);
// 2. 准备输入(Tensor)
const input = new Float32Array(1 * 3 * 224 * 224);
// ... 填充预处理后的像素数据
const feeds = {
input: new ort.Tensor('float32', input, [1, 3, 224, 224]),
};
// 3. 推理
const results = await session.run(feeds);
const output = results.output.data; // Float32Array 概率分布
// 4. 后处理:argmax 取类别
let maxIdx = 0;
for (let i = 1; i < output.length; i++) if (output[i] > output[maxIdx]) maxIdx = i;
console.log('预测类别索引:', maxIdx);
</script>
4.2 摄像头实时分类
async function classifyFrame(video) {
// 从 video 绘制到 canvas → getImageData → 预处理
const data = preprocess(video); // 归一化到 [-1,1]
const res = await session.run({ input: new ort.Tensor('float32', data, [1,3,224,224]) });
updateUI(softmax(res.output.data)); // 展示 top5
requestAnimationFrame(() => classifyFrame(video)); // 循环
}
4.3 加载策略与交互
1. 懒加载:进入页面后再 fetch wasm/onnx(避免阻塞首屏)
2. 缓存:wasm 二进制用 Cache API 缓存(二次访问秒开)
3. 预加载小模型:内嵌 base64 或 import.meta.url 预取
4. 加载进度 UI:fetch 流式 + 进度条
5. 后端降级:无 WebGPU 自动回退 WASM
一句话总结:浏览器推理 = 创建 session → 构造 Tensor → run → 后处理;配懒加载、Cache 缓存与后端降级,实时应用用 rAF 循环配合 session 复用。
5. WASI-NN:服务器端的推理提案
5.1 WASI-NN 是什么
WASI-NN:把「神经网络推理」标准化为 WASI 接口
- 模型加载 / 推理 / 卸载 的统一 API
- 后端可插拔:OpenVINO、ONNX、PyTorch、TensorFlow
- 让同一 WASM 模块在不同运行时跑不同推理后端
5.2 WASI-NN 的接口形态
// Rust 侧通过 wasi-nn crate 调用
use wasi_nn::{GraphBuilder, GraphEncoding, ExecutionTarget, Backend};
let graph = GraphBuilder::new(GraphEncoding::Onnx, ExecutionTarget::Cpu)
.build_from_bytes(&model_bytes)?; // 加载模型
let tensor = Tensor { dimensions: &[1, 3, 224, 224], r#type: TensorType::F32, data };
let context = graph.init_execution_context()?; // 建立执行上下文
context.set_input(0, tensor)?; // 设置输入
context.compute()?; // 执行推理
let out = context.get_output(0)?; // 取输出
5.3 现状与选型
WASI-NN 现状(2026):
已被 Bytecode Alliance 采纳为候选标准
主要实现在 WasmEdge / Wasmtime(研究) 等运行时
适合:serverless 推理、边缘推理、跨运行时统一
对比 ORT Web:
WASI-NN:偏服务器端、后端可插拔(OpenVINO 等)
ORT Web:偏浏览器、执行器自动选择
一句话总结:WASI-NN 把推理标准化为 WASI 接口(load→init→set_input→compute→get_output),后端可插拔(ONNX/OpenVINO)——适合 serverless 与边缘的统一推理层。
6. 边缘推理部署
6.1 Cloudflare Workers + WASM 推理
// Cloudflare Worker 跑 WASM 推理(WASI-NN / 自建 ORT)
import { AutoRouter } from 'itty-router';
const router = AutoRouter();
router.post('/classify', async (req) => {
const image = await req.arrayBuffer();
const scores = await runModel(image); // WASM 推理
return Response.json({ topClass: argmax(scores) });
});
export default router;
6.2 边缘推理的价值
边缘节点就近推理:
- 延迟:用户→边缘节点(几十 ms,比回源中心近)
- 数据本地化:图片/文本不出区域
- 弹性:无冷启动(WASM 微秒级启动)
- 多租户隔离:每 Worker 独立沙箱
6.3 边缘部署要点
1. 模型打包进 Worker bundle(或绑定 Durable Object 缓存)
2. 限制模型体积(边缘节点内存/请求体配额)
3. 结果缓存(相同输入 → 命中边缘 KV)
4. 拆分「前端推理」与「中心微调」:小模型边缘、大模型中心
一句话总结:边缘推理用 WASM 微秒启动 + 就近延迟 + 数据本地化——小模型打包进 Worker、结果缓存、大模型留中心,形成分层推理。
7. 实时应用场景
7.1 场景矩阵
| 场景 | 模型 | WASM 后端 | 数据源 |
|---|---|---|---|
| 图像分类 | MobileNet/EfficientNet | WASM+SIMD | 摄像头/文件 |
| 目标检测 | YOLO-nano | WASM+WebGPU | 视频流 |
| 人脸关键点 | Face Landmark | WASM | 摄像头 |
| 语音识别 | Whisper 小模型 | WASM+threads | 麦克风 |
| 文本分类 | BERT-tiny | WASM | 输入框 |
| 姿态估计 | MoveNet | WASM+WebGPU | 摄像头 |
7.2 检测示例流程
// 目标检测:前处理 NMS + 框绘制
const res = await session.run({ input: tensor });
const boxes = postprocessNMS(res.outputs); // 解码 + NMS
drawBoxes(canvas, boxes); // 叠画到视频帧
7.3 交互式应用注意
1. session 单例复用(创建开销大,勿每次新建)
2. rAF 节流推理(不阻塞主线程,用 web worker)
3. 移动端降级:低端机自动减小输入尺寸
4. 能耗:WebGPU/多线程在移动端权衡功耗
一句话总结:实时应用把推理放进 web worker、session 复用、输入尺寸按设备降级——分类/检测/人脸/语音各场景用合适小模型在 WASM 上流畅运行。
8. 性能基准与优化
8.1 后端加速对比(示意)
| 配置 | 相对性能 | 说明 |
|---|---|---|
| WASM 纯 CPU | 1.0x | 基准 |
| WASM + SIMD | 2-4x | 向量化,首选 |
| WASM + threads | 4-8x | 多核,需 COOP/COEP |
| WebGPU | 8-20x+ | GPU,矩阵类算子显著 |
| 原生(对照) | 20-40x | 无 JIT 层,理论上限 |
8.2 优化清单
1. 量化:INT8 权重 → 体积 1/4、推理更快
2. 图优化:graphOptimizationLevel=all
3. 线程:开启 threads + SIMD(先测 COEP 头)
4. 输入尺寸:动态 batch → 固定 batch(减调度开销)
5. 内存复用:Tensor 复用,避免反复分配
6. 算子裁剪:移除未用算子减小二进制
7. 预热:首帧前跑一次小输入预热 JIT
8.3 测量与预算
性能预算(实时场景):
单帧推理 < 16ms(60fps)或 < 100ms(交互可接受)
用 Performance API 测量:session.run 耗时 / 传输 / 预处理
瓶颈分析:预处理(JS 侧)常被忽略 → 也搬进 WASM 或 worker
一句话总结:加速阶梯 SIMD→threads→WebGPU,叠加量化与图优化——实时预算按帧耗时卡,预处理瓶颈也要搬进 WASM/worker。
9. 选型矩阵
| 场景 | 方案 |
|---|---|
| 浏览器端小模型 | ORT Web + WASM/SIMD |
| 浏览器端需要 GPU | ORT Web + WebGPU |
| 服务器端统一推理 | WASI-NN + WasmEdge |
| 边缘 serverless | Worker + WASM 推理 |
| 多核 CPU 推理 | WASM + threads |
| 隐私/离线 | WASM 本地推理(不触网) |
| 大模型 | 留中心/分片(WASM 不直接适合) |
混合架构:
前端小模型(分类/检测)→ ORT Web 本地跑
复杂请求 → 边缘 WASM 推理
超大模型 → 中心 GPU 集群
按「延迟/隐私/模型大小」分层选端
一句话总结:选型按「端」分层——浏览器用 ORT Web(WASM→WebGPU)、服务器用 WASI-NN、边缘用 Worker 推理,大模型留中心,形成端边云分层。
10. 速查表
| 需求 | 方案 |
|---|---|
| 浏览器推理 | onnxruntime-web |
| CPU 提速 | SIMD(自动启用) |
| 多核提速 | threads(需 COOP/COEP) |
| GPU 提速 | WebGPU 执行器 |
| 减体积 | INT8 量化 + 权重外部化 |
| 服务器统一推理 | WASI-NN |
| 边缘推理 | Cloudflare Worker + WASM |
| 实时不卡顿 | session 复用 + worker |
| 隐私/离线 | 纯本地推理 |
| 性能测量 | Performance API 帧耗时 |
一句话记忆:WASM AI 推理把模型变成「一套 .wasm 处处可跑」——ONNX Runtime Web 按「WASM→SIMD→threads→WebGPU」自动选后端逐级加速;模型准备用 torch.onnx.export 转 ONNX、INT8 量化减体积提速、权重外部化控内存;服务器端用 WASI-NN 把推理标准化(后端可插拔 OpenVINO/ONNX)、边缘用 Cloudflare Worker 就近推理保隐私低延迟;实时应用 session 复用 + worker + 输入降级;选型按端分层——浏览器 ORT Web、服务器 WASI-NN、大模型留中心——让推理跑在最合适的那一层。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。