LFM2.5 Encoder 350M PII Detector (q8 ONNX) β Solus v1
The wider of the two entity extractors behind Solus's PII tools: LiquidAI's LFM2.5 bidirectional encoder with a BIOES token-classification head, exported to ONNX from the published safetensors and quantized to 8 bits.
It is a token classifier, not a generative model: it labels every subword and the spans are read back out of the tags, so it cannot invent a finding that is not in the input. It covers 40 kinds of personal detail across 16 languages β identity, contact, financial, credential, device, location, healthcare and special-category β where the GLiNER2 package covers seven in seven languages.
Boundaries of format-bound values (e-mail, IBAN, card, phone, URL) are repaired after the model by the regex tier in solus_engine::pii_hybrid, a port of the pii_hybrid_decode.py shipped with the upstream model. Without that tier a token classifier tags fragments β 905 and 555 inside a phone number β and a redaction built on fragments leaves the rest of the value in the document.
Specifications
| Parameters | 0.4B |
| Quantization | 8-bit (MatMulNBits) |
| Install size | 546.0 MB |
| Minimum RAM | 2.0 GB |
| Minimum VRAM | not required |
| Window | 2,048 subwords, overlapped for longer text |
| Tagging scheme | BIOES, 161 classes over 40 entity types |
| SHA-256 | 18b1ecc9c746ae4746b6a14de39902e538e4ec6ccfbe56ac27aad70aef12d41e (model_q8.onnx) |
Installs as a directory. Every file below is required:
| File | Size |
|---|---|
model_q8.onnx |
401.3 KB |
model_q8.onnx.data |
541.1 MB |
tokenizer.json |
4.5 MB |
labels.json |
5.4 KB |
onnx_export_metadata.json |
1.4 KB |
The graph
input_ids [batch, sequence] int64
attention_mask [batch, sequence] int64
-> logits [batch, sequence, 161] float32
No cache inputs. This is a bidirectional encoder and the upstream modeling code
forces use_cache=False, so a KV-cache interface would be meaningless β worth
saying because another ONNX export of this model on the Hub ships exactly that,
with 65536-wide logits: it exports the base model's language-model head rather
than the tagger.
import json, numpy as np, onnxruntime as ort
from tokenizers import Tokenizer
labels = json.load(open("labels.json"))["labels"]
tok = Tokenizer.from_file("tokenizer.json")
sess = ort.InferenceSession("model_q8.onnx")
enc = tok.encode("Email laura@charite.de or call 905-555-0142 today.")
ids = np.array([enc.ids], dtype=np.int64)
logits = sess.run(["logits"], {"input_ids": ids, "attention_mask": np.ones_like(ids)})[0][0]
tags = [labels[c] for c in logits.argmax(-1)] # BIOES, decode over enc.offsets
model_q8.onnx.data must sit beside the graph. onnx_export_metadata.json
records the export settings and a smoke test taken from this graph.
Decoding: read the tags, then repair the boundaries
Entity types: contact.address, contact.email, contact.ip_address, contact.phone, contact.postal_code, credential.api_key, credential.connection_string, credential.jwt, credential.password, credential.private_key, developer.device_id, developer.login_credentials, device.imei, device.mac_address, financial.amount, financial.bank_account, financial.credit_card, financial.crypto_wallet, financial.iban, financial.swift_bic, healthcare.condition, healthcare.health_plan_id, healthcare.medical_record, healthcare.medication, identity.date_of_birth, identity.drivers_license, identity.national_id, identity.passport, identity.person_name, identity.ssn, identity.tax_id, legal.case_number, location.gps_coordinates, online.url, online.username, org.company_name, special.health_status, special.orientation, special.political, special.religion.
Reading BIOES over the tokenizer's offsets is not the whole decode. Like every
byte-BPE token classifier, this model fragments the boundaries of format-bound
values β it tags 905 and 555 inside a phone number, and https inside a
URL. Anything that redacts from the raw tags therefore leaves the rest of the
value in the document.
The upstream repo ships pii_hybrid_decode.py, a regex tier that fixes this:
distinctive formats (e-mail, IBAN, card, IP, URL, SSN, MAC, crypto wallet, API
key) are matched independently and own their boundaries, while false-positive-
prone shapes (phone, date of birth, amount, postal code) only widen a fragment
the model already found. Use it, or an equivalent β Solus reimplements it in
Rust as solus_engine::pii_hybrid.
Conversion
LiquidAI/LFM2.5-Encoder-350M-PII-Detector (safetensors) converted to ONNX and quantized at the Faculty of
Engineering, McMaster University. Modified from the original work, as the
LFM Open License requires it be stated: the weights were exported from
safetensors to an ONNX graph with torch.onnx.export (opset 17, use_cache
off) and then quantized to 8-bit MatMulNBits (block size 128, asymmetric).
The architecture, the head and the label set are unchanged.
Checked in stages: the fp32 export matches the PyTorch model to 3e-5 with identical spans on the export fixtures, and the 8-bit graph reproduces the same post-decode findings on all of them.
Provenance
- Original model: LiquidAI/LFM2.5-Encoder-350M-PII-Detector
- Licence: LFM Open License v1.0, included as
LICENSE. Commercial use is not licensed for a legal entity above $10,000,000 annual revenue (qualified non-profits exempt). - Published for Solus, a desktop app for running AI models entirely on your own machine.
Model tree for Fazmin/solus_v1_lfm2-encoder-350m-pii-q8
Base model
LiquidAI/LFM2.5-350M-Base