Spaces:
Running on Zero
Running on Zero
File size: 11,396 Bytes
f45c5e8 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 | """Reference voice compressor: Q-Former-style bottleneck that turns a
variable-length stack of codec codes into K learnable "speaker" tokens consumed
by the decoder as a prefix.
Architecture:
Input pipeline:
packed_or_unfolded [B, T_ref, C_in]
βββ (optional) unfold_tokens β [B, T_ref, C_total] if dataset is packed
βββ dequantize_codes β [B, T_ref, C_total] float in [-1, 1]
βββ Linear(C_total β d_model)β [B, T_ref, d_model]
βββ + sinusoidal PE β ref_feats [B, T_ref, d_model]
Queries:
nn.Parameter(K, d_model), batch-expanded to [B, K, d_model]
For each of L Q-Former blocks (pre-norm RMSNorm + SwiGLU FFN):
q = q + SelfAttn(RMSNorm(q)) # bidirectional
q = q + CrossAttn(RMSNorm(q), kv=ref_feats,
key_padding_mask=ref_mask)
q = q + SwiGLU_FFN(RMSNorm(q))
Output: [B, K, d_model] β decoder prefix
"""
from __future__ import annotations
import math
from typing import List, Optional, Tuple
import torch
import torch.nn as nn
import torch.nn.functional as F
from .codec_ops import dequantize_codes, unfold_tokens
class RMSNorm(nn.Module):
"""Classic RMSNorm with stable behavior under bf16 autocast
(compute norm in fp32 then cast back)."""
def __init__(self, dim: int, eps: float = 1e-6):
super().__init__()
self.weight = nn.Parameter(torch.ones(dim))
self.eps = eps
def forward(self, x: torch.Tensor) -> torch.Tensor:
orig_dtype = x.dtype
x32 = x.float()
rms = x32.pow(2).mean(dim=-1, keepdim=True).add(self.eps).rsqrt()
return (x32 * rms).to(orig_dtype) * self.weight
class SwiGLU(nn.Module):
"""SwiGLU FFN β two gated projections + one output projection."""
def __init__(self, d_model: int, hidden_dim: int, dropout: float = 0.0):
super().__init__()
self.gate_proj = nn.Linear(d_model, hidden_dim, bias=False)
self.up_proj = nn.Linear(d_model, hidden_dim, bias=False)
self.down_proj = nn.Linear(hidden_dim, d_model, bias=False)
self.dropout = nn.Dropout(dropout) if dropout > 0 else nn.Identity()
def forward(self, x: torch.Tensor) -> torch.Tensor:
return self.dropout(self.down_proj(F.silu(self.gate_proj(x)) * self.up_proj(x)))
class SinusoidalPositionalEncoding(nn.Module):
"""Standard Transformer sinusoidal PE, lazily extended as needed."""
def __init__(self, d_model: int, max_len: int = 2048):
super().__init__()
self.d_model = d_model
self.register_buffer("_pe", self._build(max_len, d_model), persistent=False)
@staticmethod
def _build(length: int, dim: int) -> torch.Tensor:
pe = torch.zeros(length, dim, dtype=torch.float32)
pos = torch.arange(length, dtype=torch.float32).unsqueeze(1)
div = torch.exp(
torch.arange(0, dim, 2, dtype=torch.float32) * (-math.log(10000.0) / dim)
)
pe[:, 0::2] = torch.sin(pos * div)
pe[:, 1::2] = torch.cos(pos * div)
return pe
def forward(self, x: torch.Tensor) -> torch.Tensor:
"""x: [B, T, d] β x + PE[:T]"""
T = x.shape[1]
if self._pe.shape[0] < T:
self._pe = self._build(T, self.d_model).to(self._pe.device)
return x + self._pe[:T].to(dtype=x.dtype, device=x.device)
class MultiHeadAttentionBlock(nn.Module):
"""Thin wrapper around scaled_dot_product_attention with separate q / kv paths.
Supports both self-attention (pass same tensor as q and kv) and
cross-attention (pass queries as q, ref_feats as kv). Key-padding masks on
kv go through as a 4D bool tensor (True = keep, False = mask).
"""
def __init__(self, d_model: int, num_heads: int, dropout: float = 0.0):
super().__init__()
if d_model % num_heads != 0:
raise ValueError(f"d_model {d_model} must be divisible by num_heads {num_heads}")
self.d_model = d_model
self.num_heads = num_heads
self.head_dim = d_model // num_heads
self.q_proj = nn.Linear(d_model, d_model, bias=False)
self.k_proj = nn.Linear(d_model, d_model, bias=False)
self.v_proj = nn.Linear(d_model, d_model, bias=False)
self.out_proj = nn.Linear(d_model, d_model, bias=False)
self.attn_dropout_p = float(dropout)
self.resid_dropout = nn.Dropout(dropout) if dropout > 0 else nn.Identity()
def forward(
self,
q_in: torch.Tensor, # [B, Tq, d]
kv_in: torch.Tensor, # [B, Tkv, d]
kv_mask: Optional[torch.Tensor] = None, # [B, Tkv] bool, True = keep
) -> torch.Tensor:
B, Tq, _ = q_in.shape
Tkv = kv_in.shape[1]
q = self.q_proj(q_in).view(B, Tq, self.num_heads, self.head_dim).transpose(1, 2)
k = self.k_proj(kv_in).view(B, Tkv, self.num_heads, self.head_dim).transpose(1, 2)
v = self.v_proj(kv_in).view(B, Tkv, self.num_heads, self.head_dim).transpose(1, 2)
# SDPA attn_mask: broadcastable bool. True = attend.
if kv_mask is not None:
attn_mask = kv_mask.view(B, 1, 1, Tkv)
else:
attn_mask = None
out = F.scaled_dot_product_attention(
q, k, v,
attn_mask=attn_mask,
dropout_p=self.attn_dropout_p if self.training else 0.0,
is_causal=False,
) # [B, H, Tq, head_dim]
out = out.transpose(1, 2).contiguous().view(B, Tq, self.d_model)
return self.resid_dropout(self.out_proj(out))
class QFormerBlock(nn.Module):
"""One Q-Former block: SelfAttn(q) β CrossAttn(q, ref) β FFN, all pre-norm."""
def __init__(
self,
d_model: int,
num_heads: int,
ffn_hidden: int,
dropout: float = 0.1,
):
super().__init__()
self.norm_self = RMSNorm(d_model)
self.self_attn = MultiHeadAttentionBlock(d_model, num_heads, dropout)
self.norm_cross = RMSNorm(d_model)
self.cross_attn = MultiHeadAttentionBlock(d_model, num_heads, dropout)
self.norm_ffn = RMSNorm(d_model)
self.ffn = SwiGLU(d_model, ffn_hidden, dropout)
def forward(
self,
q: torch.Tensor, # [B, K, d]
ref_feats: torch.Tensor, # [B, T_ref, d]
ref_mask: Optional[torch.Tensor] = None, # [B, T_ref] bool
) -> torch.Tensor:
# Self-attn on queries (bidirectional, no mask β queries are always valid).
h = self.norm_self(q)
q = q + self.self_attn(h, h, kv_mask=None)
# Cross-attn: queries attend to ref_feats, masked on ref padding.
h = self.norm_cross(q)
q = q + self.cross_attn(h, ref_feats, kv_mask=ref_mask)
# FFN
q = q + self.ffn(self.norm_ffn(q))
return q
class RefCompressor(nn.Module):
"""Q-Former-style compressor of codec codes into K speaker tokens.
Args:
codec: Codec geometry β any object with ``num_layers``, ``fsq_levels``
and ``do_unfold`` attributes. Determines ``C_total`` and whether
the forward pass unfolds packed layers or assumes already-unfolded
input.
compressor_cfg: Hyperparameters β any object with ``num_queries``,
``num_layers``, ``num_heads``, ``d_model``,
``ffn_hidden_size_multiplier``, ``dropout``, ``queries_init_std``.
backbone_hidden_size: Fallback for ``d_model`` when
``compressor_cfg.d_model`` is None.
"""
def __init__(
self,
codec,
compressor_cfg,
backbone_hidden_size: int,
):
super().__init__()
self.num_layers_codec = int(codec.num_layers)
self.fsq_levels: List[int] = list(codec.fsq_levels)
do_unfold_on_disk = bool(codec.do_unfold)
# If the dataset is already unfolded on disk, skip unfold in forward.
self.do_unfold_in_forward = not do_unfold_on_disk
self.c_total = self.num_layers_codec * len(self.fsq_levels)
self.d_model = int(compressor_cfg.d_model) if compressor_cfg.d_model else backbone_hidden_size
self.num_queries = int(compressor_cfg.num_queries)
self.num_blocks = int(compressor_cfg.num_layers)
self.num_heads = int(compressor_cfg.num_heads)
self.ffn_hidden = self.d_model * int(compressor_cfg.ffn_hidden_size_multiplier)
self.dropout = float(compressor_cfg.dropout)
self.input_proj = nn.Linear(self.c_total, self.d_model, bias=True)
self.pos_enc = SinusoidalPositionalEncoding(self.d_model)
self.queries = nn.Parameter(
torch.randn(self.num_queries, self.d_model) * float(compressor_cfg.queries_init_std)
)
self.blocks = nn.ModuleList([
QFormerBlock(
d_model=self.d_model,
num_heads=self.num_heads,
ffn_hidden=self.ffn_hidden,
dropout=self.dropout,
)
for _ in range(self.num_blocks)
])
# Final norm so the prefix's scale matches other inputs into the decoder.
# The learnable scalar starts at 1/sqrt(d_model) so the initial output
# L2 β 1. Shape [1], not scalar [] β matches published checkpoints
# after normalize_scalar_shapes.
self.final_norm = RMSNorm(self.d_model)
self.output_scale = nn.Parameter(torch.tensor([1.0 / math.sqrt(self.d_model)]))
def forward(
self,
ref_codes: torch.Tensor, # [B, T_ref, C_in] long
ref_mask: Optional[torch.Tensor] = None, # [B, T_ref] bool, True = real frame
) -> Tuple[torch.Tensor, torch.Tensor]:
"""Returns (prefix_out, q_normed):
prefix_out: [B, K, d_model] β output_scale * RMSNorm(q), what the decoder consumes.
q_normed: [B, K, d_model] β RMSNorm(q) before output_scale (RMS=1 per token).
"""
if ref_codes.dim() != 3:
raise ValueError(f"ref_codes must be [B, T_ref, C_in]; got {tuple(ref_codes.shape)}")
# 1. Unfold only if the dataset is packed on disk.
if self.do_unfold_in_forward:
# unfold_tokens expects [B, C, T], but we have [B, T, C] β transpose.
x = unfold_tokens(ref_codes.transpose(1, 2), self.fsq_levels).transpose(1, 2)
else:
x = ref_codes # already [B, T, C_total]
# 2. Dequantize to [-1, 1] floats; cast with the module dtype.
x = dequantize_codes(x, self.fsq_levels, self.num_layers_codec)
x = x.to(dtype=self.input_proj.weight.dtype)
# 3. Linear project to d_model, add sinusoidal PE.
x = self.input_proj(x)
x = self.pos_enc(x)
# 4. Expand learnable queries across the batch.
B = x.shape[0]
q = self.queries.to(dtype=x.dtype, device=x.device).unsqueeze(0).expand(B, -1, -1)
# 5. Run Q-Former blocks; queries attend to ref_feats with the ref mask.
for block in self.blocks:
q = block(q, ref_feats=x, ref_mask=ref_mask)
q_normed = self.final_norm(q) # RMS=1 per token
return self.output_scale * q_normed, q_normed
|