diff options
Diffstat (limited to 'train.py')
| -rw-r--r-- | train.py | 29 |
1 files changed, 16 insertions, 13 deletions
| @@ -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 | } |
