Skip to content

transformers.js:在浏览器里跑模型

Ollama 解决了"本地",但还需要装一个后台服务。transformers.js 把 HuggingFace 的模型推到浏览器里跑,连 Node 都不需要——纯前端 + WebAssembly + WebGPU。

适合场景

  • 用户自己上传文档/图片做处理(隐私敏感);
  • 离线 PDF 阅读器、本地翻译插件、浏览器扩展;
  • RAG 应用的查询端嵌入(节省服务端调用);
  • 需要"零后端"演示页。

不适合场景

  • 大型对话模型(>3B 参数)——Web 端跑起来非常慢;
  • 重计算长任务——浏览器比原生慢 3-5 倍;
  • 需要稳定 throughput 的生产服务。

一、能跑什么

transformers.js(v3+)支持的任务覆盖:

  • 嵌入(embedding)—— bge-small-zhnomic-embed-text 等小模型,这是最好的入门点
  • 文本生成 —— Qwen2.5-0.5BSmolLM2-360M 这类小模型,做摘要/分类/简单问答;
  • 翻译 —— 多语言模型;
  • 图像分类 / OCR —— 用 Florence-2TrOCR
  • 语音转文字 —— whisper-tiny / whisper-base 在浏览器里就能跑;
  • 图像生成 —— 极小的 SD-Turbo 之类的,速度感人但能跑。

二、最小可运行:浏览器里嵌入一段中文

bash
npm i @huggingface/transformers
ts
import { pipeline } from '@huggingface/transformers';

// 第一次会下模型,缓存在 IndexedDB(约 50MB)
const embed = await pipeline(
  'feature-extraction',
  'Xenova/bge-small-zh-v1.5',
  { device: 'webgpu' },          // 没 WebGPU 自动 fallback 到 wasm
);

const result = await embed('前端 RAG 的核心是检索召回质量', {
  pooling: 'mean',
  normalize: true,
});

console.log(result.data);   // Float32Array(512)

512 维向量,可以直接塞进任何向量库做检索。

三、性能实测(M2 Pro / Chrome 130)

我用同一段 1000 字中文做嵌入,比较三种方式:

方式加载时间单次推理备注
transformers.js (wasm)~2.5s380ms无加速
transformers.js (webgpu)~2.5s95ms推荐
Ollama + nomic-embed(常驻)30ms本地最快
调 OpenAI text-embedding-3-small(网络)200ms加上网络往返

结论:纯客户端跑嵌入,95ms 已经完全可以做实时检索了。文本生成会差很多,3B 以下模型勉强能用。

四、和 Ollama 怎么分工

不要重复,直接看场景:

场景用啥
Chat 主对话Ollama 或云 API
RAG 文档嵌入(离线一次性建索引)Ollama
RAG 查询嵌入(用户每次提问)transformers.js——省去后端往返
用户本地文件不上传服务器transformers.js
浏览器扩展、Web App 完全离线transformers.js
公司内服务、有后台Ollama

实际产品里两者经常配合:服务端用 Ollama 建索引,前端用 transformers.js 做查询,全程不出公司网络。

五、模型加载与缓存

pipeline() 第一次调用会从 HuggingFace CDN 下载模型,存到 IndexedDB。第二次访问直接命中缓存,毫秒级加载。

清缓存(开发时常用):

ts
import { env } from '@huggingface/transformers';
await env.useBrowserCache(false);    // 临时禁用

或开发者工具 → Application → IndexedDB 删 transformers-cache

自托管模型避免 HF 不可访问

国内访问 HuggingFace 不稳定。可以:

  1. 把模型文件下载下来传到自家 CDN;
  2. 配置 env.remoteHost
ts
import { env } from '@huggingface/transformers';
env.remoteHost = 'https://your-cdn.com/models';
env.allowRemoteModels = true;
  1. 模型文件结构跟 HF 仓库一致即可。

六、WebGPU vs WASM 怎么选

浏览器WebGPU 支持
Chrome / Edge 113+
Safari 18+✅(macOS 15 起)
Firefox🚧 实验性,nightly 可用

代码里默认 device: 'webgpu',库会自动 fallback:

ts
const embed = await pipeline(
  'feature-extraction',
  'Xenova/bge-small-zh-v1.5',
  { device: 'webgpu', dtype: 'fp16' },   // dtype 在 webgpu 下可选 fp16,更快
);

WASM 模式下用 dtype: 'q8'(8-bit 量化)能让小模型快 2 倍,但精度稍降。嵌入任务通常没差。

七、写一个浏览器内 RAG 最小 Demo

html
<input id="question" placeholder="问点啥…" />
<button id="ask">检索</button>
<ul id="hits"></ul>

<script type="module">
  import { pipeline } from 'https://cdn.jsdelivr.net/npm/@huggingface/transformers';

  // 已经预先建好的"知识库"(演示用,真实场景是从 IndexedDB 取)
  const docs = [
    { id: 1, text: '前端流式渲染:SSE 与 WebSocket 的选型...' },
    { id: 2, text: 'Ollama 让你在本地跑开发助手...' },
    { id: 3, text: 'MCP 协议把 LLM 工具调用标准化...' },
    // ... 假设有 500 条
  ];

  // 准备阶段:把每条 doc 变成向量
  const embed = await pipeline(
    'feature-extraction',
    'Xenova/bge-small-zh-v1.5',
    { device: 'webgpu' },
  );

  for (const d of docs) {
    const r = await embed(d.text, { pooling: 'mean', normalize: true });
    d.vec = r.data;
  }

  document.getElementById('ask').onclick = async () => {
    const q = document.getElementById('question').value;
    const r = await embed(q, { pooling: 'mean', normalize: true });
    const qVec = r.data;

    // 余弦相似度(已 normalize,等于点积)
    const ranked = docs
      .map((d) => ({
        ...d,
        score: d.vec.reduce((sum, v, i) => sum + v * qVec[i], 0),
      }))
      .sort((a, b) => b.score - a.score)
      .slice(0, 3);

    document.getElementById('hits').innerHTML = ranked
      .map((h) => `<li>${h.score.toFixed(3)} — ${h.text}</li>`)
      .join('');
  };
</script>

这个 demo 全部跑在浏览器里,连接断网都能用。500 条 doc 的查询延迟约 100ms,体验和服务端检索几乎无差。

八、踩坑清单

  1. 首次加载慢——50MB 模型在弱网下要等十几秒。一定要做加载进度条:
ts
const embed = await pipeline('feature-extraction', model, {
  progress_callback: (p) => console.log(p.status, p.progress),
});
  1. IndexedDB 配额——浏览器对 IndexedDB 有配额限制,多个模型可能被自动清理。重要场景用 Persistent Storage API 申请持久化。

  2. WebGPU 偶尔 crash——尤其是切换标签页或休眠后。封一层 try/catch,crash 后 fallback 到 wasm。

  3. 大模型不要在浏览器跑——3B 以上的模型在 Chrome 上加载基本会卡死。需要更大模型时上 Ollama。

  4. CSP——transformers.js 加载模型时会用 fetch 拿 weights,部署环境的 CSP 要 allowlist huggingface.co 或你自己的 CDN。

九、什么模型值得收藏

按 transformers.js 兼容性 + 体积排:

  • 嵌入Xenova/bge-small-zh-v1.5(中文)、Xenova/all-MiniLM-L6-v2(英文,22MB)
  • 小生成onnx-community/Qwen2.5-0.5B-InstructHuggingFaceTB/SmolLM2-360M-Instruct
  • OCRXenova/trocr-small-printed
  • 语音Xenova/whisper-tiny.en(39MB,英文够用)

去 HuggingFace 搜带 Xenova/onnx-community/ 前缀的仓库——这些是社区为 transformers.js 转换好的 ONNX 版本。


本站总访问量 --heart 本站访客数 -- 人次