Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
a1c3772
Add VLM support to GOLDTrainer (without vLLM yet)
Strongich Apr 5, 2026
a92473b
add vlm support with use_vllm=True
Strongich Apr 5, 2026
23784b0
Add cross-architecture VLM distillation support to GOLDTrainer
Strongich Apr 6, 2026
9a1f345
fix collator mutation bug and reject Liger kernel for VLMs
Strongich Apr 6, 2026
606d68d
pass tokenizer instead of processing_class to the ULDLoss
Strongich Apr 6, 2026
159e7a3
fix prompt_lengths split to use min instead of max after flush_left
Strongich Apr 7, 2026
bd820ad
fix prompt length split for JSD loss
Strongich Apr 7, 2026
571099e
batch VLM vLLM generation across slices & fix VLM dataset columns str…
Strongich Apr 7, 2026
eaa258d
fix VLM data pipeline
Strongich Apr 7, 2026
7c96055
Merge branch 'main' into gold_vlm_support
Strongich Apr 7, 2026
77f4ccf
fix multimodal key shape missmatch for on-policy gen
Strongich Apr 8, 2026
fd3be85
precommit ruff format
Strongich Apr 8, 2026
719c560
fix: pre-duplicate prompts for num_generations > 1 in VLM vLLM path
Strongich Apr 21, 2026
d3dcf08
wrap on-policy VLM completions as content blocks
Strongich Apr 21, 2026
23a294f
Strip student chat-template markers from ULD text fields in VLM collator
Strongich Apr 21, 2026
9c661c6
ignore empty VLM label rows from zeroing JSD loss
Strongich Apr 21, 2026
5c44e97
switch to torch_dtype
Strongich May 16, 2026
7f65ae3
Merge branch 'main' into gold_vlm_support
Strongich May 16, 2026
d9d72c5
Merge commit '7f65ae3b8e75cc37e949649a5538b4116c0038be' into gold_vlm…
Strongich May 16, 2026
794a664
fix inplace mutation & vlm tests
Strongich May 16, 2026
9b072f3
remove deprecated paged_attention
Strongich May 16, 2026
fbd9793
deepcopy only on-policy slices & remove dead code
Strongich May 16, 2026
3b5940e
emit explicit sequence tensors & fix fragile code in utils
Strongich May 16, 2026
0128467
add safe .get() to VLMcollator & fix separate tokenization edge case …
Strongich May 16, 2026
81866b6
remove manual duplication to use RepeatSampler & add args.vllm_tensor…
Strongich May 16, 2026
dcd557b
remove deepcopy to save memory & empty from memory processed slices
Strongich May 16, 2026
19cdc21
reduce peak memory by deferring slice collation
Strongich May 16, 2026
9354147
store lazy payloads when _teacher_processor is not None
Strongich May 17, 2026
a36800c
add an explicit teacher_processor for same family VLMs when use_uld_l…
Strongich May 17, 2026
831b448
store original text not from the decoded text
Strongich May 17, 2026
b23e663
consistent rendering through teacher processor
Strongich May 17, 2026
61d1efc
use skip_special_tokens = True in non-vLLM on-policy path
Strongich May 17, 2026
89704c1
remove unused slice_prompts_text_special
Strongich May 17, 2026
f4ee78b
generalize VLM model kwargs
Strongich May 17, 2026
2798388
remove dead_code & \n separations for the original_text downstream tasks
Strongich May 17, 2026
d9307bb
change examples & comletion tokens, comp_idx dtyle fixes
Strongich May 18, 2026
9215f99
Merge branch 'main' into gold_vlm_support
Strongich May 22, 2026
81b6063
delete dead code & fix eval collator
Strongich May 23, 2026
f011c65
Merge commit '9215f998f567c63632fdaef9d7573fe7d03e004c' into gold_vlm…
Strongich May 23, 2026
3c460c0
Merge branch 'main' into gold_vlm_support
Strongich May 23, 2026
25d9cad
fix forward kwargs for cross-tokenizer text-only path
Strongich May 23, 2026
4cfeb37
fix VLM collator tokenizer & align empty-image handling across rollou…
Strongich May 23, 2026
6dfd8fe
support VLM eval via GRPO-style prediction_step
Strongich May 23, 2026
325f897
add eval to GOLD VLM examples
Strongich May 23, 2026
fab8a94
Merge branch 'main' into gold_vlm_support
Strongich May 25, 2026
c187966
drop defensive guards & dedup VLM setup
Strongich May 26, 2026
030a831
Merge branch 'main' into gold_vlm_support
kashif May 26, 2026
3b04456
Merge branch 'main' into gold_vlm_support
Strongich May 26, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
196 changes: 196 additions & 0 deletions examples/scripts/gold_vlm.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
# Copyright 2020-2026 The HuggingFace Team. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""
GOLD VLM distillation on GEOQA_R1V.

# Same-family distillation (Qwen3-VL-8B → Qwen3-VL-2B)
# Uses JSD loss. Same architecture and tokenizer, so standard distillation works directly.
# vLLM enabled for faster on-policy generation.
accelerate launch examples/scripts/gold_vlm.py \
--student_model_name Qwen/Qwen3-VL-2B-Instruct \
--teacher_model_name Qwen/Qwen3-VL-8B-Instruct

# Cross-family distillation (Qwen3-VL-8B → LFM2.5-VL-1.6B)
# Uses ULD loss for different tokenizers/processors. vLLM is disabled because this path uses local VLM generation.
accelerate launch examples/scripts/gold_vlm.py \
--student_model_name LiquidAI/LFM2.5-VL-1.6B \
--teacher_model_name Qwen/Qwen3-VL-8B-Instruct \
--use_uld_loss \
--no-use_vllm
"""

import argparse

import torch
from datasets import load_dataset
from peft import LoraConfig
from transformers import AutoModelForImageTextToText, AutoProcessor

from trl.experimental.gold import GOLDConfig, GOLDTrainer


SYSTEM_PROMPT = "Answer with a single number followed by the ° symbol."


def normalize_solution(solution):
solution = str(solution).replace("<answer>", "").replace("</answer>", "").strip()
if solution and not solution.endswith("°"):
solution = f"{solution}°"
return solution


def make_conversation(example):
"""Convert GEOQA_R1V row into the chat format expected by TRL VLM trainers."""
return {
"prompt": [
{
"role": "system",
"content": [{"type": "text", "text": SYSTEM_PROMPT}],
},
{
"role": "user",
"content": [
{"type": "image"},
{"type": "text", "text": example["problem"]},
],
},
],
"completion": [
{
"role": "assistant",
"content": [{"type": "text", "text": normalize_solution(example["solution"])}],
},
],
"image": example["image"],
}


def filter_big_images(example):
image = example["image"]
return image.size[0] < 512 and image.size[1] < 512


def convert_to_rgb(example):
image = example["image"]
if image.mode != "RGB":
image = image.convert("RGB")
example["image"] = image
return example


if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--student_model_name", type=str, default="Qwen/Qwen3-VL-2B-Instruct")
parser.add_argument("--teacher_model_name", type=str, default="Qwen/Qwen3-VL-8B-Instruct")
parser.add_argument("--lmbda", type=float, default=0.5)
parser.add_argument("--use_uld_loss", action=argparse.BooleanOptionalAction, default=False)
parser.add_argument("--use_vllm", action=argparse.BooleanOptionalAction, default=True)
parser.add_argument("--vllm_mode", type=str, default="colocate")
cli_args = parser.parse_args()

# ──────────────────────────────────────────────
# Models
# ──────────────────────────────────────────────
student_model = AutoModelForImageTextToText.from_pretrained(
cli_args.student_model_name, torch_dtype=torch.bfloat16
)
teacher_model = AutoModelForImageTextToText.from_pretrained(
cli_args.teacher_model_name, torch_dtype=torch.bfloat16
)

# Freeze everything except the language model head
for name, param in student_model.named_parameters():
if "language_model" not in name:
param.requires_grad = False

processor = AutoProcessor.from_pretrained(cli_args.student_model_name, padding_side="left")

peft_config = LoraConfig(
r=16,
lora_alpha=32,
lora_dropout=0.05,
target_modules=r"^.*language_model.*\.(q_proj|k_proj|v_proj)$",
)

# ──────────────────────────────────────────────
# Dataset
# ──────────────────────────────────────────────
dataset = load_dataset("leonardPKU/GEOQA_R1V_Train_8K", split="train")
dataset = dataset.filter(filter_big_images)
dataset = dataset.map(convert_to_rgb)
dataset = dataset.map(make_conversation)

# Hold out 5% for evaluation
dataset = dataset.train_test_split(test_size=0.05, seed=42)
train_dataset = dataset["train"]
eval_dataset = dataset["test"]

# ──────────────────────────────────────────────
# Training config
# ──────────────────────────────────────────────
args = GOLDConfig(
output_dir=(
"gold-vlm-distillation-different-family" if cli_args.use_uld_loss else "gold-vlm-distillation-same-family"
),
# GOLD-specific
lmbda=cli_args.lmbda,
beta=0.5,
temperature=0.6,
max_completion_length=128,
max_grad_norm=1.0,
teacher_model_name_or_path=cli_args.teacher_model_name,
num_generations=1,
use_uld_loss=cli_args.use_uld_loss,
uld_crossentropy_weight=0.5,
uld_distillation_weight=0.5,
# vLLM
use_vllm=cli_args.use_vllm,
vllm_mode=cli_args.vllm_mode,
vllm_gpu_memory_utilization=0.5,
vllm_max_model_length=1024,
max_length=2048,
# Training schedule
per_device_train_batch_size=2,
gradient_accumulation_steps=4,
max_steps=300,
learning_rate=1e-4,
warmup_steps=10,
# Evaluation
per_device_eval_batch_size=2,
eval_strategy="steps",
eval_steps=50,
# Precision
bf16=True,
# Logging
logging_steps=10,
log_completions=True,
report_to="wandb",
)

# ──────────────────────────────────────────────
# Trainer
# ──────────────────────────────────────────────
trainer = GOLDTrainer(
model=student_model,
teacher_model=teacher_model,
args=args,
train_dataset=train_dataset,
eval_dataset=eval_dataset,
processing_class=processor,
peft_config=peft_config,
)

trainer.train()
trainer.save_model(args.output_dir)
Loading