Aluode/PerceptionLabPortable
0
1# coding=utf-82# Copyright 2025 The HuggingFace Inc. team. All rights reserved.3#4# Licensed under the Apache License, Version 2.0 (the "License");5# you may not use this file except in compliance with the License.6# You may obtain a copy of the License at7#8# http://www.apache.org/licenses/LICENSE-2.09#10# Unless required by applicable law or agreed to in writing, software11# distributed under the License is distributed on an "AS IS" BASIS,12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.13# See the License for the specific language governing permissions and14# limitations under the License.15"""Transformers Xcodec model."""16 17import math18from dataclasses import dataclass19from typing import Optional, Union20 21import torch22import torch.nn as nn23import torch.nn.functional as F24 25from ...modeling_utils import PreTrainedAudioTokenizerBase26from ...utils import ModelOutput, auto_docstring27from ..auto import AutoModel28from .configuration_xcodec import XcodecConfig29 30 31@dataclass32class XcodecOutput(ModelOutput):33 """34 Args:35 audio_codes (`torch.LongTensor` of shape `(batch_size, num_quantizers, codes_length)`, *optional*):36 Discrete code indices computed using `model.encode`.37 audio_values (`torch.FloatTensor` of shape `(batch_size, channels, num_samples)`, *optional*)38 Decoded audio values obtained using the decoder part of Xcodec.39 """40 41 audio_codes: Optional[torch.LongTensor] = None42 audio_values: Optional[torch.FloatTensor] = None43 44 45@dataclass46class XcodecEncoderOutput(ModelOutput):47 """48 Args:49 audio_codes (`torch.LongTensor` of shape `(batch_size, num_quantizers, codes_length)`, *optional*):50 Discrete code indices computed using `model.encode`.51 """52 53 audio_codes: Optional[torch.LongTensor] = None54 55 56@dataclass57class XcodecDecoderOutput(ModelOutput):58 """59 Args:60 audio_values (`torch.FloatTensor` of shape `(batch_size, channels, num_samples)`, *optional*):61 Decoded audio values obtained using the decoder part of Xcodec.62 """63 64 audio_values: Optional[torch.FloatTensor] = None65 66 67class ResidualUnit(nn.Module):68 """Residual block for SemanticEncoder and SemanticDecoder used in Xcodec."""69 70 def __init__(self, config: XcodecConfig, in_channels: int, out_channels: int, dilation: int):71 super().__init__()72 self.activation = nn.ELU()73 padding = ((config.unit_kernel_size - 1) // 2) * dilation74 self.conv1 = nn.Conv1d(75 in_channels,76 out_channels,77 config.unit_kernel_size,78 stride=1,79 padding=padding,80 dilation=dilation,81 groups=1,82 bias=False,83 )84 self.conv2 = nn.Conv1d(in_channels=out_channels, out_channels=out_channels, kernel_size=1, bias=False)85 86 def forward(self, hidden_state: torch.Tensor) -> torch.Tensor:87 output_tensor = self.activation(hidden_state)88 output_tensor = self.conv1(output_tensor)89 output_tensor = self.activation(output_tensor)90 output_tensor = self.conv2(output_tensor)91 return hidden_state + output_tensor92 93 94class SemanticEncoderBlock(nn.Module):95 def __init__(self, config: XcodecConfig, in_channels: int, out_channels: int, stride: int):96 super().__init__()97 self.res_units = nn.ModuleList(98 [ResidualUnit(config, in_channels, in_channels, dilation) for dilation in config.block_dilations]99 )100 101 # special case: stride=1, do not use kernel=2102 kernel = 3 if stride == 1 else (2 * stride)103 padding = (kernel - 1) // 2104 self.conv = nn.Conv1d(in_channels, out_channels, kernel_size=kernel, stride=stride, padding=padding, bias=True)105 106 def forward(self, hidden_state: torch.Tensor) -> torch.Tensor:107 for unit in self.res_units:108 hidden_state = unit(hidden_state)109 hidden_state = self.conv(hidden_state)110 return hidden_state111 112 113class SemanticEncoder(nn.Module):114 def __init__(self, config):115 super().__init__()116 if len(config.strides) != len(config.channel_ratios):117 raise ValueError("Number of strides must match the number of channel_ratios.")118 self.conv = nn.Conv1d(119 config.semantic_hidden_size,120 config.semantic_hidden_size,121 config.kernel_size,122 1,123 config.kernel_size // 2,124 bias=False,125 )126 127 in_channels = config.semantic_hidden_size128 conv_blocks = []129 for i, stride in enumerate(config.strides):130 out_channels = int(config.semantic_hidden_size * config.channel_ratios[i])131 conv_blocks += [SemanticEncoderBlock(config, in_channels, out_channels, stride)]132 in_channels = out_channels133 134 self.conv_blocks = nn.ModuleList(conv_blocks)135 136 def forward(self, hidden_state: torch.Tensor) -> torch.Tensor:137 hidden_state = self.conv(hidden_state)138 for block in self.conv_blocks:139 hidden_state = block(hidden_state)140 return hidden_state141 142 143class SemanticDecoderBlock(nn.Module):144 def __init__(self, config: XcodecConfig, in_channels: int, out_channels: int, stride: int):145 super().__init__()146 if stride == 1:147 self.conv = nn.Conv1d(148 in_channels,149 out_channels,150 kernel_size=3,151 stride=1,152 padding=1,153 bias=True,154 )155 else:156 kernel_size = 2 * stride157 padding = (stride + 1) // 2158 output_padding = 1 if stride % 2 == 1 else 0159 self.conv = nn.ConvTranspose1d(160 in_channels, out_channels, kernel_size, stride, padding, output_padding, bias=False161 )162 163 self.res_units = nn.ModuleList(164 [ResidualUnit(config, out_channels, out_channels, dilation) for dilation in config.block_dilations]165 )166 167 def forward(self, hidden_state: torch.Tensor) -> torch.Tensor:168 hidden_state = self.conv(hidden_state)169 for unit in self.res_units:170 hidden_state = unit(hidden_state)171 return hidden_state172 173 174class SemanticDecoder(nn.Module):175 def __init__(self, config):176 super().__init__()177 self.conv1 = nn.Conv1d(178 in_channels=config.semantic_hidden_size,179 out_channels=int(config.semantic_hidden_size * config.channel_ratios[0]),180 kernel_size=config.kernel_size,181 stride=1,182 padding=config.kernel_size // 2,183 bias=False,184 )185 conv_blocks = []186 for i, stride in enumerate(config.strides):187 in_channels = int(config.semantic_hidden_size * config.channel_ratios[i])188 189 if i < (len(config.channel_ratios) - 1):190 out_channels = int(config.semantic_hidden_size * config.channel_ratios[i + 1])191 else:192 out_channels = config.semantic_hidden_size193 194 conv_blocks += [SemanticDecoderBlock(config, in_channels, out_channels, stride)]195 196 self.conv_blocks = nn.ModuleList(conv_blocks)197 self.conv2 = nn.Conv1d(198 config.semantic_hidden_size,199 config.semantic_hidden_size,200 config.kernel_size,201 stride=1,202 padding=config.kernel_size // 2,203 bias=False,204 )205 206 def forward(self, hidden_state: torch.Tensor) -> torch.Tensor:207 hidden_state = self.conv1(hidden_state)208 for block in self.conv_blocks:209 hidden_state = block(hidden_state)210 hidden_state = self.conv2(hidden_state)211 return hidden_state212 213 214class XcodecEuclideanCodebook(nn.Module):215 """Codebook with Euclidean distance."""216 217 def __init__(self, config):218 super().__init__()219 embed = torch.zeros(config.codebook_size, config.codebook_dim)220 self.codebook_size = config.codebook_size221 self.register_buffer("inited", torch.Tensor([True]))222 self.register_buffer("cluster_size", torch.zeros(config.codebook_size))223 self.register_buffer("embed", embed)224 self.register_buffer("embed_avg", embed.clone())225 226 # Copied from transformers.models.encodec.modeling_encodec.EncodecEuclideanCodebook.quantize227 def quantize(self, hidden_states):228 embed = self.embed.t()229 scaled_states = hidden_states.pow(2).sum(1, keepdim=True)230 dist = -(scaled_states - 2 * hidden_states @ embed + embed.pow(2).sum(0, keepdim=True))231 embed_ind = dist.max(dim=-1).indices232 return embed_ind233 234 def encode(self, hidden_states):235 shape = hidden_states.shape236 hidden_states = hidden_states.reshape((-1, shape[-1]))237 embed_ind = self.quantize(hidden_states)238 embed_ind = embed_ind.view(*shape[:-1])239 return embed_ind240 241 def decode(self, embed_ind):242 quantized = F.embedding(embed_ind, self.embed)243 return quantized244 245 246class XcodecVectorQuantization(nn.Module):247 """248 Vector quantization implementation. Currently supports only euclidean distance.249 """250 251 def __init__(self, config: XcodecConfig):252 super().__init__()253 self.codebook = XcodecEuclideanCodebook(config)254 255 # Copied from transformers.models.encodec.modeling_encodec.EncodecVectorQuantization.encode256 def encode(self, hidden_states):257 hidden_states = hidden_states.permute(0, 2, 1)258 embed_in = self.codebook.encode(hidden_states)259 return embed_in260 261 # Copied from transformers.models.encodec.modeling_encodec.EncodecVectorQuantization.decode262 def decode(self, embed_ind):263 quantize = self.codebook.decode(embed_ind)264 quantize = quantize.permute(0, 2, 1)265 return quantize266 267 268class XcodecResidualVectorQuantization(nn.Module):269 """270 Residual vector quantization implementation. Follows Algorithm 1 in https://huggingface.co/papers/2107.03312271 """272 273 def __init__(self, config: XcodecConfig):274 super().__init__()275 self.quantizers = nn.ModuleList([XcodecVectorQuantization(config) for _ in range(config.num_quantizers)])276 self.frame_rate = config.frame_rate277 self.codebook_size = config.codebook_size278 self.num_quantizers = config.num_quantizers279 280 def get_bandwidth_per_quantizer(self):281 """Return bandwidth per quantizer."""282 return math.log2(self.codebook_size) * self.frame_rate / 1000283 284 def get_num_quantizers_for_bandwidth(self, bandwidth=None) -> int:285 """Return num_quantizers based on specified target bandwidth."""286 bw_per_q = self.get_bandwidth_per_quantizer()287 num_quantizers = self.num_quantizers288 if bandwidth is not None and bandwidth > 0.0:289 num_quantizers = int(max(1, math.floor(bandwidth / bw_per_q)))290 return num_quantizers291 292 def encode(self, embeddings: torch.Tensor, bandwidth=None) -> torch.Tensor:293 """294 Encode the input tensor into discrete indices using RVQ, with the number of quantizers selected based on the given bandwidth.295 Each quantizer /codebook residually quantizes the input and returns the nearest indices in terms of Euclidian distance.296 """297 num_quantizers = self.get_num_quantizers_for_bandwidth(bandwidth)298 residual = embeddings299 all_indices = []300 for quantizer in self.quantizers[:num_quantizers]:301 indices = quantizer.encode(residual)302 quantized = quantizer.decode(indices)303 residual = residual - quantized304 all_indices.append(indices)305 out_indices = torch.stack(all_indices)306 return out_indices307 308 def decode(self, codes: torch.Tensor) -> torch.Tensor:309 """Decode the given codes to their quantized representation."""310 quantized_out = torch.tensor(0.0, device=codes.device)311 for i, indices in enumerate(codes):312 quantizer = self.quantizers[i]313 quantized = quantizer.decode(indices)314 quantized_out = quantized_out + quantized315 return quantized_out316 317 318@auto_docstring319class XcodecPreTrainedModel(PreTrainedAudioTokenizerBase):320 """321 An abstract class to handle weights initialization and a simple interface for downloading and loading pretrained322 models.323 """324 325 config_class = XcodecConfig326 base_model_prefix = "xcodec"327 main_input_name = "input_values"328 329 def _init_weights(self, module):330 """Initialize the weights"""331 if isinstance(module, nn.Linear):332 module.weight.data.normal_(mean=0.0, std=self.config.initializer_range)333 if module.bias is not None:334 module.bias.data.zero_()335 elif isinstance(module, (nn.LayerNorm, nn.GroupNorm)):336 module.bias.data.zero_()337 module.weight.data.fill_(1.0)338 elif isinstance(module, nn.Conv1d):339 nn.init.kaiming_normal_(module.weight)340 if module.bias is not None:341 k = math.sqrt(module.groups / (module.in_channels * module.kernel_size[0]))342 nn.init.uniform_(module.bias, a=-k, b=k)343 elif module.__class__.__name__ == "Snake1d":344 module.alpha.data.fill_(1.0)345 elif isinstance(module, nn.ConvTranspose1d):346 module.reset_parameters()347 elif isinstance(module, nn.Embedding):348 module.weight.data.normal_(mean=0.0, std=0.02)349 elif isinstance(module, XcodecModel):350 # The conv1d are not handled correctly, as `self.acoustic_encoder/decoder` are initialized from a PreTrainedModel,351 # but then only the submodules are used (which are not PreTrainedModels...) -> here we reinit them as in DacModel352 for submodule in module.acoustic_encoder.modules():353 if isinstance(submodule, nn.Conv1d):354 nn.init.trunc_normal_(submodule.weight, std=0.02)355 nn.init.constant_(submodule.bias, 0)356 for submodule in module.acoustic_decoder.modules():357 if isinstance(submodule, nn.Conv1d):358 nn.init.trunc_normal_(submodule.weight, std=0.02)359 nn.init.constant_(submodule.bias, 0)360 361 def apply_weight_norm(self):362 """Apply weight norm in the acoustic encoder and decoder because the original checkpoint has weight norm applied."""363 weight_norm = torch.nn.utils.weight_norm364 if hasattr(torch.nn.utils.parametrizations, "weight_norm"):365 weight_norm = torch.nn.utils.parametrizations.weight_norm366 367 weight_norm(self.acoustic_encoder.conv1)368 weight_norm(self.acoustic_encoder.conv2)369 370 for block in self.acoustic_encoder.block:371 weight_norm(block.conv1)372 for res_unit in (block.res_unit1, block.res_unit2, block.res_unit3):373 weight_norm(res_unit.conv1)374 weight_norm(res_unit.conv2)375 376 weight_norm(self.acoustic_decoder.conv1, name="weight")377 weight_norm(self.acoustic_decoder.conv2, name="weight")378 379 for block in self.acoustic_decoder.block:380 weight_norm(block.conv_t1, name="weight")381 for res_unit in (block.res_unit1, block.res_unit2, block.res_unit3):382 weight_norm(res_unit.conv1, name="weight")383 weight_norm(res_unit.conv2, name="weight")384 385 def remove_weight_norm(self):386 """Remove the weight norm from the acoustic encoder and decoder."""387 for module in (self.acoustic_encoder, self.acoustic_decoder):388 for m in module.modules():389 try:390 torch.nn.utils.remove_weight_norm(m, name="weight")391 except (ValueError, AttributeError):392 pass393 if hasattr(m, "parametrizations") and "weight" in m.parametrizations:394 torch.nn.utils.parametrize.remove_parametrizations(m, "weight", leave_parametrized=True)395 396 397@auto_docstring(custom_intro="""The Xcodec neural audio codec model.""")398class XcodecModel(XcodecPreTrainedModel):399 def __init__(self, config):400 super().__init__(config)401 self.config = config402 self.pad = config.hop_length // 2403 acoustic_model = AutoModel.from_config(config.acoustic_model_config)404 self.acoustic_encoder = acoustic_model.encoder405 self.acoustic_decoder = acoustic_model.decoder406 self._adjust_dac_decoder(self.acoustic_decoder)407 self.encoder_semantic = SemanticEncoder(config)408 self.decoder_semantic = SemanticDecoder(config)409 self.semantic_model = AutoModel.from_config(config.semantic_model_config).eval()410 self.fc = nn.Linear(config.hidden_size, config.hidden_size)411 self.fc1 = nn.Linear(config.hidden_size, config.semantic_model_config.hidden_size)412 self.fc2 = nn.Linear(config.hidden_size, config.acoustic_model_config.hidden_size)413 self.quantizer = XcodecResidualVectorQuantization(config)414 415 # Initialize weights and apply final processing416 self.post_init()417 418 @staticmethod419 def _adjust_dac_decoder(decoder: nn.Module):420 r"""421 DAC implemented in Xcodec is slightly different from the HF version.422 DAC in Xcodec adjusts the output padding in every ConvTranspose1d in the decoder and removes423 the final `nn.Tanh` activation function.424 """425 for module in decoder.modules():426 if isinstance(module, nn.ConvTranspose1d):427 stride = module.stride[0] if isinstance(module.stride, tuple) else module.stride428 module.output_padding = (stride % 2,)429 if hasattr(decoder, "tanh") and isinstance(decoder.tanh, nn.Tanh):430 decoder.tanh = nn.Identity()431 432 def _extract_semantic_features(self, input_values: torch.FloatTensor) -> torch.FloatTensor:433 input_values = input_values[:, 0, :]434 input_values = F.pad(input_values, (self.pad, self.pad))435 with torch.no_grad():436 outputs = self.semantic_model(input_values, output_hidden_states=True)437 hidden_states = outputs.hidden_states438 439 stacked = torch.stack(hidden_states, dim=1)440 return stacked.mean(dim=1)441 442 @auto_docstring443 def encode(444 self,445 input_values: torch.Tensor,446 bandwidth: Optional[float] = None,447 return_dict: Optional[bool] = None,448 ) -> Union[torch.Tensor, XcodecEncoderOutput]:449 r"""450 input_values (`torch.FloatTensor` of shape `(batch_size, channels, num_samples)`):451 Float values of the input audio waveform.452 bandwidth (`float`, *optional*):453 The target bandwidth in (kbps) supports only values in `config.target_bandwidths`.454 Defaults to the highest available bandwidth `4.0` kbps.455 return_dict (`bool`, *optional*):456 Whether or not to return a [`~utils.ModelOutput`].457 458 Returns:459 `torch.LongTensor` of shape `(batch_size, num_quantizers, codes_length)` containing the discrete encoded audio codes.460 """461 return_dict = return_dict if return_dict is not None else self.config.return_dict462 463 channels = input_values.shape[1]464 if channels != 1:465 raise ValueError(f"Audio must be mono, but got {channels}")466 467 if bandwidth is None:468 bandwidth = self.config.target_bandwidths[-1]469 elif bandwidth not in self.config.target_bandwidths:470 raise ValueError(471 f"This model doesn't support the bandwidth {bandwidth}. Select one of {self.config.target_bandwidths}."472 )473 474 e_semantic_input = self._extract_semantic_features(input_values).detach()475 e_semantic = self.encoder_semantic(e_semantic_input.transpose(1, 2))476 e_acoustic = self.acoustic_encoder(input_values)477 478 if e_acoustic.shape[2] != e_semantic.shape[2]:479 # make sure they line up if frames don't match480 e_acoustic = self.acoustic_encoder(F.pad(input_values[:, 0, :], (self.pad, self.pad)).unsqueeze(1))481 482 embeddings = torch.cat([e_acoustic, e_semantic], dim=1)483 embeddings = self.fc(embeddings.transpose(1, 2)).transpose(1, 2)484 audio_codes = self.quantizer.encode(embeddings, bandwidth)485 audio_codes = audio_codes.transpose(0, 1)486 487 if not return_dict:488 return audio_codes489 490 return XcodecEncoderOutput(audio_codes)491 492 @auto_docstring493 def decode(494 self,495 audio_codes: torch.Tensor,496 return_dict: Optional[bool] = None,497 ) -> Union[torch.Tensor, XcodecDecoderOutput]:498 r"""499 audio_codes (`torch.LongTensor` of shape `(batch_size, num_quantizers, codes_length)`):500 Discrete code indices computed using `model.encode`.501 return_dict (`bool`, *optional*):502 Whether or not to return a [`~utils.ModelOutput`]503 504 Returns:505 Decoded audio values of shape `(batch_size, channels, num_samples)` obtained using the decoder part of506 Xcodec.507 """508 return_dict = return_dict if return_dict is not None else self.config.return_dict509 510 audio_codes = audio_codes.transpose(0, 1)511 quantized = self.quantizer.decode(audio_codes)512 quantized_acoustic = self.fc2(quantized.transpose(1, 2)).transpose(1, 2)513 audio_values = self.acoustic_decoder(quantized_acoustic)514 515 if not return_dict:516 return audio_values517 518 return XcodecDecoderOutput(audio_values)519 520 @auto_docstring521 def forward(522 self,523 input_values: torch.Tensor,524 audio_codes: Optional[torch.Tensor] = None,525 bandwidth: Optional[float] = None,526 return_dict: Optional[bool] = None,527 ) -> Union[tuple[torch.Tensor, torch.Tensor], XcodecOutput]:528 r"""529 input_values (`torch.FloatTensor` of shape `(batch_size, channels, num_samples)`):530 The raw float values of the input audio waveform.531 audio_codes (`torch.LongTensor` of shape `(batch_size, num_quantizers, codes_length)`:532 Discrete code indices computed using `model.encode`.533 bandwidth (`float`, *optional*):534 Target bandwidth in kbps. Must be one of `config.target_bandwidths`. Defaults to the highest available bandwidth.535 bandwidth (`float`, *optional*):536 Target bandwidth in kbps. Must be one of `config.target_bandwidths`. Defaults to the highest available bandwidth.537 return_dict (`bool`, *optional*):538 Whether to return a [`XcodecOutput`] instead of a plain tuple.539 540 Returns:541 `XcodecOutput` or tuple `(audio_codes, audio_values)`:542 - `audio_codes` of shape `(batch_size, num_quantizers, codes_length)`: the quantized discrete codes.543 - `audio_values` of shape `(batch_size, channels, num_samples)`: the reconstructed audio waveform given the codes.544 545 Example:546 547 ```python548 >>> from datasets import load_dataset549 >>> from transformers import AutoFeatureExtractor, XcodecModel550 551 >>> model_id = "hf-audio/xcodec-hubert-librispeech"552 >>> model = XcodecModel.from_pretrained(model_id)553 >>> feature_extractor = AutoFeatureExtractor.from_pretrained(model_id)554 555 >>> dataset = load_dataset("hf-internal-testing/librispeech_asr_dummy", "clean", split="validation")556 >>> dataset = dataset.cast_column("audio", Audio(sampling_rate=feature_extractor.sampling_rate))557 >>> audio_sample = dataset[0]['audio']['array']558 559 >>> inputs = feature_extractor(raw_audio=audio_sample, return_tensors="pt")560 561 >>> outputs = model(**inputs)562 >>> audio_codes = outputs.audio_codes563 >>> audio_values = outputs.audio_values564 ```565 """566 return_dict = return_dict if return_dict is not None else self.config.return_dict567 length = input_values.shape[-1]568 569 if audio_codes is None:570 audio_codes = self.encode(input_values, bandwidth, return_dict=False)571 572 audio_values = self.decode(audio_codes, return_dict=return_dict)[0][..., :length]573 574 if not return_dict:575 return (audio_codes, audio_values)576 577 return XcodecOutput(audio_codes=audio_codes, audio_values=audio_values)578 579 580__all__ = ["XcodecModel", "XcodecPreTrainedModel"]581 