{"spec_id":"line-loss-training","library":"pygal","language":"python","code":"\"\"\" anyplot.ai\nline-loss-training: Training Loss Curve\nLibrary: pygal 3.1.0 | Python 3.13.13\nQuality: 92/100 | Updated: 2026-05-14\n\"\"\"\n\nimport os\nimport sys\n\nimport numpy as np\n\n\n# Remove local directory from sys.path to avoid shadowing the pygal package\n_local_dir = os.path.abspath(os.path.dirname(__file__))\nsys.path = [p for p in sys.path if os.path.abspath(p) != _local_dir]\n\nimport pygal\nfrom pygal.style import Style\n\n\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\n\n# Theme tokens (from default-style-guide.md)\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_MUTED = \"#6B6A63\" if THEME == \"light\" else \"#A8A79F\"\n\n# Okabe-Ito palette (first series = brand green #009E73)\nIMPRINT = (\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\", \"#AE3030\", \"#2ABCCD\", \"#954477\")\n\n# Data: Simulated training loss curves showing typical overfitting behavior\nnp.random.seed(42)\nepochs = np.arange(1, 51)\n\n# Training loss: Steadily decreasing with some noise\ntrain_loss = 2.5 * np.exp(-0.08 * epochs) + 0.1 + np.random.normal(0, 0.02, len(epochs))\n\n# Validation loss: Decreases then increases (overfitting after epoch ~25)\nval_loss = 2.3 * np.exp(-0.07 * epochs) + 0.15 + 0.003 * np.maximum(0, epochs - 25) ** 1.5\nval_loss += np.random.normal(0, 0.03, len(epochs))\n\n# Find minimum validation loss epoch for annotation\nmin_val_epoch = int(epochs[np.argmin(val_loss)])\nmin_val_loss = float(np.min(val_loss))\n\n# Custom theme-adaptive style\ncustom_style = Style(\n    background=PAGE_BG,\n    plot_background=PAGE_BG,\n    foreground=INK,\n    foreground_strong=INK,\n    foreground_subtle=INK_MUTED,\n    colors=IMPRINT,\n    title_font_size=28,\n    label_font_size=22,\n    major_label_font_size=18,\n    legend_font_size=16,\n    value_font_size=14,\n    stroke_width=3,\n)\n\n# Create line chart\nchart = pygal.Line(\n    width=4800,\n    height=2700,\n    style=custom_style,\n    title=\"line-loss-training · pygal · anyplot.ai\",\n    x_title=\"Epoch\",\n    y_title=\"Cross-Entropy Loss\",\n    show_x_guides=True,\n    show_y_guides=True,\n    dots_size=6,\n    stroke_style={\"width\": 3},\n    legend_at_bottom=False,\n    legend_box_size=24,\n    margin=80,\n    x_label_rotation=0,\n    truncate_label=-1,\n    show_dots=True,\n)\n\n# Set x-axis labels (show every 5th epoch for readability)\nchart.x_labels = [str(e) if e % 5 == 0 else \"\" for e in epochs]\n\n# Add training and validation loss data\nchart.add(\"Training Loss\", list(train_loss))\nchart.add(\"Validation Loss\", list(val_loss))\n\n# Save as PNG and HTML with theme suffix\nchart.render_to_png(f\"plot-{THEME}.png\")\nwith open(f\"plot-{THEME}.html\", \"wb\") as f:\n    f.write(chart.render())\n"}