Octopus1 commited on
Commit
63711b4
·
verified ·
1 Parent(s): 4fa7707

Fix RoPE inv_freq + version-safe backbone reload in from_pretrained (compat transformers 5.x)

Browse files
Files changed (1) hide show
  1. modeling_page.py +89 -6
modeling_page.py CHANGED
@@ -97,8 +97,15 @@ class Axial2dRotaryEmbedding(nn.Module):
97
  raise ValueError(f"`dim` must be a positive multiple of 4, got {dim}.")
98
  self.dim = dim
99
  self.axis_dim = dim // 2
100
- inv_freq = 1.0 / (base ** (torch.arange(0, self.axis_dim, 2, dtype=torch.float32) / self.axis_dim))
101
- self.register_buffer("inv_freq", inv_freq, persistent=False)
 
 
 
 
 
 
 
102
 
103
  def _axis_cos_sin(self, coords, *, device, dtype):
104
  inv_freq = self.inv_freq.to(device=device, dtype=torch.float32)
@@ -444,8 +451,14 @@ class Axial2dCrossRotaryEmbedding(nn.Module):
444
  raise ValueError(f"dim must be a positive multiple of 4, got {dim}.")
445
  self.dim = dim
446
  self.axis_dim = dim // 2
447
- inv_freq = 1.0 / (base ** (torch.arange(0, self.axis_dim, 2, dtype=torch.float32) / self.axis_dim))
448
- self.register_buffer("inv_freq", inv_freq, persistent=False)
 
 
 
 
 
 
449
 
450
  def _axis_cos_sin(self, coords, *, out_dtype):
451
  if coords.ndim != 2:
@@ -703,11 +716,13 @@ class PaGEBackbone(nn.Module):
703
 
704
  @staticmethod
705
  def _dinov3_has_nested_layer(dinov3_module) -> bool:
706
- """True if this transformers version nests the layer stack under an inner `.model`."""
 
707
  inner = getattr(dinov3_module, "model", None)
708
  if not isinstance(inner, nn.Module):
709
  return False
710
- return any(k.startswith("model.layer.") for k in inner.state_dict().keys())
 
711
 
712
  def _remap_dinov3_keys(self, state_dict, prefix, *args, **kwargs):
713
  """Normalize DINOv3 backbone keys (embeddings / layer / norm / rope_embeddings)
@@ -878,6 +893,74 @@ class PaGEPreTrainedModel(PreTrainedModel):
878
  nn.init.zeros_(module.bias)
879
  nn.init.ones_(module.weight)
880
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
881
 
882
  class PaGEModel(PaGEPreTrainedModel):
883
  """
 
97
  raise ValueError(f"`dim` must be a positive multiple of 4, got {dim}.")
98
  self.dim = dim
99
  self.axis_dim = dim // 2
100
+ self.base = base
101
+ self.register_buffer("inv_freq", self._compute_inv_freq(), persistent=False)
102
+
103
+ def _compute_inv_freq(self):
104
+ return 1.0 / (self.base ** (torch.arange(0, self.axis_dim, 2, dtype=torch.float32) / self.axis_dim))
105
+
106
+ def reset_inv_freq(self):
107
+ """Recompute inv_freq (a non-persistent buffer that meta-init in from_pretrained can corrupt)."""
108
+ self.inv_freq = self._compute_inv_freq()
109
 
110
  def _axis_cos_sin(self, coords, *, device, dtype):
111
  inv_freq = self.inv_freq.to(device=device, dtype=torch.float32)
 
451
  raise ValueError(f"dim must be a positive multiple of 4, got {dim}.")
452
  self.dim = dim
453
  self.axis_dim = dim // 2
454
+ self.base = base
455
+ self.register_buffer("inv_freq", self._compute_inv_freq(), persistent=False)
456
+
457
+ def _compute_inv_freq(self):
458
+ return 1.0 / (self.base ** (torch.arange(0, self.axis_dim, 2, dtype=torch.float32) / self.axis_dim))
459
+
460
+ def reset_inv_freq(self):
461
+ self.inv_freq = self._compute_inv_freq()
462
 
463
  def _axis_cos_sin(self, coords, *, out_dtype):
464
  if coords.ndim != 2:
 
716
 
717
  @staticmethod
718
  def _dinov3_has_nested_layer(dinov3_module) -> bool:
719
+ """True if this transformers version nests the layer stack under an inner `.model`
720
+ (transformers >= 5.x). In 4.56.x the layers are flattened onto the DINOv3ViTModel itself."""
721
  inner = getattr(dinov3_module, "model", None)
722
  if not isinstance(inner, nn.Module):
723
  return False
724
+ # inner's own keys are relative to it: "layer.0.*" when nested, never "embeddings.*".
725
+ return any(k.startswith("layer.") for k in inner.state_dict().keys())
726
 
727
  def _remap_dinov3_keys(self, state_dict, prefix, *args, **kwargs):
728
  """Normalize DINOv3 backbone keys (embeddings / layer / norm / rope_embeddings)
 
893
  nn.init.zeros_(module.bias)
894
  nn.init.ones_(module.weight)
895
 
896
+ # ------------------------------------------------------------------ #
897
+ # Version-safe loading #
898
+ # ------------------------------------------------------------------ #
899
+ # The DINOv3 backbones are built from transformers' built-in DINOv3ViTModel, whose
900
+ # internal parameter naming changed between transformers 4.56.x (layers flattened:
901
+ # `model.layer.N`) and 5.x (layers nested: `model.model.layer.N`). The checkpoints store
902
+ # one convention; loading under the other leaves the backbone randomly initialized.
903
+ # `from_pretrained` in transformers >= 5 bypasses `nn.Module._load_state_dict_pre_hook`,
904
+ # so the remap hook on PaGEBackbone is not invoked by it. We therefore reload the backbone
905
+ # weights ourselves through `nn.Module.load_state_dict` (which *does* fire the hook).
906
+ @staticmethod
907
+ def _collect_safetensors(path_or_repo, **kwargs):
908
+ """Return the full state_dict from a local dir or a HF repo id."""
909
+ import os as _os
910
+ import glob as _glob
911
+ from safetensors.torch import load_file as _load_file
912
+ state = {}
913
+ if _os.path.isdir(path_or_repo):
914
+ index = _os.path.join(path_or_repo, "model.safetensors.index.json")
915
+ if _os.path.isfile(index):
916
+ import json as _json
917
+ wm = _json.load(open(index))["weight_map"]
918
+ files = sorted(set(wm.values()))
919
+ else:
920
+ files = ["model.safetensors"]
921
+ for f in files:
922
+ state.update(_load_file(_os.path.join(path_or_repo, f)))
923
+ else:
924
+ from huggingface_hub import hf_hub_download
925
+ import json as _json
926
+ repo_id = path_or_repo
927
+ try:
928
+ idx_path = hf_hub_download(repo_id=repo_id, filename="model.safetensors.index.json")
929
+ wm = _json.load(open(idx_path))["weight_map"]
930
+ files = sorted(set(wm.values()))
931
+ except Exception:
932
+ files = ["model.safetensors"]
933
+ for f in files:
934
+ p = hf_hub_download(repo_id=repo_id, filename=f)
935
+ state.update(_load_file(p))
936
+ return state
937
+
938
+ @classmethod
939
+ def from_pretrained(cls, *args, **kwargs):
940
+ model = super().from_pretrained(*args, **kwargs)
941
+ # Reload DINOv3 backbone weights version-safely (the remap hook fires here).
942
+ try:
943
+ path_or_repo = args[0] if args else kwargs.get("pretrained_model_name_or_path")
944
+ full_state = cls._collect_safetensors(path_or_repo)
945
+ for branch in ("scene_branch_backbone", "head_branch_backbone"):
946
+ if not hasattr(model, branch):
947
+ continue
948
+ bb = getattr(model, branch)
949
+ bb_state = {k[len(branch) + 1:]: v for k, v in full_state.items()
950
+ if k.startswith(branch + ".")}
951
+ if bb_state:
952
+ bb.load_state_dict(bb_state, strict=False)
953
+ # Recompute RoPE inv_freq buffers: they are non-persistent and transformers' meta-init
954
+ # during from_pretrained leaves them as garbage, which would corrupt axial RoPE.
955
+ for m in model.modules():
956
+ if hasattr(m, "reset_inv_freq") and callable(m.reset_inv_freq):
957
+ m.reset_inv_freq()
958
+ except Exception as e: # pragma: no cover
959
+ import warnings
960
+ warnings.warn(f"PaGE: version-safe backbone reload skipped ({e!r}). "
961
+ "Backbone weights may be random if your transformers version mismatches the checkpoint.")
962
+ return model
963
+
964
 
965
  class PaGEModel(PaGEPreTrainedModel):
966
  """