返回模型
说明文档
注意(2023年9月7日):此时你可能想要使用 Transformers.js,因为它维护良好且支持量化模型,体积更小。不过,如果你不想在应用中引入整个 Transformers.js 库(截至撰写时我不确定是否支持 tree-shaking),你仍然可以直接使用 ONNX Runtime Web 配合 Transformers.js 转换脚本生成的量化模型。
以下是 Transformers.js 的相关模块:
- https://huggingface.co/docs/transformers.js/api/models#module_models.CLIPTextModelWithProjection
- https://huggingface.co/docs/transformers.js/api/models#module_models.CLIPVisionModelWithProjection
这是一个使用 Transformers.js 的完整工作示例:
let quantized = false; // 改为 `true` 可使用更小的模型(例如图像模型 87mb vs 345mb),但精度会降低
let { AutoProcessor, CLIPVisionModelWithProjection, RawImage, AutoTokenizer, CLIPTextModelWithProjection } = await import('https://cdn.jsdelivr.net/npm/@xenova/transformers@2.5.4/dist/transformers.js');
let imageProcessor = await AutoProcessor.from_pretrained('Xenova/clip-vit-base-patch16');
let visionModel = await CLIPVisionModelWithProjection.from_pretrained('Xenova/clip-vit-base-patch16', {quantized});
let tokenizer = await AutoTokenizer.from_pretrained('Xenova/clip-vit-base-patch16');
let textModel = await CLIPTextModelWithProjection.from_pretrained('Xenova/clip-vit-base-patch16', {quantized});
function cosineSimilarity(A, B) {
if(A.length !== B.length) throw new Error("A.length !== B.length");
let dotProduct = 0, mA = 0, mB = 0;
for(let i = 0; i < A.length; i++){
dotProduct += A[i] * B[i];
mA += A[i] * A[i];
mB += B[i] * B[i];
}
mA = Math.sqrt(mA);
mB = Math.sqrt(mB);
let similarity = dotProduct / (mA * mB);
return similarity;
}
// 获取图像嵌入:
let image = await RawImage.read('https://i.imgur.com/RKsLoNB.png');
let imageInputs = await imageProcessor(image);
let { image_embeds } = await visionModel(imageInputs);
console.log(image_embeds.data);
// 获取文本嵌入:
let texts = ['a photo of an astronaut'];
let textInputs = tokenizer(texts, { padding: true, truncation: true });
let { text_embeds } = await textModel(textInputs);
console.log(text_embeds.data);
let similarity = cosineSimilarity(image_embeds.data, text_embeds.data);
console.log(similarity);
请注意,上述代码使用的是 clip-vit-base-patch16,而本仓库使用的是 clip-vit-base-patch32——不确定哪个更好,但如果你想测试,可以在上述代码中将 patch16 改为 patch32。另外请注意,你会在控制台中看到一些 GET/404 错误——这是预期的,因为 Transformers.js 会先尝试本地加载模型。可能有办法禁用此行为。
Transformers.js 还有大量其他可用模型,而且非常易于使用。例如,这是一个文本嵌入/检索模型的示例:
let { pipeline } = await import('https://cdn.jsdelivr.net/npm/@xenova/transformers@2.5.4/dist/transformers.js');
let extractor = await pipeline('feature-extraction', 'Xenova/e5-large-v2');
let dotProduct = (vec1, vec2) => vec1.reduce((sum, val, i) => sum + val * vec2[i], 0);
let passage1 = await extractor('passage: She likes carrots and celery.', { pooling: 'mean', normalize: true });
let passage2 = await extractor('passage: This is a good calculus guide.', { pooling: 'mean', normalize: true });
let query = await extractor('query: Taking care of rabbits', { pooling: 'mean', normalize: true });
let similarity1 = dotProduct(query.data, passage1.data);
let similarity2 = dotProduct(query.data, passage2.data);
OpenAI CLIP JavaScript
使用 ONNX web runtime 将 OpenAI 的 CLIP 模型移植到 JavaScript。我还让 LiT 模型在这里运行成功。
精简演示:
- 图像模型:https://josephrocca.github.io/openai-clip-js/onnx-image-demo.html
- 文本模型:https://josephrocca.github.io/openai-clip-js/onnx-text-demo.html
示例应用:
- 使用文本提示对本地图片文件夹进行排序/搜索:https://github.com/josephrocca/clip-image-sorter
服务端:
- Deno:https://github.com/josephrocca/openai-clip-js/blob/main/deno-example.js
- Node.js:https://github.com/josephrocca/openai-clip-js/blob/main/nodejs-example.js
注意事项:
- 模型文件比实际需要的大约 4 倍——参数使用的是 float32 而不是 uint8。如果你在"真正的"Web 应用中使用 CLIP,你可能应该对其进行量化。@minimaxir 已经完成了这项工作(1、2),该模型在 ORT Web 上首次尝试就成功了(这很神奇),但它输出的是 768 元素向量而不是 512,我认为这是因为 @minimaxir 的模型缺少最终的投影头,该投影头用于将图像嵌入放入与文本嵌入相同大小的空间中。我在 ONNX 导出 notebook 中快速尝试了一下(见 ONNX 转换后的单元格),但似乎没有成功。如果你研究这个问题并成功解决,请提交一个 issue。感谢 Twitter 上的 @congraIiIso 让我注意到 uint8 量化!编辑:我已经让量化"成功"了,但量化模型生成的嵌入似乎与正确的嵌入不够接近。详情请参阅此评论。
- 你应该使用双三次插值调整图像大小以获得最准确的嵌入。这里有一个简单的可复制粘贴的 JavaScript 双三次调整大小 + 中心裁剪函数,使用了 wasm-vips。
- 更多信息:在上面链接的图像模型演示中,图像编码器演示在预处理输入图像时使用了默认的 HTML5 canvas 调整大小算法。这显然不是双三次插值(OpenAI 的 CLIP 仓库使用的是双三次插值)。这导致嵌入与 Pytorch 给出的结果有些不同。我不确定这在实际使用中是否会成为问题,但如果对你来说很重要,你不应该使用 canvas 调整大小,而应该使用真正的双三次调整器。例如,这张宇航员图片在 Pytorch 模型中的嵌入是:
[0.3181,0.3054,-0.1548,0.0767,-0.1699,0.1320,-0.2974,-0.1940,-0.3052,0.2299,0.1995, -0.3025,0.3108,-0.2305,0.2368, ...],而 ONNX Runtime Web(wasm 后端)给出的是:[0.3635,0.3301,-0.1093,0.0598,-0.1526,0.1127,-0.3373,-0.1544,-0.2627,0.2372,-0.2012,-0.3182,0.3022,-0.2940,0.2227, ...]。如果你使用双三次算法预先调整图像大小(像这样——演示中使用的默认图像),那么嵌入基本相同。
- 更多信息:在上面链接的图像模型演示中,图像编码器演示在预处理输入图像时使用了默认的 HTML5 canvas 调整大小算法。这显然不是双三次插值(OpenAI 的 CLIP 仓库使用的是双三次插值)。这导致嵌入与 Pytorch 给出的结果有些不同。我不确定这在实际使用中是否会成为问题,但如果对你来说很重要,你不应该使用 canvas 调整大小,而应该使用真正的双三次调整器。例如,这张宇航员图片在 Pytorch 模型中的嵌入是:
- ONNX 文本模型生成的嵌入根据一些图像/文本匹配任务的"目测"来看似乎与 Pytorch 模型足够接近,但请注意存在一些看起来不小的差异。同样,我不知道这些差异是否足以显著影响实际使用。如果你设法运行了一些适当的测试,请随时提交 issue。以下是 Pytorch 和 ONNX 中 "a portrait of an astronaut with the American flag" 的嵌入:
- Pytorch:
[-0.16650, 0.05167, -0.15320, 0.44922, 0.20642, -0.29565, 0.04041, -0.41064, -0.15015, 0.31934, -0.06842, -0.25464, 0.12311, -0.09509, 0.24109, -0.04883, 0.26074, 0.00045, 0.20972, 0.36987, ...] - ONNX:
[-0.19535, 0.01808, -0.09647, 0.61671, 0.17760, -0.30735, -0.03580, -0.31977, -0.21485, 0.38863, 0.05983, -0.24685, 0.17829, -0.16579, 0.17799, -0.07826, 0.28496, -0.02429, 0.11830, 0.37698, ...]
- Pytorch:
- 模型直接从这个 HuggingFace 🤗 仓库提供给浏览器。
- 关于模型转换:
- 我使用这个 Colab notebook将 Pytorch 模型转换为 ONNX/tfjs 等。
- 我使用这个 Colab notebook将权重从 float16 转换为 float32,因为 ONNX web runtime 目前不支持 float16。这意味着模型文件是应有大小的两倍(issue)。
- 请参阅此文件顶部的注释,了解需要应用于文本模型的额外转换步骤,以避免此错误。
待办(可能):
- 如果这个 issue 得到解决,尝试 tfjs runtime。
- 尝试导出并运行 tflite 模型。
rocca/openai-clip-js
作者 rocca
↓ 0
♥ 7
创建时间: 2022-03-02 23:29:05+00:00
更新时间: 2023-06-16 12:00:56+00:00
在 Hugging Face 上查看文件 (7)
.gitattributes
README.md
clip-image-vit-32-float32.onnx
ONNX
clip-image-vit-32-uint8.onnx
ONNX
clip-text-vit-32-float32-int32.onnx
ONNX
clip-text-vit-32-uint8.onnx
ONNX
test-image-data.json