Instructions to use Octopus1/PaGE with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use Octopus1/PaGE with Transformers:
# Load model directly from transformers import AutoModel model = AutoModel.from_pretrained("Octopus1/PaGE", device_map="auto") - Notebooks
- Google Colab
- Kaggle
Fix RoPE inv_freq + version-safe backbone reload in from_pretrained (compat transformers 5.x)
Browse files- 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 |
-
|
| 101 |
-
self.register_buffer("inv_freq",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
| 448 |
-
self.register_buffer("inv_freq",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
|
|
|
| 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 |
"""
|