18、WebRTC与AI模型部署:ONNX Runtime Web、WebGPU推理、模型量化、边缘计算与浏览器端推理

说实话,WebRTC 做到一定程度,你就会发现——光传音视频已经不够了。用户想要的不只是「看到对方」,而是「看懂画面」、「听懂声音」。比如实时翻译、背景替换、表情识别、手势控制……这些能力,都得靠 AI 模型来撑。

但问题来了:AI 模型通常跑在服务器上,延迟高、成本大、还有隐私风险。能不能把模型直接塞进浏览器,在客户端本地推理?

能。而且这条路,我这两年一直在趟。

18.1 浏览器端推理的三大挑战

先泼盆冷水。浏览器不是服务器,它资源有限、环境复杂。想把 AI 模型塞进去,你得面对三个硬骨头:

  • 算力有限:浏览器拿不到 GPU 的全部能力,只能用 WebGL 或 WebGPU 这些「借来的」接口。
  • 模型体积:一个 ResNet-50 模型 100MB 起步,用户打开网页就下载 100MB?不现实。
  • 推理速度:视频是 30fps 的流,你推理一帧要 200ms,那画面就卡成幻灯片了。

我最早做浏览器端人脸检测时,用的还是 TensorFlow.js,模型 20MB,推理一帧要 150ms。后来换了 ONNX Runtime Web,配合 WebGPU,直接压到 15ms。差距就是这么大。

18.2 ONNX Runtime Web:跨平台推理引擎

ONNX Runtime Web 是微软开源的项目。它能把 ONNX 格式的模型直接跑在浏览器里。底层支持 WebGL、WebAssembly、WebGPU 三种后端。

我个人习惯用 WebGPU 后端,因为它的性能最接近原生。但要注意,WebGPU 目前只在 Chrome 和 Edge 上完整支持。Safari 和 Firefox 还在路上。

核心思路:训练时导出 ONNX 模型 → 用 ONNX Runtime Web 加载 → 在浏览器中执行推理。

下面是一个最简单的推理示例:

// 加载 ONNX 模型
const session = await ort.InferenceSession.create('./model.onnx');

// 准备输入数据(假设是图像张量)
const inputTensor = new ort.Tensor('float32', imageData, [1, 3, 224, 224]);

// 执行推理
const outputs = await session.run({ input: inputTensor });

// 获取结果
const result = outputs.output.data;
console.log('推理结果:', result);

这段代码看着简单,但实际坑不少。比如输入张量的维度顺序、数据类型转换、图像预处理……我刚开始做的时候,光调输入格式就花了两天。

小技巧:ONNX 模型导出时,建议用 opset 版本 15 以上,兼容性更好。另外,输入输出节点的名字一定要记清楚,别在代码里写错。

18.3 WebGPU 推理:把 GPU 用起来

WebGPU 是新一代的浏览器图形 API,比 WebGL 更底层、更高效。它支持计算着色器,非常适合做 AI 推理的矩阵运算。

为什么 WebGPU 比 WebGL 快?说白了,WebGL 是为渲染设计的,做通用计算是「跨界打工」。WebGPU 从一开始就支持 compute shader,数据搬运和计算效率都高得多。

我做过一个对比测试:同样的 MobileNetV2 模型,WebGL 推理耗时 45ms,WebGPU 只要 12ms。差距接近 4 倍。

启用 WebGPU 后端很简单:

const session = await ort.InferenceSession.create('./model.onnx', {
  executionProviders: ['webgpu']
});

但要注意,WebGPU 对显存有要求。如果模型太大,或者输入分辨率太高,可能会爆显存。我建议先用小模型试水,确认没问题再上生产。

注意:WebGPU 目前还不是所有浏览器都支持。生产环境一定要做 fallback 方案,比如降级到 WebGL 或 WASM。

18.4 模型量化:把模型「瘦身」

模型量化,说白了就是把模型里的浮点数换成整数。比如从 FP32 变成 INT8,模型体积直接缩小 4 倍,推理速度也能提升 2-3 倍。

你想想看,一个 100MB 的模型,量化后只有 25MB。用户打开网页,下载时间从 10 秒变成 2.5 秒,体验完全不一样。

量化的方式主要有两种:

量化类型 精度损失 体积缩小 适用场景
FP16 几乎无损失 50% 高精度要求
INT8 轻微损失 75% 大多数场景
INT4 明显损失 87.5% 极低资源场景

我一般推荐 INT8 量化。精度损失在 1-2% 以内,人眼基本看不出差别。但速度提升非常明显。

量化后的模型,在 ONNX Runtime Web 里加载方式不变:

// INT8 量化模型,加载方式一样
const session = await ort.InferenceSession.create('./model_int8.onnx');

嗯,这里要注意:量化后的模型对输入数据的范围有要求。比如 INT8 模型期望输入在 [0, 255] 之间,你如果传了 [0, 1] 的浮点数,结果会完全不对。我踩过这个坑,排查了整整一个下午。

18.5 边缘计算:把推理放在离用户最近的地方

边缘计算的概念,说白了就是「别把数据往云端传,在本地处理」。WebRTC 本身就是点对点的,天然适合边缘计算。

我做过一个项目:在浏览器端做人脸关键点检测,然后把关键点数据通过 WebRTC 的 DataChannel 传给对方。整个过程不经过服务器,延迟不到 10ms。

边缘计算的好处很明显:

  • 低延迟:本地推理,没有网络传输时间
  • 隐私安全:视频数据不出设备
  • 降低成本:不需要 GPU 服务器
  • 离线可用:断网也能跑

但缺点也有:浏览器算力有限,复杂模型跑不动。所以边缘计算适合做轻量级推理,比如人脸检测、手势识别、语音命令等。重模型还是得放服务器。

18.6 实战:WebRTC 视频流 + 实时 AI 推理

把上面这些技术串起来,就是一个完整的 WebRTC + AI 推理流程。我画了一张图,帮你理清整体架构:

WebRTC + AI 浏览器端推理架构 摄像头 视频帧采集 预处理 缩放/归一化 ONNX Runtime WebGPU 推理 量化模型 INT8 后处理 解析结果 WebRTC 发送 DataChannel / 视频流 远端接收 渲染/叠加结果 边缘计算:本地推理 数据不出设备,隐私安全

整个流程其实就三步:

  1. 采集:从摄像头获取视频帧
  2. 推理:用 ONNX Runtime Web 执行 AI 模型
  3. 发送:把推理结果通过 WebRTC 传给对方

下面是一个完整的代码骨架:

// 1. 获取视频流
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
const video = document.createElement('video');
video.srcObject = stream;
await video.play();

// 2. 加载 ONNX 模型
const session = await ort.InferenceSession.create('./face_landmark_int8.onnx', {
  executionProviders: ['webgpu']
});

// 3. 创建 Canvas 用于帧提取
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');

// 4. 推理循环
async function inferenceLoop() {
  // 绘制当前帧
  ctx.drawImage(video, 0, 0, 224, 224);
  const imageData = ctx.getImageData(0, 0, 224, 224);

  // 转换为张量
  const tensor = new ort.Tensor('float32', preprocess(imageData), [1, 3, 224, 224]);

  // 执行推理
  const result = await session.run({ input: tensor });

  // 解析结果
  const landmarks = parseLandmarks(result.output.data);

  // 通过 DataChannel 发送
  dataChannel.send(JSON.stringify({ landmarks }));

  requestAnimationFrame(inferenceLoop);
}

inferenceLoop();

这段代码我简化了很多细节,但核心逻辑就是这样的。实际项目中,你还需要考虑帧率控制、内存管理、错误恢复等问题。

经验之谈:推理循环不要用 setInterval,用 requestAnimationFrame 更合适。它能和浏览器的渲染周期同步,避免不必要的性能浪费。

18.7 避坑指南

做浏览器端 AI 推理,坑确实不少。我挑几个最常见的说说:

  • 模型加载超时:ONNX 模型文件可能很大,加载时间超过 30 秒会被浏览器杀掉。建议用 IndexedDB 做缓存,第二次加载直接从本地读。
  • WebGPU 上下文丢失:用户切换标签页或锁屏时,WebGPU 上下文可能丢失。需要监听事件并重新创建 session。
  • 内存泄漏:每次推理都会创建张量对象,如果不及时释放,内存会持续增长。我习惯用对象池来复用张量。
  • 跨域问题:模型文件如果放在 CDN,需要配置 CORS 头。否则浏览器会拒绝加载。

我曾经在一个项目中,因为忘了处理 WebGPU 上下文丢失,导致用户在看视频时突然画面卡死。排查了半天才发现是这个问题。从那以后,我所有涉及 WebGPU 的代码都会加上上下文恢复逻辑。

18.8 性能调优建议

最后,给你几个性能调优的方向:

优化方向 具体做法 预期提升
模型量化 FP32 → INT8 速度提升 2-3 倍
输入分辨率 降低到 160x160 速度提升 2 倍
跳帧推理 每 2 帧推理一次 速度提升 2 倍
WebGPU 后端 替代 WebGL 速度提升 3-4 倍
模型剪枝 去掉冗余层 体积缩小 30-50%

我个人建议的调优顺序是:先量化,再降分辨率,最后考虑跳帧。因为量化对精度影响最小,跳帧会影响体验的流畅度。

好了,这一章的内容就到这里。浏览器端 AI 推理是个很有意思的方向,WebRTC 和 AI 的结合,能做出很多以前想都不敢想的产品。希望这些经验能帮你少走弯路。


公众号:蓝海资料掘金营,微信deep3321