| import os |
| import sys |
| import numpy as np |
| import torch |
| import torch .nn as nn |
| from transformers import ( |
| AutoTokenizer ,AutoModelForSequenceClassification , |
| Trainer ,TrainingArguments ,DataCollatorWithPadding , |
| EarlyStoppingCallback , |
| ) |
| from sklearn .metrics import accuracy_score ,f1_score ,precision_recall_fscore_support |
|
|
|
|
| sys .path .append (os .path .dirname (os .path .abspath (__file__ ))) |
| from dataset import UrduTextDataset |
|
|
| NUM_LABELS =4 |
| MAX_LENGTH =128 |
|
|
|
|
| def compute_metrics (eval_pred ): |
| logits ,labels =eval_pred |
| preds =np .argmax (logits ,axis =-1 ) |
| precision ,recall ,f1 ,_ =precision_recall_fscore_support ( |
| labels ,preds ,average ='weighted',zero_division =0 ) |
| macro_f1 =f1_score (labels ,preds ,average ='macro',zero_division =0 ) |
| return { |
| 'accuracy':accuracy_score (labels ,preds ), |
| 'f1':f1 , |
| 'macro_f1':macro_f1 , |
| 'precision':precision , |
| 'recall':recall , |
| } |
|
|
|
|
| class WeightedTrainer (Trainer ): |
| """Trainer that applies class weights (+ optional label smoothing) in the loss.""" |
| def __init__ (self ,*args ,class_weights =None ,label_smoothing =0.0 ,**kwargs ): |
| super ().__init__ (*args ,**kwargs ) |
| self .class_weights =class_weights |
| self .label_smoothing =label_smoothing |
|
|
| def compute_loss (self ,model ,inputs ,return_outputs =False ,**kwargs ): |
| labels =inputs .pop ("labels") |
| outputs =model (**inputs ) |
| weight =(self .class_weights .to (model .device ) |
| if self .class_weights is not None else None ) |
| loss_fn =nn .CrossEntropyLoss ( |
| weight =weight ,label_smoothing =self .label_smoothing ) |
| loss =loss_fn (outputs .logits .view (-1 ,NUM_LABELS ),labels .view (-1 )) |
| return (loss ,outputs )if return_outputs else loss |
|
|
|
|
| def main (): |
| print ("="*60 ) |
| print (" Phase 4: Training Emotion Model") |
| print ("="*60 ) |
|
|
|
|
| torch .set_num_threads (os .cpu_count ()or 1 ) |
|
|
| model_name ="xlm-roberta-base" |
|
|
|
|
| print ("\n[1/5] Loading tokenizer and XLM-RoBERTa model...") |
| tokenizer =AutoTokenizer .from_pretrained (model_name ) |
| model =AutoModelForSequenceClassification .from_pretrained ( |
| model_name ,num_labels =NUM_LABELS ) |
|
|
|
|
| print ("\n[2/5] Loading datasets...") |
| base =os .path .dirname (os .path .dirname (os .path .abspath (__file__ ))) |
| data_dir =os .path .join (base ,'data') |
| train_files =[os .path .join (data_dir ,'semeval_emotion_train.csv')] |
| val_files =[os .path .join (data_dir ,'semeval_emotion_val.csv')] |
|
|
| train_dataset =UrduTextDataset (train_files ,tokenizer ,max_length =MAX_LENGTH ,task ="emotion") |
| val_dataset =UrduTextDataset (val_files ,tokenizer ,max_length =MAX_LENGTH ,task ="emotion") |
| print (f"Train samples: {len (train_dataset )} | Val samples: {len (val_dataset )}") |
|
|
|
|
| class_weights =train_dataset .get_class_weights (NUM_LABELS ) |
| print (f"Class weights: {class_weights .tolist ()}") |
| data_collator =DataCollatorWithPadding (tokenizer =tokenizer ) |
|
|
|
|
| print ("\n[3/5] Setting up Training Arguments...") |
| output_dir =os .path .join (base ,'models','emotion_model') |
|
|
| training_args =TrainingArguments ( |
| output_dir =output_dir , |
| num_train_epochs =6 , |
| per_device_train_batch_size =8 , |
| per_device_eval_batch_size =16 , |
| gradient_accumulation_steps =4 , |
| learning_rate =2e-5 , |
| warmup_ratio =0.1 , |
| weight_decay =0.01 , |
| lr_scheduler_type ="cosine", |
| max_grad_norm =1.0 , |
|
|
| dataloader_pin_memory =False , |
| dataloader_num_workers =0 , |
| logging_dir ='./logs/emotion', |
| logging_steps =50 , |
| eval_strategy ="epoch", |
| save_strategy ="epoch", |
| save_total_limit =2 , |
| load_best_model_at_end =True , |
| metric_for_best_model ="macro_f1", |
| greater_is_better =True , |
| seed =42 , |
| report_to ="none", |
| ) |
|
|
|
|
| print ("\n[4/5] Initializing Trainer...") |
| trainer =WeightedTrainer ( |
| model =model , |
| args =training_args , |
| train_dataset =train_dataset , |
| eval_dataset =val_dataset , |
| processing_class =tokenizer , |
| data_collator =data_collator , |
| compute_metrics =compute_metrics , |
| class_weights =class_weights , |
| label_smoothing =0.1 , |
| callbacks =[EarlyStoppingCallback (early_stopping_patience =2 )], |
| ) |
|
|
|
|
| print ("\n[5/5] Starting training loop...") |
| trainer .train () |
|
|
| print (f"\nTraining complete! Saving best model to {output_dir }") |
| trainer .save_model (output_dir ) |
| tokenizer .save_pretrained (output_dir ) |
| print ("Emotion model saved successfully!") |
|
|
|
|
| if __name__ =="__main__": |
| main () |
|
|