vector-quantize-pytorch: A Codebook Layer for VQ-VAE, Residual VQ and Grouped VQ
Vector (and Scalar) Quantization, in Pytorch
At a glance
- What is it?
- This PyTorch package supplies drop-in vector and scalar quantizer layers, including ResidualVQ, GroupedResidualVQ, kmeans codebook init and several gradient estimators. It is a layer library, not a full model, and the README documents the API rather than training recipes.
- Who is it for?
- Adopt it if you already have an encoder-decoder and need a quantization bottleneck with EMA codebook updates, straight-through, rotation trick or DiVeQ gradients, and residual or grouped residual stacks. Do not adopt it if you expect a trainable VQ-VAE, a data pipeline or a checkpointing scheme; the README covers layers only.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 13 days ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.
DEEP OPEN-SOURCE ANALYSIS
The Bottleneck Problem vector-quantize-pytorch Addresses
An autoencoder that compresses to continuous latents gives you a code you cannot index, count or feed to a discrete prior. Vector quantization replaces the continuous latent with the nearest entry in a learned codebook, so the latent becomes an integer id. The README frames the package as a vector quantization library originally transcribed from Deepmind's tensorflow implementation, made conveniently into a package, and it notes that VQ has been used by Deepmind and OpenAI for image generation (VQ-VAE-2) and music (Jukebox). The audience is therefore people who already have an encoder and decoder and need the middle layer. The library supplies that layer and nothing else: no encoder, no decoder, no training loop, no dataset code. If your project needs a discrete latent space that a transformer or an autoregressive model can consume, the missing piece is usually exactly this layer.
What VectorQuantize Returns and How the Codebook Is Updated
The basic call is small. VectorQuantize takes dim and codebook_size, plus decay for the exponential moving average and commitment_weight for the commitment loss. Calling it on a tensor of shape (1, 1024, 256) returns three values: the quantized tensor, the indices, and a scalar commit loss. The EMA mechanism is the part that differs from a plain nearest-neighbour lookup with a learned embedding table. Instead of receiving gradients, the codebook entries are moved toward the encoder outputs that selected them, with decay controlling how fast that movement happens; the README states that lower decay means the dictionary will change faster. That single number is the main stability knob in the layer. The commitment loss is returned rather than added internally, so the caller decides how to weight it in the total objective. The README also documents kmeans_init, which initializes the codebook from kmeans centroids of the first batch, with kmeans_iters controlling the number of iterations used to compute those centroids. That is a first-batch cost, not a per-step one, but it is a cost the first forward pass pays.
ResidualVQ, GroupedResidualVQ and the Shapes They Change
A single codebook at a practical size cannot represent a complex signal precisely, so the package offers recursive quantization. ResidualVQ stacks num_quantizers quantizers and quantizes the residual left by the previous stage. The shape change is the important detail: the quantized output stays (1, 1024, 256), the indices become (1, 1024, 8) for eight quantizers, and the commit loss becomes (1, 8), one value per quantizer. Passing return_all_codes = True adds a fourth return value containing all codes across the quantization layers, documented as shape (8, 1, 1024, 256). GroupedResidualVQ goes further and splits the feature dimension into groups, so with groups = 2 the indices come back as (2, 1, 1024, 8) and the commit loss as (2, 1, 8). The README attributes this grouped approach to a paper that reports equivalent results to Encodec while using far fewer codebooks. Two options from the RQ-VAE paper are exposed as flags: shared_codebook, which shares one codebook across all quantizers, and stochastic_sample_codes with sample_codebook_temp, which samples codes instead of always taking the closest match. Note that the temperature is a real behavioural switch: the README states that a temperature of 0 would be equivalent to non-stochastic sampling.
Choosing Between Straight-Through, Rotation Trick and DiVeQ Gradients
The quantization step is not differentiable, so the layer has to invent a gradient. The README describes three options. The default lineage is the straight-through estimator, where the gradient flows around the VQ layer rather than through it. Setting rotation_trick = True switches to a method that transforms the gradient through the layer so the relative angle and magnitude between the input vector and the quantized output are encoded into the gradient. Setting directional_reparam = True switches to the DiVeQ formulation, which models quantization as adding a simulated quantization error to the input vector, with the direction aligned with the nearest codeword when directional_reparam_variance is small and the magnitude equal to the actual quantization error. The README states that this makes the quantized output a differentiable function of both the input vector and the selected codeword, which means the codebook can be learned from gradients without auxiliary losses. ResidualVQ also accepts diveq = True to update codebooks by gradients rather than EMA. These are not interchangeable defaults. A team that sets directional_reparam without understanding that the codebook now learns from gradients rather than EMA has changed the training dynamics of the whole model, not just a flag.
Install and Minimal Configuration
Installation is a single pip command: pip install vector-quantize-pytorch. The import path is vector_quantize_pytorch, and the three classes shown in the README are VectorQuantize, ResidualVQ and GroupedResidualVQ. A minimal VectorQuantize configuration needs dim, codebook_size, decay and commitment_weight. Moving to residual quantization means adding num_quantizers and codebook_size to ResidualVQ, and optionally kmeans_init = True with kmeans_iters = 10. Grouped residual quantization adds groups = 2. The gradient estimator is selected by rotation_trick = True or directional_reparam = True with directional_reparam_variance, and the DiVeQ codebook update path on ResidualVQ is diveq = True. The README does not document a config file format, a CLI, or environment variables, so configuration lives in Python constructor arguments. There is also no checkpoint utility described in the supplied material, which means saving and restoring codebook state is the caller's responsibility.
Where the Library Stops
The most likely failure mode is expecting more than a layer. Nothing in the README describes a training loop, a reconstruction loss, an optimizer setup, or a way to persist a codebook. The commit loss is returned as a tensor, and if the caller ignores it, the commitment term simply does not exist in the objective; the README does not warn about this, it only shows the return signature. Dead codebook entries are acknowledged as a common problem, and the README points to techniques from various papers, including keeping the codebook in a lower dimension by projecting encoder values down before projecting back after quantization. That is a design pattern the README describes, not a flag that fixes the problem automatically. The kmeans_init path also has a scaling question the README does not answer: it computes centroids from the first batch, so a small or unrepresentative first batch produces a poor initialization. Finally, the README states that the library was originally transcribed from Deepmind's tensorflow implementation, which sets expectations about the core algorithm but says nothing about numerical parity with that implementation.
How This Differs From an Off-the-Shelf VQ-VAE Implementation
The obvious alternative is a complete VQ-VAE repository or a model implementation inside a research codebase, which typically bundles the encoder, the decoder, the quantizer and a training script. The difference is scope, not algorithm. A full VQ-VAE implementation decides the architecture for you and usually exposes a single training entry point. vector-quantize-pytorch does the opposite: it hands you the quantizer and expects you to bring the encoder and decoder. That matters when the quantizer is not the interesting part of your system, for example when you are quantizing latents from an existing audio codec or an image encoder you already trust. It also matters when you need variants that a monolithic VQ-VAE repository rarely exposes, such as grouped residual quantization over the feature dimension, shared codebooks, stochastic code sampling with a temperature, or a choice between straight-through, rotation trick and DiVeQ gradients. The trade is that you inherit the integration work: loss weighting, checkpointing and the decision about which gradient estimator your architecture actually needs.
Maintenance, Licensing and What to Check Before Adopting
The repository is MIT licensed, which permits commercial and closed-source use subject to the licence text; that is a statement about the licence identifier, not legal advice, and the full text in the repository governs. The project is not archived, and the supplied release list shows a steady cadence: 1.27.19 in January 2026, 1.27.20 later that month, 1.27.21 in February 2026, with the last push to master in September 2026. Frequent patch releases on a 1.27.x line suggest incremental fixes rather than a stable frozen API, so pinning a version in your requirements is reasonable if you depend on exact constructor behaviour. Before adopting, check three things against your own code: the exact tuple your chosen class returns, because ResidualVQ and GroupedResidualVQ add dimensions to indices and commit loss; whether the gradient estimator you selected is the one your training setup assumes; and whether kmeans_init on your first batch is affordable and representative. If your encoder and decoder already exist and the only missing piece is the discrete bottleneck, this package is a direct fit. If you need a full trainable model with a training script, it is not.
Editorial conclusion
Adopt it if you already have an encoder-decoder and need a quantization bottleneck with EMA codebook updates, straight-through, rotation trick or DiVeQ gradients, and residual or grouped residual stacks. Do not adopt it if you expect a trainable VQ-VAE, a data pipeline or a checkpointing scheme; the README covers layers only. Verify first that the codebook shape your architecture needs matches what the layer returns, that the chosen gradient estimator is the one you intend, and that kmeans_init is affordable on your first batch, since it runs kmeans over that batch at initialization.
Community notes