Repository navigation
Releases: views-platform/views-hydranet
Release list
v0.1.3
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-1get_lr(), so an unfrozen balancer trains at a constant0.001while the network decays. Pre-existing and latent — everyviews-modelsconfig pinsfreeze_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
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
Since 0.1.0:
wandbis 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
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.