Spaces:
Running on Zero
Running on Zero
Leaner idol package (inference code only)
Browse files- idol/conditioning.py +3 -16
- idol/gaussian.py +37 -369
- idol/models/birefnet.py +0 -43
- idol/models/decoder.py +174 -406
- idol/models/dit.py +62 -368
- idol/models/layers.py +318 -0
- idol/models/transformer_base.py +0 -351
- idol/modules/__init__.py +0 -0
- idol/modules/attention/__init__.py +0 -14
- idol/modules/attention/full_attn.py +0 -127
- idol/modules/attention/modules.py +0 -338
- idol/modules/mlp.py +0 -16
- idol/modules/norm.py +0 -29
- idol/modules/transformer/__init__.py +0 -2
- idol/modules/transformer/blocks.py +0 -177
- idol/modules/transformer/modulated.py +0 -381
- idol/modules/utils.py +0 -50
- idol/pipeline.py +25 -53
- idol/preprocess.py +3 -6
- idol/sampler.py +19 -29
- idol/sampling_utils.py +0 -150
idol/conditioning.py
CHANGED
|
@@ -13,12 +13,11 @@ DTYPES = {'fp32': torch.float32, 'bf16': torch.bfloat16, 'fp16': torch.float16}
|
|
| 13 |
|
| 14 |
|
| 15 |
class ImageEncoder:
|
| 16 |
-
def __init__(self, dinov3_repo: str, flux2_vae_repo: str,
|
| 17 |
dinov3_dtype: str = 'bf16', device: str = 'cuda'):
|
| 18 |
from diffusers.models import AutoencoderKLFlux2
|
| 19 |
from transformers import AutoModel
|
| 20 |
|
| 21 |
-
self.resolution = resolution
|
| 22 |
self.device = torch.device(device)
|
| 23 |
self.dinov3 = AutoModel.from_pretrained(dinov3_repo, torch_dtype=DTYPES[dinov3_dtype])
|
| 24 |
self.dinov3 = self.dinov3.eval().requires_grad_(False).to(self.device)
|
|
@@ -26,18 +25,6 @@ class ImageEncoder:
|
|
| 26 |
torch_dtype=torch.bfloat16)
|
| 27 |
self.flux2_vae = self.flux2_vae.eval().requires_grad_(False).to(self.device)
|
| 28 |
|
| 29 |
-
def to(self, device):
|
| 30 |
-
self.device = torch.device(device)
|
| 31 |
-
self.dinov3.to(self.device)
|
| 32 |
-
self.flux2_vae.to(self.device)
|
| 33 |
-
return self
|
| 34 |
-
|
| 35 |
-
def _resize(self, images):
|
| 36 |
-
size = (self.resolution, self.resolution)
|
| 37 |
-
if images.shape[-2:] != size:
|
| 38 |
-
images = F.interpolate(images, size=size, mode='bilinear', align_corners=False)
|
| 39 |
-
return images
|
| 40 |
-
|
| 41 |
def _dinov3_tokens(self, images):
|
| 42 |
mean = torch.tensor(IMAGENET_MEAN, device=images.device).view(1, 3, 1, 1)
|
| 43 |
std = torch.tensor(IMAGENET_STD, device=images.device).view(1, 3, 1, 1)
|
|
@@ -60,8 +47,8 @@ class ImageEncoder:
|
|
| 60 |
|
| 61 |
@torch.no_grad()
|
| 62 |
def __call__(self, images: torch.Tensor) -> TensorDict:
|
| 63 |
-
"""images: [B, 3,
|
| 64 |
-
images =
|
| 65 |
feature1 = self._dinov3_tokens(images)
|
| 66 |
feature2 = self._flux2_tokens(images)
|
| 67 |
return TensorDict({'feature1': feature1, 'feature2': feature2}, batch_size=feature1.shape[0])
|
|
|
|
| 13 |
|
| 14 |
|
| 15 |
class ImageEncoder:
|
| 16 |
+
def __init__(self, dinov3_repo: str, flux2_vae_repo: str,
|
| 17 |
dinov3_dtype: str = 'bf16', device: str = 'cuda'):
|
| 18 |
from diffusers.models import AutoencoderKLFlux2
|
| 19 |
from transformers import AutoModel
|
| 20 |
|
|
|
|
| 21 |
self.device = torch.device(device)
|
| 22 |
self.dinov3 = AutoModel.from_pretrained(dinov3_repo, torch_dtype=DTYPES[dinov3_dtype])
|
| 23 |
self.dinov3 = self.dinov3.eval().requires_grad_(False).to(self.device)
|
|
|
|
| 25 |
torch_dtype=torch.bfloat16)
|
| 26 |
self.flux2_vae = self.flux2_vae.eval().requires_grad_(False).to(self.device)
|
| 27 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 28 |
def _dinov3_tokens(self, images):
|
| 29 |
mean = torch.tensor(IMAGENET_MEAN, device=images.device).view(1, 3, 1, 1)
|
| 30 |
std = torch.tensor(IMAGENET_STD, device=images.device).view(1, 3, 1, 1)
|
|
|
|
| 47 |
|
| 48 |
@torch.no_grad()
|
| 49 |
def __call__(self, images: torch.Tensor) -> TensorDict:
|
| 50 |
+
"""images: [B, 3, 1024, 1024] in [0, 1] -> TensorDict(feature1=DINOv3 tokens, feature2=FLUX.2 VAE tokens)."""
|
| 51 |
+
images = images.to(self.device)
|
| 52 |
feature1 = self._dinov3_tokens(images)
|
| 53 |
feature2 = self._flux2_tokens(images)
|
| 54 |
return TensorDict({'feature1': feature1, 'feature2': feature2}, batch_size=feature1.shape[0])
|
idol/gaussian.py
CHANGED
|
@@ -1,5 +1,7 @@
|
|
| 1 |
-
|
| 2 |
import numpy as np
|
|
|
|
|
|
|
| 3 |
from plyfile import PlyData, PlyElement
|
| 4 |
|
| 5 |
|
|
@@ -7,377 +9,54 @@ def inverse_sigmoid(x):
|
|
| 7 |
return torch.log(x / (1 - x))
|
| 8 |
|
| 9 |
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
self,
|
| 13 |
-
aabb : list,
|
| 14 |
-
sh_degree : int = 0,
|
| 15 |
-
mininum_kernel_size : float = 0.0,
|
| 16 |
-
scaling_bias : float = 0.01,
|
| 17 |
-
opacity_bias : float = 0.1,
|
| 18 |
-
scaling_activation : str = "exp",
|
| 19 |
-
device='cuda'
|
| 20 |
-
):
|
| 21 |
-
self.init_params = {
|
| 22 |
-
'aabb': aabb,
|
| 23 |
-
'sh_degree': sh_degree,
|
| 24 |
-
'mininum_kernel_size': mininum_kernel_size,
|
| 25 |
-
'scaling_bias': scaling_bias,
|
| 26 |
-
'opacity_bias': opacity_bias,
|
| 27 |
-
'scaling_activation': scaling_activation,
|
| 28 |
-
}
|
| 29 |
-
|
| 30 |
-
self.sh_degree = sh_degree
|
| 31 |
-
self.active_sh_degree = sh_degree
|
| 32 |
-
self.mininum_kernel_size = mininum_kernel_size
|
| 33 |
-
self.scaling_bias = scaling_bias
|
| 34 |
-
self.opacity_bias = opacity_bias
|
| 35 |
-
self.scaling_activation_type = scaling_activation
|
| 36 |
-
self.device = device
|
| 37 |
-
self.aabb = torch.tensor(aabb, dtype=torch.float32, device=device)
|
| 38 |
-
self.setup_functions()
|
| 39 |
-
|
| 40 |
-
self._storage = {}
|
| 41 |
-
# Pre-initialize keys
|
| 42 |
-
for key in ['_xyz', 'xyz', '_features_dc', 'features_dc',
|
| 43 |
-
'_features_rest', 'features_rest',
|
| 44 |
-
'_scaling', 'scaling',
|
| 45 |
-
'_rotation', 'rotation',
|
| 46 |
-
'_opacity', 'opacity',
|
| 47 |
-
'features']:
|
| 48 |
-
self._storage[key] = None
|
| 49 |
-
|
| 50 |
-
def setup_functions(self):
|
| 51 |
-
if self.scaling_activation_type == "exp":
|
| 52 |
-
self.scaling_activation = torch.exp
|
| 53 |
-
self.inverse_scaling_activation = torch.log
|
| 54 |
-
elif self.scaling_activation_type == "softplus":
|
| 55 |
-
self.scaling_activation = torch.nn.functional.softplus
|
| 56 |
-
self.inverse_scaling_activation = lambda x: x + torch.log(-torch.expm1(-x))
|
| 57 |
-
|
| 58 |
-
self.opacity_activation = torch.sigmoid
|
| 59 |
-
self.inverse_opacity_activation = inverse_sigmoid
|
| 60 |
-
|
| 61 |
-
self.rotation_activation = torch.nn.functional.normalize
|
| 62 |
-
|
| 63 |
-
self.scale_bias = self.inverse_scaling_activation(torch.tensor(self.scaling_bias)).to(self.device)
|
| 64 |
-
self.rots_bias = torch.zeros((4)).to(self.device)
|
| 65 |
-
self.rots_bias[0] = 1
|
| 66 |
-
self.opacity_bias = self.inverse_opacity_activation(torch.tensor(self.opacity_bias)).to(self.device)
|
| 67 |
-
|
| 68 |
-
def _get_store(self, name):
|
| 69 |
-
return self._storage.get(name)
|
| 70 |
-
|
| 71 |
-
def _set_store(self, name, value):
|
| 72 |
-
self._storage[name] = value
|
| 73 |
-
|
| 74 |
-
# --- Opacity ---
|
| 75 |
-
@property
|
| 76 |
-
def opacity(self):
|
| 77 |
-
return self._get_store("opacity")
|
| 78 |
-
|
| 79 |
-
@opacity.setter
|
| 80 |
-
def opacity(self, value):
|
| 81 |
-
self.set_opacity(value)
|
| 82 |
-
|
| 83 |
-
@property
|
| 84 |
-
def _opacity(self):
|
| 85 |
-
return self._get_store("_opacity")
|
| 86 |
-
|
| 87 |
-
@_opacity.setter
|
| 88 |
-
def _opacity(self, value):
|
| 89 |
-
self.set_raw_opacity(value)
|
| 90 |
-
|
| 91 |
-
def set_opacity(self, value):
|
| 92 |
-
if value is None:
|
| 93 |
-
self._set_store("opacity", None)
|
| 94 |
-
self._set_store("_opacity", None)
|
| 95 |
-
return
|
| 96 |
-
self._set_store("opacity", value)
|
| 97 |
-
# Update raw: inverse activation
|
| 98 |
-
raw = self.inverse_opacity_activation(value) - self.opacity_bias
|
| 99 |
-
self._set_store("_opacity", raw)
|
| 100 |
-
|
| 101 |
-
def set_raw_opacity(self, value):
|
| 102 |
-
if value is None:
|
| 103 |
-
self._set_store("_opacity", None)
|
| 104 |
-
self._set_store("opacity", None)
|
| 105 |
-
return
|
| 106 |
-
self._set_store("_opacity", value)
|
| 107 |
-
# Update activated
|
| 108 |
-
activated = self.opacity_activation(value + self.opacity_bias)
|
| 109 |
-
self._set_store("opacity", activated)
|
| 110 |
-
|
| 111 |
-
# --- Scaling ---
|
| 112 |
-
@property
|
| 113 |
-
def scaling(self):
|
| 114 |
-
return self._get_store("scaling")
|
| 115 |
-
|
| 116 |
-
@scaling.setter
|
| 117 |
-
def scaling(self, value):
|
| 118 |
-
self.set_scaling(value)
|
| 119 |
-
|
| 120 |
-
@property
|
| 121 |
-
def _scaling(self):
|
| 122 |
-
return self._get_store("_scaling")
|
| 123 |
-
|
| 124 |
-
@_scaling.setter
|
| 125 |
-
def _scaling(self, value):
|
| 126 |
-
self.set_raw_scaling(value)
|
| 127 |
-
|
| 128 |
-
def set_scaling(self, value):
|
| 129 |
-
if value is None:
|
| 130 |
-
self._set_store("scaling", None)
|
| 131 |
-
self._set_store("_scaling", None)
|
| 132 |
-
return
|
| 133 |
-
self._set_store("scaling", value)
|
| 134 |
-
# Inverse activation
|
| 135 |
-
s = torch.sqrt(torch.square(value) - self.mininum_kernel_size ** 2)
|
| 136 |
-
raw = self.inverse_scaling_activation(s) - self.scale_bias
|
| 137 |
-
self._set_store("_scaling", raw)
|
| 138 |
-
|
| 139 |
-
def set_raw_scaling(self, value):
|
| 140 |
-
if value is None:
|
| 141 |
-
self._set_store("_scaling", None)
|
| 142 |
-
self._set_store("scaling", None)
|
| 143 |
-
return
|
| 144 |
-
self._set_store("_scaling", value)
|
| 145 |
-
# Forward activation
|
| 146 |
-
s = self.scaling_activation(value + self.scale_bias)
|
| 147 |
-
s = torch.square(s) + self.mininum_kernel_size ** 2
|
| 148 |
-
activated = torch.sqrt(s)
|
| 149 |
-
self._set_store("scaling", activated)
|
| 150 |
-
|
| 151 |
-
# --- Rotation ---
|
| 152 |
-
@property
|
| 153 |
-
def rotation(self):
|
| 154 |
-
return self._get_store("rotation")
|
| 155 |
-
|
| 156 |
-
@rotation.setter
|
| 157 |
-
def rotation(self, value):
|
| 158 |
-
self.set_rotation(value)
|
| 159 |
-
|
| 160 |
-
@property
|
| 161 |
-
def _rotation(self):
|
| 162 |
-
return self._get_store("_rotation")
|
| 163 |
-
|
| 164 |
-
@_rotation.setter
|
| 165 |
-
def _rotation(self, value):
|
| 166 |
-
self.set_raw_rotation(value)
|
| 167 |
-
|
| 168 |
-
def set_rotation(self, value):
|
| 169 |
-
if value is None:
|
| 170 |
-
self._set_store("rotation", None)
|
| 171 |
-
self._set_store("_rotation", None)
|
| 172 |
-
return
|
| 173 |
-
self._set_store("rotation", value)
|
| 174 |
-
# Inverse: just subtract bias
|
| 175 |
-
raw = value - self.rots_bias[None, :]
|
| 176 |
-
self._set_store("_rotation", raw)
|
| 177 |
|
| 178 |
-
def set_raw_rotation(self, value):
|
| 179 |
-
if value is None:
|
| 180 |
-
self._set_store("_rotation", None)
|
| 181 |
-
self._set_store("rotation", None)
|
| 182 |
-
return
|
| 183 |
-
self._set_store("_rotation", value)
|
| 184 |
-
# Forward activation
|
| 185 |
-
activated = self.rotation_activation(value + self.rots_bias[None, :])
|
| 186 |
-
self._set_store("rotation", activated)
|
| 187 |
|
| 188 |
-
|
| 189 |
-
|
| 190 |
-
|
| 191 |
-
|
| 192 |
-
|
| 193 |
-
|
| 194 |
-
def
|
| 195 |
-
|
| 196 |
-
|
| 197 |
-
|
| 198 |
-
|
| 199 |
-
|
| 200 |
-
|
| 201 |
-
|
| 202 |
-
|
| 203 |
-
self.
|
| 204 |
-
|
| 205 |
-
|
| 206 |
-
|
| 207 |
-
|
| 208 |
-
self._set_store("_xyz", None)
|
| 209 |
-
return
|
| 210 |
-
self._set_store("xyz", value)
|
| 211 |
-
# Inverse
|
| 212 |
-
raw = (value - self.aabb[None, :3]) / self.aabb[None, 3:]
|
| 213 |
-
self._set_store("_xyz", raw)
|
| 214 |
-
|
| 215 |
-
def set_raw_xyz(self, value):
|
| 216 |
-
if value is None:
|
| 217 |
-
self._set_store("_xyz", None)
|
| 218 |
-
self._set_store("xyz", None)
|
| 219 |
-
return
|
| 220 |
-
self._set_store("_xyz", value)
|
| 221 |
-
# Forward activation
|
| 222 |
-
activated = value * self.aabb[None, 3:] + self.aabb[None, :3]
|
| 223 |
-
self._set_store("xyz", activated)
|
| 224 |
-
|
| 225 |
-
# --- Features (Combined) ---
|
| 226 |
-
@property
|
| 227 |
-
def features(self):
|
| 228 |
-
return self._get_store("features")
|
| 229 |
-
|
| 230 |
-
@features.setter
|
| 231 |
-
def features(self, value):
|
| 232 |
-
self.set_features(value)
|
| 233 |
-
|
| 234 |
-
def set_features(self, value):
|
| 235 |
-
self._set_store("features", value)
|
| 236 |
-
if value is None:
|
| 237 |
-
self._set_store("_features_dc", None)
|
| 238 |
-
self._set_store("features_dc", None)
|
| 239 |
-
self._set_store("_features_rest", None)
|
| 240 |
-
self._set_store("features_rest", None)
|
| 241 |
-
return
|
| 242 |
-
|
| 243 |
-
# Split features into DC and Rest
|
| 244 |
-
# Assuming shape (N, 1+K, 3)
|
| 245 |
-
dc = value[:, :1, :]
|
| 246 |
-
self._set_store("_features_dc", dc)
|
| 247 |
-
self._set_store("features_dc", dc)
|
| 248 |
-
|
| 249 |
-
if value.shape[1] > 1:
|
| 250 |
-
rest = value[:, 1:, :]
|
| 251 |
-
self._set_store("_features_rest", rest)
|
| 252 |
-
self._set_store("features_rest", rest)
|
| 253 |
-
else:
|
| 254 |
-
self._set_store("_features_rest", None)
|
| 255 |
-
self._set_store("features_rest", None)
|
| 256 |
-
|
| 257 |
-
def _update_features_from_components(self):
|
| 258 |
-
dc = self._get_store("_features_dc")
|
| 259 |
-
rest = self._get_store("_features_rest")
|
| 260 |
-
if dc is None:
|
| 261 |
-
self._set_store("features", None)
|
| 262 |
-
return
|
| 263 |
-
|
| 264 |
-
if rest is not None:
|
| 265 |
-
# Concatenate on dim 1 (N, 1+K, 3)
|
| 266 |
-
f = torch.cat((dc, rest), dim=1)
|
| 267 |
-
self._set_store("features", f)
|
| 268 |
-
else:
|
| 269 |
-
self._set_store("features", dc)
|
| 270 |
-
|
| 271 |
-
# --- Features DC ---
|
| 272 |
-
@property
|
| 273 |
-
def features_dc(self):
|
| 274 |
-
return self._get_store("features_dc")
|
| 275 |
-
|
| 276 |
-
@features_dc.setter
|
| 277 |
-
def features_dc(self, value):
|
| 278 |
-
self.set_features_dc(value)
|
| 279 |
-
|
| 280 |
-
@property
|
| 281 |
-
def _features_dc(self):
|
| 282 |
-
return self._get_store("_features_dc")
|
| 283 |
-
|
| 284 |
-
@_features_dc.setter
|
| 285 |
-
def _features_dc(self, value):
|
| 286 |
-
self.set_raw_features_dc(value)
|
| 287 |
-
|
| 288 |
-
def set_features_dc(self, value):
|
| 289 |
-
self._set_store("features_dc", value)
|
| 290 |
-
self._set_store("_features_dc", value) # Identity
|
| 291 |
-
self._update_features_from_components()
|
| 292 |
-
|
| 293 |
-
def set_raw_features_dc(self, value):
|
| 294 |
-
self._set_store("_features_dc", value)
|
| 295 |
-
self._set_store("features_dc", value) # Identity
|
| 296 |
-
self._update_features_from_components()
|
| 297 |
-
|
| 298 |
-
# --- Features Rest ---
|
| 299 |
-
@property
|
| 300 |
-
def features_rest(self):
|
| 301 |
-
return self._get_store("features_rest")
|
| 302 |
-
|
| 303 |
-
@features_rest.setter
|
| 304 |
-
def features_rest(self, value):
|
| 305 |
-
self.set_features_rest(value)
|
| 306 |
-
|
| 307 |
-
@property
|
| 308 |
-
def _features_rest(self):
|
| 309 |
-
return self._get_store("_features_rest")
|
| 310 |
-
|
| 311 |
-
@_features_rest.setter
|
| 312 |
-
def _features_rest(self, value):
|
| 313 |
-
self.set_raw_features_rest(value)
|
| 314 |
-
|
| 315 |
-
def set_features_rest(self, value):
|
| 316 |
-
self._set_store("features_rest", value)
|
| 317 |
-
self._set_store("_features_rest", value)
|
| 318 |
-
self._update_features_from_components()
|
| 319 |
|
| 320 |
-
def set_raw_features_rest(self, value):
|
| 321 |
-
self._set_store("_features_rest", value)
|
| 322 |
-
self._set_store("features_rest", value)
|
| 323 |
-
self._update_features_from_components()
|
| 324 |
-
|
| 325 |
-
# --- Old API ---
|
| 326 |
-
@property
|
| 327 |
-
def get_scaling(self):
|
| 328 |
-
return self._get_store("scaling")
|
| 329 |
-
|
| 330 |
-
@property
|
| 331 |
-
def get_rotation(self):
|
| 332 |
-
return self._get_store("rotation")
|
| 333 |
-
|
| 334 |
-
@property
|
| 335 |
-
def get_xyz(self):
|
| 336 |
-
return self._get_store("xyz")
|
| 337 |
-
|
| 338 |
-
@property
|
| 339 |
-
def get_features(self):
|
| 340 |
-
return self.features
|
| 341 |
-
|
| 342 |
-
@property
|
| 343 |
-
def get_opacity(self):
|
| 344 |
-
return self._get_store("opacity")
|
| 345 |
-
|
| 346 |
-
# --- Saver and Loader ---
|
| 347 |
-
def construct_list_of_attributes(self):
|
| 348 |
-
l = ['x', 'y', 'z', 'nx', 'ny', 'nz']
|
| 349 |
-
# All channels except the 3 DC
|
| 350 |
-
for i in range(self._features_dc.shape[1]*self._features_dc.shape[2]):
|
| 351 |
-
l.append('f_dc_{}'.format(i))
|
| 352 |
-
l.append('opacity')
|
| 353 |
-
for i in range(self._scaling.shape[1]):
|
| 354 |
-
l.append('scale_{}'.format(i))
|
| 355 |
-
for i in range(self._rotation.shape[1]):
|
| 356 |
-
l.append('rot_{}'.format(i))
|
| 357 |
-
return l
|
| 358 |
-
|
| 359 |
def save_ply(self, path):
|
| 360 |
"""Write the standard 3D Gaussian Splatting PLY (z-up object turned to the y-up convention viewers use)."""
|
| 361 |
-
|
| 362 |
-
|
| 363 |
-
def ply_attributes(self):
|
| 364 |
-
"""The (N, attributes) float array save_ply writes, in construct_list_of_attributes() order."""
|
| 365 |
-
xyz = self.get_xyz.detach().cpu().numpy()
|
| 366 |
normals = np.zeros_like(xyz)
|
| 367 |
-
f_dc = self.
|
| 368 |
# Clamp before the inverse activations: a saturated opacity (exactly 1.0 after the sigmoid) or a zero
|
| 369 |
# scale would otherwise be written as +/-inf, which some PLY viewers reject.
|
| 370 |
eps = 1e-6
|
| 371 |
-
opacities = inverse_sigmoid(self.
|
| 372 |
-
scale = torch.log(self.
|
| 373 |
-
rotation =
|
| 374 |
|
| 375 |
# z-up -> y-up: rotate +90 degrees about x, i.e. (x, y, z) -> (x, -z, y)
|
| 376 |
xyz = np.stack([xyz[:, 0], -xyz[:, 2], xyz[:, 1]], axis=1)
|
| 377 |
-
rotation = rotation / np.linalg.norm(rotation, axis=1, keepdims=True)
|
| 378 |
rotation = quaternion_multiply(np.array([np.sqrt(0.5), np.sqrt(0.5), 0.0, 0.0]), rotation)
|
| 379 |
|
| 380 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 381 |
|
| 382 |
|
| 383 |
def quaternion_multiply(a, b):
|
|
@@ -390,14 +69,3 @@ def quaternion_multiply(a, b):
|
|
| 390 |
aw * by - ax * bz + ay * bw + az * bx,
|
| 391 |
aw * bz + ax * by - ay * bx + az * bw,
|
| 392 |
], axis=1)
|
| 393 |
-
|
| 394 |
-
|
| 395 |
-
def write_ply_attributes(path, attribute_names, attributes):
|
| 396 |
-
"""Write a Gaussian PLY from attribute names and an (N, len(names)) array (see Gaussian.ply_attributes)."""
|
| 397 |
-
dtype_full = [(attribute, 'f4') for attribute in attribute_names]
|
| 398 |
-
elements = np.empty(attributes.shape[0], dtype=dtype_full)
|
| 399 |
-
# Assign whole columns: constructing 262,144 Python tuples per PLY
|
| 400 |
-
# dominated validation export time and allocated millions of scalars.
|
| 401 |
-
for column_index, (attribute_name, _) in enumerate(dtype_full):
|
| 402 |
-
elements[attribute_name] = attributes[:, column_index]
|
| 403 |
-
PlyData([PlyElement.describe(elements, 'vertex')]).write(path)
|
|
|
|
| 1 |
+
"""The generated object: 3D Gaussians with position, colour, scale, rotation and opacity."""
|
| 2 |
import numpy as np
|
| 3 |
+
import torch
|
| 4 |
+
import torch.nn.functional as F
|
| 5 |
from plyfile import PlyData, PlyElement
|
| 6 |
|
| 7 |
|
|
|
|
| 9 |
return torch.log(x / (1 - x))
|
| 10 |
|
| 11 |
|
| 12 |
+
def inverse_softplus(x):
|
| 13 |
+
return x + torch.log(-torch.expm1(-x))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 14 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 15 |
|
| 16 |
+
class Gaussian:
|
| 17 |
+
"""Built from the Gaussian decoder's raw outputs; the activated values are what renderers and PLY files use.
|
| 18 |
+
|
| 19 |
+
xyz [N, 3] positions, features_dc [N, 1, 3] colour (degree-0 spherical harmonics), scaling [N, 3],
|
| 20 |
+
rotation [N, 4] unit quaternions (w, x, y, z), opacity [N, 1]. The object is z-up, inside [-0.5, 0.5]^3."""
|
| 21 |
+
|
| 22 |
+
def __init__(self, raw_xyz, features_dc, raw_scaling, raw_rotation, raw_opacity,
|
| 23 |
+
scaling_bias: float, opacity_bias: float, minimum_kernel_size: float):
|
| 24 |
+
device = raw_xyz.device
|
| 25 |
+
aabb = torch.tensor([-0.5, -0.5, -0.5, 1.0, 1.0, 1.0], dtype=torch.float32, device=device)
|
| 26 |
+
scaling_offset = inverse_softplus(torch.tensor(scaling_bias)).to(device)
|
| 27 |
+
opacity_offset = inverse_sigmoid(torch.tensor(opacity_bias)).to(device)
|
| 28 |
+
rotation_offset = torch.tensor([1.0, 0.0, 0.0, 0.0], device=device)
|
| 29 |
+
|
| 30 |
+
self.xyz = raw_xyz * aabb[None, 3:] + aabb[None, :3]
|
| 31 |
+
self.features_dc = features_dc
|
| 32 |
+
scaling = F.softplus(raw_scaling + scaling_offset)
|
| 33 |
+
self.scaling = torch.sqrt(torch.square(scaling) + minimum_kernel_size ** 2)
|
| 34 |
+
self.rotation = F.normalize(raw_rotation + rotation_offset[None, :])
|
| 35 |
+
self.opacity = torch.sigmoid(raw_opacity + opacity_offset)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 36 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 37 |
def save_ply(self, path):
|
| 38 |
"""Write the standard 3D Gaussian Splatting PLY (z-up object turned to the y-up convention viewers use)."""
|
| 39 |
+
xyz = self.xyz.detach().cpu().numpy()
|
|
|
|
|
|
|
|
|
|
|
|
|
| 40 |
normals = np.zeros_like(xyz)
|
| 41 |
+
f_dc = self.features_dc.detach().transpose(1, 2).flatten(start_dim=1).contiguous().cpu().numpy()
|
| 42 |
# Clamp before the inverse activations: a saturated opacity (exactly 1.0 after the sigmoid) or a zero
|
| 43 |
# scale would otherwise be written as +/-inf, which some PLY viewers reject.
|
| 44 |
eps = 1e-6
|
| 45 |
+
opacities = inverse_sigmoid(self.opacity.clamp(eps, 1 - eps)).detach().cpu().numpy()
|
| 46 |
+
scale = torch.log(self.scaling.clamp_min(eps)).detach().cpu().numpy()
|
| 47 |
+
rotation = self.rotation.detach().cpu().numpy()
|
| 48 |
|
| 49 |
# z-up -> y-up: rotate +90 degrees about x, i.e. (x, y, z) -> (x, -z, y)
|
| 50 |
xyz = np.stack([xyz[:, 0], -xyz[:, 2], xyz[:, 1]], axis=1)
|
|
|
|
| 51 |
rotation = quaternion_multiply(np.array([np.sqrt(0.5), np.sqrt(0.5), 0.0, 0.0]), rotation)
|
| 52 |
|
| 53 |
+
attributes = np.concatenate((xyz, normals, f_dc, opacities, scale, rotation), axis=1).astype(np.float32)
|
| 54 |
+
names = ['x', 'y', 'z', 'nx', 'ny', 'nz', 'f_dc_0', 'f_dc_1', 'f_dc_2', 'opacity',
|
| 55 |
+
'scale_0', 'scale_1', 'scale_2', 'rot_0', 'rot_1', 'rot_2', 'rot_3']
|
| 56 |
+
elements = np.empty(attributes.shape[0], dtype=[(name, 'f4') for name in names])
|
| 57 |
+
for column, name in enumerate(names):
|
| 58 |
+
elements[name] = attributes[:, column]
|
| 59 |
+
PlyData([PlyElement.describe(elements, 'vertex')]).write(path)
|
| 60 |
|
| 61 |
|
| 62 |
def quaternion_multiply(a, b):
|
|
|
|
| 69 |
aw * by - ax * bz + ay * bw + az * bx,
|
| 70 |
aw * bz + ax * by - ay * bx + az * bw,
|
| 71 |
], axis=1)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
idol/models/birefnet.py
CHANGED
|
@@ -4,7 +4,6 @@ image encoders see them."""
|
|
| 4 |
import math
|
| 5 |
|
| 6 |
import numpy as np
|
| 7 |
-
import safetensors.torch
|
| 8 |
import torch
|
| 9 |
import torch.nn as nn
|
| 10 |
import torch.nn.functional as F
|
|
@@ -14,18 +13,6 @@ from torchvision.ops import deform_conv2d
|
|
| 14 |
# BiRefNet background removal (Swin-L + ASPP-deformable decoder)
|
| 15 |
# ---------------------------------------------------------------------------
|
| 16 |
|
| 17 |
-
# -- timm-style helpers, inlined to avoid a timm dependency --------------------
|
| 18 |
-
|
| 19 |
-
def _trunc_normal_(tensor: torch.Tensor, mean: float = 0.0, std: float = 1.0,
|
| 20 |
-
a: float = -2.0, b: float = 2.0) -> torch.Tensor:
|
| 21 |
-
# Initialization helper — only used at __init__ time, the released ckpt
|
| 22 |
-
# overwrites everything in load_safetensors so the exact distribution here
|
| 23 |
-
# is unimportant.
|
| 24 |
-
with torch.no_grad():
|
| 25 |
-
tensor.normal_(mean, std).clamp_(mean + a * std, mean + b * std)
|
| 26 |
-
return tensor
|
| 27 |
-
|
| 28 |
-
|
| 29 |
# -- Swin Transformer (Swin-Large preset) -------------------------------------
|
| 30 |
|
| 31 |
class _SwinMlp(nn.Module):
|
|
@@ -77,7 +64,6 @@ class _WindowAttention(nn.Module):
|
|
| 77 |
|
| 78 |
self.qkv = nn.Linear(dim, dim * 3, bias=True)
|
| 79 |
self.proj = nn.Linear(dim, dim)
|
| 80 |
-
_trunc_normal_(self.relative_position_bias_table, std=0.02)
|
| 81 |
|
| 82 |
def forward(self, x, mask=None):
|
| 83 |
B_, N, C = x.shape
|
|
@@ -153,8 +139,6 @@ class _PatchMerging(nn.Module):
|
|
| 153 |
def forward(self, x, H, W):
|
| 154 |
B, L, C = x.shape
|
| 155 |
x = x.view(B, H, W, C)
|
| 156 |
-
if H % 2 == 1 or W % 2 == 1:
|
| 157 |
-
x = F.pad(x, (0, 0, 0, W % 2, 0, H % 2))
|
| 158 |
x0 = x[:, 0::2, 0::2, :]
|
| 159 |
x1 = x[:, 1::2, 0::2, :]
|
| 160 |
x2 = x[:, 0::2, 1::2, :]
|
|
@@ -215,11 +199,6 @@ class _SwinPatchEmbed(nn.Module):
|
|
| 215 |
self.embed_dim = embed_dim
|
| 216 |
|
| 217 |
def forward(self, x):
|
| 218 |
-
_, _, H, W = x.shape
|
| 219 |
-
if W % self.patch_size[1] != 0:
|
| 220 |
-
x = F.pad(x, (0, self.patch_size[1] - W % self.patch_size[1]))
|
| 221 |
-
if H % self.patch_size[0] != 0:
|
| 222 |
-
x = F.pad(x, (0, 0, 0, self.patch_size[0] - H % self.patch_size[0]))
|
| 223 |
x = self.proj(x)
|
| 224 |
Wh, Ww = x.size(2), x.size(3)
|
| 225 |
x = x.flatten(2).transpose(1, 2)
|
|
@@ -414,12 +393,6 @@ class _BiRefNetDecoder(nn.Module):
|
|
| 414 |
self.lateral_block3 = _BasicLatBlk(c[2], c[2])
|
| 415 |
self.lateral_block2 = _BasicLatBlk(c[3], c[3])
|
| 416 |
|
| 417 |
-
# multi-scale supervision heads (training only — kept for state_dict
|
| 418 |
-
# parity with the released checkpoint; not consumed at inference).
|
| 419 |
-
self.conv_ms_spvn_4 = nn.Conv2d(c[1], 1, 1, 1, 0)
|
| 420 |
-
self.conv_ms_spvn_3 = nn.Conv2d(c[2], 1, 1, 1, 0)
|
| 421 |
-
self.conv_ms_spvn_2 = nn.Conv2d(c[3], 1, 1, 1, 0)
|
| 422 |
-
|
| 423 |
# gradient-decoder-triggering (gdt) attention: used at inference to
|
| 424 |
# gate p4/p3/p2.
|
| 425 |
_N = 16
|
|
@@ -431,10 +404,6 @@ class _BiRefNetDecoder(nn.Module):
|
|
| 431 |
|
| 432 |
def _head_1x1():
|
| 433 |
return nn.Sequential(nn.Conv2d(_N, 1, 1, 1, 0))
|
| 434 |
-
# multi-scale supervision heads on the gdt branch (training only)
|
| 435 |
-
self.gdt_convs_pred_4 = _head_1x1()
|
| 436 |
-
self.gdt_convs_pred_3 = _head_1x1()
|
| 437 |
-
self.gdt_convs_pred_2 = _head_1x1()
|
| 438 |
# attention heads
|
| 439 |
self.gdt_convs_attn_4 = _head_1x1()
|
| 440 |
self.gdt_convs_attn_3 = _head_1x1()
|
|
@@ -527,21 +496,9 @@ class BiRefNet(nn.Module):
|
|
| 527 |
logits = self.decoder(x, x1, x2, x3, x4)
|
| 528 |
return torch.sigmoid(logits)
|
| 529 |
|
| 530 |
-
def load_safetensors(self, path: str) -> None:
|
| 531 |
-
sd = safetensors.torch.load_file(path)
|
| 532 |
-
# The decoder's gdt_convs_pred_* / conv_ms_spvn_* heads are training-only
|
| 533 |
-
# but are kept as submodules for state_dict parity. strict=True works.
|
| 534 |
-
missing, unexpected = self.load_state_dict(sd, strict=False)
|
| 535 |
-
if unexpected:
|
| 536 |
-
raise KeyError(f"[birefnet] unexpected keys (e.g. {unexpected[:3]})")
|
| 537 |
-
if missing:
|
| 538 |
-
raise KeyError(f"[birefnet] missing keys (e.g. {missing[:3]})")
|
| 539 |
-
|
| 540 |
@torch.no_grad()
|
| 541 |
def remove_background(self, image) -> "Image.Image":
|
| 542 |
from PIL import Image
|
| 543 |
-
if image.mode != "RGB":
|
| 544 |
-
image = image.convert("RGB")
|
| 545 |
W, H = image.size
|
| 546 |
arr = np.array(image, dtype=np.float32) / 255.0
|
| 547 |
t = torch.from_numpy(arr).permute(2, 0, 1).unsqueeze(0)
|
|
|
|
| 4 |
import math
|
| 5 |
|
| 6 |
import numpy as np
|
|
|
|
| 7 |
import torch
|
| 8 |
import torch.nn as nn
|
| 9 |
import torch.nn.functional as F
|
|
|
|
| 13 |
# BiRefNet background removal (Swin-L + ASPP-deformable decoder)
|
| 14 |
# ---------------------------------------------------------------------------
|
| 15 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 16 |
# -- Swin Transformer (Swin-Large preset) -------------------------------------
|
| 17 |
|
| 18 |
class _SwinMlp(nn.Module):
|
|
|
|
| 64 |
|
| 65 |
self.qkv = nn.Linear(dim, dim * 3, bias=True)
|
| 66 |
self.proj = nn.Linear(dim, dim)
|
|
|
|
| 67 |
|
| 68 |
def forward(self, x, mask=None):
|
| 69 |
B_, N, C = x.shape
|
|
|
|
| 139 |
def forward(self, x, H, W):
|
| 140 |
B, L, C = x.shape
|
| 141 |
x = x.view(B, H, W, C)
|
|
|
|
|
|
|
| 142 |
x0 = x[:, 0::2, 0::2, :]
|
| 143 |
x1 = x[:, 1::2, 0::2, :]
|
| 144 |
x2 = x[:, 0::2, 1::2, :]
|
|
|
|
| 199 |
self.embed_dim = embed_dim
|
| 200 |
|
| 201 |
def forward(self, x):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 202 |
x = self.proj(x)
|
| 203 |
Wh, Ww = x.size(2), x.size(3)
|
| 204 |
x = x.flatten(2).transpose(1, 2)
|
|
|
|
| 393 |
self.lateral_block3 = _BasicLatBlk(c[2], c[2])
|
| 394 |
self.lateral_block2 = _BasicLatBlk(c[3], c[3])
|
| 395 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 396 |
# gradient-decoder-triggering (gdt) attention: used at inference to
|
| 397 |
# gate p4/p3/p2.
|
| 398 |
_N = 16
|
|
|
|
| 404 |
|
| 405 |
def _head_1x1():
|
| 406 |
return nn.Sequential(nn.Conv2d(_N, 1, 1, 1, 0))
|
|
|
|
|
|
|
|
|
|
|
|
|
| 407 |
# attention heads
|
| 408 |
self.gdt_convs_attn_4 = _head_1x1()
|
| 409 |
self.gdt_convs_attn_3 = _head_1x1()
|
|
|
|
| 496 |
logits = self.decoder(x, x1, x2, x3, x4)
|
| 497 |
return torch.sigmoid(logits)
|
| 498 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 499 |
@torch.no_grad()
|
| 500 |
def remove_background(self, image) -> "Image.Image":
|
| 501 |
from PIL import Image
|
|
|
|
|
|
|
| 502 |
W, H = image.size
|
| 503 |
arr = np.array(image, dtype=np.float32) / 255.0
|
| 504 |
t = torch.from_numpy(arr).permute(2, 0, 1).unsqueeze(0)
|
idol/models/decoder.py
CHANGED
|
@@ -1,424 +1,192 @@
|
|
| 1 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2 |
import torch
|
| 3 |
import torch.nn as nn
|
| 4 |
import torch.nn.functional as F
|
| 5 |
-
|
| 6 |
from ..gaussian import Gaussian
|
| 7 |
-
from .
|
| 8 |
-
|
| 9 |
-
from tensordict import TensorDict
|
| 10 |
|
| 11 |
-
class FixedlenDecoder(TransformerBase):
|
| 12 |
-
def __init__(
|
| 13 |
-
self,
|
| 14 |
-
in_channels: int,
|
| 15 |
-
model_channels: int,
|
| 16 |
-
cond_channels: int,
|
| 17 |
-
num_blocks: int,
|
| 18 |
-
num_heads: Optional[int] = None,
|
| 19 |
-
num_head_channels: Optional[int] = 64,
|
| 20 |
-
mlp_ratio: float = 4.0,
|
| 21 |
-
attn_mode: Literal["full"] = "full",
|
| 22 |
-
window_num: Optional[int] = None,
|
| 23 |
-
pcd_pe_mode: Literal["pcd_ape", "pcd_ape_v2"] = "pcd_ape_v2",
|
| 24 |
-
use_fp16: bool = False,
|
| 25 |
-
use_checkpoint: bool = False,
|
| 26 |
-
qk_rms_norm: bool = True,
|
| 27 |
-
qk_rms_norm_cross: bool = True,
|
| 28 |
-
share_mod: bool = False,
|
| 29 |
-
):
|
| 30 |
-
"""
|
| 31 |
-
Gaussian Feature Fixed Length Latent Decoder.
|
| 32 |
-
Decode fixed length latent to 3D Gaussian representation, encode condition with cross attention.
|
| 33 |
-
Conditions:
|
| 34 |
-
- latent: [B, Lq, C]
|
| 35 |
-
Input:
|
| 36 |
-
- x: [B, L, 3] (points to decode)
|
| 37 |
-
"""
|
| 38 |
-
if attn_mode != "full":
|
| 39 |
-
raise ValueError(f"Only attn_mode='full' is supported in the public decoder, got {attn_mode!r}.")
|
| 40 |
-
super().__init__(
|
| 41 |
-
in_channels=model_channels,
|
| 42 |
-
model_channels=model_channels,
|
| 43 |
-
cond_channels=cond_channels,
|
| 44 |
-
num_blocks=num_blocks,
|
| 45 |
-
num_heads=num_heads,
|
| 46 |
-
num_head_channels=num_head_channels,
|
| 47 |
-
mlp_ratio=mlp_ratio,
|
| 48 |
-
use_fp16=use_fp16,
|
| 49 |
-
use_checkpoint=use_checkpoint,
|
| 50 |
-
qk_rms_norm=qk_rms_norm,
|
| 51 |
-
qk_rms_norm_cross=qk_rms_norm_cross,
|
| 52 |
-
share_mod=share_mod,
|
| 53 |
-
)
|
| 54 |
-
self.in_channels = in_channels
|
| 55 |
-
self.in_proj = nn.Linear(in_channels, model_channels)
|
| 56 |
-
self.pcd_pe_mode = pcd_pe_mode
|
| 57 |
-
if pcd_pe_mode == "pcd_ape":
|
| 58 |
-
self.pos_embedder = PcdAbsolutePositionEmbedder(
|
| 59 |
-
channels=model_channels,
|
| 60 |
-
in_channels=3,
|
| 61 |
-
)
|
| 62 |
-
elif pcd_pe_mode == "pcd_ape_v2":
|
| 63 |
-
self.pos_embedder = PcdAbsolutePositionEmbedderv2(
|
| 64 |
-
channels=model_channels,
|
| 65 |
-
in_channels=3,
|
| 66 |
-
)
|
| 67 |
-
else:
|
| 68 |
-
self.pos_embedder = None
|
| 69 |
-
|
| 70 |
-
def forward(self, x: tensordict.TensorDict = None, cond: Optional[torch.Tensor]= None) -> torch.Tensor:
|
| 71 |
-
"""
|
| 72 |
-
Args:
|
| 73 |
-
x: a dict containing:
|
| 74 |
-
- points: [B, L, 3] the query points to decode
|
| 75 |
-
cond: [B, Lq, C] the latent features as condition
|
| 76 |
-
Returns:
|
| 77 |
-
list of Gaussian representations
|
| 78 |
-
"""
|
| 79 |
-
pcd = x["points"]
|
| 80 |
-
B, L, C = pcd.shape
|
| 81 |
-
h = pcd
|
| 82 |
-
|
| 83 |
-
# token num log scaling
|
| 84 |
-
l = torch.full((B,), L).to(h)
|
| 85 |
-
l = torch.log2(l).to(h)
|
| 86 |
-
|
| 87 |
-
h = self.in_proj(h)
|
| 88 |
-
h = h + self.pos_embedder(pcd.view(-1,C)).view(B, L, -1) if hasattr(self, 'pos_embedder') else h
|
| 89 |
-
indices = None
|
| 90 |
-
# prepare spatial cache
|
| 91 |
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 95 |
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
use_fp16: bool = False,
|
| 110 |
-
use_checkpoint: bool = False,
|
| 111 |
-
qk_rms_norm: bool = True,
|
| 112 |
-
qk_rms_norm_cross: bool = True,
|
| 113 |
-
share_mod: bool = False,
|
| 114 |
-
*,
|
| 115 |
-
representation_config: dict = None,
|
| 116 |
-
use_learned_offset_scale: bool = False,
|
| 117 |
-
gaussian_center_pertube_scale: float = 1.0,
|
| 118 |
-
use_per_offset=False,
|
| 119 |
-
):
|
| 120 |
-
self.rep_config = representation_config
|
| 121 |
-
self.gaussian_center_pertube_scale = gaussian_center_pertube_scale
|
| 122 |
-
self.use_learned_offset_scale = use_learned_offset_scale
|
| 123 |
-
self.use_per_offset = use_per_offset
|
| 124 |
-
self.out_channels = self._calc_layout()
|
| 125 |
-
super().__init__(
|
| 126 |
-
in_channels = in_channels,
|
| 127 |
-
model_channels = model_channels,
|
| 128 |
-
cond_channels = cond_channels,
|
| 129 |
-
num_blocks = num_blocks,
|
| 130 |
-
num_heads = num_heads,
|
| 131 |
-
num_head_channels = num_head_channels,
|
| 132 |
-
mlp_ratio = mlp_ratio,
|
| 133 |
-
attn_mode = attn_mode,
|
| 134 |
-
window_num = window_num,
|
| 135 |
-
pcd_pe_mode = pcd_pe_mode,
|
| 136 |
-
use_fp16 = use_fp16,
|
| 137 |
-
use_checkpoint = use_checkpoint,
|
| 138 |
-
qk_rms_norm = qk_rms_norm,
|
| 139 |
-
qk_rms_norm_cross = qk_rms_norm_cross,
|
| 140 |
-
share_mod = share_mod,
|
| 141 |
-
)
|
| 142 |
-
self.out_proj = nn.Linear(model_channels, self.out_channels)
|
| 143 |
-
self._build_perturbation()
|
| 144 |
-
|
| 145 |
-
# weight init
|
| 146 |
-
self.initialize_weights()
|
| 147 |
-
if use_fp16:
|
| 148 |
-
self.convert_to_fp16()
|
| 149 |
|
| 150 |
-
|
| 151 |
-
|
| 152 |
-
|
| 153 |
-
|
| 154 |
-
|
| 155 |
-
|
| 156 |
-
|
| 157 |
-
|
| 158 |
-
|
| 159 |
-
|
| 160 |
-
|
| 161 |
-
'_rotation' : {'shape': (self.rep_config['num_gaussians'], 4), 'size': self.rep_config['num_gaussians'] * 4},
|
| 162 |
-
'_opacity' : {'shape': (self.rep_config['num_gaussians'], 1), 'size': self.rep_config['num_gaussians']},
|
| 163 |
-
}
|
| 164 |
-
if self.use_learned_offset_scale:
|
| 165 |
-
if self.use_per_offset:
|
| 166 |
-
self.layout['_offset_scale'] = {'shape': (self.rep_config['num_gaussians'], 1), 'size': self.rep_config['num_gaussians']}
|
| 167 |
-
else:
|
| 168 |
-
raise ValueError("use_per_offset must be True when use_learned_offset_scale is True")
|
| 169 |
-
start = 0
|
| 170 |
-
for k, v in self.layout.items():
|
| 171 |
-
v['range'] = (start, start + v['size'])
|
| 172 |
-
start += v['size']
|
| 173 |
-
out_channels = start
|
| 174 |
-
return out_channels
|
| 175 |
-
|
| 176 |
-
|
| 177 |
-
def _build_perturbation(self) -> None:
|
| 178 |
-
perturbation = [hammersley_sequence(3, i, self.rep_config['num_gaussians']) for i in range(self.rep_config['num_gaussians'])]
|
| 179 |
-
perturbation = torch.tensor(perturbation).float() * 2 - 1
|
| 180 |
-
perturbation = perturbation / self.rep_config['perturbe_size']
|
| 181 |
-
perturbation = torch.atanh(perturbation).to(self.device)
|
| 182 |
-
self.register_buffer('points_offset_perturbation', perturbation)
|
| 183 |
-
|
| 184 |
-
if self.use_learned_offset_scale:
|
| 185 |
-
base_offset_scale = torch.tensor(self.rep_config['offset_scale'])
|
| 186 |
-
base_offset_scale = torch.log(torch.exp(base_offset_scale) - 1.0) # inverse softplus
|
| 187 |
-
self.register_buffer('base_offset_scale', base_offset_scale)
|
| 188 |
-
|
| 189 |
-
def _get_offset(self, h):
|
| 190 |
-
B = h.shape[0]
|
| 191 |
-
if self.use_learned_offset_scale:
|
| 192 |
-
_offset_scale = h[:,:, self.layout['_offset_scale']['range'][0]:self.layout['_offset_scale']['range'][1]].reshape(B, -1, *self.layout['_offset_scale']['shape'])
|
| 193 |
-
_offset_scale = F.softplus(_offset_scale+self.base_offset_scale)
|
| 194 |
-
|
| 195 |
-
offset = h[:,:, self.layout['_xyz']['range'][0]:self.layout['_xyz']['range'][1]].reshape(B, -1, *self.layout['_xyz']['shape'])
|
| 196 |
-
offset = offset * self.rep_config['lr']['_xyz']
|
| 197 |
-
if self.rep_config['perturb_offset']:
|
| 198 |
-
offset = offset + self.points_offset_perturbation
|
| 199 |
-
offset = torch.tanh(offset) * 0.5 * self.rep_config['perturbe_size']
|
| 200 |
-
if self.use_learned_offset_scale:
|
| 201 |
-
offset = offset * _offset_scale
|
| 202 |
-
else:
|
| 203 |
-
offset = offset * self.rep_config['offset_scale']
|
| 204 |
-
return offset
|
| 205 |
-
|
| 206 |
|
| 207 |
-
|
| 208 |
-
|
| 209 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 210 |
|
| 211 |
-
|
| 212 |
-
|
| 213 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 214 |
|
| 215 |
-
|
| 216 |
-
|
| 217 |
-
|
| 218 |
-
|
| 219 |
-
offset = self._get_offset(h['features'])
|
| 220 |
-
h = h["features"]
|
| 221 |
-
ret = []
|
| 222 |
-
for i in range(h.shape[0]):
|
| 223 |
-
representation = Gaussian(
|
| 224 |
-
sh_degree=0,
|
| 225 |
-
aabb=[-0.5, -0.5, -0.5, 1.0, 1.0, 1.0],
|
| 226 |
-
mininum_kernel_size = self.rep_config['3d_filter_kernel_size'],
|
| 227 |
-
scaling_bias = self.rep_config['scaling_bias'],
|
| 228 |
-
opacity_bias = self.rep_config['opacity_bias'],
|
| 229 |
-
scaling_activation = self.rep_config['scaling_activation'],
|
| 230 |
-
device = h.device,
|
| 231 |
-
)
|
| 232 |
-
_x = x[i,:,None,:]
|
| 233 |
|
| 234 |
-
for k, v in self.layout.items():
|
| 235 |
-
if k == '_xyz':
|
| 236 |
-
_xyz = offset[i] + _x # NOTE: based on x data range == [0, 1]
|
| 237 |
-
setattr(representation, k, _xyz.flatten(0, 1))
|
| 238 |
-
elif k == '_xyz_center':
|
| 239 |
-
continue
|
| 240 |
-
elif k == '_offset_scale':
|
| 241 |
-
continue
|
| 242 |
-
else:
|
| 243 |
-
feats = h[i][:, v['range'][0]:v['range'][1]].reshape(-1, *v['shape']).flatten(0, 1)
|
| 244 |
-
feats = feats * self.rep_config['lr'][k]
|
| 245 |
-
setattr(representation, k, feats)
|
| 246 |
-
ret.append(representation)
|
| 247 |
-
return ret
|
| 248 |
-
|
| 249 |
-
def forward(self, x = None, cond = None) -> TensorDict:
|
| 250 |
-
h = super().forward(x, cond)
|
| 251 |
-
|
| 252 |
-
h = F.layer_norm(h, h.shape[-1:])
|
| 253 |
-
h_out = self.out_proj(h)
|
| 254 |
-
return TensorDict({
|
| 255 |
-
'features': h_out
|
| 256 |
-
}, batch_size=h_out.shape[:1], device=h.device)
|
| 257 |
|
| 258 |
-
|
| 259 |
-
|
| 260 |
-
|
| 261 |
-
|
| 262 |
-
|
| 263 |
-
|
| 264 |
-
|
| 265 |
-
|
| 266 |
-
|
| 267 |
-
use_fp16: bool = False,
|
| 268 |
-
use_checkpoint: bool = False,
|
| 269 |
-
share_mod: bool = False,
|
| 270 |
-
qk_rms_norm_cross: bool = True,
|
| 271 |
-
*,
|
| 272 |
-
pcd_pe_mode: Literal["pcd_ape", "pcd_ape_v2"] = "pcd_ape_v2",
|
| 273 |
-
**kwargs,
|
| 274 |
-
):
|
| 275 |
-
super().__init__(
|
| 276 |
-
in_channels = model_channels, # pe embedded
|
| 277 |
-
model_channels = model_channels,
|
| 278 |
-
cond_channels = cond_channels,
|
| 279 |
-
num_blocks = num_blocks,
|
| 280 |
-
num_heads = num_heads,
|
| 281 |
-
num_head_channels = num_head_channels,
|
| 282 |
-
mlp_ratio = mlp_ratio,
|
| 283 |
-
use_fp16 = use_fp16,
|
| 284 |
-
use_checkpoint = use_checkpoint,
|
| 285 |
-
share_mod = share_mod,
|
| 286 |
-
qk_rms_norm_cross = qk_rms_norm_cross,
|
| 287 |
-
)
|
| 288 |
-
self.out_proj = nn.Linear(self.model_channels, 2 ** 3) # to 8 logits
|
| 289 |
-
|
| 290 |
-
self.in_proj = nn.Linear(3, self.model_channels)
|
| 291 |
-
self.pcd_pe_mode = pcd_pe_mode
|
| 292 |
-
if pcd_pe_mode == "pcd_ape":
|
| 293 |
-
self.pos_embedder = PcdAbsolutePositionEmbedder(
|
| 294 |
-
channels=model_channels,
|
| 295 |
-
in_channels=3,
|
| 296 |
-
)
|
| 297 |
-
elif pcd_pe_mode == "pcd_ape_v2":
|
| 298 |
-
self.pos_embedder = PcdAbsolutePositionEmbedderv2(
|
| 299 |
-
channels=model_channels,
|
| 300 |
-
in_channels=3,
|
| 301 |
-
)
|
| 302 |
-
else:
|
| 303 |
-
self.pos_embedder = None
|
| 304 |
-
|
| 305 |
-
# weight init
|
| 306 |
-
self.initialize_weights()
|
| 307 |
-
if use_fp16:
|
| 308 |
-
self.convert_to_fp16()
|
| 309 |
-
|
| 310 |
-
def initialize_weights(self) -> None:
|
| 311 |
-
super().initialize_weights()
|
| 312 |
-
# zero-out output layers:
|
| 313 |
-
nn.init.constant_(self.out_proj.weight, 0)
|
| 314 |
-
nn.init.constant_(self.out_proj.bias, 0)
|
| 315 |
-
|
| 316 |
-
def forward(self, x: torch.Tensor, l: torch.Tensor, cond: Optional[torch.Tensor]= None, l2: Optional[torch.Tensor] = None) -> torch.Tensor:
|
| 317 |
-
"""
|
| 318 |
-
Args:
|
| 319 |
-
x: query points voxel centers, [B(=1), L, 3]
|
| 320 |
-
l: query points level, [B(=1)]
|
| 321 |
-
cond: [B(=1), Lq, C] the latent features as condition
|
| 322 |
-
Returns:
|
| 323 |
-
list of Gaussian representations
|
| 324 |
-
"""
|
| 325 |
-
B, L, C = x.shape
|
| 326 |
-
h = self.in_proj(x)
|
| 327 |
-
h = h + self.pos_embedder(x.view(-1,3)).view(B, L, -1) if hasattr(self, 'pos_embedder') else h
|
| 328 |
-
# FIXME: elegant l2 log scaling
|
| 329 |
-
if l2 is not None:
|
| 330 |
-
l2 = torch.log2(l2)
|
| 331 |
-
h = super().forward(h, l, cond, l2)
|
| 332 |
-
h = h.type(cond.dtype)
|
| 333 |
-
h = F.layer_norm(h, h.shape[-1:])
|
| 334 |
-
logits = self.out_proj(h)
|
| 335 |
-
probs = torch.softmax(logits, dim=-1)
|
| 336 |
-
return TensorDict({'logits': logits, 'probs': probs}, batch_size=logits.shape[:1], device=logits.device)
|
| 337 |
|
| 338 |
-
|
| 339 |
-
|
| 340 |
-
|
| 341 |
-
|
| 342 |
-
|
| 343 |
-
|
| 344 |
-
|
| 345 |
-
|
| 346 |
-
temperature: temperature of the sampling
|
| 347 |
-
Returns:
|
| 348 |
-
points: sampled points, [B, num_points, 3]
|
| 349 |
-
log_probs: log probabilities of the sampled points, [B, num_points]
|
| 350 |
-
"""
|
| 351 |
-
B = cond.shape[0]
|
| 352 |
-
device = cond.device
|
| 353 |
-
child_offset = torch.tensor([[i, j, k] for k in [0,1] for j in [0,1] for i in [0,1]], dtype=torch.long, device=device)
|
| 354 |
-
|
| 355 |
-
prev_coords_int = torch.zeros(B, 1, 3, dtype=torch.long, device=device) # [B, 1, 3]
|
| 356 |
-
prev_counts = torch.full((B, 1), num_points, dtype=torch.long, device=device) # [B, 1]
|
| 357 |
-
prev_log_probs = torch.zeros(B, 1, dtype=torch.float32, device=device) # [B, 1]
|
| 358 |
-
|
| 359 |
-
batch_indices_range = torch.arange(B, device=device).unsqueeze(1)
|
| 360 |
-
num_tensor = torch.full((B,), num_points, dtype=torch.long, device=device)
|
| 361 |
|
| 362 |
-
|
| 363 |
-
|
| 364 |
-
|
| 365 |
-
|
| 366 |
-
|
| 367 |
-
|
| 368 |
-
|
| 369 |
-
|
| 370 |
-
|
| 371 |
-
|
| 372 |
-
|
| 373 |
-
|
| 374 |
-
|
| 375 |
-
|
| 376 |
-
|
| 377 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 378 |
|
| 379 |
-
|
| 380 |
-
|
| 381 |
-
|
| 382 |
-
|
| 383 |
-
# mask out and padding
|
| 384 |
-
# Vectorized implementation
|
| 385 |
-
mask = sampled > 0 # [B, K]
|
| 386 |
-
valid_counts = mask.sum(dim=1) # [B]
|
| 387 |
-
max_valid = valid_counts.max().item()
|
| 388 |
-
|
| 389 |
-
# Create scatter indices
|
| 390 |
-
scatter_indices = mask.cumsum(dim=1) - 1
|
| 391 |
-
valid_mask = mask # just mask
|
| 392 |
-
|
| 393 |
-
valid_scatter_indices = scatter_indices[valid_mask] # [Total_Valid]
|
| 394 |
-
|
| 395 |
-
# Batch indices
|
| 396 |
-
valid_batch_indices = batch_indices_range.expand_as(mask)[valid_mask]
|
| 397 |
-
|
| 398 |
-
next_prev_coords_int = torch.zeros(B, max_valid, 3, dtype=child_coords_int.dtype, device=device)
|
| 399 |
-
next_prev_coords_int[valid_batch_indices, valid_scatter_indices] = child_coords_int[valid_mask]
|
| 400 |
-
|
| 401 |
-
next_prev_counts = torch.zeros(B, max_valid, dtype=sampled.dtype, device=device)
|
| 402 |
-
next_prev_counts[valid_batch_indices, valid_scatter_indices] = sampled[valid_mask]
|
| 403 |
-
|
| 404 |
-
next_prev_log_probs = torch.zeros(B, max_valid, dtype=prev_log_probs.dtype, device=device)
|
| 405 |
-
next_prev_log_probs[valid_batch_indices, valid_scatter_indices] = (prev_log_probs_expanded + pred_log_probs)[valid_mask]
|
| 406 |
-
|
| 407 |
-
prev_coords_int = next_prev_coords_int
|
| 408 |
-
prev_counts = next_prev_counts
|
| 409 |
-
prev_log_probs = next_prev_log_probs
|
| 410 |
|
| 411 |
-
|
| 412 |
-
|
| 413 |
-
|
| 414 |
-
|
| 415 |
-
|
| 416 |
-
|
| 417 |
-
coords_norm = (coords_int.to(torch.float32) + torch.rand_like(coords_int, dtype=torch.float32)) / res
|
| 418 |
|
| 419 |
-
|
| 420 |
-
|
| 421 |
-
|
| 422 |
-
|
| 423 |
-
|
| 424 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""The two decoders that turn a latent [B, 8192, 16] into Gaussians.
|
| 2 |
+
|
| 3 |
+
OctreeProbabilityFixedlenDecoder places the anchor points: starting from the unit cube, it predicts how the points
|
| 4 |
+
split among the 8 children of every occupied cell, level by level, then scatters each point inside its leaf cell.
|
| 5 |
+
GaussianFixedlenDecoder turns every anchor point into 32 Gaussians (offsets, colour, scale, rotation, opacity)."""
|
| 6 |
+
from typing import List
|
| 7 |
+
|
| 8 |
import torch
|
| 9 |
import torch.nn as nn
|
| 10 |
import torch.nn.functional as F
|
| 11 |
+
|
| 12 |
from ..gaussian import Gaussian
|
| 13 |
+
from .layers import (ModulatedTransformerCrossOnlyBlock, PcdAbsolutePositionEmbedderv2, SinusoidalEmbedder,
|
| 14 |
+
TransformerCrossBlock, convert_module_to_f16)
|
|
|
|
| 15 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 16 |
|
| 17 |
+
class OctreeProbabilityFixedlenDecoder(nn.Module):
|
| 18 |
+
def __init__(self, model_channels: int, cond_channels: int, num_blocks: int, num_heads: int,
|
| 19 |
+
mlp_ratio: float = 4.0):
|
| 20 |
+
super().__init__()
|
| 21 |
+
self.dtype = torch.float16
|
| 22 |
+
self.in_proj = nn.Linear(3, model_channels)
|
| 23 |
+
self.pos_embedder = PcdAbsolutePositionEmbedderv2(channels=model_channels, in_channels=3)
|
| 24 |
+
self.input_layer = nn.Linear(model_channels, model_channels)
|
| 25 |
+
self.l_embedder = SinusoidalEmbedder(model_channels, max_period=1024, two_pi=True)
|
| 26 |
+
self.adaLN_modulation = nn.Sequential(nn.SiLU(), nn.Linear(model_channels, 6 * model_channels, bias=True))
|
| 27 |
+
self.blocks = nn.ModuleList([
|
| 28 |
+
ModulatedTransformerCrossOnlyBlock(model_channels, ctx_channels=cond_channels, num_heads=num_heads,
|
| 29 |
+
mlp_ratio=mlp_ratio)
|
| 30 |
+
for _ in range(num_blocks)
|
| 31 |
+
])
|
| 32 |
+
self.out_proj = nn.Linear(model_channels, 2 ** 3)
|
| 33 |
+
self.blocks.apply(convert_module_to_f16)
|
| 34 |
|
| 35 |
+
def forward(self, x: torch.Tensor, level: torch.Tensor, cond: torch.Tensor) -> torch.Tensor:
|
| 36 |
+
"""x: cell centres [B, L, 3] in [0, 1]; level: the cells' octree resolution [B]; cond: latent [B, Lc, C].
|
| 37 |
+
Returns the logits [B, L, 8] of how each cell's points split among its 8 children."""
|
| 38 |
+
B, L, _ = x.shape
|
| 39 |
+
h = self.in_proj(x) + self.pos_embedder(x.view(-1, 3)).view(B, L, -1)
|
| 40 |
+
h = self.input_layer(h)
|
| 41 |
+
level_emb = self.adaLN_modulation(self.l_embedder(level.type(h.dtype))).type(self.dtype)
|
| 42 |
+
out = h.type(self.dtype)
|
| 43 |
+
context = cond.type(self.dtype)
|
| 44 |
+
for block in self.blocks:
|
| 45 |
+
out = block(out, level_emb, context)
|
| 46 |
+
out = out.type(h.dtype).type(cond.dtype)
|
| 47 |
+
return self.out_proj(F.layer_norm(out, out.shape[-1:]))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 48 |
|
| 49 |
+
@staticmethod
|
| 50 |
+
@torch.no_grad()
|
| 51 |
+
def sample(model, cond: torch.Tensor, num_points: int, level: int) -> torch.Tensor:
|
| 52 |
+
"""Place num_points anchor points [B, num_points, 3] in [0, 1]^3, descending the octree to ``level``."""
|
| 53 |
+
B = cond.shape[0]
|
| 54 |
+
device = cond.device
|
| 55 |
+
child_offset = torch.tensor([[i, j, k] for k in [0, 1] for j in [0, 1] for i in [0, 1]], dtype=torch.long,
|
| 56 |
+
device=device)
|
| 57 |
+
prev_coords_int = torch.zeros(B, 1, 3, dtype=torch.long, device=device)
|
| 58 |
+
prev_counts = torch.full((B, 1), num_points, dtype=torch.long, device=device)
|
| 59 |
+
batch_indices_range = torch.arange(B, device=device).unsqueeze(1)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 60 |
|
| 61 |
+
for l in range(1, level + 1):
|
| 62 |
+
res_p = 1 << (l - 1)
|
| 63 |
+
parent_coords_norm = (prev_coords_int.to(torch.float32) + 0.5) / res_p
|
| 64 |
+
res_tensor = torch.full((B,), 1 << l, dtype=torch.long, device=device)
|
| 65 |
+
pred_probs = torch.softmax(model(parent_coords_norm, res_tensor, cond), dim=-1) # [B, Np, 8]
|
| 66 |
+
sampled = systematic_sample(pred_probs, prev_counts).flatten(1, 2) # [B, Np*8]
|
| 67 |
+
child_coords_int = (prev_coords_int[:, :, None, :] * 2 + child_offset[None, None, :, :]).flatten(1, 2)
|
| 68 |
|
| 69 |
+
# keep the children that received points, packed to the front
|
| 70 |
+
mask = sampled > 0
|
| 71 |
+
max_valid = mask.sum(dim=1).max().item()
|
| 72 |
+
valid_scatter_indices = (mask.cumsum(dim=1) - 1)[mask]
|
| 73 |
+
valid_batch_indices = batch_indices_range.expand_as(mask)[mask]
|
| 74 |
+
prev_coords_int = torch.zeros(B, max_valid, 3, dtype=child_coords_int.dtype, device=device)
|
| 75 |
+
prev_coords_int[valid_batch_indices, valid_scatter_indices] = child_coords_int[mask]
|
| 76 |
+
prev_counts = torch.zeros(B, max_valid, dtype=sampled.dtype, device=device)
|
| 77 |
+
prev_counts[valid_batch_indices, valid_scatter_indices] = sampled[mask]
|
| 78 |
|
| 79 |
+
# each point lands at a uniformly random position inside its leaf cell
|
| 80 |
+
coords_int = torch.repeat_interleave(prev_coords_int.flatten(0, 1), prev_counts.flatten(0, 1),
|
| 81 |
+
dim=0).reshape(B, num_points, -1)
|
| 82 |
+
return (coords_int.to(torch.float32) + torch.rand_like(coords_int, dtype=torch.float32)) / (1 << level)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 83 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 84 |
|
| 85 |
+
@torch.no_grad()
|
| 86 |
+
def systematic_sample(probs: torch.Tensor, counts: torch.Tensor) -> torch.Tensor:
|
| 87 |
+
"""Split counts[...] items among the P bins of probs[..., P] by systematic resampling -> [..., P] counts."""
|
| 88 |
+
batch_shape = counts.shape
|
| 89 |
+
B = counts.numel()
|
| 90 |
+
P = probs.size(-1)
|
| 91 |
+
device = probs.device
|
| 92 |
+
probs = probs.view(B, P)
|
| 93 |
+
counts = counts.view(B)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 94 |
|
| 95 |
+
# normalise, with a uniform fallback for zero-sum rows
|
| 96 |
+
probs = probs.to(torch.float32).clamp_min_(0)
|
| 97 |
+
row_sums = probs.sum(1, keepdim=True)
|
| 98 |
+
zero_mask = row_sums.eq(0)
|
| 99 |
+
probs = probs / row_sums.clamp_min_(1)
|
| 100 |
+
if zero_mask.any():
|
| 101 |
+
probs = probs.clone()
|
| 102 |
+
probs[zero_mask.expand_as(probs)] = 1.0 / P
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 103 |
|
| 104 |
+
counts = counts.to(device=device, dtype=torch.long)
|
| 105 |
+
out = torch.zeros(B, P, dtype=torch.long, device=device)
|
| 106 |
+
cdf = probs.cumsum(dim=1).clamp(max=1.0 - 1e-12)
|
| 107 |
+
unique_n, inv = counts.unique(sorted=False, return_inverse=True)
|
| 108 |
+
for index, n in enumerate(unique_n.tolist()):
|
| 109 |
+
if n == 0:
|
| 110 |
+
continue
|
| 111 |
+
rows = (inv == index).nonzero(as_tuple=False).squeeze(1)
|
| 112 |
+
r = rows.numel()
|
| 113 |
+
u0 = torch.rand(r, 1, device=device) / float(n) # random offset in [0, 1/n)
|
| 114 |
+
grid = torch.arange(n, device=device, dtype=torch.float32)[None, :] / float(n) # stratified points
|
| 115 |
+
us = (u0 + grid).clamp(max=1.0 - 1e-12)
|
| 116 |
+
idx = torch.searchsorted(cdf.index_select(0, rows), us).clamp_max(P - 1)
|
| 117 |
+
buf = torch.zeros(r, P, dtype=torch.float32, device=device)
|
| 118 |
+
buf.scatter_add_(1, idx, torch.ones_like(idx, dtype=buf.dtype))
|
| 119 |
+
out.index_copy_(0, rows, buf.to(torch.long))
|
| 120 |
+
return out.view(*batch_shape, P)
|
| 121 |
+
|
| 122 |
+
|
| 123 |
+
class GaussianFixedlenDecoder(nn.Module):
|
| 124 |
+
def __init__(self, in_channels: int, model_channels: int, cond_channels: int, num_blocks: int, num_heads: int,
|
| 125 |
+
representation_config: dict, mlp_ratio: float = 4.0):
|
| 126 |
+
super().__init__()
|
| 127 |
+
self.dtype = torch.float16
|
| 128 |
+
self.rep_config = representation_config
|
| 129 |
+
per_anchor = representation_config['num_gaussians']
|
| 130 |
+
# output channels per anchor: offset (3), colour (3), scale (3), rotation (4), opacity (1), offset scale (1)
|
| 131 |
+
self.layout = {
|
| 132 |
+
'_xyz': (3,),
|
| 133 |
+
'_features_dc': (1, 3),
|
| 134 |
+
'_scaling': (3,),
|
| 135 |
+
'_rotation': (4,),
|
| 136 |
+
'_opacity': (1,),
|
| 137 |
+
'_offset_scale': (1,),
|
| 138 |
+
}
|
| 139 |
+
start = 0
|
| 140 |
+
self.ranges = {}
|
| 141 |
+
for name, shape in self.layout.items():
|
| 142 |
+
size = per_anchor * int(torch.tensor(shape).prod())
|
| 143 |
+
self.ranges[name] = (start, start + size)
|
| 144 |
+
start += size
|
| 145 |
+
|
| 146 |
+
self.in_proj = nn.Linear(in_channels, model_channels)
|
| 147 |
+
self.pos_embedder = PcdAbsolutePositionEmbedderv2(channels=model_channels, in_channels=3)
|
| 148 |
+
self.input_layer = nn.Linear(model_channels, model_channels)
|
| 149 |
+
self.blocks = nn.ModuleList([
|
| 150 |
+
TransformerCrossBlock(model_channels, ctx_channels=cond_channels, num_heads=num_heads, mlp_ratio=mlp_ratio)
|
| 151 |
+
for _ in range(num_blocks)
|
| 152 |
+
])
|
| 153 |
+
self.out_proj = nn.Linear(model_channels, start)
|
| 154 |
+
# fixed per-Gaussian offset pattern and the base offset scale, stored with the weights
|
| 155 |
+
self.register_buffer('points_offset_perturbation', torch.zeros(per_anchor, 3))
|
| 156 |
+
self.register_buffer('base_offset_scale', torch.zeros(()))
|
| 157 |
+
self.blocks.apply(convert_module_to_f16)
|
| 158 |
+
|
| 159 |
+
def forward(self, points: torch.Tensor, cond: torch.Tensor) -> torch.Tensor:
|
| 160 |
+
"""points: anchors [B, L, 3]; cond: latent [B, Lc, C] -> per-anchor outputs [B, L, 32 x 15]"""
|
| 161 |
+
B, L, C = points.shape
|
| 162 |
+
h = self.in_proj(points) + self.pos_embedder(points.view(-1, C)).view(B, L, -1)
|
| 163 |
+
h = self.input_layer(h)
|
| 164 |
+
out = h.type(self.dtype)
|
| 165 |
+
context = cond.type(self.dtype)
|
| 166 |
+
for block in self.blocks:
|
| 167 |
+
out = block(out, context)
|
| 168 |
+
out = out.type(h.dtype).type(cond.dtype)
|
| 169 |
+
return self.out_proj(F.layer_norm(out, out.shape[-1:]))
|
| 170 |
|
| 171 |
+
def _slice(self, features, name):
|
| 172 |
+
start, end = self.ranges[name]
|
| 173 |
+
return features[:, :, start:end].reshape(features.shape[0], -1, self.rep_config['num_gaussians'],
|
| 174 |
+
*self.layout[name])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 175 |
|
| 176 |
+
def to_representation(self, points: torch.Tensor, features: torch.Tensor) -> List[Gaussian]:
|
| 177 |
+
"""Anchors [B, L, 3] and per-anchor outputs [B, L, D] -> one Gaussian object per batch element."""
|
| 178 |
+
config = self.rep_config
|
| 179 |
+
offset_scale = F.softplus(self._slice(features, '_offset_scale') + self.base_offset_scale)
|
| 180 |
+
offset = self._slice(features, '_xyz') * config['lr']['_xyz'] + self.points_offset_perturbation
|
| 181 |
+
offset = torch.tanh(offset) * 0.5 * config['perturbe_size'] * offset_scale
|
|
|
|
| 182 |
|
| 183 |
+
objects = []
|
| 184 |
+
for i in range(features.shape[0]):
|
| 185 |
+
raw = {name: (self._slice(features[i:i + 1], name)[0].flatten(0, 1) * config['lr'][name])
|
| 186 |
+
for name in ('_features_dc', '_scaling', '_rotation', '_opacity')}
|
| 187 |
+
objects.append(Gaussian(
|
| 188 |
+
raw_xyz=(offset[i] + points[i, :, None, :]).flatten(0, 1),
|
| 189 |
+
features_dc=raw['_features_dc'], raw_scaling=raw['_scaling'], raw_rotation=raw['_rotation'],
|
| 190 |
+
raw_opacity=raw['_opacity'], scaling_bias=config['scaling_bias'],
|
| 191 |
+
opacity_bias=config['opacity_bias'], minimum_kernel_size=config['3d_filter_kernel_size']))
|
| 192 |
+
return objects
|
idol/models/dit.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
| 1 |
# Adapted from https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/transformers/transformer_wan.py
|
| 2 |
# The original license is as follows:
|
| 3 |
-
#
|
| 4 |
# Copyright 2025 The Wan Team and The HuggingFace Team. All rights reserved.
|
| 5 |
#
|
| 6 |
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -14,415 +14,109 @@
|
|
| 14 |
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 15 |
# See the License for the specific language governing permissions and
|
| 16 |
# limitations under the License.
|
| 17 |
-
|
| 18 |
-
|
| 19 |
import torch
|
| 20 |
import torch.nn as nn
|
| 21 |
import torch.nn.functional as F
|
| 22 |
-
import numpy as np
|
| 23 |
from tensordict import TensorDict
|
| 24 |
|
| 25 |
-
from .
|
| 26 |
-
|
| 27 |
-
from ..modules.attention.modules import RePo3DRotaryEmbedding, Rotary3DPoseEmbedding
|
| 28 |
-
from ..modules.utils import convert_module_to_f16, convert_module_to_f32
|
| 29 |
-
from .transformer_base import PcdAbsolutePositionEmbedder
|
| 30 |
-
|
| 31 |
-
class TimestepEmbedder(nn.Module):
|
| 32 |
-
"""
|
| 33 |
-
Embeds scalar timesteps into vector representations.
|
| 34 |
-
"""
|
| 35 |
-
def __init__(self, hidden_size, frequency_embedding_size=256):
|
| 36 |
-
super().__init__()
|
| 37 |
-
self.mlp = nn.Sequential(
|
| 38 |
-
nn.Linear(frequency_embedding_size, hidden_size, bias=True),
|
| 39 |
-
nn.SiLU(),
|
| 40 |
-
nn.Linear(hidden_size, hidden_size, bias=True),
|
| 41 |
-
)
|
| 42 |
-
self.frequency_embedding_size = frequency_embedding_size
|
| 43 |
-
|
| 44 |
-
@staticmethod
|
| 45 |
-
def timestep_embedding(t, dim, max_period=10000):
|
| 46 |
-
"""
|
| 47 |
-
Create sinusoidal timestep embeddings.
|
| 48 |
-
|
| 49 |
-
Args:
|
| 50 |
-
t: a 1-D Tensor of N indices, one per batch element.
|
| 51 |
-
These may be fractional.
|
| 52 |
-
dim: the dimension of the output.
|
| 53 |
-
max_period: controls the minimum frequency of the embeddings.
|
| 54 |
-
|
| 55 |
-
Returns:
|
| 56 |
-
an (N, D) Tensor of positional embeddings.
|
| 57 |
-
"""
|
| 58 |
-
# https://github.com/openai/glide-text2im/blob/main/glide_text2im/nn.py
|
| 59 |
-
half = dim // 2
|
| 60 |
-
freqs = torch.exp(
|
| 61 |
-
-np.log(max_period) * torch.arange(start=0, end=half, dtype=torch.float32) / half
|
| 62 |
-
).to(device=t.device)
|
| 63 |
-
args = t[:, None].float() * freqs[None]
|
| 64 |
-
embedding = torch.cat([torch.cos(args), torch.sin(args)], dim=-1)
|
| 65 |
-
if dim % 2:
|
| 66 |
-
embedding = torch.cat([embedding, torch.zeros_like(embedding[:, :1])], dim=-1)
|
| 67 |
-
return embedding
|
| 68 |
|
| 69 |
-
def forward(self, t):
|
| 70 |
-
t_freq = self.timestep_embedding(t, self.frequency_embedding_size)
|
| 71 |
-
t_emb = self.mlp(t_freq)
|
| 72 |
-
return t_emb
|
| 73 |
|
| 74 |
class LatentSeqMMFlowModel(nn.Module):
|
| 75 |
-
"""
|
| 76 |
-
Latent Sequence Multimodal Flow Model (Unified MMDiT).
|
| 77 |
-
Adapts ZImageTransformer2DModel architecture for 3D-Image unification.
|
| 78 |
-
MMDiT adapted from diffusers/models/transformers/transformer_z_image.py
|
| 79 |
-
REPO adapted from https://github.com/SakanaAI/repo
|
| 80 |
-
"""
|
| 81 |
def __init__(
|
| 82 |
self,
|
| 83 |
q_token_length: int,
|
| 84 |
in_channels: int,
|
|
|
|
| 85 |
model_channels: int,
|
| 86 |
cond_channels: int,
|
| 87 |
-
|
| 88 |
num_blocks: int,
|
| 89 |
num_refiner_blocks: int = 2,
|
| 90 |
-
num_heads:
|
| 91 |
-
num_head_channels:
|
| 92 |
-
cam_channels: Optional[int] = None,
|
| 93 |
-
cond2_channels: Optional[int] = None,
|
| 94 |
mlp_ratio: float = 4,
|
| 95 |
-
pe_mode: Literal["3d_ot_rope", "3d_ot_repo", "3d_ot_ape", "repo", "learnable", None] = "3d_ot_ape",
|
| 96 |
-
use_fp16: bool = False,
|
| 97 |
-
use_checkpoint: bool = False,
|
| 98 |
-
share_mod: bool = True,
|
| 99 |
-
qk_rms_norm: bool = False,
|
| 100 |
-
use_shift_table: bool = False,
|
| 101 |
):
|
| 102 |
super().__init__()
|
| 103 |
self.q_token_length = q_token_length
|
| 104 |
self.in_channels = in_channels
|
| 105 |
self.cam_channels = cam_channels
|
| 106 |
-
self.
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
self.num_refiner_blocks = num_refiner_blocks
|
| 112 |
-
self.num_heads = num_heads or model_channels // num_head_channels
|
| 113 |
-
self.mlp_ratio = mlp_ratio
|
| 114 |
-
self.pe_mode = pe_mode
|
| 115 |
-
self.use_fp16 = use_fp16
|
| 116 |
-
self.use_checkpoint = use_checkpoint
|
| 117 |
-
self.share_mod = share_mod
|
| 118 |
-
self.qk_rms_norm = qk_rms_norm
|
| 119 |
-
self.dtype = torch.float16 if use_fp16 else torch.float32
|
| 120 |
-
self.use_shift_table = use_shift_table
|
| 121 |
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
nn.SiLU(),
|
| 126 |
-
nn.Linear(model_channels, 6 * model_channels, bias=True)
|
| 127 |
-
)
|
| 128 |
|
|
|
|
|
|
|
| 129 |
self.input_layer = nn.Linear(in_channels, model_channels)
|
| 130 |
self.cond_embedder = nn.Linear(cond_channels, model_channels)
|
| 131 |
-
|
| 132 |
-
self.cond_embedder2 = nn.Linear(cond2_channels, model_channels)
|
| 133 |
-
else:
|
| 134 |
-
self.cond_embedder2 = None
|
| 135 |
-
|
| 136 |
-
# Positional Embeddings
|
| 137 |
-
self.use_rope = (self.pe_mode in ["3d_ot_rope", "3d_ot_repo", "repo"])
|
| 138 |
-
if pe_mode == "3d_ot_rope":
|
| 139 |
-
sobol_seq = torch.quasirandom.SobolEngine(dimension=3, scramble=True, seed=123).draw(q_token_length)
|
| 140 |
-
self.register_buffer("pos_pe", sobol_seq[None])
|
| 141 |
-
self.pos_embedder = Rotary3DPoseEmbedding(num_head_channels)
|
| 142 |
-
self.cond_pos_embedder = None
|
| 143 |
-
elif pe_mode == "3d_ot_ape":
|
| 144 |
-
sobol_seq = torch.quasirandom.SobolEngine(dimension=3, scramble=True, seed=123).draw(q_token_length)
|
| 145 |
-
self.register_buffer("pos_pe", sobol_seq[None])
|
| 146 |
-
self.pos_embedder = PcdAbsolutePositionEmbedder(model_channels)
|
| 147 |
-
self.cond_pos_embedder = None
|
| 148 |
-
elif pe_mode == "repo":
|
| 149 |
-
self.pos_embedder = None
|
| 150 |
-
self.cond_pos_embedder = None
|
| 151 |
-
self.noise_repo_layers = nn.ModuleList([
|
| 152 |
-
RePo3DRotaryEmbedding(model_channels, num_heads=self.num_heads, head_dim=num_head_channels)
|
| 153 |
-
for _ in range(num_refiner_blocks)
|
| 154 |
-
])
|
| 155 |
-
self.context_repo_layers = nn.ModuleList([
|
| 156 |
-
RePo3DRotaryEmbedding(model_channels, num_heads=self.num_heads, head_dim=num_head_channels)
|
| 157 |
-
for _ in range(num_refiner_blocks)
|
| 158 |
-
])
|
| 159 |
-
self.repo_layers = nn.ModuleList([
|
| 160 |
-
RePo3DRotaryEmbedding(model_channels, num_heads=self.num_heads, head_dim=num_head_channels)
|
| 161 |
-
for _ in range(num_blocks)
|
| 162 |
-
])
|
| 163 |
-
elif pe_mode == "3d_ot_repo":
|
| 164 |
-
sobol_seq = torch.quasirandom.SobolEngine(dimension=3, scramble=True, seed=123).draw(q_token_length)
|
| 165 |
-
self.register_buffer("pos_pe", sobol_seq[None])
|
| 166 |
-
self.pos_embedder = PcdAbsolutePositionEmbedder(model_channels)
|
| 167 |
-
self.cond_pos_embedder = None
|
| 168 |
-
self.noise_repo_layers = nn.ModuleList([
|
| 169 |
-
RePo3DRotaryEmbedding(model_channels, num_heads=self.num_heads, head_dim=num_head_channels)
|
| 170 |
-
for _ in range(num_refiner_blocks)
|
| 171 |
-
])
|
| 172 |
-
self.context_repo_layers = nn.ModuleList([
|
| 173 |
-
RePo3DRotaryEmbedding(model_channels, num_heads=self.num_heads, head_dim=num_head_channels)
|
| 174 |
-
for _ in range(num_refiner_blocks)
|
| 175 |
-
])
|
| 176 |
-
self.repo_layers = nn.ModuleList([
|
| 177 |
-
RePo3DRotaryEmbedding(model_channels, num_heads=self.num_heads, head_dim=num_head_channels)
|
| 178 |
-
for _ in range(num_blocks)
|
| 179 |
-
])
|
| 180 |
-
elif pe_mode == "learnable":
|
| 181 |
-
# Not fully implemented adaptation for learnable
|
| 182 |
-
self.pos_pe = None
|
| 183 |
-
self.pos_embedder = None
|
| 184 |
-
else:
|
| 185 |
-
self.pos_pe = None
|
| 186 |
-
self.pos_embedder = None
|
| 187 |
|
| 188 |
-
#
|
| 189 |
-
self.
|
| 190 |
-
|
| 191 |
-
|
| 192 |
-
|
| 193 |
-
|
| 194 |
-
use_checkpoint=self.use_checkpoint,
|
| 195 |
-
use_rope=self.use_rope,
|
| 196 |
-
qk_rms_norm=self.qk_rms_norm,
|
| 197 |
-
modulation=True,
|
| 198 |
-
share_mod=self.share_mod,
|
| 199 |
-
use_shift_table=self.use_shift_table,
|
| 200 |
-
)
|
| 201 |
-
for _ in range(num_refiner_blocks)
|
| 202 |
-
])
|
| 203 |
-
|
| 204 |
-
self.context_refiner = nn.ModuleList([
|
| 205 |
-
UnifiedTransformerBlock(
|
| 206 |
-
model_channels,
|
| 207 |
-
num_heads=self.num_heads,
|
| 208 |
-
mlp_ratio=self.mlp_ratio,
|
| 209 |
-
use_checkpoint=self.use_checkpoint,
|
| 210 |
-
use_rope=self.use_rope,
|
| 211 |
-
qk_rms_norm=self.qk_rms_norm,
|
| 212 |
-
modulation=False,
|
| 213 |
-
)
|
| 214 |
-
for _ in range(num_refiner_blocks)
|
| 215 |
-
])
|
| 216 |
-
if self.cam_channels is not None:
|
| 217 |
-
self.cam_refiner = MLP(
|
| 218 |
-
self.cam_channels,
|
| 219 |
-
model_channels,
|
| 220 |
-
model_channels,
|
| 221 |
-
mlp_layer_num=num_refiner_blocks,
|
| 222 |
-
)
|
| 223 |
-
|
| 224 |
-
self.blocks = nn.ModuleList([
|
| 225 |
-
UnifiedTransformerBlock(
|
| 226 |
-
model_channels,
|
| 227 |
-
num_heads=self.num_heads,
|
| 228 |
-
mlp_ratio=self.mlp_ratio,
|
| 229 |
-
use_checkpoint=self.use_checkpoint,
|
| 230 |
-
use_rope=self.use_rope,
|
| 231 |
-
qk_rms_norm=self.qk_rms_norm,
|
| 232 |
-
modulation=True,
|
| 233 |
-
share_mod=self.share_mod,
|
| 234 |
-
use_shift_table=self.use_shift_table,
|
| 235 |
-
)
|
| 236 |
-
for _ in range(num_blocks)
|
| 237 |
-
])
|
| 238 |
|
| 239 |
-
|
| 240 |
-
|
| 241 |
-
|
| 242 |
-
|
|
|
|
| 243 |
|
| 244 |
-
self.
|
| 245 |
-
|
| 246 |
-
|
| 247 |
-
|
| 248 |
-
self.initialize_weights()
|
| 249 |
-
if use_fp16:
|
| 250 |
-
self.convert_to_fp16()
|
| 251 |
|
| 252 |
-
@property
|
| 253 |
-
def device(self) -> torch.device:
|
| 254 |
-
return next(self.parameters()).device
|
| 255 |
-
|
| 256 |
-
def convert_to_fp16(self) -> None:
|
| 257 |
self.blocks.apply(convert_module_to_f16)
|
| 258 |
self.noise_refiner.apply(convert_module_to_f16)
|
| 259 |
self.context_refiner.apply(convert_module_to_f16)
|
| 260 |
|
| 261 |
-
def
|
| 262 |
-
self.blocks.apply(convert_module_to_f32)
|
| 263 |
-
self.noise_refiner.apply(convert_module_to_f32)
|
| 264 |
-
self.context_refiner.apply(convert_module_to_f32)
|
| 265 |
-
|
| 266 |
-
def initialize_weights(self) -> None:
|
| 267 |
-
def _basic_init(module):
|
| 268 |
-
if isinstance(module, nn.Linear):
|
| 269 |
-
torch.nn.init.xavier_uniform_(module.weight)
|
| 270 |
-
if module.bias is not None:
|
| 271 |
-
nn.init.constant_(module.bias, 0)
|
| 272 |
-
self.apply(_basic_init)
|
| 273 |
-
|
| 274 |
-
nn.init.normal_(self.t_embedder.mlp[0].weight, std=0.02)
|
| 275 |
-
nn.init.normal_(self.t_embedder.mlp[2].weight, std=0.02)
|
| 276 |
-
|
| 277 |
-
if self.share_mod:
|
| 278 |
-
nn.init.constant_(self.adaLN_modulation[-1].weight, 0)
|
| 279 |
-
nn.init.constant_(self.adaLN_modulation[-1].bias, 0)
|
| 280 |
-
else:
|
| 281 |
-
for block in self.blocks:
|
| 282 |
-
nn.init.constant_(block.adaLN_modulation[-1].weight, 0)
|
| 283 |
-
nn.init.constant_(block.adaLN_modulation[-1].bias, 0)
|
| 284 |
-
for block in self.noise_refiner:
|
| 285 |
-
nn.init.constant_(block.adaLN_modulation[-1].weight, 0)
|
| 286 |
-
nn.init.constant_(block.adaLN_modulation[-1].bias, 0)
|
| 287 |
-
|
| 288 |
-
if self.use_shift_table:
|
| 289 |
-
for block in self.blocks:
|
| 290 |
-
nn.init.constant_(block.shift_table, 0)
|
| 291 |
-
for block in self.noise_refiner:
|
| 292 |
-
nn.init.constant_(block.shift_table, 0)
|
| 293 |
-
nn.init.constant_(self.shift_table, 0)
|
| 294 |
-
|
| 295 |
-
if hasattr(self, 'repo_layers'):
|
| 296 |
-
for block in self.noise_repo_layers:
|
| 297 |
-
block.initialize_weights()
|
| 298 |
-
for block in self.context_repo_layers:
|
| 299 |
-
block.initialize_weights()
|
| 300 |
-
for block in self.repo_layers:
|
| 301 |
-
block.initialize_weights()
|
| 302 |
-
|
| 303 |
-
if self.cond_embedder2 is not None:
|
| 304 |
-
nn.init.constant_(self.cond_embedder2.weight, 0)
|
| 305 |
-
nn.init.constant_(self.cond_embedder2.bias, 0)
|
| 306 |
-
|
| 307 |
-
nn.init.constant_(self.out_layer.weight, 0)
|
| 308 |
-
nn.init.constant_(self.out_layer.bias, 0)
|
| 309 |
-
|
| 310 |
-
if self.cam_channels is not None:
|
| 311 |
-
nn.init.constant_(self.cam_out_layer.weight, 0)
|
| 312 |
-
nn.init.constant_(self.cam_out_layer.bias, 0)
|
| 313 |
-
|
| 314 |
-
def forward(
|
| 315 |
-
self,
|
| 316 |
-
x: TensorDict,
|
| 317 |
-
t: torch.Tensor,
|
| 318 |
-
cond: TensorDict,
|
| 319 |
-
points: Optional[torch.Tensor] = None,
|
| 320 |
-
return_mid_features: bool = False,
|
| 321 |
-
**kwargs,
|
| 322 |
-
) -> torch.Tensor:
|
| 323 |
"""
|
| 324 |
Args:
|
| 325 |
-
x: TensorDict with 'latent' [B,
|
| 326 |
-
|
|
|
|
|
|
|
|
|
|
| 327 |
"""
|
| 328 |
-
|
| 329 |
-
z = x['latent']
|
| 330 |
h_x = self.input_layer(z).type(self.dtype)
|
| 331 |
-
|
| 332 |
-
h_cond = self.cond_embedder(cond['feature1']) + self.cond_embedder2(cond['feature2'])
|
| 333 |
-
else:
|
| 334 |
-
h_cond = self.cond_embedder(cond['feature1'])
|
| 335 |
h_cond = h_cond.type(self.dtype)
|
| 336 |
-
|
| 337 |
-
t_emb = self.t_embedder(t)
|
| 338 |
-
if self.share_mod:
|
| 339 |
-
t_mod = self.adaLN_modulation(t_emb)
|
| 340 |
-
else:
|
| 341 |
-
t_mod = t_emb
|
| 342 |
-
t_mod = t_mod.type(self.dtype)
|
| 343 |
|
| 344 |
-
|
| 345 |
-
|
| 346 |
-
rope_cond = None
|
| 347 |
|
| 348 |
-
|
| 349 |
-
|
| 350 |
-
|
| 351 |
-
|
| 352 |
-
|
| 353 |
-
|
| 354 |
-
rope_cond = torch.ones(z.shape[0], h_cond.shape[1], 1, rope_x.shape[-1], device=z.device, dtype=rope_x.real.dtype)
|
| 355 |
-
rope_cond = torch.polar(rope_cond, torch.zeros_like(rope_cond))
|
| 356 |
-
|
| 357 |
-
parts = [rope_x, rope_cond]
|
| 358 |
-
if self.cam_channels is not None:
|
| 359 |
-
rope_cam = torch.ones(z.shape[0], x['camera'].shape[1], 1, rope_x.shape[-1], device=z.device, dtype=rope_x.real.dtype)
|
| 360 |
-
rope_cam = torch.polar(rope_cam, torch.zeros_like(rope_cam))
|
| 361 |
-
parts.append(rope_cam)
|
| 362 |
-
|
| 363 |
-
rotary_emb = torch.cat(parts, dim=1)
|
| 364 |
|
| 365 |
-
|
| 366 |
-
|
| 367 |
-
|
| 368 |
-
# Refine noise (z)
|
| 369 |
-
for i, block in enumerate(self.noise_refiner):
|
| 370 |
-
if "repo" in self.pe_mode:
|
| 371 |
-
rope_x = self.noise_repo_layers[i](h_x)
|
| 372 |
-
h_x = block(h_x, mod=t_mod, rotary_emb=rope_x)
|
| 373 |
-
|
| 374 |
-
# Refine context (cond)
|
| 375 |
-
# Note: We pass rope_cond (identity) if rope is used, though it might not affect much if it's identity
|
| 376 |
-
for i, block in enumerate(self.context_refiner):
|
| 377 |
-
if "repo" in self.pe_mode:
|
| 378 |
-
rope_cond = self.context_repo_layers[i](h_cond)
|
| 379 |
-
h_cond = block(h_cond, mod=None, rotary_emb=rope_cond)
|
| 380 |
-
|
| 381 |
-
# Refine camera
|
| 382 |
-
if self.cam_channels is not None:
|
| 383 |
-
cam = x['camera']
|
| 384 |
-
h_cam = self.cam_refiner(cam)
|
| 385 |
-
h_cam = h_cam.type(self.dtype)
|
| 386 |
|
| 387 |
-
|
| 388 |
-
h = torch.cat([h_x, h_cond], dim=1)
|
| 389 |
-
if self.cam_channels is not None:
|
| 390 |
-
h = torch.cat([h, h_cam], dim=1)
|
| 391 |
-
|
| 392 |
-
mid_features = None
|
| 393 |
-
mid_idx = len(self.blocks) // 2
|
| 394 |
-
for i, block in enumerate(self.blocks):
|
| 395 |
-
if "repo" in self.pe_mode:
|
| 396 |
-
rotary_emb = self.repo_layers[i](h)
|
| 397 |
-
h = block(h, mod=t_mod, rotary_emb=rotary_emb)
|
| 398 |
-
if i == mid_idx:
|
| 399 |
-
mid_features = h
|
| 400 |
-
|
| 401 |
-
# Split
|
| 402 |
-
h_x = h[:, :z.shape[1]]
|
| 403 |
-
h_x = h_x.type(z.dtype)
|
| 404 |
h_x = F.layer_norm(h_x, h_x.shape[-1:])
|
| 405 |
-
|
| 406 |
-
|
| 407 |
-
|
| 408 |
-
|
| 409 |
-
|
| 410 |
-
|
| 411 |
-
|
| 412 |
-
|
| 413 |
-
h_x = h_x * (1 + scale) + shift
|
| 414 |
-
if self.cam_channels is not None:
|
| 415 |
-
h_cam = h_cam * (1 + scale) + shift
|
| 416 |
-
|
| 417 |
-
h_x = self.out_layer(h_x)
|
| 418 |
-
if self.cam_channels is not None:
|
| 419 |
-
h_cam = self.cam_out_layer(h_cam)
|
| 420 |
-
|
| 421 |
-
out = TensorDict({
|
| 422 |
-
'latent': h_x,
|
| 423 |
-
}, batch_size=x.batch_size)
|
| 424 |
-
if self.cam_channels is not None:
|
| 425 |
-
out['camera'] = h_cam
|
| 426 |
-
if return_mid_features:
|
| 427 |
-
return out, mid_features
|
| 428 |
return out
|
|
|
|
| 1 |
# Adapted from https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/transformers/transformer_wan.py
|
| 2 |
# The original license is as follows:
|
| 3 |
+
#
|
| 4 |
# Copyright 2025 The Wan Team and The HuggingFace Team. All rights reserved.
|
| 5 |
#
|
| 6 |
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
| 14 |
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 15 |
# See the License for the specific language governing permissions and
|
| 16 |
# limitations under the License.
|
| 17 |
+
"""The denoiser: a rectified-flow transformer over the object latent (8192 tokens) and the input camera (1 token),
|
| 18 |
+
conditioned on the image tokens. Joint attention over [latent, image, camera] with learned rotary positions."""
|
| 19 |
import torch
|
| 20 |
import torch.nn as nn
|
| 21 |
import torch.nn.functional as F
|
|
|
|
| 22 |
from tensordict import TensorDict
|
| 23 |
|
| 24 |
+
from .layers import (MLP, PcdAbsolutePositionEmbedder, RePo3DRotaryEmbedding, SinusoidalEmbedder,
|
| 25 |
+
UnifiedTransformerBlock, convert_module_to_f16)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 26 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 27 |
|
| 28 |
class LatentSeqMMFlowModel(nn.Module):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 29 |
def __init__(
|
| 30 |
self,
|
| 31 |
q_token_length: int,
|
| 32 |
in_channels: int,
|
| 33 |
+
cam_channels: int,
|
| 34 |
model_channels: int,
|
| 35 |
cond_channels: int,
|
| 36 |
+
cond2_channels: int,
|
| 37 |
num_blocks: int,
|
| 38 |
num_refiner_blocks: int = 2,
|
| 39 |
+
num_heads: int = 16,
|
| 40 |
+
num_head_channels: int = 64,
|
|
|
|
|
|
|
| 41 |
mlp_ratio: float = 4,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 42 |
):
|
| 43 |
super().__init__()
|
| 44 |
self.q_token_length = q_token_length
|
| 45 |
self.in_channels = in_channels
|
| 46 |
self.cam_channels = cam_channels
|
| 47 |
+
self.dtype = torch.float16
|
| 48 |
+
|
| 49 |
+
def blocks(count, modulation=True):
|
| 50 |
+
return nn.ModuleList([UnifiedTransformerBlock(model_channels, num_heads=num_heads, mlp_ratio=mlp_ratio,
|
| 51 |
+
modulation=modulation) for _ in range(count)])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 52 |
|
| 53 |
+
def rope_layers(count):
|
| 54 |
+
return nn.ModuleList([RePo3DRotaryEmbedding(model_channels, num_heads=num_heads,
|
| 55 |
+
head_dim=num_head_channels) for _ in range(count)])
|
|
|
|
|
|
|
|
|
|
| 56 |
|
| 57 |
+
self.t_embedder = SinusoidalEmbedder(model_channels)
|
| 58 |
+
self.adaLN_modulation = nn.Sequential(nn.SiLU(), nn.Linear(model_channels, 6 * model_channels, bias=True))
|
| 59 |
self.input_layer = nn.Linear(in_channels, model_channels)
|
| 60 |
self.cond_embedder = nn.Linear(cond_channels, model_channels)
|
| 61 |
+
self.cond_embedder2 = nn.Linear(cond2_channels, model_channels)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 62 |
|
| 63 |
+
# positions of the latent tokens (a fixed scrambled Sobol sequence, stored with the weights)
|
| 64 |
+
self.register_buffer("pos_pe", torch.zeros(1, q_token_length, 3))
|
| 65 |
+
self.pos_embedder = PcdAbsolutePositionEmbedder(model_channels)
|
| 66 |
+
self.noise_repo_layers = rope_layers(num_refiner_blocks)
|
| 67 |
+
self.context_repo_layers = rope_layers(num_refiner_blocks)
|
| 68 |
+
self.repo_layers = rope_layers(num_blocks)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 69 |
|
| 70 |
+
# each stream is refined on its own, then all tokens go through the joint blocks
|
| 71 |
+
self.noise_refiner = blocks(num_refiner_blocks)
|
| 72 |
+
self.context_refiner = blocks(num_refiner_blocks, modulation=False)
|
| 73 |
+
self.cam_refiner = MLP(cam_channels, model_channels, model_channels, mlp_layer_num=num_refiner_blocks)
|
| 74 |
+
self.blocks = blocks(num_blocks)
|
| 75 |
|
| 76 |
+
self.shift_table = nn.Parameter(torch.zeros(1, 2, model_channels))
|
| 77 |
+
self.out_layer = nn.Linear(model_channels, in_channels)
|
| 78 |
+
self.cam_out_layer = nn.Linear(model_channels, cam_channels)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 79 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 80 |
self.blocks.apply(convert_module_to_f16)
|
| 81 |
self.noise_refiner.apply(convert_module_to_f16)
|
| 82 |
self.context_refiner.apply(convert_module_to_f16)
|
| 83 |
|
| 84 |
+
def forward(self, x: TensorDict, t: torch.Tensor, cond: TensorDict) -> TensorDict:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 85 |
"""
|
| 86 |
Args:
|
| 87 |
+
x: TensorDict with 'latent' [B, 8192, 16] and 'camera' [B, 1, 5]
|
| 88 |
+
t: [B] time in [0, 1000]
|
| 89 |
+
cond: TensorDict with 'feature1' [B, L_c, 1280] (DINOv3) and 'feature2' [B, L_c, 128] (FLUX.2 VAE)
|
| 90 |
+
Returns:
|
| 91 |
+
the velocity, a TensorDict shaped like x
|
| 92 |
"""
|
| 93 |
+
z, cam = x['latent'], x['camera']
|
|
|
|
| 94 |
h_x = self.input_layer(z).type(self.dtype)
|
| 95 |
+
h_cond = self.cond_embedder(cond['feature1']) + self.cond_embedder2(cond['feature2'])
|
|
|
|
|
|
|
|
|
|
| 96 |
h_cond = h_cond.type(self.dtype)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 97 |
|
| 98 |
+
t_emb = self.t_embedder(t)
|
| 99 |
+
t_mod = self.adaLN_modulation(t_emb).type(self.dtype)
|
|
|
|
| 100 |
|
| 101 |
+
h_x = h_x + self.pos_embedder(self.pos_pe).type(self.dtype)
|
| 102 |
+
for repo, block in zip(self.noise_repo_layers, self.noise_refiner):
|
| 103 |
+
h_x = block(h_x, mod=t_mod, rotary_emb=repo(h_x))
|
| 104 |
+
for repo, block in zip(self.context_repo_layers, self.context_refiner):
|
| 105 |
+
h_cond = block(h_cond, mod=None, rotary_emb=repo(h_cond))
|
| 106 |
+
h_cam = self.cam_refiner(cam).type(self.dtype)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 107 |
|
| 108 |
+
h = torch.cat([h_x, h_cond, h_cam], dim=1)
|
| 109 |
+
for repo, block in zip(self.repo_layers, self.blocks):
|
| 110 |
+
h = block(h, mod=t_mod, rotary_emb=repo(h))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 111 |
|
| 112 |
+
h_x = h[:, :z.shape[1]].type(z.dtype)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 113 |
h_x = F.layer_norm(h_x, h_x.shape[-1:])
|
| 114 |
+
h_cam = h[:, -cam.shape[1]:].type(cam.dtype)
|
| 115 |
+
h_cam = F.layer_norm(h_cam, h_cam.shape[-1:])
|
| 116 |
+
shift, scale = (self.shift_table + t_emb.unsqueeze(1)).chunk(2, dim=1)
|
| 117 |
+
h_x = h_x * (1 + scale) + shift
|
| 118 |
+
h_cam = h_cam * (1 + scale) + shift
|
| 119 |
+
|
| 120 |
+
out = TensorDict({'latent': self.out_layer(h_x)}, batch_size=x.batch_size)
|
| 121 |
+
out['camera'] = self.cam_out_layer(h_cam)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 122 |
return out
|
idol/models/layers.py
ADDED
|
@@ -0,0 +1,318 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Building blocks shared by the denoiser and the decoders: attention, positional embeddings, transformer blocks."""
|
| 2 |
+
import importlib.util
|
| 3 |
+
import os
|
| 4 |
+
from typing import Optional
|
| 5 |
+
|
| 6 |
+
import numpy as np
|
| 7 |
+
import torch
|
| 8 |
+
import torch.nn as nn
|
| 9 |
+
import torch.nn.functional as F
|
| 10 |
+
|
| 11 |
+
# flash-attn when it is installed, PyTorch SDPA otherwise (same result, flash-attn is faster).
|
| 12 |
+
# Override with ATTN_BACKEND=flash_attn|sdpa.
|
| 13 |
+
ATTN_BACKEND = os.environ.get('ATTN_BACKEND',
|
| 14 |
+
'flash_attn' if importlib.util.find_spec('flash_attn') is not None else 'sdpa')
|
| 15 |
+
if ATTN_BACKEND not in ('flash_attn', 'sdpa'):
|
| 16 |
+
raise ValueError(f'Unknown attention backend: {ATTN_BACKEND}')
|
| 17 |
+
if ATTN_BACKEND == 'flash_attn':
|
| 18 |
+
import flash_attn
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
def scaled_dot_product_attention(*args) -> torch.Tensor:
|
| 22 |
+
"""(qkv [N, L, 3, H, C]) or (q [N, L, H, C], k [N, S, H, C], v [N, S, H, C]) -> [N, L, H, C]."""
|
| 23 |
+
if ATTN_BACKEND == 'flash_attn':
|
| 24 |
+
if len(args) == 1:
|
| 25 |
+
return flash_attn.flash_attn_qkvpacked_func(args[0])
|
| 26 |
+
return flash_attn.flash_attn_func(*args)
|
| 27 |
+
q, k, v = args[0].unbind(dim=2) if len(args) == 1 else args
|
| 28 |
+
out = F.scaled_dot_product_attention(q.permute(0, 2, 1, 3), k.permute(0, 2, 1, 3), v.permute(0, 2, 1, 3))
|
| 29 |
+
return out.permute(0, 2, 1, 3)
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
def convert_module_to_f16(module):
|
| 33 |
+
"""Store a linear layer's weights in float16 (the transformer trunks run in half precision)."""
|
| 34 |
+
if isinstance(module, nn.Linear):
|
| 35 |
+
for parameter in module.parameters():
|
| 36 |
+
parameter.data = parameter.data.half()
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
class LayerNorm32(nn.LayerNorm):
|
| 40 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 41 |
+
return super().forward(x.float()).type(x.dtype)
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
class MultiHeadRMSNorm(nn.Module):
|
| 45 |
+
def __init__(self, dim: int, heads: int):
|
| 46 |
+
super().__init__()
|
| 47 |
+
self.scale = dim ** 0.5
|
| 48 |
+
self.gamma = nn.Parameter(torch.ones(heads, dim))
|
| 49 |
+
|
| 50 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 51 |
+
return (F.normalize(x.float(), dim=-1) * self.gamma * self.scale).to(x.dtype)
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
class FeedForwardNet(nn.Module):
|
| 55 |
+
def __init__(self, channels: int, mlp_ratio: float = 4.0):
|
| 56 |
+
super().__init__()
|
| 57 |
+
self.mlp = nn.Sequential(
|
| 58 |
+
nn.Linear(channels, int(channels * mlp_ratio)),
|
| 59 |
+
nn.GELU(approximate="tanh"),
|
| 60 |
+
nn.Linear(int(channels * mlp_ratio), channels),
|
| 61 |
+
)
|
| 62 |
+
|
| 63 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 64 |
+
return self.mlp(x)
|
| 65 |
+
|
| 66 |
+
|
| 67 |
+
class MLP(nn.Module):
|
| 68 |
+
def __init__(self, channels: int, inner_channels: int, channels_out: int, mlp_layer_num: int = 2):
|
| 69 |
+
super().__init__()
|
| 70 |
+
layers = []
|
| 71 |
+
for i in range(mlp_layer_num - 1):
|
| 72 |
+
layers.append(nn.Linear(channels if i == 0 else inner_channels, inner_channels))
|
| 73 |
+
layers.append(nn.GELU(approximate="tanh"))
|
| 74 |
+
layers.append(nn.Linear(inner_channels, channels_out))
|
| 75 |
+
self.mlp = nn.Sequential(*layers)
|
| 76 |
+
|
| 77 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 78 |
+
return self.mlp(x)
|
| 79 |
+
|
| 80 |
+
|
| 81 |
+
# ---------------------------------------------------------------------------------------------------------------------
|
| 82 |
+
# Positional and scalar embeddings
|
| 83 |
+
# ---------------------------------------------------------------------------------------------------------------------
|
| 84 |
+
|
| 85 |
+
class PcdAbsolutePositionEmbedder(nn.Module):
|
| 86 |
+
"""Sinusoidal embedding of 3D positions in [0, 1] (the denoiser's latent token positions)."""
|
| 87 |
+
def __init__(self, channels: int, in_channels: int = 3, max_res=16):
|
| 88 |
+
super().__init__()
|
| 89 |
+
self.channels = channels
|
| 90 |
+
self.in_channels = in_channels
|
| 91 |
+
self.freq_dim = channels // in_channels // 2
|
| 92 |
+
freqs_2exp = torch.arange(max_res, dtype=torch.float32)
|
| 93 |
+
res_dim = max(0, self.freq_dim - max_res)
|
| 94 |
+
freqs_res = torch.arange(res_dim, dtype=torch.float32) / res_dim * max_res
|
| 95 |
+
self.freqs = 2 ** torch.cat([freqs_2exp, freqs_res], dim=0)[:self.freq_dim]
|
| 96 |
+
|
| 97 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 98 |
+
"""x: (..., 3) positions -> (..., channels)"""
|
| 99 |
+
*dims, _ = x.shape
|
| 100 |
+
self.freqs = self.freqs.to(x.device)
|
| 101 |
+
out = torch.outer(x.reshape(-1), self.freqs) * 2 * torch.pi
|
| 102 |
+
embed = torch.cat([torch.sin(out), torch.cos(out)], dim=-1).reshape(*dims, -1)
|
| 103 |
+
if embed.shape[-1] < self.channels:
|
| 104 |
+
embed = torch.cat([embed, torch.zeros(*dims, self.channels - embed.shape[-1], device=embed.device)], dim=-1)
|
| 105 |
+
return embed
|
| 106 |
+
|
| 107 |
+
|
| 108 |
+
class PcdAbsolutePositionEmbedderv2(nn.Module):
|
| 109 |
+
"""Sinusoidal embedding of 3D points in [0, 1] (the decoders' query points)."""
|
| 110 |
+
def __init__(self, channels: int, in_channels: int = 3, max_res: int = 10):
|
| 111 |
+
super().__init__()
|
| 112 |
+
self.channels = channels
|
| 113 |
+
self.freq_dim = channels // in_channels // 2
|
| 114 |
+
logs = torch.linspace(0.0, float(max_res), steps=self.freq_dim, dtype=torch.float32)
|
| 115 |
+
self.register_buffer("freqs", torch.pow(2.0, logs), persistent=False)
|
| 116 |
+
|
| 117 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 118 |
+
"""x: (N, 3) -> (N, channels)"""
|
| 119 |
+
N = x.shape[0]
|
| 120 |
+
freqs = self.freqs.to(x.device, dtype=x.dtype)
|
| 121 |
+
ang = x.unsqueeze(-1) * freqs * torch.pi
|
| 122 |
+
embed = torch.cat([torch.sin(ang), torch.cos(ang)], dim=-1).reshape(N, -1)
|
| 123 |
+
if embed.shape[1] < self.channels:
|
| 124 |
+
embed = torch.cat([embed, torch.zeros(N, self.channels - embed.shape[1], device=embed.device,
|
| 125 |
+
dtype=embed.dtype)], dim=-1)
|
| 126 |
+
return embed
|
| 127 |
+
|
| 128 |
+
|
| 129 |
+
class SinusoidalEmbedder(nn.Module):
|
| 130 |
+
"""Embeds a scalar per batch element (diffusion time, octree level) into a vector."""
|
| 131 |
+
def __init__(self, hidden_size, frequency_embedding_size=256, max_period=10000, two_pi=False):
|
| 132 |
+
super().__init__()
|
| 133 |
+
self.mlp = nn.Sequential(
|
| 134 |
+
nn.Linear(frequency_embedding_size, hidden_size, bias=True),
|
| 135 |
+
nn.SiLU(),
|
| 136 |
+
nn.Linear(hidden_size, hidden_size, bias=True),
|
| 137 |
+
)
|
| 138 |
+
self.frequency_embedding_size = frequency_embedding_size
|
| 139 |
+
self.max_period = max_period
|
| 140 |
+
self.two_pi = two_pi
|
| 141 |
+
|
| 142 |
+
def forward(self, t):
|
| 143 |
+
half = self.frequency_embedding_size // 2
|
| 144 |
+
freqs = torch.exp(
|
| 145 |
+
-np.log(self.max_period) * torch.arange(start=0, end=half, dtype=torch.float32) / half
|
| 146 |
+
).to(device=t.device)
|
| 147 |
+
args = t[:, None].float() * freqs[None]
|
| 148 |
+
if self.two_pi:
|
| 149 |
+
args = args * 2 * torch.pi
|
| 150 |
+
return self.mlp(torch.cat([torch.cos(args), torch.sin(args)], dim=-1))
|
| 151 |
+
|
| 152 |
+
|
| 153 |
+
def apply_rotary_emb(hidden_states: torch.Tensor, freqs: torch.Tensor) -> torch.Tensor:
|
| 154 |
+
"""hidden_states [B, L, H, D] rotated by the complex frequencies freqs [B, L, H or 1, D/2]."""
|
| 155 |
+
x_rotated = torch.view_as_complex(hidden_states.float().reshape(*hidden_states.shape[:-1], -1, 2))
|
| 156 |
+
x_rotated = x_rotated * freqs
|
| 157 |
+
x_out = torch.view_as_real(x_rotated).reshape(*x_rotated.shape[:-1], -1)
|
| 158 |
+
return x_out.type_as(hidden_states)
|
| 159 |
+
|
| 160 |
+
|
| 161 |
+
def clamp_mul(x, f):
|
| 162 |
+
f_t = f.tanh()
|
| 163 |
+
return x * f_t + x.detach() * (f - f_t)
|
| 164 |
+
|
| 165 |
+
|
| 166 |
+
class RePo3DRotaryEmbedding(nn.Module):
|
| 167 |
+
"""Learned rotary position: each token's features predict a 3D offset per head, turned into RoPE angles."""
|
| 168 |
+
def __init__(self, model_channels: int, num_heads: int, head_dim: int, repo_hidden_ratio: float = 0.125,
|
| 169 |
+
max_freq: float = 16.0):
|
| 170 |
+
super().__init__()
|
| 171 |
+
self.num_heads = num_heads
|
| 172 |
+
repo_hidden_size = int(model_channels * repo_hidden_ratio)
|
| 173 |
+
self.norm = LayerNorm32(model_channels)
|
| 174 |
+
self.gate_map = nn.Linear(model_channels, repo_hidden_size, bias=False)
|
| 175 |
+
self.content_map = nn.Linear(model_channels, repo_hidden_size, bias=False)
|
| 176 |
+
self.act = nn.SiLU()
|
| 177 |
+
self.final_map = nn.Linear(repo_hidden_size, 3 * num_heads, bias=False)
|
| 178 |
+
# head_dim split into 3 parts for x, y, z
|
| 179 |
+
dim_0 = dim_1 = 2 * (head_dim // 6)
|
| 180 |
+
dims = [dim_0, dim_1, head_dim - dim_0 - dim_1]
|
| 181 |
+
freqs = [torch.linspace(1.0, float(max_freq), steps=d // 2, dtype=torch.float32) for d in dims]
|
| 182 |
+
self.freqs_0 = nn.Parameter(freqs[0])
|
| 183 |
+
self.freqs_1 = nn.Parameter(freqs[1])
|
| 184 |
+
self.freqs_2 = nn.Parameter(freqs[2])
|
| 185 |
+
|
| 186 |
+
def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
|
| 187 |
+
"""hidden_states [B, L, C] -> complex rotations [B, L, H, D/2]"""
|
| 188 |
+
h = self.norm(hidden_states.float())
|
| 189 |
+
out = self.final_map(self.act(self.gate_map(h)) * self.content_map(h))
|
| 190 |
+
B, L, _ = out.shape
|
| 191 |
+
delta_pos = out.reshape(B, L, self.num_heads, 3)
|
| 192 |
+
ang_0 = clamp_mul(delta_pos[..., 0].unsqueeze(-1), self.freqs_0) * torch.pi
|
| 193 |
+
ang_1 = clamp_mul(delta_pos[..., 1].unsqueeze(-1), self.freqs_1) * torch.pi
|
| 194 |
+
ang_2 = clamp_mul(delta_pos[..., 2].unsqueeze(-1), self.freqs_2) * torch.pi
|
| 195 |
+
ang = torch.cat([ang_0, ang_1, ang_2], dim=-1)
|
| 196 |
+
return torch.polar(torch.ones_like(ang), ang).type(torch.complex64)
|
| 197 |
+
|
| 198 |
+
|
| 199 |
+
# ---------------------------------------------------------------------------------------------------------------------
|
| 200 |
+
# Attention
|
| 201 |
+
# ---------------------------------------------------------------------------------------------------------------------
|
| 202 |
+
|
| 203 |
+
class MultiHeadAttention(nn.Module):
|
| 204 |
+
"""Self or cross attention with RMS-normalised queries and keys (the decoders)."""
|
| 205 |
+
def __init__(self, channels: int, num_heads: int, ctx_channels: Optional[int] = None, cross: bool = False):
|
| 206 |
+
super().__init__()
|
| 207 |
+
self.num_heads = num_heads
|
| 208 |
+
self.cross = cross
|
| 209 |
+
head_dim = channels // num_heads
|
| 210 |
+
if cross:
|
| 211 |
+
self.to_q = nn.Linear(channels, channels)
|
| 212 |
+
self.to_kv = nn.Linear(ctx_channels, channels * 2)
|
| 213 |
+
else:
|
| 214 |
+
self.to_qkv = nn.Linear(channels, channels * 3)
|
| 215 |
+
self.q_rms_norm = MultiHeadRMSNorm(head_dim, num_heads)
|
| 216 |
+
self.k_rms_norm = MultiHeadRMSNorm(head_dim, num_heads)
|
| 217 |
+
self.to_out = nn.Linear(channels, channels)
|
| 218 |
+
|
| 219 |
+
def forward(self, x: torch.Tensor, context: Optional[torch.Tensor] = None) -> torch.Tensor:
|
| 220 |
+
B, L, C = x.shape
|
| 221 |
+
if self.cross:
|
| 222 |
+
q = self.q_rms_norm(self.to_q(x).reshape(B, L, self.num_heads, -1))
|
| 223 |
+
k, v = self.to_kv(context).reshape(B, context.shape[1], 2, self.num_heads, -1).unbind(dim=2)
|
| 224 |
+
h = scaled_dot_product_attention(q, self.k_rms_norm(k), v)
|
| 225 |
+
else:
|
| 226 |
+
q, k, v = self.to_qkv(x).reshape(B, L, 3, self.num_heads, -1).unbind(dim=2)
|
| 227 |
+
h = scaled_dot_product_attention(torch.stack([self.q_rms_norm(q), self.k_rms_norm(k), v], dim=2))
|
| 228 |
+
return self.to_out(h.reshape(B, L, -1))
|
| 229 |
+
|
| 230 |
+
|
| 231 |
+
class RopeMultiHeadAttention(nn.Module):
|
| 232 |
+
"""Self attention with rotary embeddings and RMS-normalised queries and keys (the denoiser)."""
|
| 233 |
+
def __init__(self, channels: int, num_heads: int):
|
| 234 |
+
super().__init__()
|
| 235 |
+
self.num_heads = num_heads
|
| 236 |
+
self.head_dim = channels // num_heads
|
| 237 |
+
self.qkv = nn.Linear(channels, channels * 3)
|
| 238 |
+
self.q_norm = MultiHeadRMSNorm(self.head_dim, num_heads)
|
| 239 |
+
self.k_norm = MultiHeadRMSNorm(self.head_dim, num_heads)
|
| 240 |
+
self.out = nn.Linear(channels, channels)
|
| 241 |
+
|
| 242 |
+
def forward(self, x: torch.Tensor, rope_emb: torch.Tensor) -> torch.Tensor:
|
| 243 |
+
B, L, C = x.shape
|
| 244 |
+
q, k, v = self.qkv(x).reshape(B, L, 3, self.num_heads, self.head_dim).unbind(2)
|
| 245 |
+
q = self.q_norm(apply_rotary_emb(q, rope_emb))
|
| 246 |
+
k = self.k_norm(apply_rotary_emb(k, rope_emb))
|
| 247 |
+
return self.out(scaled_dot_product_attention(q, k, v).reshape(B, L, C))
|
| 248 |
+
|
| 249 |
+
|
| 250 |
+
# ---------------------------------------------------------------------------------------------------------------------
|
| 251 |
+
# Transformer blocks
|
| 252 |
+
# ---------------------------------------------------------------------------------------------------------------------
|
| 253 |
+
|
| 254 |
+
class UnifiedTransformerBlock(nn.Module):
|
| 255 |
+
"""Denoiser block: RoPE self attention + FFN, modulated by the diffusion time through a shared adaLN plus
|
| 256 |
+
this block's own shift table (modulation=False for the condition refiner, which has plain LayerNorms)."""
|
| 257 |
+
def __init__(self, channels: int, num_heads: int, mlp_ratio: float = 4.0, modulation: bool = True):
|
| 258 |
+
super().__init__()
|
| 259 |
+
self.modulation = modulation
|
| 260 |
+
self.norm1 = LayerNorm32(channels, elementwise_affine=not modulation, eps=1e-6)
|
| 261 |
+
self.norm2 = LayerNorm32(channels, elementwise_affine=not modulation, eps=1e-6)
|
| 262 |
+
self.attn = RopeMultiHeadAttention(channels, num_heads=num_heads)
|
| 263 |
+
self.mlp = FeedForwardNet(channels, mlp_ratio=mlp_ratio)
|
| 264 |
+
if modulation:
|
| 265 |
+
self.shift_table = nn.Parameter(torch.zeros(1, 6 * channels))
|
| 266 |
+
|
| 267 |
+
def forward(self, x: torch.Tensor, mod: Optional[torch.Tensor], rotary_emb: torch.Tensor) -> torch.Tensor:
|
| 268 |
+
if not self.modulation:
|
| 269 |
+
x = x + self.attn(self.norm1(x), rope_emb=rotary_emb)
|
| 270 |
+
return x + self.mlp(self.norm2(x))
|
| 271 |
+
mod = mod + self.shift_table.type(mod.dtype)
|
| 272 |
+
shift_msa, scale_msa, gate_msa, shift_mlp, scale_mlp, gate_mlp = mod.chunk(6, dim=1)
|
| 273 |
+
h = self.norm1(x)
|
| 274 |
+
h = h * (1 + scale_msa.unsqueeze(1)) + shift_msa.unsqueeze(1)
|
| 275 |
+
h = self.attn(h, rope_emb=rotary_emb)
|
| 276 |
+
x = x + h * gate_msa.unsqueeze(1)
|
| 277 |
+
h = self.norm2(x)
|
| 278 |
+
h = h * (1 + scale_mlp.unsqueeze(1)) + shift_mlp.unsqueeze(1)
|
| 279 |
+
h = self.mlp(h)
|
| 280 |
+
return x + h * gate_mlp.unsqueeze(1)
|
| 281 |
+
|
| 282 |
+
|
| 283 |
+
class TransformerCrossBlock(nn.Module):
|
| 284 |
+
"""Gaussian decoder block: self attention + cross attention to the latent + FFN."""
|
| 285 |
+
def __init__(self, channels: int, ctx_channels: int, num_heads: int, mlp_ratio: float = 4.0):
|
| 286 |
+
super().__init__()
|
| 287 |
+
self.norm1 = LayerNorm32(channels, elementwise_affine=False, eps=1e-6)
|
| 288 |
+
self.norm2 = LayerNorm32(channels, elementwise_affine=True, eps=1e-6)
|
| 289 |
+
self.norm3 = LayerNorm32(channels, elementwise_affine=False, eps=1e-6)
|
| 290 |
+
self.self_attn = MultiHeadAttention(channels, num_heads=num_heads)
|
| 291 |
+
self.cross_attn = MultiHeadAttention(channels, num_heads=num_heads, ctx_channels=ctx_channels, cross=True)
|
| 292 |
+
self.mlp = FeedForwardNet(channels, mlp_ratio=mlp_ratio)
|
| 293 |
+
|
| 294 |
+
def forward(self, x: torch.Tensor, context: torch.Tensor) -> torch.Tensor:
|
| 295 |
+
x = x + self.self_attn(self.norm1(x))
|
| 296 |
+
x = x + self.cross_attn(self.norm2(x), context)
|
| 297 |
+
return x + self.mlp(self.norm3(x))
|
| 298 |
+
|
| 299 |
+
|
| 300 |
+
class ModulatedTransformerCrossOnlyBlock(nn.Module):
|
| 301 |
+
"""Octree decoder block: cross attention to the latent + FFN, modulated by the octree level (shared adaLN)."""
|
| 302 |
+
def __init__(self, channels: int, ctx_channels: int, num_heads: int, mlp_ratio: float = 4.0):
|
| 303 |
+
super().__init__()
|
| 304 |
+
self.norm1 = LayerNorm32(channels, elementwise_affine=False, eps=1e-6)
|
| 305 |
+
self.norm2 = LayerNorm32(channels, elementwise_affine=False, eps=1e-6)
|
| 306 |
+
self.cross_attn = MultiHeadAttention(channels, num_heads=num_heads, ctx_channels=ctx_channels, cross=True)
|
| 307 |
+
self.mlp = FeedForwardNet(channels, mlp_ratio=mlp_ratio)
|
| 308 |
+
|
| 309 |
+
def forward(self, x: torch.Tensor, mod: torch.Tensor, context: torch.Tensor) -> torch.Tensor:
|
| 310 |
+
shift_msa, scale_msa, gate_msa, shift_mlp, scale_mlp, gate_mlp = mod.chunk(6, dim=1)
|
| 311 |
+
h = self.norm1(x)
|
| 312 |
+
h = h * (1 + scale_msa.unsqueeze(1)) + shift_msa.unsqueeze(1)
|
| 313 |
+
h = self.cross_attn(h, context)
|
| 314 |
+
x = x + h * gate_msa.unsqueeze(1)
|
| 315 |
+
h = self.norm2(x)
|
| 316 |
+
h = h * (1 + scale_mlp.unsqueeze(1)) + shift_mlp.unsqueeze(1)
|
| 317 |
+
h = self.mlp(h)
|
| 318 |
+
return x + h * gate_mlp.unsqueeze(1)
|
idol/models/transformer_base.py
DELETED
|
@@ -1,351 +0,0 @@
|
|
| 1 |
-
from typing import *
|
| 2 |
-
import torch
|
| 3 |
-
import torch.nn as nn
|
| 4 |
-
from ..modules.utils import convert_module_to_f16, convert_module_to_f32
|
| 5 |
-
from ..modules.transformer import AbsolutePositionEmbedder, TransformerCrossBlock, TransformerBlock
|
| 6 |
-
from ..modules.transformer.blocks import FeedForwardNet
|
| 7 |
-
from ..modules.norm import LayerNorm32
|
| 8 |
-
from ..modules.attention import MultiHeadAttention
|
| 9 |
-
from ..modules.transformer.modulated import ModulatedTransformerBlock, ModulatedTransformerCrossBlock, ModulatedTransformerCrossOnlyBlock
|
| 10 |
-
import numpy as np
|
| 11 |
-
|
| 12 |
-
class PcdAbsolutePositionEmbedder(nn.Module):
|
| 13 |
-
"""
|
| 14 |
-
Embeds spatial positions in [0,1] into vector representations.
|
| 15 |
-
"""
|
| 16 |
-
def __init__(self, channels: int, in_channels: int = 3, max_res=16):
|
| 17 |
-
super().__init__()
|
| 18 |
-
self.channels = channels
|
| 19 |
-
self.in_channels = in_channels
|
| 20 |
-
self.freq_dim = channels // in_channels // 2
|
| 21 |
-
self.max_res = max_res
|
| 22 |
-
freqs_2exp = torch.arange(max_res, dtype=torch.float32)
|
| 23 |
-
res_dim = max(0, self.freq_dim - max_res)
|
| 24 |
-
freqs_res = torch.arange(res_dim, dtype=torch.float32) / res_dim * max_res
|
| 25 |
-
self.freqs = torch.cat([freqs_2exp, freqs_res], dim=0)[:self.freq_dim]
|
| 26 |
-
self.freqs = 2 ** self.freqs # [1, 2 ^ max_res]
|
| 27 |
-
|
| 28 |
-
def _sin_cos_embedding(self, x: torch.Tensor) -> torch.Tensor:
|
| 29 |
-
"""
|
| 30 |
-
Create sinusoidal position embeddings.
|
| 31 |
-
|
| 32 |
-
Args:
|
| 33 |
-
x: a 1-D Tensor of N indices (in [0,1]^3 range)
|
| 34 |
-
|
| 35 |
-
Returns:
|
| 36 |
-
an (N, D) Tensor of positional embeddings.
|
| 37 |
-
"""
|
| 38 |
-
self.freqs = self.freqs.to(x.device)
|
| 39 |
-
out = torch.outer(x, self.freqs) * 2 * torch.pi
|
| 40 |
-
out = torch.cat([torch.sin(out), torch.cos(out)], dim=-1)
|
| 41 |
-
return out
|
| 42 |
-
|
| 43 |
-
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 44 |
-
"""
|
| 45 |
-
Args:
|
| 46 |
-
x (torch.Tensor): (N, D) or (B, N, D) tensor of spatial positions
|
| 47 |
-
"""
|
| 48 |
-
*dims, D = x.shape
|
| 49 |
-
assert D == self.in_channels, "Input dimension must match number of input channels"
|
| 50 |
-
embed = self._sin_cos_embedding(x.reshape(-1))
|
| 51 |
-
embed = embed.reshape(*dims, -1)
|
| 52 |
-
if embed.shape[-1] < self.channels:
|
| 53 |
-
embed = torch.cat([embed, torch.zeros(*dims, self.channels - embed.shape[-1], device=embed.device)], dim=-1)
|
| 54 |
-
return embed
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
class PcdAbsolutePositionEmbedderv2(nn.Module):
|
| 58 |
-
def __init__(self, channels: int, in_channels: int = 3, max_res: int = 10):
|
| 59 |
-
super().__init__()
|
| 60 |
-
self.channels = channels
|
| 61 |
-
self.in_channels = in_channels
|
| 62 |
-
self.freq_dim = channels // in_channels // 2
|
| 63 |
-
logs = torch.linspace(0.0, float(max_res), steps=self.freq_dim, dtype=torch.float32)
|
| 64 |
-
self.register_buffer("freqs", torch.pow(2.0, logs), persistent=False)
|
| 65 |
-
|
| 66 |
-
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 67 |
-
N, D = x.shape
|
| 68 |
-
assert D == self.in_channels, "Input dimension must match number of input channels"
|
| 69 |
-
freqs = self.freqs.to(x.device, dtype=x.dtype)
|
| 70 |
-
ang = x.unsqueeze(-1) * freqs * torch.pi
|
| 71 |
-
embed = torch.cat([torch.sin(ang), torch.cos(ang)], dim=-1).reshape(N, -1)
|
| 72 |
-
if embed.shape[1] < self.channels:
|
| 73 |
-
embed = torch.cat([embed, torch.zeros(N, self.channels - embed.shape[1], device=embed.device, dtype=embed.dtype)], dim=-1)
|
| 74 |
-
return embed
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
class TransformerBase(nn.Module):
|
| 78 |
-
"""
|
| 79 |
-
Transformer without output layers.
|
| 80 |
-
Serve as the base class for encoder and decoder.
|
| 81 |
-
"""
|
| 82 |
-
def __init__(
|
| 83 |
-
self,
|
| 84 |
-
in_channels: int,
|
| 85 |
-
model_channels: int,
|
| 86 |
-
cond_channels: Optional[int],
|
| 87 |
-
num_blocks: int,
|
| 88 |
-
num_heads: Optional[int] = None,
|
| 89 |
-
num_head_channels: Optional[int] = 64,
|
| 90 |
-
mlp_ratio: float = 4.0,
|
| 91 |
-
use_fp16: bool = False,
|
| 92 |
-
use_checkpoint: bool = False,
|
| 93 |
-
qk_rms_norm: bool = True,
|
| 94 |
-
qk_rms_norm_cross: bool = True,
|
| 95 |
-
use_2_cross_block: bool = False,
|
| 96 |
-
share_mod: bool = False,
|
| 97 |
-
):
|
| 98 |
-
super().__init__()
|
| 99 |
-
assert not use_2_cross_block or (cond_channels is not None), "use_2_cross_block requires cond_channels"
|
| 100 |
-
self.model_channels = model_channels
|
| 101 |
-
self.cond_channels = cond_channels
|
| 102 |
-
self.num_blocks = num_blocks
|
| 103 |
-
self.num_heads = num_heads or model_channels // num_head_channels
|
| 104 |
-
self.mlp_ratio = mlp_ratio
|
| 105 |
-
self.use_fp16 = use_fp16
|
| 106 |
-
self.use_checkpoint = use_checkpoint
|
| 107 |
-
self.qk_rms_norm = qk_rms_norm
|
| 108 |
-
self.qk_rms_norm_cross = qk_rms_norm_cross
|
| 109 |
-
self.share_mod = share_mod
|
| 110 |
-
self.use_2_cross_block = use_2_cross_block
|
| 111 |
-
|
| 112 |
-
self.dtype = torch.float16 if use_fp16 else torch.float32
|
| 113 |
-
|
| 114 |
-
self.input_layer = nn.Linear(in_channels, model_channels)
|
| 115 |
-
self.l_embedder = None
|
| 116 |
-
if cond_channels is not None:
|
| 117 |
-
self.blocks = nn.ModuleList([
|
| 118 |
-
TransformerCrossBlock(
|
| 119 |
-
model_channels,
|
| 120 |
-
ctx_channels=cond_channels,
|
| 121 |
-
num_heads=self.num_heads,
|
| 122 |
-
mlp_ratio=self.mlp_ratio,
|
| 123 |
-
use_checkpoint=self.use_checkpoint,
|
| 124 |
-
qk_rms_norm=self.qk_rms_norm,
|
| 125 |
-
qk_rms_norm_cross=self.qk_rms_norm_cross,
|
| 126 |
-
use_2_cross_block=use_2_cross_block,
|
| 127 |
-
)
|
| 128 |
-
for _ in range(num_blocks)
|
| 129 |
-
])
|
| 130 |
-
else:
|
| 131 |
-
self.blocks = nn.ModuleList([
|
| 132 |
-
TransformerBlock(
|
| 133 |
-
model_channels,
|
| 134 |
-
num_heads=self.num_heads,
|
| 135 |
-
mlp_ratio=self.mlp_ratio,
|
| 136 |
-
use_checkpoint=self.use_checkpoint,
|
| 137 |
-
qk_rms_norm=self.qk_rms_norm,
|
| 138 |
-
)
|
| 139 |
-
for _ in range(num_blocks)
|
| 140 |
-
])
|
| 141 |
-
|
| 142 |
-
@property
|
| 143 |
-
def device(self) -> torch.device:
|
| 144 |
-
"""
|
| 145 |
-
Return the device of the model.
|
| 146 |
-
"""
|
| 147 |
-
return next(self.parameters()).device
|
| 148 |
-
|
| 149 |
-
def convert_to_fp16(self) -> None:
|
| 150 |
-
"""
|
| 151 |
-
Convert the torso of the model to float16.
|
| 152 |
-
"""
|
| 153 |
-
self.blocks.apply(convert_module_to_f16)
|
| 154 |
-
|
| 155 |
-
def convert_to_fp32(self) -> None:
|
| 156 |
-
"""
|
| 157 |
-
Convert the torso of the model to float32.
|
| 158 |
-
"""
|
| 159 |
-
self.blocks.apply(convert_module_to_f32)
|
| 160 |
-
|
| 161 |
-
def initialize_weights(self) -> None:
|
| 162 |
-
# Initialize transformer layers:
|
| 163 |
-
def _basic_init(module):
|
| 164 |
-
if isinstance(module, nn.Linear):
|
| 165 |
-
torch.nn.init.xavier_uniform_(module.weight)
|
| 166 |
-
if module.bias is not None:
|
| 167 |
-
nn.init.constant_(module.bias, 0)
|
| 168 |
-
self.apply(_basic_init)
|
| 169 |
-
|
| 170 |
-
|
| 171 |
-
def forward(self, x: torch.Tensor, cond: Optional[torch.Tensor]=None, l: Optional[torch.Tensor]=None, cond2: Optional[torch.Tensor]=None, indices=None, context_indices=None, context2_indices=None) -> torch.Tensor:
|
| 172 |
-
h = self.input_layer(x)
|
| 173 |
-
h = h.type(self.dtype)
|
| 174 |
-
cond = cond.type(self.dtype) if cond is not None else None
|
| 175 |
-
cond2 = cond2.type(self.dtype) if cond2 is not None else None
|
| 176 |
-
for block in self.blocks:
|
| 177 |
-
if cond is None:
|
| 178 |
-
h = block(h, indices=indices)
|
| 179 |
-
else:
|
| 180 |
-
h = block(h, cond, context2=cond2, indices=indices, context_indices=context_indices, context2_indices=context2_indices)
|
| 181 |
-
h = h.type(x.dtype)
|
| 182 |
-
return h
|
| 183 |
-
|
| 184 |
-
class LevelEmbedder(nn.Module):
|
| 185 |
-
"""
|
| 186 |
-
Embeds scalar timesteps into vector representations.
|
| 187 |
-
"""
|
| 188 |
-
def __init__(self, hidden_size, frequency_embedding_size=256, max_period=1024):
|
| 189 |
-
super().__init__()
|
| 190 |
-
self.mlp = nn.Sequential(
|
| 191 |
-
nn.Linear(frequency_embedding_size, hidden_size, bias=True),
|
| 192 |
-
nn.SiLU(),
|
| 193 |
-
nn.Linear(hidden_size, hidden_size, bias=True),
|
| 194 |
-
)
|
| 195 |
-
self.frequency_embedding_size = frequency_embedding_size
|
| 196 |
-
self.max_period = max_period
|
| 197 |
-
|
| 198 |
-
@staticmethod
|
| 199 |
-
def level_embedding(t, dim, max_period=1024):
|
| 200 |
-
"""
|
| 201 |
-
Create sinusoidal timestep embeddings.
|
| 202 |
-
|
| 203 |
-
Args:
|
| 204 |
-
t: a 1-D Tensor of N indices, one per batch element.
|
| 205 |
-
These may be fractional.
|
| 206 |
-
dim: the dimension of the output.
|
| 207 |
-
max_period: controls the minimum frequency of the embeddings.
|
| 208 |
-
|
| 209 |
-
Returns:
|
| 210 |
-
an (N, D) Tensor of positional embeddings.
|
| 211 |
-
"""
|
| 212 |
-
# https://github.com/openai/glide-text2im/blob/main/glide_text2im/nn.py
|
| 213 |
-
half = dim // 2
|
| 214 |
-
freqs = torch.exp(
|
| 215 |
-
-np.log(max_period) * torch.arange(start=0, end=half, dtype=torch.float32) / half
|
| 216 |
-
).to(device=t.device)
|
| 217 |
-
args = t[:, None].float() * freqs[None] * 2 * torch.pi
|
| 218 |
-
embedding = torch.cat([torch.cos(args), torch.sin(args)], dim=-1)
|
| 219 |
-
if dim % 2:
|
| 220 |
-
embedding = torch.cat([embedding, torch.zeros_like(embedding[:, :1])], dim=-1)
|
| 221 |
-
return embedding
|
| 222 |
-
|
| 223 |
-
def forward(self, t):
|
| 224 |
-
t_freq = self.level_embedding(t, self.frequency_embedding_size, self.max_period)
|
| 225 |
-
t_emb = self.mlp(t_freq)
|
| 226 |
-
return t_emb
|
| 227 |
-
|
| 228 |
-
class ModulatedCrossOnlyTransformerBase(nn.Module):
|
| 229 |
-
"""
|
| 230 |
-
Transformer without output layers.
|
| 231 |
-
Serve as the base class for encoder and decoder.
|
| 232 |
-
"""
|
| 233 |
-
def __init__(
|
| 234 |
-
self,
|
| 235 |
-
in_channels: int,
|
| 236 |
-
model_channels: int,
|
| 237 |
-
cond_channels: Optional[int],
|
| 238 |
-
num_blocks: int,
|
| 239 |
-
num_heads: Optional[int] = None,
|
| 240 |
-
num_head_channels: Optional[int] = 64,
|
| 241 |
-
mlp_ratio: float = 4.0,
|
| 242 |
-
use_fp16: bool = False,
|
| 243 |
-
use_checkpoint: bool = False,
|
| 244 |
-
share_mod: bool = False,
|
| 245 |
-
additional_level_embed: bool = False,
|
| 246 |
-
qk_rms_norm_cross: bool = True,
|
| 247 |
-
):
|
| 248 |
-
super().__init__()
|
| 249 |
-
self.in_channels = in_channels
|
| 250 |
-
self.model_channels = model_channels
|
| 251 |
-
self.cond_channels = cond_channels
|
| 252 |
-
self.num_blocks = num_blocks
|
| 253 |
-
self.num_heads = num_heads or model_channels // num_head_channels
|
| 254 |
-
self.mlp_ratio = mlp_ratio
|
| 255 |
-
self.use_fp16 = use_fp16
|
| 256 |
-
self.use_checkpoint = use_checkpoint
|
| 257 |
-
self.share_mod = share_mod
|
| 258 |
-
self.qk_rms_norm_cross = qk_rms_norm_cross
|
| 259 |
-
self.dtype = torch.float16 if use_fp16 else torch.float32
|
| 260 |
-
|
| 261 |
-
self.input_layer = nn.Linear(in_channels, model_channels)
|
| 262 |
-
self.l_embedder = LevelEmbedder(model_channels)
|
| 263 |
-
if additional_level_embed:
|
| 264 |
-
self.l_embedder2 = LevelEmbedder(model_channels, max_period=100)
|
| 265 |
-
else:
|
| 266 |
-
self.l_embedder2 = None
|
| 267 |
-
if share_mod:
|
| 268 |
-
self.adaLN_modulation = nn.Sequential(
|
| 269 |
-
nn.SiLU(),
|
| 270 |
-
nn.Linear(model_channels, 6 * model_channels, bias=True)
|
| 271 |
-
)
|
| 272 |
-
|
| 273 |
-
if cond_channels is not None:
|
| 274 |
-
self.blocks = nn.ModuleList([
|
| 275 |
-
ModulatedTransformerCrossOnlyBlock(
|
| 276 |
-
model_channels,
|
| 277 |
-
ctx_channels=cond_channels,
|
| 278 |
-
num_heads=self.num_heads,
|
| 279 |
-
mlp_ratio=self.mlp_ratio,
|
| 280 |
-
use_checkpoint=self.use_checkpoint,
|
| 281 |
-
qk_rms_norm_cross=self.qk_rms_norm_cross,
|
| 282 |
-
share_mod=self.share_mod
|
| 283 |
-
)
|
| 284 |
-
for _ in range(num_blocks)
|
| 285 |
-
])
|
| 286 |
-
else:
|
| 287 |
-
raise ValueError("ModulatedCrossOnlyTransformerBase requires cond_channels to be not None.")
|
| 288 |
-
|
| 289 |
-
@property
|
| 290 |
-
def device(self) -> torch.device:
|
| 291 |
-
"""
|
| 292 |
-
Return the device of the model.
|
| 293 |
-
"""
|
| 294 |
-
return next(self.parameters()).device
|
| 295 |
-
|
| 296 |
-
def convert_to_fp16(self) -> None:
|
| 297 |
-
"""
|
| 298 |
-
Convert the torso of the model to float16.
|
| 299 |
-
"""
|
| 300 |
-
self.blocks.apply(convert_module_to_f16)
|
| 301 |
-
|
| 302 |
-
def convert_to_fp32(self) -> None:
|
| 303 |
-
"""
|
| 304 |
-
Convert the torso of the model to float32.
|
| 305 |
-
"""
|
| 306 |
-
self.blocks.apply(convert_module_to_f32)
|
| 307 |
-
|
| 308 |
-
def initialize_weights(self) -> None:
|
| 309 |
-
# Initialize transformer layers:
|
| 310 |
-
def _basic_init(module):
|
| 311 |
-
if isinstance(module, nn.Linear):
|
| 312 |
-
torch.nn.init.xavier_uniform_(module.weight)
|
| 313 |
-
if module.bias is not None:
|
| 314 |
-
nn.init.constant_(module.bias, 0)
|
| 315 |
-
self.apply(_basic_init)
|
| 316 |
-
|
| 317 |
-
# Initialize timestep embedding MLP:
|
| 318 |
-
nn.init.normal_(self.l_embedder.mlp[0].weight, std=0.02)
|
| 319 |
-
nn.init.normal_(self.l_embedder.mlp[2].weight, std=0.02)
|
| 320 |
-
|
| 321 |
-
if self.l_embedder2 is not None:
|
| 322 |
-
# zero projection init
|
| 323 |
-
nn.init.normal_(self.l_embedder2.mlp[0].weight, std=0.02)
|
| 324 |
-
nn.init.constant_(self.l_embedder2.mlp[2].weight, 0)
|
| 325 |
-
nn.init.constant_(self.l_embedder2.mlp[2].bias, 0)
|
| 326 |
-
|
| 327 |
-
# Zero-out adaLN modulation layers in DiT blocks:
|
| 328 |
-
if self.share_mod:
|
| 329 |
-
nn.init.constant_(self.adaLN_modulation[-1].weight, 0)
|
| 330 |
-
nn.init.constant_(self.adaLN_modulation[-1].bias, 0)
|
| 331 |
-
else:
|
| 332 |
-
for block in self.blocks:
|
| 333 |
-
nn.init.constant_(block.adaLN_modulation[-1].weight, 0)
|
| 334 |
-
nn.init.constant_(block.adaLN_modulation[-1].bias, 0)
|
| 335 |
-
|
| 336 |
-
def forward(self, x: torch.Tensor, l: torch.Tensor, cond: torch.Tensor, l2: Optional[torch.Tensor] = None) -> torch.Tensor:
|
| 337 |
-
h = self.input_layer(x)
|
| 338 |
-
l = l.type(h.dtype)
|
| 339 |
-
l_emb = self.l_embedder(l)
|
| 340 |
-
if self.l_embedder2 is not None and l2 is not None:
|
| 341 |
-
l2 = l2.type(h.dtype)
|
| 342 |
-
l_emb = l_emb + self.l_embedder2(l2)
|
| 343 |
-
if self.share_mod:
|
| 344 |
-
l_emb = self.adaLN_modulation(l_emb)
|
| 345 |
-
l_emb = l_emb.type(self.dtype)
|
| 346 |
-
h = h.type(self.dtype)
|
| 347 |
-
cond = cond.type(self.dtype)
|
| 348 |
-
for block in self.blocks:
|
| 349 |
-
h = block(h, l_emb, cond)
|
| 350 |
-
h = h.type(x.dtype)
|
| 351 |
-
return h
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
idol/modules/__init__.py
DELETED
|
File without changes
|
idol/modules/attention/__init__.py
DELETED
|
@@ -1,14 +0,0 @@
|
|
| 1 |
-
from typing import *
|
| 2 |
-
import importlib.util
|
| 3 |
-
import os
|
| 4 |
-
|
| 5 |
-
# flash-attn when it is installed, PyTorch SDPA otherwise (same result, flash-attn is faster).
|
| 6 |
-
# Override with ATTN_BACKEND=flash_attn|sdpa|naive.
|
| 7 |
-
BACKEND = 'flash_attn' if importlib.util.find_spec('flash_attn') is not None else 'sdpa'
|
| 8 |
-
BACKEND = os.environ.get('ATTN_BACKEND', BACKEND)
|
| 9 |
-
if BACKEND not in ('flash_attn', 'sdpa', 'naive'):
|
| 10 |
-
raise ValueError(f'Unknown attention backend: {BACKEND}')
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
from .full_attn import *
|
| 14 |
-
from .modules import *
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
idol/modules/attention/full_attn.py
DELETED
|
@@ -1,127 +0,0 @@
|
|
| 1 |
-
from typing import *
|
| 2 |
-
import torch
|
| 3 |
-
import math
|
| 4 |
-
from torch.nn.functional import scaled_dot_product_attention as sdpa
|
| 5 |
-
from . import BACKEND
|
| 6 |
-
|
| 7 |
-
if BACKEND == 'flash_attn':
|
| 8 |
-
import flash_attn
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
__all__ = [
|
| 12 |
-
'scaled_dot_product_attention',
|
| 13 |
-
]
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
def _naive_sdpa(q, k, v):
|
| 17 |
-
"""
|
| 18 |
-
Naive implementation of scaled dot product attention.
|
| 19 |
-
"""
|
| 20 |
-
q = q.permute(0, 2, 1, 3) # [N, H, L, C]
|
| 21 |
-
k = k.permute(0, 2, 1, 3) # [N, H, L, C]
|
| 22 |
-
v = v.permute(0, 2, 1, 3) # [N, H, L, C]
|
| 23 |
-
scale_factor = 1 / math.sqrt(q.size(-1))
|
| 24 |
-
attn_weight = q @ k.transpose(-2, -1) * scale_factor
|
| 25 |
-
attn_weight = torch.softmax(attn_weight, dim=-1)
|
| 26 |
-
out = attn_weight @ v
|
| 27 |
-
out = out.permute(0, 2, 1, 3) # [N, L, H, C]
|
| 28 |
-
return out
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
@overload
|
| 32 |
-
def scaled_dot_product_attention(qkv: torch.Tensor) -> torch.Tensor:
|
| 33 |
-
"""
|
| 34 |
-
Apply scaled dot product attention.
|
| 35 |
-
|
| 36 |
-
Args:
|
| 37 |
-
qkv (torch.Tensor): A [N, L, 3, H, C] tensor containing Qs, Ks, and Vs.
|
| 38 |
-
"""
|
| 39 |
-
...
|
| 40 |
-
|
| 41 |
-
@overload
|
| 42 |
-
def scaled_dot_product_attention(q: torch.Tensor, kv: torch.Tensor) -> torch.Tensor:
|
| 43 |
-
"""
|
| 44 |
-
Apply scaled dot product attention.
|
| 45 |
-
|
| 46 |
-
Args:
|
| 47 |
-
q (torch.Tensor): A [N, L, H, C] tensor containing Qs.
|
| 48 |
-
kv (torch.Tensor): A [N, L, 2, H, C] tensor containing Ks and Vs.
|
| 49 |
-
"""
|
| 50 |
-
...
|
| 51 |
-
|
| 52 |
-
@overload
|
| 53 |
-
def scaled_dot_product_attention(q: torch.Tensor, k: torch.Tensor, v: torch.Tensor) -> torch.Tensor:
|
| 54 |
-
"""
|
| 55 |
-
Apply scaled dot product attention.
|
| 56 |
-
|
| 57 |
-
Args:
|
| 58 |
-
q (torch.Tensor): A [N, L, H, Ci] tensor containing Qs.
|
| 59 |
-
k (torch.Tensor): A [N, L, H, Ci] tensor containing Ks.
|
| 60 |
-
v (torch.Tensor): A [N, L, H, Co] tensor containing Vs.
|
| 61 |
-
|
| 62 |
-
Note:
|
| 63 |
-
k and v are assumed to have the same coordinate map.
|
| 64 |
-
"""
|
| 65 |
-
...
|
| 66 |
-
|
| 67 |
-
def scaled_dot_product_attention(*args, **kwargs):
|
| 68 |
-
arg_names_dict = {
|
| 69 |
-
1: ['qkv'],
|
| 70 |
-
2: ['q', 'kv'],
|
| 71 |
-
3: ['q', 'k', 'v']
|
| 72 |
-
}
|
| 73 |
-
num_all_args = len(args) + len(kwargs)
|
| 74 |
-
assert num_all_args in arg_names_dict, f"Invalid number of arguments, got {num_all_args}, expected 1, 2, or 3"
|
| 75 |
-
for key in arg_names_dict[num_all_args][len(args):]:
|
| 76 |
-
assert key in kwargs, f"Missing argument {key}"
|
| 77 |
-
|
| 78 |
-
if num_all_args == 1:
|
| 79 |
-
qkv = args[0] if len(args) > 0 else kwargs['qkv']
|
| 80 |
-
assert len(qkv.shape) == 5 and qkv.shape[2] == 3, f"Invalid shape for qkv, got {qkv.shape}, expected [N, L, 3, H, C]"
|
| 81 |
-
device = qkv.device
|
| 82 |
-
|
| 83 |
-
elif num_all_args == 2:
|
| 84 |
-
q = args[0] if len(args) > 0 else kwargs['q']
|
| 85 |
-
kv = args[1] if len(args) > 1 else kwargs['kv']
|
| 86 |
-
assert q.shape[0] == kv.shape[0], f"Batch size mismatch, got {q.shape[0]} and {kv.shape[0]}"
|
| 87 |
-
assert len(q.shape) == 4, f"Invalid shape for q, got {q.shape}, expected [N, L, H, C]"
|
| 88 |
-
assert len(kv.shape) == 5, f"Invalid shape for kv, got {kv.shape}, expected [N, L, 2, H, C]"
|
| 89 |
-
device = q.device
|
| 90 |
-
|
| 91 |
-
elif num_all_args == 3:
|
| 92 |
-
q = args[0] if len(args) > 0 else kwargs['q']
|
| 93 |
-
k = args[1] if len(args) > 1 else kwargs['k']
|
| 94 |
-
v = args[2] if len(args) > 2 else kwargs['v']
|
| 95 |
-
assert q.shape[0] == k.shape[0] == v.shape[0], f"Batch size mismatch, got {q.shape[0]}, {k.shape[0]}, and {v.shape[0]}"
|
| 96 |
-
assert len(q.shape) == 4, f"Invalid shape for q, got {q.shape}, expected [N, L, H, Ci]"
|
| 97 |
-
assert len(k.shape) == 4, f"Invalid shape for k, got {k.shape}, expected [N, L, H, Ci]"
|
| 98 |
-
assert len(v.shape) == 4, f"Invalid shape for v, got {v.shape}, expected [N, L, H, Co]"
|
| 99 |
-
device = q.device
|
| 100 |
-
|
| 101 |
-
if BACKEND == 'flash_attn':
|
| 102 |
-
if num_all_args == 1:
|
| 103 |
-
out = flash_attn.flash_attn_qkvpacked_func(qkv)
|
| 104 |
-
elif num_all_args == 2:
|
| 105 |
-
out = flash_attn.flash_attn_kvpacked_func(q, kv)
|
| 106 |
-
elif num_all_args == 3:
|
| 107 |
-
out = flash_attn.flash_attn_func(q, k, v)
|
| 108 |
-
elif BACKEND == 'sdpa':
|
| 109 |
-
if num_all_args == 1:
|
| 110 |
-
q, k, v = qkv.unbind(dim=2)
|
| 111 |
-
elif num_all_args == 2:
|
| 112 |
-
k, v = kv.unbind(dim=2)
|
| 113 |
-
q = q.permute(0, 2, 1, 3) # [N, H, L, C]
|
| 114 |
-
k = k.permute(0, 2, 1, 3) # [N, H, L, C]
|
| 115 |
-
v = v.permute(0, 2, 1, 3) # [N, H, L, C]
|
| 116 |
-
out = sdpa(q, k, v) # [N, H, L, C]
|
| 117 |
-
out = out.permute(0, 2, 1, 3) # [N, L, H, C]
|
| 118 |
-
elif BACKEND == 'naive':
|
| 119 |
-
if num_all_args == 1:
|
| 120 |
-
q, k, v = qkv.unbind(dim=2)
|
| 121 |
-
elif num_all_args == 2:
|
| 122 |
-
k, v = kv.unbind(dim=2)
|
| 123 |
-
out = _naive_sdpa(q, k, v)
|
| 124 |
-
else:
|
| 125 |
-
raise ValueError(f"Unknown attention module: {BACKEND}")
|
| 126 |
-
|
| 127 |
-
return out
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
idol/modules/attention/modules.py
DELETED
|
@@ -1,338 +0,0 @@
|
|
| 1 |
-
from typing import *
|
| 2 |
-
import torch
|
| 3 |
-
import torch.nn as nn
|
| 4 |
-
import torch.nn.functional as F
|
| 5 |
-
from .full_attn import scaled_dot_product_attention
|
| 6 |
-
from ...modules.norm import LayerNorm32
|
| 7 |
-
|
| 8 |
-
class MultiHeadRMSNorm(nn.Module):
|
| 9 |
-
def __init__(self, dim: int, heads: int):
|
| 10 |
-
super().__init__()
|
| 11 |
-
self.scale = dim ** 0.5
|
| 12 |
-
self.gamma = nn.Parameter(torch.ones(heads, dim))
|
| 13 |
-
|
| 14 |
-
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 15 |
-
return (F.normalize(x.float(), dim = -1) * self.gamma * self.scale).to(x.dtype)
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
class RotaryPositionEmbedder(nn.Module):
|
| 19 |
-
def __init__(self, hidden_size: int, in_channels: int = 3):
|
| 20 |
-
super().__init__()
|
| 21 |
-
assert hidden_size % 2 == 0, "Hidden size must be divisible by 2"
|
| 22 |
-
self.hidden_size = hidden_size
|
| 23 |
-
self.in_channels = in_channels
|
| 24 |
-
self.freq_dim = hidden_size // in_channels // 2
|
| 25 |
-
self.freqs = torch.arange(self.freq_dim, dtype=torch.float32) / self.freq_dim
|
| 26 |
-
self.freqs = 1.0 / (10000 ** self.freqs)
|
| 27 |
-
|
| 28 |
-
def _get_phases(self, indices: torch.Tensor) -> torch.Tensor:
|
| 29 |
-
self.freqs = self.freqs.to(indices.device)
|
| 30 |
-
phases = torch.outer(indices, self.freqs)
|
| 31 |
-
phases = torch.polar(torch.ones_like(phases), phases)
|
| 32 |
-
return phases
|
| 33 |
-
|
| 34 |
-
def _rotary_embedding(self, x: torch.Tensor, phases: torch.Tensor) -> torch.Tensor:
|
| 35 |
-
x_complex = torch.view_as_complex(x.float().reshape(*x.shape[:-1], -1, 2))
|
| 36 |
-
x_rotated = x_complex * phases
|
| 37 |
-
x_embed = torch.view_as_real(x_rotated).reshape(*x_rotated.shape[:-1], -1).to(x.dtype)
|
| 38 |
-
return x_embed
|
| 39 |
-
|
| 40 |
-
def forward(self, q: torch.Tensor, k: torch.Tensor, indices: Optional[torch.Tensor] = None) -> Tuple[torch.Tensor, torch.Tensor]:
|
| 41 |
-
if indices is None:
|
| 42 |
-
indices = torch.arange(q.shape[-2], device=q.device)
|
| 43 |
-
if len(q.shape) > 2:
|
| 44 |
-
indices = indices.unsqueeze(0).expand(q.shape[:-2] + (-1,))
|
| 45 |
-
|
| 46 |
-
phases = self._get_phases(indices.reshape(-1)).reshape(*indices.shape[:-1], -1)
|
| 47 |
-
if phases.shape[-1] < self.hidden_size // 2:
|
| 48 |
-
phases = torch.cat([phases, torch.polar(
|
| 49 |
-
torch.ones(*phases.shape[:-1], self.hidden_size // 2 - phases.shape[1], device=phases.device),
|
| 50 |
-
torch.zeros(*phases.shape[:-1], self.hidden_size // 2 - phases.shape[1], device=phases.device)
|
| 51 |
-
)], dim=-1)
|
| 52 |
-
q_embed = self._rotary_embedding(q, phases)
|
| 53 |
-
k_embed = self._rotary_embedding(k, phases)
|
| 54 |
-
return q_embed, k_embed
|
| 55 |
-
|
| 56 |
-
def apply_rope(self, x: torch.Tensor, indices: Optional[torch.Tensor]) -> torch.Tensor:
|
| 57 |
-
if indices is None:
|
| 58 |
-
raise ValueError()
|
| 59 |
-
phases = self._get_phases(indices.reshape(-1)).reshape(*indices.shape[:-1], -1)
|
| 60 |
-
if phases.shape[-1] < self.hidden_size // 2:
|
| 61 |
-
phases = torch.cat([phases, torch.polar(
|
| 62 |
-
torch.ones(*phases.shape[:-1], self.hidden_size // 2 - phases.shape[-1], device=phases.device),
|
| 63 |
-
torch.zeros(*phases.shape[:-1], self.hidden_size // 2 - phases.shape[-1], device=phases.device)
|
| 64 |
-
)], dim=-1)
|
| 65 |
-
return self._rotary_embedding(x, phases)
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
class MultiHeadAttention(nn.Module):
|
| 69 |
-
def __init__(
|
| 70 |
-
self,
|
| 71 |
-
channels: int,
|
| 72 |
-
num_heads: int,
|
| 73 |
-
ctx_channels: Optional[int]=None,
|
| 74 |
-
type: Literal["self", "cross"] = "self",
|
| 75 |
-
qkv_bias: bool = True,
|
| 76 |
-
qk_rms_norm: bool = False,
|
| 77 |
-
):
|
| 78 |
-
super().__init__()
|
| 79 |
-
assert channels % num_heads == 0
|
| 80 |
-
assert type in ["self", "cross"], f"Invalid attention type: {type}"
|
| 81 |
-
|
| 82 |
-
self.channels = channels
|
| 83 |
-
self.head_dim = channels // num_heads
|
| 84 |
-
self.ctx_channels = ctx_channels if ctx_channels is not None else channels
|
| 85 |
-
self.num_heads = num_heads
|
| 86 |
-
self._type = type
|
| 87 |
-
self.qk_rms_norm = qk_rms_norm
|
| 88 |
-
|
| 89 |
-
if self._type == "self":
|
| 90 |
-
self.to_qkv = nn.Linear(channels, channels * 3, bias=qkv_bias)
|
| 91 |
-
else:
|
| 92 |
-
self.to_q = nn.Linear(channels, channels, bias=qkv_bias)
|
| 93 |
-
self.to_kv = nn.Linear(self.ctx_channels, channels * 2, bias=qkv_bias)
|
| 94 |
-
|
| 95 |
-
if self.qk_rms_norm:
|
| 96 |
-
self.q_rms_norm = MultiHeadRMSNorm(self.head_dim, num_heads)
|
| 97 |
-
self.k_rms_norm = MultiHeadRMSNorm(self.head_dim, num_heads)
|
| 98 |
-
|
| 99 |
-
self.to_out = nn.Linear(channels, channels)
|
| 100 |
-
|
| 101 |
-
def forward(self, x: torch.Tensor, context: Optional[torch.Tensor] = None, indices: Optional[torch.Tensor] = None, context_indices: Optional[torch.Tensor] = None) -> torch.Tensor:
|
| 102 |
-
B, L, C = x.shape
|
| 103 |
-
if self._type == "self":
|
| 104 |
-
qkv = self.to_qkv(x)
|
| 105 |
-
qkv = qkv.reshape(B, L, 3, self.num_heads, -1)
|
| 106 |
-
if self.qk_rms_norm:
|
| 107 |
-
q, k, v = qkv.unbind(dim=2)
|
| 108 |
-
q = self.q_rms_norm(q)
|
| 109 |
-
k = self.k_rms_norm(k)
|
| 110 |
-
qkv = torch.stack([q, k, v], dim=2)
|
| 111 |
-
h = scaled_dot_product_attention(qkv)
|
| 112 |
-
else:
|
| 113 |
-
Lkv = context.shape[1]
|
| 114 |
-
q = self.to_q(x)
|
| 115 |
-
kv = self.to_kv(context)
|
| 116 |
-
q = q.reshape(B, L, self.num_heads, -1)
|
| 117 |
-
kv = kv.reshape(B, Lkv, 2, self.num_heads, -1)
|
| 118 |
-
if self.qk_rms_norm:
|
| 119 |
-
q = self.q_rms_norm(q)
|
| 120 |
-
k, v = kv.unbind(dim=2)
|
| 121 |
-
k = self.k_rms_norm(k)
|
| 122 |
-
h = scaled_dot_product_attention(q, k, v)
|
| 123 |
-
else:
|
| 124 |
-
h = scaled_dot_product_attention(q, kv)
|
| 125 |
-
h = h.reshape(B, L, -1)
|
| 126 |
-
h = self.to_out(h)
|
| 127 |
-
return h
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
def apply_rotary_emb(hidden_states: torch.Tensor, freqs: torch.Tensor) -> torch.Tensor:
|
| 132 |
-
"""
|
| 133 |
-
Apply rotary embeddings to input tensors using the given frequencies.
|
| 134 |
-
Args:
|
| 135 |
-
hidden_states: [B, L, H, D]
|
| 136 |
-
freqs: [B, L, 1, D/2] complex tensor
|
| 137 |
-
"""
|
| 138 |
-
# [B, L, H, D] -> [B, L, H, D/2, 2] -> [B, L, H, D/2] (complex)
|
| 139 |
-
x_rotated = torch.view_as_complex(hidden_states.float().reshape(*hidden_states.shape[:-1], -1, 2))
|
| 140 |
-
|
| 141 |
-
# [B, L, H, D/2] * [B, L, 1, D/2] (broadcast) -> [B, L, H, D/2]
|
| 142 |
-
x_rotated = x_rotated * freqs
|
| 143 |
-
|
| 144 |
-
# [B, L, H, D/2] -> [B, L, H, D/2, 2] -> [B, L, H, D]
|
| 145 |
-
x_out = torch.view_as_real(x_rotated).reshape(*x_rotated.shape[:-1], -1)
|
| 146 |
-
return x_out.type_as(hidden_states)
|
| 147 |
-
|
| 148 |
-
def clamp_mul(x, f):
|
| 149 |
-
f_t = f.tanh()
|
| 150 |
-
return x * f_t + x.detach() * (f - f_t)
|
| 151 |
-
|
| 152 |
-
class RePo3DRotaryEmbedding(nn.Module):
|
| 153 |
-
def __init__(self, model_channels: int, num_heads: int, head_dim: int, repo_hidden_ratio: float = 0.125, max_freq: float = 16.0):
|
| 154 |
-
super().__init__()
|
| 155 |
-
self.num_heads = num_heads
|
| 156 |
-
self.head_dim = head_dim
|
| 157 |
-
repo_hidden_size = int(model_channels * repo_hidden_ratio)
|
| 158 |
-
|
| 159 |
-
# Lightweight Residual Network
|
| 160 |
-
self.norm = LayerNorm32(model_channels)
|
| 161 |
-
self.gate_map = nn.Linear(model_channels, repo_hidden_size, bias=False)
|
| 162 |
-
self.content_map = nn.Linear(model_channels, repo_hidden_size, bias=False)
|
| 163 |
-
self.act = nn.SiLU()
|
| 164 |
-
self.final_map = nn.Linear(repo_hidden_size, 3 * num_heads, bias=False)
|
| 165 |
-
|
| 166 |
-
# Split head_dim into 3 parts for x, y, z (same as Rotary3DPoseEmbedding)
|
| 167 |
-
self.dim_0 = 2 * (head_dim // 6)
|
| 168 |
-
self.dim_1 = 2 * (head_dim // 6)
|
| 169 |
-
self.dim_2 = head_dim - self.dim_0 - self.dim_1
|
| 170 |
-
self.max_freq = max_freq
|
| 171 |
-
|
| 172 |
-
dims = [self.dim_0, self.dim_1, self.dim_2]
|
| 173 |
-
freqs_list = []
|
| 174 |
-
for d in dims:
|
| 175 |
-
freq_dim = d // 2
|
| 176 |
-
freqs = torch.linspace(1.0, float(max_freq), steps=freq_dim, dtype=torch.float32)
|
| 177 |
-
freqs_list.append(freqs)
|
| 178 |
-
|
| 179 |
-
self.freqs_0 = nn.Parameter(freqs_list[0])
|
| 180 |
-
self.freqs_1 = nn.Parameter(freqs_list[1])
|
| 181 |
-
self.freqs_2 = nn.Parameter(freqs_list[2])
|
| 182 |
-
|
| 183 |
-
def initialize_weights(self):
|
| 184 |
-
# Initialize proj_out with small values
|
| 185 |
-
nn.init.constant_(self.final_map.weight, 0)
|
| 186 |
-
|
| 187 |
-
def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
|
| 188 |
-
# hidden_states: [B, L, C]
|
| 189 |
-
# base_rotary_emb: [1, L, 1, D/2] (or similar broadcastable shape)
|
| 190 |
-
# pos: [B, L, 3] original 3D coordinates
|
| 191 |
-
hidden_states = hidden_states.float()
|
| 192 |
-
# Lightweight fusion logic
|
| 193 |
-
h = self.norm(hidden_states)
|
| 194 |
-
feat = self.act(self.gate_map(h)) * self.content_map(h)
|
| 195 |
-
out = self.final_map(feat)
|
| 196 |
-
|
| 197 |
-
# Reshape to [B, L, 3 * H] -> [B, L, H, 3]
|
| 198 |
-
B, L, _ = out.shape
|
| 199 |
-
delta_pos = out.reshape(B, L, self.num_heads, 3)
|
| 200 |
-
|
| 201 |
-
# Compute angles for delta (rope like)
|
| 202 |
-
# delta_pos[..., 0] is [B, L, H]
|
| 203 |
-
# freqs_0 is [D0/2]
|
| 204 |
-
# We want [B, L, H, D0/2]
|
| 205 |
-
# delta_pos gradient scale clamping, avoid large freq gradient exploding
|
| 206 |
-
# forward equivalent to delta_pos * self.freqs_I * torch.pi
|
| 207 |
-
ang_0 = clamp_mul(delta_pos[..., 0].unsqueeze(-1), self.freqs_0) * torch.pi
|
| 208 |
-
ang_1 = clamp_mul(delta_pos[..., 1].unsqueeze(-1), self.freqs_1) * torch.pi
|
| 209 |
-
ang_2 = clamp_mul(delta_pos[..., 2].unsqueeze(-1), self.freqs_2) * torch.pi
|
| 210 |
-
|
| 211 |
-
# Concatenate angles [B, L, H, D/2]
|
| 212 |
-
ang = torch.cat([ang_0, ang_1, ang_2], dim=-1)
|
| 213 |
-
|
| 214 |
-
# Complex rotation for delta
|
| 215 |
-
delta_freqs_cis = torch.polar(torch.ones_like(ang), ang)
|
| 216 |
-
|
| 217 |
-
# Apply residual rotation: base * delta
|
| 218 |
-
# base_rotary_emb will broadcast to [B, L, H, D/2]
|
| 219 |
-
return delta_freqs_cis.type(torch.complex64)
|
| 220 |
-
|
| 221 |
-
|
| 222 |
-
class Rotary3DPoseEmbedding(nn.Module):
|
| 223 |
-
def __init__(self, head_dim: int, max_res: int = 10):
|
| 224 |
-
super().__init__()
|
| 225 |
-
self.head_dim = head_dim
|
| 226 |
-
|
| 227 |
-
# Split head_dim into 3 parts for x, y, z
|
| 228 |
-
# Each part must be divisible by 2 for complex RoPE
|
| 229 |
-
self.dim_0 = 2 * (head_dim // 6)
|
| 230 |
-
self.dim_1 = 2 * (head_dim // 6)
|
| 231 |
-
self.dim_2 = head_dim - self.dim_0 - self.dim_1
|
| 232 |
-
|
| 233 |
-
dims = [self.dim_0, self.dim_1, self.dim_2]
|
| 234 |
-
freqs_list = []
|
| 235 |
-
for d in dims:
|
| 236 |
-
freq_dim = d // 2
|
| 237 |
-
logs = torch.linspace(0.0, float(max_res), steps=freq_dim, dtype=torch.float32)
|
| 238 |
-
freqs = torch.pow(2.0, logs)
|
| 239 |
-
freqs_list.append(freqs)
|
| 240 |
-
|
| 241 |
-
self.register_buffer("freqs_0", freqs_list[0])
|
| 242 |
-
self.register_buffer("freqs_1", freqs_list[1])
|
| 243 |
-
self.register_buffer("freqs_2", freqs_list[2])
|
| 244 |
-
|
| 245 |
-
def forward(self, pos: torch.Tensor) -> torch.Tensor:
|
| 246 |
-
"""
|
| 247 |
-
Args:
|
| 248 |
-
pos: [B, L, 3] tensor of spatial positions in [0, 1]
|
| 249 |
-
Returns:
|
| 250 |
-
rope_emb: [B, L, 1, D/2] complex tensor
|
| 251 |
-
"""
|
| 252 |
-
# pos[..., 0]: [B, L]
|
| 253 |
-
# freqs_0: [D0/2]
|
| 254 |
-
# ang: [B, L, D0/2]
|
| 255 |
-
|
| 256 |
-
ang_0 = torch.outer(pos[..., 0].flatten(), self.freqs_0).view(*pos.shape[:-1], -1) * torch.pi
|
| 257 |
-
ang_1 = torch.outer(pos[..., 1].flatten(), self.freqs_1).view(*pos.shape[:-1], -1) * torch.pi
|
| 258 |
-
ang_2 = torch.outer(pos[..., 2].flatten(), self.freqs_2).view(*pos.shape[:-1], -1) * torch.pi
|
| 259 |
-
|
| 260 |
-
# Concatenate angles [B, L, D/2]
|
| 261 |
-
ang = torch.cat([ang_0, ang_1, ang_2], dim=-1)
|
| 262 |
-
|
| 263 |
-
# Convert to complex exponentials
|
| 264 |
-
freqs_cis = torch.polar(torch.ones_like(ang), ang)
|
| 265 |
-
|
| 266 |
-
# Add head dimension for broadcasting: [B, L, 1, D/2]
|
| 267 |
-
return freqs_cis.unsqueeze(-2).type(torch.complex64)
|
| 268 |
-
|
| 269 |
-
|
| 270 |
-
class RopeMultiHeadAttention(nn.Module):
|
| 271 |
-
def __init__(
|
| 272 |
-
self,
|
| 273 |
-
channels: int,
|
| 274 |
-
num_heads: int,
|
| 275 |
-
ctx_channels: Optional[int]=None,
|
| 276 |
-
type: Literal["self", "cross"] = "self",
|
| 277 |
-
qkv_bias: bool = True,
|
| 278 |
-
qk_rms_norm: bool = False,
|
| 279 |
-
use_rope: bool = False,
|
| 280 |
-
):
|
| 281 |
-
super().__init__()
|
| 282 |
-
self.channels = channels
|
| 283 |
-
self.num_heads = num_heads
|
| 284 |
-
self.head_dim = channels // num_heads
|
| 285 |
-
self.ctx_channels = ctx_channels if ctx_channels is not None else channels
|
| 286 |
-
self._type = type
|
| 287 |
-
self.qk_rms_norm = qk_rms_norm
|
| 288 |
-
self.use_rope = use_rope
|
| 289 |
-
|
| 290 |
-
assert channels % num_heads == 0
|
| 291 |
-
|
| 292 |
-
if self._type == "self":
|
| 293 |
-
self.qkv = nn.Linear(channels, channels * 3, bias=qkv_bias)
|
| 294 |
-
else:
|
| 295 |
-
self.q = nn.Linear(channels, channels, bias=qkv_bias)
|
| 296 |
-
self.kv = nn.Linear(self.ctx_channels, channels * 2, bias=qkv_bias)
|
| 297 |
-
|
| 298 |
-
if self.qk_rms_norm:
|
| 299 |
-
self.q_norm = MultiHeadRMSNorm(self.head_dim, num_heads)
|
| 300 |
-
self.k_norm = MultiHeadRMSNorm(self.head_dim, num_heads)
|
| 301 |
-
|
| 302 |
-
self.out = nn.Linear(channels, channels)
|
| 303 |
-
|
| 304 |
-
def forward(self, x: torch.Tensor, context: Optional[torch.Tensor] = None, rope_emb: Optional[torch.Tensor] = None) -> torch.Tensor:
|
| 305 |
-
"""
|
| 306 |
-
Args:
|
| 307 |
-
x: [B, L, C] query tensor
|
| 308 |
-
context: [B, S, C] context tensor (for cross attention)
|
| 309 |
-
rope_emb: [B, L, 1, D/2] precomputed complex RoPE embedding
|
| 310 |
-
"""
|
| 311 |
-
B, L, C = x.shape
|
| 312 |
-
|
| 313 |
-
if self._type == "self":
|
| 314 |
-
# [B, L, 3, H, D]
|
| 315 |
-
qkv = self.qkv(x).reshape(B, L, 3, self.num_heads, self.head_dim)
|
| 316 |
-
q, k, v = qkv.unbind(2) # [B, L, H, D]
|
| 317 |
-
|
| 318 |
-
if self.use_rope:
|
| 319 |
-
q = apply_rotary_emb(q, rope_emb)
|
| 320 |
-
k = apply_rotary_emb(k, rope_emb)
|
| 321 |
-
else:
|
| 322 |
-
# Cross attention
|
| 323 |
-
q = self.q(x).reshape(B, L, self.num_heads, self.head_dim)
|
| 324 |
-
if context is None:
|
| 325 |
-
raise ValueError("Context must be provided for cross attention")
|
| 326 |
-
kv = self.kv(context).reshape(B, context.shape[1], 2, self.num_heads, self.head_dim)
|
| 327 |
-
k, v = kv.unbind(2)
|
| 328 |
-
# No RoPE for cross attention per instructions
|
| 329 |
-
|
| 330 |
-
if self.qk_rms_norm:
|
| 331 |
-
q = self.q_norm(q)
|
| 332 |
-
k = self.k_norm(k)
|
| 333 |
-
|
| 334 |
-
h = scaled_dot_product_attention(q, k, v)
|
| 335 |
-
|
| 336 |
-
# [B, L, H, D] -> [B, L, C]
|
| 337 |
-
h = h.reshape(B, L, C)
|
| 338 |
-
return self.out(h)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
idol/modules/mlp.py
DELETED
|
@@ -1,16 +0,0 @@
|
|
| 1 |
-
import torch
|
| 2 |
-
import torch.nn as nn
|
| 3 |
-
from typing import *
|
| 4 |
-
|
| 5 |
-
class MLP(nn.Module):
|
| 6 |
-
def __init__(self, channels: int, inner_channels: int, channels_out: Optional[int] = None, mlp_layer_num: int = 2):
|
| 7 |
-
super().__init__()
|
| 8 |
-
layers = []
|
| 9 |
-
for i in range(mlp_layer_num - 1):
|
| 10 |
-
layers.append(nn.Linear(channels if i == 0 else inner_channels, inner_channels))
|
| 11 |
-
layers.append(nn.GELU(approximate="tanh"))
|
| 12 |
-
layers.append(nn.Linear(inner_channels, channels if channels_out is None else channels_out))
|
| 13 |
-
self.mlp = nn.Sequential(*layers)
|
| 14 |
-
|
| 15 |
-
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 16 |
-
return self.mlp(x)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
idol/modules/norm.py
DELETED
|
@@ -1,29 +0,0 @@
|
|
| 1 |
-
import torch
|
| 2 |
-
import torch.nn as nn
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
class LayerNorm32(nn.LayerNorm):
|
| 6 |
-
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 7 |
-
return super().forward(x.float()).type(x.dtype)
|
| 8 |
-
|
| 9 |
-
class RMSNorm32(nn.RMSNorm):
|
| 10 |
-
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 11 |
-
return super().forward(x.float()).type(x.dtype)
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
class GroupNorm32(nn.GroupNorm):
|
| 15 |
-
"""
|
| 16 |
-
A GroupNorm layer that converts to float32 before the forward pass.
|
| 17 |
-
"""
|
| 18 |
-
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 19 |
-
return super().forward(x.float()).type(x.dtype)
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
class ChannelLayerNorm32(LayerNorm32):
|
| 23 |
-
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 24 |
-
DIM = x.dim()
|
| 25 |
-
x = x.permute(0, *range(2, DIM), 1).contiguous()
|
| 26 |
-
x = super().forward(x)
|
| 27 |
-
x = x.permute(0, DIM-1, *range(1, DIM-1)).contiguous()
|
| 28 |
-
return x
|
| 29 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
idol/modules/transformer/__init__.py
DELETED
|
@@ -1,2 +0,0 @@
|
|
| 1 |
-
from .blocks import *
|
| 2 |
-
from .modulated import *
|
|
|
|
|
|
|
|
|
idol/modules/transformer/blocks.py
DELETED
|
@@ -1,177 +0,0 @@
|
|
| 1 |
-
from typing import *
|
| 2 |
-
import torch
|
| 3 |
-
import torch.nn as nn
|
| 4 |
-
from ..attention import MultiHeadAttention
|
| 5 |
-
from ..norm import LayerNorm32
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
class AbsolutePositionEmbedder(nn.Module):
|
| 9 |
-
"""
|
| 10 |
-
Embeds spatial positions into vector representations.
|
| 11 |
-
"""
|
| 12 |
-
def __init__(self, channels: int, in_channels: int = 3):
|
| 13 |
-
super().__init__()
|
| 14 |
-
self.channels = channels
|
| 15 |
-
self.in_channels = in_channels
|
| 16 |
-
self.freq_dim = channels // in_channels // 2
|
| 17 |
-
self.freqs = torch.arange(self.freq_dim, dtype=torch.float32) / self.freq_dim
|
| 18 |
-
self.freqs = 1.0 / (10000 ** self.freqs)
|
| 19 |
-
|
| 20 |
-
def _sin_cos_embedding(self, x: torch.Tensor) -> torch.Tensor:
|
| 21 |
-
"""
|
| 22 |
-
Create sinusoidal position embeddings.
|
| 23 |
-
|
| 24 |
-
Args:
|
| 25 |
-
x: a 1-D Tensor of N indices
|
| 26 |
-
|
| 27 |
-
Returns:
|
| 28 |
-
an (N, D) Tensor of positional embeddings.
|
| 29 |
-
"""
|
| 30 |
-
self.freqs = self.freqs.to(x.device)
|
| 31 |
-
out = torch.outer(x, self.freqs)
|
| 32 |
-
out = torch.cat([torch.sin(out), torch.cos(out)], dim=-1)
|
| 33 |
-
return out
|
| 34 |
-
|
| 35 |
-
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 36 |
-
"""
|
| 37 |
-
Args:
|
| 38 |
-
x (torch.Tensor): (N, D) tensor of spatial positions
|
| 39 |
-
"""
|
| 40 |
-
N, D = x.shape
|
| 41 |
-
assert D == self.in_channels, "Input dimension must match number of input channels"
|
| 42 |
-
embed = self._sin_cos_embedding(x.reshape(-1))
|
| 43 |
-
embed = embed.reshape(N, -1)
|
| 44 |
-
if embed.shape[1] < self.channels:
|
| 45 |
-
embed = torch.cat([embed, torch.zeros(N, self.channels - embed.shape[1], device=embed.device)], dim=-1)
|
| 46 |
-
return embed
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
class FeedForwardNet(nn.Module):
|
| 50 |
-
def __init__(self, channels: int, mlp_ratio: float = 4.0, channels_out: Optional[int] = None):
|
| 51 |
-
super().__init__()
|
| 52 |
-
self.mlp = nn.Sequential(
|
| 53 |
-
nn.Linear(channels, int(channels * mlp_ratio)),
|
| 54 |
-
nn.GELU(approximate="tanh"),
|
| 55 |
-
nn.Linear(int(channels * mlp_ratio), channels if channels_out is None else channels_out),
|
| 56 |
-
)
|
| 57 |
-
|
| 58 |
-
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 59 |
-
return self.mlp(x)
|
| 60 |
-
|
| 61 |
-
class TransformerBlock(nn.Module):
|
| 62 |
-
"""
|
| 63 |
-
Transformer block (MSA + FFN).
|
| 64 |
-
"""
|
| 65 |
-
def __init__(
|
| 66 |
-
self,
|
| 67 |
-
channels: int,
|
| 68 |
-
num_heads: int,
|
| 69 |
-
mlp_ratio: float = 4.0,
|
| 70 |
-
use_checkpoint: bool = False,
|
| 71 |
-
qk_rms_norm: bool = False,
|
| 72 |
-
qkv_bias: bool = True,
|
| 73 |
-
):
|
| 74 |
-
super().__init__()
|
| 75 |
-
self.use_checkpoint = use_checkpoint
|
| 76 |
-
self.norm1 = LayerNorm32(channels, elementwise_affine=False, eps=1e-6)
|
| 77 |
-
self.norm2 = LayerNorm32(channels, elementwise_affine=False, eps=1e-6)
|
| 78 |
-
self.attn = MultiHeadAttention(
|
| 79 |
-
channels,
|
| 80 |
-
num_heads=num_heads,
|
| 81 |
-
qkv_bias=qkv_bias,
|
| 82 |
-
qk_rms_norm=qk_rms_norm,
|
| 83 |
-
)
|
| 84 |
-
self.mlp = FeedForwardNet(
|
| 85 |
-
channels,
|
| 86 |
-
mlp_ratio=mlp_ratio,
|
| 87 |
-
)
|
| 88 |
-
|
| 89 |
-
def _forward(self, x: torch.Tensor, indices=None) -> torch.Tensor:
|
| 90 |
-
h = self.norm1(x)
|
| 91 |
-
h = self.attn(h, indices=indices)
|
| 92 |
-
x = x + h
|
| 93 |
-
h = self.norm2(x)
|
| 94 |
-
h = self.mlp(h)
|
| 95 |
-
x = x + h
|
| 96 |
-
return x
|
| 97 |
-
|
| 98 |
-
def forward(self, x: torch.Tensor, indices=None) -> torch.Tensor:
|
| 99 |
-
if self.use_checkpoint:
|
| 100 |
-
return torch.utils.checkpoint.checkpoint(self._forward, x, indices, use_reentrant=False)
|
| 101 |
-
else:
|
| 102 |
-
return self._forward(x, indices=indices)
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
class TransformerCrossBlock(nn.Module):
|
| 106 |
-
"""
|
| 107 |
-
Transformer cross-attention block (MSA + MCA + FFN).
|
| 108 |
-
"""
|
| 109 |
-
def __init__(
|
| 110 |
-
self,
|
| 111 |
-
channels: int,
|
| 112 |
-
ctx_channels: int,
|
| 113 |
-
num_heads: int,
|
| 114 |
-
mlp_ratio: float = 4.0,
|
| 115 |
-
use_checkpoint: bool = False,
|
| 116 |
-
qk_rms_norm: bool = False,
|
| 117 |
-
qk_rms_norm_cross: bool = False,
|
| 118 |
-
qkv_bias: bool = True,
|
| 119 |
-
use_2_cross_block: bool = False,
|
| 120 |
-
):
|
| 121 |
-
super().__init__()
|
| 122 |
-
self.use_checkpoint = use_checkpoint
|
| 123 |
-
self.use_2_cross_block = use_2_cross_block
|
| 124 |
-
self.norm1 = LayerNorm32(channels, elementwise_affine=False, eps=1e-6)
|
| 125 |
-
self.norm2 = LayerNorm32(channels, elementwise_affine=True, eps=1e-6)
|
| 126 |
-
self.norm3 = LayerNorm32(channels, elementwise_affine=False, eps=1e-6)
|
| 127 |
-
self.self_attn = MultiHeadAttention(
|
| 128 |
-
channels,
|
| 129 |
-
num_heads=num_heads,
|
| 130 |
-
type="self",
|
| 131 |
-
qkv_bias=qkv_bias,
|
| 132 |
-
qk_rms_norm=qk_rms_norm,
|
| 133 |
-
)
|
| 134 |
-
self.cross_attn = MultiHeadAttention(
|
| 135 |
-
channels,
|
| 136 |
-
ctx_channels=ctx_channels,
|
| 137 |
-
num_heads=num_heads,
|
| 138 |
-
type="cross",
|
| 139 |
-
qkv_bias=qkv_bias,
|
| 140 |
-
qk_rms_norm=qk_rms_norm_cross,
|
| 141 |
-
)
|
| 142 |
-
if self.use_2_cross_block:
|
| 143 |
-
self.norm2_1 = LayerNorm32(channels, elementwise_affine=True, eps=1e-6)
|
| 144 |
-
self.cross_attn2 = MultiHeadAttention(
|
| 145 |
-
channels,
|
| 146 |
-
ctx_channels=ctx_channels,
|
| 147 |
-
num_heads=num_heads,
|
| 148 |
-
type="cross",
|
| 149 |
-
qkv_bias=qkv_bias,
|
| 150 |
-
qk_rms_norm=qk_rms_norm_cross,
|
| 151 |
-
)
|
| 152 |
-
self.mlp = FeedForwardNet(
|
| 153 |
-
channels,
|
| 154 |
-
mlp_ratio=mlp_ratio,
|
| 155 |
-
)
|
| 156 |
-
|
| 157 |
-
def _forward(self, x: torch.Tensor, context: torch.Tensor, context2=None, indices=None, context_indices=None, context2_indices=None) -> torch.Tensor:
|
| 158 |
-
h = self.norm1(x)
|
| 159 |
-
h = self.self_attn(h, indices=indices)
|
| 160 |
-
x = x + h
|
| 161 |
-
h = self.norm2(x)
|
| 162 |
-
h = self.cross_attn(h, context, context_indices=context_indices)
|
| 163 |
-
x = x + h
|
| 164 |
-
if self.use_2_cross_block:
|
| 165 |
-
h = self.norm2_1(x)
|
| 166 |
-
h = self.cross_attn2(h, context2, context_indices=context2_indices)
|
| 167 |
-
x = x + h
|
| 168 |
-
h = self.norm3(x)
|
| 169 |
-
h = self.mlp(h)
|
| 170 |
-
x = x + h
|
| 171 |
-
return x
|
| 172 |
-
|
| 173 |
-
def forward(self, x: torch.Tensor, context: torch.Tensor, context2=None, indices=None, context_indices=None, context2_indices=None) -> torch.Tensor:
|
| 174 |
-
if self.use_checkpoint:
|
| 175 |
-
return torch.utils.checkpoint.checkpoint(self._forward, x, context, context2, indices, context_indices, context2_indices, use_reentrant=False)
|
| 176 |
-
else:
|
| 177 |
-
return self._forward(x, context, context2=context2, indices=indices, context_indices=context_indices, context2_indices=context2_indices)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
idol/modules/transformer/modulated.py
DELETED
|
@@ -1,381 +0,0 @@
|
|
| 1 |
-
from typing import *
|
| 2 |
-
import torch
|
| 3 |
-
import torch.nn as nn
|
| 4 |
-
|
| 5 |
-
from ..attention.modules import RopeMultiHeadAttention
|
| 6 |
-
from ..attention import MultiHeadAttention
|
| 7 |
-
from ..norm import LayerNorm32, RMSNorm32
|
| 8 |
-
from .blocks import FeedForwardNet
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
class ModulatedTransformerBlock(nn.Module):
|
| 12 |
-
"""
|
| 13 |
-
Transformer block (MSA + FFN) with adaptive layer norm conditioning.
|
| 14 |
-
"""
|
| 15 |
-
def __init__(
|
| 16 |
-
self,
|
| 17 |
-
channels: int,
|
| 18 |
-
num_heads: int,
|
| 19 |
-
mlp_ratio: float = 4.0,
|
| 20 |
-
use_checkpoint: bool = False,
|
| 21 |
-
use_rope: bool = False,
|
| 22 |
-
qk_rms_norm: bool = False,
|
| 23 |
-
qkv_bias: bool = True,
|
| 24 |
-
share_mod: bool = False,
|
| 25 |
-
):
|
| 26 |
-
super().__init__()
|
| 27 |
-
self.use_checkpoint = use_checkpoint
|
| 28 |
-
self.share_mod = share_mod
|
| 29 |
-
self.norm1 = LayerNorm32(channels, elementwise_affine=False, eps=1e-6)
|
| 30 |
-
self.norm2 = LayerNorm32(channels, elementwise_affine=False, eps=1e-6)
|
| 31 |
-
self.attn = MultiHeadAttention(
|
| 32 |
-
channels,
|
| 33 |
-
num_heads=num_heads,
|
| 34 |
-
qkv_bias=qkv_bias,
|
| 35 |
-
qk_rms_norm=qk_rms_norm,
|
| 36 |
-
)
|
| 37 |
-
self.mlp = FeedForwardNet(
|
| 38 |
-
channels,
|
| 39 |
-
mlp_ratio=mlp_ratio,
|
| 40 |
-
)
|
| 41 |
-
if not share_mod:
|
| 42 |
-
self.adaLN_modulation = nn.Sequential(
|
| 43 |
-
nn.SiLU(),
|
| 44 |
-
nn.Linear(channels, 6 * channels, bias=True)
|
| 45 |
-
)
|
| 46 |
-
|
| 47 |
-
def _forward(self, x: torch.Tensor, mod: torch.Tensor) -> torch.Tensor:
|
| 48 |
-
if self.share_mod:
|
| 49 |
-
shift_msa, scale_msa, gate_msa, shift_mlp, scale_mlp, gate_mlp = mod.chunk(6, dim=1)
|
| 50 |
-
else:
|
| 51 |
-
shift_msa, scale_msa, gate_msa, shift_mlp, scale_mlp, gate_mlp = self.adaLN_modulation(mod).chunk(6, dim=1)
|
| 52 |
-
h = self.norm1(x)
|
| 53 |
-
h = h * (1 + scale_msa.unsqueeze(1)) + shift_msa.unsqueeze(1)
|
| 54 |
-
h = self.attn(h)
|
| 55 |
-
h = h * gate_msa.unsqueeze(1)
|
| 56 |
-
x = x + h
|
| 57 |
-
h = self.norm2(x)
|
| 58 |
-
h = h * (1 + scale_mlp.unsqueeze(1)) + shift_mlp.unsqueeze(1)
|
| 59 |
-
h = self.mlp(h)
|
| 60 |
-
h = h * gate_mlp.unsqueeze(1)
|
| 61 |
-
x = x + h
|
| 62 |
-
return x
|
| 63 |
-
|
| 64 |
-
def forward(self, x: torch.Tensor, mod: torch.Tensor) -> torch.Tensor:
|
| 65 |
-
if self.use_checkpoint:
|
| 66 |
-
return torch.utils.checkpoint.checkpoint(self._forward, x, mod, use_reentrant=False)
|
| 67 |
-
else:
|
| 68 |
-
return self._forward(x, mod)
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
class ModulatedTransformerCrossBlock(nn.Module):
|
| 72 |
-
"""
|
| 73 |
-
Transformer cross-attention block (MSA + MCA + FFN) with adaptive layer norm conditioning.
|
| 74 |
-
"""
|
| 75 |
-
def __init__(
|
| 76 |
-
self,
|
| 77 |
-
channels: int,
|
| 78 |
-
ctx_channels: int,
|
| 79 |
-
num_heads: int,
|
| 80 |
-
mlp_ratio: float = 4.0,
|
| 81 |
-
use_checkpoint: bool = False,
|
| 82 |
-
use_rope: bool = False,
|
| 83 |
-
qk_rms_norm: bool = False,
|
| 84 |
-
qk_rms_norm_cross: bool = False,
|
| 85 |
-
qkv_bias: bool = True,
|
| 86 |
-
share_mod: bool = False,
|
| 87 |
-
use_shift_table: bool = False,
|
| 88 |
-
):
|
| 89 |
-
super().__init__()
|
| 90 |
-
self.use_checkpoint = use_checkpoint
|
| 91 |
-
self.share_mod = share_mod
|
| 92 |
-
self.norm1 = LayerNorm32(channels, elementwise_affine=False, eps=1e-6)
|
| 93 |
-
self.norm2 = LayerNorm32(channels, elementwise_affine=True, eps=1e-6)
|
| 94 |
-
self.norm3 = LayerNorm32(channels, elementwise_affine=False, eps=1e-6)
|
| 95 |
-
self.self_attn = MultiHeadAttention(
|
| 96 |
-
channels,
|
| 97 |
-
num_heads=num_heads,
|
| 98 |
-
type="self",
|
| 99 |
-
qkv_bias=qkv_bias,
|
| 100 |
-
qk_rms_norm=qk_rms_norm,
|
| 101 |
-
)
|
| 102 |
-
self.cross_attn = MultiHeadAttention(
|
| 103 |
-
channels,
|
| 104 |
-
ctx_channels=ctx_channels,
|
| 105 |
-
num_heads=num_heads,
|
| 106 |
-
type="cross",
|
| 107 |
-
qkv_bias=qkv_bias,
|
| 108 |
-
qk_rms_norm=qk_rms_norm_cross,
|
| 109 |
-
)
|
| 110 |
-
self.mlp = FeedForwardNet(
|
| 111 |
-
channels,
|
| 112 |
-
mlp_ratio=mlp_ratio,
|
| 113 |
-
)
|
| 114 |
-
if not share_mod:
|
| 115 |
-
self.adaLN_modulation = nn.Sequential(
|
| 116 |
-
nn.SiLU(),
|
| 117 |
-
nn.Linear(channels, 6 * channels, bias=True)
|
| 118 |
-
)
|
| 119 |
-
if use_shift_table:
|
| 120 |
-
self.shift_table = nn.Parameter(torch.randn(1, 6 * channels) / channels**0.5)
|
| 121 |
-
else:
|
| 122 |
-
self.shift_table = None
|
| 123 |
-
|
| 124 |
-
def _forward(self, x: torch.Tensor, mod: torch.Tensor, context: torch.Tensor):
|
| 125 |
-
if not self.share_mod:
|
| 126 |
-
mod = self.adaLN_modulation(mod)
|
| 127 |
-
if self.shift_table is not None:
|
| 128 |
-
mod = mod + self.shift_table.type(mod.dtype)
|
| 129 |
-
shift_msa, scale_msa, gate_msa, shift_mlp, scale_mlp, gate_mlp = mod.chunk(6, dim=1)
|
| 130 |
-
h = self.norm1(x)
|
| 131 |
-
h = h * (1 + scale_msa.unsqueeze(1)) + shift_msa.unsqueeze(1)
|
| 132 |
-
h = self.self_attn(h)
|
| 133 |
-
h = h * gate_msa.unsqueeze(1)
|
| 134 |
-
x = x + h
|
| 135 |
-
h = self.norm2(x)
|
| 136 |
-
h = self.cross_attn(h, context)
|
| 137 |
-
x = x + h
|
| 138 |
-
h = self.norm3(x)
|
| 139 |
-
h = h * (1 + scale_mlp.unsqueeze(1)) + shift_mlp.unsqueeze(1)
|
| 140 |
-
h = self.mlp(h)
|
| 141 |
-
h = h * gate_mlp.unsqueeze(1)
|
| 142 |
-
x = x + h
|
| 143 |
-
return x
|
| 144 |
-
|
| 145 |
-
def forward(self, x: torch.Tensor, mod: torch.Tensor, context: torch.Tensor):
|
| 146 |
-
if self.use_checkpoint:
|
| 147 |
-
return torch.utils.checkpoint.checkpoint(self._forward, x, mod, context, use_reentrant=False)
|
| 148 |
-
else:
|
| 149 |
-
return self._forward(x, mod, context)
|
| 150 |
-
|
| 151 |
-
|
| 152 |
-
class UnifiedTransformerBlock(nn.Module):
|
| 153 |
-
"""
|
| 154 |
-
Transformer block (MSA + FFN) with optional adaptive layer norm conditioning and RoPE support.
|
| 155 |
-
Used for Unified MMDiT.
|
| 156 |
-
"""
|
| 157 |
-
def __init__(
|
| 158 |
-
self,
|
| 159 |
-
channels: int,
|
| 160 |
-
num_heads: int,
|
| 161 |
-
mlp_ratio: float = 4.0,
|
| 162 |
-
use_checkpoint: bool = False,
|
| 163 |
-
use_rope: bool = False,
|
| 164 |
-
qk_rms_norm: bool = False,
|
| 165 |
-
qkv_bias: bool = True,
|
| 166 |
-
modulation: bool = True,
|
| 167 |
-
share_mod: bool = False,
|
| 168 |
-
use_shift_table: bool = False,
|
| 169 |
-
):
|
| 170 |
-
super().__init__()
|
| 171 |
-
self.use_checkpoint = use_checkpoint
|
| 172 |
-
self.modulation = modulation
|
| 173 |
-
self.share_mod = share_mod
|
| 174 |
-
|
| 175 |
-
# If modulation is used, we usually disable elementwise_affine in LN because modulation handles it.
|
| 176 |
-
self.norm1 = LayerNorm32(channels, elementwise_affine=not modulation, eps=1e-6)
|
| 177 |
-
self.norm2 = LayerNorm32(channels, elementwise_affine=not modulation, eps=1e-6)
|
| 178 |
-
|
| 179 |
-
self.attn = RopeMultiHeadAttention(
|
| 180 |
-
channels,
|
| 181 |
-
num_heads=num_heads,
|
| 182 |
-
type="self",
|
| 183 |
-
qkv_bias=qkv_bias,
|
| 184 |
-
use_rope=use_rope,
|
| 185 |
-
qk_rms_norm=qk_rms_norm,
|
| 186 |
-
)
|
| 187 |
-
self.mlp = FeedForwardNet(
|
| 188 |
-
channels,
|
| 189 |
-
mlp_ratio=mlp_ratio,
|
| 190 |
-
)
|
| 191 |
-
|
| 192 |
-
if modulation:
|
| 193 |
-
if not share_mod:
|
| 194 |
-
self.adaLN_modulation = nn.Sequential(
|
| 195 |
-
nn.SiLU(),
|
| 196 |
-
nn.Linear(channels, 6 * channels, bias=True)
|
| 197 |
-
)
|
| 198 |
-
if use_shift_table:
|
| 199 |
-
self.shift_table = nn.Parameter(torch.randn(1, 6 * channels) / channels**0.5)
|
| 200 |
-
else:
|
| 201 |
-
self.shift_table = None
|
| 202 |
-
|
| 203 |
-
def _forward(self, x: torch.Tensor, mod: Optional[torch.Tensor] = None, rotary_emb: Optional[torch.Tensor] = None):
|
| 204 |
-
if self.modulation:
|
| 205 |
-
assert mod is not None, "Modulation tensor must be provided when modulation is enabled"
|
| 206 |
-
if not self.share_mod:
|
| 207 |
-
mod = self.adaLN_modulation(mod)
|
| 208 |
-
if hasattr(self, 'shift_table') and self.shift_table is not None:
|
| 209 |
-
mod = mod + self.shift_table.type(mod.dtype)
|
| 210 |
-
shift_msa, scale_msa, gate_msa, shift_mlp, scale_mlp, gate_mlp = mod.chunk(6, dim=1)
|
| 211 |
-
|
| 212 |
-
h = self.norm1(x)
|
| 213 |
-
h = h * (1 + scale_msa.unsqueeze(1)) + shift_msa.unsqueeze(1)
|
| 214 |
-
h = self.attn(h, rope_emb=rotary_emb)
|
| 215 |
-
h = h * gate_msa.unsqueeze(1)
|
| 216 |
-
x = x + h
|
| 217 |
-
|
| 218 |
-
h = self.norm2(x)
|
| 219 |
-
h = h * (1 + scale_mlp.unsqueeze(1)) + shift_mlp.unsqueeze(1)
|
| 220 |
-
h = self.mlp(h)
|
| 221 |
-
h = h * gate_mlp.unsqueeze(1)
|
| 222 |
-
x = x + h
|
| 223 |
-
else:
|
| 224 |
-
h = self.norm1(x)
|
| 225 |
-
h = self.attn(h, rope_emb=rotary_emb)
|
| 226 |
-
x = x + h
|
| 227 |
-
|
| 228 |
-
h = self.norm2(x)
|
| 229 |
-
h = self.mlp(h)
|
| 230 |
-
x = x + h
|
| 231 |
-
|
| 232 |
-
return x
|
| 233 |
-
|
| 234 |
-
def forward(self, x: torch.Tensor, mod: Optional[torch.Tensor] = None, rotary_emb: Optional[torch.Tensor] = None):
|
| 235 |
-
if self.use_checkpoint:
|
| 236 |
-
return torch.utils.checkpoint.checkpoint(self._forward, x, mod, rotary_emb, use_reentrant=False)
|
| 237 |
-
else:
|
| 238 |
-
return self._forward(x, mod, rotary_emb)
|
| 239 |
-
|
| 240 |
-
|
| 241 |
-
class ModulatedTransformerCrossBlockv2(nn.Module):
|
| 242 |
-
"""
|
| 243 |
-
Transformer cross-attention block (MSA + MCA + FFN) with adaptive layer norm conditioning.
|
| 244 |
-
"""
|
| 245 |
-
def __init__(
|
| 246 |
-
self,
|
| 247 |
-
channels: int,
|
| 248 |
-
ctx_channels: int,
|
| 249 |
-
num_heads: int,
|
| 250 |
-
mlp_ratio: float = 4.0,
|
| 251 |
-
use_checkpoint: bool = False,
|
| 252 |
-
use_rope: bool = False,
|
| 253 |
-
qk_rms_norm: bool = False,
|
| 254 |
-
qk_rms_norm_cross: bool = False,
|
| 255 |
-
qkv_bias: bool = True,
|
| 256 |
-
share_mod: bool = False,
|
| 257 |
-
use_shift_table: bool = False,
|
| 258 |
-
):
|
| 259 |
-
super().__init__()
|
| 260 |
-
self.use_checkpoint = use_checkpoint
|
| 261 |
-
self.share_mod = share_mod
|
| 262 |
-
self.norm1 = LayerNorm32(channels, elementwise_affine=False, eps=1e-6)
|
| 263 |
-
self.norm2 = LayerNorm32(channels, elementwise_affine=True, eps=1e-6)
|
| 264 |
-
self.norm3 = LayerNorm32(channels, elementwise_affine=False, eps=1e-6)
|
| 265 |
-
self.self_attn = RopeMultiHeadAttention(
|
| 266 |
-
channels,
|
| 267 |
-
num_heads=num_heads,
|
| 268 |
-
type="self",
|
| 269 |
-
qkv_bias=qkv_bias,
|
| 270 |
-
use_rope=use_rope,
|
| 271 |
-
qk_rms_norm=qk_rms_norm,
|
| 272 |
-
)
|
| 273 |
-
self.cross_attn = RopeMultiHeadAttention(
|
| 274 |
-
channels,
|
| 275 |
-
ctx_channels=ctx_channels,
|
| 276 |
-
num_heads=num_heads,
|
| 277 |
-
type="cross",
|
| 278 |
-
qkv_bias=qkv_bias,
|
| 279 |
-
qk_rms_norm=qk_rms_norm_cross,
|
| 280 |
-
)
|
| 281 |
-
self.mlp = FeedForwardNet(
|
| 282 |
-
channels,
|
| 283 |
-
mlp_ratio=mlp_ratio,
|
| 284 |
-
)
|
| 285 |
-
if not share_mod:
|
| 286 |
-
self.adaLN_modulation = nn.Sequential(
|
| 287 |
-
nn.SiLU(),
|
| 288 |
-
nn.Linear(channels, 6 * channels, bias=True)
|
| 289 |
-
)
|
| 290 |
-
if use_shift_table:
|
| 291 |
-
self.shift_table = nn.Parameter(torch.randn(1, 6 * channels) / channels**0.5)
|
| 292 |
-
else:
|
| 293 |
-
self.shift_table = None
|
| 294 |
-
|
| 295 |
-
def _forward(self, x: torch.Tensor, mod: torch.Tensor, context: torch.Tensor, rotary_emb: Optional[torch.Tensor] = None):
|
| 296 |
-
if not self.share_mod:
|
| 297 |
-
mod = self.adaLN_modulation(mod)
|
| 298 |
-
if self.shift_table is not None:
|
| 299 |
-
mod = mod + self.shift_table.type(mod.dtype)
|
| 300 |
-
shift_msa, scale_msa, gate_msa, shift_mlp, scale_mlp, gate_mlp = mod.chunk(6, dim=1)
|
| 301 |
-
h = self.norm1(x)
|
| 302 |
-
h = h * (1 + scale_msa.unsqueeze(1)) + shift_msa.unsqueeze(1)
|
| 303 |
-
h = self.self_attn(h, rope_emb=rotary_emb)
|
| 304 |
-
h = h * gate_msa.unsqueeze(1)
|
| 305 |
-
x = x + h
|
| 306 |
-
h = self.norm2(x)
|
| 307 |
-
h = self.cross_attn(h, context)
|
| 308 |
-
x = x + h
|
| 309 |
-
h = self.norm3(x)
|
| 310 |
-
h = h * (1 + scale_mlp.unsqueeze(1)) + shift_mlp.unsqueeze(1)
|
| 311 |
-
h = self.mlp(h)
|
| 312 |
-
h = h * gate_mlp.unsqueeze(1)
|
| 313 |
-
x = x + h
|
| 314 |
-
return x
|
| 315 |
-
|
| 316 |
-
def forward(self, x: torch.Tensor, mod: torch.Tensor, context: torch.Tensor, rotary_emb: Optional[torch.Tensor] = None):
|
| 317 |
-
if self.use_checkpoint:
|
| 318 |
-
return torch.utils.checkpoint.checkpoint(self._forward, x, mod, context, rotary_emb, use_reentrant=False)
|
| 319 |
-
else:
|
| 320 |
-
return self._forward(x, mod, context, rotary_emb)
|
| 321 |
-
|
| 322 |
-
class ModulatedTransformerCrossOnlyBlock(nn.Module):
|
| 323 |
-
"""
|
| 324 |
-
Transformer cross-attention block (MSA + MCA + FFN) with adaptive layer norm conditioning.
|
| 325 |
-
"""
|
| 326 |
-
def __init__(
|
| 327 |
-
self,
|
| 328 |
-
channels: int,
|
| 329 |
-
ctx_channels: int,
|
| 330 |
-
num_heads: int,
|
| 331 |
-
mlp_ratio: float = 4.0,
|
| 332 |
-
use_checkpoint: bool = False,
|
| 333 |
-
qk_rms_norm_cross: bool = True,
|
| 334 |
-
qkv_bias: bool = True,
|
| 335 |
-
share_mod: bool = False,
|
| 336 |
-
):
|
| 337 |
-
super().__init__()
|
| 338 |
-
self.use_checkpoint = use_checkpoint
|
| 339 |
-
self.share_mod = share_mod
|
| 340 |
-
self.norm1 = LayerNorm32(channels, elementwise_affine=False, eps=1e-6)
|
| 341 |
-
self.norm2 = LayerNorm32(channels, elementwise_affine=False, eps=1e-6)
|
| 342 |
-
self.cross_attn = MultiHeadAttention(
|
| 343 |
-
channels,
|
| 344 |
-
ctx_channels=ctx_channels,
|
| 345 |
-
num_heads=num_heads,
|
| 346 |
-
type="cross",
|
| 347 |
-
qkv_bias=qkv_bias,
|
| 348 |
-
qk_rms_norm=qk_rms_norm_cross,
|
| 349 |
-
)
|
| 350 |
-
self.mlp = FeedForwardNet(
|
| 351 |
-
channels,
|
| 352 |
-
mlp_ratio=mlp_ratio,
|
| 353 |
-
)
|
| 354 |
-
if not share_mod:
|
| 355 |
-
self.adaLN_modulation = nn.Sequential(
|
| 356 |
-
nn.SiLU(),
|
| 357 |
-
nn.Linear(channels, 6 * channels, bias=True)
|
| 358 |
-
)
|
| 359 |
-
|
| 360 |
-
def _forward(self, x: torch.Tensor, mod: torch.Tensor, context: torch.Tensor):
|
| 361 |
-
if self.share_mod:
|
| 362 |
-
shift_msa, scale_msa, gate_msa, shift_mlp, scale_mlp, gate_mlp = mod.chunk(6, dim=1)
|
| 363 |
-
else:
|
| 364 |
-
shift_msa, scale_msa, gate_msa, shift_mlp, scale_mlp, gate_mlp = self.adaLN_modulation(mod).chunk(6, dim=1)
|
| 365 |
-
h = self.norm1(x)
|
| 366 |
-
h = h * (1 + scale_msa.unsqueeze(1)) + shift_msa.unsqueeze(1)
|
| 367 |
-
h = self.cross_attn(h, context)
|
| 368 |
-
h = h * gate_msa.unsqueeze(1)
|
| 369 |
-
x = x + h
|
| 370 |
-
h = self.norm2(x)
|
| 371 |
-
h = h * (1 + scale_mlp.unsqueeze(1)) + shift_mlp.unsqueeze(1)
|
| 372 |
-
h = self.mlp(h)
|
| 373 |
-
h = h * gate_mlp.unsqueeze(1)
|
| 374 |
-
x = x + h
|
| 375 |
-
return x
|
| 376 |
-
|
| 377 |
-
def forward(self, x: torch.Tensor, mod: torch.Tensor, context: torch.Tensor):
|
| 378 |
-
if self.use_checkpoint:
|
| 379 |
-
return torch.utils.checkpoint.checkpoint(self._forward, x, mod, context, use_reentrant=False)
|
| 380 |
-
else:
|
| 381 |
-
return self._forward(x, mod, context)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
idol/modules/utils.py
DELETED
|
@@ -1,50 +0,0 @@
|
|
| 1 |
-
import torch.nn as nn
|
| 2 |
-
|
| 3 |
-
FP16_MODULES = (
|
| 4 |
-
nn.Conv1d,
|
| 5 |
-
nn.Conv2d,
|
| 6 |
-
nn.Conv3d,
|
| 7 |
-
nn.ConvTranspose1d,
|
| 8 |
-
nn.ConvTranspose2d,
|
| 9 |
-
nn.ConvTranspose3d,
|
| 10 |
-
nn.Linear,
|
| 11 |
-
)
|
| 12 |
-
|
| 13 |
-
def convert_module_to_f16(l):
|
| 14 |
-
"""
|
| 15 |
-
Convert primitive modules to float16.
|
| 16 |
-
"""
|
| 17 |
-
if isinstance(l, FP16_MODULES):
|
| 18 |
-
for p in l.parameters():
|
| 19 |
-
p.data = p.data.half()
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
def convert_module_to_f32(l):
|
| 23 |
-
"""
|
| 24 |
-
Convert primitive modules to float32, undoing convert_module_to_f16().
|
| 25 |
-
"""
|
| 26 |
-
if isinstance(l, FP16_MODULES):
|
| 27 |
-
for p in l.parameters():
|
| 28 |
-
p.data = p.data.float()
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
def zero_module(module):
|
| 32 |
-
"""
|
| 33 |
-
Zero out the parameters of a module and return it.
|
| 34 |
-
"""
|
| 35 |
-
for p in module.parameters():
|
| 36 |
-
p.detach().zero_()
|
| 37 |
-
return module
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
def scale_module(module, scale):
|
| 41 |
-
"""
|
| 42 |
-
Scale the parameters of a module and return it.
|
| 43 |
-
"""
|
| 44 |
-
for p in module.parameters():
|
| 45 |
-
p.detach().mul_(scale)
|
| 46 |
-
return module
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
def modulate(x, shift, scale):
|
| 50 |
-
return x * (1 + scale.unsqueeze(1)) + shift.unsqueeze(1)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
idol/pipeline.py
CHANGED
|
@@ -2,7 +2,7 @@
|
|
| 2 |
import json
|
| 3 |
import os
|
| 4 |
from dataclasses import dataclass
|
| 5 |
-
from typing import
|
| 6 |
|
| 7 |
import numpy as np
|
| 8 |
import torch
|
|
@@ -15,7 +15,7 @@ from .conditioning import ImageEncoder
|
|
| 15 |
from .gaussian import Gaussian
|
| 16 |
from .models import BiRefNet, GaussianFixedlenDecoder, LatentSeqMMFlowModel, OctreeProbabilityFixedlenDecoder
|
| 17 |
from .preprocess import CROP_MARGIN, preprocess_image
|
| 18 |
-
from .sampler import
|
| 19 |
|
| 20 |
DEFAULT_REPO = 'speridlabs/idol'
|
| 21 |
WEIGHT_FILES = ['config.json', 'denoiser.safetensors', 'decoder.safetensors', 'decoder_gs.safetensors',
|
|
@@ -29,34 +29,25 @@ class IdolOutput:
|
|
| 29 |
camera: dict # predicted input camera: yaw, pitch (radians), radius, fov (degrees)
|
| 30 |
|
| 31 |
|
| 32 |
-
def _load(model, path):
|
| 33 |
-
model.load_state_dict(load_file(path), strict=True)
|
| 34 |
-
return model
|
| 35 |
-
|
| 36 |
-
|
| 37 |
class IdolPipeline:
|
| 38 |
def __init__(self, weights_dir: str, device: Optional[str] = None, dinov3_dtype: str = 'bf16'):
|
| 39 |
self.device = torch.device(device or ('cuda' if torch.cuda.is_available() else 'cpu'))
|
| 40 |
with open(os.path.join(weights_dir, 'config.json')) as f:
|
| 41 |
self.config = json.load(f)
|
| 42 |
-
config = self.config
|
| 43 |
|
| 44 |
-
def
|
| 45 |
-
|
|
|
|
| 46 |
|
| 47 |
-
self.denoiser =
|
| 48 |
-
self.decoder =
|
| 49 |
-
self.decoder_gs =
|
| 50 |
-
self.background_remover = BiRefNet()
|
| 51 |
-
self.
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
model.eval().requires_grad_(False).to(self.device)
|
| 57 |
-
remover_dtype = torch.float16 if self.device.type == 'cuda' else torch.float32
|
| 58 |
-
self.background_remover = self.background_remover.eval().to(self.device, remover_dtype)
|
| 59 |
-
self.sampler = FlowEulerCfgSampler(sigma_min=config['sigma_min'])
|
| 60 |
|
| 61 |
@classmethod
|
| 62 |
def from_pretrained(cls, repo_or_dir: str = DEFAULT_REPO, **kwargs) -> 'IdolPipeline':
|
|
@@ -66,21 +57,14 @@ class IdolPipeline:
|
|
| 66 |
from huggingface_hub import snapshot_download
|
| 67 |
return cls(snapshot_download(repo_or_dir, allow_patterns=WEIGHT_FILES), **kwargs)
|
| 68 |
|
| 69 |
-
def to(self, device) -> 'IdolPipeline':
|
| 70 |
-
self.device = torch.device(device)
|
| 71 |
-
for model in (self.denoiser, self.decoder, self.decoder_gs, self.background_remover):
|
| 72 |
-
model.to(self.device)
|
| 73 |
-
self.image_encoder.to(self.device)
|
| 74 |
-
return self
|
| 75 |
-
|
| 76 |
def preprocess(self, image: Union[str, Image.Image], mode: str = 'synthetic',
|
| 77 |
-
crop_margin: Optional[float] = None
|
| 78 |
"""mode 'synthetic' (renders, generated images of one object: crop 1.2) or 'real' (photos: crop 1.7);
|
| 79 |
crop_margin overrides the mode's value."""
|
| 80 |
if isinstance(image, str):
|
| 81 |
image = Image.open(image)
|
| 82 |
margin = crop_margin if crop_margin is not None else CROP_MARGIN[mode]
|
| 83 |
-
return preprocess_image(image, background_remover=self.background_remover,
|
| 84 |
image_size=self.config['image_size'], crop_margin=margin)
|
| 85 |
|
| 86 |
@torch.no_grad()
|
|
@@ -94,34 +78,23 @@ class IdolPipeline:
|
|
| 94 |
rescale_t: float = 3.0,
|
| 95 |
num_gaussians: int = 262144,
|
| 96 |
crop_margin: Optional[float] = None,
|
| 97 |
-
erode_radius: int = 1,
|
| 98 |
-
preprocess: bool = True,
|
| 99 |
-
callback: Optional[Callable[[int, int], None]] = None,
|
| 100 |
verbose: bool = False,
|
| 101 |
) -> IdolOutput:
|
| 102 |
"""Generate the 3D Gaussians of the object in ``image``.
|
| 103 |
|
| 104 |
-
mode / crop_margin
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
callback(step_done, steps) is called after every sampling step."""
|
| 108 |
-
if preprocess:
|
| 109 |
-
prepared = self.preprocess(image, mode=mode, crop_margin=crop_margin, erode_radius=erode_radius)
|
| 110 |
-
else:
|
| 111 |
-
prepared = (Image.open(image) if isinstance(image, str) else image).convert('RGB')
|
| 112 |
pixels = torch.from_numpy(np.array(prepared)).permute(2, 0, 1).float().div(255.0)[None].to(self.device)
|
| 113 |
|
| 114 |
torch.manual_seed(seed)
|
| 115 |
-
np.random.seed(seed)
|
| 116 |
cond = self.image_encoder(pixels)
|
| 117 |
-
neg_cond = cond.apply(torch.zeros_like)
|
| 118 |
noise = TensorDict({
|
| 119 |
'latent': torch.randn(1, self.denoiser.q_token_length, self.denoiser.in_channels, device=self.device),
|
| 120 |
'camera': torch.randn(1, 1, self.denoiser.cam_channels, device=self.device),
|
| 121 |
}, batch_size=1, device=self.device)
|
| 122 |
-
sample =
|
| 123 |
-
|
| 124 |
-
callback=callback)
|
| 125 |
gaussian = self.decode(sample['latent'], num_gaussians)
|
| 126 |
return IdolOutput(gaussian=gaussian, preprocessed_image=prepared, camera=decode_camera(sample['camera'][0, 0]))
|
| 127 |
|
|
@@ -130,8 +103,7 @@ class IdolPipeline:
|
|
| 130 |
"""Latent [1, 8192, 16] -> Gaussians: the octree decoder places num_gaussians / 32 anchor points, the
|
| 131 |
Gaussian decoder turns each anchor into 32 Gaussians."""
|
| 132 |
per_anchor = self.decoder_gs.rep_config['num_gaussians']
|
| 133 |
-
anchors = OctreeProbabilityFixedlenDecoder.sample(
|
| 134 |
-
|
| 135 |
-
|
| 136 |
-
|
| 137 |
-
return self.decoder_gs.to_representation(anchors, features)[0]
|
|
|
|
| 2 |
import json
|
| 3 |
import os
|
| 4 |
from dataclasses import dataclass
|
| 5 |
+
from typing import Optional, Union
|
| 6 |
|
| 7 |
import numpy as np
|
| 8 |
import torch
|
|
|
|
| 15 |
from .gaussian import Gaussian
|
| 16 |
from .models import BiRefNet, GaussianFixedlenDecoder, LatentSeqMMFlowModel, OctreeProbabilityFixedlenDecoder
|
| 17 |
from .preprocess import CROP_MARGIN, preprocess_image
|
| 18 |
+
from .sampler import sample_flow
|
| 19 |
|
| 20 |
DEFAULT_REPO = 'speridlabs/idol'
|
| 21 |
WEIGHT_FILES = ['config.json', 'denoiser.safetensors', 'decoder.safetensors', 'decoder_gs.safetensors',
|
|
|
|
| 29 |
camera: dict # predicted input camera: yaw, pitch (radians), radius, fov (degrees)
|
| 30 |
|
| 31 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 32 |
class IdolPipeline:
|
| 33 |
def __init__(self, weights_dir: str, device: Optional[str] = None, dinov3_dtype: str = 'bf16'):
|
| 34 |
self.device = torch.device(device or ('cuda' if torch.cuda.is_available() else 'cpu'))
|
| 35 |
with open(os.path.join(weights_dir, 'config.json')) as f:
|
| 36 |
self.config = json.load(f)
|
|
|
|
| 37 |
|
| 38 |
+
def load(model, name):
|
| 39 |
+
model.load_state_dict(load_file(os.path.join(weights_dir, f'{name}.safetensors')), strict=True)
|
| 40 |
+
return model.eval().requires_grad_(False).to(self.device)
|
| 41 |
|
| 42 |
+
self.denoiser = load(LatentSeqMMFlowModel(**self.config['denoiser']['args']), 'denoiser')
|
| 43 |
+
self.decoder = load(OctreeProbabilityFixedlenDecoder(**self.config['decoder']['args']), 'decoder')
|
| 44 |
+
self.decoder_gs = load(GaussianFixedlenDecoder(**self.config['decoder_gs']['args']), 'decoder_gs')
|
| 45 |
+
self.background_remover = load(BiRefNet(), 'birefnet')
|
| 46 |
+
if self.device.type == 'cuda':
|
| 47 |
+
self.background_remover.half()
|
| 48 |
+
encoders = self.config['image_encoders']
|
| 49 |
+
self.image_encoder = ImageEncoder(encoders['dinov3'], encoders['flux2_vae'], dinov3_dtype=dinov3_dtype,
|
| 50 |
+
device=self.device)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 51 |
|
| 52 |
@classmethod
|
| 53 |
def from_pretrained(cls, repo_or_dir: str = DEFAULT_REPO, **kwargs) -> 'IdolPipeline':
|
|
|
|
| 57 |
from huggingface_hub import snapshot_download
|
| 58 |
return cls(snapshot_download(repo_or_dir, allow_patterns=WEIGHT_FILES), **kwargs)
|
| 59 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 60 |
def preprocess(self, image: Union[str, Image.Image], mode: str = 'synthetic',
|
| 61 |
+
crop_margin: Optional[float] = None) -> Image.Image:
|
| 62 |
"""mode 'synthetic' (renders, generated images of one object: crop 1.2) or 'real' (photos: crop 1.7);
|
| 63 |
crop_margin overrides the mode's value."""
|
| 64 |
if isinstance(image, str):
|
| 65 |
image = Image.open(image)
|
| 66 |
margin = crop_margin if crop_margin is not None else CROP_MARGIN[mode]
|
| 67 |
+
return preprocess_image(image, background_remover=self.background_remover,
|
| 68 |
image_size=self.config['image_size'], crop_margin=margin)
|
| 69 |
|
| 70 |
@torch.no_grad()
|
|
|
|
| 78 |
rescale_t: float = 3.0,
|
| 79 |
num_gaussians: int = 262144,
|
| 80 |
crop_margin: Optional[float] = None,
|
|
|
|
|
|
|
|
|
|
| 81 |
verbose: bool = False,
|
| 82 |
) -> IdolOutput:
|
| 83 |
"""Generate the 3D Gaussians of the object in ``image``.
|
| 84 |
|
| 85 |
+
mode / crop_margin: see ``preprocess``.
|
| 86 |
+
num_gaussians: 32,768 to 1,048,576, rounded down to a multiple of 32."""
|
| 87 |
+
prepared = self.preprocess(image, mode=mode, crop_margin=crop_margin)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 88 |
pixels = torch.from_numpy(np.array(prepared)).permute(2, 0, 1).float().div(255.0)[None].to(self.device)
|
| 89 |
|
| 90 |
torch.manual_seed(seed)
|
|
|
|
| 91 |
cond = self.image_encoder(pixels)
|
|
|
|
| 92 |
noise = TensorDict({
|
| 93 |
'latent': torch.randn(1, self.denoiser.q_token_length, self.denoiser.in_channels, device=self.device),
|
| 94 |
'camera': torch.randn(1, 1, self.denoiser.cam_channels, device=self.device),
|
| 95 |
}, batch_size=1, device=self.device)
|
| 96 |
+
sample = sample_flow(self.denoiser, noise, cond=cond, neg_cond=cond.apply(torch.zeros_like), steps=steps,
|
| 97 |
+
rescale_t=rescale_t, cfg_strength=cfg_strength, verbose=verbose)
|
|
|
|
| 98 |
gaussian = self.decode(sample['latent'], num_gaussians)
|
| 99 |
return IdolOutput(gaussian=gaussian, preprocessed_image=prepared, camera=decode_camera(sample['camera'][0, 0]))
|
| 100 |
|
|
|
|
| 103 |
"""Latent [1, 8192, 16] -> Gaussians: the octree decoder places num_gaussians / 32 anchor points, the
|
| 104 |
Gaussian decoder turns each anchor into 32 Gaussians."""
|
| 105 |
per_anchor = self.decoder_gs.rep_config['num_gaussians']
|
| 106 |
+
anchors = OctreeProbabilityFixedlenDecoder.sample(self.decoder, latent,
|
| 107 |
+
num_points=max(1, int(num_gaussians) // per_anchor),
|
| 108 |
+
level=self.config['max_voxel_level'])
|
| 109 |
+
return self.decoder_gs.to_representation(anchors, self.decoder_gs(anchors, latent))[0]
|
|
|
idol/preprocess.py
CHANGED
|
@@ -11,22 +11,19 @@ def has_real_alpha(image: Image.Image) -> bool:
|
|
| 11 |
return image.mode == 'RGBA' and np.array(image.getchannel(3), dtype=np.int32).min() < 255
|
| 12 |
|
| 13 |
|
| 14 |
-
def preprocess_image(image: Image.Image, background_remover
|
| 15 |
crop_margin: float = 1.2) -> Image.Image:
|
| 16 |
"""Return the RGB image the encoders see.
|
| 17 |
|
| 18 |
Resize so the short side is ``image_size``; if the image has no real alpha, ``background_remover`` (BiRefNet)
|
| 19 |
-
predicts one; erode the alpha by
|
| 20 |
side ``crop_margin`` x the object's bounding box; resize to ``image_size``; composite on black."""
|
| 21 |
width, height = image.size
|
| 22 |
scale = image_size / min(width, height)
|
| 23 |
image = image.resize((max(1, int(round(width * scale))), max(1, int(round(height * scale)))), Image.LANCZOS)
|
| 24 |
if not has_real_alpha(image):
|
| 25 |
-
if background_remover is None:
|
| 26 |
-
raise ValueError('the image has no alpha channel: pass a background remover or an RGBA cutout')
|
| 27 |
image = background_remover.remove_background(image.convert('RGB'))
|
| 28 |
-
|
| 29 |
-
image.putalpha(image.getchannel(3).filter(ImageFilter.MinFilter(2 * erode_radius + 1)))
|
| 30 |
alpha = np.array(image.getchannel(3))
|
| 31 |
ys, xs = np.nonzero(alpha)
|
| 32 |
if len(xs) == 0:
|
|
|
|
| 11 |
return image.mode == 'RGBA' and np.array(image.getchannel(3), dtype=np.int32).min() < 255
|
| 12 |
|
| 13 |
|
| 14 |
+
def preprocess_image(image: Image.Image, background_remover, image_size: int = 1024,
|
| 15 |
crop_margin: float = 1.2) -> Image.Image:
|
| 16 |
"""Return the RGB image the encoders see.
|
| 17 |
|
| 18 |
Resize so the short side is ``image_size``; if the image has no real alpha, ``background_remover`` (BiRefNet)
|
| 19 |
+
predicts one; erode the alpha by 1 px so no background-coloured fringe survives; crop a square of
|
| 20 |
side ``crop_margin`` x the object's bounding box; resize to ``image_size``; composite on black."""
|
| 21 |
width, height = image.size
|
| 22 |
scale = image_size / min(width, height)
|
| 23 |
image = image.resize((max(1, int(round(width * scale))), max(1, int(round(height * scale)))), Image.LANCZOS)
|
| 24 |
if not has_real_alpha(image):
|
|
|
|
|
|
|
| 25 |
image = background_remover.remove_background(image.convert('RGB'))
|
| 26 |
+
image.putalpha(image.getchannel(3).filter(ImageFilter.MinFilter(3)))
|
|
|
|
| 27 |
alpha = np.array(image.getchannel(3))
|
| 28 |
ys, xs = np.nonzero(alpha)
|
| 29 |
if len(xs) == 0:
|
idol/sampler.py
CHANGED
|
@@ -1,38 +1,28 @@
|
|
| 1 |
"""Euler sampler for the rectified-flow denoiser, with classifier-free guidance."""
|
| 2 |
-
from typing import Callable, Optional
|
| 3 |
-
|
| 4 |
import numpy as np
|
| 5 |
import torch
|
| 6 |
from tqdm import tqdm
|
| 7 |
|
| 8 |
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
|
|
|
| 12 |
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
return model(x_t, t, cond)
|
| 16 |
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
callback: Optional[Callable[[int, int], None]] = None):
|
| 21 |
-
"""Integrate from noise (t = 1) to data (t = 0) in ``steps`` Euler steps.
|
| 22 |
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
negative_velocity = self._velocity(model, sample, t, neg_cond)
|
| 34 |
-
velocity = (1 + cfg_strength) * velocity - cfg_strength * negative_velocity
|
| 35 |
-
sample = sample - velocity * (t - t_prev)
|
| 36 |
-
if callback is not None:
|
| 37 |
-
callback(i + 1, steps)
|
| 38 |
-
return sample
|
|
|
|
| 1 |
"""Euler sampler for the rectified-flow denoiser, with classifier-free guidance."""
|
|
|
|
|
|
|
| 2 |
import numpy as np
|
| 3 |
import torch
|
| 4 |
from tqdm import tqdm
|
| 5 |
|
| 6 |
|
| 7 |
+
@torch.no_grad()
|
| 8 |
+
def sample_flow(model, noise, cond, neg_cond, steps: int = 50, rescale_t: float = 3.0, cfg_strength: float = 3.0,
|
| 9 |
+
verbose: bool = True):
|
| 10 |
+
"""Integrate from noise (t = 1) to data (t = 0) in ``steps`` Euler steps.
|
| 11 |
|
| 12 |
+
rescale_t shifts the time grid towards t = 1 (more steps at high noise).
|
| 13 |
+
cfg_strength: guided velocity = (1 + w) * v(cond) - w * v(neg_cond)."""
|
|
|
|
| 14 |
|
| 15 |
+
def velocity(sample, t, condition):
|
| 16 |
+
return model(sample, torch.tensor([1000 * t] * sample.shape[0], device=sample.device, dtype=torch.float32),
|
| 17 |
+
condition)
|
|
|
|
|
|
|
| 18 |
|
| 19 |
+
sample = noise
|
| 20 |
+
t_seq = np.linspace(1, 0, steps + 1)
|
| 21 |
+
t_seq = rescale_t * t_seq / (1 + (rescale_t - 1) * t_seq)
|
| 22 |
+
for i in tqdm(range(steps), desc='Sampling', disable=not verbose):
|
| 23 |
+
t, t_prev = t_seq[i], t_seq[i + 1]
|
| 24 |
+
v = velocity(sample, t, cond)
|
| 25 |
+
if cfg_strength > 0:
|
| 26 |
+
v = (1 + cfg_strength) * v - cfg_strength * velocity(sample, t, neg_cond)
|
| 27 |
+
sample = sample - v * (t - t_prev)
|
| 28 |
+
return sample
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
idol/sampling_utils.py
DELETED
|
@@ -1,150 +0,0 @@
|
|
| 1 |
-
import numpy as np
|
| 2 |
-
import torch
|
| 3 |
-
from typing import *
|
| 4 |
-
|
| 5 |
-
PRIMES = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53]
|
| 6 |
-
|
| 7 |
-
def radical_inverse(base, n):
|
| 8 |
-
val = 0
|
| 9 |
-
inv_base = 1.0 / base
|
| 10 |
-
inv_base_n = inv_base
|
| 11 |
-
while n > 0:
|
| 12 |
-
digit = n % base
|
| 13 |
-
val += digit * inv_base_n
|
| 14 |
-
n //= base
|
| 15 |
-
inv_base_n *= inv_base
|
| 16 |
-
return val
|
| 17 |
-
|
| 18 |
-
def halton_sequence(dim, n):
|
| 19 |
-
return [radical_inverse(PRIMES[dim], n) for dim in range(dim)]
|
| 20 |
-
|
| 21 |
-
def hammersley_sequence(dim, n, num_samples):
|
| 22 |
-
return [n / num_samples] + halton_sequence(dim - 1, n)
|
| 23 |
-
|
| 24 |
-
@torch.no_grad()
|
| 25 |
-
def sample_probs(
|
| 26 |
-
probs: torch.Tensor,
|
| 27 |
-
counts: torch.Tensor,
|
| 28 |
-
algo: Literal["iid", "residual", "systematic"] = "systematic",
|
| 29 |
-
group_rows: bool = False,
|
| 30 |
-
) -> torch.Tensor:
|
| 31 |
-
"""
|
| 32 |
-
probs : [..., P] nonnegative; rows need not be normalized
|
| 33 |
-
counts : [...] nonnegative ints
|
| 34 |
-
returns: [..., P] long tensor of counts per bin
|
| 35 |
-
handle properly for conunts = 0 case
|
| 36 |
-
"""
|
| 37 |
-
batch_shape = counts.shape
|
| 38 |
-
B = counts.numel()
|
| 39 |
-
P = probs.size(-1)
|
| 40 |
-
assert B * P == probs.numel() and probs.dim() - counts.dim() == 1
|
| 41 |
-
device = probs.device
|
| 42 |
-
probs = probs.view(B, P)
|
| 43 |
-
counts = counts.view(B)
|
| 44 |
-
|
| 45 |
-
# Normalize with uniform fallback for zero-sum rows
|
| 46 |
-
probs = probs.to(torch.float32).clamp_min_(0)
|
| 47 |
-
row_sums = probs.sum(1, keepdim=True)
|
| 48 |
-
zero_mask = row_sums.eq(0)
|
| 49 |
-
probs = probs / row_sums.clamp_min_(1)
|
| 50 |
-
if zero_mask.any():
|
| 51 |
-
probs = probs.clone()
|
| 52 |
-
probs[zero_mask.expand_as(probs)] = 1.0 / P
|
| 53 |
-
|
| 54 |
-
counts = counts.to(device=device, dtype=torch.long)
|
| 55 |
-
out = torch.zeros(B, P, dtype=torch.long, device=device)
|
| 56 |
-
|
| 57 |
-
if algo == "iid":
|
| 58 |
-
# Pure multinomial IID, batched by unique k
|
| 59 |
-
unique_k, inv = counts.unique(sorted=False, return_inverse=True)
|
| 60 |
-
for i, k in enumerate(unique_k.tolist()):
|
| 61 |
-
if k == 0:
|
| 62 |
-
continue
|
| 63 |
-
rows = (inv == i).nonzero(as_tuple=False).squeeze(1) # indices for this k
|
| 64 |
-
samples = torch.multinomial(
|
| 65 |
-
probs.index_select(0, rows), num_samples=int(k), replacement=True
|
| 66 |
-
)
|
| 67 |
-
buf = torch.zeros(rows.numel(), P, dtype=torch.float32, device=device)
|
| 68 |
-
buf.scatter_add_(1, samples, torch.ones_like(samples, dtype=buf.dtype))
|
| 69 |
-
out.index_copy_(0, rows, buf.to(torch.long))
|
| 70 |
-
|
| 71 |
-
elif algo in ("residual", "owen_scrambled_residual"):
|
| 72 |
-
# Step 1: deterministic floor allocation (vectorized)
|
| 73 |
-
expected = probs * counts.unsqueeze(1).to(torch.float32) # [B, P]
|
| 74 |
-
base = torch.floor(expected) # [B, P]
|
| 75 |
-
out += base.to(torch.long)
|
| 76 |
-
|
| 77 |
-
# Step 2: compute residuals
|
| 78 |
-
residual_mass = expected - base # [B, P]
|
| 79 |
-
m = counts - base.sum(1).to(torch.long) # [B]
|
| 80 |
-
|
| 81 |
-
# Guard: skip rows with no residuals
|
| 82 |
-
active_mask = m > 0
|
| 83 |
-
if active_mask.any():
|
| 84 |
-
rows = active_mask.nonzero(as_tuple=False).squeeze(1)
|
| 85 |
-
m_active = m[rows]
|
| 86 |
-
res_probs = residual_mass[rows]
|
| 87 |
-
res_probs /= res_probs.sum(1, keepdim=True) # normalize residuals
|
| 88 |
-
|
| 89 |
-
if algo == "residual":
|
| 90 |
-
# --- Batched multinomial ---
|
| 91 |
-
unique_m, invm = m_active.unique(sorted=False, return_inverse=True)
|
| 92 |
-
for j, mm in enumerate(unique_m.tolist()):
|
| 93 |
-
rows_m = (invm == j).nonzero(as_tuple=False).squeeze(1) # indices for this m
|
| 94 |
-
rsel = rows[rows_m]
|
| 95 |
-
if mm == 0:
|
| 96 |
-
continue
|
| 97 |
-
samples = torch.multinomial(
|
| 98 |
-
res_probs.index_select(0, rows_m),
|
| 99 |
-
num_samples=int(mm),
|
| 100 |
-
replacement=True,
|
| 101 |
-
)
|
| 102 |
-
buf = torch.zeros(rows_m.numel(), P, dtype=torch.float32, device=device)
|
| 103 |
-
buf.scatter_add_(1, samples, torch.ones_like(samples, dtype=buf.dtype))
|
| 104 |
-
out.index_copy_(0, rsel, out[rsel] + buf.to(torch.long))
|
| 105 |
-
else:
|
| 106 |
-
raise NotImplementedError("Owen-scrambled residual not implemented yet")
|
| 107 |
-
assert out.sum() == counts.sum(), f"sampling count mismatch, input probs max: {probs.max().item()}, counts: {counts.sum().item()}, output sum: {out.sum().item()}"
|
| 108 |
-
|
| 109 |
-
elif algo == "systematic":
|
| 110 |
-
cdf = probs.cumsum(dim=1).clamp(max=1.0 - 1e-12)
|
| 111 |
-
if group_rows:
|
| 112 |
-
# Stable grouping retains both ascending count order (torch.unique's
|
| 113 |
-
# existing order) and original row order within each group. Therefore
|
| 114 |
-
# torch.rand sees exactly the same sequence of call shapes and rows.
|
| 115 |
-
# One metadata transfer replaces one synchronizing nonzero per group.
|
| 116 |
-
sorted_counts, row_order = torch.sort(counts, stable=True)
|
| 117 |
-
unique_n, group_sizes = torch.unique_consecutive(sorted_counts, return_counts=True)
|
| 118 |
-
group_metadata = torch.stack((unique_n, group_sizes), dim=1).tolist()
|
| 119 |
-
row_ranges = []
|
| 120 |
-
row_offset = 0
|
| 121 |
-
for count, group_size in group_metadata:
|
| 122 |
-
row_ranges.append((count, row_order[row_offset:row_offset + group_size]))
|
| 123 |
-
row_offset += group_size
|
| 124 |
-
else:
|
| 125 |
-
unique_n, inv = counts.unique(sorted=False, return_inverse=True)
|
| 126 |
-
row_ranges = ((count, (inv == index).nonzero(as_tuple=False).squeeze(1))
|
| 127 |
-
for index, count in enumerate(unique_n.tolist()) if count != 0)
|
| 128 |
-
for n, rows in row_ranges:
|
| 129 |
-
if n == 0:
|
| 130 |
-
continue
|
| 131 |
-
r = rows.numel()
|
| 132 |
-
|
| 133 |
-
# random offset per row in [0,1/n)
|
| 134 |
-
U0 = torch.rand(r, 1, device=device) / float(n) # [r,1]
|
| 135 |
-
# stratified points
|
| 136 |
-
grid = torch.arange(n, device=device, dtype=torch.float32)[None, :] / float(n) # [1,n]
|
| 137 |
-
us = (U0 + grid).clamp(max=1.0 - 1e-12) # [r,n]
|
| 138 |
-
|
| 139 |
-
cdf_rows = cdf.index_select(0, rows) # [r,P]
|
| 140 |
-
idx = torch.searchsorted(cdf_rows, us).clamp_max(probs.size(1) - 1) # [r,n]
|
| 141 |
-
|
| 142 |
-
buf = torch.zeros(r, P, dtype=torch.float32, device=device)
|
| 143 |
-
buf.scatter_add_(1, idx, torch.ones_like(idx, dtype=buf.dtype))
|
| 144 |
-
out.index_copy_(0, rows, buf.to(torch.long))
|
| 145 |
-
|
| 146 |
-
else:
|
| 147 |
-
raise ValueError(f"Unknown algo {algo}")
|
| 148 |
-
|
| 149 |
-
out = out.view(*batch_shape, P)
|
| 150 |
-
return out
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|