""" Offline supervised fine-tuning for ChartQA (two-stage cold start before RLSD/OPD). Usage: accelerate launch main_sft.py --config config/config_rlsd_chartqa.py bash scripts/train_chartqa_sft.sh """ from __future__ import annotations import argparse import os from functools import partial from accelerate import Accelerator from datasets import Dataset from transformers import Trainer, TrainingArguments from config.loader import load_config from data_utils.commom_util import collate_fn, define_task_data_func from main import load_model_and_processor def main() -> None: parser = argparse.ArgumentParser(description="ChartQA offline SFT (hint + Answer GT).") parser.add_argument( "--config", type=str, default="config/config_rlsd_chartqa.py", help="Config module (uses training.sft_args and dataset.train_dataset).", ) parser.add_argument( "--pretrained_model_path", type=str, default=None, help="Override CONFIG model path (e.g. base 0.5B before RL).", ) args = parser.parse_args() config = load_config(args.config) model_config = dict(config["model"]) if args.pretrained_model_path: model_config["pretrained_model_path"] = args.pretrained_model_path training_config = config["training"] task = training_config["task"] sft_args = dict(training_config.get("sft_args") or config.get("training", {}).get("sft_args", {})) if not sft_args: raise ValueError("Config must define training.sft_args for offline SFT.") output_dir = os.environ.get("DYME_SFT_OUTPUT_DIR", sft_args.get("output_dir", "./outputs/chartqa-sft")) sft_args["output_dir"] = output_dir sft_args.setdefault("remove_unused_columns", False) accelerator = Accelerator() if accelerator.is_main_process: os.makedirs(output_dir, exist_ok=True) model, processor = load_model_and_processor(model_config) data_func = define_task_data_func(task, mode="sft") train_list = data_func(json_path=config["dataset"]["train_dataset"]) train_dataset = Dataset.from_list(train_list) label_id = processor.tokenizer.convert_tokens_to_ids("<|im_start|>") data_collator = partial(collate_fn, processor=processor, label_id=label_id) train_args = TrainingArguments(**sft_args) trainer = Trainer( model=model, args=train_args, train_dataset=train_dataset, data_collator=data_collator, ) trainer.train() trainer.save_model(os.path.join(output_dir, "final_checkpoint")) if accelerator.is_main_process: processor.save_pretrained(os.path.join(output_dir, "final_checkpoint")) if __name__ == "__main__": main()