Transforms
Transforms allow you to post-process model outputs after ONNX inference and before returning results. They run inside the model binary, operating directly on tensors for high performance.
Transforms run on Lua 5.4 in a sandboxed environment. The transforms feature does not support LuaJIT currently.
Why Use Transforms?
Common use cases:
Normalize embeddings for cosine similarity
Apply softmax to convert logits to probabilities
Pool embeddings to create sentence representations
Scale outputs for specific downstream tasks
Getting Started
A transform is a Lua script that defines a Postprocess function:
---@param arr Tensor
---@return Tensor
function Postprocess(arr, ...)
-- your postprocessing logic
return tensor
endWith a handful of exceptions, the Postprocess function must return a Tensor with the exact same shape as the input Tensor provided for that model type. The exceptions are as follows:
Embedding and sentence embedding models can modify the length of
hidden(useful for matryoshka embeddings)Sentence embeddings are given a
Tensorof shape[batch_size, seq_len, hidden]and attention mask of[batch_size, seq_len], and must return aTensorof shape[batch_size, hidden]. In other words, it expects a pooling operation along dimensionseq_len.
We provide a built-in API for standard tensor operations. To learn more, check out our Tensor API reference page. You can find the stub file here.
If you don't see an op that you need, please don't hesitate to create an issue on Github.
Creating a New Transform
To create a new transform, use the encoderfile CLI:
Input Signatures
The input signature of Postprocess depends on the type of model being used.
Embedding
Sequence Classification
Token Classification
Sentence Embedding
Typical Transform Patterns
Most transforms fall into one of 3 patterns:
1. Elementwise Transforms
Safe: they preserve shape automatically.
Examples:
scaling (
tensor * 1.5)activation functions (
tensor:exp())
2. Normalization Across Axis
These also preserve shape.
Examples:
Lp normalization: (
tensor:lp_normalize(p, axis))subtracting mean per batch or per token
applying softmax across a specific dimension (
tensor:softmax(2))
3. Mask-aware adjustments
When working with sentence embedding models:
Best Practices
Performance Implications
Transforms run synchronously during inference, so expensive Lua-side loops will increase latency. If you don't see an op that you need, please don't hesitate to create an issue on Github.
A typical transform follows this structure:
Debugging Transforms
You can inspect shape and values using:
Errors typically fall into:
axis out of range → axis must be 1-indexed and ≤ tensor rank
broadcasting errors → the two shapes are incompatible
returned value is not a tensor → must return a Tensor userdata object
shape mismatch → you modified rank or dimensions
Configuration
Transforms are embedded at build time. You can specify them in your config.yml either as a file path or inline.
Or, they can be passed inline:
Last updated