#!/usr/bin/env python3 """ Train LoRA adapter on Cyron summary dataset. Uses Hugging Face TRL for SFT training. """ import argparse import os import yaml from pathlib import Path import torch def train(config_path): """Train LoRA adapter using TRL.""" with open(config_path) as f: config = yaml.safe_load(f) from transformers import ( AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig, TrainingArguments, ) from peft import ( prepare_model_for_kbit_training, LoraConfig, get_peft_model, ) from trl import SFTTrainer, SFTConfig print(f"Loading model: {config['base_model']}") # Load model - let the model's own quantization config handle it # (Ornith uses CompressedTensors, not BitsAndBytes) model = AutoModelForCausalLM.from_pretrained( config["base_model"], torch_dtype=torch.bfloat16, device_map="auto", # Let transformers distribute across GPUs ) # Add LoRA lora_config = LoraConfig( r=config["lora_r"], lora_alpha=config["lora_alpha"], lora_dropout=config["lora_dropout"], target_modules=config["target_modules"], task_type=config.get("lora_task_type", "CAUSAL_LM"), ) model = get_peft_model(model, lora_config) model.print_trainable_parameters() # Load tokenizer tokenizer = AutoTokenizer.from_pretrained(config["base_model"]) tokenizer.pad_token = tokenizer.eos_token tokenizer.padding_side = "right" # Load dataset from datasets import load_dataset dataset = load_dataset( "json", data_files={ "train": config["dataset"][0]["path"].replace("../", ""), "test": config["dataset"][0]["path"].replace("../", "").replace("combined_20k.jsonl", "test.jsonl"), }, ) # Training arguments training_args = TrainingArguments( output_dir=config["train_params"]["output_dir"], num_train_epochs=config["train_params"]["num_train_epochs"], per_device_train_batch_size=config["train_params"]["per_device_train_batch_size"], gradient_accumulation_steps=config["train_params"]["gradient_accumulation_steps"], learning_rate=config["train_params"]["learning_rate"], lr_scheduler_type=config["train_params"]["lr_scheduler_type"], weight_decay=config["train_params"]["weight_decay"], warmup_ratio=config["train_params"]["warmup_ratio"], max_seq_length=config["train_params"]["max_seq_length"], logging_steps=config["train_params"]["logging_steps"], save_steps=config["train_params"]["save_steps"], save_total_limit=config["train_params"]["save_total_limit"], evaluation_strategy=config.get("eval_strategy", "steps"), eval_steps=config.get("eval_steps", 100), mixed_precision=config.get("mixed_precision", "bf16"), gradient_checkpointing=config.get("gradient_checkpointing", True), ) # SFT Trainer (DeepSpeed handles distributed training via config) from trl import SFTTrainer trainer = SFTTrainer( model=model, tokenizer=tokenizer, train_dataset=dataset["train"], eval_dataset=dataset["test"], args=training_args, max_seq_length=config["train_params"]["max_seq_length"], ) # Train print("Starting training...") trainer.train() # Save trainer.save_model(config["train_params"]["output_dir"]) tokenizer.save_pretrained(config["train_params"]["output_dir"]) print(f"Training complete! Model saved to {config['train_params']['output_dir']}") def main(): parser = argparse.ArgumentParser(description="Train LoRA adapter") parser.add_argument("--config", type=str, default="configs/llama2-7b-lora.yaml", help="Training configuration file") args = parser.parse_args() train(args.config) if __name__ == "__main__": main()