kerzgrr's picture
Add Diffusion Miner teacher checkpoint, Tile VAE, live infer, and gameplay GIF
b4b7ca9 verified
Raw History Blame Contribute Delete
34.9 kB
from __future__ import annotations
import math
from dataclasses import dataclass
import torch
import torch.nn.functional as F
from torch import nn
from torch.utils.checkpoint import checkpoint
from livediffusion.config import ModelConfig
@dataclass(slots=True)
class PrefixCache:
key_values: tuple[tuple[torch.Tensor, torch.Tensor], ...]
pooled: torch.Tensor
MemoryCache = PrefixCache
def _video_positions(
frames: int,
height: int,
width: int,
time_offset: int,
device: torch.device,
) -> torch.Tensor:
time = torch.arange(time_offset, time_offset + frames, device=device)
y = torch.arange(height, device=device)
x = torch.arange(width, device=device)
grid = torch.meshgrid(time, y, x, indexing="ij")
return torch.stack(grid, dim=-1).reshape(-1, 3).float()
class Rotary3D(nn.Module):
"""3D RoPE with a shorter temporal base so time and space do not share a spectrum."""
def __init__(
self,
head_size: int,
time_base: float = 1_000.0,
space_base: float = 10_000.0,
) -> None:
super().__init__()
self.rotary_size = (head_size // 6) * 6
self.axis_size = self.rotary_size // 3
axis_half = self.axis_size // 2
steps = torch.arange(axis_half, dtype=torch.float32) / max(axis_half, 1)
time_frequencies = torch.exp(-math.log(time_base) * steps)
space_frequencies = torch.exp(-math.log(space_base) * steps)
self.register_buffer(
"frequencies",
torch.stack((time_frequencies, space_frequencies, space_frequencies)),
persistent=False,
)
@staticmethod
def _rotate_half(inputs: torch.Tensor) -> torch.Tensor:
pairs = inputs.reshape(*inputs.shape[:-1], -1, 2)
first, second = pairs.unbind(dim=-1)
return torch.stack((-second, first), dim=-1).flatten(-2)
def forward(self, inputs: torch.Tensor, positions: torch.Tensor) -> torch.Tensor:
angles = torch.cat(
[
positions[:, axis, None] * self.frequencies[axis][None]
for axis in range(3)
],
dim=-1,
)
cosine = angles.cos().repeat_interleave(2, dim=-1)[None, None]
sine = angles.sin().repeat_interleave(2, dim=-1)[None, None]
rotary, remainder = inputs.split(
(self.rotary_size, inputs.shape[-1] - self.rotary_size),
dim=-1,
)
rotated = rotary * cosine.to(rotary.dtype)
rotated = rotated + self._rotate_half(rotary) * sine.to(rotary.dtype)
return torch.cat((rotated, remainder), dim=-1)
class ScalarEmbedding(nn.Module):
def __init__(self, hidden_size: int) -> None:
super().__init__()
self.hidden_size = hidden_size
self.mlp = nn.Sequential(
nn.Linear(hidden_size, hidden_size),
nn.SiLU(),
nn.Linear(hidden_size, hidden_size),
)
def forward(self, value: torch.Tensor) -> torch.Tensor:
value = value.reshape(-1).float()
half = self.hidden_size // 2
frequencies = torch.exp(
-math.log(10_000.0)
* torch.arange(half, device=value.device, dtype=torch.float32)
/ half
)
angles = value[:, None] * frequencies[None] * 1_000.0
embedding = torch.cat((angles.cos(), angles.sin()), dim=-1)
return self.mlp(embedding.to(dtype=self.mlp[0].weight.dtype))
class SwiGLU(nn.Module):
def __init__(self, hidden_size: int, ratio: float) -> None:
super().__init__()
inner_size = int(hidden_size * ratio * 2 / 3)
inner_size = ((inner_size + 63) // 64) * 64
self.input = nn.Linear(hidden_size, inner_size * 2, bias=False)
self.output = nn.Linear(inner_size, hidden_size, bias=False)
def forward(self, inputs: torch.Tensor) -> torch.Tensor:
values, gates = self.input(inputs).chunk(2, dim=-1)
return self.output(values * F.silu(gates))
class SelfAttention(nn.Module):
def __init__(self, hidden_size: int, num_heads: int, qk_norm: bool) -> None:
super().__init__()
self.num_heads = num_heads
self.head_size = hidden_size // num_heads
self.qkv = nn.Linear(hidden_size, hidden_size * 3, bias=False)
self.output = nn.Linear(hidden_size, hidden_size, bias=False)
self.q_norm = (
nn.RMSNorm(self.head_size, eps=1e-6)
if qk_norm
else nn.Identity()
)
self.k_norm = (
nn.RMSNorm(self.head_size, eps=1e-6)
if qk_norm
else nn.Identity()
)
self.rotary = Rotary3D(self.head_size)
def forward(self, inputs: torch.Tensor, positions: torch.Tensor) -> torch.Tensor:
batch, tokens, channels = inputs.shape
query, key, value = self.qkv(inputs).reshape(
batch,
tokens,
3,
self.num_heads,
self.head_size,
).unbind(dim=2)
query = self.rotary(self.q_norm(query).transpose(1, 2), positions)
key = self.rotary(self.k_norm(key).transpose(1, 2), positions)
value = value.transpose(1, 2)
attended = F.scaled_dot_product_attention(query, key, value)
return self.output(attended.transpose(1, 2).reshape(batch, tokens, channels))
class PrefixAttention(nn.Module):
"""Target attention over the joint clean-prefix and noisy-target sequence.
Keys and values may use fewer heads than queries. Prefix and target keys are
concatenated, so both sides share the same reduced head count.
"""
def __init__(
self,
hidden_size: int,
num_heads: int,
qk_norm: bool,
kv_heads: int = 0,
) -> None:
super().__init__()
self.num_heads = num_heads
self.head_size = hidden_size // num_heads
self.kv_heads = kv_heads or num_heads
kv_size = self.kv_heads * self.head_size
self.target_qkv = nn.Linear(hidden_size, hidden_size + kv_size * 2, bias=False)
self.prefix_key_value = nn.Linear(hidden_size, kv_size * 2, bias=False)
self.output = nn.Linear(hidden_size, hidden_size, bias=False)
self.q_norm = (
nn.RMSNorm(self.head_size, eps=1e-6)
if qk_norm
else nn.Identity()
)
self.k_norm = (
nn.RMSNorm(self.head_size, eps=1e-6)
if qk_norm
else nn.Identity()
)
self.rotary = Rotary3D(self.head_size)
def project_prefix(
self,
prefix: torch.Tensor,
positions: torch.Tensor,
) -> tuple[torch.Tensor, torch.Tensor]:
batch, tokens, _ = prefix.shape
key, value = self.prefix_key_value(prefix).reshape(
batch,
tokens,
2,
self.kv_heads,
self.head_size,
).unbind(dim=2)
key = self.rotary(self.k_norm(key).transpose(1, 2), positions)
return key, value.transpose(1, 2)
def forward_cached(
self,
target: torch.Tensor,
target_positions: torch.Tensor,
prefix_key_value: tuple[torch.Tensor, torch.Tensor],
) -> torch.Tensor:
batch, tokens, channels = target.shape
kv_size = self.kv_heads * self.head_size
projected = self.target_qkv(target)
query, key, value = projected.split((channels, kv_size, kv_size), dim=-1)
query = query.reshape(batch, tokens, self.num_heads, self.head_size)
key = key.reshape(batch, tokens, self.kv_heads, self.head_size)
value = value.reshape(batch, tokens, self.kv_heads, self.head_size)
query = self.rotary(self.q_norm(query).transpose(1, 2), target_positions)
key = self.rotary(self.k_norm(key).transpose(1, 2), target_positions)
value = value.transpose(1, 2)
prefix_key, prefix_value = prefix_key_value
key = torch.cat((prefix_key, key), dim=2)
value = torch.cat((prefix_value, value), dim=2)
attended = F.scaled_dot_product_attention(
query,
key,
value,
enable_gqa=self.kv_heads != self.num_heads,
)
return self.output(attended.transpose(1, 2).reshape(batch, tokens, channels))
@dataclass(slots=True)
class WorldMemoryState:
plane: torch.Tensor
registers: torch.Tensor
class WorldMemory(nn.Module):
"""Gated recurrent memory at latent resolution, plus global register state.
The plane is a convolutional GRU over one cell per latent position, so an
irreversible world edit stays represented after it scrolls out of the
attention prefix. Registers carry frame-global state the plane cannot hold.
"""
def __init__(
self,
latent_channels: int,
hidden_size: int,
memory_channels: int,
read_patch: int,
register_tokens: int,
) -> None:
super().__init__()
self.memory_channels = memory_channels
self.read_patch = read_patch
self.register_tokens = register_tokens
self.cell = nn.Conv2d(
latent_channels + memory_channels,
memory_channels * 3,
kernel_size=3,
padding=1,
)
self.read = nn.Conv2d(
memory_channels,
hidden_size,
kernel_size=read_patch,
stride=read_patch,
)
self.read_norm = nn.RMSNorm(hidden_size, eps=1e-6)
self.scale_embedding = nn.Parameter(torch.zeros(1, 1, hidden_size))
self.register_update = (
nn.Linear(memory_channels + hidden_size, hidden_size * 2)
if register_tokens > 0
else None
)
def initial_state(
self,
batch: int,
height: int,
width: int,
registers: torch.Tensor,
device: torch.device,
dtype: torch.dtype,
) -> WorldMemoryState:
plane = torch.zeros(
batch,
self.memory_channels,
height,
width,
device=device,
dtype=dtype,
)
return WorldMemoryState(plane, registers)
def advance(
self,
state: WorldMemoryState,
frames: torch.Tensor,
) -> WorldMemoryState:
plane = state.plane
for index in range(frames.shape[1]):
latent = frames[:, index].to(plane.dtype)
reset, update, candidate = self.cell(
torch.cat((latent, plane), dim=1)
).chunk(3, dim=1)
gate = torch.sigmoid(update)
proposal = torch.tanh(candidate + torch.sigmoid(reset) * plane)
plane = plane * (1.0 - gate) + proposal * gate
registers = state.registers
if self.register_update is not None:
pooled = plane.mean(dim=(2, 3))
summary = torch.cat(
(pooled[:, None].expand(-1, registers.shape[1], -1), registers),
dim=-1,
)
gate, proposal = self.register_update(summary).chunk(2, dim=-1)
gate = torch.sigmoid(gate)
registers = registers * (1.0 - gate) + torch.tanh(proposal) * gate
return WorldMemoryState(plane, registers)
def tokens(self, state: WorldMemoryState) -> torch.Tensor:
read = self.read(state.plane).flatten(2).transpose(1, 2)
return self.read_norm(read) + self.scale_embedding
class SharedModulation(nn.Module):
"""One adaLN projection shared by every block, with per-block offsets.
Per-block modulation costs 6 * hidden^2 each; sharing recovers that budget
for depth while keeping each block independently steerable.
"""
def __init__(self, hidden_size: int, blocks: int) -> None:
super().__init__()
self.projection = nn.Linear(hidden_size, hidden_size * 6)
self.offsets = nn.Parameter(torch.zeros(blocks, hidden_size * 6))
def forward(self, condition: torch.Tensor, block_index: int) -> torch.Tensor:
return self.projection(F.silu(condition)) + self.offsets[block_index]
class PrefixEncoderBlock(nn.Module):
"""Clean-context stack that runs before the joint prefix/target DiT."""
def __init__(self, config: ModelConfig) -> None:
super().__init__()
hidden_size = config.hidden_size
self.norm1 = nn.RMSNorm(hidden_size, eps=1e-6)
self.attention = SelfAttention(
hidden_size,
config.num_heads,
config.qk_norm,
)
self.norm2 = nn.RMSNorm(hidden_size, eps=1e-6)
self.mlp = SwiGLU(hidden_size, config.mlp_ratio)
self.attn_scale = nn.Parameter(torch.ones(hidden_size))
self.mlp_scale = nn.Parameter(torch.ones(hidden_size))
def forward(self, prefix: torch.Tensor, positions: torch.Tensor) -> torch.Tensor:
prefix = prefix + self.attn_scale * self.attention(self.norm1(prefix), positions)
return prefix + self.mlp_scale * self.mlp(self.norm2(prefix))
class PrefixDiTBlock(nn.Module):
def __init__(self, config: ModelConfig) -> None:
super().__init__()
hidden_size = config.hidden_size
self.prefix_norm1 = nn.RMSNorm(hidden_size, eps=1e-6)
self.prefix_attention = SelfAttention(
hidden_size,
config.num_heads,
config.qk_norm,
)
self.prefix_norm2 = nn.RMSNorm(hidden_size, eps=1e-6)
self.prefix_mlp = SwiGLU(hidden_size, config.mlp_ratio)
self.prefix_projection_norm = nn.RMSNorm(hidden_size, eps=1e-6)
self.target_norm1 = nn.RMSNorm(
hidden_size,
eps=1e-6,
elementwise_affine=False,
)
self.target_attention = PrefixAttention(
hidden_size,
config.num_heads,
config.qk_norm,
config.kv_heads,
)
self.target_norm2 = nn.RMSNorm(
hidden_size,
eps=1e-6,
elementwise_affine=False,
)
self.target_mlp = SwiGLU(hidden_size, config.mlp_ratio)
self.modulation = (
None
if config.shared_modulation
else nn.Linear(hidden_size, hidden_size * 6)
)
@staticmethod
def _modulate(
inputs: torch.Tensor,
shift: torch.Tensor,
scale: torch.Tensor,
) -> torch.Tensor:
return inputs * (1.0 + scale[:, None]) + shift[:, None]
def advance_prefix(
self,
prefix: torch.Tensor,
positions: torch.Tensor,
) -> torch.Tensor:
prefix = prefix + self.prefix_attention(self.prefix_norm1(prefix), positions)
return prefix + self.prefix_mlp(self.prefix_norm2(prefix))
def prefix_key_value(
self,
prefix: torch.Tensor,
positions: torch.Tensor,
) -> tuple[torch.Tensor, torch.Tensor]:
return self.target_attention.project_prefix(
self.prefix_projection_norm(prefix),
positions,
)
def advance_target(
self,
target: torch.Tensor,
condition: torch.Tensor,
target_positions: torch.Tensor,
prefix_key_value: tuple[torch.Tensor, torch.Tensor],
modulation: torch.Tensor | None = None,
) -> torch.Tensor:
if modulation is None:
if self.modulation is None:
raise RuntimeError("Shared modulation must be supplied by the trunk.")
modulation = self.modulation(F.silu(condition))
shift1, scale1, gate1, shift2, scale2, gate2 = modulation.chunk(6, dim=-1)
normalized = self._modulate(self.target_norm1(target), shift1, scale1)
target = target + gate1[:, None] * self.target_attention.forward_cached(
normalized,
target_positions,
prefix_key_value,
)
normalized = self._modulate(self.target_norm2(target), shift2, scale2)
return target + gate2[:, None] * self.target_mlp(normalized)
def forward(
self,
prefix: torch.Tensor,
target: torch.Tensor,
condition: torch.Tensor,
prefix_positions: torch.Tensor,
target_positions: torch.Tensor,
modulation: torch.Tensor | None = None,
) -> tuple[torch.Tensor, torch.Tensor]:
prefix = self.advance_prefix(prefix, prefix_positions)
key_value = self.prefix_key_value(prefix, prefix_positions)
target = self.advance_target(
target,
condition,
target_positions,
key_value,
modulation,
)
return prefix, target
class CausalLatentVideoDiT(nn.Module):
"""Multiscale clean-prefix/noisy-suffix latent video transformer."""
def __init__(
self,
model_config: ModelConfig,
latent_channels: int,
history_frames: int = 12,
) -> None:
super().__init__()
self.model_config = model_config
self.latent_channels = latent_channels
self.history_frames = history_frames
self.gradient_checkpointing = False
hidden_size = model_config.hidden_size
target_patch = model_config.target_patch_size
coarse_patch = model_config.memory_patch_size
fine_patch = model_config.fine_memory_patch_size
self.target_patch_embed = nn.Conv3d(
latent_channels,
hidden_size,
kernel_size=(1, target_patch, target_patch),
stride=(1, target_patch, target_patch),
)
self.coarse_prefix_embed = nn.Conv3d(
latent_channels,
hidden_size,
kernel_size=(1, coarse_patch, coarse_patch),
stride=(1, coarse_patch, coarse_patch),
)
self.fine_prefix_embed = nn.Conv3d(
latent_channels,
hidden_size,
kernel_size=(1, fine_patch, fine_patch),
stride=(1, fine_patch, fine_patch),
)
self.motion_prefix_embed = nn.Conv3d(
latent_channels,
hidden_size,
kernel_size=(1, fine_patch, fine_patch),
stride=(1, fine_patch, fine_patch),
)
self.coarse_scale_embedding = nn.Parameter(torch.zeros(1, 1, hidden_size))
self.fine_scale_embedding = nn.Parameter(torch.zeros(1, 1, hidden_size))
self.register_tokens = (
nn.Parameter(torch.randn(1, model_config.register_tokens, hidden_size) * 0.02)
if model_config.register_tokens > 0
else None
)
self.memory_blocks = nn.ModuleList(
PrefixEncoderBlock(model_config)
for _ in range(model_config.prefix_encoder_depth)
)
self.t_embedding = ScalarEmbedding(hidden_size)
self.r_embedding = ScalarEmbedding(hidden_size)
self.delta_embedding = ScalarEmbedding(hidden_size)
self.time_projection = nn.Sequential(
nn.Linear(hidden_size * 3, hidden_size),
nn.SiLU(),
nn.Linear(hidden_size, hidden_size),
)
self.prefix_condition = nn.Linear(hidden_size, hidden_size)
self.action_projection = (
nn.Sequential(
nn.Linear(model_config.action_dim, hidden_size),
nn.SiLU(),
nn.Linear(hidden_size, hidden_size),
)
if model_config.action_dim > 0
else None
)
self.context_projection = (
nn.Linear(model_config.context_dim, hidden_size, bias=False)
if model_config.context_dim > 0
else None
)
self.blocks = nn.ModuleList(
PrefixDiTBlock(model_config) for _ in range(model_config.depth)
)
self.shared_modulation = (
SharedModulation(hidden_size, model_config.depth)
if model_config.shared_modulation
else None
)
self.world_memory = (
WorldMemory(
latent_channels,
hidden_size,
model_config.memory_channels,
coarse_patch,
model_config.register_tokens,
)
if model_config.memory_channels > 0
else None
)
self.output_norm = nn.RMSNorm(
hidden_size,
eps=1e-6,
elementwise_affine=False,
)
self.output_modulation = nn.Linear(hidden_size, hidden_size * 2)
self.output_projection = nn.Linear(
hidden_size,
target_patch * target_patch * latent_channels,
)
self.outcome_norm = nn.RMSNorm(hidden_size, eps=1e-6)
self.reward_head = nn.Linear(hidden_size, 1)
self.terminal_head = nn.Linear(hidden_size, 1)
self.reset_parameters()
def enable_activation_checkpointing(self, enabled: bool = True) -> None:
self.gradient_checkpointing = enabled
def reset_parameters(self) -> None:
for module in self.modules():
if isinstance(module, nn.Linear):
nn.init.xavier_uniform_(module.weight)
if module.bias is not None:
nn.init.zeros_(module.bias)
for embedding in (
self.target_patch_embed,
self.coarse_prefix_embed,
self.fine_prefix_embed,
self.motion_prefix_embed,
):
nn.init.normal_(embedding.weight, std=0.02)
nn.init.normal_(self.coarse_scale_embedding, std=0.02)
nn.init.normal_(self.fine_scale_embedding, std=0.02)
for block in self.blocks:
if block.modulation is not None:
nn.init.zeros_(block.modulation.weight)
nn.init.zeros_(block.modulation.bias)
if self.shared_modulation is not None:
nn.init.zeros_(self.shared_modulation.projection.weight)
nn.init.zeros_(self.shared_modulation.projection.bias)
nn.init.zeros_(self.shared_modulation.offsets)
if self.world_memory is not None:
nn.init.zeros_(self.world_memory.scale_embedding)
nn.init.zeros_(self.output_modulation.weight)
nn.init.zeros_(self.output_modulation.bias)
nn.init.zeros_(self.output_projection.weight)
nn.init.zeros_(self.output_projection.bias)
nn.init.zeros_(self.reward_head.weight)
nn.init.zeros_(self.reward_head.bias)
nn.init.zeros_(self.terminal_head.weight)
nn.init.zeros_(self.terminal_head.bias)
@staticmethod
def _patch(
embedding: nn.Conv3d,
video: torch.Tensor,
time_offset: int,
) -> tuple[torch.Tensor, torch.Tensor]:
tokens = embedding(video.transpose(1, 2))
_, _, frames, height, width = tokens.shape
positions = _video_positions(
frames,
height,
width,
time_offset,
video.device,
)
return tokens.flatten(2).transpose(1, 2), positions
def _encode_prefix(
self,
history: torch.Tensor,
context: torch.Tensor | None = None,
history_actions: torch.Tensor | None = None,
) -> tuple[torch.Tensor, torch.Tensor]:
if history.shape[1] < self.history_frames:
raise ValueError(
f"Expected at least {self.history_frames} history frames, "
f"got {history.shape[1]}."
)
batch = history.shape[0]
registers = (
self.register_tokens.expand(batch, -1, -1)
if self.register_tokens is not None
else None
)
memory_tokens: torch.Tensor | None = None
if self.world_memory is not None:
# Every observed frame is folded into the recurrent state, including
# frames older than the attention window.
state = self.world_memory.initial_state(
batch,
history.shape[-2],
history.shape[-1],
registers
if registers is not None
else history.new_zeros(batch, 0, self.model_config.hidden_size),
history.device,
history.dtype,
)
state = self.world_memory.advance(state, history)
memory_tokens = self.world_memory.tokens(state)
if registers is not None:
registers = state.registers
window = history[:, -self.history_frames :]
coarse, coarse_positions = self._patch(
self.coarse_prefix_embed,
window,
0,
)
if self.action_projection is not None and history_actions is not None:
window_actions = history_actions[:, -self.history_frames :]
tokens_per_frame = coarse.shape[1] // self.history_frames
coarse = coarse + self.action_projection(
window_actions
).repeat_interleave(tokens_per_frame, dim=1)
history = window
fine_frames = self.model_config.fine_history_frames
fine_history = history[:, -fine_frames:]
previous = history[:, -fine_frames - 1 : -1]
if previous.shape[1] != fine_frames:
previous = torch.cat((fine_history[:, :1], fine_history[:, :-1]), dim=1)
motion = fine_history - previous
fine, fine_positions = self._patch(
self.fine_prefix_embed,
fine_history,
self.history_frames - fine_frames,
)
motion_tokens, _ = self._patch(
self.motion_prefix_embed,
motion,
self.history_frames - fine_frames,
)
coarse = coarse + self.coarse_scale_embedding
fine = fine + motion_tokens + self.fine_scale_embedding
prefix = torch.cat((coarse, fine), dim=1)
positions = torch.cat((coarse_positions, fine_positions), dim=0)
if memory_tokens is not None:
coarse_patch = self.model_config.memory_patch_size
memory_positions = _video_positions(
1,
history.shape[-2] // coarse_patch,
history.shape[-1] // coarse_patch,
-2,
history.device,
)
prefix = torch.cat((memory_tokens, prefix), dim=1)
positions = torch.cat((memory_positions, positions), dim=0)
if registers is not None:
register_count = registers.shape[1]
register_positions = torch.stack(
(
torch.full((register_count,), -1.0, device=history.device),
torch.zeros(register_count, device=history.device),
torch.arange(register_count, device=history.device, dtype=torch.float32),
),
dim=-1,
)
prefix = torch.cat((registers, prefix), dim=1)
positions = torch.cat((register_positions, positions), dim=0)
if self.context_projection is not None and context is not None:
projected = self.context_projection(context)
prefix = torch.cat((prefix, projected), dim=1)
positions = torch.cat(
(
positions,
torch.zeros(projected.shape[1], 3, device=history.device),
),
dim=0,
)
for block in self.memory_blocks:
prefix = block(prefix, positions)
return prefix, positions
def _pool_prefix(self, prefix: torch.Tensor) -> torch.Tensor:
register_count = self.model_config.register_tokens
if register_count > 0:
return prefix[:, :register_count].mean(dim=1)
return prefix.mean(dim=1)
def _condition(
self,
prefix: torch.Tensor,
r: torch.Tensor,
t: torch.Tensor,
) -> torch.Tensor:
time = self.time_projection(
torch.cat(
(
self.t_embedding(t),
self.r_embedding(r),
self.delta_embedding(t - r),
),
dim=-1,
)
)
return time + self.prefix_condition(self._pool_prefix(prefix))
def build_memory_cache(
self,
history: torch.Tensor,
context: torch.Tensor | None = None,
history_actions: torch.Tensor | None = None,
) -> PrefixCache:
prefix, positions = self._encode_prefix(history, context, history_actions)
pooled = self.prefix_condition(self._pool_prefix(prefix))
key_values: list[tuple[torch.Tensor, torch.Tensor]] = []
for block in self.blocks:
prefix = block.advance_prefix(prefix, positions)
key_values.append(block.prefix_key_value(prefix, positions))
return PrefixCache(tuple(key_values), pooled)
def predict_outcomes(
self,
history: torch.Tensor,
actions: torch.Tensor | None = None,
frame_count: int = 1,
memory_cache: PrefixCache | None = None,
history_actions: torch.Tensor | None = None,
) -> tuple[torch.Tensor, torch.Tensor]:
if memory_cache is None:
prefix, _ = self._encode_prefix(history, None, history_actions)
pooled = self.prefix_condition(self._pool_prefix(prefix))
else:
pooled = memory_cache.pooled
hidden = pooled[:, None].expand(-1, frame_count, -1)
if self.action_projection is not None:
if actions is None or actions.shape[1] != frame_count:
raise ValueError("Outcome actions must match frame_count.")
hidden = hidden + self.action_projection(actions)
hidden = self.outcome_norm(hidden)
return self.reward_head(hidden).squeeze(-1), self.terminal_head(hidden).squeeze(-1)
def _unpatchify(
self,
patches: torch.Tensor,
frames: int,
height: int,
width: int,
) -> torch.Tensor:
batch = patches.shape[0]
patch = self.model_config.target_patch_size
patches = patches.reshape(
batch,
frames,
height,
width,
patch,
patch,
self.latent_channels,
)
return patches.permute(0, 1, 6, 2, 4, 3, 5).reshape(
batch,
frames,
self.latent_channels,
height * patch,
width * patch,
)
def forward(
self,
noisy_target: torch.Tensor,
history: torch.Tensor,
r: torch.Tensor,
t: torch.Tensor,
actions: torch.Tensor | None = None,
context: torch.Tensor | None = None,
memory_cache: PrefixCache | None = None,
history_actions: torch.Tensor | None = None,
) -> torch.Tensor:
target_volume = self.target_patch_embed(noisy_target.transpose(1, 2))
batch, _, frames, height, width = target_volume.shape
target = target_volume.flatten(2).transpose(1, 2)
target_positions = _video_positions(
frames,
height,
width,
self.history_frames,
noisy_target.device,
)
if memory_cache is None:
prefix, prefix_positions = self._encode_prefix(
history,
context,
history_actions,
)
condition = self._condition(prefix, r, t)
else:
prefix = None
prefix_positions = None
condition = self.time_projection(
torch.cat(
(
self.t_embedding(t),
self.r_embedding(r),
self.delta_embedding(t - r),
),
dim=-1,
)
) + memory_cache.pooled
if self.action_projection is not None:
if actions is None or actions.shape[:2] != (batch, frames):
raise ValueError("Actions must match [batch, target_frames, action_dim].")
action_embedding = self.action_projection(actions)
target = target + action_embedding.repeat_interleave(height * width, dim=1)
condition = condition + action_embedding.mean(dim=1)
for block_index, block in enumerate(self.blocks):
modulation = (
self.shared_modulation(condition, block_index)
if self.shared_modulation is not None
else None
)
if memory_cache is None:
if prefix is None or prefix_positions is None:
raise RuntimeError("Prefix state is unavailable.")
if self.training and self.gradient_checkpointing:
prefix, target = checkpoint(
block,
prefix,
target,
condition,
prefix_positions,
target_positions,
modulation,
use_reentrant=False,
)
else:
prefix, target = block(
prefix,
target,
condition,
prefix_positions,
target_positions,
modulation,
)
else:
target = block.advance_target(
target,
condition,
target_positions,
memory_cache.key_values[block_index],
modulation,
)
shift, scale = self.output_modulation(F.silu(condition)).chunk(2, dim=-1)
target = self.output_norm(target) * (1.0 + scale[:, None]) + shift[:, None]
return self._unpatchify(
self.output_projection(target),
frames,
height,
width,
)
CausalVideoDiT = CausalLatentVideoDiT