|
import torch
|
|
from torch import nn
|
|
from torch.nn import functional as F
|
|
|
|
from einops import rearrange
|
|
from vector_quantize_pytorch import ResidualVQ, FSQ
|
|
from .nn.quantize import ResidualVectorQuantize as DACResidualVQ
|
|
|
|
|
|
class Bottleneck(nn.Module):
|
|
def __init__(self, is_discrete: bool = False):
|
|
super().__init__()
|
|
|
|
self.is_discrete = is_discrete
|
|
|
|
def encode(self, x, return_info=False, **kwargs):
|
|
raise NotImplementedError
|
|
|
|
def decode(self, x):
|
|
raise NotImplementedError
|
|
|
|
|
|
class DiscreteBottleneck(Bottleneck):
|
|
def __init__(self, num_quantizers, codebook_size, tokens_id):
|
|
super().__init__(is_discrete=True)
|
|
|
|
self.num_quantizers = num_quantizers
|
|
self.codebook_size = codebook_size
|
|
self.tokens_id = tokens_id
|
|
|
|
def decode_tokens(self, codes, **kwargs):
|
|
raise NotImplementedError
|
|
|
|
|
|
class TanhBottleneck(Bottleneck):
|
|
def __init__(self):
|
|
super().__init__(is_discrete=False)
|
|
self.tanh = nn.Tanh()
|
|
|
|
def encode(self, x, return_info=False):
|
|
info = {}
|
|
|
|
x = torch.tanh(x)
|
|
|
|
if return_info:
|
|
return x, info
|
|
else:
|
|
return x
|
|
|
|
def decode(self, x):
|
|
return x
|
|
|
|
|
|
@torch.jit.script
|
|
def vae_sample_kl(mean, scale):
|
|
stdev = nn.functional.softplus(scale) + 1e-4
|
|
var = stdev * stdev
|
|
logvar = torch.log(var)
|
|
latents = torch.randn_like(mean) * stdev + mean
|
|
|
|
kl = (mean * mean + var - logvar - 1).sum(1).mean()
|
|
|
|
return latents, kl
|
|
|
|
|
|
@torch.jit.script
|
|
def vae_sample(mean, scale):
|
|
stdev = nn.functional.softplus(scale) + 1e-4
|
|
latents = torch.randn_like(mean) * stdev + mean
|
|
return latents
|
|
|
|
|
|
class VAEBottleneck(Bottleneck):
|
|
def __init__(self):
|
|
super().__init__(is_discrete=False)
|
|
|
|
def encode(self, x, return_info=False, **kwargs):
|
|
mean, scale = x.chunk(2, dim=1)
|
|
|
|
if return_info:
|
|
info = {}
|
|
x, kl = vae_sample_kl(mean, scale)
|
|
info["kl"] = kl
|
|
return x, info
|
|
else:
|
|
x = vae_sample(mean, scale)
|
|
return x
|
|
|
|
def decode(self, x):
|
|
return x
|
|
|
|
|
|
def compute_mean_kernel(x, y):
|
|
kernel_input = (x[:, None] - y[None]).pow(2).mean(2) / x.shape[-1]
|
|
return torch.exp(-kernel_input).mean()
|
|
|
|
|
|
def compute_mmd(latents):
|
|
latents_reshaped = latents.permute(0, 2, 1).reshape(-1, latents.shape[1])
|
|
noise = torch.randn_like(latents_reshaped)
|
|
|
|
latents_kernel = compute_mean_kernel(latents_reshaped, latents_reshaped)
|
|
noise_kernel = compute_mean_kernel(noise, noise)
|
|
latents_noise_kernel = compute_mean_kernel(latents_reshaped, noise)
|
|
|
|
mmd = latents_kernel + noise_kernel - 2 * latents_noise_kernel
|
|
return mmd.mean()
|
|
|
|
|
|
class WassersteinBottleneck(Bottleneck):
|
|
def __init__(self, noise_augment_dim: int = 0):
|
|
super().__init__(is_discrete=False)
|
|
|
|
self.noise_augment_dim = noise_augment_dim
|
|
|
|
def encode(self, x, return_info=False):
|
|
info = {}
|
|
|
|
if self.training and return_info:
|
|
mmd = compute_mmd(x)
|
|
info["mmd"] = mmd
|
|
|
|
if return_info:
|
|
return x, info
|
|
|
|
return x
|
|
|
|
def decode(self, x):
|
|
|
|
if self.noise_augment_dim > 0:
|
|
noise = torch.randn(x.shape[0], self.noise_augment_dim,
|
|
x.shape[-1]).type_as(x)
|
|
x = torch.cat([x, noise], dim=1)
|
|
|
|
return x
|
|
|
|
|
|
class L2Bottleneck(Bottleneck):
|
|
def __init__(self):
|
|
super().__init__(is_discrete=False)
|
|
|
|
def encode(self, x, return_info=False):
|
|
info = {}
|
|
|
|
x = F.normalize(x, dim=1)
|
|
|
|
if return_info:
|
|
return x, info
|
|
else:
|
|
return x
|
|
|
|
def decode(self, x):
|
|
return F.normalize(x, dim=1)
|
|
|
|
|
|
class RVQBottleneck(DiscreteBottleneck):
|
|
def __init__(self, **quantizer_kwargs):
|
|
super().__init__(num_quantizers = quantizer_kwargs["num_quantizers"], codebook_size = quantizer_kwargs["codebook_size"], tokens_id = "quantizer_indices")
|
|
self.quantizer = ResidualVQ(**quantizer_kwargs)
|
|
self.num_quantizers = quantizer_kwargs["num_quantizers"]
|
|
|
|
def encode(self, x, return_info=False, **kwargs):
|
|
info = {}
|
|
|
|
x = rearrange(x, "b c n -> b n c")
|
|
x, indices, loss = self.quantizer(x)
|
|
x = rearrange(x, "b n c -> b c n")
|
|
|
|
info["quantizer_indices"] = indices
|
|
info["quantizer_loss"] = loss.mean()
|
|
|
|
if return_info:
|
|
return x, info
|
|
else:
|
|
return x
|
|
|
|
def decode(self, x):
|
|
return x
|
|
|
|
def decode_tokens(self, codes, **kwargs):
|
|
latents = self.quantizer.get_outputs_from_indices(codes)
|
|
|
|
return self.decode(latents, **kwargs)
|
|
|
|
|
|
class RVQVAEBottleneck(DiscreteBottleneck):
|
|
def __init__(self, **quantizer_kwargs):
|
|
super().__init__(num_quantizers = quantizer_kwargs["num_quantizers"], codebook_size = quantizer_kwargs["codebook_size"], tokens_id = "quantizer_indices")
|
|
self.quantizer = ResidualVQ(**quantizer_kwargs)
|
|
self.num_quantizers = quantizer_kwargs["num_quantizers"]
|
|
|
|
def encode(self, x, return_info=False):
|
|
info = {}
|
|
|
|
x, kl = vae_sample(*x.chunk(2, dim=1))
|
|
|
|
info["kl"] = kl
|
|
|
|
x = rearrange(x, "b c n -> b n c")
|
|
x, indices, loss = self.quantizer(x)
|
|
x = rearrange(x, "b n c -> b c n")
|
|
|
|
info["quantizer_indices"] = indices
|
|
info["quantizer_loss"] = loss.mean()
|
|
|
|
if return_info:
|
|
return x, info
|
|
else:
|
|
return x
|
|
|
|
def decode(self, x):
|
|
return x
|
|
|
|
def decode_tokens(self, codes, **kwargs):
|
|
latents = self.quantizer.get_outputs_from_indices(codes)
|
|
|
|
return self.decode(latents, **kwargs)
|
|
|
|
|
|
class DACRVQBottleneck(DiscreteBottleneck):
|
|
def __init__(self, quantize_on_decode=False, **quantizer_kwargs):
|
|
super().__init__(num_quantizers = quantizer_kwargs["n_codebooks"], codebook_size = quantizer_kwargs["codebook_size"], tokens_id = "codes")
|
|
self.quantizer = DACResidualVQ(**quantizer_kwargs)
|
|
self.num_quantizers = quantizer_kwargs["n_codebooks"]
|
|
self.quantize_on_decode = quantize_on_decode
|
|
|
|
def encode(self, x, return_info=False, **kwargs):
|
|
info = {}
|
|
|
|
info["pre_quantizer"] = x
|
|
|
|
if self.quantize_on_decode:
|
|
return x, info if return_info else x
|
|
|
|
z, codes, latents, commitment_loss, codebook_loss = self.quantizer(x, **kwargs)
|
|
|
|
output = {
|
|
"z": z,
|
|
"codes": codes,
|
|
"latents": latents,
|
|
"vq/commitment_loss": commitment_loss,
|
|
"vq/codebook_loss": codebook_loss,
|
|
}
|
|
|
|
output["vq/commitment_loss"] /= self.num_quantizers
|
|
output["vq/codebook_loss"] /= self.num_quantizers
|
|
|
|
info.update(output)
|
|
|
|
if return_info:
|
|
return output["z"], info
|
|
|
|
return output["z"]
|
|
|
|
def decode(self, x):
|
|
|
|
if self.quantize_on_decode:
|
|
x = self.quantizer(x)[0]
|
|
|
|
return x
|
|
|
|
def decode_tokens(self, codes, **kwargs):
|
|
latents, _, _ = self.quantizer.from_codes(codes)
|
|
|
|
return self.decode(latents, **kwargs)
|
|
|
|
|
|
class DACRVQVAEBottleneck(DiscreteBottleneck):
|
|
def __init__(self, quantize_on_decode=False, **quantizer_kwargs):
|
|
super().__init__(num_quantizers = quantizer_kwargs["n_codebooks"], codebook_size = quantizer_kwargs["codebook_size"], tokens_id = "codes")
|
|
self.quantizer = DACResidualVQ(**quantizer_kwargs)
|
|
self.num_quantizers = quantizer_kwargs["n_codebooks"]
|
|
self.quantize_on_decode = quantize_on_decode
|
|
|
|
def encode(self, x, return_info=False, n_quantizers: int = None):
|
|
info = {}
|
|
|
|
mean, scale = x.chunk(2, dim=1)
|
|
|
|
x, kl = vae_sample(mean, scale)
|
|
|
|
info["pre_quantizer"] = x
|
|
info["kl"] = kl
|
|
|
|
if self.quantize_on_decode:
|
|
return x, info if return_info else x
|
|
|
|
z, codes, latents, commitment_loss, codebook_loss = self.quantizer(x, n_quantizers=n_quantizers)
|
|
|
|
output = {
|
|
"z": z,
|
|
"codes": codes,
|
|
"latents": latents,
|
|
"vq/commitment_loss": commitment_loss,
|
|
"vq/codebook_loss": codebook_loss,
|
|
}
|
|
|
|
output["vq/commitment_loss"] /= self.num_quantizers
|
|
output["vq/codebook_loss"] /= self.num_quantizers
|
|
|
|
info.update(output)
|
|
|
|
if return_info:
|
|
return output["z"], info
|
|
|
|
return output["z"]
|
|
|
|
def decode(self, x):
|
|
|
|
if self.quantize_on_decode:
|
|
x = self.quantizer(x)[0]
|
|
|
|
return x
|
|
|
|
def decode_tokens(self, codes, **kwargs):
|
|
latents, _, _ = self.quantizer.from_codes(codes)
|
|
|
|
return self.decode(latents, **kwargs)
|
|
|
|
|
|
class FSQBottleneck(DiscreteBottleneck):
|
|
def __init__(self, dim, levels):
|
|
super().__init__(num_quantizers = 1, codebook_size = levels ** dim, tokens_id = "quantizer_indices")
|
|
self.quantizer = FSQ(levels=[levels] * dim)
|
|
|
|
def encode(self, x, return_info=False):
|
|
info = {}
|
|
|
|
x = rearrange(x, "b c n -> b n c")
|
|
x, indices = self.quantizer(x)
|
|
x = rearrange(x, "b n c -> b c n")
|
|
|
|
info["quantizer_indices"] = indices
|
|
|
|
if return_info:
|
|
return x, info
|
|
else:
|
|
return x
|
|
|
|
def decode(self, x):
|
|
return x
|
|
|
|
def decode_tokens(self, tokens, **kwargs):
|
|
latents = self.quantizer.indices_to_codes(tokens)
|
|
|
|
return self.decode(latents, **kwargs) |