kjarni-wasm
在网页中运行变压器模型。与PyTorch相同的输出,没有服务器。
Kjarni是一个转换器推理库,这是它的WebAssembly版本: 嵌入、分类、重新排序、语义搜索和浏览器聊天, 在Web Worker中运行,所以页面永远不会冻结。同样的引擎为 Python, c#, Go, c++和Rust。
npm i kjarni-wasm
或者没有捆绑器,直接从CDN:
<script type="module">
import { Kjarni } from "https://cdn.jsdelivr.net/npm/[email protected]/dist/index.js";
</script>
复制这个,它就运行了
一个文件,没有安装任何东西。另存为 quickstart.html,提供目录服务;
打开它:一个真正的模型下载并按意思排列三个句子。
curl -sO https://raw.githubusercontent.com/olafurjohannsson/kjarni/main/crates/kjarni-wasm/examples/quickstart.html
python3 -m http.server 8000
# then open http://localhost:8000/quickstart.html
需要服务器,因为ES模块无法加载 file://. 任何静态
服务器就行。
<pre id="out">starting…</pre>
<script type="module">
import init, { WasmModel } from "https://cdn.jsdelivr.net/npm/[email protected]/pkg/kjarni_wasm.js";
await init();
const url = "https://huggingface.co/olafuraron/all-MiniLM-L6-v2-q8/resolve/main/all-MiniLM-L6-v2-q8.kjq";
const model = WasmModel.from_quantized(new Uint8Array(await (await fetch(url)).arrayBuffer()));
const texts = [
"How do I get my money back?",
"What is your refund policy?",
"The weather in Reykjavik is unpredictable.",
];
// Normalised, so a dot product is the cosine similarity.
const flat = model.encode(texts, true);
const dim = flat.length / texts.length;
const vec = (i) => flat.subarray(i * dim, (i + 1) * dim);
const cosine = (a, b) => a.reduce((sum, x, i) => sum + x * b[i], 0);
out.textContent = `related: ${cosine(vec(0), vec(1)).toFixed(4)}\n`
+ `unrelated: ${cosine(vec(0), vec(2)).toFixed(4)}`;
</script>
related: 0.5268
unrelated: -0.0470
这将直接导入wasm模块,而不是导入 Kjarni 包装器,以便运行
在主线程上,没有Worker:对于一个只编码了几个句子的页面来说很好,
不是用来聊天的。使用 Kjarni 有关更大的内容,请参阅Chat部分。
分数与本地库略有不同(0.5510 / -0.0630),因为 浏览器模型是int8量化的。排名是一样的,这是什么搜索 用途。
快速浏览
import { Kjarni } from "kjarni-wasm";
const kjarni = await Kjarni.load({
encoder: "https://huggingface.co/olafuraron/all-MiniLM-L6-v2-q8/resolve/main/all-MiniLM-L6-v2-q8.kjq",
});
const [doctor, physician] = await kjarni.encode(["doctor", "physician"]);
console.log(Kjarni.cosine(doctor, physician)); // 0.8598
// or in one call
console.log(await kjarni.similarity("doctor", "physician"));
该URL是一个真正的、公开的23 MB文件。它只下载一次,浏览器就会缓存,其他内容就不会离开电脑。看到 模型 对于其余部分,在生产中使用您自己的原产地而不是盗链。
每个方法都返回一个promise,工作发生在工作线程上。一个模型 在页面继续绘制时加载或生成回复。
为什么是工人?
推理是计算绑定的和同步的。在主线程上,对批处理进行编码 冻结标签,只要它需要,并生成一个聊天回复冻结它 秒。这是浏览器AI功能最常见的表现方式 坏了。
这个包把所有这些都放在一个工人身上,给你承诺,所以你从来没有 想想看。
加载模型
模型是 .kjq 文件:配置,tokenizer和int8权重在一个下载。
const kjarni = await Kjarni.load({
encoder: model("all-MiniLM-L6-v2-q8"),
classifier: model("distilbert-sentiment-q8"),
reranker: model("ms-marco-MiniLM-L-6-v2-q8"),
onProgress: (capability, loaded, total) => {
console.log(`${capability}: ${Math.round((loaded / total) * 100)}%`);
},
});
只加载您使用的内容。每个模型都是一个真正的下载,从23mb的MiniLM到
一个聊天模特几百块,所以 onProgress 值得连接到进度条。
字节被传输到工作线程而不是被复制,这很重要:复制一个 500mb模型是一个可见的暂停和双倍峰值内存。
分类
const [result] = await kjarni.classify("This is wonderful");
console.log(result.label, result.score); // POSITIVE 0.9999
const batch = await kjarni.classify(["Great service", "Worst purchase ever"]);
console.log(await kjarni.labels()); // ["NEGATIVE", "POSITIVE"]
Reranking
将查询和文档一起评分,这明显比比较更准确 嵌入的。索引指向输入,因此您可以保留自己的id。
const ranked = await kjarni.rerank("how do I cancel?", documents, 5);
for (const { index, score } of ranked) console.log(score, documents[index]);
闲谈,聊天
一个0.5B语言模型在选项卡中运行,令牌在解码后到达。
const kjarni = await Kjarni.load({
chat: model("qwen05b-q8"),
chatModelId: "qwen2.5-0.5b-instruct",
});
let out = "";
for await (const piece of kjarni.chat("Explain RAG in one sentence.")) out += piece;
这是有效的,因为权重在内存中保持块量化。Wasm32代表a
单次分配 isize::MAX,原来是2gb, Qwen2.5 0.5B扩展到f32
需要1.98 GB,因此预先进行量化的加载程序会在加载时捕获。这个 .kjq
容器携带引擎就地读取的块量化权重,即
500mb而不是1.98 GB,解码速度比f32快而不是更慢。
在笔记本电脑CPU上,预计每秒会有几个令牌。在Web Worker: generation中运行它 占用线程直到它返回,因此在主线程上,TAB停止绘制 对于整个回复。
chat() 完成提示;它不转弯。多回合对话,何处有
应用模型自己的聊天模板,并记住较早的回合
在 WasmChat (send_stream, clear_history),目前尚未浮出水面
包装。
清理
await kjarni.close();
JavaScript垃圾收集器无法访问Wasm句柄,因此删除
引用泄露了权重。 close() 释放模型并停止worker。
需求
带有WebAssembly和模块工作器的浏览器:Chrome和Edge 80+, Firefox 114+, Safari 15 +。不需要WebGPU,推理在CPU上运行。
模型
.kjq 是一个单独的文件,包含配置,标记器和int8权重。这些都是
并托管在hug Face上,每个模型一个存储库,带有存储库
和文件名匹配:
const model = (name: string) =>
`https://huggingface.co/olafuraron/${name}/resolve/main/${name}.kjq`;
| 通过 | 名字 | 大小 | 它是做什么的 |
|---|---|---|---|
encoder | all-MiniLM-L6-v2-q8 | 23 MB | 嵌入,相似度,搜索 |
reranker | ms-marco-MiniLM-L-6-v2-q8 | 23 MB | cross-encoder reranking |
classifier | distilbert-sentiment-q8 | 68 MB | 积极/消极情绪 |
chat | qwen05b-q8 | 508 MB | 一个局部语言模型,块量化,所以它适合 |
hugs Face提供了允许的CORS,因此浏览器可以从 任何起源。对于生产,复制你需要的东西,并从你自己的来源提供: 您可以控制缓存,并且您的页面不依赖于其他人的正常运行时间。
只加载您使用的内容,然后连接 onProgress 对于一些可见的东西:它们是真实的
下载,沉默的一分钟读起来就像一个破碎的页面。
同样的引擎
Kjarni是几个包背后的一个Rust引擎。浏览器是一个目标;如果 您需要相同产品的服务器端一半:
- 现场演示:这个包,在一个页面中运行
- Kjarni 和
Kjarni.Extensions.AI
c#的NuGet,包括
IEmbeddingGenerator和IChatClient - 去, c++ 以及读取标准输入和写入JSON的CLI
- 来源和问题
制作自己的 .kjq
看到 格式指南. 任何bert族编码器, 交叉编码器,分类器或小解码器从拥抱脸转换与一个 脚本。
