Skip to content

Releases: views-platform/views-hydranet

v0.1.3

Choose a tag to compare

@Polichinel Polichinel released this 16 Oct 02:01
2b54560

Does this change a forecast for an existing config?

No. Every addition in 0.1.3 is opt-in and default-off. A config that does not set the new key behaves exactly as it did on 0.1.2 — verified by measurement, not assumption: the training loss was computed with and without the new per-task accumulator on identical input, and the total/regression/classification values matched to the bit.

(This question leads the notes because eight views-models models install this package through a range pin — views-hydranet~=0.1.1 — so they pick up a new version without choosing it. See #399.)

Why 0.1.3 and not 0.2.0

A new config field would normally argue for a minor bump. It is a patch deliberately: the pod runner pins >=0.1.1,<0.2.0, so a 0.2.0 would not be collected and would require an edit to production infrastructure in views-models. 0.1.3 is picked up with no runner change.


Added — body_nll_denominator

Selects what the zero-truncated negative-binomial body loss divides its per-cell losses by.

value meaning
"positives" default. The count of cells with a positive target — the historical reduction, unchanged.
"all_cells" Every supervised cell — the denominator the joint hurdle NLL actually has.

Why it exists. Setting output_distribution: 'truncated_nb' silently moved this denominator from all cells to positive cells. At a 1-in-222 positive rate that rescaled one task-loss term 65×, and no config-identity check could see it, because arm_identity_check.py compares config values. Measured consequence on the shared trunk: the body's gradient-norm ratio against the gate went from 0.31 to 10.5 and cos(∇body, ∇gate) flipped from +0.72 to −0.20, with 75% of training windows in conflict.

"all_cells" also replaces a random denominator with a fixed one. The per-step positive count is often 0 or 1 for a rare target (median 0.88 positives per window for non-state violence), so E[Σ/N_pos] ≠ E[Σ]/E[N_pos] — the "positives" gradient is biased and high-variance on rare targets.

Rejected where it would be inert, rather than accepted and ignored:

  • requires output_distribution: 'truncated_nb' — every other family already reduces over all cells
  • requires body_supervision: 'all' — under 'active' the supervision mask is a positive mask, so the denominator collapses back and the flag becomes bit-identical to "positives"
  • the rule is enforced once and invoked from two places, so a caller that bypasses the config schema (a raw dict driver) hits the same check

Added — per-task loss instrumentation

When trajectory_log_path is set, the trajectory CSV now carries one loss/<task> column per task (body and gate, per target) alongside the existing pooled loss_reg / loss_cls.

Previously the engine collapsed the balancer's task vector to two sums and nothing else, so a per-target loss question had to be reconstructed from saved weights after the fact. That is exactly how the 65× above was found — weeks late.

Fixed — the trajectory log can come home from a pod run

Its parent directory is now created. A pod's deliver directory only contains artifact/, parquet/ and draws/, so an absolute path is the only way this file survives the run — and without the mkdir it raised FileNotFoundError after the data pull and the model build.


Not included

  • The balancer's learning rate never reaches its parameter group. _LRScheduler.step() zips against a length-1 get_lr(), so an unfrozen balancer trains at a constant 0.001 while the network decays. Pre-existing and latent — every views-models config pins freeze_multitask_balancer: True — and excluded deliberately rather than changing training dynamics inside a correctness release.
  • A CHANGELOG (#399).

How it was reviewed

/code-review medium → /review-diff → an independent guard audit in a clean context (36 mutations across 7 categories; 10 survivors, each addressed or recorded as an equivalent mutant) → two further mutation rounds that each found another defect in the fixes.

That process caught, in order: the instrumentation shipping completely inert (a hand-built return dict dropped the key, so the CSV never gained a column); a guard that compared a list to itself; a validation rule stated twice unevenly so one caller skipped half of it; three stale interface contracts; and four separate guards that compared a value to itself or to the constant that produced it.

Final state: 2247 tests passing, guards mutation-verified, rehearsed on TestPyPI as 0.1.3.dev9 and installed back before this release was cut.

Full diff: v0.1.2...v0.1.3

v0.1.2

Choose a tag to compare

@Polichinel Polichinel released this 29 Sep 04:37
f255152

Since 0.1.1:

  • fix: the forecast path no longer passes a flag the history-handler sniffer cannot honour, which removes the blocker that made forecasting impossible (#390). The volume decides which handler is used; the caller no longer overrides it.
  • docs: guide §7 — reading a HydraNet forecast: six targets, counts (not log1p), already gate-composed, S read from the array, one eval → one directory.
  • docs: the publishing guide now carries the TestPyPI rehearsal as a standing step.

Public API unchanged: from views_hydranet import HydranetManager.

Calibration and evaluation runs are byte-identical to 0.1.1 — only the forecast path changed.

v0.1.1

Choose a tag to compare

@Polichinel Polichinel released this 19 Sep 02:33
e3a039f

Since 0.1.0:

  • wandb is now a declared dependency (>=0.18.7,<1.0, matching views-pipeline-core 3.3.0). It was imported at five sites and arrived only through pipeline-core's pin (#381).
  • A CPU fallback now says why. The banner distinguishes a CPU-only torch build, no visible driver, and a driver too old for this torch's CUDA build — and says what to install. New config field require_cuda (default False) makes a CPU device a hard stop (#377).
  • Docs: M75 (hard-gate threshold), M76 (the wheel reproduces the checkout bit for bit), README Runtime Expectations.

Public API unchanged: from views_hydranet import HydranetManager.

v0.1.0

Choose a tag to compare

@Polichinel Polichinel released this 15 Sep 13:29
c7cbff0

First PyPI release of views-hydranet.

Public API: from views_hydranet import HydranetManager — and nothing else (#181/#279). Every other module is internal and carries no stability promise.

What this release is: the cell clamp promoted to a production setting (ADR-027 §2.1, ΔAP@h36 +0.14 to +0.19 on the roster models); the C-184 BatchNorm recalibration pass now sees clean data (C-328); the publication path itself — TestPyPI rehearsal inside the workflow, wheel contract that imports the package; and Epic #353, which fixed the 25 traps /code-review max found in the release before it shipped.

Consumers: the 8 HydraNet models in views-models pin views-hydranet<1.0.0 (#340).

Guide: docs/guides/publishing-to-pypi.md.