fixup(class-a-audit-batch-4b): invert MIN_HOLD_TEMPERATURE dir_acc → temp mapping
Post-commit kernel-formula audit of `compute_min_hold_penalty` at
trade_physics.cuh:567-577 showed the original `temp = TEMP_MIN +
(TEMP_MAX - TEMP_MIN) × skill` mapping was BACKWARDS for the
WR-plateau scenario this 8-commit chain targets.
Formula recap:
soft_factor = deficit / (deficit + T)
HIGH T → soft_factor → 0 → forgiving (no exit penalty)
LOW T → soft_factor → 1 → sharp (max exit penalty)
The deleted schedule's `START=50 → END=5` therefore meant
"permissive early, strict late" (classic explore-then-exploit), not
"permissive when committed, strict when uncertain" as the
substituted mapping assumed.
WR-plateau scenario: dir_acc ≈ 0.46-0.48 (model committed but
wrong) — the model needs a permissive (HIGH T) exit ramp to bail
out of bad committed directions, not maximum exit penalty (LOW T)
locking it into them.
Inverted the mapping using `confusion = 1 - skill`:
- dir_acc ≈ 0.5 (random / plateau) → confusion=1 → temp=50
(permissive, plateau exit ramp)
- dir_acc → 1.0 (saturated skill) → confusion=0 → temp=5
(strict, force informed commitment)
This preserves the deleted schedule's epoch-0 anchor (T=50
cold-start) while reacting to actual realized skill instead of an
epoch-time anchor.
Files touched (atomic per `feedback_no_partial_refactor`):
- min_hold_temperature_update_kernel.cu — formula + header doctring.
- sp14_isv_slots.rs — slot-doc comment expanded with new mapping
+ fixup-rationale paragraph.
- gpu_aux_trunk.rs — `MinHoldTemperatureUpdateOps` docstring.
- gpu_dqn_trainer.rs — cubin docstring + ISV_TOTAL_DIM doc-comment.
- tests/sp14_oracle_tests.rs — Tests 3 + 4 flipped (high dir_acc
now expects temp=TEMP_MIN; low dir_acc now expects TEMP_MAX);
Tests 1, 2, 5 numerically unchanged (sentinel guard fires before
the mapping; midpoint dir_acc=0.75 has skill=confusion=0.5 so
pre/post-fixup numerics match).
- docs/dqn-wire-up-audit.md — Item 4 entry rewritten with the
fixup rationale + mapping table updated.
Verification:
cargo check -p ml --tests --all-targets PASS
cargo test sp14_audit_4b (lib) 4/4 PASS (slot layout
locks unchanged)
GPU oracle re-run deferred to next L40S smoke (kernel was rebuilt
in-place so cubin contents change; the 5 oracles encode the new
mapping and will exercise the new cubin).
Per `pearl_first_observation_bootstrap.md` (sentinel→target
replace; sentinel anchors unchanged) +
`pearl_symmetric_clamp_audit.md` (bilateral clamp on target_temp
preserved) + `feedback_no_partial_refactor.md`.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -45,19 +45,20 @@ T(e) = T_end + (T_start - T_end) × exp(-e / decay)
|
||||
(formerly `state_layout.cuh::MIN_HOLD_TEMPERATURE_{START=50, END=5, DECAY=20}` + `training_loop.rs::min_hold_temperature_for_epoch`) with an ISV-driven signal-driven temperature derived from the model's directional accuracy skill. Producer kernel: `min_hold_temperature_update_kernel.cu` (NEW file, single-thread cold-path, per-epoch boundary launch).
|
||||
|
||||
**Driving signal — dir_acc skill (NOT dir_entropy_deficit):**
|
||||
The audit-spec called for `dir_entropy_deficit`. On verification, no `dir_entropy` slot exists in any ISV layout (only `val_dir_entropy` on CPU side via HEALTH_DIAG snapshots, and `ENTROPY_DIST_REF_INDEX=429` allocated but unused). The cleanest available signal is `ISV[AUX_DIR_ACC_SHORT_EMA_INDEX=373]` — the SP13 fast EMA of binary directional accuracy (sentinel 0.5 = random baseline). This serves as a parallel proxy for "model commits" via dir_acc skill:
|
||||
The audit-spec called for `dir_entropy_deficit`. On verification, no `dir_entropy` slot exists in any ISV layout (only `val_dir_entropy` on CPU side via HEALTH_DIAG snapshots, and `ENTROPY_DIST_REF_INDEX=429` allocated but unused). The cleanest available signal is `ISV[AUX_DIR_ACC_SHORT_EMA_INDEX=373]` — the SP13 fast EMA of binary directional accuracy (sentinel 0.5 = random baseline). This serves as a parallel proxy for "model is uncertain" via dir_acc confusion:
|
||||
```
|
||||
skill = clamp((short_ema − 0.5) / 0.5, 0, 1) ∈ [0, 1]
|
||||
temp = TEMP_MIN + (TEMP_MAX − TEMP_MIN) × skill
|
||||
skill = clamp((short_ema − 0.5) / 0.5, 0, 1) ∈ [0, 1]
|
||||
confusion = 1 − skill ∈ [0, 1]
|
||||
temp = TEMP_MIN + (TEMP_MAX − TEMP_MIN) × confusion
|
||||
```
|
||||
- skill=0 (no skill, dir_acc ≤ 0.5) → temp=TEMP_MIN=5 (sharp commitment pressure)
|
||||
- skill=1 (saturated dir_acc=1.0) → temp=TEMP_MAX=50 (permissive — informed exits, no need to penalize sharply)
|
||||
- confusion=1 (no skill, dir_acc ≤ 0.5) → temp=TEMP_MAX=50 (permissive — exit ramp out of plateau / random-baseline commitments)
|
||||
- confusion=0 (saturated dir_acc=1.0) → temp=TEMP_MIN=5 (strict — force the model to stay in informed positions)
|
||||
|
||||
This matches the audit-spec semantic ("model commits → temp HIGH"). Skilled directional commitment IS the signal — using `dir_entropy_deficit` would have required either a new producer (out-of-scope) OR using the unused `ENTROPY_DIST_REF` slot which has no documented producer plan.
|
||||
The mapping uses `confusion` (not raw `skill`) because the consumer formula `compute_min_hold_penalty` (`soft_factor = deficit / (deficit + T)` at trade_physics.cuh:575) makes HIGH T = forgiving (saturation factor → 0, no exit penalty) and LOW T = sharp (saturation factor → 1, max exit penalty). The deleted schedule's `START=50 → END=5` therefore meant "permissive early, strict late" (classic explore-then-exploit). Mapping `skill → temp` directly would be backwards: at the WR-plateau scenario this 8-commit chain targets (dir_acc ≈ 0.46-0.48, model committed but wrong), `skill=0` → `temp=5` would lock the model into bad committed directions exactly when an exit ramp is needed. The `confusion` inversion preserves the deleted schedule's epoch-0 anchor (T=50 cold-start) and gives the plateau scenario its designed exit ramp.
|
||||
|
||||
**Slot allocation:** ISV[460..461) — `MIN_HOLD_TEMPERATURE_ADAPTIVE_INDEX=460` (sentinel 50.0, bounds [5, 50], α=0.05). Locked by `sp14_audit_4b_min_hold_temperature_slot_layout_locked` test in `sp14_isv_slots.rs`.
|
||||
|
||||
**Why this matters (per Class A audit):** The epoch-driven schedule pinned LOW temp (T=5, sharp) at end of training. This works ONLY if the model gets more skilled with training — exactly the assumption that fails when WR plateaus. Maximum punishment is delivered exactly when the model most needs forgiveness to escape the plateau. The signal-driven replacement decouples temperature from epoch number: when the model is at random baseline (≈46-48% WR), dir_acc ≈ 0.5 → skill ≈ 0 → temp=5 (sharp), forcing commitment. When the model breaks through to dir_acc > 0.55, skill rises and temp climbs (permissive — let the model exit short trades on real signals).
|
||||
**Why this matters (per Class A audit):** The epoch-driven schedule pinned LOW temp (T=5, sharp) at end of training. This works ONLY if the model gets more skilled with training — exactly the assumption that fails when WR plateaus. Maximum punishment is delivered exactly when the model most needs forgiveness to escape the plateau. The signal-driven replacement decouples temperature from epoch number and inverts the dir_acc → temp coupling: when the model is at random baseline (≈46-48% WR), dir_acc ≈ 0.5 → confusion ≈ 1 → temp=50 (permissive, exit ramp out of bad committed direction). When the model breaks through to dir_acc → 1.0, confusion → 0 and temp drops toward 5 (strict — force commitment because it's now informed). This is "permissive when uncertain, strict when informed" — the opposite of the broken epoch-anchored schedule.
|
||||
|
||||
**DELETED atomically:** Per `feedback_no_legacy_aliases.md` + `feedback_no_partial_refactor.md`:
|
||||
1. `state_layout.cuh::MIN_HOLD_TEMPERATURE_{START, END, DECAY}` #defines.
|
||||
@@ -80,7 +81,7 @@ This matches the audit-spec semantic ("model commits → temp HIGH"). Skilled di
|
||||
- `crates/ml/build.rs` — kernel added to compile list.
|
||||
- `crates/ml/src/trainers/dqn/state_reset_registry.rs` — `sp14_audit_4b_min_hold_temperature_adaptive` registry entry.
|
||||
- `crates/ml/src/trainers/dqn/trainer/training_loop.rs` — DELETED `min_hold_temperature_for_epoch` body, DELETED both call sites, ADDED `read_min_hold_temperature_from_isv` helper, ADDED launcher invocation per-epoch, ADDED FoldReset dispatch arm.
|
||||
- `crates/ml/tests/sp14_oracle_tests.rs` — 5 GPU oracle tests (Pearl-A bootstrap; sentinel dir_acc preserves ISV; high dir_acc → temp HIGH; low dir_acc → temp LOW; mid-cadence EMA after bootstrap).
|
||||
- `crates/ml/tests/sp14_oracle_tests.rs` — 5 GPU oracle tests (Pearl-A bootstrap; sentinel dir_acc preserves ISV; high dir_acc → temp LOW [strict, informed commitment]; low dir_acc → temp HIGH [permissive, plateau exit ramp]; mid-cadence EMA after bootstrap).
|
||||
|
||||
### Verification
|
||||
|
||||
@@ -100,11 +101,13 @@ ISV_TOTAL_DIM: 459 → 461.
|
||||
The audit-spec was correct on Item 3 (plan_threshold floor) — line 44, formula `fmaxf(0.1f, 0.5f * ema)`, `0.1f` is the absolute floor and `0.5f * ema` is the relative one. Picked Design Y (inline) per the audit's recommendation.
|
||||
|
||||
The audit-spec was **partially wrong on Item 4** in two ways:
|
||||
1. **Driving signal:** `dir_entropy_deficit` does not exist as an ISV signal. No `dir_entropy` slot exists, only `val_dir_entropy` on CPU-side HEALTH_DIAG snapshots (post-validation, not consumable by GPU producers). `ENTROPY_DIST_REF_INDEX=429` is allocated but unused. Substituted `dir_acc skill` from `AUX_DIR_ACC_SHORT_EMA_INDEX=373` as the closest semantically-equivalent signal (parallel to entropy deficit: high skill = committed, low skill = uncertain). Mapping preserves the spec's intent ("model commits → temp HIGH").
|
||||
1. **Driving signal:** `dir_entropy_deficit` does not exist as an ISV signal. No `dir_entropy` slot exists, only `val_dir_entropy` on CPU-side HEALTH_DIAG snapshots (post-validation, not consumable by GPU producers). `ENTROPY_DIST_REF_INDEX=429` is allocated but unused. Substituted `dir_acc skill` from `AUX_DIR_ACC_SHORT_EMA_INDEX=373` as the closest semantically-equivalent signal (parallel to entropy deficit: high skill = committed, low skill = uncertain).
|
||||
2. **Slot allocation:** Audit-spec proposed slot 460 for MIN_HOLD_TEMPERATURE — confirmed and used.
|
||||
|
||||
This is the **7th time** the audit doc has been wrong (per the 6-prior-errors counter from the prompt). The verification heuristic in the prompt — "verify everything before editing" — caught this and prevented a wasted producer-kernel build (entropy slot would have errored at compile time).
|
||||
|
||||
**Mapping-direction fixup (2026-05-08):** The initial wiring of this fix used `temp = TEMP_MIN + (TEMP_MAX − TEMP_MIN) × skill` based on the audit-spec phrase "model commits → temp HIGH". On post-commit review the kernel formula `compute_min_hold_penalty` was re-read at trade_physics.cuh:567-577: `soft_factor = deficit / (deficit + T)`. HIGH T → soft_factor → 0 → forgiving (no exit penalty); LOW T → soft_factor → 1 → sharp (max exit penalty). The deleted schedule's `START=50 → END=5` therefore meant "permissive early, strict late" (classic explore-then-exploit), not "permissive when committed, strict when uncertain". The original `skill → temp` mapping was backwards for the WR-plateau scenario this 8-commit chain targets: at plateau (dir_acc ≈ 0.46-0.48, committed but wrong), `skill=0 → temp=5` would have locked the model into bad committed directions with maximum exit penalty — exactly when an exit ramp is needed. Inverted to `confusion = 1 − skill` so plateau → temp HIGH (permissive exit ramp) and saturated skill → temp LOW (strict, force informed commitment). Tests 3 and 4 flipped accordingly; tests 1, 2, 5 (sentinel guard, midpoint, midpoint EMA) are numerically unchanged because their dir_acc inputs sit at 0.5/0.75 fixed points where `skill ≈ confusion` or the guard fires before the mapping.
|
||||
|
||||
Reading directly from `compute_min_hold_penalty` in `trade_physics.cuh:567-577` confirmed the consumer signature takes `min_hold_temperature` as a scalar parameter. Migrating to direct ISV read inside the kernel was an option, but kept the kernel signature stable to preserve the existing test scaffold (`sp12_reward_math_test_kernel.cu` at line 36 also uses the scalar arg). The training loop seeds the scalar from ISV[460] per-epoch — same indirection pattern as `effective_min_hold_target` (Class A P0-C, slot 451) which also maintains scalar-passed cold-start fallback alongside ISV consumer logic.
|
||||
|
||||
## 2026-05-08 — Class A audit-fix Batch 4-A: DD saturation floor adaptive + legacy DD path Case A
|
||||
|
||||
Reference in New Issue
Block a user