Instructions to use zgpu-ai/zlm-v1-iab-domain-classifier-edge with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers.js
How to use zgpu-ai/zlm-v1-iab-domain-classifier-edge with Transformers.js:
// npm i @huggingface/transformers import { pipeline } from '@huggingface/transformers'; // Allocate pipeline const pipe = await pipeline('text-classification', 'zgpu-ai/zlm-v1-iab-domain-classifier-edge');
zlm-v1-iab-domain-classifier-edge
The on-device build of ZeroGPU/zlm-v1-iab-domain-classifier:
a 149M-parameter ModernBERT model that classifies a web destination into IAB content and audience
categories from its URL alone, packaged as a 153 MB int8 ONNX file that runs in the browser.
This repository is the exact bundle ZeroGPU serves to edge devices (browsers, Node.js / Docker workers, Android) in production, laid out for transformers.js and onnxruntime. For the full-precision model, the PyTorch checkpoint, the training details and the complete benchmark, see the main repository.
What is different from the main repository
- One output.
logitshas shape[batch, 2271]: the 704 IAB content logits followed by the 1,567 IAB audience logits. Labels inconfig.jsoncarry a head prefix (content|Sports,audience|Interest | Sports | Basketball |), so a standard text-classification pipeline can run it. - Per-channel int8 quantization (QUInt8, dynamic) of the full-precision graph.
- 64-token input window, the length the model was trained with (
max_seq_lengthinpostprocess.json). - Head-level thresholds: content 0.60, audience 0.50 (
postprocess.json).
Input format
The model reads a deterministic rendering of the URL, not the raw URL:
"<host> | <segmented words> | tld:<tld>"
https://www.sportsnews.co.uk/foo -> "sportsnews.co.uk | sports news | tld:co.uk"
espn.com -> "espn.com | espn | tld:com"
Lowercase; strip scheme, www., port, path and query; split the host on ., - and digits; peel off the
TLD; word-segment glued labels (sportsnews โ sports news, via
wordninja). The reference implementation url_text.py is included.
Files
| File | Purpose |
|---|---|
onnx/model_quantized.onnx |
int8 graph, 153 MB. Inputs input_ids, attention_mask (int64); output logits [batch, 2271] |
config.json |
ModernBertForSequenceClassification config with a flat id2label over both heads |
postprocess.json |
Serving settings: input format, 64-token window, per-head thresholds |
tokenizer.json, tokenizer_config.json |
ModernBERT tokenizer (merges stored as "a b" strings for transformers.js 2.x) |
url_text.py |
Reference URL โ input-string renderer |
config.json keeps a few informational fields inherited from the training wrapper
(base_model_name_or_path, head_architecture, max_seq_length); inference does not read them. The
backbone is gte-modernbert-base and the serving window is the 64 tokens in postprocess.json. The repository
contains no PyTorch weights.
Usage
Python (onnxruntime)
import json
import sys
import numpy as np
import onnxruntime as ort
from huggingface_hub import snapshot_download
from tokenizers import Tokenizer
root = snapshot_download("ZeroGPU/zlm-v1-iab-domain-classifier-edge")
sys.path.insert(0, root)
from url_text import url_to_text # pip install wordninja for word segmentation
post = json.load(open(f"{root}/postprocess.json"))
id2label = json.load(open(f"{root}/config.json", encoding="utf-8"))["id2label"]
tok = Tokenizer.from_file(f"{root}/tokenizer.json")
tok.enable_truncation(post["max_seq_length"]) # 64
sess = ort.InferenceSession(f"{root}/onnx/model_quantized.onnx")
enc = tok.encode(url_to_text("https://www.espn.com"))
logits = sess.run(["logits"], {
"input_ids": np.array([enc.ids], dtype=np.int64),
"attention_mask": np.array([enc.attention_mask], dtype=np.int64),
})[0][0]
probs = 1.0 / (1.0 + np.exp(-logits)) # multi-label: independent sigmoid per label
cuts = {"content": post["head_thresholds"]["iabcontent_names"], "audience": post["head_thresholds"]["iabaudience_names"]}
picked = sorted(
((id2label[str(i)], float(p)) for i, p in enumerate(probs) if p >= cuts[id2label[str(i)].split("|", 1)[0]]),
key=lambda t: -t[1],
)
print(picked[:6]) # [('content|Sports', 0.99...), ('audience|Sports', 0.99...), ...]
Classify one URL at a time, or in tightly padded batches: dynamic int8 quantization computes activation ranges over the whole input, padding included, and large padded batches lower the scores' accuracy.
JavaScript (transformers.js v3 โ browser, Node.js, workers)
ModernBERT needs transformers.js v3 (@huggingface/transformers).
import { pipeline } from '@huggingface/transformers';
const classify = await pipeline('text-classification', 'ZeroGPU/zlm-v1-iab-domain-classifier-edge', { dtype: 'q8' });
// Input is the rendered URL string (see "Input format")
const out = await classify('espn.com | espn | tld:com', { top_k: 10, function_to_apply: 'sigmoid' });
const picked = out.filter(({ label, score }) => score >= (label.startsWith('content|') ? 0.6 : 0.5));
console.log(picked);
Pass function_to_apply: 'sigmoid': the model is multi-label, so labels are scored independently.
Evaluation
Content micro-F1 at the 0.60 default threshold on the 784-URL gold set (ZeroGPU/iab-url-gold-784, labeled independently by GPT-5.5), one URL at a time without padding:
| Model | Size | Precision | Recall | Content F1 | Same top label as fp32 |
|---|---|---|---|---|---|
| This repository (int8, per-channel) | 153 MB | 0.4185 | 0.3242 | 0.3654 | 76% |
| Main repository, fp32 | 605 MB | 0.4243 | 0.3508 | 0.3841 | โ |
| GPT-5.4-nano | โ | 0.3332 | 0.3744 | 0.3526 | โ |
The same 784 rows also selected the training epoch and the decision thresholds, so these scores are a mild upper bound on fully unseen data. The per-category results and the full evaluation are in the main repository.
Latency
onnxruntime 1.30, one input, CPU (Intel Core Ultra 9 275HX), median of 50 runs. A typical rendered URL is 10โ20 tokens.
| Threads | 16 tokens | 64 tokens |
|---|---|---|
| 1 | 16.5 ms | 44.5 ms |
| 4 | 8.7 ms | 20.7 ms |
In production ZeroGPU serves this bundle to devices with at least 8 GB of RAM.
Limitations and intended use
- A bare domain often under-determines the topic; treat the output as probabilistic enrichment signals.
- Accuracy varies by category; see the per-category table in the main repository.
- Labels come from a fixed snapshot of the IAB taxonomies (704 content + 1,567 audience).
- Trained predominantly on ASCII / English-tokenizable domains.
- The taxonomy includes sensitive categories. Do not use the model to infer sensitive attributes of individual people; review predictions in sensitive categories before acting on them.
License and attribution
- Model weights: Apache-2.0 (same as the base model,
Alibaba-NLP/gte-modernbert-base). - Built on ModernBERT (Answer.AI / LightOn).
- Label space follows the IAB Tech Lab Content & Audience Taxonomies, ยฉ IAB Technology Laboratory.
Citation
@misc{zerogpu2026iabdomainclassifier,
title = {zlm-v1-iab-domain-classifier: URL-only IAB classification with a fine-tuned ModernBERT},
author = {ZeroGPU},
year = {2026},
url = {https://huggingface.co/ZeroGPU/zlm-v1-iab-domain-classifier}
}
- Downloads last month
- 26,411
Model tree for zgpu-ai/zlm-v1-iab-domain-classifier-edge
Base model
answerdotai/ModernBERT-baseEvaluation results
- Content micro-F1 (int8, this repository) on 784-URL gold benchmark (GPT-5.5 labels)self-reported0.365
- Content micro-precision on 784-URL gold benchmark (GPT-5.5 labels)self-reported0.418
- Content micro-recall on 784-URL gold benchmark (GPT-5.5 labels)self-reported0.324