Kwker

ONNX Runtime

Run an ONNX model you already have through ONNX Runtime, with Kwker underneath. Kwker takes the models it runs faster and leaves every other model to ONNX Runtime's own providers. It runs the BERT-family encoders - BERT, RoBERTa, XLM-R, MPNet, ModernBERT, NomicBERT, EuroBERT and the MiniLM, BGE, E5, GTE and Nomic embedding and reranking models - as optimum and torch.onnx export them. Results match ONNX Runtime's CPU provider: the model's float32 weights are used as they are.

Times on a 4-core Xeon without AMX, 128-token texts, medians of three runs (ONNX Runtime's CPU provider on the same file, then the same session with Kwker):

Model file 1 text 8 texts
all-MiniLM-L6-v2, the Hub's onnx/model.onnx 16.9 → 11.3 ms (1.5x) 103.5 → 64.9 ms (1.6x)
its int8 file onnx/model_qint8_avx512.onnx 12.8 → 4.9 ms (2.6x) 64.5 → 25.5 ms (2.5x)
a static QDQ int8 export of it (quantize_static) 15.0 → 4.8 ms (3.1x) 91.3 → 26.0 ms (3.5x)
transformers.js' model_fp16.onnx 19.2 → 10.6 ms (1.8x) 91.5 → 54.7 ms (1.7x)
transformers.js' model_q4.onnx 17.0 → 11.4 ms (1.5x) 107.7 → 55.8 ms (1.9x)
thenlper/gte-small (12 layers), optimum's float16 onnx/model_O4.onnx 41.4 → 21.5 ms (1.9x) 232.7 → 116.4 ms (2.0x)
gte-small's int8 file onnx/model_qint8_avx512_vnni.onnx 23.7 → 8.7 ms (2.7x) 130.2 → 45.8 ms (2.8x)
Snowflake/snowflake-arctic-embed-xs, 4-bit onnx/model_bnb4.onnx 19.9 → 11.1 ms (1.8x) 111.4 → 67.3 ms (1.7x)
sentence-transformers/all-mpnet-base-v2 (MPNet, 12 layers), the Hub's onnx/model.onnx 91.2 → 78.8 ms (1.2x) 766.2 → 554.7 ms (1.4x)
nomic-ai/modernbert-embed-base (ModernBERT, 22 layers), the Hub's onnx/model.onnx 182.3 → 120.2 ms (1.5x) 1173.0 → 733.2 ms (1.6x)
nomic-ai/nomic-embed-text-v1.5 (NomicBERT, 12 layers), the Hub's onnx/model.onnx 139.1 → 103.6 ms (1.3x) 883.4 → 607.8 ms (1.5x)
EuroBERT/EuroBERT-210m (EuroBERT, 12 layers), a torch.onnx export 137.0 → 92.7 ms (1.5x) 909.8 → 589.4 ms (1.5x)

The reranker cross-encoder/ms-marco-MiniLM-L6-v2 scored 32 query-passage pairs of 128 tokens 1.8x faster (218.9 against 404.0 ms).

Prerequisites

Step 1: open a session with Kwker

Pass kwker.onnx.session_options() where you create the session. Nothing else in your code changes:

PythonRuns on your machine.
import onnxruntime as ort
import kwker.onnx

sess = ort.InferenceSession("model.onnx", sess_options=kwker.onnx.session_options())

Step 2: run it

The session runs as before. Here a tiny random BERT, exported to ONNX, stands in for a real model:

PythonNeeds torch, transformers, onnxruntime, onnx: runs on your machine.
import os
import tempfile
import numpy as np
import onnxruntime as ort
import torch
from transformers import BertConfig, BertModel
import kwker.onnx

class Hidden(torch.nn.Module):  # (the hidden states alone: an ONNX graph's outputs are plain tensors)
    def __init__(self, model):
        super().__init__()
        self.model = model

    def forward(self, input_ids, attention_mask):
        return self.model(input_ids=input_ids, attention_mask=attention_mask).last_hidden_state


model = BertModel(BertConfig(vocab_size=1000, hidden_size=128, num_hidden_layers=2, num_attention_heads=2,
                             intermediate_size=256), add_pooling_layer=False)
path = os.path.join(tempfile.mkdtemp(), "model.onnx")
ids = torch.tensor([[101, 592, 88, 102]])
torch.onnx.export(Hidden(model).eval(), (ids, torch.ones_like(ids)), path, input_names=["input_ids", "attention_mask"],
                  output_names=["last_hidden_state"], opset_version=17, dynamo=False,
                  dynamic_axes={"input_ids": {0: "batch", 1: "length"}, "attention_mask": {0: "batch", 1: "length"}})

sess = ort.InferenceSession(path, sess_options=kwker.onnx.session_options())
feeds = {"input_ids": ids.numpy(), "attention_mask": np.ones((1, 4), np.int64)}
hidden = sess.run(None, feeds)[0]
cpu = ort.InferenceSession(path, providers=["CPUExecutionProvider"]).run(None, feeds)[0]
print(hidden.shape, np.allclose(hidden, cpu, atol=1e-5))
Output
(1, 4, 128) True

Options

session_options() takes the precision and the thread count:

PythonRuns on your machine.
import kwker.onnx

so = kwker.onnx.session_options(precision="bf16", threads=4)

Notes

Next steps