update 01
This commit is contained in:
+136
-16
@@ -183,6 +183,8 @@
|
||||
"BATCH_SIZE = 64\n",
|
||||
"N_EPOCHS = 60\n",
|
||||
"PATIENCE = 10\n",
|
||||
"CONV_THRESHOLD = 1e-4 # cải thiện val_loss < THRESHOLD → đánh dấu hội tụ\n",
|
||||
"CONV_WINDOW = 3 # cần CONV_WINDOW bước liên tiếp thỏa mãn\n",
|
||||
"\n",
|
||||
"# ── Tensors & DataLoaders ──────────────────────────────────────────────────────\n",
|
||||
"X_tr_t = torch.FloatTensor(X_train_np)\n",
|
||||
@@ -203,12 +205,16 @@
|
||||
"scheduler = optim.lr_scheduler.ReduceLROnPlateau(optimizer, mode=\"min\", factor=0.5, patience=5)\n",
|
||||
"\n",
|
||||
"# ── Training loop ──────────────────────────────────────────────────────────────\n",
|
||||
"train_losses, val_losses, val_accs = [], [], []\n",
|
||||
"train_losses, val_losses, val_accs, lr_history = [], [], [], []\n",
|
||||
"best_val_loss = float(\"inf\")\n",
|
||||
"best_epoch = 1\n",
|
||||
"best_state_dict = None\n",
|
||||
"patience_counter = 0\n",
|
||||
"convergence_epoch = None # ← điểm hội tụ sẽ được ghi lại ở đây\n",
|
||||
"early_stop_epoch = None\n",
|
||||
"\n",
|
||||
"print(\"🚀 Training MobileNetV3 + LR-ASPP...\")\n",
|
||||
"print(f\" conv_threshold={CONV_THRESHOLD} conv_window={CONV_WINDOW} patience={PATIENCE}\")\n",
|
||||
"for epoch in range(1, N_EPOCHS + 1):\n",
|
||||
" # --- Train ---\n",
|
||||
" model.train()\n",
|
||||
@@ -239,40 +245,154 @@
|
||||
"\n",
|
||||
" scheduler.step(avg_val_loss)\n",
|
||||
" lr = optimizer.param_groups[0][\"lr\"]\n",
|
||||
" lr_history.append(lr)\n",
|
||||
"\n",
|
||||
" train_losses.append(avg_train_loss)\n",
|
||||
" val_losses.append(avg_val_loss)\n",
|
||||
" val_accs.append(val_acc)\n",
|
||||
"\n",
|
||||
" if epoch % 5 == 0 or epoch == 1:\n",
|
||||
" print(f\"Epoch {epoch:3d}/{N_EPOCHS} train_loss={avg_train_loss:.4f} \"\n",
|
||||
" f\"val_loss={avg_val_loss:.4f} val_acc={val_acc:.4f} lr={lr:.2e}\")\n",
|
||||
" # ── Phát hiện điểm hội tụ (sliding window trên val_loss) ──────────────\n",
|
||||
" if convergence_epoch is None and epoch >= CONV_WINDOW + 1:\n",
|
||||
" window = val_losses[-(CONV_WINDOW + 1):]\n",
|
||||
" improvements = [abs(window[i] - window[i - 1]) for i in range(1, len(window))]\n",
|
||||
" if all(imp < CONV_THRESHOLD for imp in improvements):\n",
|
||||
" convergence_epoch = epoch - CONV_WINDOW + 1\n",
|
||||
" print(f\" 📍 Hội tụ phát hiện tại epoch {convergence_epoch} \"\n",
|
||||
" f\"(val_loss={val_losses[convergence_epoch-1]:.4f})\")\n",
|
||||
"\n",
|
||||
" # Early stopping\n",
|
||||
" if epoch % 5 == 0 or epoch == 1:\n",
|
||||
" print(f\" Epoch {epoch:3d}/{N_EPOCHS} train={avg_train_loss:.4f} \"\n",
|
||||
" f\"val={avg_val_loss:.4f} acc={val_acc:.4f} lr={lr:.2e}\")\n",
|
||||
"\n",
|
||||
" # ── Early stopping ─────────────────────────────────────────────────────\n",
|
||||
" if avg_val_loss < best_val_loss:\n",
|
||||
" best_val_loss = avg_val_loss\n",
|
||||
" best_epoch = epoch\n",
|
||||
" best_state_dict = {k: v.clone() for k, v in model.state_dict().items()}\n",
|
||||
" patience_counter = 0\n",
|
||||
" else:\n",
|
||||
" patience_counter += 1\n",
|
||||
" if patience_counter >= PATIENCE:\n",
|
||||
" print(f\"⏹ Early stopping at epoch {epoch}.\")\n",
|
||||
" early_stop_epoch = epoch\n",
|
||||
" print(f\"⏹ Early stopping tại epoch {epoch} (patience={PATIENCE})\")\n",
|
||||
" break\n",
|
||||
"\n",
|
||||
"# Restore best weights\n",
|
||||
"model.load_state_dict(best_state_dict)\n",
|
||||
"print(f\"\\n✅ Training hoàn tất! Best val_loss={best_val_loss:.4f}\")\n",
|
||||
"total_epochs = len(train_losses)\n",
|
||||
"print(f\"\\n✅ Training hoàn tất! Best epoch={best_epoch} Best val_loss={best_val_loss:.4f}\")\n",
|
||||
"if convergence_epoch:\n",
|
||||
" print(f\" Điểm hội tụ : epoch {convergence_epoch}\")\n",
|
||||
"\n",
|
||||
"# ── Learning curves ──────────────────────────────────────────────────────────\n",
|
||||
"fig, axes = plt.subplots(1, 2, figsize=(13, 4))\n",
|
||||
"axes[0].plot(train_losses, label=\"Train\")\n",
|
||||
"axes[0].plot(val_losses, label=\"Val\")\n",
|
||||
"axes[0].set_title(\"Loss\")\n",
|
||||
"axes[0].legend()\n",
|
||||
"axes[1].plot(val_accs)\n",
|
||||
"axes[1].set_title(\"Val Accuracy\")\n",
|
||||
"# ═══════════════════════════════════════════════════════════════════════════════\n",
|
||||
"# PHÂN TÍCH ĐIỂM HỘI TỤ — MobileNetV3 + LR-ASPP\n",
|
||||
"# EMA-smoothed curves + annotated convergence / best / early-stop markers\n",
|
||||
"# ═══════════════════════════════════════════════════════════════════════════════\n",
|
||||
"epochs_axis = list(range(1, total_epochs + 1))\n",
|
||||
"\n",
|
||||
"# Exponential Moving Average smoothing\n",
|
||||
"def ema(values, alpha=0.2):\n",
|
||||
" s = [values[0]]\n",
|
||||
" for v in values[1:]:\n",
|
||||
" s.append(alpha * v + (1 - alpha) * s[-1])\n",
|
||||
" return s\n",
|
||||
"\n",
|
||||
"val_losses_ema = ema(val_losses, alpha=0.3)\n",
|
||||
"train_losses_ema = ema(train_losses, alpha=0.3)\n",
|
||||
"val_accs_ema = ema(val_accs, alpha=0.3)\n",
|
||||
"\n",
|
||||
"# ── Tính ΔVal-loss per epoch ──────────────────────────────────────────────────\n",
|
||||
"delta_val = [abs(val_losses_ema[i] - val_losses_ema[i-1])\n",
|
||||
" for i in range(1, len(val_losses_ema))]\n",
|
||||
"\n",
|
||||
"fig, axes = plt.subplots(2, 2, figsize=(16, 10))\n",
|
||||
"\n",
|
||||
"# --- Top-left: Loss curves ---\n",
|
||||
"ax = axes[0, 0]\n",
|
||||
"ax.plot(epochs_axis, train_losses, \"b-\", alpha=0.25, linewidth=0.8)\n",
|
||||
"ax.plot(epochs_axis, train_losses_ema, \"b-\", linewidth=2, label=\"Train loss (EMA)\")\n",
|
||||
"ax.plot(epochs_axis, val_losses, \"g-\", alpha=0.25, linewidth=0.8)\n",
|
||||
"ax.plot(epochs_axis, val_losses_ema, \"g-\", linewidth=2, label=\"Val loss (EMA)\")\n",
|
||||
"ax.axvline(x=best_epoch, color=\"red\", linestyle=\"--\", linewidth=1.5,\n",
|
||||
" label=f\"Best epoch={best_epoch}\")\n",
|
||||
"if convergence_epoch:\n",
|
||||
" ax.axvline(x=convergence_epoch, color=\"orange\", linestyle=\":\", linewidth=1.5,\n",
|
||||
" label=f\"HỘI TỤ epoch={convergence_epoch}\")\n",
|
||||
"if early_stop_epoch:\n",
|
||||
" ax.axvline(x=early_stop_epoch, color=\"gray\", linestyle=\"-.\", linewidth=1.5,\n",
|
||||
" label=f\"Early stop epoch={early_stop_epoch}\")\n",
|
||||
"ax.set_xlabel(\"Epoch\")\n",
|
||||
"ax.set_ylabel(\"Loss\")\n",
|
||||
"ax.set_title(\"Loss Curves (raw + EMA)\")\n",
|
||||
"ax.legend(fontsize=8)\n",
|
||||
"ax.grid(True, alpha=0.3)\n",
|
||||
"\n",
|
||||
"# --- Top-right: Val accuracy ---\n",
|
||||
"ax = axes[0, 1]\n",
|
||||
"ax.plot(epochs_axis, val_accs, \"g-\", alpha=0.3, linewidth=0.8)\n",
|
||||
"ax.plot(epochs_axis, val_accs_ema, \"g-\", linewidth=2, label=\"Val acc (EMA)\")\n",
|
||||
"ax.axvline(x=best_epoch, color=\"red\", linestyle=\"--\", linewidth=1.5,\n",
|
||||
" label=f\"Best epoch={best_epoch} ({val_accs[best_epoch-1]*100:.2f}%)\")\n",
|
||||
"if convergence_epoch:\n",
|
||||
" ax.axvline(x=convergence_epoch, color=\"orange\", linestyle=\":\", linewidth=1.5,\n",
|
||||
" label=f\"HỘI TỤ epoch={convergence_epoch} ({val_accs[convergence_epoch-1]*100:.2f}%)\")\n",
|
||||
"if early_stop_epoch:\n",
|
||||
" ax.axvline(x=early_stop_epoch, color=\"gray\", linestyle=\"-.\", linewidth=1.5,\n",
|
||||
" label=f\"Early stop\")\n",
|
||||
"ax.set_xlabel(\"Epoch\")\n",
|
||||
"ax.set_ylabel(\"Accuracy\")\n",
|
||||
"ax.set_title(\"Val Accuracy Curve\")\n",
|
||||
"ax.legend(fontsize=8)\n",
|
||||
"ax.grid(True, alpha=0.3)\n",
|
||||
"\n",
|
||||
"# --- Bottom-left: ΔVal-loss (marginal improvement) ---\n",
|
||||
"ax = axes[1, 0]\n",
|
||||
"ax.bar(epochs_axis[1:], delta_val,\n",
|
||||
" color=[\"green\" if d > CONV_THRESHOLD else \"salmon\" for d in delta_val],\n",
|
||||
" alpha=0.75)\n",
|
||||
"ax.axhline(y=CONV_THRESHOLD, color=\"red\", linestyle=\"--\",\n",
|
||||
" label=f\"Threshold = {CONV_THRESHOLD:.0e}\")\n",
|
||||
"if convergence_epoch:\n",
|
||||
" ax.axvline(x=convergence_epoch, color=\"orange\", linestyle=\":\", linewidth=1.5,\n",
|
||||
" label=f\"HỘI TỤ epoch={convergence_epoch}\")\n",
|
||||
"ax.set_xlabel(\"Epoch\")\n",
|
||||
"ax.set_ylabel(\"|ΔVal Loss|\")\n",
|
||||
"ax.set_title(\"Marginal Val-Loss Improvement per Epoch\")\n",
|
||||
"ax.legend(fontsize=8)\n",
|
||||
"ax.grid(True, alpha=0.3)\n",
|
||||
"\n",
|
||||
"# --- Bottom-right: Learning rate schedule ---\n",
|
||||
"ax = axes[1, 1]\n",
|
||||
"ax.semilogy(epochs_axis, lr_history, \"purple\", linewidth=2)\n",
|
||||
"if convergence_epoch:\n",
|
||||
" ax.axvline(x=convergence_epoch, color=\"orange\", linestyle=\":\", linewidth=1.5,\n",
|
||||
" label=f\"HỘI TỤ epoch={convergence_epoch}\")\n",
|
||||
"ax.axvline(x=best_epoch, color=\"red\", linestyle=\"--\", linewidth=1.5,\n",
|
||||
" label=f\"Best epoch={best_epoch}\")\n",
|
||||
"ax.set_xlabel(\"Epoch\")\n",
|
||||
"ax.set_ylabel(\"Learning Rate (log)\")\n",
|
||||
"ax.set_title(\"Learning Rate Schedule (ReduceLROnPlateau)\")\n",
|
||||
"ax.legend(fontsize=8)\n",
|
||||
"ax.grid(True, alpha=0.3)\n",
|
||||
"\n",
|
||||
"plt.suptitle(\"MobileNetV3 + LR-ASPP — Convergence Analysis\", fontsize=13, fontweight=\"bold\")\n",
|
||||
"plt.tight_layout()\n",
|
||||
"plt.show()\n"
|
||||
"plt.show()\n",
|
||||
"\n",
|
||||
"# ── Tổng kết ──────────────────────────────────────────────────────────────────\n",
|
||||
"print(f\"\\n{'═'*60}\")\n",
|
||||
"print(f\" Tổng số epoch : {total_epochs}\")\n",
|
||||
"print(f\" Best epoch : {best_epoch} (val_loss={best_val_loss:.4f})\")\n",
|
||||
"print(f\" Best val accuracy : {val_accs[best_epoch-1]*100:.4f}%\")\n",
|
||||
"if convergence_epoch:\n",
|
||||
" print(f\" Điểm HỘI TỤ : epoch {convergence_epoch} \"\n",
|
||||
" f\"(val_acc={val_accs[convergence_epoch-1]*100:.2f}%)\")\n",
|
||||
" wasted = total_epochs - convergence_epoch\n",
|
||||
" print(f\" Epochs sau hội tụ : {wasted} \"\n",
|
||||
" f\"(có thể giảm N_EPOCHS không ảnh hưởng nhiều đến kết quả)\")\n",
|
||||
"if early_stop_epoch:\n",
|
||||
" print(f\" Early stop tại epoch: {early_stop_epoch}\")\n",
|
||||
"print(f\"{'═'*60}\")\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user