1. 浏览器端深度学习的真实战场:为什么要在浏览器里跑模型
把深度学习模型塞进浏览器,这件事放在五年前还像个玩具。那会儿大家的态度很统一:训练在服务器,推理也在服务器,浏览器老老实实当个画界面的就行。但这几年情况变了,越来越多的产品开始把推理甚至轻量训练搬到浏览器端,TensorFlow.js 就是这股浪潮里最成熟的一套方案。它让 JavaScript 开发者不用碰 Python、不用搭后端服务,直接在网页里加载模型、处理数据、输出结果。
我最早接触 TensorFlow.js 是在一个图像分类的小项目上。当时的需求很简单:用户上传一张照片,前端直接给出分类结果,不想把图片传到服务器。原因有两个,一是隐私,二是延迟。图片上传再下载的往返时间,在移动网络下经常要一两秒,而本地推理只要几百毫秒。这个体验差距是实打实的。后来项目越做越多,从图像分类扩展到姿态估计、文本情感分析、实时目标检测,我逐渐意识到浏览器端深度学习不是"能不能做"的问题,而是"怎么做好"的问题。
TensorFlow.js 的核心价值在于它把模型推理这件事从服务端解放出来,交给用户的设备去完成。这带来几个直接好处:数据不出本地,隐私风险大幅降低;没有网络往返,响应更快;服务器成本下降,因为算力分摊到了每个用户的设备上。但代价也很明显:浏览器环境是个"受限的沙盒",算力、内存、线程调度都远不如原生环境,稍不注意就会踩坑。
这篇文章适合几类人看:一是前端工程师,想在项目里集成 AI 能力但不想碰后端;二是算法工程师,模型训练好了想部署到 Web 端;三是技术负责人,在评估浏览器端推理方案的可行性和成本。不管你之前有没有深度学习背景,我都会从架构讲到实操,把 TensorFlow.js 的内幕和避坑经验一次说清楚。
2. TensorFlow.js 架构内幕:三层结构到底怎么运转
2.1 从 API 层到后端层:一次推理请求的完整旅程
TensorFlow.js 的架构可以粗略分成三层:最上面是API 层,中间是引擎层,最底下是后端层。这三层各司其职,理解它们的分工,是排查性能问题和诡异 bug 的基础。
API 层就是你日常写代码用到的那部分,比如tf.tensor()、model.predict()、tf.image.resizeBilinear()这些。这一层是纯 JavaScript 实现的,负责把用户的调用翻译成引擎能理解的指令。它的设计目标是"像 NumPy 一样好用",所以 API 风格和 Python 版的 TensorFlow 高度一致,学过 Python 版的人几乎零成本迁移。
引擎层是中间调度中枢,负责管理张量的生命周期、内存分配、算子注册和后端选择。你写的每一个tf.xxx()调用,最终都会落到引擎层,由它决定这个算子该交给哪个后端去执行。引擎层还负责张量的引用计数,这一点非常关键——JavaScript 有垃圾回收,但 GPU 显存不归 GC 管,所以 TensorFlow.js 自己实现了一套内存管理机制,需要手动dispose()或者用tf.tidy()包裹。
后端层是真正干活的地方,也是性能差异的来源。TensorFlow.js 支持多种后端:WebGL、WASM、WebGPU,以及纯 CPU 的 JavaScript 后端。每个后端都实现了同一套算子接口,引擎层根据当前环境和配置选择最合适的那个。你在浏览器里跑同一个模型,用 WebGL 后端和用 WASM 后端,性能可能差好几倍,原因就在这里。
一次推理请求的完整流程是这样的:你调用model.predict(input),API 层把输入转成张量,引擎层检查后端状态、分配输出张量、把算子派发给后端,后端执行计算(可能在 GPU 上,也可能在 CPU 上),结果回传到引擎层,最后返回给你一个张量。整个过程听起来简单,但每一层都有坑。
2.2 后端选型:WebGL、WASM、WebGPU 到底怎么选
后端选型是 TensorFlow.js 项目里最重要的决策之一,选错了后面全是麻烦。我把这三个主流后端的特性整理成一张表,方便你对照自己的场景做判断。
| 后端 | 计算设备 | 优势 | 劣势 | 适用场景 |
|---|---|---|---|---|
| WebGL | GPU | 并行能力强,大矩阵运算快 | 精度受限于 float16/float32,部分算子缺失,显存管理复杂 | 图像类模型、卷积网络 |
| WASM | CPU | 精度完整,算子覆盖全,兼容性好 | 并行度有限,大模型慢 | 文本模型、小模型、精度敏感场景 |
| WebGPU | GPU | 性能最强,支持 compute shader | 浏览器支持还不普及,驱动兼容性参差 | 新项目、追求极致性能 |
WebGL 后端是 TensorFlow.js 最早支持、也是最成熟的 GPU 后端。它把张量运算映射成 WebGL 的着色器程序,利用 GPU 的并行能力加速。图像类模型在 WebGL 上通常表现最好,因为卷积运算天然适合 GPU。但 WebGL 有几个硬伤:一是精度问题,WebGL 1.0 只保证 float16 精度,虽然大多数实现支持 float32,但不是所有设备都靠谱;二是算子覆盖不全,一些冷门算子没有 GPU 实现,会回退到 CPU,导致性能断崖式下跌;三是显存管理,WebGL 的纹理数量有限,大模型容易爆显存。
WASM 后端走的是另一条路,它把 TensorFlow 的 C++ 算子编译成 WebAssembly,在 CPU 上执行。WASM 的优势是精度完整、算子齐全、兼容性极好,几乎所有现代浏览器都支持。它的短板是并行能力有限,虽然有 SIMD 和多线程支持,但和 GPU 的并行度不在一个量级。文本模型、小规模模型、对精度要求高的场景,WASM 是更稳妥的选择。
WebGPU 是这两年的新秀,它提供了更底层的 GPU 访问能力,支持 compute shader,性能上限比 WebGL 高不少。但 WebGPU 的浏览器支持还在铺开阶段,不同显卡驱动的兼容性也有差异。我的建议是:新项目可以尝试 WebGPU,但一定要保留 WebGL 或 WASM 作为降级方案。
提示:后端选择不是一锤定音的,可以在运行时动态切换。TensorFlow.js 提供了
tf.setBackend()和tf.ready(),你可以先尝试 WebGPU,失败再降级到 WebGL,最后兜底 WASM。
2.3 张量内存管理:为什么你的页面越跑越卡
张量内存管理是 TensorFlow.js 里最容易翻车的地方,没有之一。JavaScript 开发者习惯了 GC 自动回收,但 GPU 显存和 WASM 堆内存不归 GC 管,必须手动释放。如果你在循环里不断创建张量而不释放,页面内存会持续上涨,最后卡死或者崩溃。
TensorFlow.js 提供了两种内存管理方式。第一种是手动dispose(),每个张量用完就调tensor.dispose()。这种方式最直接,但容易漏掉,尤其是在异常分支里。第二种是tf.tidy(),它接受一个函数,函数里创建的所有张量在函数返回后自动释放,除非你显式返回它们。tf.tidy()是官方推荐的方式,能覆盖绝大多数场景。
// 手动 dispose,容易漏 const a = tf.tensor([1, 2, 3]); const b = tf.tensor([4, 5, 6]); const c = a.add(b); a.dispose(); b.dispose(); // c 还要用,先不释放 // tf.tidy,自动管理 const result = tf.tidy(() => { const a = tf.tensor([1, 2, 3]); const b = tf.tensor([4, 5, 6]); return a.add(b); // 只有返回值被保留,a 和 b 自动释放 });这里有个细节要注意:tf.tidy()只管理函数内部创建的张量,外部传入的张量不会被释放。如果你在 tidy 里引用了外部张量,它不会被回收,这是符合预期的,因为外部张量可能还要用。但如果你不小心在 tidy 里创建了张量又没返回,它会被释放,后续访问就会报错。
还有一个隐蔽的坑:tf.tidy()不能跨异步操作。如果你在 tidy 里写了await,tidy 的上下文会丢失,内存管理就失效了。异步场景下要么手动 dispose,要么用tf.engine().startScope()和endScope()手动管理作用域。
3. 算力调度实战:让模型在浏览器里跑得又快又稳
3.1 模型加载与预热:别让用户等在白屏前
模型加载是用户感知最强的环节。一个几 MB 的模型,在慢网络下可能要好几秒,这段时间如果页面白屏,用户直接就走了。我的做法是分三步走:先加载模型结构,再加载权重,最后做一次预热推理。
TensorFlow.js 支持两种模型格式:Layers 模型(JSON + 权重文件)和Graph 模型(model.json + 分片权重)。Layers 模型适合自己训练的场景,Graph 模型适合从 Python 转换过来的场景。加载方式也不一样:
// 加载 Layers 模型 const model = await tf.loadLayersModel('/models/my-model/model.json'); // 加载 Graph 模型 const model = await tf.loadGraphModel('/models/my-model/model.json');加载过程中可以监听进度,给用户一个进度条:
const model = await tf.loadLayersModel('/models/my-model/model.json', { onProgress: (fraction) => { console.log(`加载进度:${(fraction * 100).toFixed(1)}%`); updateProgressBar(fraction); } });预热推理这一步很多人会忽略,但它很重要。第一次推理时,后端需要编译着色器、分配显存、初始化算子,耗时可能是后续推理的好几倍。如果你不在加载后立即预热,用户第一次点击按钮时会感觉明显卡顿。预热的方法很简单,用一张全零的输入张量跑一次predict,然后 dispose 掉结果:
const warmupInput = tf.zeros([1, 224, 224, 3]); const warmupResult = model.predict(warmupInput); warmupResult.dispose(); warmupInput.dispose();注意:预热用的输入形状必须和实际推理一致,否则后端不会缓存对应的编译结果,预热就白做了。
3.2 批处理与并发:一次算多个比一个一个算快得多
浏览器端推理的一个常见误区是"一次处理一个样本"。GPU 的并行能力很强,一次算一个样本和一次算八个样本,耗时可能差不多。所以只要内存允许,尽量做批处理。
假设你在做一个实时分类的应用,摄像头每帧产生一张图片。如果你每帧都单独推理,GPU 的利用率很低。更好的做法是攒几帧一起推理,或者用一个固定大小的批次队列。当然,批处理会增加延迟,所以要权衡。对于实时性要求高的场景,批次大小设为 2 到 4 比较合适;对于离线处理场景,可以设大一些。
并发方面,TensorFlow.js 的推理调用默认是异步的,但底层后端不一定支持真正的并行。WebGL 后端的命令是排队执行的,你连续调两次predict,它们会依次在 GPU 上跑。WASM 后端如果开了多线程,理论上可以并行,但实际效果取决于算子实现。我的经验是:不要指望 TensorFlow.js 帮你做并发调度,自己控制好调用节奏更靠谱。
3.3 算子融合与图优化:让计算图更精简
TensorFlow.js 在加载 Graph 模型时会做一些图优化,比如常量折叠、算子融合。但这些优化是有限的,很多优化需要你在转换模型时就做好。从 Python 转换模型到 TensorFlow.js 格式时,可以用tfjs-converter工具,它支持一些优化选项:
tensorflowjs_converter \ --input_format=tf_saved_model \ --output_format=tfjs_graph_model \ --optimize_graph=true \ /path/to/saved_model \ /path/to/output--optimize_graph会做一些基础的图优化,比如去掉训练专用的算子、合并连续的 reshape。但更激进的优化,比如把 Conv + BatchNorm + ReLU 融合成一个算子,需要在训练框架里做,转换工具帮不了你。
还有一个实用技巧:如果你的模型里有大量小算子,考虑在 Python 端把它们合并成一个大算子。浏览器端的算子调度开销比原生环境大,算子数量多会明显拖慢速度。我做过一个对比,同一个模型,把 20 个小算子合并成 3 个大算子后,WebGL 后端的推理速度提升了将近 40%。
4. 生产级避坑实战:那些文档里不会写的教训
4.1 精度问题:为什么你的模型在浏览器里结果不对
精度问题是浏览器端深度学习最隐蔽的坑。同一个模型,在 Python 里跑结果正常,在浏览器里跑结果偏差很大,十有八九是精度问题。
WebGL 后端默认使用 float32 精度,但有些设备只支持 float16,或者驱动会把 float32 降级成 float16。float16 的精度范围小,大数会溢出,小数会下溢,累加运算的误差会累积。如果你的模型对精度敏感,比如涉及大量累加或者数值范围跨度大,WebGL 后端可能给出错误结果。
排查精度问题的方法是对比不同后端的结果。先用 WASM 后端跑一遍,再用 WebGL 后端跑一遍,如果结果差异明显,就是精度问题。解决办法有几个:一是切换到 WASM 后端,牺牲速度换精度;二是用tf.float32显式指定精度,但设备不支持的话还是会被降级;三是在模型设计上避免精度敏感的操作,比如用 layernorm 代替 batchnorm,用 log-sum-exp 技巧避免数值溢出。
// 检查当前后端的精度支持 const backend = tf.getBackend(); console.log(`当前后端:${backend}`); if (backend === 'webgl') { const testTensor = tf.tensor([1e-8, 1e8]); const result = testTensor.mul(testTensor); console.log('精度测试结果:', await result.data()); testTensor.dispose(); result.dispose(); }4.2 内存泄漏:页面跑十分钟就崩了怎么办
内存泄漏是生产环境的头号杀手。我遇到过一个案例:一个实时姿态估计的应用,用户用十分钟左右页面就崩溃。排查后发现是每帧都在创建新的张量,但没有释放。虽然每帧只泄漏几 MB,但一秒 30 帧,十分钟就是十几 GB,浏览器扛不住。
排查内存泄漏的工具是tf.memory(),它会返回当前张量数量、字节数等信息:
console.log(tf.memory()); // { numTensors: 42, numDataBuffers: 42, numBytes: 12345678, ... }如果numTensors持续上涨,说明有泄漏。定位泄漏点的方法是在关键位置打日志,观察张量数量的变化。常见的泄漏点包括:事件监听器里创建的张量没释放、异步回调里的张量没释放、异常分支里的张量没释放。
解决内存泄漏的核心原则是:谁创建,谁释放。如果一段代码创建了张量,它就要负责释放,除非它把张量返回给调用方。用tf.tidy()包裹同步代码是最省心的方式,异步代码则要手动管理。
提示:
tf.tidy()里不要写await,否则 tidy 会失效。异步场景用tf.engine().startScope()和tf.engine().endScope()手动管理。
4.3 跨域与资源加载:模型文件加载失败的排查思路
模型文件加载失败是新手最常遇到的问题。浏览器有同源策略,模型文件如果放在不同的域名下,需要服务器配置 CORS 头。常见的报错是Access to fetch at '...' from origin '...' has been blocked by CORS policy。
解决办法是在模型文件所在的服务器上配置Access-Control-Allow-Origin头。如果模型文件放在 CDN 上,大多数 CDN 都支持配置 CORS。如果模型文件放在本地开发服务器上,可以用http-server或者webpack-dev-server的代理功能。
还有一个坑是模型文件的路径问题。TensorFlow.js 加载模型时,会先加载model.json,然后根据里面的路径去加载权重分片。如果model.json里的路径是相对路径,它会相对于model.json的位置解析。如果你把model.json和权重文件放在不同目录,路径就会错。最稳妥的做法是把所有模型文件放在同一个目录下,用相对路径引用。
4.4 移动端适配:手机浏览器上的性能陷阱
移动端是浏览器端深度学习的主战场,但也是坑最多的地方。移动设备的 GPU 性能、内存容量、浏览器实现都和桌面端有差异,桌面端跑得好好的模型,到手机上可能直接崩。
第一个坑是显存限制。移动设备的显存通常只有几百 MB,大模型很容易爆。解决办法是量化模型,把 float32 权重转成 int8 或者 float16,模型体积能缩小到四分之一甚至更少。TensorFlow.js 支持加载量化模型,转换时加--quantize_float16或--quantize_uint8参数即可。
第二个坑是浏览器后台限制。移动浏览器在页面切到后台时会暂停 JavaScript 执行,如果你的推理逻辑依赖requestAnimationFrame,切回前台后可能会有一段时间的卡顿。解决办法是在visibilitychange事件里暂停和恢复推理循环。
第三个坑是发热和耗电。GPU 持续高负载会导致手机发热,发热后 GPU 会降频,推理速度下降。如果你的应用需要长时间运行,建议降低推理频率,或者在后端之间动态切换,比如温度高时切到 WASM 后端。
| 问题 | 现象 | 排查方法 | 解决方案 |
|---|---|---|---|
| 显存不足 | 推理报错或页面崩溃 | 查看tf.memory()的 numBytes | 量化模型、减小批次 |
| 后台暂停 | 切回前台后卡顿 | 监听 visibilitychange | 暂停/恢复推理循环 |
| GPU 降频 | 运行一段时间后变慢 | 监控推理耗时 | 降低频率、切换后端 |
| 精度异常 | 结果和预期不符 | 对比不同后端结果 | 切换 WASM、调整模型 |
5. 从开发到上线:一套可复用的工程化方案
5.1 模型转换与优化:Python 到 JavaScript 的完整链路
模型从 Python 到浏览器,中间要经过转换。TensorFlow.js 提供了tensorflowjs_converter工具,支持从 SavedModel、Keras H5、TFHub 等格式转换。转换命令的基本形式是:
tensorflowjs_converter \ --input_format=keras \ --output_format=tfjs_layers_model \ --quantize_float16 \ /path/to/model.h5 \ /path/to/output_dir转换过程中有几个关键决策。第一是模型格式,Layers 模型适合自己训练的模型,Graph 模型适合从其他框架转换的模型。第二是量化策略,float16 量化能减半体积,精度损失很小;uint8 量化能减到四分之一,但精度损失明显,适合对精度要求不高的场景。第三是分片大小,大模型的权重会被分成多个文件,分片太小会增加请求数,分片太大不利于并行加载,一般 4MB 到 8MB 比较合适。
转换完成后,一定要在浏览器里验证模型输出。用同一份输入数据,分别在 Python 和浏览器里跑一遍,对比输出差异。如果差异超过阈值,说明转换过程有问题,需要检查量化策略或者算子兼容性。
5.2 性能监控与降级策略:线上出问题怎么快速定位
生产环境必须有监控和降级。监控方面,至少要采集这几个指标:推理耗时、内存占用、后端类型、错误率。推理耗时可以用performance.now()打点,内存占用用tf.memory(),后端类型用tf.getBackend(),错误率用 try-catch 统计。
async function monitoredPredict(model, input) { const start = performance.now(); try { const result = await model.predict(input).data(); const duration = performance.now() - start; reportMetric('inference_duration', duration); reportMetric('memory_bytes', tf.memory().numBytes); return result; } catch (error) { reportMetric('inference_error', 1); throw error; } }降级策略方面,我一般会准备三套方案:WebGPU 优先,失败降级 WebGL,再失败降级 WASM。降级逻辑在应用启动时执行一次,把可用的后端列表存下来,后续推理直接用选定的后端。如果运行过程中某个后端持续报错,可以动态切换。
async function selectBackend() { const candidates = ['webgpu', 'webgl', 'wasm', 'cpu']; for (const backend of candidates) { try { await tf.setBackend(backend); await tf.ready(); console.log(`成功切换到 ${backend} 后端`); return backend; } catch (e) { console.warn(`${backend} 后端不可用,尝试下一个`); } } throw new Error('没有可用的后端'); }5.3 用户体验优化:让 AI 功能感觉不到"在计算"
浏览器端推理再快,也有延迟。用户点击按钮后等几百毫秒,体验就不够流畅。优化用户体验的核心思路是"让等待变得不可感知"。
第一个技巧是预加载和预推理。如果用户的操作路径可预测,可以提前加载模型、提前推理。比如一个图片编辑应用,用户打开编辑器时就可以在后台加载模型,用户选好图片时就可以开始推理,等用户点"应用"时结果已经算好了。
第二个技巧是渐进式展示。如果推理需要时间,可以先展示低精度结果,再逐步细化。比如图像分割,可以先展示粗略的掩码,再逐步优化边缘。用户看到东西在动,就不会觉得卡。
第三个技巧是 Web Worker。把推理放在 Worker 线程里,主线程继续响应交互,页面不会卡死。TensorFlow.js 在 Worker 里的用法和主线程基本一致,但要注意 Worker 里不能直接访问 DOM,结果要通过postMessage传回主线程。
// 主线程 const worker = new Worker('inference-worker.js'); worker.postMessage({ type: 'predict', data: imageData }); worker.onmessage = (e) => { const result = e.data; renderResult(result); }; // inference-worker.js importScripts('https://cdn.jsdelivr.net/npm/@tensorflow/tfjs'); let model; async function init() { model = await tf.loadLayersModel('/models/my-model/model.json'); } self.onmessage = async (e) => { if (e.data.type === 'predict') { const input = tf.tensor(e.data.data); const output = model.predict(input); const result = await output.data(); input.dispose(); output.dispose(); self.postMessage(result); } }; init();注意:Worker 里加载的 TensorFlow.js 和主线程是独立的实例,模型也要在 Worker 里单独加载。这会增加内存占用,但换来了主线程的流畅。
6. 踩过的坑与独家经验:这些教训值多少钱
6.1 那些年我遇到的诡异 bug 和排查过程
第一个诡异 bug 是"模型在 Chrome 上正常,在 Safari 上结果全错"。排查了两天才发现,Safari 的 WebGL 实现有个特性:它会把 float32 纹理降级成 float16,而且不报错。解决办法是检测 Safari 并强制使用 WASM 后端,或者在模型转换时就用 float16 量化,让精度损失在可控范围内。
第二个诡异 bug 是"页面刷新后第一次推理特别慢,第二次就正常了"。这个其实是预期行为,第一次推理要编译着色器。但用户不知道,他们只觉得"第一次点很卡"。解决办法是加载模型后立即做一次预热推理,把编译开销提前消化掉。
第三个诡异 bug 是"模型在本地开发环境正常,部署到线上就报错"。排查后发现是 CORS 问题,本地开发时模型和页面同源,线上部署时模型放在 CDN 上,跨域了。解决办法是给 CDN 配置 CORS 头,或者把模型文件放到同源服务器上。
第四个诡异 bug 是"内存持续上涨,但tf.memory()显示张量数量正常"。这个最隐蔽,最后发现是 WebGL 纹理泄漏。TensorFlow.js 的 WebGL 后端会缓存纹理,如果张量释放了但纹理没释放,显存会持续上涨。解决办法是定期调用tf.engine().endScope()强制清理,或者升级到最新版本的 TensorFlow.js,新版本修复了不少纹理泄漏问题。
6.2 性能调优的取舍:什么时候该放弃浏览器端方案
浏览器端深度学习不是万能的,有些场景就是不适合。我总结了几条判断标准:
如果模型超过 50MB,浏览器端加载和推理都会很吃力,建议放服务端。如果推理延迟要求低于 50ms,浏览器端很难稳定达到,建议放服务端或者用原生应用。如果模型需要频繁更新,浏览器端每次更新都要用户重新下载,体验不好,建议放服务端。如果目标设备是低端手机,GPU 性能弱、内存小,浏览器端推理可能还不如服务端快。
反过来,如果数据隐私要求高、网络不稳定、服务器成本敏感、推理频率低,浏览器端方案就很有优势。我做过一个项目,用户上传身份证照片做 OCR,因为涉及隐私,必须本地处理。用 TensorFlow.js 在浏览器里跑 OCR 模型,虽然比服务端慢一点,但用户接受度很高,因为数据不出本地。
6.3 工具链与调试技巧:我的日常工具箱
调试 TensorFlow.js 应用,我常用的工具和技巧有这么几个。
第一个是tf.memory(),前面提过,排查内存问题的利器。第二个是tf.getBackend(),确认当前后端。第三个是tf.env(),查看环境变量,比如WEBGL_VERSION、WEBGL_MAX_TEXTURE_SIZE等。第四个是 Chrome 的 Performance 面板,可以录制推理过程,看 GPU 利用率和耗时分布。
还有一个技巧是用tf.profile()做性能分析,它会返回每个算子的耗时:
const profile = await tf.profile(() => { return model.predict(input); }); console.log(profile.kernels);profile.kernels会列出每个算子的名称、耗时、后端等信息。如果某个算子耗时特别长,可以考虑优化它,比如换后端、合并算子、或者用更高效的实现。
提示:
tf.profile()本身有性能开销,只在调试时用,不要在生产环境常开。
7. 浏览器端深度学习的边界与可能性
TensorFlow.js 让我最兴奋的一点,是它把 AI 能力带到了每一个有浏览器的设备上。不需要安装、不需要后端、不需要网络,打开网页就能用。这种"零门槛"的分发能力,是原生应用和服务端方案都比不了的。
但边界也很清晰。浏览器是个沙盒,算力、内存、线程都受限。它适合做推理,不适合做训练;适合做轻量模型,不适合做大模型;适合做隐私敏感的场景,不适合做超低延迟的场景。认清这些边界,才能做出正确的技术选型。
我在实际项目里的体会是:浏览器端深度学习不是要取代服务端,而是补充服务端。它把一部分计算从云端拉到了端侧,降低了延迟、保护了隐私、节省了成本。但它也有自己的天花板,超过天花板的部分,还是要交给服务端。
最后分享一个小技巧:如果你的模型在浏览器里跑得不够快,先别急着换方案,试试把模型量化一下。float16 量化通常能提速 30% 到 50%,精度损失几乎可以忽略。这个投入产出比,比换后端、换框架都高。