{"spec_id":"line-loss-training","library":"altair","language":"python","code":"\"\"\" anyplot.ai\nline-loss-training: Training Loss Curve\nLibrary: altair 6.1.0 | Python 3.13.13\nQuality: 80/100 | Updated: 2026-05-14\n\"\"\"\n\nimport altair as alt\nimport numpy as np\nimport pandas as pd\n\n\n# Data - Simulating neural network training loss curves\nnp.random.seed(42)\nepochs = np.arange(1, 51)\n\n# Training loss: exponential decay with noise (continues decreasing)\ntrain_loss = 2.5 * np.exp(-0.08 * epochs) + 0.15 + np.random.normal(0, 0.02, len(epochs))\n\n# Validation loss: decay then overfitting (U-shape after minimum)\nval_base = 2.5 * np.exp(-0.07 * epochs) + 0.35\nval_loss = val_base + np.random.normal(0, 0.025, len(epochs))\n# Add overfitting: loss increases after epoch 25\nval_loss[25:] = val_loss[25:] + np.linspace(0, 0.35, 25)\n\n# Find minimum validation loss epoch for annotation\nmin_val_epoch = epochs[np.argmin(val_loss)]\nmin_val_loss = np.min(val_loss)\n\n# Create DataFrame in long format for Altair\ndf = pd.DataFrame(\n    {\n        \"Epoch\": np.tile(epochs, 2),\n        \"Loss\": np.concatenate([train_loss, val_loss]),\n        \"Type\": [\"Training Loss\"] * len(epochs) + [\"Validation Loss\"] * len(epochs),\n    }\n)\n\n# Point for minimum validation loss annotation\nmin_point_df = pd.DataFrame({\"Epoch\": [min_val_epoch], \"Loss\": [min_val_loss], \"Type\": [\"Optimal Stopping Point\"]})\n\n# Base line chart\nlines = (\n    alt.Chart(df)\n    .mark_line(strokeWidth=3)\n    .encode(\n        x=alt.X(\"Epoch:Q\", title=\"Epoch\", axis=alt.Axis(labelFontSize=18, titleFontSize=22)),\n        y=alt.Y(\"Loss:Q\", title=\"Cross-Entropy Loss\", axis=alt.Axis(labelFontSize=18, titleFontSize=22)),\n        color=alt.Color(\n            \"Type:N\",\n            scale=alt.Scale(domain=[\"Training Loss\", \"Validation Loss\"], range=[\"#306998\", \"#FFD43B\"]),\n            legend=alt.Legend(title=\"Curve Type\", labelFontSize=16, titleFontSize=18),\n        ),\n    )\n)\n\n# Add points on lines for visibility\npoints = (\n    alt.Chart(df)\n    .mark_point(size=60, filled=True)\n    .encode(\n        x=\"Epoch:Q\",\n        y=\"Loss:Q\",\n        color=alt.Color(\n            \"Type:N\",\n            scale=alt.Scale(domain=[\"Training Loss\", \"Validation Loss\"], range=[\"#306998\", \"#FFD43B\"]),\n            legend=None,\n        ),\n    )\n)\n\n# Annotation for minimum validation loss\nmin_marker = (\n    alt.Chart(min_point_df)\n    .mark_point(size=300, shape=\"diamond\", filled=True, color=\"#E63946\")\n    .encode(x=\"Epoch:Q\", y=\"Loss:Q\")\n)\n\n# Text annotation for optimal stopping point\nmin_text = (\n    alt.Chart(min_point_df)\n    .mark_text(align=\"left\", dx=12, dy=-10, fontSize=16, fontWeight=\"bold\", color=\"#E63946\")\n    .encode(x=\"Epoch:Q\", y=\"Loss:Q\", text=alt.value(f\"Min Val Loss (Epoch {min_val_epoch})\"))\n)\n\n# Combine all layers\nchart = (\n    (lines + points + min_marker + min_text)\n    .properties(\n        width=1600,\n        height=900,\n        title=alt.Title(\"line-loss-training · altair · pyplots.ai\", fontSize=28, anchor=\"middle\"),\n    )\n    .configure_axis(labelFontSize=18, titleFontSize=22, gridOpacity=0.3)\n    .configure_legend(labelFontSize=16, titleFontSize=18)\n    .configure_view(strokeWidth=0)\n)\n\n# Save as PNG (4800 x 2700 with scale_factor=3)\nchart.save(\"plot.png\", scale_factor=3.0)\n\n# Save as HTML for interactivity\nchart.save(\"plot.html\")\n"}