--- license: mit base_model: Qwen/Qwen2.5-VL-7B tags: - vision-language - document-to-markdown - reinforcement-learning - grpo - qwen2.5 - markdown model_name: NuMarkdown-reasoning library_name: transformers pipeline_tag: text-generation ---

🖥️ API / Platform   |   📑 Blog   |   🗣️ Discord

--- # NuMarkdown-reasoning 📄 **NuMarkdown-reasoning** is the first reasoning vision-language model trained specifically to convert documents into clean GitHub-flavoured Markdown. It is a fine-tune of **Qwen 2.5-VL-7B** using ~10 k synthetic doc-to-Reasoning-to-Markdown pairs, followed by a RL phase (GRPO) with a layout-centric reward. *(note: the number of thinking tokens can vary from 20% to 2X the number of token of the final answers)* ## Results **NuMarkdown-reasoning** is significantly better than similar size non-reasoning models trained for markdown generation on complex documents, and achieve competitive results against top close sources alternatives. ### Arena ranking (using trueskill-2 ranking system):

| Rank | Model | μ | σ | μ − 3σ | | ---- | --------------------------------------- | ----- | ---- | ------ | | 🥇 1 | **gemini-flash-reasoning** | 26.75 | 0.80 | 24.35 | | 🥈 2 | **NuMarkdown-reasoning** | 26.10 | 0.79 | 23.72 | | 🥉 3 | **NuMarkdown-reasoning-w/o\_grpo** | 25.32 | 0.80 | 22.93 | | 4 | **OCRFlux-3B** | 24.63 | 0.80 | 22.22 | | 5 | **gpt-4o** | 24.48 | 0.80 | 22.08 | | 6 | **gemini-flash-w/o\_reasoning** | 24.11 | 0.79 | 21.74 | | 7 | **RolmoOCR** | 23.53 | 0.82 | 21.07 |

*we plan to realease a markdown arena, similar to llmArena, for complex document to markdown task to help evaluate different document to markdown solution* ### Win-rate against others models (image-only):

## Training 1. **SFT**: One-epoch supervised fine-tune on synthetic reasoning trace generated from public PDFs (10K input/output pairs). 2. **RL (GRPO)**: RL phase using a structure-aware reward (5K difficults image examples). **Model before GRPO loose 80% time vs post GRPO model (see win-rate matrix)** ## Quick start: 🤗 Transformers ```python from __future__ import annotations import torch from PIL import Image from transformers import AutoProcessor, Qwen2_5_VLForConditionalGeneration model_id = "Numind/NuMarkdown-reasoning" processor = AutoProcessor.from_pretrained( model_id, trust_remote_code=True, ) model = Qwen2_5_VLForConditionalGeneration.from_pretrained( model_id, torch_dtype=torch.bfloat16, attn_implementation="flash_attention_2", device_map="auto", trust_remote_code=True, ) img = Image.open("invoice.png").convert("RGB") messages = [{ "role": "user", "content": [ {"type": "image"}, ], }] prompt = processor.apply_chat_template(messages, tokenize=False, add_generation_prompt=True) enc = processor(text=prompt, images=[img], return_tensors="pt").to(model.device) with torch.no_grad(): out = model.generate(**enc, max_new_tokens=5000) print(processor.decode(out[0].split("")[1].split("")[0], skip_special_tokens=True)) ``` ## VLLM: ```python from PIL import Image from vllm import LLM, SamplingParams from transformers import AutoProcessor model_id = "Numind/NuMarkdown-reasoning" llm = LLM( model=model_id, tokenizer=model_id, dtype="bfloat16", gpu_memory_utilization=0.85, max_num_seqs=256, enforce_eager=True, trust_remote_code=True ) sampling_params = SamplingParams( temperature=0.8, max_tokens=5000, ) processor = AutoProcessor.from_pretrained(model_id, trust_remote_code=True) inputs = [] messages = [{ "role": "user", "content": [ {"type": "image"}, # {"type": "text", "text": guideline}, ] }] prompt = processor.apply_chat_template( messages, tokenize=False, add_generation_prompt=True, ) image = Image.open("invoice.png").convert("RGB") inputs.append({ "prompt": prompt, "multi_modal_data": {"image": image} }) outs = llm.generate(inputs, sampling_params) preds = [o.outputs[0].text.strip().split("")[1].split("")[0] for o in outs] ```