vector-quantize-pytorch 1.15.6__tar.gz → 1.16.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (26) hide show
  1. {vector_quantize_pytorch-1.15.6 → vector_quantize_pytorch-1.16.2}/PKG-INFO +12 -1
  2. {vector_quantize_pytorch-1.15.6 → vector_quantize_pytorch-1.16.2}/README.md +11 -0
  3. {vector_quantize_pytorch-1.15.6 → vector_quantize_pytorch-1.16.2}/pyproject.toml +1 -1
  4. {vector_quantize_pytorch-1.15.6 → vector_quantize_pytorch-1.16.2}/tests/test_readme.py +19 -8
  5. {vector_quantize_pytorch-1.15.6 → vector_quantize_pytorch-1.16.2}/vector_quantize_pytorch/residual_vq.py +123 -6
  6. {vector_quantize_pytorch-1.15.6 → vector_quantize_pytorch-1.16.2}/vector_quantize_pytorch/vector_quantize_pytorch.py +82 -30
  7. {vector_quantize_pytorch-1.15.6 → vector_quantize_pytorch-1.16.2}/.github/workflows/build.yml +0 -0
  8. {vector_quantize_pytorch-1.15.6 → vector_quantize_pytorch-1.16.2}/.github/workflows/python-publish.yml +0 -0
  9. {vector_quantize_pytorch-1.15.6 → vector_quantize_pytorch-1.16.2}/.github/workflows/test.yml +0 -0
  10. {vector_quantize_pytorch-1.15.6 → vector_quantize_pytorch-1.16.2}/.gitignore +0 -0
  11. {vector_quantize_pytorch-1.15.6 → vector_quantize_pytorch-1.16.2}/LICENSE +0 -0
  12. {vector_quantize_pytorch-1.15.6 → vector_quantize_pytorch-1.16.2}/examples/autoencoder.py +0 -0
  13. {vector_quantize_pytorch-1.15.6 → vector_quantize_pytorch-1.16.2}/examples/autoencoder_fsq.py +0 -0
  14. {vector_quantize_pytorch-1.15.6 → vector_quantize_pytorch-1.16.2}/examples/autoencoder_lfq.py +0 -0
  15. {vector_quantize_pytorch-1.15.6 → vector_quantize_pytorch-1.16.2}/images/fsq.png +0 -0
  16. {vector_quantize_pytorch-1.15.6 → vector_quantize_pytorch-1.16.2}/images/lfq.png +0 -0
  17. {vector_quantize_pytorch-1.15.6 → vector_quantize_pytorch-1.16.2}/images/vq.png +0 -0
  18. {vector_quantize_pytorch-1.15.6 → vector_quantize_pytorch-1.16.2}/ruff.toml +0 -0
  19. {vector_quantize_pytorch-1.15.6 → vector_quantize_pytorch-1.16.2}/tests/test_latent_quantization.py +0 -0
  20. {vector_quantize_pytorch-1.15.6 → vector_quantize_pytorch-1.16.2}/vector_quantize_pytorch/__init__.py +0 -0
  21. {vector_quantize_pytorch-1.15.6 → vector_quantize_pytorch-1.16.2}/vector_quantize_pytorch/finite_scalar_quantization.py +0 -0
  22. {vector_quantize_pytorch-1.15.6 → vector_quantize_pytorch-1.16.2}/vector_quantize_pytorch/latent_quantization.py +0 -0
  23. {vector_quantize_pytorch-1.15.6 → vector_quantize_pytorch-1.16.2}/vector_quantize_pytorch/lookup_free_quantization.py +0 -0
  24. {vector_quantize_pytorch-1.15.6 → vector_quantize_pytorch-1.16.2}/vector_quantize_pytorch/random_projection_quantizer.py +0 -0
  25. {vector_quantize_pytorch-1.15.6 → vector_quantize_pytorch-1.16.2}/vector_quantize_pytorch/residual_fsq.py +0 -0
  26. {vector_quantize_pytorch-1.15.6 → vector_quantize_pytorch-1.16.2}/vector_quantize_pytorch/residual_lfq.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: vector-quantize-pytorch
3
- Version: 1.15.6
3
+ Version: 1.16.2
4
4
  Summary: Vector Quantization - Pytorch
5
5
  Project-URL: Homepage, https://pypi.org/project/vector-quantize-pytorch/
6
6
  Project-URL: Repository, https://github.com/lucidrains/vector-quantizer-pytorch
@@ -732,3 +732,14 @@ assert loss.item() >= 0
732
732
  url = {https://api.semanticscholar.org/CorpusID:256901024}
733
733
  }
734
734
  ```
735
+
736
+ ```bibtex
737
+ @article{Huijben2024ResidualQW,
738
+ title = {Residual Quantization with Implicit Neural Codebooks},
739
+ author = {Iris Huijben and Matthijs Douze and Matthew Muckley and Ruud van Sloun and Jakob Verbeek},
740
+ journal = {ArXiv},
741
+ year = {2024},
742
+ volume = {abs/2401.14732},
743
+ url = {https://api.semanticscholar.org/CorpusID:267301189}
744
+ }
745
+ ```
@@ -688,3 +688,14 @@ assert loss.item() >= 0
688
688
  url = {https://api.semanticscholar.org/CorpusID:256901024}
689
689
  }
690
690
  ```
691
+
692
+ ```bibtex
693
+ @article{Huijben2024ResidualQW,
694
+ title = {Residual Quantization with Implicit Neural Codebooks},
695
+ author = {Iris Huijben and Matthijs Douze and Matthew Muckley and Ruud van Sloun and Jakob Verbeek},
696
+ journal = {ArXiv},
697
+ year = {2024},
698
+ volume = {abs/2401.14732},
699
+ url = {https://api.semanticscholar.org/CorpusID:267301189}
700
+ }
701
+ ```
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "vector-quantize-pytorch"
3
- version = "1.15.6"
3
+ version = "1.16.2"
4
4
  description = "Vector Quantization - Pytorch"
5
5
  authors = [
6
6
  { name = "Phil Wang", email = "lucidrains@gmail.com" }
@@ -62,19 +62,31 @@ def test_vq_mask():
62
62
  assert (mask_quantized[:, 512:] == 0.).all()
63
63
  assert (mask_indices[:, 512:] == -1).all()
64
64
 
65
- def test_residual_vq():
65
+ @pytest.mark.parametrize('implicit_neural_codebook', (True, False))
66
+ @pytest.mark.parametrize('use_cosine_sim', (True, False))
67
+ @pytest.mark.parametrize('train', (True, False))
68
+ def test_residual_vq(
69
+ implicit_neural_codebook,
70
+ use_cosine_sim,
71
+ train
72
+ ):
66
73
  from vector_quantize_pytorch import ResidualVQ
67
74
 
68
75
  residual_vq = ResidualVQ(
69
- dim = 256,
70
- num_quantizers = 8, # specify number of quantizers
71
- codebook_size = 1024, # codebook size
76
+ dim = 32,
77
+ num_quantizers = 8,
78
+ codebook_size = 128,
79
+ implicit_neural_codebook = implicit_neural_codebook,
80
+ use_cosine_sim = use_cosine_sim,
72
81
  )
73
82
 
74
- x = torch.randn(1, 1024, 256)
83
+ x = torch.randn(1, 256, 32)
75
84
 
76
- quantized, indices, commit_loss = residual_vq(x)
77
- quantized, indices, commit_loss, all_codes = residual_vq(x, return_all_codes = True)
85
+ residual_vq.train(train)
86
+
87
+ quantized, indices, commit_loss = residual_vq(x, freeze_codebook = train and not implicit_neural_codebook)
88
+ quantized_out = residual_vq.get_output_from_indices(indices)
89
+ assert torch.allclose(quantized, quantized_out, atol = 1e-6)
78
90
 
79
91
  def test_residual_vq2():
80
92
  from vector_quantize_pytorch import ResidualVQ
@@ -91,7 +103,6 @@ def test_residual_vq2():
91
103
  x = torch.randn(1, 1024, 256)
92
104
  quantized, indices, commit_loss = residual_vq(x)
93
105
 
94
-
95
106
  def test_grouped_residual_vq():
96
107
  from vector_quantize_pytorch import GroupedResidualVQ
97
108
 
@@ -22,6 +22,9 @@ from einx import get_at
22
22
  def exists(val):
23
23
  return val is not None
24
24
 
25
+ def first(it):
26
+ return it[0]
27
+
25
28
  def default(val, d):
26
29
  return val if exists(val) else d
27
30
 
@@ -34,6 +37,64 @@ def round_up_multiple(num, mult):
34
37
  def is_distributed():
35
38
  return dist.is_initialized() and dist.get_world_size() > 1
36
39
 
40
+ # the mlp for generating the neural implicit codebook
41
+ # from Huijben et al. https://arxiv.org/abs/2401.14732
42
+
43
+ class MLP(Module):
44
+ def __init__(
45
+ self,
46
+ dim,
47
+ dim_hidden = None,
48
+ depth = 4, # they used 4 layers in the paper
49
+ l2norm_output = False
50
+ ):
51
+ super().__init__()
52
+ dim_hidden = default(dim_hidden, dim)
53
+
54
+ self.proj_in = nn.Linear(2 * dim, dim)
55
+
56
+ layers = ModuleList([])
57
+
58
+ for _ in range(depth):
59
+ layers.append(nn.Sequential(
60
+ nn.Linear(dim, dim_hidden),
61
+ nn.SiLU(),
62
+ nn.Linear(dim_hidden, dim)
63
+ ))
64
+
65
+ self.layers = layers
66
+ self.l2norm_output = l2norm_output
67
+
68
+ def forward(
69
+ self,
70
+ codes,
71
+ *,
72
+ condition
73
+ ):
74
+ one_headed = codes.ndim == 2
75
+
76
+ if one_headed:
77
+ codes = rearrange(codes, 'c d -> 1 c d')
78
+
79
+ heads, num_codes, batch, seq_len = codes.shape[0], codes.shape[-2], condition.shape[0], condition.shape[-2]
80
+
81
+ codes = repeat(codes, 'h c d -> h b n c d', n = seq_len, b = batch)
82
+ condition = repeat(condition, 'b n d -> h b n c d', c = num_codes, h = heads)
83
+
84
+ x = torch.cat((condition, codes), dim = -1)
85
+ x = self.proj_in(x)
86
+
87
+ for layer in self.layers:
88
+ x = layer(x) + x
89
+
90
+ if self.l2norm_output:
91
+ x = F.normalize(x, dim = -1)
92
+
93
+ if not one_headed:
94
+ return x
95
+
96
+ return rearrange(x, '1 ... -> ...')
97
+
37
98
  # main class
38
99
 
39
100
  class ResidualVQ(Module):
@@ -50,7 +111,9 @@ class ResidualVQ(Module):
50
111
  quantize_dropout_cutoff_index = 0,
51
112
  quantize_dropout_multiple_of = 1,
52
113
  accept_image_fmap = False,
53
- **kwargs
114
+ implicit_neural_codebook = False, # QINCo from https://arxiv.org/abs/2401.14732
115
+ mlp_kwargs: dict = dict(),
116
+ **vq_kwargs
54
117
  ):
55
118
  super().__init__()
56
119
  assert heads == 1, 'residual vq is not compatible with multi-headed codes'
@@ -65,7 +128,16 @@ class ResidualVQ(Module):
65
128
  self.num_quantizers = num_quantizers
66
129
 
67
130
  self.accept_image_fmap = accept_image_fmap
68
- self.layers = ModuleList([VectorQuantize(dim = codebook_dim, codebook_dim = codebook_dim, accept_image_fmap = accept_image_fmap, **kwargs) for _ in range(num_quantizers)])
131
+
132
+ self.implicit_neural_codebook = implicit_neural_codebook
133
+
134
+ if implicit_neural_codebook:
135
+ vq_kwargs.update(
136
+ learnable_codebook = True,
137
+ ema_update = False
138
+ )
139
+
140
+ self.layers = ModuleList([VectorQuantize(dim = codebook_dim, codebook_dim = codebook_dim, accept_image_fmap = accept_image_fmap, **vq_kwargs) for _ in range(num_quantizers)])
69
141
 
70
142
  assert all([not vq.has_projections for vq in self.layers])
71
143
 
@@ -76,6 +148,12 @@ class ResidualVQ(Module):
76
148
  self.quantize_dropout_cutoff_index = quantize_dropout_cutoff_index
77
149
  self.quantize_dropout_multiple_of = quantize_dropout_multiple_of # encodec paper proposes structured dropout, believe this was set to 4
78
150
 
151
+ # setting up the MLPs for implicit neural codebooks
152
+
153
+ self.mlps = ModuleList([MLP(dim = codebook_dim, l2norm_output = first(self.layers).use_cosine_sim, **mlp_kwargs) for _ in range(num_quantizers - 1)])
154
+
155
+ # sharing codebook logic
156
+
79
157
  if not shared_codebook:
80
158
  return
81
159
 
@@ -120,7 +198,31 @@ class ResidualVQ(Module):
120
198
  mask = indices == -1.
121
199
  indices = indices.masked_fill(mask, 0) # have it fetch a dummy code to be masked out later
122
200
 
123
- all_codes = get_at('q [c] d, b n q -> q b n d', self.codebooks, indices)
201
+ if not self.implicit_neural_codebook:
202
+ # gather all the codes
203
+
204
+ all_codes = get_at('q [c] d, b n q -> q b n d', self.codebooks, indices)
205
+
206
+ else:
207
+ # else if using implicit neural codebook, codes will need to be derived layer by layer
208
+
209
+ code_transform_mlps = (None, *self.mlps)
210
+
211
+ all_codes = []
212
+ quantized_out = 0.
213
+
214
+ for codes, indices, maybe_transform_mlp in zip(self.codebooks, indices.unbind(dim = -1), code_transform_mlps):
215
+
216
+ if exists(maybe_transform_mlp):
217
+ codes = maybe_transform_mlp(codes, condition = quantized_out)
218
+ layer_codes = get_at('b n [c] d, b n -> b n d', codes, indices)
219
+ else:
220
+ layer_codes = get_at('[c] d, b n -> b n d', codes, indices)
221
+
222
+ all_codes.append(layer_codes)
223
+ quantized_out += layer_codes
224
+
225
+ all_codes = torch.stack(all_codes)
124
226
 
125
227
  # mask out any codes that were dropout-ed
126
228
 
@@ -195,9 +297,16 @@ class ResidualVQ(Module):
195
297
  null_indices = torch.full(null_indices_shape, -1., device = device, dtype = torch.long)
196
298
  null_loss = torch.full((1,), 0., device = device, dtype = x.dtype)
197
299
 
300
+ # setup the mlps for implicit neural codebook
301
+
302
+ maybe_code_transforms = (None,) * len(self.layers)
303
+
304
+ if self.implicit_neural_codebook:
305
+ maybe_code_transforms = (None, *self.mlps)
306
+
198
307
  # go through the layers
199
308
 
200
- for quantizer_index, layer in enumerate(self.layers):
309
+ for quantizer_index, (vq, maybe_mlp) in enumerate(zip(self.layers, maybe_code_transforms)):
201
310
 
202
311
  if should_quantize_dropout and quantizer_index > rand_quantize_dropout_index:
203
312
  all_indices.append(null_indices)
@@ -208,12 +317,20 @@ class ResidualVQ(Module):
208
317
  if return_loss:
209
318
  layer_indices = indices[..., quantizer_index]
210
319
 
211
- quantized, *rest = layer(
320
+ # setup the transform code function to be passed into VectorQuantize forward
321
+
322
+ if exists(maybe_mlp):
323
+ maybe_mlp = partial(maybe_mlp, condition = quantized_out)
324
+
325
+ # vector quantize forward
326
+
327
+ quantized, *rest = vq(
212
328
  residual,
213
329
  mask = mask,
214
330
  indices = layer_indices,
215
331
  sample_codebook_temp = sample_codebook_temp,
216
- freeze_codebook = freeze_codebook
332
+ freeze_codebook = freeze_codebook,
333
+ codebook_transform_fn = maybe_mlp
217
334
  )
218
335
 
219
336
  residual = residual - quantized.detach()
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  from functools import partial, cache
2
4
  from collections import namedtuple
3
5
 
@@ -59,10 +61,13 @@ def ema_inplace(old, new, decay):
59
61
  old.mul_(decay).add_(new * (1 - decay))
60
62
 
61
63
  def pack_one(t, pattern):
62
- return pack([t], pattern)
64
+ packed, ps = pack([t], pattern)
65
+
66
+ def unpack_one(to_unpack, unpack_pattern = None):
67
+ unpacked, = unpack(to_unpack, ps, default(unpack_pattern, pattern))
68
+ return unpacked
63
69
 
64
- def unpack_one(t, ps, pattern):
65
- return unpack(t, ps, pattern)[0]
70
+ return packed, unpack_one
66
71
 
67
72
  def lens_to_mask(lens, max_length):
68
73
  seq = torch.arange(max_length, device = lens.device)
@@ -240,12 +245,6 @@ def kmeans(
240
245
 
241
246
  return means, bins
242
247
 
243
- def batched_embedding(indices, embeds):
244
- batch, dim = indices.shape[1], embeds.shape[-1]
245
- indices = repeat(indices, 'h b n -> h b n d', d = dim)
246
- embeds = repeat(embeds, 'h c d -> h b c d', b = batch)
247
- return embeds.gather(2, indices)
248
-
249
248
  # distributed helpers
250
249
 
251
250
  @cache
@@ -465,7 +464,8 @@ class EuclideanCodebook(Module):
465
464
  x,
466
465
  sample_codebook_temp = None,
467
466
  mask = None,
468
- freeze_codebook = False
467
+ freeze_codebook = False,
468
+ codebook_transform_fn: Callable | None = None
469
469
  ):
470
470
  needs_codebook_dim = x.ndim < 4
471
471
  sample_codebook_temp = default(sample_codebook_temp, self.sample_codebook_temp)
@@ -476,7 +476,7 @@ class EuclideanCodebook(Module):
476
476
  x = rearrange(x, '... -> 1 ...')
477
477
 
478
478
  dtype = x.dtype
479
- flatten, ps = pack_one(x, 'h * d')
479
+ flatten, unpack_one = pack_one(x, 'h * d')
480
480
 
481
481
  if exists(mask):
482
482
  mask = repeat(mask, 'b n -> c (b h n)', c = flatten.shape[0], h = flatten.shape[-2] // (mask.shape[0] * mask.shape[1]))
@@ -486,24 +486,51 @@ class EuclideanCodebook(Module):
486
486
  if self.affine_param:
487
487
  self.update_affine(flatten, self.embed, mask = mask)
488
488
 
489
- embed = self.embed if self.learnable_codebook else self.embed.detach()
489
+ # affine params
490
490
 
491
491
  if self.affine_param:
492
492
  codebook_std = self.codebook_variance.clamp(min = 1e-5).sqrt()
493
493
  batch_std = self.batch_variance.clamp(min = 1e-5).sqrt()
494
494
  embed = (embed - self.codebook_mean) * (batch_std / codebook_std) + self.batch_mean
495
495
 
496
- dist = -cdist(flatten, embed)
496
+ # get maybe learnable codes
497
+
498
+ embed = self.embed if self.learnable_codebook else self.embed.detach()
499
+
500
+ # handle maybe implicit neural codebook
501
+ # and calculate distance
502
+
503
+ if exists(codebook_transform_fn):
504
+ transformed_embed = codebook_transform_fn(embed)
505
+ transformed_embed = rearrange(transformed_embed, 'h b n c d -> h (b n) c d')
506
+ broadcastable_input = rearrange(flatten, '... d -> ... 1 d')
507
+
508
+ dist = -F.pairwise_distance(broadcastable_input, transformed_embed)
509
+ else:
510
+ dist = -cdist(flatten, embed)
511
+
512
+ # sample or argmax depending on temperature
497
513
 
498
514
  embed_ind, embed_onehot = self.gumbel_sample(dist, dim = -1, temperature = sample_codebook_temp, training = self.training)
499
515
 
500
- embed_ind = unpack_one(embed_ind, ps, 'h *')
516
+ embed_ind = unpack_one(embed_ind, 'h *')
517
+
518
+ if exists(codebook_transform_fn):
519
+ transformed_embed = unpack_one(transformed_embed, 'h * c d')
501
520
 
502
521
  if self.training:
503
- unpacked_onehot = unpack_one(embed_onehot, ps, 'h * c')
504
- quantize = einsum('h b n c, h c d -> h b n d', unpacked_onehot, embed)
522
+ unpacked_onehot = unpack_one(embed_onehot, 'h * c')
523
+
524
+ if exists(codebook_transform_fn):
525
+ quantize = einsum('h b n c, h b n c d -> h b n d', unpacked_onehot, transformed_embed)
526
+ else:
527
+ quantize = einsum('h b n c, h c d -> h b n d', unpacked_onehot, embed)
528
+
505
529
  else:
506
- quantize = batched_embedding(embed_ind, embed)
530
+ if exists(codebook_transform_fn):
531
+ quantize = einx.get_at('h b n [c] d, h b n -> h b n d', transformed_embed, embed_ind)
532
+ else:
533
+ quantize = einx.get_at('h [c] d, h b n -> h b n d', embed, embed_ind)
507
534
 
508
535
  if self.training and self.ema_update and not freeze_codebook:
509
536
 
@@ -533,7 +560,7 @@ class EuclideanCodebook(Module):
533
560
  if needs_codebook_dim:
534
561
  quantize, embed_ind = map(lambda t: rearrange(t, '1 ... -> ...'), (quantize, embed_ind))
535
562
 
536
- dist = unpack_one(dist, ps, 'h * d')
563
+ dist = unpack_one(dist, 'h * d')
537
564
 
538
565
  return quantize, embed_ind, dist
539
566
 
@@ -650,7 +677,8 @@ class CosineSimCodebook(Module):
650
677
  x,
651
678
  sample_codebook_temp = None,
652
679
  mask = None,
653
- freeze_codebook = False
680
+ freeze_codebook = False,
681
+ codebook_transform_fn: Callable | None = None
654
682
  ):
655
683
  needs_codebook_dim = x.ndim < 4
656
684
  sample_codebook_temp = default(sample_codebook_temp, self.sample_codebook_temp)
@@ -662,7 +690,7 @@ class CosineSimCodebook(Module):
662
690
 
663
691
  dtype = x.dtype
664
692
 
665
- flatten, ps = pack_one(x, 'h * d')
693
+ flatten, unpack_one = pack_one(x, 'h * d')
666
694
 
667
695
  if exists(mask):
668
696
  mask = repeat(mask, 'b n -> c (b h n)', c = flatten.shape[0], h = flatten.shape[-2] // (mask.shape[0] * mask.shape[1]))
@@ -671,16 +699,37 @@ class CosineSimCodebook(Module):
671
699
 
672
700
  embed = self.embed if self.learnable_codebook else self.embed.detach()
673
701
 
674
- dist = einsum('h n d, h c d -> h n c', flatten, embed)
702
+ # handle maybe implicit neural codebook
703
+ # and compute cosine sim distance
704
+
705
+ if exists(codebook_transform_fn):
706
+ transformed_embed = codebook_transform_fn(embed)
707
+ transformed_embed = rearrange(transformed_embed, 'h b n c d -> h (b n) c d')
708
+ transformed_embed = l2norm(transformed_embed)
709
+
710
+ dist = einsum('h n d, h n c d -> h n c', flatten, transformed_embed)
711
+ else:
712
+ dist = einsum('h n d, h c d -> h n c', flatten, embed)
675
713
 
676
714
  embed_ind, embed_onehot = self.gumbel_sample(dist, dim = -1, temperature = sample_codebook_temp, training = self.training)
677
- embed_ind = unpack_one(embed_ind, ps, 'h *')
715
+ embed_ind = unpack_one(embed_ind, 'h *')
716
+
717
+ if exists(codebook_transform_fn):
718
+ transformed_embed = unpack_one(transformed_embed, 'h * c d')
678
719
 
679
720
  if self.training:
680
- unpacked_onehot = unpack_one(embed_onehot, ps, 'h * c')
681
- quantize = einsum('h b n c, h c d -> h b n d', unpacked_onehot, embed)
721
+ unpacked_onehot = unpack_one(embed_onehot, 'h * c')
722
+
723
+ if exists(codebook_transform_fn):
724
+ quantize = einsum('h b n c, h b n c d -> h b n d', unpacked_onehot, transformed_embed)
725
+ else:
726
+ quantize = einsum('h b n c, h c d -> h b n d', unpacked_onehot, embed)
727
+
682
728
  else:
683
- quantize = batched_embedding(embed_ind, embed)
729
+ if exists(codebook_transform_fn):
730
+ quantize = einx.get_at('h b n [c] d, h b n -> h b n d', transformed_embed, embed_ind)
731
+ else:
732
+ quantize = einx.get_at('h [c] d, h b n -> h b n d', embed, embed_ind)
684
733
 
685
734
  if self.training and self.ema_update and not freeze_codebook:
686
735
  if exists(mask):
@@ -702,13 +751,13 @@ class CosineSimCodebook(Module):
702
751
  embed_normalized = self.embed_avg / rearrange(cluster_size, '... -> ... 1')
703
752
  embed_normalized = l2norm(embed_normalized)
704
753
 
705
- self.embed.data.copy_(l2norm(embed_normalized))
754
+ self.embed.data.copy_(embed_normalized)
706
755
  self.expire_codes_(x)
707
756
 
708
757
  if needs_codebook_dim:
709
758
  quantize, embed_ind = map(lambda t: rearrange(t, '1 ... -> ...'), (quantize, embed_ind))
710
759
 
711
- dist = unpack_one(dist, ps, 'h * d')
760
+ dist = unpack_one(dist, 'h * d')
712
761
  return quantize, embed_ind, dist
713
762
 
714
763
  # main class
@@ -845,6 +894,7 @@ class VectorQuantize(Module):
845
894
  affine_param_codebook_decay = affine_param_codebook_decay,
846
895
  )
847
896
 
897
+ self.use_cosine_sim = use_cosine_sim
848
898
  self._codebook = codebook_class(**codebook_kwargs)
849
899
 
850
900
  self.in_place_codebook_optimizer = in_place_codebook_optimizer(self._codebook.parameters()) if exists(in_place_codebook_optimizer) else None
@@ -882,7 +932,7 @@ class VectorQuantize(Module):
882
932
  if not is_multiheaded:
883
933
  codes = codebook[indices]
884
934
  else:
885
- indices, ps = pack_one(indices, 'b * h')
935
+ indices, unpack_one = pack_one(indices, 'b * h')
886
936
  indices = rearrange(indices, 'b n h -> b h n')
887
937
 
888
938
  indices = repeat(indices, 'b h n -> b h n d', d = codebook.shape[-1])
@@ -890,7 +940,7 @@ class VectorQuantize(Module):
890
940
 
891
941
  codes = codebook.gather(2, indices)
892
942
  codes = rearrange(codes, 'b h n d -> b n (h d)')
893
- codes = unpack_one(codes, ps, 'b * d')
943
+ codes = unpack_one(codes, 'b * d')
894
944
 
895
945
  if not self.channel_last:
896
946
  codes = rearrange(codes, 'b ... d -> b d ...')
@@ -910,6 +960,7 @@ class VectorQuantize(Module):
910
960
  sample_codebook_temp = None,
911
961
  freeze_codebook = False,
912
962
  return_loss_breakdown = False,
963
+ codebook_transform_fn: Callable | None = None
913
964
  ):
914
965
  orig_input = x
915
966
 
@@ -962,7 +1013,8 @@ class VectorQuantize(Module):
962
1013
  codebook_forward_kwargs = dict(
963
1014
  sample_codebook_temp = sample_codebook_temp,
964
1015
  mask = mask,
965
- freeze_codebook = freeze_codebook
1016
+ freeze_codebook = freeze_codebook,
1017
+ codebook_transform_fn = codebook_transform_fn
966
1018
  )
967
1019
 
968
1020
  # quantize