Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
fec2744
feature(math_prm): keep minimal upstream stage3 path
HansBug Mar 20, 2026
d8590af
fix(math_prm): sync stage3 rollout updates from working branch
HansBug Mar 20, 2026
3ff0caf
fix(wandb): remove live heartbeat logging
HansBug Mar 22, 2026
6ae4d56
fix(math_prm): sync runtime eval updates from working branch
HansBug Mar 26, 2026
3f5470a
style(math_prm): remove trailing whitespace from stage3 files
HansBug Mar 26, 2026
a81a817
fix(strategy): reload keep-on-gpu rollout actor after sync
HansBug Mar 31, 2026
902050a
fix(math_prm): sync stage3 training path from working branch
HansBug Apr 27, 2026
a36c860
fix(math_prm): bring stage3 doc/training/runtime updates from working…
HansBug Apr 27, 2026
d25d64e
Merge remote-tracking branch 'main/main' into dev/math_prm_train
HansBug Apr 27, 2026
34e97b1
fix(math_prm): address PR #53 review comments
HansBug Apr 27, 2026
ceb4441
fix(profile): restore profile_recorder.py and re-export StepProfileRe…
HansBug Apr 27, 2026
b905c23
fix(math_prm): K1 KL estimator + freeze fix + ratio diagnostics
HansBug Apr 29, 2026
67c1d31
fix(math_prm): align URSA log_probs with VLM forward and revert KL hacks
HansBug Apr 30, 2026
cce5ae5
fix(math_prm): protect PRM from actor-leaked image tokens + add resum…
HansBug May 5, 2026
3c54da8
fix(math_prm): align rollout/eval with bare HF generate; gate EOS pat…
HansBug May 7, 2026
f23e687
feat(math_prm): add per-step PRM reward path (URSA paper variant 2)
HansBug May 8, 2026
95ee722
fix(math_prm): URSA-native step-boundary alignment for per-step PRM r…
HansBug May 8, 2026
2220049
feat(math_prm): add --per_step_reward_mode {raw,group_norm} for varia…
HansBug May 8, 2026
f6987cf
chore(math_prm): wire .env auto-source + PER_STEP_REWARD_MODE into of…
HansBug May 8, 2026
4eda088
chore(math_prm): map LIGHTRFT_WANDB_API_KEY -> WANDB_API_KEY in launc…
HansBug May 8, 2026
9e5cbd5
fix(math_prm): protect actor.forward from actor-leaked <|image|> tokens
HansBug May 11, 2026
0fca7c1
fix(math_prm): cap eval DataLoader bs by local_hf_generate_max_batch_…
HansBug May 11, 2026
3b45ea9
chore: route math prm outputs to configured root
HansBug May 18, 2026
f901761
fix(math_prm): keep URSA logprob forward cache-free
HansBug May 18, 2026
db4df06
feat(math_prm): strict URSA paper Eq.9 advantage estimator + diagnostics
HansBug May 25, 2026
e7513e0
fix(math_prm): broaden ursa_variant2 monkey-patch + smoke-test plumbing
HansBug May 25, 2026
2663a5e
feat(math_prm): forward step_rewards through multi-RM aggregator + ch…
HansBug May 25, 2026
1a7cabc
chore(math_prm): variant2 launch — tee training log so it shows live …
HansBug May 25, 2026
230977b
Merge upstream/main into dev/math_prm_train
HansBug Jun 3, 2026
956a850
math_prm: address Agent Review #1 — clean debug artifacts + README re…
HansBug Jun 3, 2026
215ba1a
math_prm: address Agent Review #2 — clean stale docs + explicit register
HansBug Jun 3, 2026
4b7ab05
math_prm: drop inert --per_step_reward_mode CLI arg from variant 2 la…
HansBug Jun 3, 2026
95cb755
style(lightrft): yapf reformat 7 files flagged by format-check
HansBug Jun 3, 2026
11c3b4e
fix(lightrft): drop merge leftovers that broke flake8
HansBug Jun 3, 2026
bbfdaa8
fix(lightrft): restore generate_fn def + suppress zero general_model_…
HansBug Jun 3, 2026
a6202bb
fix(lightrft): narrow abs-zero skip set to general_model_reward only
HansBug Jun 3, 2026
ca33772
docs(math_prm): R5 polish — drop unused deps, fix README step labels,…
HansBug Jun 3, 2026
0b219aa
fix(math-prm): address review compatibility issues
HansBug Jul 1, 2026
b5be0ce
fix(math-prm): penalize post-answer continuation
HansBug Jul 2, 2026
f085791
style: apply CI formatting fixes
HansBug Jul 2, 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
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -1219,4 +1219,8 @@ wandb*
examples/demo_grpo/results*
build/*
examples/math_benchmarks/eval_results/
.llmconfig.yaml
.llmconfig.yaml

# Local agent tool state
.claude/
.codex
2 changes: 1 addition & 1 deletion docs/source/best_practice/faq_zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
- 更好的显存优化(推理引擎休眠、优化器卸载)
- 改进的推理引擎(vLLM, SGLang)
- 为了提高效率,支持奖励模型 (RM) 的同机部署 (Co-location)
- 更灵活的分布式训练策略,支持 FSDP 和 DeepSpeed ZeRO
- 更灵活的分布式训练策略,支持 FSDP 和 DeepSpeed ZeRO

### Q: 支持哪些模型?

Expand Down
2 changes: 1 addition & 1 deletion docs/source/best_practice/model.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ response = "The image shows a cute brown dog playing in the park."

# 1. Scalar Reward Model
srm_score = srm(image=image, system_prompt=system_prompt, query=query, response=response)
print(srm_score)
print(srm_score)
# Output: tensor([0.88]) -> Yields a direct scalar float value

# 2. Generative Reward Model
Expand Down
2 changes: 1 addition & 1 deletion docs/source/best_practice/model_zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ response = "The image shows a cute brown dog playing in the park."

# 1. 标量奖励模型 (SRM)
srm_score = srm(image=image, system_prompt=system_prompt, query=query, response=response)
print(srm_score)
print(srm_score)
# 输出: tensor([0.88]) -> 得出一个直接的浮点标量分数值

# 2. 生成式奖励模型 (GRM)
Expand Down
2 changes: 1 addition & 1 deletion examples/gsm8k_geo3k/run_ppo_geo3k_qwen2.5_vl_7b.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
# - Answer Accuracy (90%): Correctness of the final answer.
#
# PPO vs GRPO Note:
# Unlike GRPO, this PPO setup uses GAE (Generalized Advantage Estimation), requires a
# Unlike GRPO, this PPO setup uses GAE (Generalized Advantage Estimation), requires a
# Critic model, and treats KL divergence as a reward penalty rather than a loss term.
#

Expand Down
74 changes: 37 additions & 37 deletions examples/gsm8k_geo3k/test_geo3k_lora.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
"""
LoRA Evaluation Script for Geo3K Dataset

This script evaluates a LoRA-fine-tuned vision-language model (e.g. Qwen2.5-VL)
on mathematical reasoning benchmarks (e.g. Geo3K). It automatically merges the LoRA adapter
This script evaluates a LoRA-fine-tuned vision-language model (e.g. Qwen2.5-VL)
on mathematical reasoning benchmarks (e.g. Geo3K). It automatically merges the LoRA adapter
into the base weights and runs high-throughput inference using vLLM engine.

Key Features:
Expand Down Expand Up @@ -64,11 +64,11 @@ def is_rank_0(self):
class MockArgs:
"""
Mock arguments class to simulate training arguments for data blending.

This guarantees compatibility with LightRFT's dataset loading utilities
which expect a parsed arguments object containing specific configuration keys.
"""

def __init__(self, seed: int = 42, **kwargs):
self.seed = seed
self.input_key = "prompt"
Expand All @@ -84,7 +84,7 @@ def __init__(self, seed: int = 42, **kwargs):
def parse_args() -> argparse.Namespace:
"""
Parse command-line arguments for evaluation.

:return: Parsed arguments
:rtype: argparse.Namespace
"""
Expand Down Expand Up @@ -117,11 +117,11 @@ def parse_args() -> argparse.Namespace:
def merge_lora_weights(base_model_path: str, lora_path: str, save_dir: str) -> str:
"""
Merge LoRA weights into the base model and save to disk.

This function loads the base model and LoRA adapter, merges them, and saves
the resulting full weights along with the tokenizer and processor. This ensures
that vLLM can load a unified model directly for high-throughput inference.

:param base_model_path: Path to the underlying base model
:type base_model_path: str
:param lora_path: Path to the LoRA adapter weights
Expand All @@ -137,32 +137,32 @@ def merge_lora_weights(base_model_path: str, lora_path: str, save_dir: str) -> s
torch_dtype=torch.bfloat16,
device_map="auto"
)

print(f"Loading LoRA from {lora_path}...")
model = PeftModel.from_pretrained(model, lora_path)

print("Merging adapter...")
model = model.merge_and_unload()

print(f"Saving merged model to {save_dir}...")
model.save_pretrained(save_dir, safe_serialization=True)

tokenizer, processor = get_tokenizer_processor_vl(base_model_path, model, "left", use_fast=True)
tokenizer.save_pretrained(save_dir)
processor.save_pretrained(save_dir)

print("Merged model saved successfully.")
return save_dir


def evaluate_model(model_path: str, args: argparse.Namespace) -> None:
"""
Evaluate the merged model using vLLM on the specified dataset.

Loads configuration and data identically to the training pipeline,
generates responses using vLLM, and calculates accuracy and format rewards
generates responses using vLLM, and calculates accuracy and format rewards
using the predefined reward functions.

:param model_path: Path to the merged model weights
:type model_path: str
:param args: Parsed command-line arguments with evaluation settings
Expand All @@ -172,15 +172,15 @@ def evaluate_model(model_path: str, args: argparse.Namespace) -> None:
mock_args = MockArgs(seed=args.seed, system_prompt=args.system_prompt)
# create a dynamic object that PromptDatasetVL expects for strategy.args
mock_strategy = type('MockStrategyParams', (), {'args': mock_args, 'print': print, 'is_rank_0': lambda self: True})()

tokenizer, processor = get_tokenizer_processor_vl(model_path, None, "left", use_fast=True)

print(f"Loading evaluation data from {args.eval_data}, split='{args.eval_split}'...")
eval_data = blending_datasets(
args.eval_data, "1.0", mock_strategy, args.seed, return_eval=False,
train_split=args.eval_split
)

if args.max_samples:
eval_data = eval_data.select(range(min(args.max_samples, len(eval_data))))

Expand All @@ -192,9 +192,9 @@ def evaluate_model(model_path: str, args: argparse.Namespace) -> None:
strategy=mock_strategy,
input_template=None
)

print(f"Evaluation dataset loaded: {len(eval_dataset)} samples")

print("Initializing vLLM engine...")
engine = LLM(
model=model_path,
Expand All @@ -203,12 +203,12 @@ def evaluate_model(model_path: str, args: argparse.Namespace) -> None:
max_model_len=4096,
limit_mm_per_prompt={"image": 10}
)

im_end_id = tokenizer.convert_tokens_to_ids("<|im_end|>")
stop_token_ids = [tokenizer.eos_token_id]
if im_end_id is not None:
stop_token_ids.append(im_end_id)

sampling_params = SamplingParams(
temperature=args.temperature,
top_p=args.top_p,
Expand All @@ -218,56 +218,56 @@ def evaluate_model(model_path: str, args: argparse.Namespace) -> None:

vllm_inputs = []
refs = []

for i in range(len(eval_dataset)):
prompt, images, reference, label = eval_dataset[i]

inp = {"prompt": prompt}
if images and len(images) > 0:
inp["multi_modal_data"] = {"image": images}

vllm_inputs.append(inp)

refs.append(reference)

print("Running vLLM inference generation...")
outputs = engine.generate(vllm_inputs, sampling_params)

results = []
total_acc = 0
total_fmt = 0

for i, output in enumerate(outputs):
generated_text = output.outputs[0].text
gt = refs[i]

if isinstance(gt, list) and len(gt) > 0:
gt = gt[0]

acc_reward = geo3k_accuracy_reward_fn(generated_text, str(gt))
fmt_reward = geo3k_format_reward_fn(generated_text)

total_acc += acc_reward
total_fmt += fmt_reward

results.append({
"prompt": vllm_inputs[i]["prompt"],
"generated": generated_text,
"ground_truth": gt,
"accuracy": acc_reward,
"format": fmt_reward,
})

avg_acc = total_acc / len(outputs) if len(outputs) > 0 else 0
avg_fmt = total_fmt / len(outputs) if len(outputs) > 0 else 0

print(f"\n{'='*40}")
print(f"--- Final Evaluation Results ---")
print(f"Total Evaluated Samples: {len(outputs)}")
print(f"Average Accuracy Reward: {avg_acc:.4f} ({(avg_acc*100):.2f}%)")
print(f"Average Format Correctness: {avg_fmt:.4f} ({(avg_fmt*100):.2f}%)")
print(f"{'='*40}\n")

output_json_path = os.path.join(args.output_dir, "eval_results.json")
with open(output_json_path, "w", encoding="utf-8") as f:
json.dump(results, f, indent=4, ensure_ascii=False)
Expand All @@ -280,14 +280,14 @@ def evaluate_model(model_path: str, args: argparse.Namespace) -> None:

if __name__ == "__main__":
args = parse_args()

os.makedirs(args.output_dir, exist_ok=True)
merged_model_dir = os.path.join(args.output_dir, "merged_model")

if not os.path.exists(merged_model_dir):
print(f"Starting LoRA merge process...")
merge_lora_weights(args.base_model, args.lora_path, merged_model_dir)
else:
print(f"Merged model path '{merged_model_dir}' already exists. Skipping merging step...")

evaluate_model(merged_model_dir, args)
Loading
Loading