Files
wehub-resource-sync 94057c3d3e
PR Test (NPU) / check-changes (push) Has been cancelled
PR Test (NPU) / pr-gate (push) Has been cancelled
PR Test (NPU) / set-image-config (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-4-npu-a3 (push) Has been cancelled
PR Test (NPU) / stage-b-test-16-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-1-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-2-npu-a3 (push) Has been cancelled
PR Test (Arm64) / pr-gate (push) Has been cancelled
PR Test (Arm64) / check-changes (push) Has been cancelled
PR Test (Arm64) / build-test (push) Has been cancelled
PR Test (sgl-router) / gate (push) Has been cancelled
PR Test (sgl-router) / tier-1 — lint (push) Has been cancelled
PR Test (sgl-router) / tier-2 — build + test (push) Has been cancelled
PR Test (sgl-router) / tier-3 — docker (placeholder) (push) Has been cancelled
PR Test (sgl-router) / tier-3 — k8s integration (push) Has been cancelled
PR Test (sgl-router) / tier-3 — e2e (push) Has been cancelled
PR Test (sgl-router) / finish (push) Has been cancelled
PR Test (NPU) / single-node-poc (map[name:qwen3_6_27b_w8a8_1p_in64k_out1k_50ms runner:linux-aarch64-a3-2 test_case:test/registered/ascend/performance/qwen3_6_27b/test_npu_qwen3_6_27b_w8a8_1p_in64k_out1k_50ms.py test_type:perf]) (push) Has been cancelled
PR Test (NPU) / pr-test-npu-finish (push) Has been cancelled
PR Test (Xeon) / pr-gate (push) Has been cancelled
PR Test (Xeon) / check-changes (push) Has been cancelled
PR Test (Xeon) / build-test (, xeon-gnr, base-b-test-cpu) (push) Has been cancelled
PR Test (XPU) / check-changes (push) Has been cancelled
PR Test (XPU) / pr-gate (push) Has been cancelled
PR Test (XPU) / stage-a-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / wait-for-stage-a (push) Has been cancelled
PR Test (XPU) / stage-b-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / finish (push) Has been cancelled
CI Model Inventory / build-inventory (push) Has been cancelled
Lint / lint (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Compilation Check (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Manual Policy (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Request Processing (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Summary (push) Has been cancelled
PR Test (SMG) / build-wheel (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on windows (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (x86_64 - auto) (push) Has been cancelled
PR Test (SMG) / python-unit-tests (push) Has been cancelled
PR Test (SMG) / unit-tests (push) Has been cancelled
PR Test (SMG) / benchmarks (push) Has been cancelled
PR Test (SMG) / chat-completions (push) Has been cancelled
PR Test (SMG) / chat-completions-4gpu (push) Has been cancelled
PR Test (SMG) / e2e (push) Has been cancelled
PR Test (SMG) / docker-build-test (push) Has been cancelled
PR Test (SMG) / k8s-integration (push) Has been cancelled
PR Test (SMG) / finish (push) Has been cancelled
PR Test (SMG) / summarize-benchmarks (push) Has been cancelled
Release SGLang Model Gateway Docker Image / publish (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Build SDist (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Upload to PyPI (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (aarch64, 12.9, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (x86_64, 12.9, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu129 (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (aarch64, 13.0, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (x86_64, 13.0, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu130 (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 700) (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 720) (push) Has been cancelled
Release SGLang Kernels / release-rocm700 (push) Has been cancelled
Release SGLang Kernels / release-rocm720 (push) Has been cancelled
Release SGLang Kernels / build-musa43 (43, 3.10) (push) Has been cancelled
Release SGLang Kernels / release-musa43 (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:38:16 +08:00

1358 lines
50 KiB
Python

"""MiMo audio: tokenizer, encoding utilities, and audio encoder."""
# Audio tokenizer adapted from https://github.com/XiaomiMiMo/MiMo-Audio-Tokenizer.git
import logging
import math
import os
import typing as tp
from dataclasses import dataclass
from functools import wraps
from typing import Optional, Tuple
import torch
import torch.nn as nn
import torch.nn.functional as F
from einops import rearrange
from transformers.activations import ACT2FN
from transformers.configuration_utils import PretrainedConfig
from transformers.modeling_utils import PreTrainedModel
from transformers.models.qwen2.configuration_qwen2 import Qwen2Config
from transformers.models.qwen2.modeling_qwen2 import Qwen2Model
from sglang.srt.layers.quantization.base_config import QuantizationConfig
from sglang.srt.runtime_context import get_server_args
from sglang.srt.utils import is_cuda
if is_cuda():
from sgl_kernel.flash_attn import flash_attn_varlen_func
else:
def flash_attn_varlen_func(*args, **kwargs):
raise RuntimeError("MiMoAudioTokenizer requires CUDA to run.")
logger = logging.getLogger(__name__)
def _compute_default_rope_parameters(
config=None, device=None, seq_len=None, **rope_kwargs
):
if config is not None and len(rope_kwargs) > 0:
raise ValueError(
"Unexpected arguments: `**rope_kwargs` and `config` are mutually exclusive"
)
if len(rope_kwargs) > 0:
base = rope_kwargs["base"]
dim = rope_kwargs["dim"]
elif config is not None:
base = config.rope_theta
partial_rotary_factor = (
config.partial_rotary_factor
if hasattr(config, "partial_rotary_factor")
else 1.0
)
head_dim = getattr(config, "head_dim", None)
if head_dim is None:
head_dim = config.hidden_size // config.num_attention_heads
logger.info(
"audio.head_dim not set; defaulting to hidden_size/num_heads = %d",
head_dim,
)
dim = int(head_dim * partial_rotary_factor)
attention_factor = 1.0
inv_freq = 1.0 / (
base
** (
torch.arange(0, dim, 2, dtype=torch.int64).to(
device=device, dtype=torch.float
)
/ dim
)
)
return inv_freq, attention_factor
_ROPE_INIT_FUNCTIONS = {
"default": _compute_default_rope_parameters,
}
def _dynamic_rope_update(rope_forward):
def longrope_frequency_update(self, position_ids, device):
seq_len = torch.max(position_ids) + 1
if hasattr(self.config, "original_max_position_embeddings"):
original_max_position_embeddings = (
self.config.original_max_position_embeddings
)
else:
original_max_position_embeddings = self.config.max_position_embeddings
if seq_len > original_max_position_embeddings:
if not hasattr(self, "long_inv_freq"):
self.long_inv_freq, _ = self.rope_init_fn(
self.config, device, seq_len=original_max_position_embeddings + 1
)
self.register_buffer("inv_freq", self.long_inv_freq, persistent=False)
else:
self.original_inv_freq = self.original_inv_freq.to(device)
self.register_buffer("inv_freq", self.original_inv_freq, persistent=False)
def dynamic_frequency_update(self, position_ids, device):
seq_len = torch.max(position_ids) + 1
if seq_len > self.max_seq_len_cached: # growth
inv_freq, self.attention_scaling = self.rope_init_fn(
self.config, device, seq_len=seq_len
)
self.register_buffer("inv_freq", inv_freq, persistent=False)
self.max_seq_len_cached = seq_len
if (
seq_len < self.original_max_seq_len
and self.max_seq_len_cached > self.original_max_seq_len
):
self.original_inv_freq = self.original_inv_freq.to(device)
self.register_buffer("inv_freq", self.original_inv_freq, persistent=False)
self.max_seq_len_cached = self.original_max_seq_len
@wraps(rope_forward)
def wrapper(self, x, position_ids):
if "dynamic" in self.rope_type:
dynamic_frequency_update(self, position_ids, device=x.device)
elif self.rope_type == "longrope":
longrope_frequency_update(self, position_ids, device=x.device)
return rope_forward(self, x, position_ids)
return wrapper
class AudioRotaryEmbedding(nn.Module):
def __init__(self, base, dim, max_seq_len, rope_type="default", device=None):
super().__init__()
self.max_seq_len = max_seq_len
self.rope_type = rope_type
self.rope_init_fn = _ROPE_INIT_FUNCTIONS[self.rope_type]
inv_freq, self.attention_scaling = self.rope_init_fn(
device=device, base=base, dim=dim
)
self.register_buffer("inv_freq", inv_freq, persistent=False)
self.original_inv_freq = self.inv_freq
@torch.no_grad()
@_dynamic_rope_update
def forward(self, x, position_ids):
inv_freq_expanded = self.inv_freq[:, None].float().expand(-1, 1).to(x.device)
position_ids_expanded = position_ids[None, :].float()
device_type = (
x.device.type
if isinstance(x.device.type, str) and x.device.type != "mps"
else "cpu"
)
with torch.autocast(device_type=device_type, enabled=False): # Force float32
freqs = (
inv_freq_expanded.float() @ position_ids_expanded.float()
).transpose(0, 1)
emb = torch.cat((freqs, freqs), dim=-1)
cos = emb.cos() * self.attention_scaling
sin = emb.sin() * self.attention_scaling
return cos.to(dtype=x.dtype), sin.to(dtype=x.dtype)
class EuclideanCodebook(nn.Module):
"""Codebook with Euclidean distance (inference-only)."""
def __init__(
self, dim: int, codebook_size: int, kmeans_init: bool = False, **kwargs
):
super().__init__()
init_fn = self._uniform_init if not kmeans_init else torch.zeros
embed = init_fn(codebook_size, dim)
self.codebook_size = codebook_size
self.register_buffer("inited", torch.Tensor([not kmeans_init]))
self.register_buffer("cluster_size", torch.zeros(codebook_size))
self.register_buffer("embed", embed)
self.register_buffer("embed_avg", embed.clone())
def preprocess(self, x):
x = rearrange(x, "... d -> (...) d")
return x
def quantize(self, x):
embed = self.embed.t()
dist_val = -(
x.pow(2).sum(1, keepdim=True)
- 2 * x @ embed
+ embed.pow(2).sum(0, keepdim=True)
)
embed_ind = dist_val.max(dim=-1).indices
return embed_ind
def postprocess_emb(self, embed_ind, shape):
return embed_ind.view(*shape[:-1])
def dequantize(self, embed_ind):
quantize = F.embedding(embed_ind, self.embed)
return quantize
def encode(self, x):
shape = x.shape
x = self.preprocess(x)
embed_ind = self.quantize(x)
embed_ind = self.postprocess_emb(embed_ind, shape)
return embed_ind
def decode(self, embed_ind):
quantize = self.dequantize(embed_ind)
return quantize
@staticmethod
def _uniform_init(*shape: int):
t = torch.empty(shape)
nn.init.kaiming_uniform_(t)
return t
class VectorQuantization(nn.Module):
"""Vector quantization with euclidean distance (inference-only)."""
def __init__(
self,
dim: int,
codebook_size: int,
codebook_dim: tp.Optional[int] = None,
kmeans_init: bool = True,
**kwargs,
):
super().__init__()
_codebook_dim: int = codebook_dim if codebook_dim is not None else dim
requires_projection = _codebook_dim != dim
self.project_in = (
nn.Linear(dim, _codebook_dim) if requires_projection else nn.Identity()
)
self.project_out = (
nn.Linear(_codebook_dim, dim) if requires_projection else nn.Identity()
)
self._codebook = EuclideanCodebook(
dim=_codebook_dim,
codebook_size=codebook_size,
kmeans_init=kmeans_init,
)
self.codebook_size = codebook_size
@property
def codebook(self):
return self._codebook.embed
def encode(self, x):
x = self.project_in(x)
embed_in = self._codebook.encode(x)
return embed_in
def decode(self, embed_ind):
quantize = self._codebook.decode(embed_ind)
quantize = self.project_out(quantize)
return quantize
class ResidualVectorQuantization(nn.Module):
"""Residual vector quantization implementation.
Follows Algorithm 1. in https://arxiv.org/pdf/2107.03312.pdf
"""
def __init__(self, *, num_quantizers, codebook_size, **kwargs):
super().__init__()
if isinstance(codebook_size, int):
codebook_size = [codebook_size] * num_quantizers
elif len(codebook_size) < num_quantizers:
codebook_size += [codebook_size[-1]] * (num_quantizers - len(codebook_size))
self.layers = nn.ModuleList(
[
VectorQuantization(codebook_size=codebook_size[i], **kwargs)
for i in range(num_quantizers)
]
)
def encode(
self, x: torch.Tensor, n_q: tp.Optional[int] = None, st: tp.Optional[int] = None
) -> torch.Tensor:
residual = x
all_indices = []
n_q = len(self.layers) if n_q is None else n_q
st = 0 if st is None else st
for layer in self.layers[st:n_q]:
indices = layer.encode(residual)
quantized = layer.decode(indices)
residual = residual - quantized
all_indices.append(indices)
out_indices = torch.stack(all_indices)
return out_indices
def decode(self, q_indices: torch.Tensor, st: int = 0) -> torch.Tensor:
quantized_out = self.layers[st].decode(q_indices[0])
for i in range(1, len(q_indices)):
layer = self.layers[st + i]
quantized = layer.decode(q_indices[i])
quantized_out = quantized_out + quantized
return quantized_out
class ResidualVectorQuantizer(nn.Module):
"""Residual Vector Quantizer (inference-only)."""
def __init__(
self,
dimension: int = 256,
n_q: int = 8,
bins: int | list = 1024,
kmeans_init: bool = True,
**kwargs,
):
super().__init__()
self.n_q = n_q
self.vq = ResidualVectorQuantization(
dim=dimension,
codebook_size=bins,
num_quantizers=n_q,
kmeans_init=kmeans_init,
)
def encode(
self, x: torch.Tensor, n_q: tp.Optional[int] = None, st: tp.Optional[int] = None
) -> torch.Tensor:
n_q = n_q if n_q else self.n_q
st = st or 0
codes = self.vq.encode(x, n_q=n_q, st=st)
return codes
def decode(self, codes: torch.Tensor, st: int = 0) -> torch.Tensor:
quantized = self.vq.decode(codes, st=st)
return quantized
class MiMoAudioTokenizerConfig(PretrainedConfig):
model_type = "mimo_audio_tokenizer"
def __init__(
self,
max_audio_seconds: int = 1800,
stride_size: int = 2,
avg_pooler: int = 1,
d_model: int = 768,
scale_embedding: bool = True,
kernel_size: int = 3,
activation_function: str = "gelu",
encoder_layers: int = 8,
encoder_skip_layer_id: int = None,
encoder_attention_heads: int = 12,
encoder_ffn_dim: int = 3072,
encoder_causal: bool = False,
encoder_attn_window_size: list = None,
decoder_layers: int = 8,
decoder_attention_heads: int = 12,
decoder_ffn_dim: int = 3072,
decoder_kernel_size: int = 3,
decoder_stride_size: int = 2,
decoder_causal: bool = True,
decoder_attn_window_size: list = None,
nfft: int = 1024,
vocoder_dim: int = 512,
vocoder_intermediate_dim: int = 4096,
vocoder_num_layers: int = 30,
n_mels: int = 80,
sampling_rate: int = 24000,
hop_length: int = 240,
window_size: int = 1024,
vocoder_padding: str = "same",
fmin: int = 0,
fmax: int = None,
num_quantizers: int = 12,
codebook_size: list = None,
threshold_ema_dead_code: int = 10,
position_embedding_type: str = "rope",
rope_theta: int = 10000,
rope_type: str = "default",
ln_type: str = "LayerNorm",
vocoder_attention_heads: int = 4,
vocoder_attn_window_size: list = None,
use_istft_only: bool = False,
hybrid_attention: bool = False,
hybrid_block_size: int = 8,
swa_per_block: int = 2,
**kwargs,
):
super().__init__(**kwargs)
self.max_audio_seconds = max_audio_seconds
self.stride_size = stride_size
self.avg_pooler = avg_pooler
self.d_model = d_model
self.scale_embedding = scale_embedding
self.kernel_size = kernel_size
self.activation_function = activation_function
self.encoder_layers = encoder_layers
self.encoder_skip_layer_id = encoder_skip_layer_id
self.encoder_attention_heads = encoder_attention_heads
self.encoder_ffn_dim = encoder_ffn_dim
self.encoder_causal = encoder_causal
self.encoder_attn_window_size = (
encoder_attn_window_size
if encoder_attn_window_size is not None
else [-1, -1]
)
self.decoder_layers = decoder_layers
self.decoder_attention_heads = decoder_attention_heads
self.decoder_ffn_dim = decoder_ffn_dim
self.decoder_kernel_size = decoder_kernel_size
self.decoder_stride_size = decoder_stride_size
self.decoder_causal = decoder_causal
self.decoder_attn_window_size = (
decoder_attn_window_size
if decoder_attn_window_size is not None
else [-1, -1]
)
self.nfft = nfft
self.vocoder_dim = vocoder_dim
self.vocoder_intermediate_dim = vocoder_intermediate_dim
self.vocoder_num_layers = vocoder_num_layers
self.n_mels = n_mels
self.sampling_rate = sampling_rate
self.hop_length = hop_length
self.window_size = window_size
self.vocoder_padding = vocoder_padding
self.fmin = fmin
self.fmax = fmax
self.num_quantizers = num_quantizers
self.codebook_size = codebook_size if codebook_size is not None else [1024]
self.threshold_ema_dead_code = threshold_ema_dead_code
self.position_embedding_type = position_embedding_type
self.rope_theta = rope_theta
self.rope_type = rope_type
self.ln_type = ln_type
self.vocoder_attention_heads = vocoder_attention_heads
self.vocoder_attn_window_size = (
vocoder_attn_window_size
if vocoder_attn_window_size is not None
else [40, 10]
)
self.use_istft_only = use_istft_only
self.hybrid_attention = hybrid_attention
self.hybrid_block_size = hybrid_block_size
self.swa_per_block = swa_per_block
def get_sequence_mask(inputs, inputs_length):
if inputs.dim() == 3:
bsz, tgt_len, _ = inputs.size()
else:
bsz, tgt_len = inputs_length.shape[0], torch.max(inputs_length)
sequence_mask = torch.arange(0, tgt_len).to(inputs.device)
sequence_mask = torch.lt(sequence_mask, inputs_length.reshape(bsz, 1)).view(
bsz, tgt_len, 1
)
unpacking_index = torch.cumsum(sequence_mask.to(torch.int64).view(-1), dim=0) - 1
return sequence_mask, unpacking_index
def unpack_hidden_states(
hidden_states, lengths, sequence_mask=None, unpacking_index=None
):
bsz = lengths.shape[0]
if sequence_mask is None or unpacking_index is None:
sequence_mask, unpacking_index = get_sequence_mask(hidden_states, lengths)
hidden_states = torch.index_select(hidden_states, 0, unpacking_index).view(
bsz, torch.max(lengths), hidden_states.shape[-1]
)
return torch.where(sequence_mask, hidden_states, 0)
def get_position_ids(lengths):
total_len = lengths.sum()
offset = torch.cat([torch.zeros(1).to(lengths), lengths[:-1].cumsum(dim=0)])
offset = torch.repeat_interleave(offset, lengths)
return torch.arange(0, total_len).to(offset) - offset
LAYER_NORM = {"LayerNorm": nn.LayerNorm}
class AudioEncoderAttention(nn.Module):
def __init__(
self,
embed_dim: int,
num_heads: int,
window_size: Tuple[int, int] = (-1, -1),
causal: bool = False,
):
super().__init__()
self.embed_dim = embed_dim
self.num_heads = num_heads
self.head_dim = embed_dim // num_heads
self.window_size = window_size
self.causal = causal
self.k_proj = nn.Linear(embed_dim, embed_dim, bias=False)
self.v_proj = nn.Linear(embed_dim, embed_dim, bias=True)
self.q_proj = nn.Linear(embed_dim, embed_dim, bias=True)
self.out_proj = nn.Linear(embed_dim, embed_dim, bias=True)
def forward(
self,
hidden_states: torch.Tensor,
cu_seqlens: torch.Tensor,
max_seqlen: int,
rope_position_embeddings=None,
):
bsz, _ = hidden_states.size()
query_states = self.q_proj(hidden_states).view(
bsz, self.num_heads, self.head_dim
)
key_states = self.k_proj(hidden_states).view(bsz, self.num_heads, self.head_dim)
value_states = self.v_proj(hidden_states).view(
bsz, self.num_heads, self.head_dim
)
if rope_position_embeddings is not None:
cos, sin = rope_position_embeddings
query_states, key_states = self.apply_rotary_pos_emb(
query_states, key_states, cos, sin
)
attn_output = flash_attn_varlen_func(
query_states,
key_states,
value_states,
cu_seqlens,
cu_seqlens,
max_seqlen,
max_seqlen,
causal=self.causal,
window_size=self.window_size,
)
attn_output = attn_output.reshape(bsz, self.embed_dim)
attn_output = self.out_proj(attn_output)
return attn_output
@staticmethod
def _rotate_half(x):
x1 = x[..., : x.shape[-1] // 2]
x2 = x[..., x.shape[-1] // 2 :]
return torch.cat((-x2, x1), dim=-1)
@classmethod
def apply_rotary_pos_emb(cls, q, k, cos, sin, unsqueeze_dim=1):
cos = cos.unsqueeze(unsqueeze_dim)
sin = sin.unsqueeze(unsqueeze_dim)
q_embed = (q * cos) + (cls._rotate_half(q) * sin)
k_embed = (k * cos) + (cls._rotate_half(k) * sin)
return q_embed, k_embed
class AudioEncoderTransformerLayer(nn.Module):
def __init__(
self,
config: MiMoAudioTokenizerConfig,
causal: bool,
attn_window_size: Tuple[int, int] = (-1, -1),
):
super().__init__()
self.embed_dim = config.d_model
self.self_attn = AudioEncoderAttention(
embed_dim=self.embed_dim,
num_heads=config.encoder_attention_heads,
window_size=attn_window_size,
causal=causal,
)
self.self_attn_layer_norm = LAYER_NORM[config.ln_type](self.embed_dim)
self.activation_fn = ACT2FN[config.activation_function]
self.fc1 = nn.Linear(self.embed_dim, config.encoder_ffn_dim)
self.fc2 = nn.Linear(config.encoder_ffn_dim, self.embed_dim)
self.final_layer_norm = LAYER_NORM[config.ln_type](self.embed_dim)
def forward(
self,
hidden_states: torch.Tensor,
cu_seqlens: torch.Tensor,
max_seqlen: int,
rope_position_embeddings: Tuple[torch.Tensor, torch.Tensor],
) -> torch.Tensor:
residual = hidden_states
hidden_states = self.self_attn_layer_norm(hidden_states)
hidden_states = self.self_attn(
hidden_states,
cu_seqlens,
max_seqlen,
rope_position_embeddings=rope_position_embeddings,
)
hidden_states = residual + hidden_states
residual = hidden_states
hidden_states = self.final_layer_norm(hidden_states)
hidden_states = self.activation_fn(self.fc1(hidden_states))
hidden_states = self.fc2(hidden_states)
hidden_states = residual + hidden_states
return hidden_states
class AudioEncoder(nn.Module):
def __init__(
self,
config: MiMoAudioTokenizerConfig,
):
super().__init__()
self.config = config
self.max_source_positions = (
config.max_audio_seconds * config.sampling_rate // config.hop_length
) // config.stride_size
self.embed_scale = math.sqrt(config.d_model) if config.scale_embedding else 1.0
self.skip_layer_idx = config.encoder_skip_layer_id
self.conv1 = nn.Conv1d(
config.n_mels,
config.d_model,
kernel_size=config.kernel_size,
padding=1,
)
self.conv2 = nn.Conv1d(
config.d_model,
config.d_model,
kernel_size=config.kernel_size,
stride=config.stride_size,
padding=1,
)
self.position_embedding = AudioRotaryEmbedding(
config.rope_theta,
config.d_model // config.encoder_attention_heads,
self.max_source_positions,
config.rope_type,
)
attn_window_sizes = []
if config.hybrid_attention:
for i in range(config.encoder_layers):
if i % config.swa_per_block < config.swa_per_block - 1:
attn_window_sizes.append(tuple(config.encoder_attn_window_size))
else:
attn_window_sizes.append((-1, -1))
else:
attn_window_sizes = [
tuple(config.encoder_attn_window_size)
] * config.encoder_layers
self.layers = nn.ModuleList(
[
AudioEncoderTransformerLayer(
config=config,
causal=config.encoder_causal,
attn_window_size=attn_window_sizes[i],
)
for i in range(config.encoder_layers)
]
)
self.layer_norm = LAYER_NORM[config.ln_type](config.d_model)
if config.avg_pooler != 1:
self.down_sample_layer = nn.Sequential(
nn.Conv1d(
config.d_model,
config.d_model,
config.avg_pooler,
config.avg_pooler,
bias=False,
),
nn.GELU(),
)
self.down_sample_norm = LAYER_NORM[config.ln_type](config.d_model)
else:
self.down_sample_layer = None
if config.num_quantizers != 0:
self.quantizer = ResidualVectorQuantizer(
dimension=config.d_model,
n_q=config.num_quantizers,
bins=config.codebook_size,
threshold_ema_dead_code=config.threshold_ema_dead_code,
)
else:
self.quantizer = None
def get_features(self, input_features, output_length):
input_features = input_features.to(self.conv1.weight)
inputs_embeds = nn.functional.gelu(self.conv1(input_features))
inputs_embeds = nn.functional.gelu(self.conv2(inputs_embeds))
inputs_embeds = inputs_embeds.permute(0, 2, 1)
bsz, tgt_len, _ = inputs_embeds.size()
hidden_states = inputs_embeds
position_ids = get_position_ids(output_length).long().to(input_features.device)
rope_position_embeddings = self.position_embedding(input_features, position_ids)
attention_mask, unpacking_index = get_sequence_mask(
hidden_states, output_length
)
hidden_states = torch.masked_select(hidden_states, attention_mask).view(
torch.sum(output_length), self.config.d_model
)
cu_seqlens = F.pad(
torch.cumsum(output_length, dim=0), (1, 0), "constant", 0
).to(device=hidden_states.device, dtype=torch.int32)
max_seqlen = torch.max(output_length).to(torch.int32).item()
skip_connect_hidden_states = 0.0
for idx, encoder_layer in enumerate(self.layers):
hidden_states = encoder_layer(
hidden_states,
cu_seqlens,
max_seqlen,
rope_position_embeddings=rope_position_embeddings,
)
if (self.skip_layer_idx is not None) and idx == self.skip_layer_idx - 1:
skip_connect_hidden_states = hidden_states.clone()
hidden_states += skip_connect_hidden_states
hidden_states = self.layer_norm(hidden_states)
if self.down_sample_layer is not None:
hidden_states = torch.index_select(hidden_states, 0, unpacking_index).view(
bsz, tgt_len, self.config.d_model
)
if hidden_states.size(1) % self.config.avg_pooler:
pad_len = (
self.config.avg_pooler
- hidden_states.size(1) % self.config.avg_pooler
)
hidden_states = torch.nn.functional.pad(
hidden_states, (0, 0, 0, pad_len), mode="constant", value=0.0
)
tgt_len += pad_len
tgt_len = tgt_len // self.config.avg_pooler
hidden_states = self.down_sample_layer(hidden_states.transpose(1, 2))
output_length = (
output_length // self.config.avg_pooler
+ (output_length % self.config.avg_pooler != 0).int()
)
hidden_states = hidden_states.transpose(1, 2)
attention_mask, unpacking_index = get_sequence_mask(
hidden_states, output_length
)
hidden_states = torch.masked_select(hidden_states, attention_mask).view(
torch.sum(output_length), self.config.d_model
)
hidden_states = self.down_sample_norm(hidden_states)
return (
hidden_states,
output_length,
attention_mask,
unpacking_index,
tgt_len,
bsz,
)
def get_output_length(self, mel_len):
tgt_len = mel_len + 3 - self.config.kernel_size
return (tgt_len + 2 - self.config.kernel_size) // self.config.stride_size + 1
@torch.no_grad()
def encode(
self,
input_features,
input_lens=None,
output_length=None,
return_codes_only=False,
n_q=None,
use_quantizer=True,
):
if output_length is None:
output_length = self.get_output_length(input_lens)
input_features = unpack_hidden_states(input_features, input_lens)
hidden_states, output_length, attention_mask, unpacking_index, tgt_len, bsz = (
self.get_features(
input_features=input_features.transpose(1, 2),
output_length=output_length,
)
)
dtype = hidden_states.dtype
if use_quantizer and self.quantizer is not None:
self.quantizer.float()
codes = self.quantizer.encode(hidden_states.float(), n_q=n_q)
if return_codes_only:
return codes, output_length
hidden_states = self.quantizer.decode(codes)
hidden_states = hidden_states.to(dtype)
else:
codes = None
hidden_states_packed = hidden_states.clone()
hidden_states = torch.index_select(hidden_states, 0, unpacking_index).view(
bsz, tgt_len, self.config.d_model
)
hidden_states = torch.where(attention_mask, hidden_states, 0)
return hidden_states, hidden_states_packed, output_length, codes
@torch.no_grad()
def decode_vq(self, codes):
self.quantizer.float()
return self.quantizer.decode(codes)
class MiMoAudioTokenizer(PreTrainedModel):
config_class = MiMoAudioTokenizerConfig
def __init__(self, config: MiMoAudioTokenizerConfig):
super().__init__(config)
self.config = config
self.sampling_rate = config.sampling_rate
self.encoder = AudioEncoder(config=config)
self.downsample_rate = int(config.hop_length * 2 * config.avg_pooler)
def get_output_length(self, mel_len):
tgt_len = mel_len + 3 - self.config.kernel_size
return (tgt_len + 2 - self.config.kernel_size) // self.config.stride_size + 1
@torch.no_grad()
def encode(self, mels, input_lens, use_quantizer=True):
input_features = mels
encoder_output_length = self.get_output_length(input_lens)
hidden_states, hidden_states_packed, encoder_output_length, codes = (
self.encoder.encode(
input_features, input_lens=input_lens, use_quantizer=use_quantizer
)
)
return hidden_states, hidden_states_packed, encoder_output_length, codes
def group_by_length(features: torch.Tensor, lengths: torch.Tensor, max_length: int):
if features.size(0) != lengths.sum().item():
raise ValueError(
f"Feature size mismatch: {features.size(0)} vs {lengths.sum().item()}"
)
split_points = []
current_sum = 0
for i, seq_len in enumerate(lengths):
if current_sum + seq_len > max_length and current_sum > 0:
split_points.append(i)
current_sum = seq_len.item()
else:
current_sum += seq_len.item()
# Convert split points to group sizes
group_sizes = []
prev = 0
for point in split_points:
group_sizes.append(point - prev)
prev = point
if prev < len(lengths):
group_sizes.append(len(lengths) - prev)
len_groups = torch.split(lengths, group_sizes)
feature_sizes = [group.sum().item() for group in len_groups]
feature_groups = torch.split(features, feature_sizes)
return feature_groups, len_groups
@torch.no_grad()
def encode_batch(
audio_tokenizer_encoder,
input_features: torch.Tensor,
input_lens: torch.Tensor,
max_length: int = 256000,
):
feature_groups, len_groups = group_by_length(input_features, input_lens, max_length)
encoded_parts = []
for features, lengths in zip(feature_groups, len_groups):
codes, _ = audio_tokenizer_encoder.encode( # codes are also packed
input_features=features, input_lens=lengths, return_codes_only=True
)
encoded_parts.append(codes)
return torch.cat(encoded_parts, dim=-1)
def _segment_lengths_for_mel(mel: torch.Tensor, segment_size: int):
"""Split mel into segments of segment_size with a possible shorter remainder."""
input_len = mel.size(0)
segs = [segment_size] * (input_len // segment_size)
if input_len % segment_size > 0:
segs.append(input_len % segment_size)
return segs
@torch.no_grad()
def tokenize_audio_batch(mels, audio_tokenizer_encoder, segment_size=6000, device=None):
"""
Tokenize multiple mels in one encode_batch call.
Returns list of code tensors, each [T_i, C] for that mel.
"""
if not mels:
return []
if device is None:
device = next(audio_tokenizer_encoder.parameters()).device
# Build segment lengths per mel
input_len_seg_per_mel = [_segment_lengths_for_mel(m, segment_size) for m in mels]
input_lens_flat = [s for segs in input_len_seg_per_mel for s in segs]
input_features = torch.cat([m.to(device) for m in mels], dim=0)
input_lens_t = torch.tensor(input_lens_flat, dtype=torch.long, device=device)
codes_packed = encode_batch(
audio_tokenizer_encoder,
input_features=input_features,
input_lens=input_lens_t,
)
codes = codes_packed.transpose(0, 1).detach() # [total_code_T, C]
# Code length per mel: must match encoder's actual output (get_output_length + optional avg_pooler downsampling)
code_lengths = []
for segs in input_len_seg_per_mel:
out_len = audio_tokenizer_encoder.get_output_length(
torch.tensor(segs, dtype=torch.long, device=device)
)
if getattr(audio_tokenizer_encoder, "down_sample_layer", None) is not None:
avg = audio_tokenizer_encoder.config.avg_pooler
out_len = out_len // avg + (out_len % avg != 0).long()
code_lengths.append(out_len.sum().item())
code_list = torch.split(codes, code_lengths)
return list(code_list)
@dataclass
class MiMoAudioEncoderConfig:
tokenizer_version: str = "v1"
speech_vocab_size: str = "1025-1025-129-129-129-129-129-129"
speech_zeroemb_idx: str = "1024-1024-128-128-128-128-128-128"
group_size: int = 4
audio_channels: int = 8
input_local_layers: int = 6
input_local_dim: int = 1024
input_full_attention: bool = True
input_local_attn_heads: int = 64
input_local_head_dim: int = 16
input_local_intermediate_size: int = 4096
input_local_hidden_dropout: float = 0.0
out_hidden_size: int = 4096 # mimo vl hidden dim
rope_theta: float = 640000.0
partial_rotary_factor: float = 0.334
projection_layers: int = 1
add_post_norm: bool = False
audio_segment_size: int = 6000
class AudioProjection(nn.Module):
def __init__(
self,
input_size,
hidden_size,
output_size,
quant_config: Optional[QuantizationConfig] = None,
prefix: str = "",
) -> None:
super().__init__()
self.input_size = input_size
self.hidden_size = hidden_size
self.output_size = output_size
self.mlp = nn.Sequential(
nn.Linear(self.input_size, self.hidden_size, bias=False),
nn.GELU(),
nn.Linear(self.hidden_size, self.output_size, bias=False),
)
def forward(self, x: torch.Tensor) -> torch.Tensor:
return self.mlp(x)
class MiMoV2AudioConfig:
def __init__(
self,
speech_vocab_size: str | int = "1280",
speech_lm_head_sizes: str | int | None = None,
speech_zeroemb_idx: str | int = "1280",
delay_pattern: str = "0-1-2-3-4-5-6-7-7-7-7-7-7-7-7-7-7-7-7-7",
group_size: int = 4,
audio_channels: int = 20,
input_local_dim: int = 1024,
input_local_layers: int = 6,
input_local_attn_heads: int = 16,
input_local_intermediate_size: int = 4096,
input_local_rope_theta: float = 640000.0,
input_local_partial_rotary_factor: float = 1.0,
output_local_dim: int = 1024,
output_local_layers: int = 6,
output_local_attn_heads: int = 16,
output_local_intermediate_size: int = 4096,
output_local_rope_theta: float = 640000.0,
output_local_partial_rotary_factor: float = 1.0,
input_projection_layers: int = 2,
output_projection_layers: int = 2,
add_encoder_post_norm: bool = True,
audio_config: dict = None,
**kwargs,
):
for key, value in kwargs.items():
setattr(self, key, value)
if audio_config is not None:
self._load_from_audio_config(audio_config)
else:
self.speech_vocab_size = speech_vocab_size
self.speech_lm_head_sizes = (
speech_lm_head_sizes
if speech_lm_head_sizes is not None
else speech_vocab_size
)
self.speech_zeroemb_idx = speech_zeroemb_idx
self.delay_pattern = delay_pattern
self.group_size = group_size
self.audio_channels = audio_channels
self.input_local_dim = input_local_dim
self.input_local_layers = input_local_layers
self.input_local_attn_heads = input_local_attn_heads
self.input_local_intermediate_size = input_local_intermediate_size
self.input_local_rope_theta = input_local_rope_theta
self.input_local_partial_rotary_factor = input_local_partial_rotary_factor
self.output_local_dim = output_local_dim
self.output_local_layers = output_local_layers
self.output_local_attn_heads = output_local_attn_heads
self.output_local_intermediate_size = output_local_intermediate_size
self.output_local_rope_theta = output_local_rope_theta
self.output_local_partial_rotary_factor = output_local_partial_rotary_factor
self.input_projection_layers = input_projection_layers
self.output_projection_layers = output_projection_layers
self.add_encoder_post_norm = add_encoder_post_norm
self._attn_implementation_internal = "sdpa"
def _load_from_audio_config(self, audio_config: dict):
"""Load audio parameters from audio_config dict in checkpoint.
Uses naming that matches megatron2hf conversion output to minimize manual mapping.
"""
self.group_size = audio_config.get("group_size", 4)
self.audio_channels = audio_config.get("audio_channels", 20)
self.speech_vocab_size = audio_config.get("speech_vocab_size", "1280")
self.speech_lm_head_sizes = audio_config.get(
"speech_lm_head_sizes", self.speech_vocab_size
)
self.speech_zeroemb_idx = audio_config.get("speech_zeroemb_idx", "1280")
# Per-channel decode delays; len must equal audio_channels.
self.delay_pattern = audio_config.get(
"audio_output_delay_pattern", "0-1-2-3-4-5-6-7-7-7-7-7-7-7-7-7-7-7-7-7"
)
self.input_local_dim = audio_config.get("input_local_dim", 1024)
self.input_local_layers = audio_config.get("input_local_layers", 6)
self.input_local_attn_heads = audio_config.get("input_local_attn_heads", 16)
self.input_local_intermediate_size = audio_config.get(
"input_local_intermediate_size", 4096
)
self.input_local_rope_theta = audio_config.get(
"input_local_rope_theta", 640000.0
)
self.input_local_partial_rotary_factor = audio_config.get(
"input_local_partial_rotary_factor", 1.0
)
self.output_local_dim = audio_config.get("output_local_dim", 1024)
self.output_local_layers = audio_config.get("output_local_layers", 6)
self.output_local_attn_heads = audio_config.get("output_local_attn_heads", 16)
self.output_local_intermediate_size = audio_config.get(
"output_local_intermediate_size", 4096
)
self.output_local_rope_theta = audio_config.get(
"output_local_rope_theta", 640000.0
)
self.output_local_partial_rotary_factor = audio_config.get(
"output_local_partial_rotary_factor", 1.0
)
self.input_projection_layers = audio_config.get("input_projection_layers", 2)
self.output_projection_layers = audio_config.get("output_projection_layers", 2)
self.add_encoder_post_norm = audio_config.get("add_encoder_post_norm", True)
def _parse_maybe_list(self, value: str | int, length: int) -> list[int]:
if isinstance(value, str) and "-" in value:
return [int(s) for s in value.split("-")]
return [int(value)] * length
def parsed_speech_empty_ids(self):
return self._parse_maybe_list(self.speech_zeroemb_idx, self.audio_channels)
def parsed_speech_vocab_sizes(self):
return self._parse_maybe_list(self.speech_vocab_size, self.audio_channels)
def parsed_speech_lm_head_sizes(self):
return self._parse_maybe_list(self.speech_lm_head_sizes, self.audio_channels)
def parsed_delay_pattern(self):
return self._parse_maybe_list(self.delay_pattern, self.audio_channels)
def input_local_config(self):
"""Create config for input local transformer."""
config = Qwen2Config()
for attr in dir(self):
if not attr.startswith("_") and hasattr(config, attr):
setattr(config, attr, getattr(self, attr))
config.hidden_size = self.input_local_dim
config.num_hidden_layers = self.input_local_layers
config.num_attention_heads = self.input_local_attn_heads
config.num_key_value_heads = self.input_local_attn_heads
config.head_dim = getattr(
self,
"input_local_head_dim",
self.input_local_dim // self.input_local_attn_heads,
)
config.intermediate_size = self.input_local_intermediate_size
config.rope_theta = self.input_local_rope_theta
config.partial_rotary_factor = self.input_local_partial_rotary_factor
config._attn_implementation_internal = "sdpa"
return config
def output_local_config(self):
"""Create config for output local transformer."""
config = Qwen2Config()
for attr in dir(self):
if not attr.startswith("_") and hasattr(config, attr):
setattr(config, attr, getattr(self, attr))
config.hidden_size = self.output_local_dim
config.num_hidden_layers = self.output_local_layers
config.num_attention_heads = self.output_local_attn_heads
config.num_key_value_heads = self.output_local_attn_heads
config.head_dim = self.output_local_dim // self.output_local_attn_heads
config.intermediate_size = self.output_local_intermediate_size
config.rope_theta = self.output_local_rope_theta
config.partial_rotary_factor = self.output_local_partial_rotary_factor
config._attn_implementation_internal = "sdpa"
return config
class AudioEncoderMixin:
"""LM model mixin that adds MiMo audio encoder components.
Components are attached as top-level attributes (no ``audio_encoder.``
prefix), matching the checkpoint state_dict layout. Inner naming
variations are normalized via ``AUDIO_WEIGHT_REMAP``.
Hot config fields are cached as direct ``self.audio_*`` attributes at
build time so helper methods can stay short and uniform (no
``self.audio_config.foo`` indirection inside hot paths).
Subclasses call ``self.build_audio_encoder(audio_config)`` from their
``__init__`` after the language model is constructed; the mixin's
``get_audio_feature`` then handles audio item batching end-to-end.
"""
AUDIO_WEIGHT_REMAP: tuple[tuple[str, str], ...] = (
("audio_projection", "projection"),
("speech_group_downcast", "projection"),
("audio_input_local_transformer", "input_local_transformer"),
)
def build_audio_encoder(self, config) -> None:
if not isinstance(config, MiMoV2AudioConfig):
cfg_dict = vars(config) if hasattr(config, "__dict__") else config.__dict__
config = MiMoV2AudioConfig(**cfg_dict)
self.audio_channels = config.audio_channels
self.audio_group_size = config.group_size
self.audio_segment_size = config.audio_segment_size
self.audio_input_local_dim = config.input_local_dim
self.audio_input_full_attention = config.input_full_attention
self.audio_out_hidden_size = config.out_hidden_size
speech_vocab_size = config._parse_maybe_list(
config.speech_vocab_size, self.audio_channels
)
speech_empty_ids = config._parse_maybe_list(
config.speech_zeroemb_idx, self.audio_channels
)
input_local_config = Qwen2Config(
hidden_size=self.audio_input_local_dim,
num_hidden_layers=config.input_local_layers,
num_attention_heads=config.input_local_attn_heads,
num_key_value_heads=config.input_local_attn_heads,
intermediate_size=config.input_local_intermediate_size,
attention_dropout=config.input_local_hidden_dropout,
rope_theta=config.rope_theta,
partial_rotary_factor=config.partial_rotary_factor,
)
input_local_config.head_dim = config.input_local_head_dim
self.input_local_transformer = Qwen2Model(input_local_config)
if not config.add_post_norm:
self.input_local_transformer.norm = nn.Identity()
self.speech_embeddings = nn.ModuleList(
[
nn.Embedding(
speech_vocab_size[i],
self.audio_input_local_dim,
padding_idx=speech_empty_ids[i],
)
for i in range(self.audio_channels)
]
)
if config.projection_layers == 1:
self.projection = nn.Linear(
self.audio_input_local_dim * self.audio_group_size,
self.audio_out_hidden_size,
bias=False,
)
elif config.projection_layers == 2:
self.projection = AudioProjection(
self.audio_input_local_dim * self.audio_group_size,
self.audio_input_local_dim * self.audio_group_size * 4,
self.audio_out_hidden_size,
)
else:
raise ValueError(f"Invalid projection layers: {config.projection_layers}")
model_path = get_server_args().model_path
if not os.path.isdir(model_path):
from huggingface_hub import snapshot_download
model_path = snapshot_download(
model_path,
allow_patterns=["audio_tokenizer/*"],
)
audio_tokenizer_path = os.path.join(model_path, "audio_tokenizer")
dev = torch.device(f"cuda:{torch.cuda.current_device()}")
self.audio_tokenizer = self._load_mimo_audio_tokenizer(
audio_tokenizer_path, dev
)
@staticmethod
def _load_mimo_audio_tokenizer(
path: str, device: torch.device
) -> MiMoAudioTokenizer:
"""Load MiMoAudioTokenizer manually to avoid new-transformers compat issues."""
import json
from safetensors.torch import load_file
config_path = os.path.join(path, "config.json")
with open(config_path) as f:
config_dict = json.load(f)
config = MiMoAudioTokenizer.config_class(**config_dict)
model = MiMoAudioTokenizer(config)
safetensors_path = os.path.join(path, "model.safetensors")
bin_path = os.path.join(path, "pytorch_model.bin")
if os.path.exists(safetensors_path):
state_dict = load_file(safetensors_path, device="cpu")
elif os.path.exists(bin_path):
state_dict = torch.load(bin_path, map_location="cpu", weights_only=True)
else:
raise FileNotFoundError(
f"No model weights found in {path} "
"(expected model.safetensors or pytorch_model.bin)"
)
# strict=False: upstream ckpt also carries decoder/vocoder weights
# that this encoder-only MiMoAudioTokenizer doesn't materialize.
model.load_state_dict(state_dict, strict=False)
model = model.to(device=device, dtype=torch.bfloat16)
model.eval()
model.requires_grad_(False)
return model
def apply_input_local_transformer(
self, speech_embeddings: torch.Tensor
) -> torch.Tensor:
return self.input_local_transformer(
inputs_embeds=speech_embeddings,
return_dict=True,
is_causal=not self.audio_input_full_attention, # for SDPA
).last_hidden_state # [T//group_size, group_size, input_local_dim]
def apply_speech_embeddings(self, audio_codes: torch.Tensor) -> torch.Tensor:
embeds = torch.zeros(
(audio_codes.shape[0], self.audio_group_size, self.audio_input_local_dim),
dtype=next(self.speech_embeddings[0].parameters()).dtype,
device=audio_codes.device,
)
for i in range(self.audio_channels):
embeds.add_(self.speech_embeddings[i](audio_codes[:, :, i]))
return embeds
def pad_audio_codes(self, audio: torch.Tensor) -> torch.Tensor:
T = audio.shape[0]
audio = audio[:, : self.audio_channels]
padded_T = (
(T + self.audio_group_size - 1)
// self.audio_group_size
* self.audio_group_size
)
padded_audio = torch.cat(
[
audio,
torch.zeros(
padded_T - T,
self.audio_channels,
dtype=torch.int32,
device=audio.device,
)
+ audio[-1, :],
],
dim=0,
)
return padded_audio.reshape(
padded_T // self.audio_group_size,
self.audio_group_size,
self.audio_channels,
)
def get_audio_feature(self, items) -> torch.Tensor:
"""Compute audio features for a list of audio MultimodalDataItem.
Each item.feature is either a mel tensor or a list of mel tensors
(long audio split into chunks).
"""
all_mels = []
for item in items:
f = item.feature
if isinstance(f, (list, tuple)):
all_mels.extend(f)
else:
all_mels.append(f)
if not all_mels:
device = next(self.projection.parameters()).device
dtype = next(self.projection.parameters()).dtype
return torch.empty(
0, self.audio_out_hidden_size, device=device, dtype=dtype
)
device = next(self.audio_tokenizer.encoder.parameters()).device
code_list = tokenize_audio_batch(
all_mels,
self.audio_tokenizer.encoder,
segment_size=self.audio_segment_size,
device=device,
)
audio_codes = torch.cat([self.pad_audio_codes(c) for c in code_list], dim=0)
embeds = self.apply_input_local_transformer(
self.apply_speech_embeddings(audio_codes)
)
return self.projection(embeds.reshape(embeds.shape[0], -1))
@classmethod
def remap_audio_weight_name(cls, name: str) -> str:
"""Normalize inner audio weight name variations to canonical form."""
for src, dst in cls.AUDIO_WEIGHT_REMAP:
if src in name:
return name.replace(src, dst)
return name