Pulpie Orange Small ONNX

Optimized ONNX export of feyninc/pulpie-orange-small, a 210M-parameter EuroBERT token-classification model for extracting main content from HTML.

The model takes tokenized simplified HTML blocks packed as:

[BOS] block_0 [<|sep|>] block_1 [<|sep|>] ... [EOS]

It returns logits shaped [batch_size, sequence_length, 2]. Pulpie reads predictions at the <|sep|> token positions; class 1 is main, class 0 is other.

Files

File Description
model.onnx Validated FP32 ONNX Runtime model, dynamic batch and sequence axes
tokenizer.json, tokenizer_config.json, special_tokens_map.json Source tokenizer files
config.json, configuration_eurobert.py, modeling_eurobert.py Source config/code for reference and tokenizer/model metadata
export_report.json Export settings and ONNX graph summary
verification_report.json Checker, parity, extraction, benchmark, and memory results
quantized_report.json Int8 dynamic-quantization experiment results

Install

pip install onnxruntime transformers "pulpie[markdown]" huggingface_hub numpy

Raw ONNX Runtime Inference

import numpy as np
import onnxruntime as ort
from huggingface_hub import hf_hub_download
from transformers import AutoTokenizer

repo_id = "Mike0021/pulpie-orange-small-onnx"
model_path = hf_hub_download(repo_id, "model.onnx")

tokenizer = AutoTokenizer.from_pretrained(repo_id, trust_remote_code=True)
sep_id = tokenizer.convert_tokens_to_ids("<|sep|>")

block = '<p _item_id="0">This is the main article text.</p>'
input_ids = [tokenizer.bos_token_id]
input_ids += tokenizer.encode(block, add_special_tokens=False)
input_ids += [sep_id, tokenizer.eos_token_id]

inputs = {
    "input_ids": np.asarray([input_ids], dtype=np.int64),
    "attention_mask": np.ones((1, len(input_ids)), dtype=np.int64),
}

session = ort.InferenceSession(model_path, providers=["CPUExecutionProvider"])
logits = session.run(["logits"], inputs)[0]
label = int(logits[0, input_ids.index(sep_id)].argmax())
print("main" if label == 1 else "other")

Pulpie-Style Extraction

import numpy as np
import onnxruntime as ort
from huggingface_hub import hf_hub_download
from transformers import AutoTokenizer

from pulpie.chunker import SEP_TOKEN, extract_blocks, pack_chunks, tokenize_blocks
from pulpie.markdown import to_markdown
from pulpie.model_utils import extract_item_ids, predictions_to_labels
from pulpie.reconstruct import extract_main_html
from pulpie.simplify import simplify

repo_id = "Mike0021/pulpie-orange-small-onnx"
model_path = hf_hub_download(repo_id, "model.onnx")
tokenizer = AutoTokenizer.from_pretrained(repo_id, trust_remote_code=True)
session = ort.InferenceSession(model_path, providers=["CPUExecutionProvider"])

def extract_markdown(html: str) -> str:
    simplified, map_html = simplify(html)
    blocks = extract_blocks(simplified)
    item_ids = extract_item_ids(blocks)
    sep_id = tokenizer.convert_tokens_to_ids(SEP_TOKEN)
    chunks = pack_chunks(
        tokenize_blocks(blocks, tokenizer),
        max_tokens=8192,
        sep_token_id=sep_id,
        bos_token_id=tokenizer.bos_token_id,
        eos_token_id=tokenizer.eos_token_id,
    )

    predictions = [0] * len(blocks)
    for chunk_ids, block_indices in chunks:
        input_ids = np.asarray([chunk_ids], dtype=np.int64)
        attention_mask = np.ones_like(input_ids, dtype=np.int64)
        logits = session.run(["logits"], {"input_ids": input_ids, "attention_mask": attention_mask})[0][0]
        sep_positions = np.nonzero(input_ids[0] == sep_id)[0]
        preds = logits[sep_positions].argmax(axis=-1).tolist()
        for i, block_idx in enumerate(block_indices):
            if i < len(preds):
                predictions[block_idx] = int(preds[i])

    labels = predictions_to_labels(item_ids, predictions)
    return to_markdown(extract_main_html(map_html, labels))

Verification

Environment: CPU-only, PyTorch 2.12.1+cu130, Transformers 4.57.6, ONNX 1.22.0, ONNX Runtime 1.27.0.

Check Result
onnx.checker.check_model(model.onnx) Pass
onnxruntime.InferenceSession Pass
Nonstandard ONNX node domains None
Main ONNX opset 18
Dynamic axes batch_size, sequence_length
Graph nodes 671
End-to-end ONNX extraction Non-empty Markdown, 149 chars on article_basic

Numerical parity against PyTorch FP32/eager:

Input Shape Max abs diff Mean abs diff
article_basic [1, 86, 2] 6.50e-6 1.73e-6
docs_page [1, 131, 2] 8.58e-6 1.53e-6
news_article [1, 140, 2] 1.00e-5 1.55e-6

All three inputs passed np.allclose(..., atol=1e-4, rtol=1e-3).

Benchmarks

CPU forward benchmark on a 131-token pulpie chunk, 5 measured reps:

Runtime Mean latency Min Max
PyTorch FP32 eager 181.29 ms 143.41 ms 233.41 ms
ONNX Runtime FP32 110.98 ms 107.31 ms 119.91 ms

ONNX Runtime was 1.63x faster on this CPU benchmark. No CUDA device was available in the export environment, so GPU latency is not reported.

Approximate process RSS after model load and one inference:

Runtime RSS Delta after tokenizer/input prep
PyTorch FP32 eager 1723 MB 751 MB
ONNX Runtime FP32 1916 MB 945 MB

File sizes:

Artifact Size
Source model.safetensors bf16 404 MB
Validated model.onnx FP32 808 MB

Quantization

Dynamic int8 quantization was evaluated locally. It reduced the ONNX file from 808 MB to 203 MB and loaded in ONNX Runtime, but it did not preserve extraction behavior: on the three verification examples it predicted no main blocks and produced empty Markdown. Because this did not maintain accuracy, the int8 model is not the validated artifact here. See quantized_report.json for the measured failure details.

Reproduction

The accepted model was exported with torch.onnx.export, opset 17 requested and opset 18 emitted by the current PyTorch exporter. ONNX Runtime extended optimization was tested but introduced com.microsoft fused nodes, so the uploaded model.onnx uses ORT basic optimization to keep the graph standard-domain and checker-clean.

python scripts/export_onnx.py --source-model artifacts/source_model --output-dir artifacts/onnx_model
python scripts/verify_onnx.py --source-model artifacts/source_model --onnx-model artifacts/onnx_model/model.onnx

Model weights retain the source model's CC BY-NC 4.0 license.

Downloads last month
-
Inference Providers NEW
This model isn't deployed by any Inference Provider. ๐Ÿ™‹ Ask for provider support

Model tree for Mike0021/pulpie-orange-small-onnx

Quantized
(2)
this model

Space using Mike0021/pulpie-orange-small-onnx 1