← 返回信息流
技术博客Hugging Face Blog·19 小时前

在 Transformers.js 中实验跨域存储 API

原标题:Experimenting with the proposed Cross-Origin Storage API in Transformers.js

速览

本文介绍了在 Transformers.js 环境中实验提议的跨域存储 API 的过程。该 API 旨在解决 Web 应用中跨域数据访问的安全与便利性问题。通过实际测试,展示了其在特定场景下的应用潜力。

AI 深度解读

在 Transformers.js 中实验跨域存储 API:解决浏览器缓存隔离难题

背景

Transformers.js 为 Web 开发者提供了一套简便的接口,使其能够在 Web 应用中利用 Transformer 模型的力量。通过特定的任务管道(pipelines),开发者可以轻松地在浏览器端运行推理任务。例如,以下代码展示了如何设置一个自动语音识别(ASR)管道:

import { pipeline } from 'https://cdn.jsdelivr.net/npm/@huggingface/[email protected]';
const asr = await pipeline(
'automatic-speech-recognition',
'Xenova/whisper-tiny.en',
{ device: 'webgpu' },
);
const result = await asr('jfk.wav');
console.log(result);

在这个例子中,Xenova/whisper-tiny.en 是一个针对常见英语 ASR 任务的极佳选择,也是 Transformers.js 默认模型解析机制中的默认 ASR 模型。当在浏览器中运行此示例时,Transformers.js 会自动处理相关模型资源和 Wasm 文件的下载与缓存。

然而,随着 Web 应用中 AI 模型的普及,一个显著的性能和存储问题浮现出来:缓存隔离导致的重复下载。即使多个不同来源(origin)的应用使用完全相同的模型文件或底层 Wasm 运行时,浏览器的默认缓存机制也会将它们视为不同的资源,导致重复下载和磁盘空间浪费。

核心内容

缓存挑战:模型与运行时资源的重复

1. AI 模型资源的重复下载

Xenova/whisper-tiny.en 为例,这是一个非常流行的模型。如果另一个来自不同来源(origin)的 Web 应用也使用了相同的模型,浏览器不会复用第一个应用已缓存的资源。即使两个应用请求的是字节完全相同的文件,浏览器也会重新下载并缓存这 177 MB 的数据。这不仅浪费带宽,还占用宝贵的本地存储空间。

2. WebAssembly (Wasm) 运行时的重复下载

问题不仅限于 AI 模型。考虑一个更复杂的场景:在一个玩具示例中添加情感分析管道。默认情况下,情感分析使用 Xenova/distilbert-base-uncased-finetuned-sst-2-english 模型。

const classifier = await pipeline('sentiment-analysis');
const sentiment = await classifier(result.text);
pre.append('\n\n' + JSON.stringify(sentiment, null, 2));

虽然这两个 AI 模型完全不同,但它们都依赖于同一个底层 ONNX Runtime 库提供的 Wasm 运行时文件:ort-wasm-simd-threaded.asyncify.wasm(大小约为 4,733 kB)。当在不同来源的应用中运行此示例时,浏览器会在 Network 标签页中显示该 Wasm 运行时被再次下载和缓存。

这意味着,即使应用之间没有共享相同的 AI 模型,浏览器仍然会对共享的 Wasm 资源发出冗余请求,并再次缓存它们,从而消耗硬盘空间。

缓存隔离的技术根源

1. 资源来源

  • AI 模型资源:默认来自 Hugging Face Hub,最终通过 Hugging Face CDN 提供服务。例如,请求 https://huggingface.co/Xenova/distilbert-base-uncased-finetuned-sst-2-english/resolve/main/config.json 会被重定向到最终的 CDN URL。
  • Wasm 运行时资源:默认来自 jsDelivr CDN。例如,ort-wasm-simd-threaded.asyncify.wasm 来自 https://cdn.jsdelivr.net/npm/onnxruntime-web@...

2. 为什么相同 URL 无法共享缓存?

尽管不同来源的应用最终可能从相同的 CDN URL 获取资源,但浏览器的缓存机制并非基于 URL 简单共享。为了防止定时攻击(timing attacks)——即网站通过 HTTP 请求响应时间来推断浏览器是否曾访问过同一资源,从而泄露用户隐私——浏览器实施了缓存分区(Cache Partitioning)

3. Chrome 的具体实现

在 Chrome 中,缓存键(Cache Key)由资源 URL网络隔离键(Network Isolation Key, NIK) 共同组成。NIK 由顶级站点(top-level site)和当前帧站点(current-frame site)构成。

假设两个应用分别托管在 https://googlechrome.github.iohttps://rawcdn.rawgit.net,它们都请求同一个 Wasm 文件: https://cdn.jsdelivr.net/npm/[email protected]/dist/ort-wasm-simd-threaded.asyncify.wasm

由于它们的 NIK 不同,浏览器会生成两个不同的缓存键。因此,即使资源 URL 完全相同,也不会发生缓存命中(Cache Hit),导致重复下载和存储。这就是跨域存储提案(Cross-Origin Storage proposal) 旨在解决的核心挑战。

跨域存储 API(Cross-Origin Storage API)的引入

跨域存储(COS)API 是一个早期的提案,目前尚未在任何浏览器中原生实现。为了实验该功能,可以使用 Cross-Origin Storage 扩展来注入 navigator.crossOriginStorage polyfill。

该 API 引入了一个专用的 navigator.crossOriginStorage 接口,允许 Web 应用跨越来源边界存储和检索大文件。其核心创新在于:文件不是通过 URL 或来源来标识,而是通过加密哈希(Cryptographic Hash)来标识。

工作原理

  1. 哈希标识:COS 使用文件的 SHA-256 哈希值作为唯一标识符。
  2. 跨域识别:无论文件来自哪个来源,只要哈希值相同,COS 就认为它们是同一个文件。
  3. 流程示例
const hash = {
  algorithm: 'SHA-256',
  value: '8f434346648f6b96df89dda901c5176b10a6d83961dd3c1ac88b59b2dc327aa4',
};

try {
  // 尝试从跨域存储中获取文件句柄
  const handle = await navigator.crossOriginStorage.requestFileHandle(hash);
  // 缓存命中!获取文件为 Blob 并直接使用
  const fileBlob = await handle.getFile();
} catch (err) {
  // 缓存未命中。从网络下载,然后存储以供下次使用
  const fileBlob = await fetch('https://cdn.jsdelivr.net/.../ort-wasm-simd-threaded.asyncify.wasm')
    .then(r => r.blob());
  
  // 将文件写入跨域存储,允许所有来源访问
  const handle = await navigator.crossOriginStorage.requestFileHandle(
    hash,
    { create: true, origins: '*' },
  );
  const writableStream = await handle.createWritable();
  await writableStream.write(fileBlob);
  await writableStream.close();
}
  • 缓存命中:如果资源存在于 COS 中,API 返回一个 FileSystemFileHandle,可以通过 getFile() 直接读取 Blob(生成的 File 对象继承自 Blob)。
  • 缓存未命中:如果资源不在 COS 中,应用回退到网络请求,下载资源后将其写入 COS。写入时指定 origins: '*',使得其他无关的、甚至来自完全不同来源的应用也能访问该文件。

这种设计巧妙地绕过了基于来源的缓存隔离限制,实现了基于内容哈希的全局缓存共享。

关键要点

  • 浏览器缓存隔离机制:出于安全和隐私考虑(防止定时攻击),现代浏览器(如 Chrome)使用网络隔离键(NIK)对缓存进行分区,导致不同来源的应用即使请求相同的 CDN 资源,也无法共享缓存。
  • AI Web 应用的存储浪费:Transformers.js 等库在浏览器中运行 AI 模型时,会导致模型文件和底层 Wasm 运行时被重复下载和存储,造成显著的带宽和磁盘空间浪费。
  • 跨域存储 API 的核心创新:通过加密哈希(而非 URL)标识文件,实现了跨来源的文件共享。只要文件内容相同,哈希值就相同,从而实现全局
查看原文 →huggingface.co