Skip to content

fix(hpo): sign-safe penalties, head_layer_size_length handling, honor random_state - #431

Open
ChrisW09 wants to merge 2 commits into
mainfrom
fix/hpo-driver
Open

fix(hpo): sign-safe penalties, head_layer_size_length handling, honor random_state#431
ChrisW09 wants to merge 2 commits into
mainfrom
fix/hpo-driver

Conversation

@ChrisW09

Copy link
Copy Markdown
Collaborator

Fixes #420

Problems

  1. A crashed trial can win the search. The exception path returned best_val_loss * 100. When the best loss is negative — routine for LSS models, whose val_loss is a mean NLL — that is an extremely good objective, so gp_minimize could return the crashing configuration as result.x.
  2. Pruning threshold inverts for negative losses. best_epoch_val_loss * 1.5 sits below a negative best loss, so every subsequent trial exceeded the threshold at prune_epoch and was pruned, gutting the search.
  3. head_layer_size_length corrupts head_layer_sizes. The trial loop special-cases the key first, but the final apply loop only checked key.startswith("head_layer_size_") — which the length key matches. The length (1–5) went through round_to_nearest_160, was prepended as a layer size, and the list was never truncated: [0, s1, ..., s5], a config no trial ever evaluated.
  4. HPO ignored the estimator seed (random_state=42 hardcoded).
  5. Sanity-check validation polluted val_losses, shifting epoch_val_loss_at() — the pruning baseline — by one epoch. Verified: a 2-epoch fit produced 3 entries.

Fix

Sign-safe arithmetic for both the penalty and the threshold, an explicit head_layer_size_length branch plus truncation in the apply loop, random_state plumbed into gp_minimize, and a trainer.sanity_checking guard in on_validation_epoch_end.

Tests

New tests/test_hpo_pruning.py: the penalty/threshold expressions stay worse than the incumbent across negative, zero and positive losses; the startswith collision and round_to_nearest_16(1..5) == 0 are pinned as the reason the length branch is needed; and a fit asserts len(val_losses) == 2 after 2 epochs.

🤖 Generated with Claude Code

ChrisW09 and others added 2 commits July 27, 2026 21:25
… random_state

- The crashed-trial penalty best_val_loss*100 rewarded crashes whenever
  the best loss was negative (routine for LSS NLL), so gp_minimize could
  return a configuration that failed to fit as the best hyperparameters.
  It is now best + 100*abs(best) + 1.
- The pruning threshold best*1.5 fell *below* a negative best loss,
  pruning every subsequent trial at prune_epoch; now best + 0.5*abs(best).
- The final best-params loop lacked the head_layer_size_length branch the
  trial loop has, and 'head_layer_size_length'.startswith('head_layer_size_')
  is True: the optimized length was rounded to 0, prepended as a layer
  size, and the list was never truncated to that length.
- gp_minimize now uses the estimator's random_state when set instead of a
  hardcoded 42.
- on_validation_epoch_end no longer records the pre-training sanity-check
  loss, which shifted epoch_val_loss_at() -- the pruning baseline -- by
  one epoch.

Fixes #420

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
model._task_model is typed ITaskModel | None, which has no .val_losses.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] HPO driver: crashed trials can win the search, head_layer_sizes gets corrupted, best params are never refit

1 participant