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:
@@ -8,7 +8,7 @@ use std::path::{Path, PathBuf};
|
||||
use std::process::Command;
|
||||
|
||||
const KERNELS: &[&str] = &[
|
||||
"mamba2_alpha_kernel", // gate reference (Phase A->Mamba2 baseline)
|
||||
"mamba2_alpha_kernel", // Mamba2 SSM scan kernel (used by PerceptionTrainer's encoder prefix)
|
||||
"snap_feature_assemble",
|
||||
"cfc_step",
|
||||
"multi_horizon_heads",
|
||||
|
||||
@@ -1,94 +0,0 @@
|
||||
//! Validation gate runner — reads two `alpha_train_summary.json`
|
||||
//! artifacts (one CfC, one Mamba2 baseline), emits `phase_a_gate.json`,
|
||||
//! exits 0 on PASS, 1 on FAIL.
|
||||
//!
|
||||
//! Both summaries must be generated against the SAME data window
|
||||
//! (train + val quarters identical) for a meaningful comparison.
|
||||
//! Run via:
|
||||
//! alpha_gate \
|
||||
//! --cfc-summary artifacts/cfc/alpha_train_summary.json \
|
||||
//! --mamba2-summary artifacts/mamba2/alpha_train_summary.json \
|
||||
//! --tolerance 0.01 \
|
||||
//! --out artifacts/phase_a_gate.json
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use clap::Parser;
|
||||
use ml_alpha::gate::cfc_vs_mamba2::{
|
||||
gate_verdict, load_summary, GateReport, GateVerdict, HORIZONS,
|
||||
};
|
||||
use serde::Serialize;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(name = "alpha_gate")]
|
||||
struct Cli {
|
||||
#[arg(long)]
|
||||
cfc_summary: PathBuf,
|
||||
|
||||
#[arg(long)]
|
||||
mamba2_summary: PathBuf,
|
||||
|
||||
#[arg(long, default_value_t = 0.01)]
|
||||
tolerance: f32,
|
||||
|
||||
#[arg(long)]
|
||||
out: PathBuf,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct GateOutput {
|
||||
report: GateReport,
|
||||
verdict: GateVerdict,
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
tracing_subscriber::fmt::init();
|
||||
let cli = Cli::parse();
|
||||
|
||||
let cfc = load_summary(&cli.cfc_summary).context("load CfC summary")?;
|
||||
let mamba2 = load_summary(&cli.mamba2_summary).context("load Mamba2 summary")?;
|
||||
|
||||
anyhow::ensure!(
|
||||
cfc.horizons == mamba2.horizons,
|
||||
"horizon mismatch: CfC={:?}, Mamba2={:?}",
|
||||
cfc.horizons,
|
||||
mamba2.horizons
|
||||
);
|
||||
|
||||
let report = GateReport {
|
||||
horizons: HORIZONS,
|
||||
cfc_auc: cfc.final_val_auc,
|
||||
mamba2_auc: mamba2.final_val_auc,
|
||||
tolerance: cli.tolerance,
|
||||
};
|
||||
let verdict = gate_verdict(&report);
|
||||
|
||||
if let Some(parent) = cli.out.parent() {
|
||||
std::fs::create_dir_all(parent).with_context(|| format!("mkdir {}", parent.display()))?;
|
||||
}
|
||||
let output = GateOutput { report: report.clone(), verdict: verdict.clone() };
|
||||
std::fs::write(
|
||||
&cli.out,
|
||||
serde_json::to_vec_pretty(&output).context("serialize gate output")?,
|
||||
)
|
||||
.with_context(|| format!("write {}", cli.out.display()))?;
|
||||
|
||||
if verdict.pass {
|
||||
tracing::info!(
|
||||
cfc_auc = ?report.cfc_auc,
|
||||
mamba2_auc = ?report.mamba2_auc,
|
||||
deltas = ?verdict.deltas,
|
||||
"CfC-vs-Mamba2 gate: PASS"
|
||||
);
|
||||
Ok(())
|
||||
} else {
|
||||
tracing::error!(
|
||||
failing_horizons = ?verdict.failing_horizons,
|
||||
cfc_auc = ?report.cfc_auc,
|
||||
mamba2_auc = ?report.mamba2_auc,
|
||||
deltas = ?verdict.deltas,
|
||||
"CfC-vs-Mamba2 gate: FAIL"
|
||||
);
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,7 @@
|
||||
use anyhow::{Context, Result};
|
||||
use clap::Parser;
|
||||
use ml_alpha::data::loader::{MultiHorizonLoader, MultiHorizonLoaderConfig};
|
||||
use ml_alpha::gate::auc::{compute_auc, AucInput};
|
||||
use ml_alpha::eval::auc::{compute_auc, AucInput};
|
||||
use ml_alpha::heads::N_HORIZONS;
|
||||
use ml_alpha::trainer::perception::{PerceptionTrainer, PerceptionTrainerConfig};
|
||||
use ml_core::device::MlDevice;
|
||||
|
||||
7
crates/ml-alpha/src/eval/mod.rs
Normal file
7
crates/ml-alpha/src/eval/mod.rs
Normal file
@@ -0,0 +1,7 @@
|
||||
//! Evaluation utilities for the perception trainer.
|
||||
//!
|
||||
//! Per-horizon AUC (Mann-Whitney U) is the primary signal-quality
|
||||
//! metric. Computed slow-path on (probs, labels) pairs accumulated
|
||||
//! during the validation pass.
|
||||
|
||||
pub mod auc;
|
||||
@@ -1,143 +0,0 @@
|
||||
//! CfC-vs-Mamba2 validation gate logic.
|
||||
//!
|
||||
//! Per spec Section 4 (with 2026-05-16 stacked amendment):
|
||||
//! "stacked AUC ≥ Mamba2-only baseline AUC at every horizon"
|
||||
//!
|
||||
//! This module owns the verdict semantics. The binary
|
||||
//! `examples/alpha_gate.rs` orchestrates running both trainers and
|
||||
//! consuming this module's verdict.
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::path::Path;
|
||||
|
||||
pub const HORIZONS: [usize; 5] = [30, 100, 300, 1000, 6000];
|
||||
|
||||
/// Summary emitted by `alpha_train` (one per backbone).
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct AlphaTrainSummary {
|
||||
pub epochs: usize,
|
||||
pub n_hid: usize,
|
||||
pub seq_len: usize,
|
||||
pub horizons: [usize; 5],
|
||||
pub final_train_loss: f32,
|
||||
pub final_val_loss: f32,
|
||||
pub final_val_auc: [f32; 5],
|
||||
pub n_train_seqs_consumed: usize,
|
||||
pub n_val_seqs_consumed: usize,
|
||||
}
|
||||
|
||||
pub fn load_summary(path: &Path) -> Result<AlphaTrainSummary> {
|
||||
let bytes = std::fs::read(path).with_context(|| format!("read {}", path.display()))?;
|
||||
serde_json::from_slice(&bytes).with_context(|| format!("parse {}", path.display()))
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct GateReport {
|
||||
pub horizons: [usize; 5],
|
||||
pub cfc_auc: [f32; 5],
|
||||
pub mamba2_auc: [f32; 5],
|
||||
pub tolerance: f32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct GateVerdict {
|
||||
pub pass: bool,
|
||||
pub failing_horizons: Vec<usize>,
|
||||
pub deltas: [f32; 5],
|
||||
}
|
||||
|
||||
impl GateVerdict {
|
||||
pub fn is_pass(&self) -> bool { self.pass }
|
||||
pub fn failing_horizons(&self) -> Vec<usize> { self.failing_horizons.clone() }
|
||||
}
|
||||
|
||||
/// Verdict: CfC AUC must be ≥ Mamba2 AUC − tolerance at every horizon.
|
||||
pub fn gate_verdict(r: &GateReport) -> GateVerdict {
|
||||
let mut deltas = [0f32; 5];
|
||||
let mut failing = Vec::new();
|
||||
let mut pass = true;
|
||||
for i in 0..5 {
|
||||
let delta = r.cfc_auc[i] - r.mamba2_auc[i];
|
||||
deltas[i] = delta;
|
||||
if delta < -r.tolerance {
|
||||
pass = false;
|
||||
failing.push(r.horizons[i]);
|
||||
}
|
||||
}
|
||||
GateVerdict { pass, failing_horizons: failing, deltas }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn gate_passes_when_cfc_meets_or_exceeds_mamba2() {
|
||||
let report = GateReport {
|
||||
horizons: HORIZONS,
|
||||
cfc_auc: [0.59, 0.62, 0.65, 0.68, 0.71],
|
||||
mamba2_auc: [0.58, 0.61, 0.64, 0.67, 0.70],
|
||||
tolerance: 0.01,
|
||||
};
|
||||
let v = gate_verdict(&report);
|
||||
assert!(v.is_pass());
|
||||
assert!(v.failing_horizons().is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gate_passes_within_tolerance() {
|
||||
// CfC 0.005 below Mamba2 at every horizon, tolerance 0.01 -> PASS.
|
||||
let report = GateReport {
|
||||
horizons: HORIZONS,
|
||||
cfc_auc: [0.575, 0.605, 0.635, 0.665, 0.695],
|
||||
mamba2_auc: [0.58, 0.61, 0.64, 0.67, 0.70],
|
||||
tolerance: 0.01,
|
||||
};
|
||||
let v = gate_verdict(&report);
|
||||
assert!(v.is_pass(), "CfC within tolerance should PASS");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gate_fails_when_cfc_lags_at_long_horizon() {
|
||||
let report = GateReport {
|
||||
horizons: HORIZONS,
|
||||
cfc_auc: [0.59, 0.62, 0.65, 0.62, 0.63],
|
||||
mamba2_auc: [0.58, 0.61, 0.64, 0.67, 0.70],
|
||||
tolerance: 0.01,
|
||||
};
|
||||
let v = gate_verdict(&report);
|
||||
assert!(!v.is_pass());
|
||||
assert_eq!(v.failing_horizons(), vec![1000, 6000]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gate_fails_when_cfc_lags_at_short_horizon() {
|
||||
let report = GateReport {
|
||||
horizons: HORIZONS,
|
||||
cfc_auc: [0.50, 0.62, 0.65, 0.68, 0.71],
|
||||
mamba2_auc: [0.58, 0.61, 0.64, 0.67, 0.70],
|
||||
tolerance: 0.01,
|
||||
};
|
||||
let v = gate_verdict(&report);
|
||||
assert!(!v.is_pass());
|
||||
assert_eq!(v.failing_horizons(), vec![30]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deltas_sign_tracks_relative_perf() {
|
||||
let report = GateReport {
|
||||
horizons: HORIZONS,
|
||||
cfc_auc: [0.70, 0.50, 0.65, 0.60, 0.80],
|
||||
mamba2_auc: [0.60, 0.60, 0.65, 0.70, 0.70],
|
||||
tolerance: 0.01,
|
||||
};
|
||||
let v = gate_verdict(&report);
|
||||
// CfC ahead at h=30, 6000; behind at 100, 1000; tied at 300
|
||||
assert!(v.deltas[0] > 0.0);
|
||||
assert!(v.deltas[1] < 0.0);
|
||||
assert!((v.deltas[2]).abs() < 1e-6);
|
||||
assert!(v.deltas[3] < 0.0);
|
||||
assert!(v.deltas[4] > 0.0);
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
//! Validation gate — CfC vs Mamba2 multi-horizon AUC comparison.
|
||||
//!
|
||||
//! Per the design spec Section 4 gate criterion: CfC AUC ≥ Mamba2 AUC
|
||||
//! at every horizon (with the 2026-05-16 stacked amendment shifting the
|
||||
//! framing to "stacked AUC ≥ Mamba2-only baseline AUC").
|
||||
|
||||
pub mod auc;
|
||||
pub mod cfc_vs_mamba2;
|
||||
@@ -27,7 +27,7 @@
|
||||
// Task 16+: pub mod gate;
|
||||
pub mod cfc;
|
||||
pub mod data;
|
||||
pub mod gate;
|
||||
pub mod eval;
|
||||
pub mod heads;
|
||||
pub mod isv;
|
||||
pub mod pinned;
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
//! `feedback_no_htod_htoh_only_mapped_pinned.md`. Mirror of
|
||||
//! `crates/ml/src/cuda_pipeline/mapped_pinned.rs::MappedF32Buffer` —
|
||||
//! duplicated here because adding `ml-alpha -> ml` would cycle (ml
|
||||
//! depends on ml-alpha for the Mamba2 gate baseline). Move both to
|
||||
//! `ml-core::mapped_pinned` to deduplicate (out of scope for Phase A).
|
||||
//! depends on ml-alpha for `mamba2_block`). Move both to
|
||||
//! `ml-core::mapped_pinned` to deduplicate (deferred).
|
||||
|
||||
#![allow(unsafe_code)]
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
#
|
||||
# Stacked Mamba2 -> CfC -> heads supervised pretrain on MBP-10 from the
|
||||
# training-data PVC. Emits `alpha_train_summary.json` to MinIO-backed
|
||||
# feature-cache PVC with per-horizon val AUC for downstream gate
|
||||
# consumption. Runs on a single L40S; ~30-90 min wall depending on
|
||||
# feature-cache PVC with per-horizon val AUC for monitoring. Runs on
|
||||
# a single L40S; ~30-90 min wall depending on
|
||||
# n-train-seqs.
|
||||
#
|
||||
# DAG:
|
||||
|
||||
Reference in New Issue
Block a user