{"spec_id":"line-loss-training","library":"plotnine","language":"python","code":"\"\"\" anyplot.ai\nline-loss-training: Training Loss Curve\nLibrary: plotnine 0.15.4 | Python 3.13.13\nQuality: 93/100 | Updated: 2026-05-14\n\"\"\"\n\nimport os\nimport sys\n\n\n# Prevent this file from shadowing the plotnine library when run from its own directory\nsys.path = [p for p in sys.path if not p or os.path.abspath(p) != os.path.abspath(os.path.dirname(__file__))]\n\nimport numpy as np\nimport pandas as pd\nfrom plotnine import (\n    aes,\n    annotate,\n    element_line,\n    element_rect,\n    element_text,\n    geom_line,\n    geom_point,\n    geom_vline,\n    ggplot,\n    labs,\n    scale_color_manual,\n    theme,\n    theme_minimal,\n)\n\n\n# Theme tokens\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_SOFT = \"#4A4A44\" if THEME == \"light\" else \"#B8B7B0\"\nRULE = \"rgba(26,26,23,0.10)\" if THEME == \"light\" else \"rgba(240,239,232,0.10)\"\n\n# Okabe-Ito palette\nTRAINING_COLOR = \"#009E73\"  # bluish green\nVALIDATION_COLOR = \"#C475FD\"  # vermillion\n\n# Data - Simulated training history with typical loss curve behavior\nnp.random.seed(42)\nepochs = np.arange(1, 51)\n\n# Training loss: starts high, decreases with diminishing returns\ntrain_loss = 2.5 * np.exp(-0.08 * epochs) + 0.15 + np.random.normal(0, 0.02, len(epochs))\n\n# Validation loss: follows training initially, then diverges (overfitting)\nval_loss = 2.5 * np.exp(-0.06 * epochs) + 0.25 + np.random.normal(0, 0.03, len(epochs))\n# Add uptick after epoch 30 to show clear overfitting\nval_loss[30:] += np.linspace(0, 0.25, 20)\n\n# Find optimal stopping point (minimum validation loss)\noptimal_epoch = epochs[np.argmin(val_loss)]\n\n# Create long-format DataFrame for plotnine\ndf = pd.DataFrame(\n    {\n        \"Epoch\": np.concatenate([epochs, epochs]),\n        \"Loss\": np.concatenate([train_loss, val_loss]),\n        \"Type\": [\"Training Loss\"] * len(epochs) + [\"Validation Loss\"] * len(epochs),\n    }\n)\n\n# Plot\nplot = (\n    ggplot(df, aes(x=\"Epoch\", y=\"Loss\", color=\"Type\"))\n    + geom_line(size=1.5, alpha=0.9)\n    + geom_point(size=3, alpha=0.7)\n    + geom_vline(xintercept=optimal_epoch, linetype=\"dashed\", color=INK_SOFT, size=0.8, alpha=0.6)\n    + annotate(\n        \"text\",\n        x=optimal_epoch + 1.5,\n        y=np.min(val_loss) + (np.max(val_loss) - np.min(val_loss)) * 0.1,\n        label=f\"Best: {int(optimal_epoch)}\",\n        size=14,\n        ha=\"left\",\n        color=INK_SOFT,\n    )\n    + scale_color_manual(values={\"Training Loss\": TRAINING_COLOR, \"Validation Loss\": VALIDATION_COLOR})\n    + labs(title=\"line-loss-training · plotnine · anyplot.ai\", x=\"Epoch\", y=\"Cross-Entropy Loss\", color=\"\")\n    + theme_minimal()\n    + theme(\n        figure_size=(16, 9),\n        plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG),\n        panel_background=element_rect(fill=PAGE_BG),\n        panel_grid_major=element_line(color=INK, size=0.3, alpha=0.10),\n        panel_grid_minor=element_line(color=INK, size=0.2, alpha=0.05),\n        panel_border=element_rect(color=INK_SOFT, fill=None),\n        text=element_text(size=14, color=INK),\n        axis_title=element_text(size=20, color=INK),\n        axis_text=element_text(size=16, color=INK_SOFT),\n        axis_line=element_line(color=INK_SOFT),\n        plot_title=element_text(size=24, weight=\"bold\", color=INK),\n        legend_position=\"top\",\n        legend_direction=\"horizontal\",\n        legend_background=element_rect(fill=PAGE_BG, color=INK_SOFT),\n        legend_text=element_text(size=16, color=INK_SOFT),\n    )\n)\n\n# Save\nscript_dir = os.path.dirname(os.path.abspath(__file__))\nplot.save(os.path.join(script_dir, f\"plot-{THEME}.png\"), dpi=300, verbose=False)\n"}