标签:TensorFlow.js · WebGPU · YOLO-Nano · PWA · 零后端 · 隐私计算
----
1. 背景:为什么要在浏览器里做目标检测?
2024 年,公司做“AI 拍照识物”小程序,结果用户照片要先上传到云端,再返回结果。
•  隐私投诉:一天 200+ 工单,“我的照片会不会泄露?”
•  网络抖动:地铁里 3G 环境,上传 3 MB 图片失败率 30 %。
•  成本:GPU 推理 + CDN 流量,每月账单 6 万+。
于是老板一句话:“能不能让用户手机自己算?不准没关系,快就行!”
----
2. 技术选型:YOLO-Nano + WebGPU 的化学反应
方案    模型大小    首次加载    推理延迟    离线可用
YOLOv8-n    6.2 MB    2.1 s    38 ms    ✅
YOLO-Nano-JS    1.7 MB    0.8 s    24 ms    ✅
云端 YOLOv8-m    0 MB    0.1 s    120 ms    ❌
结论:YOLO-Nano-JS 完胜,在骁龙 8 Gen2 实测 24 ms / 帧。
----
3. 三步把 PyTorch 模型搬进浏览器
3.1 训练 & 蒸馏(Python 侧)

# train.py
from ultralytics import YOLO
model = YOLO('yolov8n.yaml')
model.train(data='coco128.yaml', epochs=50)

# 蒸馏到更小通道
from torch import nn
class NanoHead(nn.Module):
    def __init__(self, c1, c2):
        super().__init__()
        self.conv = nn.Conv2d(c1, c2//2, 1)  # 通道减半

模型量化后 1.7 MB(FP16 → INT8)。
3.2 转换格式

tensorflowjs_converter \
  --input_format=tf_saved_model \
  --output_format=tfjs_graph_model \
  --quantize_uint8=* \
  ./saved_model ./web_model

产出 model.json + group1-shard1of2.bin。
3.3 前端一行代码调用

<script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@latest"></script>
<script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs-backend-webgpu"></script>

<video id="webcam" autoplay playsinline></video>
<canvas id="canvas"></canvas>

<script type="module">
import * as cocoSsd from './yolo-nano-js';
await cocoSsd.load();                // 加载模型
const stream = await navigator.mediaDevices.getUserMedia({video: true});
document.getElementById('webcam').srcObject = stream;

setInterval(async () => {
  const predictions = await cocoSsd.detect(document.getElementById('webcam'));
  drawBoxes(predictions);            // 自己画框
}, 40);  // 25 FPS
</script>

----
4. 性能实测:千元机也不卡
机型    芯片    首次加载    单帧延迟    功耗
iPhone 13    A15    0.7 s    22 ms    380 mW
Redmi Note12    Snapdragon 4 Gen1    1.1 s    35 ms    520 mW
桌面 Chrome    RTX 3060    0.4 s    9 ms    28 W
----
5. 进阶技巧:让 PWA 像原生 App
5.1 Service Worker 缓存模型

// sw.js
const CACHE_NAME = 'ai-cache-v1';
const urlsToCache = [
  '/model.json',
  '/group1-shard1of2.bin',
  '/index.html'
];

self.addEventListener('install', event => {
  event.waitUntil(
    caches.open(CACHE_NAME).then(c => c.addAll(urlsToCache))
  );
});

5.2 Web Share Target API(接收外部图片)

// manifest.json
"share_target": {
  "action": "/receive",
  "method": "POST",
  "enctype": "multipart/form-data",
  "params": {
    "files": [{ "name": "image", "accept": ["image/*"] }]
  }
}

----
6. 隐私 & 安全:零数据出浏览器
•  零上传:所有推理在 WebGPU 计算管线完成。
•  零持久化:IndexedDB 仅缓存模型文件,用户一键清空。
•  零追踪:不调用任何第三方域名,通过 CSP 限制外链。
----
7. 踩坑记录
1.  WebGPU 兼容性:
早期 Safari TP 不支持 writeBuffer,降级 WebGL 后延迟翻倍。
解决:动态检测 navigator.gpu,不支持就弹提示。
2.  模型加载阻塞 UI:
1.7 MB 文件在 3G 网络要 6 s。
解决:使用 fetch + ReadableStream 分段加载 + 进度条。
3.  安卓 WebView 崩溃:
部分厂商 WebView 89 版本有 bug。
解决:UA 检测 + 强制跳转系统浏览器。
----
8. 开源仓库 & 在线体验
GitHub:
https://github.com/frontai/yolo-nano-js
在线 Demo(需支持 WebGPU):
https://frontai.github.io/yolo-nano-js/
----
9. 结语:前端的尽头是“端智能”
当模型只有 1.7 MB,
当浏览器原生支持 GPU 计算,
你会发现 “前端工程师”也能写 AI,而且不用买服务器。
如果这篇文章帮到你,欢迎 Star ⭐;
也欢迎留言分享你在浏览器里跑 AI 的奇思妙想!

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐