Spaces:
Running on Zero
Running on Zero
Download models/fsq.py from matteuscruz/tinyworldmodels: direct link, hf CLI and curl.
- Browser
- Download file 2.92 kB
-
https://huggingface.co/spaces/matteuscruz/tinyworldmodels/resolve/main/models/fsq.py
- Command line
-
hf download hf://spaces/matteuscruz/tinyworldmodels/models/fsq.py
-
curl -L -o fsq.py https://huggingface.co/spaces/matteuscruz/tinyworldmodels/resolve/main/models/fsq.py
2.92 kB
| # Finite Scalar Quantization from https://arxiv.org/pdf/2309.15505 | |
| # quantizes each dimension independently by bounding to 0, num_bins then rounding to nearest integer | |
| # prevents token collapse and no auxiliary losses necessary | |
| import torch | |
| import torch.nn as nn | |
| from einops import rearrange | |
| class FiniteScalarQuantizer(nn.Module): | |
| def __init__(self, latent_dim=5, num_bins=4): | |
| super().__init__() | |
| self.num_bins = num_bins # D | |
| self.levels_np = torch.tensor(latent_dim * [num_bins]) | |
| self.codebook_size = num_bins**latent_dim # L^D | |
| # fsq basis [L^0, L^1, ..., L^(L-1)] for converting between indices and latents | |
| self.register_buffer('basis', (num_bins**torch.arange(latent_dim, dtype=torch.long))) | |
| def scale_and_shift(self, z): | |
| # scale and shift z from [-1, 1] to [0, num_bins - 1] | |
| return 0.5 * (z + 1) * (self.num_bins - 1) | |
| def unscale_and_unshift(self, z): | |
| # unscale and unshift z from [0, num_bins - 1] to [-1, 1] | |
| return 2 * z / (self.num_bins - 1) - 1 | |
| def forward(self, z): | |
| # z: [B, T, P, L] | |
| # apply 0.5 * (tanh(z) + 1) to go from z range to [0, num_bins - 1] | |
| tanh_z = torch.tanh(z) | |
| bounded_z = self.scale_and_shift(tanh_z) | |
| # round to nearest integer | |
| rounded_z = torch.round(bounded_z) | |
| # stopgrad for straight-through gradient bypassing quantization | |
| quantized_z = bounded_z + (rounded_z - bounded_z).detach() | |
| # normalize back to [-1, 1] | |
| quantized_z = self.unscale_and_unshift(quantized_z) | |
| return quantized_z | |
| def get_codebook_usage(self, quantized_z): | |
| unique_bins = torch.unique(quantized_z).shape[0] | |
| return unique_bins / self.num_bins | |
| def get_indices_from_latents(self, latents, dim=-1): | |
| # to get fsq indices, for each dimension, we get the index and add it (so multiply by L then sum) | |
| # for each dimension of each latent, get sum of (value * L^current_dim) along latent dim which is the index of that latent in the codebook | |
| # codebook size = L^latent_dim | |
| # latents: [*, L] | |
| # go from [-1, 1] to [0, num_bins - 1] in each dimension | |
| digits = torch.round(self.scale_and_shift(latents)).clamp(0, self.num_bins-1) | |
| # get indices for each latent by summing (value * L^current_dim_idx) along latent dim | |
| indices = torch.sum(digits * self.basis.to(latents.device), dim=dim).long() # [*] | |
| return indices | |
| def get_latents_from_indices(self, indices, dim=-1): | |
| # indices: [*] | |
| # recover each entry of latent in range [0, num_bins - 1] by repeatedly dividing by L^current_dim and taking mod | |
| digits = (indices.unsqueeze(-1) // self.basis) % self.num_bins # [*, L] | |
| # go from [0, num_bins - 1] to [-1, 1] in each dimension | |
| latents = self.unscale_and_unshift(digits) # [*, L] | |
| return latents | |