Skip to content

Add live fit monitoring (streaming callbacks) to the course#46

Open
seabbs-bot wants to merge 5 commits into
sbfnk:mainfrom
seabbs-bot:live-fit-monitoring
Open

Add live fit monitoring (streaming callbacks) to the course#46
seabbs-bot wants to merge 5 commits into
sbfnk:mainfrom
seabbs-bot:live-fit-monitoring

Conversation

@seabbs-bot

@seabbs-bot seabbs-bot commented Jun 4, 2026

Copy link
Copy Markdown

Closes #45.

This surfaces the streaming-callback / TensorBoard pattern we use in epiforecasts/BVDOutbreakSize in the course, adapted to the Turing version pinned here (transition.stats is read directly rather than through the newer AbstractMCMC.ParamsWithStats constructor).

What and where

Issue #45 proposed two teaching ideas. This PR does both, keeping the sessions light by putting the substantive how-to on a reference page (matching the existing reference/logdensity_interface.qmd pattern).

New reference page reference/live_monitoring.qmd — "Watching a fit unfold":

  • The AbstractMCMC per-iteration callback hook and what transition.stats exposes for NUTS (log-density, divergence flag, step size, acceptance rate).
  • A dependency-free progress_recorder closure that streams the log-density and a running divergence count, wrapped in try/catch so it can never abort a fit.
  • Streaming the warmup steps (discard_adapt = false) to watch the log-density climb into the typical set.
  • Neal's funnel as a worked pathology: divergences pile up from the first steps, the live signal you'd want on a slow model.
  • An optional, collapsed TensorBoard backend (run locally — TensorBoardLogger.jl is not a course dependency), mirroring the optional extension in BVDOutbreakSize.

sessions/mcmc.qmd — the hand-coded Metropolis-Hastings idea from the issue: a small animated GIF showing a well-tuned chain vs a sticky (too-small-step) one filling in side by side, so students watch good vs bad mixing. Plus a collapsed pointer to the reference page.

sessions/mcmc_diagnostics.qmd — a collapsed note linking to the reference page from the divergent-transitions discussion, framing live monitoring as the slow-model complement to the post-hoc checks.

Notes

  • Both new/changed pages render locally with quarto render. The animation and all callback code execute headlessly (GKSwstype=100).
  • No new dependencies: the progress recorder uses only Turing; the TensorBoard block is illustrative (eval: false).
  • Follows the callout conventions in CONTRIBUTING.md (collapsed blue notes for optional context).

This was opened by a bot. Please ping @seabbs for any questions.

Summary by CodeRabbit

Documentation

  • Added comprehensive guide for live monitoring of MCMC sampling progress using callbacks, including real-time statistics tracking
  • Included animated trace visualisations and TensorBoard integration examples for monitoring MCMC diagnostics
  • New guidance on detecting divergences early during sampling for computationally expensive models

Surface the streaming-callback pattern from epiforecasts/BVDOutbreakSize
in the course (issue sbfnk#45):

- New reference page reference/live_monitoring.qmd showing the
  AbstractMCMC per-iteration callback hook, a dependency-free progress
  recorder that streams log-density and divergences from transition.stats,
  the warmup log-density climb, a Neal's funnel divergence example, and an
  optional TensorBoard backend to run locally.
- mcmc.qmd: animate the hand-coded Metropolis-Hastings chain so students
  watch well-tuned vs sticky mixing unfold, plus a collapsed pointer to
  the reference page.
- mcmc_diagnostics.qmd: collapsed note linking to the reference page from
  the divergent-transitions discussion.
@coderabbitai

coderabbitai Bot commented Jun 4, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a72b37ab-756a-42e4-8112-339964ced85d

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Walkthrough

This PR adds teaching materials demonstrating live streaming of MCMC diagnostics during sampling. A new reference page explains the AbstractMCMC callback= mechanism and provides a dependency-free progress recorder utility, two worked examples (one with warmup diagnostics, one with a pathological funnel fit), and a TensorBoard integration pattern. Related updates to the MCMC and diagnostics sessions show animated Metropolis-Hastings mixing and link to the live monitoring approach.

Changes

MCMC Live Monitoring Reference and Examples

Layer / File(s) Summary
Reference page introduction and purpose
reference/live_monitoring.qmd
Establishes the motivation: post-hoc diagnostics are insufficient for debugging long runs; streaming per-iteration diagnostics during sampling aids intuition and early problem detection.
AbstractMCMC callback mechanism and transition diagnostics
reference/live_monitoring.qmd
Documents the callback= hook and how to inspect transition.stats fields for per-step sampler diagnostics, explaining sampler-robustness across NUTS and other samplers.
Dependency-free progress recording helper
reference/live_monitoring.qmd
Defines progress_recorder utility to extract divergence counts and log-joint values with throttled logging and exception safety via try/catch.
Example: streaming diagnostics during warmup and adaptation
reference/live_monitoring.qmd
Demonstrates the progress recorder streaming and plotting log-density and cumulative divergences during warmup, with interpretation of divergence behaviour.
Example: Neal's funnel pathological fit
reference/live_monitoring.qmd
Shows a deliberately unhealthy fit where divergences accumulate from the start, illustrating how live streaming reveals sampler pathology visually.
TensorBoard live curves integration
reference/live_monitoring.qmd
Introduces an illustrative tensorboard_callback implementation using TensorBoardLogger.jl to log real-valued transition statistics at configurable intervals, with shell instructions and best-practice notes.
Guidance on when to stream callbacks and supporting references
reference/live_monitoring.qmd
Provides a checklist for using streaming callbacks and gathers references for the callback interface, TensorBoard, and funnel/HMC materials.
Metropolis-Hastings animated chain mixing visualisation
sessions/mcmc.qmd
Adds "Watching the chain explore" section with animated side-by-side traces of well-tuned vs mis-tuned proposal distributions and a callout linking to live monitoring patterns.
Live divergence monitoring note in diagnostics session
sessions/mcmc_diagnostics.qmd
Adds a note explaining that streaming per-iteration callbacks enable early detection of unpromising runs, linking to the live monitoring reference page.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Add live fit monitoring (streaming callbacks) to the course' directly and clearly summarises the main change: introducing live/streaming fit monitoring examples and integrating them into course sessions.
Linked Issues check ✅ Passed The pull request fully addresses the objectives in issue #45: it demonstrates live per-iteration MCMC diagnostics via callbacks in both Metropolis-Hastings (animated GIF trace comparison) and Turing/NUTS (progress_recorder and TensorBoard examples), integrated into existing sessions (mcmc.qmd and mcmc_diagnostics.qmd) with optional, lightweight additions.
Out of Scope Changes check ✅ Passed All changes are within scope: the new reference/live_monitoring.qmd page, mcmc.qmd additions (animated trace comparison and reference link), and mcmc_diagnostics.qmd additions (callback note and reference link) directly support the issue objectives and integrate cleanly into the existing course structure.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (3)
reference/live_monitoring.qmd (3)

107-109: 💤 Low value

Bare catch block hides all exceptions.

The catch block on lines 107-109 silently swallows all exceptions, including potential bugs in the callback logic itself. While the comment explains that "a progress callback must never bring down a fit," consider at least logging caught exceptions during development.

💡 Optional: Log exceptions for debugging
         catch e
-            # A progress callback must never bring down a fit.
+            # A progress callback must never bring down a fit.
+            `@debug` "Progress callback error" exception=(e, catch_backtrace())
         end

This preserves the fail-safe behaviour whilst enabling debugging with ENV["JULIA_DEBUG"] = "Main".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@reference/live_monitoring.qmd` around lines 107 - 109, Replace the bare
"catch" that silently swallows all exceptions with a named exception handler
(e.g., "catch e") and log the exception when debugging is enabled; specifically,
in the block that wraps the progress callback (the catch surrounding the
progress callback invocation in live_monitoring), change to "catch e" and call a
logger (for example `@error` or print with string(e) and optionally stacktrace)
guarded by ENV["JULIA_DEBUG"] == "Main" so normal runs keep the fail-safe
behaviour but developers can see exceptions during debugging.

253-253: 💤 Low value

Add a trailing newline at end of file.

Many text editors and POSIX standards expect files to end with a newline character.

📝 Proposed fix
 - [Betancourt (2017), A Conceptual Introduction to Hamiltonian Monte Carlo](https://arxiv.org/abs/1701.02434) — why divergences signal geometric problems
+
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@reference/live_monitoring.qmd` at line 253, The file
reference/live_monitoring.qmd is missing a trailing newline; open the file and
add a single newline character at the end of the file (ensure the final line
ends with '\n'), save and commit so the file terminates with a newline as
required by POSIX and common editors.

30-30: ⚡ Quick win

Clarify wording for the “companion proposals” references in reference/live_monitoring.qmd (line 30).

Line 30 mixes a repository link (epiforecasts/BVDOutbreakSize) with an issue link (EpiBranch.jl#112), so “companion proposals” is slightly misleading; rephrase to make the resource types clear. Both links resolve successfully.

Suggested change
-The companion proposals are [epiforecasts/BVDOutbreakSize](https://github.com/epiforecasts/BVDOutbreakSize) and [epiforecasts/EpiBranch.jl#112](https://github.com/epiforecasts/EpiBranch.jl/issues/112).
+The companion resources are the [BVDOutbreakSize](https://github.com/epiforecasts/BVDOutbreakSize) repository and [EpiBranch.jl#112](https://github.com/epiforecasts/EpiBranch.jl/issues/112).
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@reference/live_monitoring.qmd` at line 30, Change the phrase "companion
proposals" to explicitly state the resource types: mention that one is a
repository (epiforecasts/BVDOutbreakSize) and the other is an issue/discussion
in EpiBranch.jl (EpiBranch.jl#112); update the sentence on line containing "The
companion proposals are" to something like "Related resources: the repository
epiforecasts/BVDOutbreakSize and issue EpiBranch.jl#112" so readers can tell one
is a repo and the other is an issue.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@reference/live_monitoring.qmd`:
- Around line 107-109: Replace the bare "catch" that silently swallows all
exceptions with a named exception handler (e.g., "catch e") and log the
exception when debugging is enabled; specifically, in the block that wraps the
progress callback (the catch surrounding the progress callback invocation in
live_monitoring), change to "catch e" and call a logger (for example `@error` or
print with string(e) and optionally stacktrace) guarded by ENV["JULIA_DEBUG"] ==
"Main" so normal runs keep the fail-safe behaviour but developers can see
exceptions during debugging.
- Line 253: The file reference/live_monitoring.qmd is missing a trailing
newline; open the file and add a single newline character at the end of the file
(ensure the final line ends with '\n'), save and commit so the file terminates
with a newline as required by POSIX and common editors.
- Line 30: Change the phrase "companion proposals" to explicitly state the
resource types: mention that one is a repository (epiforecasts/BVDOutbreakSize)
and the other is an issue/discussion in EpiBranch.jl (EpiBranch.jl#112); update
the sentence on line containing "The companion proposals are" to something like
"Related resources: the repository epiforecasts/BVDOutbreakSize and issue
EpiBranch.jl#112" so readers can tell one is a repo and the other is an issue.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 481791cf-3354-4294-bb89-e5723b0230b9

📥 Commits

Reviewing files that changed from the base of the PR and between 16773f8 and 87b755d.

📒 Files selected for processing (3)
  • reference/live_monitoring.qmd
  • sessions/mcmc.qmd
  • sessions/mcmc_diagnostics.qmd

Comment thread reference/live_monitoring.qmd Outdated
Comment thread reference/live_monitoring.qmd Outdated
Comment thread reference/live_monitoring.qmd Outdated
Comment thread reference/live_monitoring.qmd Outdated
Comment thread reference/live_monitoring.qmd
Comment thread reference/live_monitoring.qmd
Comment thread reference/live_monitoring.qmd Outdated
Comment thread reference/live_monitoring.qmd Outdated
Comment thread reference/live_monitoring.qmd Outdated
Comment thread reference/live_monitoring.qmd
Comment thread sessions/mcmc.qmd Outdated
Comment thread sessions/mcmc.qmd Outdated
Comment thread sessions/mcmc.qmd Outdated
Comment thread sessions/mcmc.qmd Outdated
Comment thread sessions/mcmc_diagnostics.qmd
…ions

Responds to PR review on the live-fit-monitoring page:

- reference/live_monitoring.qmd: drop the BVD package-extension asides;
  rename the recorder section to "A simple progress recorder"; turn a
  code comment into prose; add a note on why the built-in mcmc_callback
  (AbstractMCMC >= 5.13) can't be used on the pinned Turing; add an
  optional example that animates the fit from inside the callback; rework
  the TensorBoard section to use the new shared helper, note it is
  reusable elsewhere (e.g. PMCMC), and that TensorBoard is also a handy
  viewer for completed fits; show multiple-chain run dirs instead of the
  single-chain caveat.
- src/tensorboard_callback.jl: new opt-in TensorBoard streaming helper
  (loads TensorBoardLogger only when you ask for it), logging params and
  diagnostics as scalars and running histograms.
- mcmc.qmd: trim the animation intro, fold the "good proposal is hard to
  find" motivation into the sticky-vs-tuned demo, and move the Turing
  callback pointer to after NUTS is introduced.
- mcmc_diagnostics.qmd: reframe the note as an optional dashboard for
  watching inference in progress and reviewing results.
@seabbs-bot

Copy link
Copy Markdown
Author

Pushed a revision addressing all the review comments (replied inline and resolved each thread).

Highlights:

  • mcmc_callback: AbstractMCMC ≥5.13 ships it (with a TensorBoard callback). Turing 0.42's compat allows bumping AbstractMCMC to 5.15, and I tested it — but the built-in callback calls getparams(state), which the pinned Turing 0.42.2 / DynamicPPL 0.39.9 don't implement, so it errors. Reverted the bump and kept the small explicit closure (reads transition.stats), with a collapsed note recording this. Worth revisiting once the course moves to a newer Turing.
  • TensorBoard helper moved into src/tensorboard_callback.jl — opt-in (loads TensorBoardLogger only when you ask), logs params + diagnostics as scalars and running histograms, reusable with any sample(...) call. Verified end-to-end (event files, grouped tags, divergence flag, histograms). The page includes it and shows its source folded.
  • In-callback live animation added as an optional example, plus the wording/ordering fixes in mcmc.qmd and mcmc_diagnostics.qmd.

Re-rendered reference/live_monitoring.qmd and sessions/mcmc.qmd locally; both build, animations included.

This was opened by a bot. Please ping @seabbs for any questions.

Bumps the inference stack to the latest Turing (0.45.0), which makes the
official AbstractMCMC mcmc_callback / ParamsWithStats interface work and
replaces MCMCChains with FlexiChains as the default chain type.

Dependency changes (Project.toml / Manifest.toml):
- Turing 0.42.2 -> 0.45.0 (DynamicPPL 0.41, AbstractMCMC 5.15).
- Add AbstractMCMC, TensorBoardLogger, FlexiChains as direct deps.

TensorBoard helper (src/tensorboard_callback.jl):
- Rewritten to read parameters and diagnostics through the official
  AbstractMCMC.ParamsWithStats interface, logging grouped params/ and
  diagnostics/ tags with scalar traces and running histograms (the
  grouped-tabs dashboard from BVDOutbreakSize). Verified end-to-end.

Reference page (reference/live_monitoring.qmd):
- Drop the "mcmc_callback errors on the pinned Turing" caveat (now works);
  present the built-in mcmc_callback one-liner alongside the grouped helper.

Session migration MCMCChains -> FlexiChains (verified by re-render):
- `using MCMCChains` -> `using FlexiChains`.
- Diagnostics: `DataFrame(rhat/ess/mcse/mean(chain))` -> one
  `DataFrame(summarystats(chain))` (`:param`, `:rhat`, `:ess_bulk`, ...).
- Divergences: `chain.info[:divergent_transitions]` ->
  `sum(skipmissing(vec(chain[:numerical_error])))`.
- Energy: `chain.info[:energy]` -> `vec(chain[:hamiltonian_energy])`.
- Per-chain `chain[:, :, i]` -> `chain[chain = i]`; `length` -> `size(.,1)`.
- Posterior predictive `predictions[Symbol("obs[$j]")]` ->
  `predictions[@varname(obs[j])]`.
- `describe` -> `summarystats`; `corner` -> StatsPlots `cornerplot`.
@seabbs-bot

Copy link
Copy Markdown
Author

Heads up: this PR's scope grew. It now also bumps the course to Turing 0.45.0 and migrates from MCMCChains to FlexiChains (the new default chain type), so the live-monitoring feature can use the official mcmc_callback / ParamsWithStats interface and the grouped-tabs TensorBoard dashboard.

What changed beyond the original live-monitoring page:

  • Turing 0.42.2 → 0.45.0; added AbstractMCMC, TensorBoardLogger, FlexiChains as direct deps.
  • All chain-handling sessions migrated to the FlexiChains API (diagnostics tables, divergences via chain[:numerical_error], energy via chain[:hamiltonian_energy], per-chain chain[chain=i], predict via @varname(obs[j]), describesummarystats, cornercornerplot).

Validation: re-rendered the whole site fresh on 0.45. 8/10 sessions render clean with zero errors; the last two (seitl, universal_differential_equations) were still rendering on a heavily-loaded machine when this was pushed — I'll confirm/fix those in a follow-up commit if needed.

This was opened by a bot. Please ping @seabbs for any questions.

`rand(model)` now returns a `VarNamedTuple`, so `params.R_0` field access
errored. Wrap with `NamedTuple(rand(model))` to restore field access.
Two breakages from the dependency bump (unrelated to FlexiChains):

- Lux 1.29 + ForwardDiff no longer differentiates the neural-ODE loss
  (`Lux.PartialsFn` error). Switch training to reverse-mode `AutoZygote`,
  with SciMLSensitivity providing the adjoint through the ODE solve (added
  as a direct dep). Reverse-mode is the standard choice for neural-network
  training anyway; explanations updated to match.
- `sol.retcode != :Success` is always true now that retcode is a
  `ReturnCode` enum, so `predict_ude` always returned NaN (constant loss ->
  nothing gradient -> `iterate(::Nothing)`). Use
  `SciMLBase.successful_retcode`.
@seabbs-bot

Copy link
Copy Markdown
Author

Migration complete — all 10 sessions now render clean on Turing 0.45 + FlexiChains (full fresh re-render, no errors).

The two non-chain sessions that broke on the stack bump (not the FlexiChains migration) are fixed:

  • seitl: rand(model) now returns a VarNamedTuple in DynamicPPL 0.41 → wrapped with NamedTuple(...) to keep field access.
  • universal_differential_equations: Lux 1.29 + ForwardDiff broke the neural-ODE gradient → switched training to reverse-mode AutoZygote (SciMLSensitivity adjoint, added as a dep); also fixed an always-NaN predict_ude caused by retcode != :Success (retcode is now a ReturnCode enum) via SciMLBase.successful_retcode.

This was opened by a bot. Please ping @seabbs for any questions.

@sbfnk

sbfnk commented Jun 17, 2026

Copy link
Copy Markdown
Owner

@seabbs-bot can we split this in 2 PRs, one for the changes needed to work with latest Turing (and need to happen) from the live monitoring (less convinced all of this needs to be exposed to the course participants)?

@seabbs

seabbs commented Jun 18, 2026

Copy link
Copy Markdown
Collaborator

Sure happy to rebase this on an updated main. I think in general this flags issues from not having dependabot etc running across this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Teaching aid: stream MCMC fits live to build convergence/divergence intuition

3 participants