NeuroVision-API / src /train.py
pahariomnisavanataryan's picture
Add workflow scripts, fix ignore mappings, and push tracking updates for dvc
039c272
Raw
History Blame Contribute Delete
6.18 kB
import os
import yaml
import nltk
import pickle
import torch
import mlflow
import mlflow.pytorch
from tqdm import tqdm
from preprocess_data import VQADataset
from nltk.tokenize import word_tokenize
from torch.utils.data import DataLoader
from model import load_model_processor
from transformers import get_linear_schedule_with_warmup
from nltk.translate.bleu_score import sentence_bleu , SmoothingFunction
from mlflow.models import infer_signature
nltk.download('all')
config = yaml.safe_load(open("./config.yaml", "r"))["data_location"]
model_config = yaml.safe_load(open("./config.yaml", "r"))["finetune_model"]
params = yaml.safe_load(open("./param.yaml", "r"))["params"]
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model, processor = load_model_processor()
print(f"Loading processed data from {config['train_processed_data']} and {config['test_processed_data']}.")
with open(f'./{config["train_processed_data"]}', "rb") as f:
train_dataset = pickle.load(f)
with open(f'./{config["test_processed_data"]}', "rb") as f:
test_dataset = pickle.load(f)
print(f"Loaded processed data successfully!!!")
print(f"Length of train dataset is {len(train_dataset)} and test dataset is {len(test_dataset)}")
batch_size = params["batch_size"]
num_epochs = params["num_epochs"]
patience = params["patience"]
gradient_accumulation_steps = params["gradient_accumulation_steps"]
print("Preparing dataloaders................")
train_dataloader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True, pin_memory=True)
valid_dataloader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False, pin_memory=True)
print("Dataloaders prepared successfully!!!")
mlflow.set_experiment("VQA_Model_Training")
min_bleu_score = 0
early_stopping_hook = 0
tracking_information = []
optimizer = torch.optim.AdamW(filter(lambda p: p.requires_grad, model.parameters()), lr=float(params["learning_rate"]), weight_decay=float(params["weight_decay"]))
total_steps = len(train_dataloader) * num_epochs
warmup_steps = total_steps // 10
scheduler = get_linear_schedule_with_warmup(optimizer, num_warmup_steps=warmup_steps, num_training_steps=total_steps)
scaler = torch.amp.GradScaler('cuda')
with mlflow.start_run():
for epoch in range(num_epochs):
model.train()
epoch_loss = 0
for step, batch in enumerate(tqdm(train_dataloader, desc=f"Epoch {epoch+1} Training")):
input_ids = batch.pop('input_ids').to(device)
pixel_values = batch.pop('pixel_values').to(device)
attention_mask = batch.pop('attention_mask').to(device)
labels = batch.pop('labels').to(device)
with torch.amp.autocast('cuda', dtype=torch.float16):
outputs = model(input_ids=input_ids, pixel_values=pixel_values, attention_mask=attention_mask, labels=labels)
loss = outputs.loss / gradient_accumulation_steps
scaler.scale(loss).backward()
if (step + 1) % gradient_accumulation_steps == 0 or (step + 1) == len(train_dataloader):
scaler.unscale_(optimizer)
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
scaler.step(optimizer)
scaler.update()
optimizer.zero_grad()
epoch_loss += loss.item() * gradient_accumulation_steps
# Validation Loop
model.eval()
eval_loss = 0
bleu_scores = []
smooth_fn = SmoothingFunction().method1
with torch.no_grad():
for batch in tqdm(valid_dataloader, desc=f"Epoch {epoch+1} Validating"):
input_ids = batch.pop('input_ids').to(device)
pixel_values = batch.pop('pixel_values').to(device)
attention_mask = batch.pop('attention_mask').to(device)
labels = batch.pop('labels').to(device)
with torch.amp.autocast('cuda', dtype=torch.float16):
outputs = model(input_ids=input_ids, pixel_values=pixel_values, attention_mask=attention_mask, labels=labels)
eval_loss += outputs.loss.item()
generated_ids = model.generate(input_ids=input_ids, pixel_values=pixel_values, attention_mask=attention_mask, max_length=8)
predictions = processor.tokenizer.batch_decode(generated_ids, skip_special_tokens=True)
references = processor.tokenizer.batch_decode(labels, skip_special_tokens=True)
for pred, ref in zip(predictions, references):
bleu_scores.append(sentence_bleu([word_tokenize(ref)], word_tokenize(pred), smoothing_function=smooth_fn))
avg_train_loss = epoch_loss / len(train_dataloader)
avg_eval_loss = eval_loss / len(valid_dataloader)
avg_bleu_score = sum(bleu_scores) / len(bleu_scores)
tracking_information.append((avg_train_loss, avg_eval_loss, avg_bleu_score, optimizer.param_groups[0]["lr"]))
mlflow.log_metrics({"train_loss": avg_train_loss, "eval_loss": avg_eval_loss, "bleu_score": avg_bleu_score}, step=epoch)
mlflow.log_param(f"learning_rate_{epoch}", optimizer.param_groups[0]['lr'])
print(f"Epoch {epoch+1} - Train Loss: {avg_train_loss:.4f} - Eval Loss: {avg_eval_loss:.4f} - BLEU Score: {avg_bleu_score:.4f} - LR: {optimizer.param_groups[0]['lr']}")
if avg_bleu_score >= min_bleu_score:
model.save_pretrained(model_config["best"], from_pt=True)
print(f"Model improved (BLEU: {avg_bleu_score:.4f})! Saved to {model_config['best']}")
min_bleu_score = avg_bleu_score
early_stopping_hook = 0
else:
early_stopping_hook += 1
if early_stopping_hook > patience:
print("Early stopping triggered.")
break
model.save_pretrained(model_config["last"], from_pt=True)
scheduler.step()
# Log the saved PEFT adapters as an artifact instead of trying to pickle the Quantized model
mlflow.log_artifacts(model_config["best"], artifact_path="VQA_model_best")
print("Model adapters logged with MLflow.")