94057c3d3e
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
647 lines
23 KiB
Python
647 lines
23 KiB
Python
# Copied and adapted from: https://github.com/descriptinc/descript-audio-codec
|
|
|
|
# SPDX-License-Identifier: MIT
|
|
|
|
import math
|
|
from bisect import bisect_right
|
|
from typing import Union
|
|
|
|
import torch
|
|
import torch.nn.functional as F
|
|
from einops import rearrange
|
|
from torch import nn
|
|
|
|
from sglang.multimodal_gen.configs.models.vaes.dac import DacVAEConfig
|
|
from sglang.multimodal_gen.runtime.managers.memory_managers.layerwise_offload import (
|
|
LayerwiseOffloadableModuleMixin,
|
|
)
|
|
from sglang.multimodal_gen.runtime.models.vaes.common import (
|
|
DiagonalGaussianDistribution,
|
|
)
|
|
|
|
|
|
def _snake(x, alpha):
|
|
shape = x.shape
|
|
x = x.reshape(shape[0], shape[1], -1)
|
|
x = x + (alpha + 1e-9).reciprocal() * torch.sin(alpha * x).pow(2)
|
|
x = x.reshape(shape)
|
|
return x
|
|
|
|
|
|
# Scripting this brings model speed up 1.4x
|
|
snake = torch.jit.script(_snake)
|
|
|
|
|
|
# ROCm HIPRTC can fail to compile the scripted bf16 Snake kernel.
|
|
def _should_use_eager_snake_on_rocm_bf16(x: torch.Tensor, alpha: torch.Tensor) -> bool:
|
|
return (
|
|
torch.version.hip is not None
|
|
and (x.is_cuda or alpha.is_cuda)
|
|
and (x.dtype == torch.bfloat16 or alpha.dtype == torch.bfloat16)
|
|
)
|
|
|
|
|
|
class Snake1d(nn.Module):
|
|
def __init__(self, channels):
|
|
super().__init__()
|
|
self.alpha = nn.Parameter(torch.ones(1, channels, 1))
|
|
|
|
def forward(self, x):
|
|
if _should_use_eager_snake_on_rocm_bf16(x, self.alpha):
|
|
return _snake(x, self.alpha)
|
|
return snake(x, self.alpha)
|
|
|
|
|
|
class VectorQuantize(nn.Module):
|
|
"""
|
|
Implementation of VQ similar to Karpathy's repo:
|
|
https://github.com/karpathy/deep-vector-quantization
|
|
Additionally uses following tricks from Improved VQGAN
|
|
(https://arxiv.org/pdf/2110.04627.pdf):
|
|
1. Factorized codes: Perform nearest neighbor lookup in low-dimensional space
|
|
for improved codebook usage
|
|
2. l2-normalized codes: Converts euclidean distance to cosine similarity which
|
|
improves training stability
|
|
"""
|
|
|
|
def __init__(self, input_dim: int, codebook_size: int, codebook_dim: int):
|
|
super().__init__()
|
|
self.codebook_size = codebook_size
|
|
self.codebook_dim = codebook_dim
|
|
|
|
self.in_proj = nn.Conv1d(input_dim, codebook_dim, kernel_size=1)
|
|
self.out_proj = nn.Conv1d(codebook_dim, input_dim, kernel_size=1)
|
|
self.codebook = nn.Embedding(codebook_size, codebook_dim)
|
|
|
|
def forward(self, z):
|
|
"""Quantize the input tensor using a fixed codebook and return the corresponding codebook vectors.
|
|
|
|
Args:
|
|
z (torch.Tensor): Input tensor with shape ``[B, D, T]``.
|
|
|
|
Returns:
|
|
tuple: A tuple containing:
|
|
- z_q (torch.Tensor): Quantized continuous representation with shape ``[B, D, T]``.
|
|
- commitment_loss (torch.Tensor): Commitment loss scalar to train encoder to predict
|
|
vectors closer to codebook entries.
|
|
- codebook_loss (torch.Tensor): Codebook loss scalar to update the codebook.
|
|
- indices (torch.Tensor): Codebook indices (quantized discrete representation) with shape ``[B, T]``.
|
|
- z_e (torch.Tensor): Projected latents (continuous representation before quantization) with shape ``[B, D, T]``.
|
|
"""
|
|
|
|
# Factorized codes (ViT-VQGAN) Project input into low-dimensional space
|
|
z_e = self.in_proj(z) # z_e : (B x D x T)
|
|
z_q, indices = self.decode_latents(z_e)
|
|
|
|
commitment_loss = F.mse_loss(z_e, z_q.detach(), reduction="none").mean([1, 2])
|
|
codebook_loss = F.mse_loss(z_q, z_e.detach(), reduction="none").mean([1, 2])
|
|
|
|
z_q = (
|
|
z_e + (z_q - z_e).detach()
|
|
) # noop in forward pass, straight-through gradient estimator in backward pass
|
|
|
|
z_q = self.out_proj(z_q)
|
|
|
|
return z_q, commitment_loss, codebook_loss, indices, z_e
|
|
|
|
def embed_code(self, embed_id):
|
|
return F.embedding(embed_id, self.codebook.weight)
|
|
|
|
def decode_code(self, embed_id):
|
|
return self.embed_code(embed_id).transpose(1, 2)
|
|
|
|
def decode_latents(self, latents):
|
|
encodings = rearrange(latents, "b d t -> (b t) d")
|
|
codebook = self.codebook.weight # codebook: (N x D)
|
|
|
|
# L2 normalize encodings and codebook (ViT-VQGAN)
|
|
encodings = F.normalize(encodings)
|
|
codebook = F.normalize(codebook)
|
|
|
|
# Compute euclidean distance with codebook
|
|
dist = (
|
|
encodings.pow(2).sum(1, keepdim=True)
|
|
- 2 * encodings @ codebook.t()
|
|
+ codebook.pow(2).sum(1, keepdim=True).t()
|
|
)
|
|
indices = rearrange((-dist).max(1)[1], "(b t) -> b t", b=latents.size(0))
|
|
z_q = self.decode_code(indices)
|
|
return z_q, indices
|
|
|
|
|
|
class ResidualVectorQuantize(nn.Module):
|
|
"""
|
|
Introduced in SoundStream: An end2end neural audio codec
|
|
https://arxiv.org/abs/2107.03312
|
|
"""
|
|
|
|
def __init__(
|
|
self,
|
|
input_dim: int = 512,
|
|
n_codebooks: int = 9,
|
|
codebook_size: int = 1024,
|
|
codebook_dim: Union[int, list] = 8,
|
|
quantizer_dropout: float = 0.0,
|
|
):
|
|
super().__init__()
|
|
if isinstance(codebook_dim, int):
|
|
codebook_dim = [codebook_dim for _ in range(n_codebooks)]
|
|
|
|
self.n_codebooks = n_codebooks
|
|
self.codebook_dim = codebook_dim
|
|
self.codebook_size = codebook_size
|
|
dim_offsets = [0]
|
|
for dim in self.codebook_dim:
|
|
dim_offsets.append(dim_offsets[-1] + dim)
|
|
self._codebook_dim_offsets = tuple(dim_offsets)
|
|
|
|
self.quantizers = nn.ModuleList(
|
|
[
|
|
VectorQuantize(input_dim, codebook_size, codebook_dim[i])
|
|
for i in range(n_codebooks)
|
|
]
|
|
)
|
|
self.quantizer_dropout = quantizer_dropout
|
|
|
|
def forward(self, z, n_quantizers: int = None):
|
|
"""Quantize the input tensor using a fixed set of codebooks and return the corresponding codebook vectors.
|
|
|
|
Args:
|
|
z (torch.Tensor): Input tensor with shape ``[B, D, T]``.
|
|
n_quantizers (int, optional): Number of quantizers to use. If ``None``,
|
|
all quantizers are used. When ``n_quantizers`` < ``self.n_codebooks``,
|
|
quantizer dropout is applied. Note: if ``self.quantizer_dropout`` > 0
|
|
and in training mode, this argument is ignored and a random number of
|
|
quantizers is used.
|
|
|
|
Returns:
|
|
tuple: A tuple containing:
|
|
- z_q (torch.Tensor): Quantized continuous representation with shape ``[B, D, T]``.
|
|
- codes (torch.Tensor): Codebook indices for each codebook with shape ``[B, N, T]``
|
|
(quantized discrete representation of input).
|
|
- latents (torch.Tensor): Projected latents with shape ``[B, N*D, T]``
|
|
(continuous representation before quantization).
|
|
- commitment_loss (torch.Tensor): Commitment loss scalar to train encoder to predict
|
|
vectors closer to codebook entries.
|
|
- codebook_loss (torch.Tensor): Codebook loss scalar to update the codebook.
|
|
"""
|
|
z_q = 0
|
|
residual = z
|
|
commitment_loss = 0
|
|
codebook_loss = 0
|
|
|
|
codebook_indices = []
|
|
latents = []
|
|
|
|
if n_quantizers is None:
|
|
n_quantizers = self.n_codebooks
|
|
quantizers = self.quantizers
|
|
if self.training:
|
|
batch_size = z.shape[0]
|
|
device = z.device
|
|
n_quantizers = torch.full(
|
|
(batch_size,),
|
|
self.n_codebooks + 1,
|
|
device=device,
|
|
dtype=torch.long,
|
|
)
|
|
if self.quantizer_dropout > 0:
|
|
dropout = torch.randint(
|
|
1,
|
|
self.n_codebooks + 1,
|
|
(batch_size,),
|
|
device=device,
|
|
)
|
|
n_dropout = int(batch_size * self.quantizer_dropout)
|
|
if n_dropout > 0:
|
|
n_quantizers[:n_dropout] = dropout[:n_dropout]
|
|
|
|
for i, quantizer in enumerate(quantizers):
|
|
z_q_i, commitment_loss_i, codebook_loss_i, indices_i, z_e_i = quantizer(
|
|
residual
|
|
)
|
|
|
|
# Create mask to apply quantizer dropout
|
|
mask = i < n_quantizers
|
|
z_q = z_q + z_q_i * mask[:, None, None]
|
|
residual = residual - z_q_i
|
|
|
|
# Sum losses
|
|
commitment_loss += (commitment_loss_i * mask).mean()
|
|
codebook_loss += (codebook_loss_i * mask).mean()
|
|
|
|
codebook_indices.append(indices_i)
|
|
latents.append(z_e_i)
|
|
else:
|
|
for i, quantizer in enumerate(quantizers):
|
|
if i >= n_quantizers:
|
|
break
|
|
z_q_i, commitment_loss_i, codebook_loss_i, indices_i, z_e_i = quantizer(
|
|
residual
|
|
)
|
|
z_q = z_q + z_q_i
|
|
residual = residual - z_q_i
|
|
|
|
commitment_loss += commitment_loss_i.mean()
|
|
codebook_loss += codebook_loss_i.mean()
|
|
|
|
codebook_indices.append(indices_i)
|
|
latents.append(z_e_i)
|
|
|
|
codes = torch.stack(codebook_indices, dim=1)
|
|
latents = torch.cat(latents, dim=1)
|
|
|
|
return z_q, codes, latents, commitment_loss, codebook_loss
|
|
|
|
def from_codes(self, codes: torch.Tensor):
|
|
"""Reconstruct the continuous representation from quantized codes.
|
|
|
|
Args:
|
|
codes (torch.Tensor): Quantized discrete representation with shape ``[B, N, T]``.
|
|
|
|
Returns:
|
|
tuple: A tuple containing:
|
|
- z_q (torch.Tensor): Quantized continuous representation with shape ``[B, D, T]``.
|
|
- z_p (torch.Tensor): Concatenated latent space representation with shape ``[B, N*D, T]``.
|
|
- codes (torch.Tensor): Original input codebook indices with shape ``[B, N, T]``.
|
|
"""
|
|
z_q = 0.0
|
|
z_p = []
|
|
n_codebooks = codes.shape[1]
|
|
for i in range(n_codebooks):
|
|
z_p_i = self.quantizers[i].decode_code(codes[:, i, :])
|
|
z_p.append(z_p_i)
|
|
|
|
z_q_i = self.quantizers[i].out_proj(z_p_i)
|
|
z_q = z_q + z_q_i
|
|
return z_q, torch.cat(z_p, dim=1), codes
|
|
|
|
def from_latents(self, latents: torch.Tensor):
|
|
"""Reconstruct the continuous representation from unquantized latents.
|
|
|
|
Args:
|
|
latents (torch.Tensor): Continuous representation after projection with shape ``[B, N*D, T]``.
|
|
|
|
Returns:
|
|
tuple: A tuple containing:
|
|
- z_q (torch.Tensor): Quantized representation of full-projected space with shape ``[B, D, T]``.
|
|
- z_p (torch.Tensor): Quantized representation of latent space with shape ``[B, N*D, T]``.
|
|
- codes (torch.Tensor): Codebook indices with shape ``[B, N, T]``.
|
|
"""
|
|
z_q = 0
|
|
z_p = []
|
|
codes = []
|
|
dims = self._codebook_dim_offsets
|
|
n_codebooks = bisect_right(dims, latents.shape[1]) - 1
|
|
for i in range(n_codebooks):
|
|
j, k = dims[i], dims[i + 1]
|
|
z_p_i, codes_i = self.quantizers[i].decode_latents(latents[:, j:k, :])
|
|
z_p.append(z_p_i)
|
|
codes.append(codes_i)
|
|
|
|
z_q_i = self.quantizers[i].out_proj(z_p_i)
|
|
z_q = z_q + z_q_i
|
|
|
|
return z_q, torch.cat(z_p, dim=1), torch.stack(codes, dim=1)
|
|
|
|
|
|
class ResidualUnit(nn.Module):
|
|
def __init__(self, dim: int = 16, dilation: int = 1):
|
|
super().__init__()
|
|
pad = ((7 - 1) * dilation) // 2
|
|
self.block = nn.Sequential(
|
|
Snake1d(dim),
|
|
nn.Conv1d(dim, dim, kernel_size=7, dilation=dilation, padding=pad),
|
|
Snake1d(dim),
|
|
nn.Conv1d(dim, dim, kernel_size=1),
|
|
)
|
|
|
|
def forward(self, x):
|
|
y = self.block(x)
|
|
pad = (x.shape[-1] - y.shape[-1]) // 2
|
|
if pad > 0:
|
|
x = x[..., pad:-pad]
|
|
return x + y
|
|
|
|
|
|
class EncoderBlock(nn.Module):
|
|
def __init__(self, dim: int = 16, stride: int = 1):
|
|
super().__init__()
|
|
self.block = nn.Sequential(
|
|
ResidualUnit(dim // 2, dilation=1),
|
|
ResidualUnit(dim // 2, dilation=3),
|
|
ResidualUnit(dim // 2, dilation=9),
|
|
Snake1d(dim // 2),
|
|
nn.Conv1d(
|
|
dim // 2,
|
|
dim,
|
|
kernel_size=2 * stride,
|
|
stride=stride,
|
|
padding=math.ceil(stride / 2),
|
|
),
|
|
)
|
|
|
|
def forward(self, x):
|
|
return self.block(x)
|
|
|
|
|
|
class Encoder(nn.Module):
|
|
def __init__(
|
|
self,
|
|
d_model: int = 64,
|
|
strides: list = [2, 4, 8, 8],
|
|
d_latent: int = 64,
|
|
):
|
|
super().__init__()
|
|
# Create first convolution
|
|
self.block = [nn.Conv1d(1, d_model, kernel_size=7, padding=3)]
|
|
|
|
# Create EncoderBlocks that double channels as they downsample by `stride`
|
|
for stride in strides:
|
|
d_model *= 2
|
|
self.block += [EncoderBlock(d_model, stride=stride)]
|
|
|
|
# Create last convolution
|
|
self.block += [
|
|
Snake1d(d_model),
|
|
nn.Conv1d(d_model, d_latent, kernel_size=3, padding=1),
|
|
]
|
|
|
|
# Wrap black into nn.Sequential
|
|
self.block = nn.Sequential(*self.block)
|
|
self.enc_dim = d_model
|
|
|
|
def forward(self, x):
|
|
return self.block(x)
|
|
|
|
|
|
class DecoderBlock(nn.Module):
|
|
def __init__(self, input_dim: int = 16, output_dim: int = 8, stride: int = 1):
|
|
super().__init__()
|
|
self.block = nn.Sequential(
|
|
Snake1d(input_dim),
|
|
nn.ConvTranspose1d(
|
|
input_dim,
|
|
output_dim,
|
|
kernel_size=2 * stride,
|
|
stride=stride,
|
|
padding=math.ceil(stride / 2),
|
|
output_padding=stride % 2,
|
|
),
|
|
ResidualUnit(output_dim, dilation=1),
|
|
ResidualUnit(output_dim, dilation=3),
|
|
ResidualUnit(output_dim, dilation=9),
|
|
)
|
|
|
|
def forward(self, x):
|
|
return self.block(x)
|
|
|
|
|
|
class Decoder(nn.Module):
|
|
def __init__(
|
|
self,
|
|
input_channel,
|
|
channels,
|
|
rates,
|
|
d_out: int = 1,
|
|
):
|
|
super().__init__()
|
|
|
|
# Add first conv layer
|
|
layers = [nn.Conv1d(input_channel, channels, kernel_size=7, padding=3)]
|
|
|
|
# Add upsampling + MRF blocks
|
|
for i, stride in enumerate(rates):
|
|
input_dim = channels // 2**i
|
|
output_dim = channels // 2 ** (i + 1)
|
|
layers += [DecoderBlock(input_dim, output_dim, stride)]
|
|
|
|
# Add final conv layer
|
|
layers += [
|
|
Snake1d(output_dim),
|
|
nn.Conv1d(output_dim, d_out, kernel_size=7, padding=3),
|
|
nn.Tanh(),
|
|
]
|
|
|
|
self.model = nn.Sequential(*layers)
|
|
|
|
def forward(self, x):
|
|
return self.model(x)
|
|
|
|
|
|
class DAC(nn.Module, LayerwiseOffloadableModuleMixin):
|
|
layerwise_offload_dit_group_enabled = False
|
|
layer_names = ["encoder.block", "decoder.model"]
|
|
|
|
def __init__(
|
|
self,
|
|
config: DacVAEConfig,
|
|
):
|
|
super().__init__()
|
|
|
|
self.continuous = config.continuous
|
|
self.decoder_dim = config.decoder_dim
|
|
self.decoder_rates = config.decoder_rates
|
|
self.encoder_dim = config.encoder_dim
|
|
self.encoder_rates = config.encoder_rates
|
|
self.hop_length = math.prod(config.encoder_rates)
|
|
self.sample_rate = config.sample_rate
|
|
|
|
if config.latent_dim is None:
|
|
latent_dim = config.encoder_dim * (2 ** len(config.encoder_rates))
|
|
else:
|
|
latent_dim = config.latent_dim
|
|
|
|
self.latent_dim = latent_dim
|
|
|
|
if config.load_encoder:
|
|
self.encoder = Encoder(config.encoder_dim, config.encoder_rates, latent_dim)
|
|
|
|
if not config.continuous:
|
|
self.n_codebooks = config.n_codebooks
|
|
self.codebook_size = config.codebook_size
|
|
self.codebook_dim = config.codebook_dim
|
|
self.quantizer = ResidualVectorQuantize(
|
|
input_dim=latent_dim,
|
|
n_codebooks=config.n_codebooks,
|
|
codebook_size=config.codebook_size,
|
|
codebook_dim=config.codebook_dim,
|
|
quantizer_dropout=config.quantizer_dropout,
|
|
)
|
|
else:
|
|
self.quant_conv = torch.nn.Conv1d(latent_dim, 2 * latent_dim, 1)
|
|
self.post_quant_conv = torch.nn.Conv1d(latent_dim, latent_dim, 1)
|
|
|
|
if config.load_decoder:
|
|
self.decoder = Decoder(
|
|
latent_dim,
|
|
config.decoder_dim,
|
|
config.decoder_rates,
|
|
)
|
|
|
|
self.apply(self.init_weights)
|
|
|
|
@staticmethod
|
|
def init_weights(m):
|
|
if isinstance(m, nn.Conv1d):
|
|
nn.init.trunc_normal_(m.weight, std=0.02)
|
|
nn.init.constant_(m.bias, 0)
|
|
|
|
@property
|
|
def dtype(self):
|
|
return next(self.parameters()).dtype
|
|
|
|
@property
|
|
def device(self):
|
|
return next(self.parameters()).device
|
|
|
|
def preprocess(self, audio_data, sample_rate):
|
|
if sample_rate is None:
|
|
sample_rate = self.sample_rate
|
|
assert sample_rate == self.sample_rate
|
|
|
|
length = audio_data.shape[-1]
|
|
right_pad = math.ceil(length / self.hop_length) * self.hop_length - length
|
|
audio_data = nn.functional.pad(audio_data, (0, right_pad))
|
|
|
|
return audio_data
|
|
|
|
def encode(
|
|
self,
|
|
audio_data: torch.Tensor,
|
|
n_quantizers: int = None,
|
|
):
|
|
"""Encode audio data into latent representations.
|
|
|
|
This method processes audio through the encoder network and optionally applies
|
|
vector quantization (in VQ mode) or projects to a Gaussian distribution (in
|
|
continuous mode) to produce latent representations.
|
|
|
|
Args:
|
|
audio_data (torch.Tensor): Audio data to encode, with shape ``[B, 1, T]``.
|
|
n_quantizers (int, optional): Number of quantizers to use. If ``None``,
|
|
all quantizers are used. Only applicable in VQ mode (``continuous=False``).
|
|
|
|
Returns:
|
|
tuple: A tuple containing:
|
|
- z (torch.Tensor): Encoded representation. In VQ mode, this is the
|
|
quantized continuous representation with shape ``[B, D, T]``. In
|
|
continuous mode, this is a ``DiagonalGaussianDistribution`` object.
|
|
- codes (torch.Tensor or None): Codebook indices with shape ``[B, N, T]``
|
|
in VQ mode, ``None`` in continuous mode.
|
|
- latents (torch.Tensor or None): Projected latents with shape ``[B, N*D, T]``
|
|
in VQ mode, ``None`` in continuous mode.
|
|
- commitment_loss (torch.Tensor): Commitment loss scalar.
|
|
- codebook_loss (torch.Tensor): Codebook loss scalar.
|
|
|
|
Note:
|
|
In continuous mode, the encoded representation is projected through a
|
|
quantization convolution layer and wrapped in a ``DiagonalGaussianDistribution``
|
|
for VAE training.
|
|
"""
|
|
z = self.encoder(audio_data) # [B x D x T]
|
|
if not self.continuous:
|
|
z, codes, latents, commitment_loss, codebook_loss = self.quantizer(
|
|
z, n_quantizers
|
|
)
|
|
else:
|
|
z = self.quant_conv(z) # [B x 2D x T]
|
|
z = DiagonalGaussianDistribution(z)
|
|
codes, latents, commitment_loss, codebook_loss = None, None, 0, 0
|
|
|
|
return z, codes, latents, commitment_loss, codebook_loss
|
|
|
|
def decode(self, z: torch.Tensor):
|
|
"""Decode latent representations back to audio waveforms.
|
|
|
|
This method takes latent representations (either quantized from VQ mode or sampled
|
|
from the posterior in continuous mode) and reconstructs the corresponding audio
|
|
through the decoder network.
|
|
|
|
Args:
|
|
z (torch.Tensor): Latent representation to decode, with shape ``[B, D, T]``.
|
|
In VQ mode (``continuous=False``), this is the quantized continuous
|
|
representation. In continuous mode (``continuous=True``), this is sampled
|
|
from the posterior distribution.
|
|
|
|
Returns:
|
|
torch.Tensor: Decoded audio data with shape ``[B, 1, T']``. The output length
|
|
T' is determined by the decoder's upsampling rates and may differ from the
|
|
input temporal dimension T.
|
|
|
|
Note:
|
|
In continuous mode (``continuous=True``), the input is first passed through
|
|
a post-quantization convolution layer before being fed to the decoder.
|
|
"""
|
|
if not self.continuous:
|
|
audio = self.decoder(z)
|
|
else:
|
|
z = self.post_quant_conv(z)
|
|
audio = self.decoder(z)
|
|
|
|
return audio
|
|
|
|
def forward(
|
|
self,
|
|
audio_data: torch.Tensor,
|
|
sample_rate: int = None,
|
|
n_quantizers: int = None,
|
|
):
|
|
"""Model forward pass.
|
|
|
|
Args:
|
|
audio_data (torch.Tensor): Audio to encode, shape [B, 1, T].
|
|
sample_rate (int, optional): Sample rate in Hz. Defaults to
|
|
``self.sample_rate`` when ``None``.
|
|
n_quantizers (int, optional): Number of quantizers to use. When ``None``,
|
|
all quantizers are used. Only used in VQ mode (``continuous=False``).
|
|
|
|
Returns:
|
|
dict: A dictionary containing different keys depending on the mode:
|
|
|
|
**VQ Mode (``continuous=False``):**
|
|
- "audio" (torch.Tensor): Decoded audio, shape [B, 1, length].
|
|
- "z" (torch.Tensor): Quantized continuous representation, shape [B, D, T].
|
|
- "codes" (torch.Tensor): Codebook indices, shape [B, N, T].
|
|
- "latents" (torch.Tensor): Projected latents, shape [B, N*D, T].
|
|
- "vq/commitment_loss" (torch.Tensor): Commitment loss.
|
|
- "vq/codebook_loss" (torch.Tensor): Codebook loss.
|
|
|
|
**Continuous Mode (``continuous=True``):**
|
|
- "audio" (torch.Tensor): Decoded audio, shape [B, 1, length].
|
|
- "z" (torch.Tensor): Latent representation, shape [B, D, T].
|
|
- "kl_loss" (torch.Tensor): KL divergence loss (for VAE training).
|
|
"""
|
|
length = audio_data.shape[-1]
|
|
audio_data = self.preprocess(audio_data, sample_rate)
|
|
if not self.continuous:
|
|
z, codes, latents, commitment_loss, codebook_loss = self.encode(
|
|
audio_data, n_quantizers
|
|
)
|
|
|
|
x = self.decode(z)
|
|
return {
|
|
"audio": x[..., :length],
|
|
"z": z,
|
|
"codes": codes,
|
|
"latents": latents,
|
|
"vq/commitment_loss": commitment_loss,
|
|
"vq/codebook_loss": codebook_loss,
|
|
}
|
|
else:
|
|
posterior, _, _, _, _ = self.encode(audio_data, n_quantizers)
|
|
z = posterior.sample()
|
|
x = self.decode(z)
|
|
|
|
kl_loss = posterior.kl(dims=(1, 2))
|
|
kl_loss = kl_loss.mean()
|
|
|
|
return {
|
|
"audio": x[..., :length],
|
|
"z": z,
|
|
"kl_loss": kl_loss,
|
|
}
|
|
|
|
|
|
EntryClass = DAC
|