chore: import upstream snapshot with attribution
PR Test (NPU) / check-changes (push) Has been cancelled
PR Test (NPU) / pr-gate (push) Has been cancelled
PR Test (NPU) / set-image-config (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-4-npu-a3 (push) Has been cancelled
PR Test (NPU) / stage-b-test-16-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-1-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-2-npu-a3 (push) Has been cancelled
PR Test (Arm64) / pr-gate (push) Has been cancelled
PR Test (Arm64) / check-changes (push) Has been cancelled
PR Test (Arm64) / build-test (push) Has been cancelled
PR Test (sgl-router) / gate (push) Has been cancelled
PR Test (sgl-router) / tier-1 — lint (push) Has been cancelled
PR Test (sgl-router) / tier-2 — build + test (push) Has been cancelled
PR Test (sgl-router) / tier-3 — docker (placeholder) (push) Has been cancelled
PR Test (sgl-router) / tier-3 — k8s integration (push) Has been cancelled
PR Test (sgl-router) / tier-3 — e2e (push) Has been cancelled
PR Test (sgl-router) / finish (push) Has been cancelled
PR Test (NPU) / single-node-poc (map[name:qwen3_6_27b_w8a8_1p_in64k_out1k_50ms runner:linux-aarch64-a3-2 test_case:test/registered/ascend/performance/qwen3_6_27b/test_npu_qwen3_6_27b_w8a8_1p_in64k_out1k_50ms.py test_type:perf]) (push) Has been cancelled
PR Test (NPU) / pr-test-npu-finish (push) Has been cancelled
PR Test (Xeon) / pr-gate (push) Has been cancelled
PR Test (Xeon) / check-changes (push) Has been cancelled
PR Test (Xeon) / build-test (, xeon-gnr, base-b-test-cpu) (push) Has been cancelled
PR Test (XPU) / check-changes (push) Has been cancelled
PR Test (XPU) / pr-gate (push) Has been cancelled
PR Test (XPU) / stage-a-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / wait-for-stage-a (push) Has been cancelled
PR Test (XPU) / stage-b-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / finish (push) Has been cancelled
CI Model Inventory / build-inventory (push) Has been cancelled
Lint / lint (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Compilation Check (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Manual Policy (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Request Processing (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Summary (push) Has been cancelled
PR Test (SMG) / build-wheel (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on windows (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (x86_64 - auto) (push) Has been cancelled
PR Test (SMG) / python-unit-tests (push) Has been cancelled
PR Test (SMG) / unit-tests (push) Has been cancelled
PR Test (SMG) / benchmarks (push) Has been cancelled
PR Test (SMG) / chat-completions (push) Has been cancelled
PR Test (SMG) / chat-completions-4gpu (push) Has been cancelled
PR Test (SMG) / e2e (push) Has been cancelled
PR Test (SMG) / docker-build-test (push) Has been cancelled
PR Test (SMG) / k8s-integration (push) Has been cancelled
PR Test (SMG) / finish (push) Has been cancelled
PR Test (SMG) / summarize-benchmarks (push) Has been cancelled
Release SGLang Model Gateway Docker Image / publish (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Build SDist (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Upload to PyPI (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (aarch64, 12.9, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (x86_64, 12.9, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu129 (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (aarch64, 13.0, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (x86_64, 13.0, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu130 (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 700) (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 720) (push) Has been cancelled
Release SGLang Kernels / release-rocm700 (push) Has been cancelled
Release SGLang Kernels / release-rocm720 (push) Has been cancelled
Release SGLang Kernels / build-musa43 (43, 3.10) (push) Has been cancelled
Release SGLang Kernels / release-musa43 (push) Has been cancelled
PR Test (NPU) / check-changes (push) Has been cancelled
PR Test (NPU) / pr-gate (push) Has been cancelled
PR Test (NPU) / set-image-config (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-4-npu-a3 (push) Has been cancelled
PR Test (NPU) / stage-b-test-16-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-1-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-2-npu-a3 (push) Has been cancelled
PR Test (Arm64) / pr-gate (push) Has been cancelled
PR Test (Arm64) / check-changes (push) Has been cancelled
PR Test (Arm64) / build-test (push) Has been cancelled
PR Test (sgl-router) / gate (push) Has been cancelled
PR Test (sgl-router) / tier-1 — lint (push) Has been cancelled
PR Test (sgl-router) / tier-2 — build + test (push) Has been cancelled
PR Test (sgl-router) / tier-3 — docker (placeholder) (push) Has been cancelled
PR Test (sgl-router) / tier-3 — k8s integration (push) Has been cancelled
PR Test (sgl-router) / tier-3 — e2e (push) Has been cancelled
PR Test (sgl-router) / finish (push) Has been cancelled
PR Test (NPU) / single-node-poc (map[name:qwen3_6_27b_w8a8_1p_in64k_out1k_50ms runner:linux-aarch64-a3-2 test_case:test/registered/ascend/performance/qwen3_6_27b/test_npu_qwen3_6_27b_w8a8_1p_in64k_out1k_50ms.py test_type:perf]) (push) Has been cancelled
PR Test (NPU) / pr-test-npu-finish (push) Has been cancelled
PR Test (Xeon) / pr-gate (push) Has been cancelled
PR Test (Xeon) / check-changes (push) Has been cancelled
PR Test (Xeon) / build-test (, xeon-gnr, base-b-test-cpu) (push) Has been cancelled
PR Test (XPU) / check-changes (push) Has been cancelled
PR Test (XPU) / pr-gate (push) Has been cancelled
PR Test (XPU) / stage-a-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / wait-for-stage-a (push) Has been cancelled
PR Test (XPU) / stage-b-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / finish (push) Has been cancelled
CI Model Inventory / build-inventory (push) Has been cancelled
Lint / lint (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Compilation Check (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Manual Policy (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Request Processing (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Summary (push) Has been cancelled
PR Test (SMG) / build-wheel (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on windows (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (x86_64 - auto) (push) Has been cancelled
PR Test (SMG) / python-unit-tests (push) Has been cancelled
PR Test (SMG) / unit-tests (push) Has been cancelled
PR Test (SMG) / benchmarks (push) Has been cancelled
PR Test (SMG) / chat-completions (push) Has been cancelled
PR Test (SMG) / chat-completions-4gpu (push) Has been cancelled
PR Test (SMG) / e2e (push) Has been cancelled
PR Test (SMG) / docker-build-test (push) Has been cancelled
PR Test (SMG) / k8s-integration (push) Has been cancelled
PR Test (SMG) / finish (push) Has been cancelled
PR Test (SMG) / summarize-benchmarks (push) Has been cancelled
Release SGLang Model Gateway Docker Image / publish (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Build SDist (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Upload to PyPI (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (aarch64, 12.9, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (x86_64, 12.9, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu129 (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (aarch64, 13.0, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (x86_64, 13.0, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu130 (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 700) (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 720) (push) Has been cancelled
Release SGLang Kernels / release-rocm700 (push) Has been cancelled
Release SGLang Kernels / release-rocm720 (push) Has been cancelled
Release SGLang Kernels / build-musa43 (43, 3.10) (push) Has been cancelled
Release SGLang Kernels / release-musa43 (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,810 @@
|
||||
# Copied and adapted from: https://github.com/hao-ai-lab/FastVideo
|
||||
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from abc import ABC, abstractmethod
|
||||
from functools import lru_cache
|
||||
from math import isqrt, prod
|
||||
from typing import Optional, cast
|
||||
|
||||
import numpy as np
|
||||
import torch
|
||||
import torch.distributed as dist
|
||||
from diffusers.models.autoencoders.vae import DiagonalGaussianDistribution
|
||||
from diffusers.utils.torch_utils import randn_tensor
|
||||
from torch import nn
|
||||
|
||||
from sglang.multimodal_gen.configs.models import VAEConfig
|
||||
from sglang.multimodal_gen.configs.models.vaes.base import (
|
||||
should_use_spatial_shard_parallel_decode,
|
||||
)
|
||||
from sglang.multimodal_gen.runtime.distributed import (
|
||||
get_decode_parallel_group_coordinator,
|
||||
get_decode_parallel_world_size,
|
||||
get_sp_parallel_rank,
|
||||
get_sp_world_size,
|
||||
model_parallel_is_initialized,
|
||||
)
|
||||
from sglang.multimodal_gen.runtime.managers.memory_managers.layerwise_offload import (
|
||||
LayerwiseOffloadableModuleMixin,
|
||||
)
|
||||
|
||||
|
||||
@lru_cache(maxsize=1)
|
||||
def _cached_decode_parallel_world_size(
|
||||
is_dist_initialized: bool, is_model_parallel_initialized: bool, group_id: int
|
||||
) -> int:
|
||||
if not is_dist_initialized or not is_model_parallel_initialized:
|
||||
return 1
|
||||
return get_decode_parallel_world_size()
|
||||
|
||||
|
||||
def _decode_parallel_world_size() -> int:
|
||||
is_dist_initialized = dist.is_initialized()
|
||||
is_model_parallel_initialized = model_parallel_is_initialized()
|
||||
if not is_dist_initialized or not is_model_parallel_initialized:
|
||||
return _cached_decode_parallel_world_size(
|
||||
is_dist_initialized, is_model_parallel_initialized, 0
|
||||
)
|
||||
return _cached_decode_parallel_world_size(
|
||||
is_dist_initialized,
|
||||
is_model_parallel_initialized,
|
||||
id(get_decode_parallel_group_coordinator()),
|
||||
)
|
||||
|
||||
|
||||
def has_decode_parallel_world() -> bool:
|
||||
return _decode_parallel_world_size() > 1
|
||||
|
||||
|
||||
def can_install_spatial_shard_parallel_decode(config: VAEConfig | None) -> bool:
|
||||
world_size = _decode_parallel_world_size()
|
||||
return (
|
||||
config is not None
|
||||
and world_size > 1
|
||||
and should_use_spatial_shard_parallel_decode(config, world_size=world_size)
|
||||
)
|
||||
|
||||
|
||||
def should_run_spatial_shard_parallel_decode(
|
||||
config: VAEConfig, z: torch.Tensor
|
||||
) -> bool:
|
||||
world_size = _decode_parallel_world_size()
|
||||
return world_size > 1 and should_use_spatial_shard_parallel_decode(
|
||||
config, z, world_size
|
||||
)
|
||||
|
||||
|
||||
class ParallelTiledVAE(ABC, nn.Module, LayerwiseOffloadableModuleMixin):
|
||||
layerwise_offload_dit_group_enabled = False
|
||||
layer_names = [
|
||||
"encoder.down_blocks",
|
||||
"decoder.up_blocks",
|
||||
"encoder.down",
|
||||
"decoder.up",
|
||||
]
|
||||
tile_sample_min_height: int
|
||||
tile_sample_min_width: int
|
||||
tile_sample_min_num_frames: int
|
||||
tile_sample_stride_height: int
|
||||
tile_sample_stride_width: int
|
||||
tile_sample_stride_num_frames: int
|
||||
blend_num_frames: int
|
||||
use_tiling: bool
|
||||
use_temporal_tiling: bool
|
||||
use_parallel_tiling: bool
|
||||
use_parallel_decode: bool
|
||||
parallel_decode_mode: str
|
||||
|
||||
def __init__(self, config: VAEConfig, **kwargs) -> None:
|
||||
super().__init__()
|
||||
self.config = config
|
||||
self.tile_sample_min_height = config.tile_sample_min_height
|
||||
self.tile_sample_min_width = config.tile_sample_min_width
|
||||
self.tile_sample_min_num_frames = config.tile_sample_min_num_frames
|
||||
self.tile_sample_stride_height = config.tile_sample_stride_height
|
||||
self.tile_sample_stride_width = config.tile_sample_stride_width
|
||||
self.tile_sample_stride_num_frames = config.tile_sample_stride_num_frames
|
||||
self.blend_num_frames = config.blend_num_frames
|
||||
self.use_tiling = config.use_tiling
|
||||
self.use_temporal_tiling = config.use_temporal_tiling
|
||||
self.use_parallel_tiling = config.use_parallel_tiling
|
||||
self.use_parallel_decode = config.use_parallel_decode
|
||||
self.parallel_decode_mode = config.parallel_decode_mode
|
||||
|
||||
@property
|
||||
def device(self):
|
||||
return next(self.parameters()).device
|
||||
|
||||
@property
|
||||
def temporal_compression_ratio(self) -> int:
|
||||
return cast(int, self.config.temporal_compression_ratio)
|
||||
|
||||
@property
|
||||
def spatial_compression_ratio(self) -> int:
|
||||
return cast(int, self.config.spatial_compression_ratio)
|
||||
|
||||
@property
|
||||
def scaling_factor(self) -> float | torch.Tensor:
|
||||
return cast(float | torch.Tensor, self.config.scaling_factor)
|
||||
|
||||
@abstractmethod
|
||||
def _encode(self, *args, **kwargs) -> torch.Tensor:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def _decode(self, *args, **kwargs) -> torch.Tensor:
|
||||
pass
|
||||
|
||||
def encode(self, x: torch.Tensor) -> DiagonalGaussianDistribution:
|
||||
batch_size, num_channels, num_frames, height, width = x.shape
|
||||
latent_num_frames = (num_frames - 1) // self.temporal_compression_ratio + 1
|
||||
|
||||
if (
|
||||
self.use_tiling
|
||||
and self.use_temporal_tiling
|
||||
and num_frames > self.tile_sample_min_num_frames
|
||||
):
|
||||
latents = self.tiled_encode(x)[:, :, :latent_num_frames]
|
||||
elif self.use_tiling and (
|
||||
width > self.tile_sample_min_width or height > self.tile_sample_min_height
|
||||
):
|
||||
latents = self.spatial_tiled_encode(x)[:, :, :latent_num_frames]
|
||||
else:
|
||||
latents = self._encode(x)[:, :, :latent_num_frames]
|
||||
return DiagonalGaussianDistribution(latents)
|
||||
|
||||
def decode(self, z: torch.Tensor) -> torch.Tensor:
|
||||
batch_size, num_channels, num_frames, height, width = z.shape
|
||||
tile_latent_min_height = (
|
||||
self.tile_sample_min_height // self.spatial_compression_ratio
|
||||
)
|
||||
tile_latent_min_width = (
|
||||
self.tile_sample_stride_width // self.spatial_compression_ratio
|
||||
)
|
||||
tile_latent_min_num_frames = (
|
||||
self.tile_sample_min_num_frames // self.temporal_compression_ratio
|
||||
)
|
||||
num_sample_frames = (num_frames - 1) * self.temporal_compression_ratio + 1
|
||||
|
||||
if should_run_spatial_shard_parallel_decode(self.config, z):
|
||||
return self._decode(z)[:, :, :num_sample_frames]
|
||||
|
||||
if (
|
||||
self.parallel_decode_mode == "tiled"
|
||||
and self.use_tiling
|
||||
and self.use_parallel_tiling
|
||||
and get_sp_world_size() > 1
|
||||
):
|
||||
return self.parallel_tiled_decode(z)[:, :, :num_sample_frames]
|
||||
if (
|
||||
self.use_tiling
|
||||
and self.use_temporal_tiling
|
||||
and num_frames > tile_latent_min_num_frames
|
||||
):
|
||||
return self.tiled_decode(z)[:, :, :num_sample_frames]
|
||||
|
||||
if self.use_tiling and (
|
||||
width > tile_latent_min_width or height > tile_latent_min_height
|
||||
):
|
||||
return self.spatial_tiled_decode(z)[:, :, :num_sample_frames]
|
||||
|
||||
return self._decode(z)[:, :, :num_sample_frames]
|
||||
|
||||
def blend_v(
|
||||
self, a: torch.Tensor, b: torch.Tensor, blend_extent: int
|
||||
) -> torch.Tensor:
|
||||
blend_extent = min(a.shape[-2], b.shape[-2], blend_extent)
|
||||
for y in range(blend_extent):
|
||||
b[:, :, :, y, :] = a[:, :, :, -blend_extent + y, :] * (
|
||||
1 - y / blend_extent
|
||||
) + b[:, :, :, y, :] * (y / blend_extent)
|
||||
return b
|
||||
|
||||
def blend_h(
|
||||
self, a: torch.Tensor, b: torch.Tensor, blend_extent: int
|
||||
) -> torch.Tensor:
|
||||
blend_extent = min(a.shape[-1], b.shape[-1], blend_extent)
|
||||
for x in range(blend_extent):
|
||||
b[:, :, :, :, x] = a[:, :, :, :, -blend_extent + x] * (
|
||||
1 - x / blend_extent
|
||||
) + b[:, :, :, :, x] * (x / blend_extent)
|
||||
return b
|
||||
|
||||
def blend_t(
|
||||
self, a: torch.Tensor, b: torch.Tensor, blend_extent: int
|
||||
) -> torch.Tensor:
|
||||
blend_extent = min(a.shape[-3], b.shape[-3], blend_extent)
|
||||
for x in range(blend_extent):
|
||||
b[:, :, x, :, :] = a[:, :, -blend_extent + x, :, :] * (
|
||||
1 - x / blend_extent
|
||||
) + b[:, :, x, :, :] * (x / blend_extent)
|
||||
return b
|
||||
|
||||
def spatial_tiled_encode(self, x: torch.Tensor) -> torch.Tensor:
|
||||
r"""Encode a batch of images using a tiled encoder.
|
||||
|
||||
Args:
|
||||
x (`torch.Tensor`): Input batch of videos.
|
||||
|
||||
Returns:
|
||||
`torch.Tensor`:
|
||||
The latent representation of the encoded videos.
|
||||
"""
|
||||
_, _, _, height, width = x.shape
|
||||
# latent_height = height // self.spatial_compression_ratio
|
||||
# latent_width = width // self.spatial_compression_ratio
|
||||
|
||||
tile_latent_min_height = (
|
||||
self.tile_sample_min_height // self.spatial_compression_ratio
|
||||
)
|
||||
tile_latent_min_width = (
|
||||
self.tile_sample_min_width // self.spatial_compression_ratio
|
||||
)
|
||||
tile_latent_stride_height = (
|
||||
self.tile_sample_stride_height // self.spatial_compression_ratio
|
||||
)
|
||||
tile_latent_stride_width = (
|
||||
self.tile_sample_stride_width // self.spatial_compression_ratio
|
||||
)
|
||||
|
||||
blend_height = tile_latent_min_height - tile_latent_stride_height
|
||||
blend_width = tile_latent_min_width - tile_latent_stride_width
|
||||
|
||||
# Split x into overlapping tiles and encode them separately.
|
||||
# The tiles have an overlap to avoid seams between tiles.
|
||||
rows = []
|
||||
for i in range(0, height, self.tile_sample_stride_height):
|
||||
row = []
|
||||
for j in range(0, width, self.tile_sample_stride_width):
|
||||
tile = x[
|
||||
:,
|
||||
:,
|
||||
:,
|
||||
i : i + self.tile_sample_min_height,
|
||||
j : j + self.tile_sample_min_width,
|
||||
]
|
||||
tile = self._encode(tile)
|
||||
row.append(tile)
|
||||
rows.append(row)
|
||||
|
||||
return self._merge_spatial_tiles(
|
||||
rows,
|
||||
blend_height,
|
||||
blend_width,
|
||||
tile_latent_stride_height,
|
||||
tile_latent_stride_width,
|
||||
)
|
||||
|
||||
def parallel_tiled_decode(self, z: torch.FloatTensor) -> torch.FloatTensor:
|
||||
"""
|
||||
Parallel version of tiled_decode that distributes both temporal and spatial computation across GPUs
|
||||
"""
|
||||
world_size, rank = get_sp_world_size(), get_sp_parallel_rank()
|
||||
_, _, T, H, W = z.shape
|
||||
|
||||
tile_latent_min_height = (
|
||||
self.tile_sample_min_height // self.spatial_compression_ratio
|
||||
)
|
||||
tile_latent_min_width = (
|
||||
self.tile_sample_min_width // self.spatial_compression_ratio
|
||||
)
|
||||
tile_latent_min_num_frames = (
|
||||
self.tile_sample_min_num_frames // self.temporal_compression_ratio
|
||||
)
|
||||
tile_latent_stride_height = (
|
||||
self.tile_sample_stride_height // self.spatial_compression_ratio
|
||||
)
|
||||
tile_latent_stride_width = (
|
||||
self.tile_sample_stride_width // self.spatial_compression_ratio
|
||||
)
|
||||
tile_latent_stride_num_frames = (
|
||||
self.tile_sample_stride_num_frames // self.temporal_compression_ratio
|
||||
)
|
||||
|
||||
blend_height = self.tile_sample_min_height - self.tile_sample_stride_height
|
||||
blend_width = self.tile_sample_min_width - self.tile_sample_stride_width
|
||||
|
||||
# Calculate tile dimensions
|
||||
num_t_tiles = (
|
||||
T + tile_latent_stride_num_frames - 1
|
||||
) // tile_latent_stride_num_frames
|
||||
num_h_tiles = (H + tile_latent_stride_height - 1) // tile_latent_stride_height
|
||||
num_w_tiles = (W + tile_latent_stride_width - 1) // tile_latent_stride_width
|
||||
total_spatial_tiles = num_h_tiles * num_w_tiles
|
||||
total_tiles = num_t_tiles * total_spatial_tiles
|
||||
tiles_per_rank = (total_tiles + world_size - 1) // world_size
|
||||
start_tile_idx = rank * tiles_per_rank
|
||||
end_tile_idx = min((rank + 1) * tiles_per_rank, total_tiles)
|
||||
|
||||
local_results = []
|
||||
local_dim_metadata = []
|
||||
for global_idx in range(start_tile_idx, end_tile_idx):
|
||||
t_idx = global_idx // total_spatial_tiles
|
||||
spatial_idx = global_idx % total_spatial_tiles
|
||||
h_idx = spatial_idx // num_w_tiles
|
||||
w_idx = spatial_idx % num_w_tiles
|
||||
|
||||
t_start = t_idx * tile_latent_stride_num_frames
|
||||
h_start = h_idx * tile_latent_stride_height
|
||||
w_start = w_idx * tile_latent_stride_width
|
||||
|
||||
tile = z[
|
||||
:,
|
||||
:,
|
||||
t_start : t_start + tile_latent_min_num_frames + 1,
|
||||
h_start : h_start + tile_latent_min_height,
|
||||
w_start : w_start + tile_latent_min_width,
|
||||
]
|
||||
decoded_tile = self._decode(tile)
|
||||
if t_start > 0:
|
||||
decoded_tile = decoded_tile[:, :, 1:, :, :]
|
||||
local_results.append(decoded_tile.reshape(-1))
|
||||
local_dim_metadata.append(decoded_tile.shape)
|
||||
|
||||
if local_results:
|
||||
results = torch.cat(local_results, dim=0).contiguous()
|
||||
else:
|
||||
results = z.new_empty((0,), dtype=z.dtype)
|
||||
del local_results
|
||||
|
||||
local_size = torch.tensor(
|
||||
[results.size(0)], device=results.device, dtype=torch.int64
|
||||
)
|
||||
all_sizes = [
|
||||
torch.zeros(1, device=results.device, dtype=torch.int64)
|
||||
for _ in range(world_size)
|
||||
]
|
||||
dist.all_gather(all_sizes, local_size)
|
||||
max_size = max(size.item() for size in all_sizes)
|
||||
|
||||
padded_results = torch.zeros(
|
||||
max_size, device=results.device, dtype=results.dtype
|
||||
)
|
||||
padded_results[: results.size(0)] = results
|
||||
|
||||
gathered_dim_metadata = [None] * world_size
|
||||
gathered_results = (
|
||||
torch.zeros_like(padded_results)
|
||||
.repeat(world_size, *[1] * len(padded_results.shape))
|
||||
.contiguous()
|
||||
)
|
||||
dist.all_gather_into_tensor(gathered_results, padded_results)
|
||||
dist.all_gather_object(gathered_dim_metadata, local_dim_metadata)
|
||||
gathered_dim_metadata = cast(list[list[torch.Size]], gathered_dim_metadata)
|
||||
|
||||
data: list = [
|
||||
[[[] for _ in range(num_w_tiles)] for _ in range(num_h_tiles)]
|
||||
for _ in range(num_t_tiles)
|
||||
]
|
||||
global_idx = 0
|
||||
for i, per_rank_metadata in enumerate(gathered_dim_metadata):
|
||||
start_shape = 0
|
||||
for shape in per_rank_metadata:
|
||||
mul_shape = prod(shape)
|
||||
current_data = gathered_results[
|
||||
i, start_shape : start_shape + mul_shape
|
||||
].reshape(shape)
|
||||
t_idx = global_idx // total_spatial_tiles
|
||||
spatial_idx = global_idx % total_spatial_tiles
|
||||
h_idx = spatial_idx // num_w_tiles
|
||||
w_idx = spatial_idx % num_w_tiles
|
||||
data[t_idx][h_idx][w_idx] = current_data
|
||||
start_shape += mul_shape
|
||||
global_idx += 1
|
||||
|
||||
result_slices = []
|
||||
last_slice_data = None
|
||||
for i, tem_data in enumerate(data):
|
||||
slice_data = self._merge_spatial_tiles(
|
||||
tem_data,
|
||||
blend_height,
|
||||
blend_width,
|
||||
self.tile_sample_stride_height,
|
||||
self.tile_sample_stride_width,
|
||||
)
|
||||
if i > 0:
|
||||
slice_data = self.blend_t(
|
||||
last_slice_data, slice_data, self.blend_num_frames
|
||||
)
|
||||
result_slices.append(
|
||||
slice_data[:, :, : self.tile_sample_stride_num_frames, :, :]
|
||||
)
|
||||
else:
|
||||
result_slices.append(
|
||||
slice_data[:, :, : self.tile_sample_stride_num_frames + 1, :, :]
|
||||
)
|
||||
last_slice_data = slice_data
|
||||
return torch.cat(result_slices, dim=2)
|
||||
|
||||
def parallel_patch_decode(self, z: torch.FloatTensor) -> torch.FloatTensor:
|
||||
world_size, rank = get_sp_world_size(), get_sp_parallel_rank()
|
||||
if world_size <= 1:
|
||||
return self._decode(z)
|
||||
|
||||
tile_latent_min_height = (
|
||||
self.tile_sample_min_height // self.spatial_compression_ratio
|
||||
)
|
||||
tile_latent_min_width = (
|
||||
self.tile_sample_min_width // self.spatial_compression_ratio
|
||||
)
|
||||
tile_latent_stride_height = (
|
||||
self.tile_sample_stride_height // self.spatial_compression_ratio
|
||||
)
|
||||
tile_latent_stride_width = (
|
||||
self.tile_sample_stride_width // self.spatial_compression_ratio
|
||||
)
|
||||
overlap_h = max(0, tile_latent_min_height - tile_latent_stride_height)
|
||||
overlap_w = max(0, tile_latent_min_width - tile_latent_stride_width)
|
||||
halo_h = overlap_h // 2
|
||||
halo_w = overlap_w // 2
|
||||
|
||||
_, _, _, latent_h, latent_w = z.shape
|
||||
scale = self.spatial_compression_ratio
|
||||
out_h = latent_h * scale
|
||||
out_w = latent_w * scale
|
||||
root = isqrt(world_size)
|
||||
grid_rows, grid_cols = 1, world_size
|
||||
for rows in range(root, 0, -1):
|
||||
if world_size % rows == 0:
|
||||
grid_rows, grid_cols = rows, world_size // rows
|
||||
break
|
||||
patch_id = rank
|
||||
patch_row = patch_id // grid_cols
|
||||
patch_col = patch_id % grid_cols
|
||||
|
||||
h0 = (patch_row * latent_h) // grid_rows
|
||||
h1 = ((patch_row + 1) * latent_h) // grid_rows
|
||||
w0 = (patch_col * latent_w) // grid_cols
|
||||
w1 = ((patch_col + 1) * latent_w) // grid_cols
|
||||
|
||||
ext_h0 = max(0, h0 - halo_h)
|
||||
ext_h1 = min(latent_h, h1 + halo_h)
|
||||
ext_w0 = max(0, w0 - halo_w)
|
||||
ext_w1 = min(latent_w, w1 + halo_w)
|
||||
|
||||
local_patch = z[:, :, :, ext_h0:ext_h1, ext_w0:ext_w1]
|
||||
decoded_patch = self._decode(local_patch)
|
||||
|
||||
crop_top = (h0 - ext_h0) * scale
|
||||
crop_bottom = crop_top + (h1 - h0) * scale
|
||||
crop_left = (w0 - ext_w0) * scale
|
||||
crop_right = crop_left + (w1 - w0) * scale
|
||||
decoded_core = decoded_patch[
|
||||
:, :, :, crop_top:crop_bottom, crop_left:crop_right
|
||||
].contiguous()
|
||||
|
||||
local_result = decoded_core.reshape(-1)
|
||||
local_dim_metadata = torch.tensor(
|
||||
decoded_core.shape, device=z.device, dtype=torch.int64
|
||||
)
|
||||
local_position = torch.tensor(
|
||||
[h0 * scale, h1 * scale, w0 * scale, w1 * scale],
|
||||
device=z.device,
|
||||
dtype=torch.int64,
|
||||
)
|
||||
gathered_positions = [
|
||||
torch.empty_like(local_position) for _ in range(world_size)
|
||||
]
|
||||
dist.all_gather(gathered_positions, local_position)
|
||||
|
||||
local_size = torch.tensor(
|
||||
[local_result.size(0)], device=z.device, dtype=torch.int64
|
||||
)
|
||||
gathered_dim_metadata = [
|
||||
torch.empty_like(local_dim_metadata) for _ in range(world_size)
|
||||
]
|
||||
dist.all_gather(gathered_dim_metadata, local_dim_metadata)
|
||||
|
||||
all_sizes = [
|
||||
torch.zeros(1, device=z.device, dtype=torch.int64)
|
||||
for _ in range(world_size)
|
||||
]
|
||||
dist.all_gather(all_sizes, local_size)
|
||||
max_size = max(size.item() for size in all_sizes)
|
||||
|
||||
padded_results = torch.zeros(max_size, device=z.device, dtype=z.dtype)
|
||||
padded_results[: local_result.size(0)] = local_result
|
||||
gathered_results = torch.empty(
|
||||
(world_size, *padded_results.shape),
|
||||
device=padded_results.device,
|
||||
dtype=padded_results.dtype,
|
||||
)
|
||||
dist.all_gather_into_tensor(gathered_results, padded_results)
|
||||
|
||||
dec = z.new_empty(
|
||||
(
|
||||
decoded_core.shape[0],
|
||||
decoded_core.shape[1],
|
||||
decoded_core.shape[2],
|
||||
out_h,
|
||||
out_w,
|
||||
)
|
||||
)
|
||||
for src_rank, positions in enumerate(gathered_positions):
|
||||
h_start, h_end, w_start, w_end = [int(x.item()) for x in positions]
|
||||
shape = tuple(int(x.item()) for x in gathered_dim_metadata[src_rank])
|
||||
patch = gathered_results[src_rank][: prod(shape)].reshape(shape)
|
||||
dec[:, :, :, h_start:h_end, w_start:w_end] = patch
|
||||
return dec
|
||||
|
||||
def _merge_spatial_tiles(
|
||||
self, tiles, blend_height, blend_width, stride_height, stride_width
|
||||
) -> torch.Tensor:
|
||||
"""Helper function to merge spatial tiles with blending"""
|
||||
result_rows = []
|
||||
for i, row in enumerate(tiles):
|
||||
result_row = []
|
||||
for j, tile in enumerate(row):
|
||||
if i > 0:
|
||||
tile = self.blend_v(tiles[i - 1][j], tile, blend_height)
|
||||
if j > 0:
|
||||
tile = self.blend_h(row[j - 1], tile, blend_width)
|
||||
result_row.append(tile[:, :, :, :stride_height, :stride_width])
|
||||
result_rows.append(torch.cat(result_row, dim=-1))
|
||||
return torch.cat(result_rows, dim=-2)
|
||||
|
||||
def spatial_tiled_decode(self, z: torch.Tensor) -> torch.Tensor:
|
||||
r"""
|
||||
Decode a batch of images using a tiled decoder.
|
||||
|
||||
Args:
|
||||
z (`torch.Tensor`): Input batch of latent vectors.
|
||||
|
||||
Returns:
|
||||
`torch.Tensor`:
|
||||
The decoded images.
|
||||
"""
|
||||
|
||||
_, _, _, height, width = z.shape
|
||||
# sample_height = height * self.spatial_compression_ratio
|
||||
# sample_width = width * self.spatial_compression_ratio
|
||||
|
||||
tile_latent_min_height = (
|
||||
self.tile_sample_min_height // self.spatial_compression_ratio
|
||||
)
|
||||
tile_latent_min_width = (
|
||||
self.tile_sample_min_width // self.spatial_compression_ratio
|
||||
)
|
||||
tile_latent_stride_height = (
|
||||
self.tile_sample_stride_height // self.spatial_compression_ratio
|
||||
)
|
||||
tile_latent_stride_width = (
|
||||
self.tile_sample_stride_width // self.spatial_compression_ratio
|
||||
)
|
||||
|
||||
blend_height = self.tile_sample_min_height - self.tile_sample_stride_height
|
||||
blend_width = self.tile_sample_min_width - self.tile_sample_stride_width
|
||||
|
||||
# Split z into overlapping tiles and decode them separately.
|
||||
# The tiles have an overlap to avoid seams between tiles.
|
||||
rows = []
|
||||
for i in range(0, height, tile_latent_stride_height):
|
||||
row = []
|
||||
for j in range(0, width, tile_latent_stride_width):
|
||||
tile = z[
|
||||
:,
|
||||
:,
|
||||
:,
|
||||
i : i + tile_latent_min_height,
|
||||
j : j + tile_latent_min_width,
|
||||
]
|
||||
decoded = self._decode(tile)
|
||||
row.append(decoded)
|
||||
rows.append(row)
|
||||
return self._merge_spatial_tiles(
|
||||
rows,
|
||||
blend_height,
|
||||
blend_width,
|
||||
self.tile_sample_stride_height,
|
||||
self.tile_sample_stride_width,
|
||||
)
|
||||
|
||||
def tiled_encode(self, x: torch.Tensor) -> torch.Tensor:
|
||||
_, _, num_frames, height, width = x.shape
|
||||
|
||||
# tile_latent_min_num_frames = self.tile_sample_min_num_frames // self.temporal_compression_ratio
|
||||
tile_latent_stride_num_frames = (
|
||||
self.tile_sample_stride_num_frames // self.temporal_compression_ratio
|
||||
)
|
||||
|
||||
row = []
|
||||
for i in range(0, num_frames, self.tile_sample_stride_num_frames):
|
||||
tile = x[:, :, i : i + self.tile_sample_min_num_frames + 1, :, :]
|
||||
if self.use_tiling and (
|
||||
height > self.tile_sample_min_height
|
||||
or width > self.tile_sample_min_width
|
||||
):
|
||||
tile = self.spatial_tiled_encode(tile)
|
||||
else:
|
||||
tile = self._encode(tile)
|
||||
if i > 0:
|
||||
tile = tile[:, :, 1:, :, :]
|
||||
row.append(tile)
|
||||
result_row = []
|
||||
for i, tile in enumerate(row):
|
||||
if i > 0:
|
||||
tile = self.blend_t(row[i - 1], tile, self.blend_num_frames)
|
||||
result_row.append(tile[:, :, :tile_latent_stride_num_frames, :, :])
|
||||
else:
|
||||
result_row.append(tile[:, :, : tile_latent_stride_num_frames + 1, :, :])
|
||||
enc = torch.cat(result_row, dim=2)
|
||||
return enc
|
||||
|
||||
def tiled_decode(self, z: torch.Tensor) -> torch.Tensor:
|
||||
batch_size, num_channels, num_frames, height, width = z.shape
|
||||
|
||||
tile_latent_min_height = (
|
||||
self.tile_sample_min_height // self.spatial_compression_ratio
|
||||
)
|
||||
tile_latent_min_width = (
|
||||
self.tile_sample_min_width // self.spatial_compression_ratio
|
||||
)
|
||||
tile_latent_min_num_frames = (
|
||||
self.tile_sample_min_num_frames // self.temporal_compression_ratio
|
||||
)
|
||||
tile_latent_stride_num_frames = (
|
||||
self.tile_sample_stride_num_frames // self.temporal_compression_ratio
|
||||
)
|
||||
|
||||
row = []
|
||||
for i in range(0, num_frames, tile_latent_stride_num_frames):
|
||||
tile = z[:, :, i : i + tile_latent_min_num_frames + 1, :, :]
|
||||
if self.use_tiling and (
|
||||
tile.shape[-1] > tile_latent_min_width
|
||||
or tile.shape[-2] > tile_latent_min_height
|
||||
):
|
||||
decoded = self.spatial_tiled_decode(tile)
|
||||
else:
|
||||
decoded = self._decode(tile)
|
||||
if i > 0:
|
||||
decoded = decoded[:, :, 1:, :, :]
|
||||
row.append(decoded)
|
||||
result_row = []
|
||||
for i, tile in enumerate(row):
|
||||
if i > 0:
|
||||
tile = self.blend_t(row[i - 1], tile, self.blend_num_frames)
|
||||
result_row.append(
|
||||
tile[:, :, : self.tile_sample_stride_num_frames, :, :]
|
||||
)
|
||||
else:
|
||||
result_row.append(
|
||||
tile[:, :, : self.tile_sample_stride_num_frames + 1, :, :]
|
||||
)
|
||||
|
||||
dec = torch.cat(result_row, dim=2)
|
||||
return dec
|
||||
|
||||
def enable_tiling(
|
||||
self,
|
||||
tile_sample_min_height: int | None = None,
|
||||
tile_sample_min_width: int | None = None,
|
||||
tile_sample_min_num_frames: int | None = None,
|
||||
tile_sample_stride_height: int | None = None,
|
||||
tile_sample_stride_width: int | None = None,
|
||||
tile_sample_stride_num_frames: int | None = None,
|
||||
blend_num_frames: int | None = None,
|
||||
use_tiling: bool | None = None,
|
||||
use_temporal_tiling: bool | None = None,
|
||||
use_parallel_tiling: bool | None = None,
|
||||
) -> None:
|
||||
r"""
|
||||
Enable tiled VAE decoding. When this option is enabled, the VAE will split the input tensor into tiles to
|
||||
compute decoding and encoding in several steps. This is useful for saving a large amount of memory and to allow
|
||||
processing larger images.
|
||||
|
||||
Args:
|
||||
tile_sample_min_height (`int`, *optional*):
|
||||
The minimum height required for a sample to be separated into tiles across the height dimension.
|
||||
tile_sample_min_width (`int`, *optional*):
|
||||
The minimum width required for a sample to be separated into tiles across the width dimension.
|
||||
tile_sample_min_num_frames (`int`, *optional*):
|
||||
The minimum number of frames required for a sample to be separated into tiles across the frame
|
||||
dimension.
|
||||
tile_sample_stride_height (`int`, *optional*):
|
||||
The minimum amount of overlap between two consecutive vertical tiles. This is to ensure that there are
|
||||
no tiling artifacts produced across the height dimension.
|
||||
tile_sample_stride_width (`int`, *optional*):
|
||||
The stride between two consecutive horizontal tiles. This is to ensure that there are no tiling
|
||||
artifacts produced across the width dimension.
|
||||
tile_sample_stride_num_frames (`int`, *optional*):
|
||||
The stride between two consecutive frame tiles. This is to ensure that there are no tiling artifacts
|
||||
produced across the frame dimension.
|
||||
"""
|
||||
self.use_tiling = True
|
||||
self.tile_sample_min_height = (
|
||||
tile_sample_min_height or self.tile_sample_min_height
|
||||
)
|
||||
self.tile_sample_min_width = tile_sample_min_width or self.tile_sample_min_width
|
||||
self.tile_sample_min_num_frames = (
|
||||
tile_sample_min_num_frames or self.tile_sample_min_num_frames
|
||||
)
|
||||
self.tile_sample_stride_height = (
|
||||
tile_sample_stride_height or self.tile_sample_stride_height
|
||||
)
|
||||
self.tile_sample_stride_width = (
|
||||
tile_sample_stride_width or self.tile_sample_stride_width
|
||||
)
|
||||
self.tile_sample_stride_num_frames = (
|
||||
tile_sample_stride_num_frames or self.tile_sample_stride_num_frames
|
||||
)
|
||||
if blend_num_frames is not None:
|
||||
self.blend_num_frames = blend_num_frames
|
||||
else:
|
||||
self.blend_num_frames = (
|
||||
self.tile_sample_min_num_frames - self.tile_sample_stride_num_frames
|
||||
)
|
||||
self.use_tiling = use_tiling or self.use_tiling
|
||||
self.use_temporal_tiling = use_temporal_tiling or self.use_temporal_tiling
|
||||
self.use_parallel_tiling = use_parallel_tiling or self.use_parallel_tiling
|
||||
|
||||
def disable_tiling(self) -> None:
|
||||
r"""
|
||||
Disable tiled VAE decoding. If `enable_tiling` was previously enabled, this method will go back to computing
|
||||
decoding in one step.
|
||||
"""
|
||||
self.use_tiling = False
|
||||
|
||||
|
||||
# adapted from https://github.com/huggingface/diffusers/blob/e7ffeae0a191f710881d1fbde00cd6ff025e81f2/src/diffusers/models/autoencoders/vae.py#L691
|
||||
class DiagonalGaussianDistribution:
|
||||
|
||||
def __init__(self, parameters: torch.Tensor, deterministic: bool = False):
|
||||
self.parameters = parameters
|
||||
self.mean, self.logvar = torch.chunk(parameters, 2, dim=1)
|
||||
self.logvar = torch.clamp(self.logvar, -30.0, 20.0)
|
||||
self.deterministic = deterministic
|
||||
self.std = torch.exp(0.5 * self.logvar)
|
||||
self.var = torch.exp(self.logvar)
|
||||
if self.deterministic:
|
||||
self.var = self.std = torch.zeros_like(
|
||||
self.mean, device=self.parameters.device, dtype=self.parameters.dtype
|
||||
)
|
||||
|
||||
def sample(self, generator: torch.Generator | None = None) -> torch.Tensor:
|
||||
# make sure sample is on the same device as the parameters and has same dtype
|
||||
sample = randn_tensor(
|
||||
self.mean.shape,
|
||||
generator=generator,
|
||||
device=self.parameters.device,
|
||||
dtype=self.parameters.dtype,
|
||||
)
|
||||
x = self.mean + self.std * sample
|
||||
return x
|
||||
|
||||
def kl(
|
||||
self,
|
||||
other: Optional["DiagonalGaussianDistribution"] = None,
|
||||
dims: tuple[int, ...] = (1, 2, 3),
|
||||
) -> torch.Tensor:
|
||||
if self.deterministic:
|
||||
return torch.Tensor([0.0])
|
||||
else:
|
||||
if other is None:
|
||||
return 0.5 * torch.sum(
|
||||
torch.pow(self.mean, 2) + self.var - 1.0 - self.logvar,
|
||||
dim=dims,
|
||||
)
|
||||
else:
|
||||
return 0.5 * torch.sum(
|
||||
torch.pow(self.mean - other.mean, 2) / other.var
|
||||
+ self.var / other.var
|
||||
- 1.0
|
||||
- self.logvar
|
||||
+ other.logvar,
|
||||
dim=dims,
|
||||
)
|
||||
|
||||
def nll(
|
||||
self, sample: torch.Tensor, dims: tuple[int, ...] = (1, 2, 3)
|
||||
) -> torch.Tensor:
|
||||
if self.deterministic:
|
||||
return torch.Tensor([0.0])
|
||||
logtwopi = np.log(2.0 * np.pi)
|
||||
return 0.5 * torch.sum(
|
||||
logtwopi + self.logvar + torch.pow(sample - self.mean, 2) / self.var,
|
||||
dim=dims,
|
||||
)
|
||||
|
||||
def mode(self) -> torch.Tensor:
|
||||
return self.mean
|
||||
Reference in New Issue
Block a user