KarlQuant commited on
Commit
9485efe
Β·
verified Β·
1 Parent(s): 95dbe1e

Upload Quasar_axrvi_ranker.py

Browse files
Files changed (1) hide show
  1. Quasar_axrvi_ranker.py +95 -1
Quasar_axrvi_ranker.py CHANGED
@@ -9847,6 +9847,69 @@ class HFSyncLayer:
9847
  # SECTION 18b β€” RANKER CHECKPOINT MANAGER
9848
  # ══════════════════════════════════════════════════════════════════════════════════════
9849
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9850
  class RankerCheckpointManager:
9851
  """
9852
  Bridge-facing checkpoint controller for QuasarAXRVIBridge.
@@ -10103,6 +10166,18 @@ class RankerCheckpointManager:
10103
  current_num_assets = bridge.axrvi_net.num_assets if bridge.axrvi_net is not None else -1
10104
  _model_compatible = True
10105
 
 
 
 
 
 
 
 
 
 
 
 
 
10106
  if ckpt_num_assets != -1 and ckpt_num_assets != current_num_assets:
10107
  ckpt_assets = ckpt.get("asset_symbols", "unknown")
10108
  logger.warning(
@@ -10131,7 +10206,26 @@ class RankerCheckpointManager:
10131
  tr = bridge.trainer
10132
  if _model_compatible:
10133
  if "optimizer" in ckpt:
10134
- tr.optimizer.load_state_dict(ckpt["optimizer"])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10135
  if "scheduler" in ckpt:
10136
  tr.scheduler.load_state_dict(ckpt["scheduler"])
10137
  else:
 
9847
  # SECTION 18b β€” RANKER CHECKPOINT MANAGER
9848
  # ══════════════════════════════════════════════════════════════════════════════════════
9849
 
9850
+ def _sanitize_optimizer_state_by_shape(optimizer, opt_state: dict) -> dict:
9851
+ """
9852
+ Drop any per-parameter Adam state (exp_avg / exp_avg_sq / etc.) whose
9853
+ stored tensor shape no longer matches the corresponding *live* param
9854
+ in `optimizer`.
9855
+
9856
+ Why this exists: relying on a saved 'num_assets' checkpoint field (or
9857
+ any other metadata flag) to detect a stale-optimizer situation is
9858
+ fragile β€” older checkpoints may not have that field at all, and even
9859
+ when shapes match at model-load time (e.g. a lazily-created submodule
9860
+ like FABLECLCU wasn't in the checkpoint dict yet, so it's just a
9861
+ "missing key" rather than a raise), the *optimizer* checkpoint can
9862
+ still hold a state entry at that same param slot sized for the old N.
9863
+ That mismatch only surfaces later, deep inside adamw()'s
9864
+ `exp_avg.lerp_(grad, ...)`, once the lazily-created param gets
9865
+ attached to the optimizer's existing (stale-shaped) state β€” by which
9866
+ point it's a hard crash on every training step rather than a clean,
9867
+ loggable restore-time skip.
9868
+
9869
+ Comparing shapes directly against the live optimizer's params is
9870
+ metadata-agnostic: it catches the mismatch regardless of why it
9871
+ happened, and only ever discards the specific entries that are
9872
+ actually incompatible β€” every other trained parameter's momentum
9873
+ keeps flowing through untouched.
9874
+ """
9875
+ if not opt_state or "state" not in opt_state:
9876
+ return opt_state
9877
+
9878
+ # Flatten in the same order load_state_dict expects: params in the
9879
+ # order they appear across param_groups, which is how the integer
9880
+ # keys in opt_state['state'] are indexed.
9881
+ live_params = [p for group in optimizer.param_groups for p in group["params"]]
9882
+
9883
+ state = opt_state["state"]
9884
+ dropped = []
9885
+ for idx in list(state.keys()):
9886
+ if idx >= len(live_params):
9887
+ # Optimizer has fewer params now than the checkpoint did
9888
+ # (e.g. a whole layer removed) β€” nothing to compare against.
9889
+ dropped.append(idx)
9890
+ del state[idx]
9891
+ continue
9892
+ live_shape = tuple(live_params[idx].shape)
9893
+ entry = state[idx]
9894
+ mismatched = any(
9895
+ hasattr(v, "shape") and tuple(v.shape) != live_shape
9896
+ for v in entry.values()
9897
+ if hasattr(v, "shape") and v.dim() > 0 # skip scalar 'step' counters
9898
+ )
9899
+ if mismatched:
9900
+ dropped.append(idx)
9901
+ del state[idx]
9902
+
9903
+ if dropped:
9904
+ logger.warning(
9905
+ f"[OptimizerRestore] Dropped {len(dropped)} stale optimizer state "
9906
+ f"entr{'y' if len(dropped) == 1 else 'ies'} with shape mismatches "
9907
+ f"against the current model (fresh Adam state will be used for "
9908
+ f"those params; every other param's momentum is preserved)."
9909
+ )
9910
+ return opt_state
9911
+
9912
+
9913
  class RankerCheckpointManager:
9914
  """
9915
  Bridge-facing checkpoint controller for QuasarAXRVIBridge.
 
10166
  current_num_assets = bridge.axrvi_net.num_assets if bridge.axrvi_net is not None else -1
10167
  _model_compatible = True
10168
 
10169
+ # NOTE: older checkpoints (e.g. ones restored from HF before
10170
+ # 'num_assets' was added to the save dict) don't carry this field,
10171
+ # so this check can silently miss a real mismatch and report
10172
+ # "compatible". That's fine here β€” axrvi_net.load_state_dict()
10173
+ # below is strict=False and a shape-mismatched tensor at a shared
10174
+ # key would raise immediately regardless of that flag, so it's
10175
+ # safe either way. The optimizer side is *not* self-protecting the
10176
+ # same way (stale state can attach to a lazily-created param
10177
+ # later without erroring at load time), which is why it gets its
10178
+ # own metadata-independent shape check below via
10179
+ # _sanitize_optimizer_state_by_shape() rather than trusting this
10180
+ # flag alone.
10181
  if ckpt_num_assets != -1 and ckpt_num_assets != current_num_assets:
10182
  ckpt_assets = ckpt.get("asset_symbols", "unknown")
10183
  logger.warning(
 
10206
  tr = bridge.trainer
10207
  if _model_compatible:
10208
  if "optimizer" in ckpt:
10209
+ # FIX 5: don't trust `_model_compatible` alone β€” it's
10210
+ # derived from a 'num_assets' checkpoint field that
10211
+ # older checkpoints (like ones restored from HF before
10212
+ # this field existed) simply don't have, silently
10213
+ # defaulting to "compatible". Validate optimizer state
10214
+ # against live param shapes directly as a second,
10215
+ # metadata-independent line of defense β€” see
10216
+ # _sanitize_optimizer_state_by_shape() for why this
10217
+ # can't be caught at axrvi_net.load_state_dict() time.
10218
+ try:
10219
+ safe_opt_state = _sanitize_optimizer_state_by_shape(
10220
+ tr.optimizer, ckpt["optimizer"]
10221
+ )
10222
+ tr.optimizer.load_state_dict(safe_opt_state)
10223
+ except Exception as e:
10224
+ logger.warning(
10225
+ f"[Restore] ⚠️ Optimizer state restore failed "
10226
+ f"({e}) β€” continuing with fresh optimizer state "
10227
+ f"rather than crashing the restore."
10228
+ )
10229
  if "scheduler" in ckpt:
10230
  tr.scheduler.load_state_dict(ckpt["scheduler"])
10231
  else: