summaryrefslogtreecommitdiff
path: root/train.py
diff options
context:
space:
mode:
Diffstat (limited to 'train.py')
-rw-r--r--train.py29
1 files changed, 16 insertions, 13 deletions
diff --git a/train.py b/train.py
index 545b299..4a04a30 100644
--- a/train.py
+++ b/train.py
@@ -19,11 +19,11 @@ def train_model(
19 patience: int = 5, 19 patience: int = 5,
20 model_name: str = "", 20 model_name: str = "",
21) -> dict: 21) -> dict:
22 """Train model with mixed precision, early stopping on val weighted-F1. 22 """Train model with mixed precision, early stopping on val macro F1.
23 23
24 Returns a dict with: 24 Returns a dict with:
25 best_model_state, train_losses, val_losses, val_f1s, 25 best_model_state, train_losses, val_losses, val_f1s (macro), val_weighted_f1s,
26 train_time (seconds), epochs_trained 26 train_time (seconds), epochs_trained, best_val_f1 (macro)
27 """ 27 """
28 scaler = torch.amp.GradScaler('cuda') if device.type == 'cuda' else None 28 scaler = torch.amp.GradScaler('cuda') if device.type == 'cuda' else None
29 29
@@ -31,7 +31,7 @@ def train_model(
31 best_state = None 31 best_state = None
32 patience_counter = 0 32 patience_counter = 0
33 33
34 train_losses, val_losses, val_f1s = [], [], [] 34 train_losses, val_losses, val_f1s, val_weighted_f1s = [], [], [], []
35 start_time = time.time() 35 start_time = time.time()
36 36
37 for epoch in range(1, num_epochs + 1): 37 for epoch in range(1, num_epochs + 1):
@@ -97,21 +97,23 @@ def train_model(
97 val_loss = val_running_loss / n_val 97 val_loss = val_running_loss / n_val
98 val_losses.append(val_loss) 98 val_losses.append(val_loss)
99 99
100 val_f1 = f1_score(all_labels, all_preds, average='weighted', zero_division=0) 100 val_f1_macro = f1_score(all_labels, all_preds, average='macro', zero_division=0)
101 val_f1s.append(val_f1) 101 val_f1_weighted = f1_score(all_labels, all_preds, average='weighted', zero_division=0)
102 val_f1s.append(val_f1_macro)
103 val_weighted_f1s.append(val_f1_weighted)
102 104
103 current_lr = scheduler.get_last_lr()[0] if hasattr(scheduler, 'get_last_lr') else optimizer.param_groups[0]['lr'] 105 current_lr = scheduler.get_last_lr()[0] if hasattr(scheduler, 'get_last_lr') else optimizer.param_groups[0]['lr']
104 print( 106 print(
105 f"[{model_name}] Epoch {epoch:3d}/{num_epochs} | " 107 f"[{model_name}] Epoch {epoch:3d}/{num_epochs} | "
106 f"train loss: {train_loss:.4f} | val loss: {val_loss:.4f} | " 108 f"train loss: {train_loss:.4f} | val loss: {val_loss:.4f} | "
107 f"val F1: {val_f1:.4f} | lr: {current_lr:.2e}" 109 f"macro F1: {val_f1_macro:.4f} | weighted F1: {val_f1_weighted:.4f} | lr: {current_lr:.2e}"
108 ) 110 )
109 111
110 scheduler.step() 112 scheduler.step()
111 113
112 # ── Early stopping ──────────────────────────────────────────────────── 114 # ── Early stopping (monitored on macro F1) ───────────────────────────
113 if val_f1 > best_val_f1: 115 if val_f1_macro > best_val_f1:
114 best_val_f1 = val_f1 116 best_val_f1 = val_f1_macro
115 best_state = copy.deepcopy(model.state_dict()) 117 best_state = copy.deepcopy(model.state_dict())
116 patience_counter = 0 118 patience_counter = 0
117 else: 119 else:
@@ -123,14 +125,15 @@ def train_model(
123 train_time = time.time() - start_time 125 train_time = time.time() - start_time
124 epochs_trained = len(train_losses) 126 epochs_trained = len(train_losses)
125 127
126 print(f" Training complete: {epochs_trained} epochs, {train_time:.1f}s, best val F1: {best_val_f1:.4f}") 128 print(f" Training complete: {epochs_trained} epochs, {train_time:.1f}s, best val macro F1: {best_val_f1:.4f}")
127 129
128 return { 130 return {
129 'best_model_state': best_state, 131 'best_model_state': best_state,
130 'train_losses': train_losses, 132 'train_losses': train_losses,
131 'val_losses': val_losses, 133 'val_losses': val_losses,
132 'val_f1s': val_f1s, 134 'val_f1s': val_f1s, # macro F1 per epoch (used for early stopping)
135 'val_weighted_f1s': val_weighted_f1s,
133 'train_time': train_time, 136 'train_time': train_time,
134 'epochs_trained': epochs_trained, 137 'epochs_trained': epochs_trained,
135 'best_val_f1': best_val_f1, 138 'best_val_f1': best_val_f1, # best macro F1
136 } 139 }