Prepare input with image and text

January 27, 2026 · View on GitHub

Breaking the SFT Plateau: Multimodal Structured Reinforcement Learning for Chart-to-Code Generation

Lei Chen, Xuanle Zhao, Zhixiong Zeng†, Jing Huang, Liming Zheng, Yufeng Zhong, Lin Ma*

Meituan Group
† Project Leader; * Corresponding Author

MSRL (Multimodal Structured Reinforcement Learning) is a reinforcement learning strategy designed to break through the SFT performance plateau in chart-to-code generation. As the first method applying multimodal structured rewards to this domain, MSRL addresses limitations in handling information-dense visual inputs through an innovative multi-granularity reward system. The approach combines rule-based textual rewards that validate code correctness from five key dimensions with visual rewards using a "render-and-compare" mechanism to assess structural similarity between generated and original charts. MSRL employs a two-stage curriculum learning strategy, training initially on textual rewards before incorporating visual signals. Experimental results show MSRL improves the high-level metrics by 6.2% and 9.9% on ChartMimic and ReachQA benchmarks respectively, marking the first time open-source models achieve competitive performance with advanced closed-source models in the chart domain.

📢 News and Updates

  • 2026.01.27 Our MSRL is accepted by ICLR 2026! 🎉
  • 2025.08.26 We upload our model weights MSRL and MSRL-SFT to HuggingFace.
  • 2025.08.19 🔥🔥🔥 We release the technical report of MSRL at arXiv link.

🤗 Models

ModelDownload Link
MSRL-SFTDocTron/MSRL-SFT
MSRLDocTron/MSRL

The MSRL-SFT employs Qwen2.5VL-7B-Instruct as the initial model and performs supervised fine-tuning with a 2.8M Chart2Code dataset. The MSRL builds upon the SFT model and undergoes two-stage RL training using a high-quality 33K Chart2Code dataset.

📊 Performance

Model Params ChartMimic ReachQA
Exec.Rate Low-Level High-Level Exec.Rate Low-Level High-Level
Proprietary
GeminiProVision - 68.2 53.8 53.3 74.0 67.0 67.8
Claude-3-opus - 83.3 60.5 60.1 89.0 51.7 61.1
GPT-4V - 91.2 76.4 78.9 88.0 69.5 78.6
GPT-4o - 93.2 79.0 83.5 92.8 81.8 84.0
Open-Source General-Domain
Qwen2-VL-7B 7B 67.0 32.9 35.0 55.4 22.6 29.3
Qwen2.5-VL-7B 7B 73.2 44.6 41.6 62.2 36.9 37.6
InternVL2-8B 8B 61.8 34.4 38.9 50.8 24.1 24.2
InternVL2-26B 26B 69.3 41.4 47.4 55.4 29.0 28.8
Qwen2-VL-72B 72B 73.3 54.4 50.9 77.2 50.0 48.1
Open-Source Chart-Domain
ChartLlama 13B 57.5 24.8 28.1 54.8 11.1 8.1
TinyChart 3B 42.5 26.3 25.9 34.4 11.6 11.2
ChartVLM-L 14B 19.5 15.8 13.9 8.2 2.1 3.9
Chart2Code 7B 62.1 42.9 33.3 63.6 52.3 49.7
ChartCoder 7B 91.4 72.5 74.0 83.8 67.9 69.4
MSRL-SFT 7B 93.2 73.0 77.6 92.2 78.6 80.0
MSRL 7B 96.5 78.6 83.8 98.2 86.1 89.9

🔍 Usage Example

Below is an example of how to use MSRL for chart-to-code generation:

from transformers import Qwen2_5_VLForConditionalGeneration, AutoProcessor
from qwen_vl_utils import process_vision_info

# Load model
model_path = 'DocTron/MSRL'
# model_path = 'DocTron/MSRL-SFT'

# Load the model on the available device(s)
model = Qwen2_5_VLForConditionalGeneration.from_pretrained(model_path, torch_dtype="auto", device_map="cuda")

# Use the following instruction and pixel range by default
instruction = """
You are an expert Python developer who specializes in writing matplotlib code based on a given picture. I found a very nice picture in a STEM paper, but there is no corresponding source code available. I need your help to generate the Python code that can reproduce the picture based on the picture I provide.
Now, please give me the matplotlib code that reproduces the picture below, starting with "```python" and ending with "```".
"""

processor = AutoProcessor.from_pretrained(model_path, min_pixels=1280*28*28, max_pixels=16384*28*28)

# Prepare input with image and text
messages = [
    {"role": "user", "content": [
            {"type": "image", "image": "assets/example_case.jpg"},
            {"type": "text", "text": instruction},
        ]
    },
]

# Preparation for inference
text = processor.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
images, videos = process_vision_info([messages])
inputs = processor(text=text, images=images, videos=videos, padding=True, return_tensors='pt')
inputs = inputs.to(model.device)

# Inference: Generation of the output
generated_ids = model.generate(**inputs, max_new_tokens=4096, top_p=1, temperature=0.1)
generated_ids = [
    output_ids[len(input_ids):] for input_ids, output_ids in zip(inputs.input_ids, generated_ids)
]
generated_code = processor.tokenizer.batch_decode(
    generated_ids, skip_special_tokens=True, clean_up_tokenization_spaces=False
)[0]
print(generated_code)

📌 Acknowledgement

We sincerely appreciate LLaMA-Factory and MM-EUREKA for providing reference training framework.

📖 Citation

If you find this project useful, please feel free to leave a star and cite our paper:

@misc{chen2025breaking,
      title={Breaking the SFT Plateau: Multimodal Structured Reinforcement Learning for Chart-to-Code Generation}, 
      author={Lei Chen and Xuanle Zhao and Zhixiong Zeng and Jing Huang and Liming Zheng and Yufeng Zhong and Lin Ma},
      year={2025},
      eprint={2508.13587},
      archivePrefix={arXiv},
      primaryClass={cs.AI},
      url={https://arxiv.org/abs/2508.13587}, 
}