ManyOtherFunctions's picture
Upload 17 files
9040f4d verified
Raw History Blame Contribute Delete
9.95 kB
"""Local PyTorch MERT2 encoder for the unified tokenizer checkpoint."""
from typing import Dict, Sequence
import torch
import torch.nn.functional as F
from torch import nn
from torchaudio.transforms import AmplitudeToDB, MelScale, Spectrogram
SAMPLE_RATE = 24000
HOP = 960
class MelFrontend(nn.Module):
def __init__(self, cfg: Dict):
super().__init__()
bins = cfg["num_mel_bins"]
self.register_buffer("mel_mean", torch.zeros(bins))
self.register_buffer("mel_std", torch.ones(bins))
self.spectrogram = Spectrogram(
n_fft=cfg["n_fft"],
win_length=cfg["win_length"],
hop_length=cfg["hop_length"],
power=2.0,
)
self.mel_scale = MelScale(
n_mels=bins,
sample_rate=cfg["sampling_rate"],
n_stft=cfg["n_fft"] // 2 + 1,
)
self.amplitude_to_db = AmplitudeToDB(stype="power", top_db=None)
@torch.no_grad()
def forward(self, waveform: torch.Tensor) -> torch.Tensor:
with torch.autocast(device_type=waveform.device.type, enabled=False):
mel = self.amplitude_to_db(self.mel_scale(self.spectrogram(waveform.float())))
mel = mel[..., :-1].transpose(-1, -2)
return (mel - self.mel_mean) / self.mel_std.clamp_min(1e-5)
class Transpose(nn.Module):
def forward(self, x):
return x.transpose(1, 2)
class GlobalResponseNorm(nn.Module):
def __init__(self, dim: int):
super().__init__()
self.weight = nn.Parameter(torch.zeros(1, 1, dim))
self.bias = nn.Parameter(torch.zeros(1, 1, dim))
def forward(self, x):
magnitude = torch.norm(x, p=2, dim=1, keepdim=True)
return self.weight * (x * (magnitude / (magnitude.mean(dim=-1, keepdim=True) + 1e-6))) + self.bias + x
class ConvNextLayer(nn.Module):
def __init__(self, dim: int, eps: float):
super().__init__()
self.depthwise_block = nn.Sequential(
Transpose(),
nn.Conv1d(dim, dim, 7, padding=3, groups=dim),
Transpose(),
)
self.pointwise_block = nn.Sequential(
nn.LayerNorm(dim, eps=eps),
nn.Linear(dim, 4 * dim),
nn.GELU(),
GlobalResponseNorm(4 * dim),
nn.Linear(4 * dim, dim),
)
def forward(self, x):
return x + self.pointwise_block(self.depthwise_block(x))
class ConvNextBlock(nn.Module):
def __init__(self, cin: int, cout: int, stride: int, depth: int, eps: float):
super().__init__()
self.resampling_layer = (
nn.Sequential(
nn.LayerNorm(cin, eps=eps),
Transpose(),
nn.Conv1d(cin, cout, 2, stride=stride),
Transpose(),
)
if cin != cout or stride > 1
else nn.Identity()
)
self.convnext_layers = nn.Sequential(*[ConvNextLayer(cout, eps) for _ in range(depth)])
def forward(self, x):
return self.convnext_layers(self.resampling_layer(x))
class RotaryEmbedding(nn.Module):
def __init__(self, cfg: Dict):
super().__init__()
head_dim = cfg["hidden_size"] // cfg["num_attention_heads"]
inv = 1.0 / (
cfg["rotary_embedding_base"]
** (torch.arange(0, head_dim, 2, dtype=torch.float32) / head_dim)
)
self.register_buffer("inv_freq", inv, persistent=False)
def forward(self, x):
positions = torch.arange(x.shape[1], device=x.device, dtype=torch.float32)
angles = torch.einsum("i,j->ij", positions, self.inv_freq.float())
angles = torch.cat((angles, angles), dim=-1)[None, :, None, :]
return angles.cos().to(x.dtype), angles.sin().to(x.dtype)
def rotate_half(x):
first, second = x.chunk(2, dim=-1)
return torch.cat((-second, first), dim=-1)
class SelfAttention(nn.Module):
def __init__(self, cfg: Dict, causal: bool):
super().__init__()
width = cfg["hidden_size"]
self.num_heads = cfg["num_attention_heads"]
self.head_dim = width // self.num_heads
self.causal = causal
self.query_proj = nn.Linear(width, width)
self.key_proj = nn.Linear(width, width)
self.value_proj = nn.Linear(width, width)
self.out_proj = nn.Linear(width, width)
def forward(self, x, rope):
batch, frames, width = x.shape
shape = (batch, frames, self.num_heads, self.head_dim)
q, k, v = (
projection(x).reshape(shape)
for projection in (self.query_proj, self.key_proj, self.value_proj)
)
cos, sin = rope
q = q * cos + rotate_half(q) * sin
k = k * cos + rotate_half(k) * sin
out = F.scaled_dot_product_attention(
q.transpose(1, 2), k.transpose(1, 2), v.transpose(1, 2), is_causal=self.causal
)
return self.out_proj(out.transpose(1, 2).reshape(batch, frames, width))
class FeedForward(nn.Module):
def __init__(self, cfg: Dict):
super().__init__()
self.w_1 = nn.Linear(cfg["hidden_size"], cfg["intermediate_size"])
self.w_2 = nn.Linear(cfg["intermediate_size"], cfg["hidden_size"])
def forward(self, x):
return self.w_2(F.gelu(self.w_1(x)))
class ConvolutionModule(nn.Module):
def __init__(self, cfg: Dict):
super().__init__()
width = cfg["hidden_size"]
kernel = cfg["conv_depthwise_kernel_size"]
eps = cfg["layer_norm_eps"]
self.layer_norm = nn.LayerNorm(width, eps=eps)
self.conv_block = nn.Sequential(
Transpose(),
nn.Conv1d(width, 2 * width, 1, bias=False),
nn.GLU(dim=1),
nn.Conv1d(
width,
width,
kernel,
padding=(kernel - 1) // 2,
groups=width,
bias=False,
),
nn.Sequential(Transpose(), nn.LayerNorm(width, eps=eps), Transpose()),
nn.GELU(),
nn.Conv1d(width, width, 1, bias=False),
Transpose(),
)
def forward(self, x):
return self.conv_block(self.layer_norm(x))
class ConformerBlock(nn.Module):
def __init__(self, cfg: Dict, causal: bool):
super().__init__()
width, eps = cfg["hidden_size"], cfg["layer_norm_eps"]
self.ffn1_layer_norm = nn.LayerNorm(width, eps=eps)
self.ffn1 = FeedForward(cfg)
self.attn_layer_norm = nn.LayerNorm(width, eps=eps)
self.attn = SelfAttention(cfg, causal)
self.conv_module = ConvolutionModule(cfg)
self.ffn2_layer_norm = nn.LayerNorm(width, eps=eps)
self.ffn2 = FeedForward(cfg)
self.final_layer_norm = nn.LayerNorm(width, eps=eps)
def forward(self, x, rope):
x = x + 0.5 * self.ffn1(self.ffn1_layer_norm(x))
x = self.attn(self.attn_layer_norm(x), rope) + x
x = self.conv_module(x) + x
x = x + 0.5 * self.ffn2(self.ffn2_layer_norm(x))
return self.final_layer_norm(x)
class MERT2Encoder(nn.Module):
"""Mono 24 kHz waveform to 25 Hz Conformer block outputs."""
def __init__(self, cfg: Dict, causal: bool, layers: int):
super().__init__()
self.feature_extractor = MelFrontend(cfg)
channels = [cfg["num_mel_bins"]] + list(cfg["subsampling_channels"])
self.subsampling_module = nn.Sequential(
*[
ConvNextBlock(
channels[i],
channels[i + 1],
(1, 2, 2)[i],
cfg["subsampling_depths"][i],
cfg["subsampling_layer_norm_eps"],
)
for i in range(3)
]
)
self.layers = nn.ModuleList([ConformerBlock(cfg, causal) for _ in range(layers)])
self.embed_positions = RotaryEmbedding(cfg)
def forward(self, waveform: torch.Tensor, output: int) -> torch.Tensor:
mel = self.feature_extractor(waveform)
first = self.subsampling_module[0].convnext_layers[0].depthwise_block[1]
x = self.subsampling_module(mel.to(first.weight.dtype))
rope = self.embed_positions(x)
for index, layer in enumerate(self.layers):
x = layer(x, rope)
if index == output:
return x
raise ValueError(f"Requested encoder block {output}, but only {len(self.layers)} are present")
@torch.inference_mode()
def features(self, mono24: torch.Tensor, output: int, chunk: float = 0) -> torch.Tensor:
"""Return fp32 [frames, 1024] features, optionally in fixed-second pieces."""
if mono24.ndim != 1 or mono24.shape[0] < 1025:
raise ValueError("The encoder needs at least 1025 mono samples at 24 kHz")
with torch.autocast(
device_type=mono24.device.type,
dtype=torch.bfloat16,
enabled=mono24.device.type == "cuda",
):
if not chunk:
return self(mono24[None], output)[0].float()
samples = int(SAMPLE_RATE * chunk)
pieces = [
piece
for piece in (mono24[start : start + samples] for start in range(0, len(mono24), samples))
if len(piece) >= SAMPLE_RATE
]
if not pieces:
raise ValueError(f"Audio is shorter than the minimum one-second {chunk}-second chunk")
full = [piece for piece in pieces if len(piece) == samples]
groups = ([torch.stack(full)] if full else []) + [piece[None] for piece in pieces if len(piece) < samples]
parts = [self(group, output) for group in groups]
frames = int(round(len(mono24) / HOP))
joined = torch.cat([part.reshape(-1, part.shape[-1]) for part in parts]).float()
return F.interpolate(
joined.T[None], size=frames, mode="linear", align_corners=False
)[0].T