Plots outliers interactions and Model Tracking Upgrade - #296
Merged
Conversation
A curve point is the mean of one step's batch, which hides the samples that blew up inside it. Alongside each averaged point the logger now reports which samples sat off the curve's own rolling trend, so a plot can show both the aggregate and the anomalies within it. Detection (_TrendTracker) keeps an EMA of the per-step average plus an EMA of squared deviation, and flags a sample when it falls further from the trend than k rolling standard deviations. Two guards keep it quiet: no flagging until the curve has min_steps of history (so a fresh loss curve's steep warm-up isn't one long anomaly), and the band never narrows below a fraction of |EMA| (so ordinary jitter on an almost-flat curve can't clear a 3-sigma test). Deviation is measured two-sided, so this works for accuracy-shaped signals as well as loss-shaped ones. Each point carries the top-N off-trend samples with their ids plus the true flagged count, which is what lets a consumer tell "one sample spiked" from "the whole batch drifted" — and gives the UI real sample ids to filter a data grid on. Notable details: - signals gains outliers/outlier_count/sample_count. A DB file written before those columns is ALTERed on open, and every INSERT now names its columns, since migrated columns land at the end of the table and would break a positional INSERT ... SELECT *. - The full-history downsample keeps every outlier-bearing point. An outlier is by nature a single step, so plain striding would have thrown away most of exactly what this feature exists to surface. - Detection is env-tunable (WL_SIGNAL_OUTLIER_*) and can be turned off. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Detection already maintained a rolling band (EMA plus k rolling standard deviations, with a relative floor) to decide which samples are off-trend, but only the verdict reached the UI. The band itself is now recorded on every point, so a plot can draw the region a value was expected to fall inside instead of showing an unexplained marker. The band is snapshotted BEFORE the point is folded into the trend — the same instant find_outliers reads it — so what the UI draws is exactly what the flag was judged against, and a spike is never measured partly against itself. Two behaviour notes: - Signals with no per-sample data now advance a trend too. A band describes the curve, not the batch, so an aggregate-only signal (lr, a scalar metric) gets one; it simply has no sample ids to attribute an outlier to. - Evaluation markers are excluded. They are separate points under their own hash, and folding them in would corrupt the training curve's trend. signals gains trend_value / trend_margin, defaulted to NULL rather than 0 so "no band recorded" stays distinguishable from a real band centred on zero. On the wire the pair is accompanied by an explicit has_trend_band flag, since proto3 scalars have no presence. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two changes driven by moving anomaly display into the error band itself. 1. Absolute value range. Each point now carries value_min / value_max: the real lowest and highest sample value in that step's batch, not a standard deviation. This is what the UI draws the band from, so a step containing an outlier pushes the band out to that outlier's own value. A std-derived band does the opposite — it averages the spike toward the batch mean and buries the thing worth seeing. The trend band is still recorded (it is what decides which samples count as off-trend) but is no longer drawn. 2. GetStepSamples. "Highlight step samples" now means the WHOLE batch behind a plotted point, not only its off-trend members, so the ids have to come from the per_sample table rather than from the outlier list on the aggregated point. get_step_sample_ids answers that with a cap plus the true pre-cap total, so a caller can say "showing 2000 of 4096". Ids sort numeric-aware, so "9" precedes "10". An empty result is returned as success, not failure: signals that log only an aggregate legitimately have no per-sample rows, and the UI needs to tell that apart from an error. signals gains value_min / value_max, NULL-defaulted so "not recorded" stays distinguishable from a real zero range, with the same ALTER-on-open migration as the existing added columns. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Pull request overview
This PR extends WeightsLab’s signal/plot pipeline to (1) carry per-step outlier context to the UI (including fetching the full batch behind a plotted point) and (2) add a new “per-step model signals” API (gradient/weight norms and activation stats) with end-to-end examples, docs, and tests.
Changes:
- Add signal outlier payloads (trend band, per-step min/max range, top-N outliers) and downsampling that preserves outlier-bearing points; introduce
GetStepSamplesRPC to fetch the full batch behind a plotted step. - Introduce
wl.save_model_signalsandwl.track_model_signalsplus an implementation module that installs hooks and flushes per-step metrics. - Add extensive docs/examples/tests for both outliers and model signals.
Reviewed changes
Copilot reviewed 20 out of 21 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| weightslab/trainer/services/experiment_service.py | Forwards new outlier/trend/range fields to proto points, preserves outliers when downsampling, adds GetStepSamples. |
| weightslab/src.py | Adds public per-step model-signal write path + wiring for model-signal tracking via watch_or_edit. |
| weightslab/proto/experiment_service.proto | Adds SignalOutlier, additional LoggerDataPoint fields, plus GetStepSamples RPC/messages. |
| weightslab/proto/experiment_service_pb2.py | Regenerated protobuf Python stubs to include new messages/fields. |
| weightslab/proto/experiment_service_pb2_grpc.py | Regenerated gRPC service/client stubs to include GetStepSamples. |
| weightslab/examples/Usecases/wl-fashion-mnist-signals/main.py | New runnable example demonstrating model-signal tracking. |
| weightslab/examples/Usecases/wl-fashion-mnist-signals/config.yaml | Config for the Fashion-MNIST model-signals example. |
| weightslab/examples/Notebooks/Usecases/ws-segmentation-loss-shapes-classification.ipynb | New/updated notebook use case content (segmentation + decorated signal). |
| weightslab/examples/Notebooks/Local/wl-local-studio-quickstart.ipynb | Notebook updated, but currently contains unresolved merge conflict markers. |
| weightslab/components/model_signals.py | New implementation for per-step model-signal collection/flush via hooks. |
| weightslab/backend/logger.py | Adds outlier detection, trend tracking, schema migrations, range tracking, and step-sample-id queries. |
| weightslab/init.py | Re-exports save_model_signals / track_model_signals from the public API. |
| tests/general/test_model_signals.py | Unit tests for save_model_signals and hook-based track_model_signals. |
| tests/backend/test_signal_outliers.py | Tests for trend/outlier/range plumbing, persistence/migration, and service-level downsampling behavior. |
| docs/user_functions.rst | Documents new public APIs and model kwargs. |
| docs/model_interaction.rst | Adds model-signal guidance and best practices. |
| docs/logger.rst | Explains signal types and plot grouping (including model signals). |
| docs/examples/usecases/model_signals.rst | New docs page for the model-signals use case. |
| docs/examples/usecases/index.rst | Adds model-signals use case to the index. |
| docs/_static/examples-gallery.js | Adds model-signals card to the examples gallery. |
| AGENTS.md | Adds guidance on choosing the right save_* verb and model-signal tracking. |
Files not reviewed (1)
- weightslab/proto/experiment_service_pb2.py: Generated file
Suppressed comments (2)
weightslab/examples/Notebooks/Local/wl-local-studio-quickstart.ipynb:72
- This cell has merge conflict markers around the
sourcefield; the resulting notebook JSON is invalid.
<<<<<<< HEAD
"source": "import os\n\n# Imports weightslab\nimport weightslab as wl\n\n# Serve the weightslab app\nwl.serve()\n\n# Define root log dir for experiment\nroot_log_dir = os.environ.get(\"WEIGHTSLAB_ROOT_LOG_DIR\", None)"
=======
"source": [
"import os\n",
weightslab/examples/Notebooks/Local/wl-local-studio-quickstart.ipynb:102
- There are merge conflict markers at the end of the notebook as well, leaving duplicate/marked closing braces and invalid JSON.
<<<<<<< HEAD
}
=======
}
>>>>>>> origin/main
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: guillaume-byte <237722353+guillaume-byte@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Model signals: docs + example
Adds documentation and a worked example for the new per-step, per-layer model
signal tracking feature ("model signals"), covering how to use and interpret
these training-dynamics signals, updated API references, and a gallery entry.
UI Plots Zoom on Outliers
Allow the UI to request outlayers for a specific run and step.
Documentation and examples
model_signals.rst: tracking and interpreting per-layertraining dynamics (gradient norms, weight norms, activation stats) on
Fashion-MNIST, with explanations and code samples.
summary for discoverability.
API and usage docs
wl.save_model_signalsandwl.track_model_signals, plus the newwl.watch_or_editarguments for configuring model signal collection.and per-step (model) signals, with guidance on correct usage and plotting
behavior.
Concepts and best practices
explaining the purpose, integration, and interpretation of model signals,
including diagnostic patterns and model-architecture best practices.
Navigation
and clarified where to find integrations and references.