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 模型
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);
这段代码看着简单,但实际坑不少。比如输入张量的维度顺序、数据类型转换、图像预处理……我刚开始做的时候,光调输入格式就花了两天。
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 对显存有要求。如果模型太大,或者输入分辨率太高,可能会爆显存。我建议先用小模型试水,确认没问题再上生产。
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 推理流程。我画了一张图,帮你理清整体架构:
整个流程其实就三步:
- 采集:从摄像头获取视频帧
- 推理:用 ONNX Runtime Web 执行 AI 模型
- 发送:把推理结果通过 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();
这段代码我简化了很多细节,但核心逻辑就是这样的。实际项目中,你还需要考虑帧率控制、内存管理、错误恢复等问题。
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 的结合,能做出很多以前想都不敢想的产品。希望这些经验能帮你少走弯路。