| import os |
| import time |
| import torch |
| import torch.nn as nn |
| import torch.optim as optim |
| from torch.utils.data import DataLoader, random_split |
| import sys |
|
|
| project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) |
| sys.path.insert(0, project_root) |
|
|
| from arthaml.data.vocabulary import Vocabulary |
| from arthaml.data.dataset import TranslationDataset |
| from arthaml.models.encoder import Encoder |
| from arthaml.models.decoder import Attention, Decoder |
| from arthaml.models.seq2seq import Seq2Seq |
| from arthaml.evaluation.bleu import compute_bleu |
|
|
| def train(model, iterator, optimizer, criterion, clip, device): |
| model.train() |
| epoch_loss = 0 |
| |
| for i, batch in enumerate(iterator): |
| src = batch['src'].to(device) |
| tgt = batch['tgt'].to(device) |
| src_len = batch['src_len'].to(device) |
| |
| optimizer.zero_grad() |
| |
| output = model(src, src_len, tgt, teacher_forcing_ratio=0.5) |
| |
| output_dim = output.shape[-1] |
| |
| |
| output = output[:, 1:].reshape(-1, output_dim) |
| tgt = tgt[:, 1:].reshape(-1) |
| |
| loss = criterion(output, tgt) |
| loss.backward() |
| |
| torch.nn.utils.clip_grad_norm_(model.parameters(), clip) |
| optimizer.step() |
| |
| epoch_loss += loss.item() |
| |
| return epoch_loss / len(iterator) |
|
|
| def evaluate(model, iterator, criterion, device): |
| model.eval() |
| epoch_loss = 0 |
| |
| with torch.no_grad(): |
| for i, batch in enumerate(iterator): |
| src = batch['src'].to(device) |
| tgt = batch['tgt'].to(device) |
| src_len = batch['src_len'].to(device) |
| |
| output = model(src, src_len, tgt, teacher_forcing_ratio=0) |
| |
| output_dim = output.shape[-1] |
| |
| output = output[:, 1:].reshape(-1, output_dim) |
| tgt = tgt[:, 1:].reshape(-1) |
| |
| loss = criterion(output, tgt) |
| epoch_loss += loss.item() |
| |
| return epoch_loss / len(iterator) |
|
|
| def main(): |
| device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') |
| print(f"Using device: {device}") |
| |
| os.makedirs(os.path.join(project_root, 'models'), exist_ok=True) |
| |
| print("Loading vocabularies...") |
| vocab_en = Vocabulary("English") |
| vocab_en.load(os.path.join(project_root, 'data/processed/vocab_en.json')) |
| vocab_kn = Vocabulary("Kannada") |
| vocab_kn.load(os.path.join(project_root, 'data/processed/vocab_kn.json')) |
| |
| print("Loading dataset...") |
| data_path = os.path.join(project_root, 'data/processed/clean_50k.json') |
| dataset = TranslationDataset(data_path, vocab_en, vocab_kn, max_len=50) |
| |
| val_size = int(0.1 * len(dataset)) |
| train_size = len(dataset) - val_size |
| train_dataset, val_dataset = random_split(dataset, [train_size, val_size]) |
| |
| train_iterator = DataLoader(train_dataset, batch_size=32, shuffle=True) |
| val_iterator = DataLoader(val_dataset, batch_size=32, shuffle=False) |
| |
| print("Initializing models...") |
| encoder = Encoder(vocab_size=21551, embed_dim=256, hidden_size=512, num_layers=2, dropout_p=0.3, padding_idx=0) |
| attention = Attention(hidden_size=512) |
| decoder = Decoder(attention, vocab_size=26802, embed_dim=256, hidden_size=512, num_layers=2, dropout_p=0.3, padding_idx=0) |
| |
| model = Seq2Seq(encoder, decoder, device).to(device) |
| |
| optimizer = optim.Adam(model.parameters(), lr=0.001) |
| criterion = nn.CrossEntropyLoss(ignore_index=0) |
| |
| N_EPOCHS = 10 |
| CLIP = 1.0 |
| |
| best_val_loss = float('inf') |
| best_bleu = 0.0 |
| |
| print("Starting training...") |
| for epoch in range(N_EPOCHS): |
| start_time = time.time() |
| |
| train_loss = train(model, train_iterator, optimizer, criterion, CLIP, device) |
| val_loss = evaluate(model, val_iterator, criterion, device) |
| |
| bleu_score = compute_bleu(model, val_dataset, vocab_kn, device, n=500) |
| |
| end_time = time.time() |
| time_taken = int(end_time - start_time) |
| |
| if val_loss < best_val_loss: |
| best_val_loss = val_loss |
| torch.save(model.state_dict(), os.path.join(project_root, 'models/best_model.pth')) |
| |
| if bleu_score > best_bleu: |
| best_bleu = bleu_score |
| |
| print(f"Epoch {epoch+1:02} | Train Loss: {train_loss:.3f} | Val Loss: {val_loss:.3f} | BLEU: {bleu_score:.2f} | Time: {time_taken}s") |
| |
| torch.save(model.state_dict(), os.path.join(project_root, 'models/final_model.pth')) |
| print(f"\nTraining Complete!") |
| print(f"Best Val Loss: {best_val_loss:.3f}") |
| print(f"Best BLEU Score: {best_bleu:.2f}") |
|
|
| if __name__ == "__main__": |
| main() |
|
|