marathi-mitra / src /train.py
ninadp's picture
updated file structure
a1be1d1
Raw
History Blame Contribute Delete
7.22 kB
# ═══════════════════════════════════════════════════════════
# src/train.py β€” Marathi Mitra
#
# Reproduces best model using config.yaml settings
# Run: python src/train.py
#
# Prerequisites:
# 1. pip install -r requirements.txt
# 2. .env file with HF_TOKEN and HF_USERNAME
# 3. data/vocabulary.json exists
# ═══════════════════════════════════════════════════════════
import os
import sys
import json
import yaml
import torch
from dotenv import load_dotenv
from datasets import Dataset
from transformers import (
AutoModelForCausalLM,
AutoTokenizer,
BitsAndBytesConfig,
)
from peft import (
LoraConfig,
get_peft_model,
prepare_model_for_kbit_training,
)
from trl import SFTTrainer, SFTConfig
from huggingface_hub import login
# ── Add data folder to path ──────────────────────────────────
sys.path.append(os.path.join(os.path.dirname(__file__), "..", "data"))
from create_dataset import create_dataset
# ── Load config ──────────────────────────────────────────────
def load_config(path="config.yaml"):
with open(path, "r") as f:
config = yaml.safe_load(f)
print(f"βœ… Config loaded from {path}")
return config
# ── Load credentials ─────────────────────────────────────────
def load_credentials():
load_dotenv(".env")
token = os.getenv("HF_TOKEN")
username = os.getenv("HF_USERNAME")
assert token, "❌ HF_TOKEN not found in .env"
assert username, "❌ HF_USERNAME not found in .env"
print(f"βœ… Credentials loaded for: {username}")
return token, username
# ── Prepare dataset ──────────────────────────────────────────
def prepare_dataset(config):
# Regenerate dataset from vocabulary.json
print("Preparing dataset...")
examples = create_dataset()
dataset = Dataset.from_list(examples)
print(f"βœ… Dataset ready: {len(dataset)} examples")
return dataset
# ── Load base model ──────────────────────────────────────────
def load_model(config):
model_name = config["model"]["name"]
print(f"Loading base model: {model_name}")
bnb_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type="nf4",
bnb_4bit_compute_dtype=torch.float16,
bnb_4bit_use_double_quant=True,
)
model = AutoModelForCausalLM.from_pretrained(
model_name,
quantization_config=bnb_config,
device_map="auto",
trust_remote_code=True,
)
model = prepare_model_for_kbit_training(model)
tokenizer = AutoTokenizer.from_pretrained(
model_name,
trust_remote_code=True,
)
tokenizer.pad_token = tokenizer.eos_token
tokenizer.padding_side = "right"
print(f"βœ… Model loaded")
print(f"βœ… Memory: {model.get_memory_footprint() / 1e9:.2f} GB")
return model, tokenizer
# ── Apply LoRA ───────────────────────────────────────────────
def apply_lora(model, config):
lora_cfg = config["lora"]
lora_config = LoraConfig(
r=lora_cfg["r"],
lora_alpha=lora_cfg["alpha"],
target_modules=lora_cfg["target_modules"],
lora_dropout=lora_cfg["dropout"],
bias="none",
task_type="CAUSAL_LM",
)
model = get_peft_model(model, lora_config)
model.print_trainable_parameters()
return model
# ── Train ────────────────────────────────────────────────────
def train(model, tokenizer, dataset, config):
train_cfg = config["training"]
training_args = SFTConfig(
output_dir=train_cfg["output_dir"],
num_train_epochs=train_cfg["epochs"],
per_device_train_batch_size=train_cfg["batch_size"],
gradient_accumulation_steps=train_cfg["gradient_accumulation_steps"],
learning_rate=train_cfg["learning_rate"],
fp16=train_cfg["fp16"],
logging_steps=train_cfg["logging_steps"],
max_seq_length=config["model"]["max_seq_length"],
dataset_text_field=config["data"]["text_field"],
warmup_ratio=train_cfg["warmup_ratio"],
save_strategy="epoch",
report_to="none",
)
trainer = SFTTrainer(
model=model,
args=training_args,
train_dataset=dataset,
tokenizer=tokenizer,
)
print(f"\nπŸš€ Training with best config:")
print(f" learning_rate: {train_cfg['learning_rate']}")
print(f" epochs: {train_cfg['epochs']}")
print(f" r: {config['lora']['r']}")
print(f" lora_alpha: {config['lora']['alpha']}")
trainer.train()
# Print loss progression
print("\nLoss progression:")
for log in trainer.state.log_history:
if "loss" in log:
print(f" Step {log['step']:3d} β†’ Loss: {log['loss']:.4f}")
final_loss = trainer.state.log_history[-1].get("train_loss", None)
print(f"\nβœ… Training complete!")
print(f"βœ… Final loss: {final_loss:.4f}")
return trainer
# ── Save to Hugging Face Hub ─────────────────────────────────
def save_model(model, tokenizer, config, token, username):
repo = config["huggingface"]["model_repo"]
# Replace placeholder username if needed
repo = repo.replace("your-username", username)
print(f"\nSaving model to: {repo}")
model.push_to_hub(repo, token=token)
tokenizer.push_to_hub(repo, token=token)
print(f"βœ… Model saved!")
print(f"βœ… View at: https://huggingface.co/{repo}")
# ── Main ─────────────────────────────────────────────────────
def main():
print("=" * 60)
print("Marathi Mitra β€” Reproducing Best Model")
print("=" * 60)
# Step 1 β€” Load config and credentials
config = load_config()
token, username = load_credentials()
login(token=token)
# Step 2 β€” Prepare dataset
dataset = prepare_dataset(config)
# Step 3 β€” Load model
model, tokenizer = load_model(config)
# Step 4 β€” Apply LoRA
model = apply_lora(model, config)
# Step 5 β€” Train
trainer = train(model, tokenizer, dataset, config)
# Step 6 β€” Save to Hub
save_model(model, tokenizer, config, token, username)
print("\n" + "=" * 60)
print("βœ… Done! Model reproduced and saved.")
print("=" * 60)
if __name__ == "__main__":
main()