浏览器也能跑本地 AI:用 Transformers.js + WebGPU 做一个最小推理 Demo,cpolar 给同事远程体验

发布时间:2026/9/7 20:25:26
浏览器也能跑本地 AI:用 Transformers.js + WebGPU 做一个最小推理 Demo,cpolar 给同事远程体验 浏览器也能跑本地 AI用 Transformers.js WebGPU 做一个最小推理 Democpolar 给同事远程体验我准备写这个 Demo 时先被一个名字带偏了huggingface/kernels。候选资料把它描述成“提供 WebGPU 内核的 JavaScript 包”但我查了 Hugging Face 的官方仓库结论并不是这样Kernel Hub 当前对应的是 Python 包kernels官方快速开始要求torch2.5和 CUDA示例也是from kernels import get_kernel。浏览器里用 GPU 跑模型真正对得上的官方 JavaScript API 是huggingface/transformersTransformers.js。这篇把题目纠正成一个能落地的版本不用服务器推理不上传输入用一个固定句子做情感分类浏览器优先走 WebGPU失败时切到 WASM CPU。跑通后再用 cpolar 只分享这个静态体验页。1 先把 Hugging Face 两个“kernels”概念分开huggingface/kernelsGitHub 仓库的定位是 Kernel Hub从 Hugging Face Hub 加载计算内核官方 README 给出的安装命令是pip install kernels它不是本文要用的浏览器依赖也不能写成npm install huggingface/kernels后在页面里调用 WebGPU。这个事实一定要先讲清楚否则读者会在第一条命令处卡住。本文的浏览器链路是huggingface/transformers ONNX Runtime Web。Transformers.js 官方文档给出的 GPU 用法就是在pipeline的第三个参数里设置{ device: webgpu }同一个 pipeline 也支持不传device使用浏览器 WASM 后端。WebGPU 也不是“有 Chrome 就必定可用”。页面需要navigator.gpu、浏览器实现以及可用的 GPU 适配器。Firefox、Safari 和旧版 Chromium 的支持状态并不完全相同所以 Demo 必须准备降级路径。2 环境准备Node.js 静态服务和支持 WebGPU 的浏览器2.1 创建最小项目只需要 Node.js 提供静态文件服务不安装模型服务也不需要账号。创建目录和文件mkdir -p hf-webgpu-demo cd hf-webgpu-demo touch index.html server.mjs把服务绑定到127.0.0.1是为了让 cpolar 映射前先锁住本机入口代码中没有文件上传、资料保存和对话记录。2.2 写一个固定输入页面下面的页面使用官方文档示例中的Xenova/distilbert-base-uncased-finetuned-sst-2-english做英文情感分类。第一次运行会从 Hugging Face Hub 下载模型文件到浏览器缓存页面本身不会把用户输入发送到本地服务为了避免演示变成资料收集工具输入框使用readonly只保留固定样例。!doctype html html langzh-CN head meta charsetutf-8 meta nameviewport contentwidthdevice-width, initial-scale1 titleWebGPU 本地推理 Demo/title style body { max-width: 760px; margin: 40px auto; padding: 0 18px; font: 16px/1.7 system-ui, sans-serif; } textarea { width: 100%; box-sizing: border-box; padding: 12px; } button { margin: 12px 0; padding: 9px 16px; cursor: pointer; } #status { white-space: pre-wrap; background: #f4f6f8; padding: 12px; border-radius: 8px; } /style /head body h1浏览器本地情感分类/h1 p固定样例不上传、不保存真实资料。/p textarea idinput rows3 readonlyWebGPU makes this small demo surprisingly useful./textarea button idrun开始推理/button pre idstatus等待开始/pre script typemodule import { pipeline } from https://cdn.jsdelivr.net/npm/huggingface/transformers4.2.0; const input document.querySelector(#input); const runButton document.querySelector(#run); const status document.querySelector(#status); const model Xenova/distilbert-base-uncased-finetuned-sst-2-english; async function loadAndRun(device) { status.textContent 正在加载模型后端${device}第一次需要下载模型文件; const classifier await pipeline(sentiment-analysis, model, device webgpu ? { device: webgpu } : {}); const result await classifier(input.value); return result[0]; } runButton.addEventListener(click, async () { runButton.disabled true; const started performance.now(); try { let device webgpu; if (!navigator.gpu) device wasm; let output; try { output await loadAndRun(device); } catch (error) { if (device ! webgpu) throw error; status.textContent WebGPU 初始化失败改用 WASM CPU。; output await loadAndRun(wasm); device wasm; } const elapsed Math.round(performance.now() - started); status.textContent JSON.stringify({ device, label: output.label, score: Number(output.score.toFixed(6)), elapsed_ms: elapsed }, null, 2); } catch (error) { status.textContent 推理失败${error.message}\n请检查浏览器控制台和网络连接。; } finally { runButton.disabled false; } }); /script /body /html这里有两个容易忽略的点。navigator.gpu只能做能力初筛不能保证模型初始化一定成功所以代码仍然用try/catch包住 WebGPU pipeline如果页面卡在“加载模型”先看开发者工具的 Network 和 Console而不是反复点击按钮。elapsed_ms只是本次页面生命周期的粗略耗时包含模型加载时间不能拿它当严谨性能基准。想测推理速度应先预热模型再单独统计多次推理。3 启动并验证先在本机看结果3.1 写本地静态服务器server.mjs使用 Node.js 内置模块不引入第三方依赖import { createServer } from node:http; import { readFile } from node:fs/promises; import { extname, join, normalize } from node:path; import { fileURLToPath } from node:url; const root fileURLToPath(new URL(., import.meta.url)); const types { .html: text/html; charsetutf-8, .js: text/javascript; charsetutf-8 }; const server createServer(async (req, res) { const requestPath req.url / ? /index.html : req.url; const filePath normalize(join(root, requestPath)); if (!filePath.startsWith(root)) { res.writeHead(403); res.end(Forbidden); return; } try { const body await readFile(filePath); res.writeHead(200, { Content-Type: types[extname(filePath)] ?? application/octet-stream }); res.end(body); } catch { res.writeHead(404); res.end(Not Found); } }); server.listen(8080, 127.0.0.1, () { console.log(Demo: http://127.0.0.1:8080); });运行node server.mjs浏览器打开http://127.0.0.1:8080点击“开始推理”。成功时页面会显示device、label、score和elapsed_ms首轮下载模型等待时间较长是正常的。若浏览器不支持 WebGPU页面会直接显示wasm这不是报错而是明确的 CPU 降级结果。3.2 浏览器支持范围怎么判断Transformers.js WebGPU 指南明确提醒WebGPU 在不少浏览器中仍处于实验阶段官方文档还给出了约 70% 的全球支持率数据该页面注明数据截至 2024 年 10 月。这类数字会随时间变化部署前应以目标浏览器实际检测为准。本文选择 Chromium 系浏览器做演示并保留 WASM 兜底。若要确认 GPU 适配器是否真的可申请可以在控制台执行const adapter await navigator.gpu?.requestAdapter(); console.log({ webgpu: Boolean(navigator.gpu), adapter: Boolean(adapter) });adapter为null时不要把问题归咎于模型先检查浏览器版本、系统 GPU 驱动和浏览器的 WebGPU 开关。远程同事看到wasm也不奇怪GPU 能力取决于访问者自己的浏览器和设备。4 用 cpolar 临时分享体验页本地验证成功后才有必要让同事远程打开。cpolar 在这里仅负责把127.0.0.1:8080变成临时 HTTPS 入口不参与模型推理也不接触任何真实资料。先按 cpolar 官方下载页完成安装和账号绑定再在运行静态服务的终端执行cpolar http 8080官方命令行文档的 HTTP 示例就是这种写法。终端输出公网地址后把地址发给同事即可免费随机地址会变化本文只做短时验收不把它当固定服务地址。安全边界要守住页面只提供固定样例textarea是只读没有上传入口和业务 API。cpolar 只映射 HTTP 静态服务不映射终端、模型管理端口或本机目录。验收结束后回到运行 cpolar 的终端按CtrlC再停止node server.mjs。不要把包含密钥、Cookie、个人资料的文件放进 Demo 目录。如果公网页面打不开按这个顺序查本机http://127.0.0.1:8080是否正常、静态服务是否还在、cpolar 终端是否显示在线地址。页面能打开但推理失败时优先看访问者浏览器 Console模型下载和 WebGPU 初始化都发生在访问者浏览器里。5 总结这次真正跑通的是一个不依赖后端推理服务的浏览器 Demo固定句子在页面中交给 Transformers.js优先调用 WebGPU初始化失败或环境不支持时切到 WASMcpolar 只在最后短时分享静态体验入口。与此同时也把kernelsPython 包和浏览器端 Transformers.js 的职责区分开了。huggingface/kernels官方快速开始是pip install kernels要求 PyTorch 与 CUDA不是本文的 JavaScript WebGPU 包。浏览器端使用huggingface/transformers的pipelineAPIWebGPU 配置为{ device: webgpu }。本地服务绑定127.0.0.1cpolar 隧道按需启动体验结束立即关闭。这个小页面适合做浏览器 GPU 能力验收不适合直接包装成面向所有设备的稳定推理服务。要继续扩展时可以换成官方标记为transformers.js的模型但仍要保留能力检测、WASM 降级和临时分享的安全边界。参考资料Hugging Face kernels 官方仓库Transformers.js 官方仓库Transformers.js WebGPU 指南MDN WebGPU APIcpolar 官网下载页cpolar HTTP 隧道官方文档入口