diff --git a/plots/line-loss-training/implementations/altair.py b/plots/line-loss-training/implementations/altair.py new file mode 100644 index 0000000000..9757106387 --- /dev/null +++ b/plots/line-loss-training/implementations/altair.py @@ -0,0 +1,102 @@ +""" pyplots.ai +line-loss-training: Training Loss Curve +Library: altair 6.0.0 | Python 3.13.11 +Quality: 91/100 | Created: 2025-12-31 +""" + +import altair as alt +import numpy as np +import pandas as pd + + +# Data - Simulating neural network training loss curves +np.random.seed(42) +epochs = np.arange(1, 51) + +# Training loss: exponential decay with noise (continues decreasing) +train_loss = 2.5 * np.exp(-0.08 * epochs) + 0.15 + np.random.normal(0, 0.02, len(epochs)) + +# Validation loss: decay then overfitting (U-shape after minimum) +val_base = 2.5 * np.exp(-0.07 * epochs) + 0.35 +val_loss = val_base + np.random.normal(0, 0.025, len(epochs)) +# Add overfitting: loss increases after epoch 25 +val_loss[25:] = val_loss[25:] + np.linspace(0, 0.35, 25) + +# Find minimum validation loss epoch for annotation +min_val_epoch = epochs[np.argmin(val_loss)] +min_val_loss = np.min(val_loss) + +# Create DataFrame in long format for Altair +df = pd.DataFrame( + { + "Epoch": np.tile(epochs, 2), + "Loss": np.concatenate([train_loss, val_loss]), + "Type": ["Training Loss"] * len(epochs) + ["Validation Loss"] * len(epochs), + } +) + +# Point for minimum validation loss annotation +min_point_df = pd.DataFrame({"Epoch": [min_val_epoch], "Loss": [min_val_loss], "Type": ["Optimal Stopping Point"]}) + +# Base line chart +lines = ( + alt.Chart(df) + .mark_line(strokeWidth=3) + .encode( + x=alt.X("Epoch:Q", title="Epoch", axis=alt.Axis(labelFontSize=18, titleFontSize=22)), + y=alt.Y("Loss:Q", title="Cross-Entropy Loss", axis=alt.Axis(labelFontSize=18, titleFontSize=22)), + color=alt.Color( + "Type:N", + scale=alt.Scale(domain=["Training Loss", "Validation Loss"], range=["#306998", "#FFD43B"]), + legend=alt.Legend(title="Curve Type", labelFontSize=16, titleFontSize=18), + ), + ) +) + +# Add points on lines for visibility +points = ( + alt.Chart(df) + .mark_point(size=60, filled=True) + .encode( + x="Epoch:Q", + y="Loss:Q", + color=alt.Color( + "Type:N", + scale=alt.Scale(domain=["Training Loss", "Validation Loss"], range=["#306998", "#FFD43B"]), + legend=None, + ), + ) +) + +# Annotation for minimum validation loss +min_marker = ( + alt.Chart(min_point_df) + .mark_point(size=300, shape="diamond", filled=True, color="#E63946") + .encode(x="Epoch:Q", y="Loss:Q") +) + +# Text annotation for optimal stopping point +min_text = ( + alt.Chart(min_point_df) + .mark_text(align="left", dx=12, dy=-10, fontSize=16, fontWeight="bold", color="#E63946") + .encode(x="Epoch:Q", y="Loss:Q", text=alt.value(f"Min Val Loss (Epoch {min_val_epoch})")) +) + +# Combine all layers +chart = ( + (lines + points + min_marker + min_text) + .properties( + width=1600, + height=900, + title=alt.Title("line-loss-training · altair · pyplots.ai", fontSize=28, anchor="middle"), + ) + .configure_axis(labelFontSize=18, titleFontSize=22, gridOpacity=0.3) + .configure_legend(labelFontSize=16, titleFontSize=18) + .configure_view(strokeWidth=0) +) + +# Save as PNG (4800 x 2700 with scale_factor=3) +chart.save("plot.png", scale_factor=3.0) + +# Save as HTML for interactivity +chart.save("plot.html") diff --git a/plots/line-loss-training/metadata/altair.yaml b/plots/line-loss-training/metadata/altair.yaml new file mode 100644 index 0000000000..e5e04d1b7e --- /dev/null +++ b/plots/line-loss-training/metadata/altair.yaml @@ -0,0 +1,28 @@ +library: altair +specification_id: line-loss-training +created: '2025-12-31T00:11:50Z' +updated: '2025-12-31T00:16:02Z' +generated_by: claude-opus-4-5-20251101 +workflow_run: 20608675031 +issue: 2860 +python_version: 3.13.11 +library_version: 6.0.0 +preview_url: https://storage.googleapis.com/pyplots-images/plots/line-loss-training/altair/plot.png +preview_thumb: https://storage.googleapis.com/pyplots-images/plots/line-loss-training/altair/plot_thumb.png +preview_html: https://storage.googleapis.com/pyplots-images/plots/line-loss-training/altair/plot.html +quality_score: 91 +review: + strengths: + - Excellent implementation of overfitting visualization with validation loss diverging + from training loss after epoch 25 + - Clean use of Altair's layered composition with separate chart objects for lines, + points, and annotations + - Red diamond marker with annotation clearly indicates the optimal stopping point + at minimum validation loss + - Colorblind-safe color scheme with strong contrast between blue training and yellow + validation curves + - Proper title format and axis labeling including loss function type as specified + weaknesses: + - Legend title "Curve Type" is generic; could be more descriptive + - Points on every epoch (50 points per curve) add visual noise; could use opacity + or fewer markers