feat(aux-supervision): per-epoch aux observability in alpha_train (B6.5)

Smoke 1 at HEAD 21e7dfd63 showed BCE bit-identical to baseline
(asymmetric stop-grad isolation working) but zero aux signal in logs.
Adds:

- Per-epoch tracing 'aux snapshot' line with aux_huber_h{10,100,1000},
  aux_dir_acc_h{10,100,1000}, stop_grad_aux_to_encoder
- AlphaTrainSummary JSON fields: final_aux_huber_ema_per_h,
  final_aux_dir_acc_ema_per_h, final_stop_grad_aux_to_encoder

Reads PerceptionTrainer's pub fields directly (no accessor noise).

After re-running Smoke 1 we'll have empirical evidence whether aux is
learning on REAL ES MBP-10 data (vs the synthetic constant-direction
test where dir_acc trivially hits 1.0). This informs B7's gate threshold
design.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
jgrusewski
2026-05-22 10:12:55 +02:00
parent 21e7dfd63c
commit 2e06297671

View File

@@ -239,6 +239,21 @@ struct AlphaTrainSummary {
/// `--smoothness-base-lambda` is unset (default).
#[serde(default)]
final_smooth_loss_per_horizon: [f32; N_HORIZONS],
/// Per-horizon Huber-loss EMA on the D-style aux supervision targets
/// (Layer B). Stays at 0.0 if aux supervision is disabled. Tracks
/// `PerceptionTrainer.aux_huber_ema_per_h` at the final epoch.
#[serde(default)]
final_aux_huber_ema_per_h: [f32; N_AUX_HORIZONS],
/// Per-horizon directional accuracy EMA on the D-label sign (Layer B).
/// > 0.85 with `aux_huber_ema < threshold` triggers the asymmetric
/// stop-grad lift (`stop_grad_aux_to_encoder = false`).
#[serde(default)]
final_aux_dir_acc_ema_per_h: [f32; N_AUX_HORIZONS],
/// Whether the asymmetric stop-grad was still active at the final epoch.
/// `true` means aux trunk gradient never flowed back into the encoder
/// (still "passive auditor" mode); `false` means the lift fired.
#[serde(default)]
final_stop_grad_aux_to_encoder: bool,
}
/// Linear warmup then cosine decay to `lr_min`. `step_idx` is the
@@ -636,6 +651,23 @@ fn main() -> Result<()> {
tracing::warn!(epoch, error = %e, "isv snapshot failed (continuing)");
}
}
// SDD-3 Layer B6.5: aux supervision metrics per epoch (Layer B).
// aux_huber_ema_per_h close to 0 = aux head learning; baseline
// (predicting per-horizon mean of D-labels) is ~0.5-1.0. dir_acc
// > 0.5 means aux predicts direction better than chance. Both
// > thresholds trigger the asymmetric stop-grad lift (E3 design).
tracing::info!(
epoch,
aux_huber_h10 = trainer.aux_huber_ema_per_h[0],
aux_huber_h100 = trainer.aux_huber_ema_per_h[1],
aux_huber_h1000 = trainer.aux_huber_ema_per_h[2],
aux_dir_acc_h10 = trainer.aux_dir_acc_ema_per_h[0],
aux_dir_acc_h100 = trainer.aux_dir_acc_ema_per_h[1],
aux_dir_acc_h1000 = trainer.aux_dir_acc_ema_per_h[2],
stop_grad_aux_to_encoder = trainer.stop_grad_aux_to_encoder,
"aux snapshot"
);
final_val_loss = val_avg;
final_val_auc = per_horizon_auc;
n_val_seqs_consumed = val_loader.yielded();
@@ -728,6 +760,9 @@ fn main() -> Result<()> {
early_stopped,
best_h1000_ckpt_path,
final_smooth_loss_per_horizon: trainer.last_smoothness_loss_per_horizon(),
final_aux_huber_ema_per_h: trainer.aux_huber_ema_per_h,
final_aux_dir_acc_ema_per_h: trainer.aux_dir_acc_ema_per_h,
final_stop_grad_aux_to_encoder: trainer.stop_grad_aux_to_encoder,
};
let summary_path = cli.out.join("alpha_train_summary.json");
std::fs::write(&summary_path, serde_json::to_vec_pretty(&summary).context("serialize summary")?)