Spaces:
Sleeping
Sleeping
File size: 16,804 Bytes
6e4b62e | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 | """
ๆจ็ๅทฅๅ
ท๏ผไพ run_replay_loop_two_chunk ๅ่ฏไผฐ่ๆฌไฝฟ็จใ
ๆไพ๏ผload_pipeline_and_ckptใload_prompt_for_videoใsample_trajectory_samples_from_datasetใ
VWM-style ็ฎๅ็๏ผไฝฟ็จ DiTBlock_w_Action + MLP_CamPose๏ผblock ๅ
action_mlp๏ผ๏ผ
ๅป้ค CameraEncoder / camera_encoder_shallow ็ญๅไฝ่ทฏๅพใ
"""
import os
import re
import sys
import csv
import random
_script_dir = os.path.dirname(os.path.abspath(__file__))
_repo_root = os.path.dirname(os.path.dirname(_script_dir))
if _repo_root not in sys.path:
sys.path.insert(0, _repo_root)
import torch
import torch.nn as nn
from safetensors.torch import load_file as safe_load_file
from diffsynth.pipelines.wan_video_new import WanVideoPipeline, ModelConfig
from diffsynth.models.wan_video_dit import SelfAttention, CrossAttention, GateModule, modulate
from diffsynth.models.memory.block_wise_ssm import BlockWiseStateSpaceMemory
from diffsynth.models.memory.videossm_hybrid import HybridStateSpaceMemory
DEFAULT_NEGATIVE_PROMPT = "oversaturated colors, overexposed, static, blurry details"
# โโ MLP_CamPose + DiTBlock_w_Action๏ผไธ่ฎญ็ปไพง train.py ๅฎๅ
จไธ่ด๏ผโโโโโโโโโโ
class MLP_CamPose(nn.Module):
def __init__(self, out_dim, pose_dim=12):
super().__init__()
self.proj = nn.Linear(pose_dim, out_dim)
nn.init.zeros_(self.proj.weight)
nn.init.zeros_(self.proj.bias)
def forward(self, x):
return self.proj(x)
class DiTBlock_w_Action(nn.Module):
def __init__(self, has_image_input, dim, num_heads, ffn_dim, eps=1e-6,
add_action_attn=False, action_use_temporal_attention=True,
use_cam_pose=False, use_block_wise_ssm=False, use_videossm_hybrid=False,
videossm_kernel_size=3, videossm_expand=2):
super().__init__()
self.dim = dim
self.num_heads = num_heads
self.ffn_dim = ffn_dim
if add_action_attn:
self.self_attn_with_action = SelfAttention(dim, num_heads, eps)
nn.init.zeros_(self.self_attn_with_action.o.weight)
nn.init.zeros_(self.self_attn_with_action.o.bias)
if use_cam_pose:
self.action_mlp = MLP_CamPose(dim)
else:
self.action_mlp = MLP_CamPose(dim)
self.self_attn = SelfAttention(dim, num_heads, eps)
self.cross_attn = CrossAttention(dim, num_heads, eps, has_image_input=has_image_input)
self.norm1 = nn.LayerNorm(dim, eps=eps, elementwise_affine=False)
self.norm2 = nn.LayerNorm(dim, eps=eps, elementwise_affine=False)
self.norm3 = nn.LayerNorm(dim, eps=eps)
self.ffn = nn.Sequential(nn.Linear(dim, ffn_dim), nn.GELU(approximate='tanh'), nn.Linear(ffn_dim, dim))
self.modulation = nn.Parameter(torch.randn(1, 6, dim) / dim**0.5)
self.gate = GateModule()
self.action_use_temporal_attention = action_use_temporal_attention
self.use_block_wise_ssm = bool(use_block_wise_ssm)
self.use_videossm_hybrid = bool(use_videossm_hybrid)
if use_block_wise_ssm:
self.block_wise_ssm = BlockWiseStateSpaceMemory(dim)
if use_videossm_hybrid:
self.videossm_hybrid = HybridStateSpaceMemory(
dim, kernel_size=videossm_kernel_size, expand=videossm_expand
)
def forward(self, x, context, t_mod, freqs, actions=None):
has_seq = len(t_mod.shape) == 4
chunk_dim = 2 if has_seq else 1
shift_msa, scale_msa, gate_msa, shift_mlp, scale_mlp, gate_mlp = (
self.modulation.to(dtype=t_mod.dtype, device=t_mod.device) + t_mod).chunk(6, dim=chunk_dim)
if has_seq:
shift_msa, scale_msa, gate_msa, shift_mlp, scale_mlp, gate_mlp = (
shift_msa.squeeze(2), scale_msa.squeeze(2), gate_msa.squeeze(2),
shift_mlp.squeeze(2), scale_mlp.squeeze(2), gate_mlp.squeeze(2),
)
num_frames = None
if actions is not None:
original_x = x
actions = self.action_mlp(actions.to(x.dtype)).to(x.dtype)
bs, num_frames, dim = actions.shape
actions = actions.reshape(bs, num_frames, 1, dim)
x = x.reshape(bs, num_frames, -1, dim)
x = x + actions
if hasattr(self, "self_attn_with_action"):
if not self.action_use_temporal_attention:
x = x.reshape(bs, -1, dim)
x = original_x + self.self_attn_with_action(x, freqs)
else:
from einops import rearrange
x = rearrange(x, "b f p d -> (b p) f d")
attn_out = self.self_attn_with_action(x)
attn_out = rearrange(attn_out, "(b p) f d -> b f p d", b=bs)
x = original_x + attn_out.reshape(bs, -1, dim)
else:
x = x.reshape(bs, -1, dim)
input_x = modulate(self.norm1(x), shift_msa, scale_msa)
x = self.gate(x, gate_msa, self.self_attn(input_x, freqs))
if num_frames is not None:
if hasattr(self, "block_wise_ssm"):
x = self.block_wise_ssm(x, f=num_frames)
if hasattr(self, "videossm_hybrid"):
spatial = x.shape[1] // int(num_frames) if int(num_frames) > 0 else 0
x = self.videossm_hybrid(x, f=num_frames, h=1, w=spatial)
x = x + self.cross_attn(self.norm3(x), context)
input_x = modulate(self.norm2(x), shift_mlp, scale_mlp)
x = self.gate(x, gate_mlp, self.ffn(input_x))
return x
# โโ Utility functions โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
def load_pose_rt(json_file, frame_idx):
"""ไปๆฐๆฎ้ camera json ่ฏปๅๅๅธง 12 ็ปด RTใ"""
from src.model_training.fov_retrieval import load_camera_pose, pose_to_rt
pose = load_camera_pose(json_file, int(frame_idx))
if pose is None:
return None
return pose_to_rt(pose, constrain_to_xy=True)
def get_relative_rt(rt, ref_rt):
"""ๅๅธง็ธๅฏนไฝๅงฟใ"""
from src.model_training.fov_retrieval import convert_rt_to_relative
if rt is None or ref_rt is None or len(rt) < 12 or len(ref_rt) < 12:
return None
out = convert_rt_to_relative([rt], ref_rt)
return out[0] if out else None
def load_prompt_for_video(dataset_base, video_name):
"""ไป dataset ็ฎๅฝไธ็ metadata CSV ่ฏปๅ่ฏฅ่ง้ข็ promptใ"""
if not dataset_base or not video_name:
return None
vn = str(video_name).replace(".mp4", "").replace(".avi", "").strip()
for name in ("metadata_full.csv", "metadata.csv", "prompts.csv"):
path = os.path.join(dataset_base, name)
if not os.path.isfile(path):
continue
try:
with open(path, "r", encoding="utf-8") as f:
for row in csv.DictReader(f):
if row.get("video_name", "").strip() == vn:
p = row.get("prompt", "").strip()
if p:
return p
except Exception:
pass
return None
def sample_trajectory_samples_from_dataset(dataset_base, num_samples=4, num_frames=81, seed=42):
"""ไป dataset ๆไธพ (video_name, start_frame)ใ"""
frames_dir = os.path.join(dataset_base, "frames")
if not os.path.isdir(frames_dir):
return []
candidates = []
for vn in sorted(os.listdir(frames_dir)):
vd = os.path.join(frames_dir, vn)
if not os.path.isdir(vd):
continue
try:
names = [f for f in os.listdir(vd) if f.endswith(".png")]
indices = sorted({int(os.path.splitext(n)[0]) for n in names if n[:-4].isdigit()})
if not indices:
continue
max_idx = max(indices)
for start in indices:
if start + num_frames - 1 <= max_idx:
candidates.append((vn, start))
except Exception:
continue
if not candidates:
return []
rng = random.Random(seed)
if len(candidates) <= num_samples:
return candidates
return [candidates[i] for i in rng.sample(range(len(candidates)), num_samples)]
# โโ Pipeline loading (VWM-style) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
def _build_action_blocks(
pipe,
add_action_attn=False,
action_use_temporal_attention=True,
block_wise_block_ids=None,
videossm_block_ids=None,
):
"""Replace DiT blocks with DiTBlock_w_Action (VWM cam_infer.py style)."""
dit = pipe.dit
old_blocks = dit.blocks
has_image_input = getattr(dit, "has_image_input", False)
dim = dit.dim
num_heads = getattr(dit, "num_heads", None) or getattr(old_blocks[0], "num_heads", None)
ffn_dim = getattr(dit, "ffn_dim", None) or getattr(old_blocks[0], "ffn_dim", None)
eps = getattr(dit, "eps", 1e-6)
block_dtype = next(old_blocks[0].parameters()).dtype
block_device = next(old_blocks[0].parameters()).device
block_wise_block_ids = set(block_wise_block_ids or [])
videossm_block_ids = set(videossm_block_ids or [])
new_blocks = torch.nn.ModuleList()
for block_id, old_block in enumerate(old_blocks):
new_block = DiTBlock_w_Action(
has_image_input=has_image_input,
dim=dim, num_heads=num_heads, ffn_dim=ffn_dim, eps=eps,
add_action_attn=add_action_attn,
action_use_temporal_attention=action_use_temporal_attention,
use_cam_pose=True,
use_block_wise_ssm=block_id in block_wise_block_ids,
use_videossm_hybrid=block_id in videossm_block_ids,
)
new_block = new_block.to(dtype=block_dtype, device=block_device)
for attr in ("self_attn", "cross_attn", "norm1", "norm2", "norm3", "ffn"):
if hasattr(old_block, attr) and hasattr(new_block, attr):
getattr(new_block, attr).load_state_dict(getattr(old_block, attr).state_dict())
if hasattr(old_block, "modulation") and hasattr(new_block, "modulation"):
with torch.no_grad():
new_block.modulation.copy_(old_block.modulation.to(dtype=block_dtype))
new_blocks.append(new_block)
dit.blocks = new_blocks
print(f"[loop_utils] Replaced {len(new_blocks)} blocks with DiTBlock_w_Action (MLP_CamPose)")
if block_wise_block_ids:
print(f"[loop_utils] Loaded Block-wise SSM slots on blocks: {sorted(block_wise_block_ids)[:8]}{'...' if len(block_wise_block_ids) > 8 else ''}")
if videossm_block_ids:
print(f"[loop_utils] Loaded VideoSSM hybrid slots on blocks: {sorted(videossm_block_ids)[:8]}{'...' if len(videossm_block_ids) > 8 else ''}")
def load_pipeline_and_ckpt(
ckpt_path,
dit_path,
text_encoder_path,
vae_path,
device="cuda",
add_action_attn=False,
action_use_temporal_attention=True,
tokenizer_path=None,
# Legacy kwargs accepted but ignored (CameraEncoder removed)
**kwargs,
):
"""Load WanVideoPipeline, replace blocks with DiTBlock_w_Action, load ckpt (strict=False).
VWM-style: no CameraEncoder, no complex inference logic. Action is injected
via MLP_CamPose (nn.Linear(12, dim), zero-init) inside each DiTBlock_w_Action.
"""
print(f"[loop_utils] Loading pipeline (DiT -> {device})")
if not tokenizer_path:
import os as _os
_base = _os.path.dirname(dit_path)
_cand = _os.path.join(_base, "google", "umt5-xxl")
if _os.path.isdir(_cand):
tokenizer_path = _cand
print(f"[loop_utils] Auto-detected tokenizer at {tokenizer_path}")
model_configs = [
ModelConfig(path=dit_path, offload_device=device),
ModelConfig(path=text_encoder_path, offload_device="cpu"),
ModelConfig(path=vae_path, offload_device="cpu"),
]
pipe = WanVideoPipeline.from_pretrained(
torch_dtype=torch.bfloat16,
device=device,
model_configs=model_configs,
tokenizer_config=ModelConfig(path=tokenizer_path) if tokenizer_path else None,
)
ckpt = None
block_wise_block_ids = set()
videossm_block_ids = set()
action_attn_block_ids = set()
if ckpt_path and os.path.isfile(ckpt_path):
ckpt = safe_load_file(ckpt_path)
for key in ckpt.keys():
m = re.match(r"blocks\.(\d+)\.block_wise_ssm\.", key)
if m:
block_wise_block_ids.add(int(m.group(1)))
m = re.match(r"blocks\.(\d+)\.videossm_hybrid\.", key)
if m:
videossm_block_ids.add(int(m.group(1)))
m = re.match(r"blocks\.(\d+)\.self_attn_with_action\.", key)
if m:
action_attn_block_ids.add(int(m.group(1)))
if action_attn_block_ids and not add_action_attn:
add_action_attn = True
print("[loop_utils] Detected action-attention weights in checkpoint; enabling self_attn_with_action")
# Replace blocks with DiTBlock_w_Action, including memory slots implied by ckpt keys.
_build_action_blocks(
pipe,
add_action_attn=add_action_attn,
action_use_temporal_attention=action_use_temporal_attention,
block_wise_block_ids=block_wise_block_ids,
videossm_block_ids=videossm_block_ids,
)
# Load ckpt (strict=False: base model keys match, action_mlp keys are extra)
if ckpt_path and not os.path.isfile(ckpt_path):
print(f"[loop_utils] WARNING: ckpt not found: {ckpt_path} โ running with base model weights only!")
if ckpt_path and os.path.isfile(ckpt_path):
if ckpt is None:
ckpt = safe_load_file(ckpt_path)
missing, unexpected = pipe.dit.load_state_dict(ckpt, strict=False)
action_keys = [k for k in ckpt if "action_mlp" in k]
if not missing and not unexpected:
print(f"[loop_utils] Ckpt loaded: {len(ckpt)} keys, perfect match")
else:
print(f"[loop_utils] Ckpt loaded: {len(ckpt)} keys, "
f"missing={len(missing)}, unexpected={len(unexpected)}, "
f"action_mlp_keys={len(action_keys)}")
if missing:
for k in sorted(missing)[:5]:
print(f" missing: {k}")
if unexpected:
for k in sorted(unexpected)[:5]:
print(f" unexpected: {k}")
# Optional: load SpatialGridMemory if present in ckpt
_smsd = {
k.replace("spatial_memory_module.", "", 1): v
for k, v in ckpt.items()
if k.startswith("spatial_memory_module.")
}
if _smsd:
try:
from diffsynth.models.memory.spatial_grid_memory import SpatialGridMemory
except ImportError:
SpatialGridMemory = None
if SpatialGridMemory is not None:
dim = pipe.dit.dim
w = _smsd.get("spatial_to_tokens")
if w is not None:
g2, num_tok = int(w.shape[0]), int(w.shape[1])
gsz = int(round(g2 ** 0.5))
if gsz * gsz != g2:
gsz = 8
sm = SpatialGridMemory(dim, grid_size=gsz, num_tokens=num_tok)
sm.load_state_dict(_smsd, strict=False)
sm = sm.to(dtype=next(pipe.dit.parameters()).dtype, device=next(pipe.dit.parameters()).device)
pipe.spatial_memory_module = sm
pipe.use_spatial_memory_legacy = False
print(f"[loop_utils] Loaded spatial_memory_module (grid={gsz}, tokens={num_tok})")
_srmsd = {
k.replace("spatial_memory_readout_module.", "", 1): v
for k, v in ckpt.items()
if k.startswith("spatial_memory_readout_module.")
}
if _srmsd:
try:
from diffsynth.models.memory.spatial_grid_memory import SpatialCrossAttnReadout
except ImportError:
SpatialCrossAttnReadout = None
if SpatialCrossAttnReadout is not None:
dim = pipe.dit.dim
readout = SpatialCrossAttnReadout(dim=dim, num_heads=8)
readout.load_state_dict(_srmsd, strict=False)
readout = readout.to(dtype=next(pipe.dit.parameters()).dtype, device=next(pipe.dit.parameters()).device)
pipe.spatial_memory_readout_module = readout
print("[loop_utils] Loaded spatial_memory_readout_module")
if getattr(pipe, "enable_vram_management", None):
pipe.enable_vram_management()
return pipe
|