feat(ml-alpha): CfcTrunk save/load checkpoint + --checkpoint CLI (C14)
CfcTrunk::save_checkpoint(path) reads each device weight tensor back
via memcpy_dtoh and bincode-serialises into a CheckpointV1 envelope:
{ version, n_in, n_hid,
w_in, w_rec, b, tau,
heads_w, heads_b,
proj_w, proj_b, proj_g, proj_n }
Total ~22k-25k f32 = ~90 KB per trunk. Tiny.
CfcTrunk::load_checkpoint(dev, cfg, path) deserialises + validates
(version == 1, n_in/n_hid match the supplied CfcConfig — a model
trained for one arch can't silently load against another). Constructs
a fresh trunk via new_random (for kernel bindings + scratch buffers)
then overwrites every weight tensor via memcpy_htod. The random init
values are thrown away — marginally wasteful, but keeps the
construction code paths unified.
Roundtrip test (--ignored, CUDA-required): save trunk_A → load → read
back every device tensor and assert bit-equality between trunk_A and
the loaded trunk_B. Passed locally. Dim-mismatch rejection test runs
without CUDA (verifies bincode envelope serialise/deserialise).
bin/fxt-backtest --checkpoint <path>: when set, overrides --seed and
loads from disk. When absent, warns loudly that the trunk is
random-initialised and backtest results are noise. This makes the
binary genuinely useful as a deployment tool — point it at a trained
checkpoint and run real backtests.
Adds bincode workspace dep to ml-alpha (was already in workspace
dependencies, just not in ml-alpha's [dependencies] block). serde
features bumped to ["derive"] (was using workspace default which
omits derive macros).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -75,10 +75,15 @@ struct RunArgs {
|
||||
/// Cap on events processed; 0 = exhaust the input stream.
|
||||
#[arg(long, default_value_t = 0)]
|
||||
max_events: u64,
|
||||
/// Random seed for v1 trunk initialisation (a checkpoint loader is
|
||||
/// follow-up work — see harness.rs::new doc).
|
||||
/// Random seed for trunk initialisation when --checkpoint is not given.
|
||||
#[arg(long, default_value_t = 0xC0FFEE)]
|
||||
seed: u64,
|
||||
/// Path to a CfcTrunk bincode checkpoint (see ml-alpha
|
||||
/// CfcTrunk::save_checkpoint). When set, overrides --seed and the
|
||||
/// trunk runs with trained weights. Without it the trunk is
|
||||
/// random-initialised — useful for plumbing tests, not real backtests.
|
||||
#[arg(long)]
|
||||
checkpoint: Option<PathBuf>,
|
||||
/// Output directory; per-cell artifacts written to <out>/cell_NNNN/.
|
||||
#[arg(long)]
|
||||
out: PathBuf,
|
||||
@@ -140,8 +145,18 @@ fn run(args: RunArgs) -> Result<()> {
|
||||
};
|
||||
|
||||
let dev = MlDevice::cuda(0).map_err(|e| anyhow::anyhow!("MlDevice::cuda(0): {e}"))?;
|
||||
let trunk = CfcTrunk::new_random(&dev, &CfcConfig::default(), args.seed)
|
||||
.context("CfcTrunk::new_random")?;
|
||||
let trunk = if let Some(ckpt_path) = &args.checkpoint {
|
||||
tracing::info!(checkpoint = %ckpt_path.display(), "loading CfcTrunk checkpoint");
|
||||
CfcTrunk::load_checkpoint(&dev, &CfcConfig::default(), ckpt_path)
|
||||
.with_context(|| format!("load checkpoint {}", ckpt_path.display()))?
|
||||
} else {
|
||||
tracing::warn!(
|
||||
seed = args.seed,
|
||||
"no --checkpoint provided; using new_random trunk (random init — backtest results are NOISE)"
|
||||
);
|
||||
CfcTrunk::new_random(&dev, &CfcConfig::default(), args.seed)
|
||||
.context("CfcTrunk::new_random")?
|
||||
};
|
||||
|
||||
let cfg = BacktestHarnessConfig {
|
||||
data_root: args.data.clone(),
|
||||
|
||||
Reference in New Issue
Block a user