refactor(ml-alpha): drop competitive-gate machinery — stacked is default
Per user direction "no gating, this is the new default": the stacked
Mamba2 -> CfC -> heads design is THE production architecture. There's
no competing-baseline comparison to run. Validation reduces to normal
training metrics (per-horizon val AUC, train loss curve, sanity floor
of >0.5 AUC).
Deletions:
- crates/ml-alpha/src/gate/cfc_vs_mamba2.rs (gate verdict logic)
- crates/ml-alpha/src/gate/mod.rs
- crates/ml-alpha/examples/alpha_gate.rs (gate runner binary)
Renames:
- crates/ml-alpha/src/gate/auc.rs -> crates/ml-alpha/src/eval/auc.rs
- lib.rs: pub mod gate -> pub mod eval (gate implied comparison;
eval doesn't)
Spec amendments:
- Drop the "Gate baseline strategy" amendment (committed earlier
this session)
- Reframe the stacked-architecture amendment as a "decision" not a
"gate"; production path is unambiguous
- Reframe Section 4 "Validation gate: CfC must meet Mamba2" -> just
"Validation: per-horizon val AUC" with the >0.5 sanity floor
Doc cleanups: stale "Mamba2 gate baseline" mentions in build.rs and
pinned_mem.rs replaced with neutral wording. The Argo template
comment about "downstream gate consumption" becomes "for monitoring".
Test status: all 26+ ml-alpha tests pass. AUC tests (6/6) still pass
under the eval:: namespace.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -1,84 +1,21 @@
|
||||
# ml-alpha — Greenfield CfC + PPO Trading System Design
|
||||
|
||||
**Date**: 2026-05-16
|
||||
**Status**: Approved with **2026-05-16 stacked-architecture amendment** (see Section 2.5 below)
|
||||
**Status**: Approved with **2026-05-16 stacked-architecture decision** (see below)
|
||||
**Supersedes**: `2026-05-16-alpha-ppo-trainer.md` (the earlier DQN-port plan, abandoned per user direction to rebuild from first principles)
|
||||
|
||||
## 2026-05-16 Amendment: Mamba2 → CfC stacked perception (Option A)
|
||||
## 2026-05-16 Decision: Mamba2 → CfC stacked perception is the default
|
||||
|
||||
The original spec framed CfC as a *replacement* for Mamba2 at the perception trunk, with a hard gate requiring CfC AUC ≥ Mamba2 AUC at every horizon. Mid-execution of Plan 1, the architecture was revised to a **stacked design** where Mamba2 stays in the system as a frozen-or-jointly-trained sequence encoder feeding into CfC. The CfC layer learns per-cell time-constants over the SSM output.
|
||||
The original spec framed CfC as a *replacement* for Mamba2 at the perception trunk, with a competitive gate requiring CfC AUC ≥ Mamba2 AUC at every horizon. Mid-execution of Plan 1, the architecture was revised to a **stacked design** where Mamba2 acts as the sequence encoder feeding into CfC. The CfC layer learns per-cell time-constants over the SSM output.
|
||||
|
||||
**Why the change**: Phase 1d.3 already validated `Mamba2 → MLP stacker`. Replacing the MLP stacker with CfC strictly extends the validated path rather than swapping a known-good encoder for an unvalidated one. The gate now proves "the CfC layer is additive" rather than "CfC alone beats Mamba2 alone."
|
||||
**Why**: Phase 1d.3 already validated `Mamba2 → MLP stacker`. Replacing the MLP stacker with CfC strictly extends the validated path. The stacked model is now THE default production architecture — there's no competing-baseline gate (CfC vs Mamba2-alone was the old framing); validation is just normal training metrics (per-horizon val AUC, train loss curve, sanity-check baseline floor of >0.5 AUC).
|
||||
|
||||
**Affected sections**:
|
||||
- Section 2 — `Perception trunk` reads: Mamba2 SSM (input → 128-dim h_mamba) → CfC cell (128-dim h_mamba → 128-dim h_cfc) → multi-horizon heads + projection
|
||||
- Section 4 — Gate: `stacked AUC ≥ Mamba2-only AUC at every horizon` (with the Mamba2-only AUC measured from a comparable stacker-MLP architecture for fairness)
|
||||
- Section 4 — Validation: per-horizon val AUC monitored each epoch; no competitive comparison required
|
||||
- Section 7 — Param count: ~50K trainable (15K Mamba2 + 35K CfC stack)
|
||||
|
||||
**Fallback path (Option B — parallel + fused)**: if the stacked gate fails, fallback is dual-stream (Mamba2 + CfC read snapshots in parallel; outputs concat → projection → heads). Documented as a Plan-2 amendment if A's gate fails; not implemented in Plan 1.
|
||||
|
||||
The CfC kernels (`cfc_step.cu`), heads, projection, BCE, AdamW, and Graph A are unchanged. Only the trunk's forward path adds a Mamba2 prefix.
|
||||
|
||||
## 2026-05-16 Amendment: Gate baseline strategy
|
||||
|
||||
The stacked-architecture amendment moved the gate criterion from
|
||||
"CfC AUC ≥ Mamba2 AUC at every horizon" to
|
||||
"**stacked AUC ≥ Mamba2-only baseline AUC at every horizon**."
|
||||
This amendment defines what the Mamba2-only baseline actually is, since the field is no longer a separate model — it's an *ablation of the same trainer*.
|
||||
|
||||
### Why an ablation is the right baseline
|
||||
|
||||
Apples-to-apples — same data window, same hyperparameter pipeline, same code path. The only difference is whether the CfC layer is in the loop. That makes the verdict "did adding CfC help" unambiguous.
|
||||
|
||||
The alternative — training a separate "Mamba2 + MLP stacker" model like Phase 1d.3 — would re-introduce the disjoint-model comparison the stacked amendment already rejected (different code paths, different optimizers, different data preprocessing). Using prior session AUC numbers as a static reference is even weaker: different data, different training regime, no scientific basis for declaring delta>0.
|
||||
|
||||
### Three concrete ablation options
|
||||
|
||||
1. **`--bypass-cfc` flag.** Add a config flag on `PerceptionTrainer` that, when set, runs Mamba2 → heads directly (skips the CfC step entirely). Two binary args differ; everything else identical. Cleanest "is CfC additive?" test.
|
||||
|
||||
2. **`--mamba2-state-dim 2`**. Minimum allowed Mamba2 state (kernel requires ≥2). CfC stays in the loop but Mamba2's capacity is crippled. Tests "does richer Mamba2 help once CfC is present?" — useful but answers a different question.
|
||||
|
||||
3. **Frozen-CfC.** Initialize CfC with identity-ish weights (W_in≈I, W_rec≈0, tau→0 so decay→1) and freeze. Mamba2 + heads still train. Effectively "Mamba2 → pass-through → heads". Cleaner than (1) because it doesn't require a code path branch but harder to implement correctly.
|
||||
|
||||
### Recommendation
|
||||
|
||||
**Option 1** for v1. Add `--bypass-cfc` to `alpha_train` and a corresponding `bypass_cfc: bool` to `PerceptionTrainerConfig`. When set:
|
||||
- Forward: `Mamba2.forward_train(window) → h_enriched → heads → BCE`
|
||||
- Backward: `BCE → heads_backward → Mamba2.backward_from_h_enriched`
|
||||
- Skip both `cfc_step` and `cfc_step_backward`
|
||||
- Skip the 3 CfC AdamWs (W_in, W_rec, b); keep the 2 heads AdamWs (heads_w, heads_b) and the Mamba2AdamW.
|
||||
|
||||
Estimated effort: ~80 lines added to `perception.rs` (one branch in `step()`, one short config field, three optional optimizer instances). Same finite-diff validation applies (already covered by `backward_finite_diff.rs` for the kept pieces).
|
||||
|
||||
### Cluster submission protocol
|
||||
|
||||
```bash
|
||||
# Submit twice on the same SHA — same compile cache, same data, same seed.
|
||||
./scripts/argo-alpha-perception.sh --watch # stacked
|
||||
./scripts/argo-alpha-perception.sh --bypass-cfc --watch --seed 0x4242 # ablation
|
||||
# Both summaries land at /feature-cache/alpha-perception-runs/<sha>/
|
||||
# (the bypass run goes to a sibling subdir to avoid clobbering).
|
||||
alpha_gate \
|
||||
--cfc-summary /feature-cache/.../stacked/alpha_train_summary.json \
|
||||
--mamba2-summary /feature-cache/.../bypass/alpha_train_summary.json \
|
||||
--tolerance 0.01 \
|
||||
--out /feature-cache/.../phase_a_gate.json
|
||||
```
|
||||
|
||||
`--bypass-cfc` plumbing in the Argo template adds one workflow parameter and one flag conditional in the `train` step's bash. Both runs share the `ensure-binary` cache.
|
||||
|
||||
### Verdict semantics (unchanged from `gate_verdict`)
|
||||
|
||||
`gate_verdict` already implements the right logic: pass if `cfc_auc[h] ≥ mamba2_auc[h] − tolerance` at every horizon, fail otherwise with per-horizon delta + failing-horizons list. Tolerance default 0.01 stays. The "cfc" / "mamba2" naming in the verdict report becomes the "stacked" / "bypass" naming in this amendment; the gate logic doesn't care which side is which as long as both summaries have the same horizon array.
|
||||
|
||||
### Open work (next session, before cluster gate)
|
||||
|
||||
1. Add `bypass_cfc: bool` to `PerceptionTrainerConfig` + branching in `step()`
|
||||
2. Add `--bypass-cfc` CLI flag to `alpha_train`
|
||||
3. Add workflow parameter + bash conditional to `alpha-perception-template.yaml`
|
||||
4. Submit both runs, fetch summaries, run `alpha_gate`, commit `phase_a_gate.json` to artifacts
|
||||
|
||||
Total estimated effort: 1-2 hours focused, mostly code-mechanical.
|
||||
The CfC kernels (`cfc_step.cu`), heads, projection, BCE, AdamW, and Graph A are unchanged. Only the trunk's forward path adds the Mamba2 prefix.
|
||||
|
||||
## TL;DR
|
||||
|
||||
@@ -412,14 +349,14 @@ The existing `crates/trading_engine/src/brokers/interactive_brokers.rs` (1177 LO
|
||||
- Epochs: 5 over the training window
|
||||
- Time-constant init: log-uniform over [10ms, 1000s]
|
||||
|
||||
### Validation gate: CfC must meet Mamba2
|
||||
### Validation: per-horizon val AUC
|
||||
|
||||
After Phase A on the first 2Q (Q1-Q2 2024):
|
||||
- Measure val AUC at each of the 5 horizons on Q2 (held out from training)
|
||||
- Reference: Mamba2 stacker baseline AUC ≈ 0.66-0.71 at K=6000 from prior session runs
|
||||
- **Gate**: CfC AUC ≥ Mamba2 AUC at every horizon (within 0.01 tolerance). If not met, pause and investigate; do not proceed to Phase B.
|
||||
After perception pretrain on the training window:
|
||||
- Measure val AUC at each of the 5 horizons on the held-out window
|
||||
- Sanity floor: every horizon AUC > 0.50 (above chance). The model is broken if any falls below.
|
||||
- Reference: Phase 1d.3 stacker AUC was 0.66-0.71 at K=6000 on similar data; useful as a rough orientation but not a hard gate.
|
||||
|
||||
If the gate fails, the revert path is clean: the perception layer is the only component that depends on CfC. A revert to Mamba2 trunk (existing code, validated) leaves the policy, env, reward, online update, and runtime integration untouched.
|
||||
Per the 2026-05-16 stacked-architecture decision, perception is the default production path — there's no competing-baseline gate. Training continues to Phase B whenever the perception trainer converges cleanly (loss curve descending, val AUC > 0.50 everywhere). If perception fails to converge, the spec amendment doesn't have a planned alternative path — investigate the trainer.
|
||||
|
||||
### Phase B: PPO policy training on frozen perception
|
||||
|
||||
|
||||
Reference in New Issue
Block a user