Instructions to use oklenAI/udm_doc_extract_qwen3.5_2B with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use oklenAI/udm_doc_extract_qwen3.5_2B with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-generation", model="oklenAI/udm_doc_extract_qwen3.5_2B") messages = [ {"role": "user", "content": "Who are you?"}, ] pipe(messages)# Load model directly from transformers import AutoTokenizer, AutoModelForCausalLM tokenizer = AutoTokenizer.from_pretrained("oklenAI/udm_doc_extract_qwen3.5_2B") model = AutoModelForCausalLM.from_pretrained("oklenAI/udm_doc_extract_qwen3.5_2B", device_map="auto") messages = [ {"role": "user", "content": "Who are you?"}, ] inputs = tokenizer.apply_chat_template( messages, add_generation_prompt=True, tokenize=True, return_dict=True, return_tensors="pt", ).to(model.device) outputs = model.generate(**inputs, max_new_tokens=40) print(tokenizer.decode(outputs[0][inputs["input_ids"].shape[-1]:])) - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- vLLM
How to use oklenAI/udm_doc_extract_qwen3.5_2B with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "oklenAI/udm_doc_extract_qwen3.5_2B" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "oklenAI/udm_doc_extract_qwen3.5_2B", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }'Use Docker
docker model run hf.co/oklenAI/udm_doc_extract_qwen3.5_2B
- SGLang
How to use oklenAI/udm_doc_extract_qwen3.5_2B with SGLang:
Install from pip and serve model
# Install SGLang from pip: pip install sglang # Start the SGLang server: python3 -m sglang.launch_server \ --model-path "oklenAI/udm_doc_extract_qwen3.5_2B" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "oklenAI/udm_doc_extract_qwen3.5_2B", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }'Use Docker images
docker run --gpus all \ --shm-size 32g \ -p 30000:30000 \ -v ~/.cache/huggingface:/root/.cache/huggingface \ --env "HF_TOKEN=<secret>" \ --ipc=host \ lmsysorg/sglang:latest \ python3 -m sglang.launch_server \ --model-path "oklenAI/udm_doc_extract_qwen3.5_2B" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "oklenAI/udm_doc_extract_qwen3.5_2B", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }' - Docker Model Runner
How to use oklenAI/udm_doc_extract_qwen3.5_2B with Docker Model Runner:
docker model run hf.co/oklenAI/udm_doc_extract_qwen3.5_2B
Qwen3.5-2B distilled for math-content extraction from web pages
Give it the raw text of a web page; it returns just the mathematical content, verbatim.
Trained on 197,269 (page → extraction) pairs produced by GPT-5.6 on UltraData-Math
L2-preview — the full udm_l2preview_gpt56_extract_200k train split.
This is a student of GPT-5.6, not an oracle. Every number below measures agreement with the teacher's extraction on held-out pages. It does not measure whether the teacher was right.
Measured on 498 held-out pages
| value | |
|---|---|
| charF1 vs teacher | 0.9453 |
| LCS vs teacher | 0.9423 |
| strict order-preservation | 97.14% |
| contiguous-span coverage (mean) | 0.9801 |
| pages with coverage < 0.9 | 6.02% |
| length-ratio error | 0.0620 |
| formula-token excess vs source | 10.64% |
| boilerplate residue | 3.21% |
| empty-output rate | 0.00% |
| 20-gram repeat rate (mean) | 0.0078 |
| pages with repeat rate > 0.3 | 0.8% |
The held-out set is a 498-page stratified slice of the dataset's own heldout split
(page length × formula density), content-disjoint from training.
More data would not have helped — we checked
| Train rows | 5,002 | 19,999 | 100,000 | 197,269 (this model) |
|---|---|---|---|---|
| charF1 | 0.9116 | 0.9325 | 0.9447 | 0.9453 |
Going from 100,000 to 197,269 moved charF1 by +0.0007, 95% CI [−0.0037, +0.0054]
(paired bootstrap, same 498 pages; 97 pages better, 89 worse, 312 identical). Fitting the four
points to y = C − A·n^(−b) gives a ceiling of 0.9512 with max residual 0.0010 — inside the
0.0054 evaluation standard error. From here, another +0.005 would take ≈4.6M pairs, 23× the
training set.
Practical consequence: the 100,000-row checkpoint is statistically indistinguishable from this one (it is very slightly better on contiguous coverage, very slightly worse on charF1, neither significant). If you are reproducing this, train on 100,000 and save half the compute. We released the full-pool model because it is the endpoint of the curve, not because it is better.
Usage
The prompt is not optional — the model was trained with this exact instruction as a prefix, and
it is shipped as extract_prompt.txt.
import torch
from transformers import AutoTokenizer, AutoModelForCausalLM
M = "path/to/this/model"
tk = AutoTokenizer.from_pretrained(M)
model = AutoModelForCausalLM.from_pretrained(M, dtype=torch.bfloat16, device_map="cuda")
PROMPT = open(f"{M}/extract_prompt.txt").read().rstrip()
def extract(page_text: str, max_new_tokens: int = 6144) -> str:
ids = tk(PROMPT + "\n\n" + page_text + "\n\n", return_tensors="pt").to(model.device)
out = model.generate(**ids, max_new_tokens=max_new_tokens, do_sample=False)
return tk.decode(out[0][ids["input_ids"].shape[1]:], skip_special_tokens=True)
Greedy decoding. Our evaluation used max_new_tokens=6144; shorter caps silently truncate the
long pages, which are the ones that matter.
Training recipe
Base Qwen3.5-2B-Base. One epoch, lr 1e-5, per-device batch 1 × grad-accum 16 (effective batch 16), max sequence length 12,288, bf16, SDPA attention, gradient checkpointing on, warmup ratio 0.03, cosine schedule, weight decay 0, grad-norm clip 1.0, seed 7. Loss is masked to the output span only — the page is context the student is given, not something it should learn to reproduce, or it could score well by echoing input. Sequences over the limit are truncated from the input side: losing the page tail beats losing the target being scored.
1,724 minutes on one A100-80GB. Every rung of the scaling curve above used this recipe byte-for-byte; the only variable was the number of training rows.
Limits — read before running it over a corpus
formula-token excess 10.64%is not 10.64% hallucination. The criterion counts any excess of one token, and the prompt itself mandates notation repair. The tail worth watching is excess > 10 tokens. Measure repetition directly (we use a 20-gram repeat rate) rather than trusting the token count as a proxy.- 0.8% of pages come back with a 20-gram repeat rate above 0.3 — that is a generation loop, and at corpus scale it is thousands of pages. Filter on it; do not assume the empty-output rate of 0.00% means nothing goes wrong.
- Empty output is a valid answer. The prompt instructs the model to emit nothing when a page has no substantive math. The 0.00% empty rate above is on 498 math-bearing pages and says nothing about behaviour on non-math pages.
- The model has never seen a page longer than 20,000 characters. This is the sharpest
limitation and it is not obvious from the training recipe. The corpus this model was
distilled on was normalised with an upper cut that dropped pages over 20,000 chars
rather than truncating them, so the longest training page is 19,997 chars (measured over
all 197,269 pairs). The dataset's length stratification has a
>12Kbucket holding 19.2% of the data — but its real range is 12K–20K, because stratification ran after that cut. Measured against a realistic target corpus (TeraflopAI/udml2-labeled): 28.1% of pages are longer than the longest page in training by characters, 5.5% by tokens, up to 49,953 chars / 24,961 tokens. On that band the model is out of distribution and we have not measured how it behaves. Do not truncate to compensate — the context window is 262,144 and the whole page fits; truncation guarantees loss where full input merely risks it. Measure output quality on long pages before trusting them. - Trained at 12,288 tokens.
max_position_embeddingsin the config is 262,144 because the base model allows it. 1,692 training pairs (0.86%) had their input clipped by this limit, losing 2.95M input tokens in total; one pair had its target clipped. Behaviour far beyond the training length is untested. - Faithfulness is measured against the teacher, by construction. In the parent dataset, 99.722% of generated characters appear verbatim in the source page and net altered text is 0.1733% — but that is measured on the teacher's output. This student was not audited against source pages directly.
- English web pages of mathematical content. Other languages and other domains are untested.
Files
| File | MD5 |
|---|---|
model.safetensors |
74a23296647d219952d18d6ebe31a6ea |
tokenizer.json |
4010c9c068169f29bca5d89c5d8e967c |
config.json |
d6c1c2df441ab7e4968d1b70c86db3a2 |
extract_prompt.txt |
5fb78ad1d48cc54fb94db3304a1afbe4 |
- Downloads last month
- -
Model tree for oklenAI/udm_doc_extract_qwen3.5_2B
Base model
Qwen/Qwen3.5-2B-Base