immanuelpeter's picture
Upload vision tower bundle
f9bbdff verified
Raw History Blame Contribute Delete
3.58 kB
"""Load the immanuelpeter/C-RADIOv4-H-3.5-Super vision bundle.
No language model weights are requested.
"""
import json
from pathlib import Path
import torch
from safetensors.torch import load_file
from torch import nn
from transformers import AutoConfig
from transformers.dynamic_module_utils import get_class_from_dynamic_module
class RMSNorm(nn.Module):
def __init__(self, width):
super().__init__()
self.weight = nn.Parameter(torch.ones(width))
def forward(self, x):
value = x.float()
value = value * torch.rsqrt(value.square().mean(-1, keepdim=True) + 1e-5)
return (self.weight.float() * value).to(x.dtype)
class SquaredReLU(nn.Module):
def forward(self, x):
return torch.relu(x).square()
def pixel_shuffle(x):
n, h, w, c = x.shape
x = x.reshape(n, h, w // 2, c * 2).permute(0, 2, 1, 3).contiguous()
return x.reshape(n, w // 2, h // 2, c * 4).permute(0, 2, 1, 3).contiguous()
class Vision(nn.Module):
def __init__(self, bundle, dtype=torch.bfloat16):
super().__init__()
bundle = Path(bundle)
self.settings = json.loads((bundle / "config.json").read_text())
config = AutoConfig.from_pretrained(
self.settings["code_config_source"], revision=self.settings["code_config_revision"], trust_remote_code=True,
)
radio_cls = get_class_from_dynamic_module(
"hf_model.RADIOModel", "nvidia/C-RADIOv4-H", revision=self.settings["radio_code_revision"],
)
self.tower = radio_cls(config.vision_config)
generator = self.tower.radio_model.model.patch_generator
generator.video_embedder = nn.Linear(2 * 3 * 16 * 16, 1280, bias=False)
weights = load_file(bundle / "model.safetensors")
self.tower.radio_model.summary_idxs = weights.pop("summary_idxs")
weights["radio_model.summary_idxs"] = self.tower.radio_model.summary_idxs
self.tower.load_state_dict(weights, strict=True)
self.tower.to(dtype)
conditioner = self.tower.radio_model.input_conditioner
conditioner.float()
conditioner.load_state_dict({
name: weights[f"radio_model.input_conditioner.{name}"]
for name in ("norm_mean", "norm_std")
}, strict=True)
conditioner.dtype = dtype
self.final_norm = nn.LayerNorm(1280, eps=1e-6) if self.settings["vision_final_layernorm"] else nn.Identity()
if self.settings["vision_final_layernorm"]:
self.final_norm.load_state_dict(load_file(bundle / "final_norm.safetensors"), strict=True)
self.final_norm.to(dtype)
self.projector = nn.Sequential(
RMSNorm(5120), nn.Linear(5120, 20480, bias=False),
SquaredReLU(), nn.Linear(20480, 4096, bias=False),
)
self.projector.load_state_dict(load_file(bundle / "projector.safetensors"), strict=True)
self.projector.to(dtype)
self.eval()
@torch.inference_mode()
def forward(self, pixel_values):
"""Accept RGB float32 pixels in [0, 1] on a grid divisible by 32."""
h, w = pixel_values.shape[-2:]
if h % 32 or w % 32:
raise ValueError("pixel dimensions must be divisible by 32")
tower = self.tower(pixel_values.float()).features
merged = pixel_shuffle(self.final_norm(tower).reshape(len(tower), h // 16, w // 16, 1280)).flatten(1, 2)
return {"tower": tower, "merged": merged, "projected": self.projector(merged)}
def load_vision(bundle, dtype=torch.bfloat16):
return Vision(bundle, dtype=dtype)