Spaces:
Running
Running
| # -*- coding: utf-8 -*- | |
| # Copyright 2019 Tomoki Hayashi | |
| # MIT License (https://opensource.org/licenses/MIT) | |
| """Vector quantization modules. | |
| These codes are modified from https://github.com/ritheshkumar95/pytorch-vqvae. | |
| """ | |
| import torch | |
| from torch.autograd import Function | |
| class VectorQuantization(Function): | |
| """Vector quantization modele.""" | |
| def forward(ctx, inputs, codebook): | |
| """Calculate forward propagation. | |
| Args: | |
| inputs (Tensor): Input tensor (B, `*`, embed_dim). | |
| codebook (Tensor): Embedding weights (num_embeds, embed_dim). | |
| Returns: | |
| LongTensor: Codebook indices (B, `*`). | |
| """ | |
| embedding_size = codebook.size(1) | |
| inputs_size = inputs.size() | |
| inputs_flatten = inputs.view(-1, embedding_size) | |
| codebook_sqr = torch.sum(codebook**2, dim=1) | |
| inputs_sqr = torch.sum(inputs_flatten**2, dim=1, keepdim=True) | |
| # Compute the distances to the codebook | |
| distances = torch.addmm( | |
| codebook_sqr + inputs_sqr, | |
| inputs_flatten, | |
| codebook.t(), | |
| alpha=-2.0, | |
| beta=1.0, | |
| ) | |
| _, indices_flatten = torch.min(distances, dim=1) | |
| indices = indices_flatten.view(*inputs_size[:-1]) | |
| ctx.mark_non_differentiable(indices) | |
| return indices | |
| def backward(ctx, grad_output): | |
| """Calculate backward propagation.""" | |
| raise RuntimeError( | |
| "Trying to call `.grad()` on graph containing " | |
| "`VectorQuantization`. The function `VectorQuantization` " | |
| "is not differentiable. Use `VectorQuantizationStraightThrough` " | |
| "if you want a straight-through estimator of the gradient." | |
| ) | |
| class VectorQuantizationStraightThrough(Function): | |
| """Differentiable vector quantize module with straight through technique.""" | |
| def forward(ctx, inputs, codebook): | |
| """Calculate forward propagation. | |
| Args: | |
| inputs (Tensor): Input tensor (B, `*`, embed_dim). | |
| codebook (Tensor): Embedding weights (num_embeds, embed_dim). | |
| Returns: | |
| Tensor: Codebook embeddings (B, `*`, embed_dim). | |
| LongTensor: Codebook indices (B, `*`). | |
| """ | |
| indices = vector_quantize(inputs, codebook) | |
| indices_flatten = indices.view(-1) | |
| ctx.save_for_backward(indices_flatten, codebook) | |
| ctx.mark_non_differentiable(indices_flatten) | |
| codes_flatten = torch.index_select(codebook, dim=0, index=indices_flatten) | |
| codes = codes_flatten.view_as(inputs) | |
| return (codes, indices_flatten) | |
| def backward(ctx, grad_output, grad_indices): | |
| """Calculate backward propagation.""" | |
| grad_inputs, grad_codebook = None, None | |
| if ctx.needs_input_grad[0]: | |
| # Straight-through estimator | |
| grad_inputs = grad_output.clone() | |
| if ctx.needs_input_grad[1]: | |
| # Gradient wrt. the codebook | |
| indices, codebook = ctx.saved_tensors | |
| embedding_size = codebook.size(1) | |
| grad_output_flatten = grad_output.contiguous().view(-1, embedding_size) | |
| grad_codebook = torch.zeros_like(codebook) | |
| grad_codebook.index_add_(0, indices, grad_output_flatten) | |
| return (grad_inputs, grad_codebook) | |
| # register functions | |
| vector_quantize = VectorQuantization.apply | |
| vector_quantize_straight_through = VectorQuantizationStraightThrough.apply | |
| __all__ = [vector_quantize, vector_quantize_straight_through] | |