Prompt48 commited on
Commit
21fdc32
·
verified ·
1 Parent(s): bfbecd0

Upload edit\Qwen3-TTS-test\qwen_tts\core\tokenizer_25hz\vq\core_vq.py with huggingface_hub

Browse files
edit//Qwen3-TTS-test//qwen_tts//core//tokenizer_25hz//vq//core_vq.py ADDED
@@ -0,0 +1,523 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Meta Platforms, Inc. and affiliates.
2
+ # All rights reserved.
3
+ #
4
+ # This source code is licensed under the license found in the
5
+ # LICENSE file in the root directory of this source tree.
6
+ #
7
+ # This implementation is inspired from
8
+ # https://github.com/lucidrains/vector-quantize-pytorch
9
+ # which is released under MIT License. Hereafter, the original license:
10
+ # MIT License
11
+ #
12
+ # Copyright (c) 2020 Phil Wang
13
+ #
14
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
15
+ # of this software and associated documentation files (the "Software"), to deal
16
+ # in the Software without restriction, including without limitation the rights
17
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
18
+ # copies of the Software, and to permit persons to whom the Software is
19
+ # furnished to do so, subject to the following conditions:
20
+ #
21
+ # The above copyright notice and this permission notice shall be included in all
22
+ # copies or substantial portions of the Software.
23
+ #
24
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
25
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
27
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
29
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30
+ # SOFTWARE.
31
+
32
+ """Core vector quantization implementation."""
33
+ import random
34
+ import typing as tp
35
+ from random import randrange
36
+
37
+ import numpy as np
38
+ from einops import rearrange, repeat
39
+ from math import ceil
40
+ import torch
41
+ from torch import nn
42
+ import torch.nn.functional as F
43
+
44
+
45
+ def round_up_multiple(num, mult):
46
+ return ceil(num / mult) * mult
47
+
48
+ def default(val: tp.Any, d: tp.Any) -> tp.Any:
49
+ return val if val is not None else d
50
+
51
+
52
+ def ema_inplace(moving_avg, new, decay: float):
53
+ moving_avg.data.mul_(decay).add_(new, alpha=(1 - decay))
54
+
55
+
56
+ def laplace_smoothing(x, n_categories: int, epsilon: float = 1e-5):
57
+ return (x + epsilon) / (x.sum() + n_categories * epsilon)
58
+
59
+
60
+ def uniform_init(*shape: int):
61
+ t = torch.empty(shape)
62
+ nn.init.kaiming_uniform_(t)
63
+ return t
64
+
65
+
66
+ def sample_vectors(samples, num: int):
67
+ num_samples, device = samples.shape[0], samples.device
68
+
69
+ if num_samples >= num:
70
+ indices = torch.randperm(num_samples, device=device)[:num]
71
+ else:
72
+ indices = torch.randint(0, num_samples, (num,), device=device)
73
+
74
+ return samples[indices]
75
+
76
+
77
+ @torch.no_grad()
78
+ def kmeans(samples, num_clusters: int, num_iters: int = 10):
79
+ dim, dtype = samples.shape[-1], samples.dtype
80
+
81
+ means = sample_vectors(samples, num_clusters)
82
+
83
+ for _ in range(num_iters):
84
+ dists = -(
85
+ samples.pow(2).sum(1, keepdim=True)
86
+ - 2 * torch.matmul(samples, means.t())
87
+ + means.t().pow(2).sum(0, keepdim=True)
88
+ )
89
+
90
+ buckets = dists.max(dim=-1).indices
91
+ del dists
92
+ bins = torch.bincount(buckets, minlength=num_clusters)
93
+ zero_mask = bins == 0
94
+ bins_min_clamped = bins.masked_fill(zero_mask, 1)
95
+
96
+ new_means = buckets.new_zeros(num_clusters, dim, dtype=dtype)
97
+ new_means.scatter_add_(0, repeat(buckets, "n -> n d", d=dim), samples)
98
+ new_means = new_means / bins_min_clamped[..., None]
99
+
100
+ means = torch.where(zero_mask[..., None], means, new_means)
101
+ return means, bins
102
+
103
+
104
+ def preprocess(x):
105
+ x = rearrange(x, "... d -> (...) d")
106
+ return x
107
+
108
+
109
+ def postprocess_emb(embed_ind, shape):
110
+ return embed_ind.view(*shape[:-1])
111
+
112
+
113
+ class EuclideanCodebook(nn.Module):
114
+ """Codebook with Euclidean distance.
115
+ Args:
116
+ dim (int): Dimension.
117
+ codebook_size (int): Codebook size.
118
+ kmeans_init (bool): Whether to use k-means to initialize the codebooks.
119
+ If set to true, run the k-means algorithm on the first training batch and use
120
+ the learned centroids as initialization.
121
+ kmeans_iters (int): Number of iterations used for k-means algorithm at initialization.
122
+ decay (float): Decay for exponential moving average over the codebooks.
123
+ epsilon (float): Epsilon value for numerical stability.
124
+ threshold_ema_dead_code (int): Threshold for dead code expiration. Replace any codes
125
+ that have an exponential moving average cluster size less than the specified threshold with
126
+ randomly selected vector from the current batch.
127
+ """
128
+
129
+ def __init__(
130
+ self,
131
+ dim: int,
132
+ codebook_size: int,
133
+ kmeans_init: int = False,
134
+ kmeans_iters: int = 10,
135
+ decay: float = 0.99,
136
+ epsilon: float = 1e-5,
137
+ threshold_ema_dead_code: float = 2.0,
138
+ ):
139
+ super().__init__()
140
+ self.decay = decay
141
+ self.codebook_size = codebook_size
142
+ self.kmeans_iters = kmeans_iters
143
+ self.epsilon = epsilon
144
+ self.threshold_ema_dead_code = threshold_ema_dead_code
145
+
146
+ self.inited = None
147
+ self.cluster_size = None
148
+ self.embed = None
149
+ self.embed_avg = None
150
+ self.training = True
151
+
152
+ def init_embed_(self, data):
153
+ if self.inited:
154
+ return
155
+
156
+ embed, cluster_size = kmeans(data, self.codebook_size, self.kmeans_iters)
157
+ self.embed.data.copy_(embed)
158
+ self.embed_avg.data.copy_(embed.clone())
159
+ self.cluster_size.data.copy_(cluster_size)
160
+ self.inited.data.copy_(torch.Tensor([True]))
161
+ # Make sure all buffers across workers are in sync after initialization
162
+ # distrib.broadcast_tensors([self.embed, self.embed_avg, self.cluster_size, self.inited])
163
+
164
+ def replace_(self, samples, mask):
165
+ modified_codebook = torch.where(
166
+ mask[..., None], sample_vectors(samples, self.codebook_size), self.embed
167
+ )
168
+ self.embed.data.copy_(modified_codebook)
169
+
170
+ def expire_codes_(self, batch_samples):
171
+ if self.threshold_ema_dead_code == 0:
172
+ return
173
+
174
+ cluster_size = self.cluster_size / sum(self.cluster_size) * self.codebook_size
175
+ expired_codes = cluster_size < self.threshold_ema_dead_code
176
+ if not torch.any(expired_codes):
177
+ return
178
+ else:
179
+ print(f"VQ expire infos: num_expire={sum(expired_codes)}, cluster_size[:5]={cluster_size[:5]}")
180
+
181
+ batch_samples = rearrange(batch_samples, "... d -> (...) d")
182
+ self.replace_(batch_samples, mask=expired_codes)
183
+ # sync buffers outside for efficiency
184
+ # distrib.broadcast_tensors(self.buffers())
185
+
186
+ def quantize(self, x):
187
+ embed = self.embed.t()
188
+ dist = -(
189
+ x.pow(2).sum(1, keepdim=True)
190
+ - 2 * x @ embed
191
+ + embed.pow(2).sum(0, keepdim=True)
192
+ )
193
+ embed_ind = dist.max(dim=-1).indices
194
+ return embed_ind
195
+
196
+ def dequantize(self, embed_ind):
197
+ quantize = F.embedding(embed_ind, self.embed)
198
+ return quantize
199
+
200
+ def encode(self, x, buffers):
201
+ self.inited, self.cluster_size, self.embed, self.embed_avg = buffers
202
+
203
+ shape = x.shape
204
+ # pre-process
205
+ x = preprocess(x)
206
+ # quantize
207
+ embed_ind = self.quantize(x)
208
+ # post-process
209
+ embed_ind = postprocess_emb(embed_ind, shape)
210
+ return embed_ind
211
+
212
+ def decode(self, embed_ind, buffers):
213
+ self.inited, self.cluster_size, self.embed, self.embed_avg = buffers
214
+
215
+ quantize = self.dequantize(embed_ind)
216
+ return quantize
217
+
218
+ def forward(self, x, buffers):
219
+ self.inited, self.cluster_size, self.embed, self.embed_avg = buffers
220
+
221
+ shape, dtype = x.shape, x.dtype
222
+ x = preprocess(x)
223
+
224
+ self.init_embed_(x)
225
+ if self.training:
226
+ # We do the expiry of code at that point as buffers are in sync
227
+ # and all the workers will take the same decision.
228
+ self.expire_codes_(x)
229
+
230
+ embed_ind = self.quantize(x)
231
+ embed_onehot = F.one_hot(embed_ind, self.codebook_size).type(dtype)
232
+ embed_ind = postprocess_emb(embed_ind, shape)
233
+ quantize = self.dequantize(embed_ind)
234
+
235
+ if self.training:
236
+ ema_inplace(self.cluster_size, embed_onehot.sum(0), self.decay)
237
+ embed_sum = x.t() @ embed_onehot
238
+ ema_inplace(self.embed_avg, embed_sum.t(), self.decay)
239
+ cluster_size = (
240
+ laplace_smoothing(self.cluster_size, self.codebook_size, self.epsilon)
241
+ * self.cluster_size.sum()
242
+ )
243
+ embed_normalized = self.embed_avg / cluster_size.unsqueeze(1)
244
+ self.embed.data.copy_(embed_normalized)
245
+ # Note: after ema update, there is a very small difference between codebooks on GPUs.
246
+ # The impact can be very small, ignore it.
247
+
248
+ return quantize, embed_ind
249
+
250
+
251
+ class VectorQuantization(nn.Module):
252
+ """Vector quantization implementation.
253
+ Currently, supports only euclidean distance.
254
+ Args:
255
+ dim (int): Dimension
256
+ codebook_size (int): Codebook size
257
+ codebook_dim (int): Codebook dimension. If not defined, uses the specified dimension in dim.
258
+ decay (float): Decay for exponential moving average over the codebooks.
259
+ epsilon (float): Epsilon value for numerical stability.
260
+ kmeans_init (bool): Whether to use kmeans to initialize the codebooks.
261
+ kmeans_iters (int): Number of iterations used for kmeans initialization.
262
+ threshold_ema_dead_code (int): Threshold for dead code expiration. Replace any codes
263
+ that have an exponential moving average cluster size less than the specified threshold with
264
+ randomly selected vector from the current batch.
265
+ commitment_weight (float): Weight for commitment loss.
266
+ """
267
+ def __init__(
268
+ self,
269
+ dim: int,
270
+ codebook_size: int,
271
+ codebook_dim: tp.Optional[int] = None,
272
+ decay: float = 0.99,
273
+ epsilon: float = 1e-5,
274
+ kmeans_init: bool = True,
275
+ kmeans_iters: int = 50,
276
+ threshold_ema_dead_code: float = 2.0,
277
+ commitment_weight: float = 1.,
278
+ ):
279
+ super().__init__()
280
+ _codebook_dim: int = default(codebook_dim, dim)
281
+
282
+ requires_projection = _codebook_dim != dim
283
+ self.project_in = (nn.Linear(dim, _codebook_dim)) if requires_projection else (nn.Identity())
284
+ self.project_out = (nn.Linear(_codebook_dim, dim)) if requires_projection else (nn.Identity())
285
+
286
+ self.epsilon = epsilon
287
+ self.commitment_weight = commitment_weight
288
+
289
+ self._codebook = EuclideanCodebook(dim=_codebook_dim, codebook_size=codebook_size,
290
+ kmeans_init=kmeans_init, kmeans_iters=kmeans_iters,
291
+ decay=decay, epsilon=epsilon,
292
+ threshold_ema_dead_code=threshold_ema_dead_code)
293
+ self.codebook_size = codebook_size
294
+ self.training = True
295
+
296
+ @property
297
+ def codebook(self):
298
+ return self._codebook.embed
299
+
300
+ def encode(self, x, buffers):
301
+ # x = rearrange(x, "b d n -> b n d")
302
+ x = self.project_in(x)
303
+ embed_in = self._codebook.encode(x, buffers)
304
+ return embed_in
305
+
306
+ def decode(self, embed_ind, buffers):
307
+ quantize = self._codebook.decode(embed_ind, buffers)
308
+ quantize = self.project_out(quantize)
309
+ # quantize = rearrange(quantize, "b n d -> b d n")
310
+ return quantize
311
+
312
+ def forward(self, x, buffers):
313
+ device = x.device
314
+ # x = rearrange(x, "b d n -> b n d")
315
+ x = self.project_in(x)
316
+
317
+ quantize, embed_ind = self._codebook(x, buffers)
318
+
319
+ if self.training:
320
+ quantize = x + (quantize - x).detach()
321
+
322
+ loss = torch.tensor([0.0], device=device, requires_grad=self.training)
323
+
324
+ if self.training:
325
+ if self.commitment_weight > 0:
326
+ commit_loss = F.mse_loss(quantize.detach(), x)
327
+ loss = loss + commit_loss * self.commitment_weight
328
+
329
+ quantize = self.project_out(quantize)
330
+ # quantize = rearrange(quantize, "b n d -> b d n")
331
+ return quantize, embed_ind, loss
332
+
333
+
334
+ class DistributedResidualVectorQuantization(nn.Module):
335
+ """Efficient distributed residual vector quantization implementation.
336
+ Follows Algorithm 1. in https://arxiv.org/pdf/2107.03312.pdf
337
+ """
338
+ def __init__(self, *,
339
+ num_quantizers,
340
+ quantize_dropout: bool = False,
341
+ rand_num_quant: tp.Optional[tp.List] = None,
342
+ **kwargs):
343
+ super().__init__()
344
+ """
345
+ dim: int,
346
+ codebook_size: int,
347
+ codebook_dim: tp.Optional[int] = None,
348
+ """
349
+ codebook_size, codebook_dim = kwargs["codebook_size"], kwargs["codebook_dim"] if kwargs["codebook_dim"] else kwargs["dim"]
350
+ kmeans_init = kwargs["kmeans_init"]
351
+ if isinstance(kmeans_init, bool):
352
+ if not kwargs["kmeans_init"]:
353
+ # use uniform init
354
+ embed = uniform_init(num_quantizers, codebook_size, codebook_dim)
355
+ inited = True
356
+ else:
357
+ # to perform kmeans init on first batch
358
+ embed = torch.zeros(num_quantizers, codebook_size, codebook_dim)
359
+ inited = False
360
+ elif isinstance(kmeans_init, str):
361
+ # use prepared kmeans init
362
+ embed = np.load(kmeans_init)
363
+ embed = torch.from_numpy(embed)
364
+ if embed.dim() == 2:
365
+ embed = embed.unsqueeze(0)
366
+ inited = True
367
+ else:
368
+ raise TypeError("kmeans_init should be either a bool or string path to init weights.")
369
+
370
+ self.register_buffer("inited", torch.Tensor([[inited] for _ in range(num_quantizers)]))
371
+ self.register_buffer("cluster_size", torch.zeros(num_quantizers, codebook_size))
372
+ self.register_buffer("embed", embed)
373
+ self.register_buffer("embed_avg", embed.clone())
374
+
375
+ self.q0_ds_ratio = 1
376
+ if "q0_ds_ratio" in kwargs:
377
+ self.q0_ds_ratio = kwargs.pop("q0_ds_ratio")
378
+
379
+ self.layers = nn.ModuleList()
380
+ for i in range(num_quantizers):
381
+ vq_args = dict(**kwargs)
382
+ vq = VectorQuantization(**vq_args)
383
+ self.layers.append(vq)
384
+
385
+ self.quantize_dropout = quantize_dropout
386
+ self.rand_num_quant = rand_num_quant
387
+
388
+ def forward(self, x, n_q: tp.Optional[int] = None):
389
+ quantized_out = torch.zeros_like(x)
390
+ residual = x
391
+ bb, cc, tt = x.shape
392
+ device = x.device
393
+
394
+ all_losses = []
395
+ all_indices = []
396
+ all_sub_quants = []
397
+ n_q = n_q or len(self.layers)
398
+
399
+ should_quantize_dropout = self.training and self.quantize_dropout and self.rand_num_quant is not None
400
+ if should_quantize_dropout:
401
+ rand_quantize_dropout_index = random.choice(self.rand_num_quant)
402
+
403
+ null_indices_shape = (x.shape[0], x.shape[2])
404
+ null_indices = torch.full(null_indices_shape, -1., device=device, dtype=torch.long)
405
+ null_loss = torch.full((1,), 0., device=device, dtype=x.dtype)
406
+ null_sub_quant = torch.full(x.shape, -1, device=device, dtype=x.dtype)
407
+
408
+ for quantizer_index, layer in enumerate(self.layers[:n_q]):
409
+ # dropout except the first quantizer
410
+ if should_quantize_dropout and quantizer_index >= rand_quantize_dropout_index:
411
+ all_indices.append(null_indices)
412
+ all_losses.append(null_loss)
413
+ all_sub_quants.append(null_sub_quant)
414
+ continue
415
+
416
+ quant_in = residual
417
+ if self.q0_ds_ratio > 1 and quantizer_index == 0:
418
+ quant_in = F.interpolate(quant_in, size=[tt//2])
419
+ quantized, indices, loss = layer(quant_in, [
420
+ self.inited[quantizer_index],
421
+ self.cluster_size[quantizer_index],
422
+ self.embed[quantizer_index],
423
+ self.embed_avg[quantizer_index]
424
+ ])
425
+ if self.q0_ds_ratio > 1 and quantizer_index == 0:
426
+ quantized = F.interpolate(quantized, size=[tt])
427
+ indices = F.interpolate(indices.unsqueeze(1).float(), size=[tt]).squeeze(1).long()
428
+ residual = residual - quantized
429
+ quantized_out = quantized_out + quantized
430
+
431
+ all_indices.append(indices)
432
+ all_losses.append(loss)
433
+ all_sub_quants.append(quantized)
434
+
435
+ # sync buffers after one forward step
436
+ # distrib.broadcast_tensors(self.buffers())
437
+ out_losses, out_indices, out_sub_quants = map(torch.stack, (all_losses, all_indices, all_sub_quants))
438
+
439
+ return quantized_out, out_indices, out_losses
440
+
441
+ def encode(self, x: torch.Tensor, n_q: tp.Optional[int] = None) -> torch.Tensor:
442
+ residual = x
443
+ all_indices = []
444
+ n_q = n_q or len(self.layers)
445
+ for i, layer in enumerate(self.layers[:n_q]):
446
+ indices = layer.encode(residual, [
447
+ self.inited[i],
448
+ self.cluster_size[i],
449
+ self.embed[i],
450
+ self.embed_avg[i]
451
+ ])
452
+ quantized = layer.decode(indices, [
453
+ self.inited[i],
454
+ self.cluster_size[i],
455
+ self.embed[i],
456
+ self.embed_avg[i]
457
+ ])
458
+ residual = residual - quantized
459
+ all_indices.append(indices)
460
+ out_indices = torch.stack(all_indices)
461
+ return out_indices
462
+
463
+ def decode(self, q_indices: torch.Tensor) -> torch.Tensor:
464
+ quantized_out = torch.tensor(0.0, device=q_indices.device)
465
+ for i, indices in enumerate(q_indices):
466
+ layer = self.layers[i]
467
+ quantized = layer.decode(indices, [
468
+ self.inited[i],
469
+ self.cluster_size[i],
470
+ self.embed[i],
471
+ self.embed_avg[i]
472
+ ])
473
+ quantized_out = quantized_out + quantized
474
+ return quantized_out
475
+
476
+
477
+ class DistributedGroupResidualVectorQuantization(nn.Module):
478
+ """Efficient distributed group residual vector quantization implementation.
479
+ Follows Algorithm 1. in https://arxiv.org/abs/2305.02765
480
+ Group Then rvq
481
+ """
482
+ def __init__(self, *,
483
+ num_groups,
484
+ num_quantizers,
485
+ quantize_dropout: bool = False,
486
+ rand_num_quant: tp.Optional[tp.List] = None,
487
+ **kwargs):
488
+ super().__init__()
489
+ self.rvqs = nn.ModuleList(
490
+ [
491
+ DistributedResidualVectorQuantization(
492
+ num_quantizers=num_quantizers,
493
+ quantize_dropout=quantize_dropout,
494
+ rand_num_quant=rand_num_quant,
495
+ **kwargs
496
+ )
497
+ for _ in range(num_groups)
498
+ ]
499
+ )
500
+ self.num_groups = num_groups
501
+
502
+ def forward(self, x, n_q: tp.Optional[int] = None):
503
+ x_lst = torch.chunk(x, chunks=self.num_groups, dim=1)
504
+ all_quantized_out = []
505
+ all_indices = []
506
+ all_losses = []
507
+ for mod, item in zip(self.rvqs, x_lst):
508
+ quantized_out, out_indices, out_losses = mod(item, n_q)
509
+ all_quantized_out.append(quantized_out)
510
+ all_indices.append(out_indices)
511
+ all_losses.append(out_losses)
512
+
513
+ out_losses = torch.stack(all_losses, dim=1).mean(dim=1)
514
+
515
+ return torch.cat(all_quantized_out, dim=1), torch.stack(all_indices, dim=1), out_losses
516
+
517
+ def encode(self, x: torch.Tensor, n_q: tp.Optional[int] = None) -> torch.Tensor:
518
+ x_lst = torch.chunk(x, chunks=self.num_groups, dim=1)
519
+ return torch.stack([mod.encode(item, n_q) for mod, item in zip(self.rvqs, x_lst)], dim=1)
520
+
521
+ def decode(self, q_indices: torch.Tensor) -> torch.Tensor:
522
+ q_indices_lst = torch.chunk(q_indices, chunks=self.num_groups, dim=1)
523
+ return torch.cat([mod.decode(item.squeeze(1)) for mod, item in zip(self.rvqs, q_indices_lst)], dim=1)