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. logits has shape [batch, 2271]: the 704 IAB content logits followed by the 1,567 IAB audience logits. Labels in config.json carry 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_length in postprocess.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

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
Inference Providers NEW
This model isn't deployed by any Inference Provider. ๐Ÿ™‹ Ask for provider support

Model tree for zgpu-ai/zlm-v1-iab-domain-classifier-edge

Evaluation results

  • Content micro-F1 (int8, this repository) on 784-URL gold benchmark (GPT-5.5 labels)
    self-reported
    0.365
  • Content micro-precision on 784-URL gold benchmark (GPT-5.5 labels)
    self-reported
    0.418
  • Content micro-recall on 784-URL gold benchmark (GPT-5.5 labels)
    self-reported
    0.324