Feature Extraction
Transformers
Safetensors
audio_embeddings
audio
custom_code
self-supervised-learning
audio-embeddings
best-rq-2
audioset
Instructions to use ltuncay/BEST-RQ-2 with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use ltuncay/BEST-RQ-2 with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("feature-extraction", model="ltuncay/BEST-RQ-2", trust_remote_code=True)# Load model directly from transformers import AutoModel model = AutoModel.from_pretrained("ltuncay/BEST-RQ-2", trust_remote_code=True, device_map="auto") - Notebooks
- Google Colab
- Kaggle
| # MIT License | |
| # | |
| # Copyright (c) 2026 audio-embeddings contributors | |
| # | |
| # Permission is hereby granted, free of charge, to any person obtaining a copy | |
| # of this software and associated documentation files (the "Software"), to deal | |
| # in the Software without restriction, including without limitation the rights | |
| # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| # copies of the Software, and to permit persons to whom the Software is | |
| # furnished to do so, subject to the following conditions: | |
| # | |
| # The above copyright notice and this permission notice shall be included in all | |
| # copies or substantial portions of the Software. | |
| # | |
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
| # SOFTWARE. | |
| """Trainable audio embeddings with the same extraction policy as HEAR.""" | |
| from __future__ import annotations | |
| from dataclasses import dataclass | |
| from pathlib import Path | |
| from typing import Any | |
| import torch | |
| from torch.nn.utils.rnn import pad_sequence | |
| from transformers import PreTrainedModel | |
| from transformers.utils import ModelOutput | |
| from .adapters import SpectrogramPatchAdapter, WaveformConvAdapter | |
| from .adapters import resolve_adapter_spec | |
| from .configuration_audio import AudioEmbeddingConfig | |
| class AudioEmbeddingOutput(ModelOutput): | |
| last_hidden_state: torch.Tensor | None = None | |
| pooler_output: torch.Tensor | None = None | |
| attention_mask: torch.Tensor | None = None | |
| timestamps_ms: torch.Tensor | None = None | |
| class AudioEmbeddingModel(PreTrainedModel): | |
| config_class = AudioEmbeddingConfig | |
| base_model_prefix = "adapter" | |
| main_input_name = "input_values" | |
| # RoPE modules are shared by every attention block. Save all buffer keys, | |
| # so loading does not need special tied-buffer handling. | |
| _supports_assign_param_buffer = False | |
| def __init__(self, config: AudioEmbeddingConfig) -> None: | |
| super().__init__(config) | |
| adapter_config = config.to_adapter_config() | |
| # Transformers 5 loads under a default meta device, but torchaudio's | |
| # filter-bank constructors need real values. Build on CPU; HF subsequently | |
| # loads the checkpoint tensors onto the requested device/dtype. | |
| with torch.device("cpu"): | |
| spec = resolve_adapter_spec(adapter_config) | |
| adapter_type = ( | |
| SpectrogramPatchAdapter | |
| if spec.adapter_key == "spectrogram_patch" | |
| else WaveformConvAdapter | |
| ) | |
| self.adapter = adapter_type(adapter_config, spec) | |
| self.post_init() | |
| def _init_weights(self, module: torch.nn.Module) -> None: | |
| """Preserve initialization performed by the research components themselves.""" | |
| def save_pretrained( | |
| self, save_directory: str | Path, *args: Any, **kwargs: Any | |
| ) -> None: | |
| if kwargs.get("state_dict") is None: | |
| kwargs["state_dict"] = { | |
| key: value.detach().clone().contiguous() | |
| for key, value in self.state_dict().items() | |
| } | |
| return super().save_pretrained(save_directory, *args, **kwargs) | |
| def forward( | |
| self, | |
| input_values: torch.Tensor, | |
| attention_mask: torch.Tensor | None = None, | |
| return_dict: bool | None = None, | |
| ) -> AudioEmbeddingOutput | tuple[torch.Tensor, ...]: | |
| if input_values.ndim != 2 or min(input_values.shape) <= 0: | |
| raise ValueError( | |
| "input_values must have shape [batch, samples] with nonempty axes" | |
| ) | |
| if ( | |
| not input_values.is_floating_point() | |
| or not torch.isfinite(input_values).all() | |
| ): | |
| raise ValueError( | |
| "input_values must contain finite floating-point waveforms" | |
| ) | |
| if attention_mask is None: | |
| lengths = [input_values.shape[1]] * input_values.shape[0] | |
| else: | |
| if attention_mask.shape != input_values.shape: | |
| raise ValueError( | |
| "attention_mask must have the same shape as input_values" | |
| ) | |
| if not torch.all((attention_mask == 0) | (attention_mask == 1)): | |
| raise ValueError("attention_mask must contain only zeros and ones") | |
| lengths_tensor = attention_mask.long().sum(dim=1) | |
| expected = ( | |
| torch.arange(input_values.shape[1], device=attention_mask.device)[None] | |
| < lengths_tensor[:, None] | |
| ) | |
| if not torch.equal(attention_mask.bool(), expected) or torch.any( | |
| lengths_tensor == 0 | |
| ): | |
| raise ValueError( | |
| "attention_mask must describe nonempty, right-padded waveforms" | |
| ) | |
| lengths = lengths_tensor.tolist() | |
| # Clear non-buffer RoPE caches between calls: inference-mode caches cannot | |
| # be reused for autograd, and .to(device/dtype) does not move these caches. | |
| rope = self.adapter.encoder.rope | |
| if rope is not None: | |
| for name in ("cached_cos_sin", "cached_cos_sin_h", "cached_cos_sin_w"): | |
| if hasattr(rope, name): | |
| setattr(rope, name, None) | |
| outputs = [] | |
| for waveform, length in zip(input_values, lengths): | |
| if isinstance(self.adapter, SpectrogramPatchAdapter): | |
| minimum = self.adapter.spectrogram.mel_spec.n_fft // 2 + 1 | |
| if length < minimum: | |
| raise ValueError( | |
| f"Audio requires at least {minimum} samples for this spectrogram; got {length}" | |
| ) | |
| outputs.append( | |
| self.adapter.extract( | |
| waveform[:length], preset_name=self.config.extraction_preset | |
| ) | |
| ) | |
| hidden = pad_sequence( | |
| [item.timestamp_embeddings for item in outputs], batch_first=True | |
| ) | |
| frame_lengths = torch.tensor( | |
| [item.timestamp_embeddings.shape[0] for item in outputs], | |
| device=hidden.device, | |
| ) | |
| frame_mask = ( | |
| torch.arange(hidden.shape[1], device=hidden.device)[None] | |
| < frame_lengths[:, None] | |
| ) | |
| result = AudioEmbeddingOutput( | |
| last_hidden_state=hidden, | |
| pooler_output=torch.stack([item.scene_embedding for item in outputs]), | |
| attention_mask=frame_mask.long(), | |
| timestamps_ms=pad_sequence( | |
| [item.timestamps_ms for item in outputs], | |
| batch_first=True, | |
| padding_value=-1.0, | |
| ), | |
| ) | |
| return ( | |
| result | |
| if (self.config.return_dict if return_dict is None else return_dict) | |
| else result.to_tuple() | |
| ) | |
| AudioEmbeddingModel.register_for_auto_class("AutoModel") | |
| from .adapters import __name__ as _bundled_adapters # noqa: F401 | |
| from .extraction import __name__ as _bundled_extraction # noqa: F401 | |
| from .patch_embed import __name__ as _bundled_patch_embed # noqa: F401 | |
| from .spectrogram import __name__ as _bundled_spectrogram # noqa: F401 | |
| from .vit import __name__ as _bundled_vit # noqa: F401 | |
| from .rope import __name__ as _bundled_rope # noqa: F401 | |
| from .transformer import __name__ as _bundled_transformer # noqa: F401 | |
| from .normalization import __name__ as _bundled_normalization # noqa: F401 | |
| from .waveform_feature_encoder import __name__ as _bundled_waveform_feature_encoder # noqa: F401 | |