diff options
| author | Sam Scholten | 2026-10-07 15:33:41 +1000 |
|---|---|---|
| committer | Sam Scholten | 2026-10-07 15:33:41 +1000 |
| commit | c465409aaa5672e3466b6f8744b737fa84993f2b (patch) | |
| tree | 4e88defa764768c7f6d104a1a902d5e092023fc0 /cmds/negcap.go | |
| parent | 2f75794194209586d0b17d26e4e87d94d3e6aead (diff) | |
| download | fluxrec-c465409aaa5672e3466b6f8744b737fa84993f2b.tar.gz fluxrec-c465409aaa5672e3466b6f8744b737fa84993f2b.zip | |
export: per-feed negative ceiling (--neg-feed-cap-pct)
No feed may hold more than 5% (floor 50) of the negative target, counting
existing history: uncapped, a few firehoses own the negative class because
the read+unstarred pool is sized by feed volume. Enforced by skipping
ceiling-full feeds mid seeded permutation walk; shortfall vs the ratio is
reported, neg_capped added to the run summary. Also makes bulk mark-read
of high-volume feeds safe for training data.
Diffstat (limited to 'cmds/negcap.go')
| -rw-r--r-- | cmds/negcap.go | 34 |
1 files changed, 34 insertions, 0 deletions
diff --git a/cmds/negcap.go b/cmds/negcap.go new file mode 100644 index 0000000..f531b14 --- /dev/null +++ b/cmds/negcap.go @@ -0,0 +1,34 @@ +// Per-feed negative ceiling, shared by `export` (enforced while sampling) +// and `compact-labels` (enforced against existing history). +// +// Why: negatives are sampled from the read+unstarred pool, whose size is +// dominated by high-volume feeds. Without a ceiling, a handful of firehoses +// own most of the negative class, and the model learns "wire headline" as +// its main axis instead of "headline samsc liked". The ceiling is on each +// feed's *total* negatives (existing history + this run's additions), not +// just the run's sample: a per-run cap would only slow domination, not +// bound it. +package cmds + +// negFeedCapFloor is the absolute minimum ceiling. It keeps a young, +// still-small negative pool from capping feeds at a handful of rows: +// early training needs volume more than it needs balance. +const negFeedCapFloor = 50 + +// feedNegCap returns the maximum negatives any single feed may hold in the +// pool: pct% of the target (negRatio × posTotal), floored at negFeedCapFloor +// and never above the target itself. pct <= 0 means uncapped (0). +func feedNegCap(pct, negRatio, posTotal int) int { + if pct <= 0 { + return 0 + } + target := negRatio * posTotal + cap := pct * target / 100 + if cap < negFeedCapFloor { + cap = negFeedCapFloor + } + if cap > target { + cap = target + } + return cap +} |
