Files
vllm-omni/vllm_omni/diffusion/models/dreamzero/action_encoder.py
2026-06-02 19:50:20 +00:00

101 lines
3.8 KiB
Python

# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
"""Action encoder/decoder for DreamZero."""
from __future__ import annotations
import torch
import torch.nn as nn
import torch.nn.functional as F
def swish(x: torch.Tensor) -> torch.Tensor:
"""swish activation: x * sigmoid(x)"""
return x * torch.sigmoid(x)
class SinusoidalPositionalEncoding(nn.Module):
"""Sinusoidal encoding: (B, T) timesteps → (B, T, dim)"""
def __init__(self, embedding_dim: int) -> None:
super().__init__()
self.embedding_dim = embedding_dim
def forward(self, timesteps: torch.Tensor) -> torch.Tensor:
timesteps = timesteps.float()
half_dim = self.embedding_dim // 2
exponent = -torch.arange(half_dim, dtype=torch.float, device=timesteps.device) * (
torch.log(torch.tensor(10000.0)) / half_dim
)
freqs = timesteps.unsqueeze(-1) * exponent.exp()
return torch.cat([torch.sin(freqs), torch.cos(freqs)], dim=-1)
class CategorySpecificLinear(nn.Module):
"""Per-category linear: W[cat_id] @ x + b[cat_id]
Attributes:
W: (num_categories, input_dim, hidden_dim) — note: 0.02 * randn init
b: (num_categories, hidden_dim) — zero init
"""
def __init__(self, num_categories: int, input_dim: int, hidden_dim: int) -> None:
super().__init__()
self.W = nn.Parameter(0.02 * torch.randn(num_categories, input_dim, hidden_dim))
self.b = nn.Parameter(torch.zeros(num_categories, hidden_dim))
def forward(self, x: torch.Tensor, cat_ids: torch.Tensor) -> torch.Tensor:
selected_W = self.W[cat_ids]
selected_b = self.b[cat_ids]
return torch.bmm(x, selected_W) + selected_b.unsqueeze(1)
class CategorySpecificMLP(nn.Module):
"""Two-layer MLP: layer1 (relu) → layer2"""
def __init__(self, num_categories: int, input_dim: int, hidden_dim: int, output_dim: int) -> None:
super().__init__()
self.layer1 = CategorySpecificLinear(num_categories, input_dim, hidden_dim)
self.layer2 = CategorySpecificLinear(num_categories, hidden_dim, output_dim)
def forward(self, x: torch.Tensor, cat_ids: torch.Tensor) -> torch.Tensor:
hidden = F.relu(self.layer1(x, cat_ids))
return self.layer2(hidden, cat_ids)
class MultiEmbodimentActionEncoder(nn.Module):
"""Encode actions with embodiment-specific weights + sinusoidal timestep.
Flow: actions → W1 → concat(a_emb, pos_enc(timesteps)) → W2 (swish) → W3
Args:
action_dim: action vector dimension (e.g. 32)
hidden_size: output/hidden dimension (e.g. 5120 = model dim)
num_embodiments: number of robot types (e.g. 32)
"""
def __init__(self, action_dim: int, hidden_size: int, num_embodiments: int) -> None:
super().__init__()
self.hidden_size = hidden_size
self.W1 = CategorySpecificLinear(num_embodiments, action_dim, hidden_size)
self.W2 = CategorySpecificLinear(num_embodiments, 2 * hidden_size, hidden_size)
self.W3 = CategorySpecificLinear(num_embodiments, hidden_size, hidden_size)
self.pos_encoding = SinusoidalPositionalEncoding(hidden_size)
def forward(self, actions: torch.Tensor, timesteps: torch.Tensor, cat_ids: torch.Tensor) -> torch.Tensor:
"""
Args:
actions: (B, T, action_dim)
timesteps: (B, T) — per-token timestep
cat_ids: (B,) — embodiment id per sample
Returns:
(B, T, hidden_size)
"""
a_emb = self.W1(actions, cat_ids)
tau_emb = self.pos_encoding(timesteps).to(dtype=a_emb.dtype)
x = torch.cat([a_emb, tau_emb], dim=-1)
x = swish(self.W2(x, cat_ids))
x = self.W3(x, cat_ids)
return x