Download code/model.py from maxpicy/mamba-cot-tts: direct link, hf CLI and curl.
- Browser
- Download file 58.9 kB
-
https://huggingface.co/maxpicy/mamba-cot-tts/resolve/main/code/model.py
- Command line
-
hf download hf://maxpicy/mamba-cot-tts/code/model.py
-
curl -L -o model.py https://huggingface.co/maxpicy/mamba-cot-tts/resolve/main/code/model.py
58.9 kB
| # model.py: MambaCoTModel: the backbone (pure Mamba-2, the hybrid attention graft, or the transformer), speaker | |
| # input injection, the plan injector, and the Mamba-2 depth module that generates codebooks 1-9 per frame. | |
| from typing import Optional | |
| import torch | |
| import torch.nn as nn | |
| import torch.nn.functional as F | |
| from transformers import AutoTokenizer | |
| from mamba_ssm import MambaLMHeadModel | |
| from tokenizer import expand_tokenizer, TokenRegistry, load_base_tokenizer, NUM_SPEECH_TOKENS | |
| try: | |
| from torch.nn import RMSNorm | |
| except ImportError: | |
| from mamba_ssm.ops.triton.layer_norm import RMSNorm | |
| class AdaLN(nn.Module): | |
| def __init__(self, d_model: int, speaker_dim: int = 256): | |
| super().__init__() | |
| self.proj = nn.Linear(speaker_dim, 2 * d_model) | |
| nn.init.zeros_(self.proj.weight) | |
| nn.init.zeros_(self.proj.bias) | |
| def forward(self, x: torch.Tensor, speaker_emb: torch.Tensor) -> torch.Tensor: | |
| scale, shift = self.proj(speaker_emb).chunk(2, dim=-1) | |
| return x * (1 + scale.unsqueeze(1)) + shift.unsqueeze(1) | |
| class SpeakerEncoder(nn.Module): | |
| def __init__(self, num_speakers: int = 1000, speaker_dim: int = 256): | |
| super().__init__() | |
| self.embedding = nn.Embedding(num_speakers, speaker_dim) | |
| def forward(self, speaker_ids: torch.LongTensor) -> torch.Tensor: | |
| return self.embedding(speaker_ids) | |
| class DelayEmbedding(nn.Module): | |
| def __init__(self, base: nn.Embedding, num_levels: int = 8, | |
| codebook_size: int = 2048, codebook_sizes=None): | |
| super().__init__() | |
| self.base = base | |
| self.num_levels = num_levels | |
| self.codebook_size = codebook_size | |
| self.level_sizes = (list(codebook_sizes) if codebook_sizes | |
| else [codebook_size] * num_levels) | |
| assert len(self.level_sizes) == num_levels | |
| ref = base.weight | |
| self.level_embeds = nn.ModuleList([ | |
| nn.Embedding(self.level_sizes[k] + 1, ref.shape[1], | |
| device=ref.device, dtype=ref.dtype) | |
| for k in range(num_levels) | |
| ]) | |
| for e in self.level_embeds: | |
| nn.init.zeros_(e.weight) | |
| self._codes = None | |
| self._speaker = None | |
| self._plan = None | |
| def weight(self): | |
| return self.base.weight | |
| def set_codes(self, codes): | |
| self._codes = codes | |
| def set_speaker(self, vec): | |
| self._speaker = vec | |
| def set_plan(self, vec): | |
| self._plan = vec | |
| def forward(self, input_ids: torch.Tensor) -> torch.Tensor: | |
| emb = self.base(input_ids) | |
| codes = self._codes | |
| self._codes = None | |
| if codes is not None: | |
| valid = codes[..., 0] >= 0 | |
| for k in range(self.num_levels): | |
| idx = (codes[..., k] + 1).clamp(min=0) | |
| emb = emb + self.level_embeds[k](idx) * valid.unsqueeze(-1) | |
| plan = self._plan | |
| self._plan = None | |
| if plan is not None: | |
| emb = emb + plan | |
| spk = self._speaker | |
| self._speaker = None | |
| if spk is not None: | |
| emb = emb + spk.unsqueeze(1) | |
| return emb | |
| PLAN_BIN_CHANNELS = ("pitch", "duration", "energy") | |
| PLAN_BIN_ABSENT = -1 | |
| _PLAN_WITHOUT_INJECTION_WARNED = False | |
| def _warn_plan_without_injection() -> None: | |
| global _PLAN_WITHOUT_INJECTION_WARNED | |
| if not _PLAN_WITHOUT_INJECTION_WARNED: | |
| _PLAN_WITHOUT_INJECTION_WARNED = True | |
| print("[plan-injection] WARNING: plan tensors were supplied but plan " | |
| "injection is DISABLED on this model (enable_plan_injection() " | |
| "was never called) — the plan reaches the model only through the " | |
| "<THINK> prefix. Intended for the injection-OFF arm; if this run " | |
| "was meant to inject, the flag is missing.", flush=True) | |
| def build_plan_schedule(plan_durations, audio_start, n_frames, seq_len=None, | |
| device=None) -> torch.LongTensor: | |
| rows = _plan_duration_rows(plan_durations) | |
| B = len(rows) | |
| starts = _as_int_vector(audio_start, B, "audio_start") | |
| counts = _as_int_vector(n_frames, B, "n_frames") | |
| for b in range(B): | |
| if starts[b] < 0: | |
| raise ValueError(f"audio_start[{b}]={starts[b]} is negative — " | |
| f"audio_start is the first AUDIO FRAME position") | |
| if counts[b] < 0: | |
| raise ValueError(f"n_frames[{b}]={counts[b]} is negative") | |
| need = max((starts[b] + counts[b] for b in range(B)), default=0) | |
| if seq_len is None: | |
| seq_len = need | |
| else: | |
| seq_len = int(seq_len) | |
| if need > seq_len: | |
| raise ValueError( | |
| f"audio block runs past seq_len: max(audio_start + n_frames)=" | |
| f"{need} > seq_len={seq_len}. Pass the COLLATED sequence length " | |
| f"and the per-row audio_start/n_frames of that same batch.") | |
| sched = torch.full((B, seq_len), -1, dtype=torch.long, device=device) | |
| for b in range(B): | |
| durs, a0, nf = rows[b], starts[b], counts[b] | |
| if not durs or nf == 0: | |
| continue | |
| ends = torch.tensor(durs, dtype=torch.long, device=device).cumsum(0) | |
| last_real = max((w for w, d in enumerate(durs) if d > 0), default=-1) | |
| if last_real < 0: | |
| continue | |
| f = torch.arange(nf, dtype=torch.long, device=device) | |
| idx = torch.searchsorted(ends, f, right=True).clamp(max=last_real) | |
| sched[b, a0:a0 + nf] = idx | |
| return sched | |
| def _plan_duration_rows(plan_durations): | |
| if hasattr(plan_durations, "ndim") and hasattr(plan_durations, "tolist"): | |
| if int(plan_durations.ndim) != 2: | |
| raise ValueError(f"plan_durations array must be [B, W], got " | |
| f"{int(plan_durations.ndim)} dimension(s)") | |
| raw = plan_durations.tolist() | |
| else: | |
| raw = list(plan_durations) | |
| if raw and isinstance(raw[0], (int, float)): | |
| raise ValueError("plan_durations must be a list of PER-ROW duration " | |
| "lists (or a [B, W] array) — got a flat sequence") | |
| raw = [list(r) for r in raw] | |
| rows = [] | |
| for b, row in enumerate(raw): | |
| out, padding = [], False | |
| for w, v in enumerate(row): | |
| v = float(v) | |
| if v < 0: | |
| if v != PLAN_BIN_ABSENT: | |
| raise ValueError( | |
| f"plan_durations[{b}][{w}]={v}: the only accepted " | |
| f"negative is the pad sentinel {PLAN_BIN_ABSENT}") | |
| padding = True | |
| continue | |
| if padding: | |
| raise ValueError( | |
| f"plan_durations[{b}]: pad sentinel at an interior position " | |
| f"(word {w} is a real duration after padding started) — " | |
| f"padding must be a trailing run") | |
| out.append(int(v + 0.5)) | |
| rows.append(out) | |
| return rows | |
| def _as_int_vector(x, B: int, name: str): | |
| if hasattr(x, "tolist"): | |
| x = x.tolist() | |
| try: | |
| iter(x) | |
| except TypeError: | |
| return [int(x)] * B | |
| vals = [int(v) for v in x] | |
| if len(vals) != B: | |
| raise ValueError(f"{name} has length {len(vals)} but the batch has " | |
| f"{B} row(s)") | |
| return vals | |
| class PlanInjector(nn.Module): | |
| def __init__(self, d_model: int, n_pitch_bins: int, n_duration_bins: int, | |
| n_energy_bins: int, d_plan: int = 256, | |
| scaffold_dropout: float = 0.3, device=None, dtype=None): | |
| super().__init__() | |
| kw = {"device": device, "dtype": dtype} | |
| if not 0.0 <= scaffold_dropout <= 1.0: | |
| raise ValueError(f"scaffold_dropout must be a probability in " | |
| f"[0, 1], got {scaffold_dropout}") | |
| self.bin_sizes = (int(n_pitch_bins), int(n_duration_bins), | |
| int(n_energy_bins)) | |
| self.d_plan = int(d_plan) | |
| self.scaffold_dropout = float(scaffold_dropout) | |
| self.embeds = nn.ModuleList([ | |
| nn.Embedding(n + 1, d_plan, **kw) for n in self.bin_sizes | |
| ]) | |
| for e in self.embeds: | |
| nn.init.normal_(e.weight, std=0.02) | |
| with torch.no_grad(): | |
| e.weight[0].zero_() | |
| self.proj = nn.Linear(d_plan, d_model, **kw) | |
| nn.init.zeros_(self.proj.weight) | |
| nn.init.zeros_(self.proj.bias) | |
| def word_vectors(self, plan_bins: torch.Tensor) -> torch.Tensor: | |
| if plan_bins.dim() != 3 or plan_bins.shape[-1] != len(PLAN_BIN_CHANNELS): | |
| raise ValueError( | |
| f"plan_bins must be [B, W, {len(PLAN_BIN_CHANNELS)}] with column " | |
| f"order {PLAN_BIN_CHANNELS}, got shape {tuple(plan_bins.shape)}") | |
| vec = None | |
| for c, name in enumerate(PLAN_BIN_CHANNELS): | |
| n_bins = self.bin_sizes[c] | |
| col = plan_bins[..., c] | |
| hi = n_bins if name == "pitch" else n_bins - 1 | |
| bad = (col < PLAN_BIN_ABSENT) | (col > hi) | |
| if bool(bad.any()): | |
| v = int(col[bad][0]) | |
| raise ValueError( | |
| f"plan_bins {name} bin {v} out of range: expected " | |
| f"0..{n_bins - 1} or {PLAN_BIN_ABSENT} (pad/absent)" | |
| + (f" or {n_bins} (unvoiced sentinel)" if name == "pitch" | |
| else "")) | |
| idx = torch.where((col >= 0) & (col < n_bins), col + 1, | |
| torch.zeros_like(col)) | |
| e = self.embeds[c](idx) | |
| vec = e if vec is None else vec + e | |
| return vec | |
| def forward(self, plan_bins: torch.Tensor, | |
| schedule: torch.Tensor) -> torch.Tensor: | |
| if schedule.dim() != 2: | |
| raise ValueError(f"schedule must be [B, S], got " | |
| f"{tuple(schedule.shape)}") | |
| if plan_bins.shape[0] != schedule.shape[0]: | |
| raise ValueError(f"batch mismatch: plan_bins B={plan_bins.shape[0]} " | |
| f"vs schedule B={schedule.shape[0]}") | |
| W = plan_bins.shape[1] | |
| if W == 0: | |
| raise ValueError( | |
| "plan_bins has zero words. A batch in which no row carries a " | |
| "plan must OMIT plan_bins entirely (as the collator omits " | |
| "prosody_vecs), so the injection channel is off rather than " | |
| "silently injecting nothing.") | |
| top = int(schedule.max()) if schedule.numel() else -1 | |
| if top >= W: | |
| raise ValueError( | |
| f"schedule references word {top} but plan_bins holds {W} word " | |
| f"slot(s) — schedule and plan came from different rows.") | |
| vec = self.word_vectors(plan_bins) | |
| valid = schedule >= 0 | |
| idx = schedule.clamp(min=0).unsqueeze(-1).expand(-1, -1, self.d_plan) | |
| gathered = vec.gather(1, idx) | |
| out = self.proj(gathered) | |
| out = out * valid.unsqueeze(-1).to(out.dtype) | |
| if self.training and self.scaffold_dropout > 0.0: | |
| keep = (torch.rand(out.shape[0], 1, 1, device=out.device) | |
| >= self.scaffold_dropout).to(out.dtype) | |
| out = out * keep | |
| return out | |
| class DepthModule(nn.Module): | |
| def __init__(self, d_model: int, num_levels: int = 8, | |
| codebook_size: int = 2048, d_depth: int = 1024, | |
| num_layers: int = 2, cross_frame: int = 0, | |
| codebook_sizes=None, device=None, dtype=None, | |
| depth_arch: str = "gru", depth_heads: int = 8, | |
| level_decay: float = 1.0, depth_cond: str = "add"): | |
| super().__init__() | |
| kw = {"device": device, "dtype": dtype} | |
| self.depth_arch = depth_arch | |
| self.level_decay = float(level_decay) | |
| self.num_levels = num_levels | |
| self.codebook_size = codebook_size | |
| self.level_sizes = (list(codebook_sizes) if codebook_sizes | |
| else [codebook_size] * num_levels) | |
| assert len(self.level_sizes) == num_levels | |
| self.d_depth = d_depth | |
| self.cross_frame = int(cross_frame) | |
| self.h_proj = nn.Linear(d_model, d_depth, bias=False, **kw) | |
| self.depth_cond = depth_cond | |
| if depth_cond == "prefix": | |
| self.shared_embed = nn.Embedding(max(self.level_sizes), d_depth, **kw) | |
| self.level_embed = nn.Parameter(torch.zeros(num_levels, d_depth, **kw)) | |
| nn.init.normal_(self.shared_embed.weight, std=0.02) | |
| nn.init.normal_(self.level_embed, std=0.02) | |
| else: | |
| self.code_embeds = nn.ModuleList([ | |
| nn.Embedding(self.level_sizes[k], d_depth, **kw) | |
| for k in range(num_levels - 1) | |
| ]) | |
| if self.cross_frame > 0: | |
| self.cross_embeds = nn.ModuleList([ | |
| nn.Embedding(self.level_sizes[1 + c], d_depth, **kw) | |
| for c in range(self.cross_frame) | |
| ]) | |
| self.cross_start = nn.Parameter(torch.zeros(d_depth, **kw)) | |
| for e in self.cross_embeds: | |
| nn.init.normal_(e.weight, std=0.02) | |
| if depth_cond != "prefix": | |
| self.start = nn.Parameter(torch.zeros(d_depth, **kw)) | |
| if depth_arch == "mamba2": | |
| from mamba_ssm.modules.mamba2 import Mamba2 | |
| self.depth_ssm = nn.ModuleList([ | |
| Mamba2(d_model=d_depth, **kw) for _ in range(num_layers) | |
| ]) | |
| self.depth_norms = nn.ModuleList([ | |
| RMSNorm(d_depth, **kw) for _ in range(num_layers) | |
| ]) | |
| elif depth_arch == "transformer": | |
| self.depth_pos = nn.Parameter(torch.zeros(num_levels, d_depth, **kw)) | |
| nn.init.normal_(self.depth_pos, std=0.02) | |
| layer = nn.TransformerEncoderLayer( | |
| d_model=d_depth, nhead=depth_heads, | |
| dim_feedforward=4 * d_depth, dropout=0.0, | |
| activation="gelu", batch_first=True, norm_first=True, **kw) | |
| self.depth_tf = nn.TransformerEncoder(layer, num_layers=num_layers) | |
| else: | |
| self.gru = nn.GRU(d_depth, d_depth, num_layers=num_layers, | |
| batch_first=True, **kw) | |
| self.norm = RMSNorm(d_depth, **kw) | |
| self.heads = nn.ModuleList([ | |
| nn.Linear(d_depth, self.level_sizes[k], bias=False, **kw) | |
| for k in range(num_levels) | |
| ]) | |
| for h in self.heads: | |
| nn.init.normal_(h.weight, std=0.02) | |
| if depth_cond != "prefix": | |
| for e in self.code_embeds: | |
| nn.init.normal_(e.weight, std=0.02) | |
| def _cross_summary(self, prev_codes): | |
| if self.cross_frame <= 0: | |
| return None | |
| if prev_codes is None: | |
| return self.cross_start.unsqueeze(0) | |
| lv = prev_codes[:, 1:1 + self.cross_frame] | |
| valid = (lv >= 0).all(dim=-1, keepdim=True) | |
| summ = torch.zeros(prev_codes.shape[0], self.d_depth, | |
| device=prev_codes.device, dtype=self.cross_start.dtype) | |
| for c in range(self.cross_frame): | |
| summ = summ + self.cross_embeds[c](lv[:, c].clamp(min=0)) | |
| return torch.where(valid, summ, self.cross_start.unsqueeze(0)) | |
| def _inputs_teacher_forced(self, h, targets, prev_codes=None): | |
| hp = self.h_proj(h) | |
| cs = self._cross_summary(prev_codes) | |
| if cs is not None: | |
| hp = hp + cs | |
| if self.depth_cond == "prefix": | |
| steps = [hp] | |
| for k in range(self.num_levels - 1): | |
| steps.append(self.shared_embed(targets[:, k].clamp(min=0))) | |
| x = torch.stack(steps, dim=1) | |
| return x + self.level_embed[:self.num_levels].unsqueeze(0) | |
| steps = [self.start.unsqueeze(0).expand(h.shape[0], -1)] | |
| for k in range(self.num_levels - 1): | |
| steps.append(self.code_embeds[k](targets[:, k].clamp(min=0))) | |
| x = torch.stack(steps, dim=1) | |
| return x + hp.unsqueeze(1) | |
| def _depth_run(self, x): | |
| if self.depth_arch == "mamba2": | |
| for norm, blk in zip(self.depth_norms, self.depth_ssm): | |
| x = x + blk(norm(x)) | |
| return x | |
| if self.depth_arch == "transformer": | |
| K = x.shape[1] | |
| x = x + self.depth_pos[:K].unsqueeze(0) | |
| mask = torch.triu(torch.full((K, K), float("-inf"), | |
| device=x.device, dtype=x.dtype), | |
| diagonal=1) | |
| return self.depth_tf(x, mask=mask) | |
| y, _ = self.gru(x) | |
| return y | |
| def forward_loss(self, h, targets, semantic_weight: float = 1.0, | |
| prev_codes=None, frame_weights=None): | |
| x = self._inputs_teacher_forced(h, targets, prev_codes) | |
| y = self._depth_run(x) | |
| y = self.norm(y) | |
| d = float(getattr(self, "level_decay", 1.0) or 1.0) | |
| ws = [semantic_weight if k == 0 else d ** (k - 1) | |
| for k in range(self.num_levels)] | |
| if d != 1.0: | |
| scale = self.num_levels / max(sum(ws), 1e-8) | |
| ws = [w * scale for w in ws] | |
| loss = torch.tensor(0.0, device=h.device, dtype=torch.float32) | |
| for k in range(self.num_levels): | |
| logits = self.heads[k](y[:, k]).float() | |
| if k == 0 and frame_weights is not None: | |
| per = F.cross_entropy(logits, targets[:, 0], reduction="none") | |
| w = frame_weights.to(per.dtype) | |
| ce = (per * w).sum() / w.sum().clamp(min=1e-8) | |
| else: | |
| ce = F.cross_entropy(logits, targets[:, k]) | |
| loss = loss + ws[k] * ce | |
| return loss | |
| def generate_frame(self, h, level_temps=None, top_k=0, top_p=0.0, | |
| sticky_prev=None, sticky_bias=0.0, prev_codes=None, | |
| antifan_prev2=None, antifan_bias=0.0, | |
| antifan_levels=None, rep_window=None, | |
| rep_penalty=1.0, ras_tau=0.0, ras_win=10, | |
| return_argmax=False, cb0_mask=None): | |
| _af_levels = (set(antifan_levels) if antifan_levels is not None | |
| else set(range(1, min(4, self.num_levels)))) | |
| amax = [] if return_argmax else None | |
| hp = self.h_proj(h) | |
| if self.cross_frame > 0: | |
| pc = None | |
| if prev_codes is not None: | |
| pc = torch.as_tensor(prev_codes, device=h.device, | |
| dtype=torch.long).view(1, -1) | |
| hp = hp + self._cross_summary(pc) | |
| state = None | |
| if self.depth_cond == "prefix": | |
| steps = [(hp + self.level_embed[0]).unsqueeze(1)] | |
| else: | |
| steps = [(self.start.unsqueeze(0) + hp).unsqueeze(1)] | |
| out = [] | |
| for k in range(self.num_levels): | |
| if self.depth_arch in ("transformer", "mamba2"): | |
| yk = self._depth_run(torch.cat(steps, dim=1))[:, -1] | |
| else: | |
| y, state = self.gru(steps[-1], state) | |
| yk = y[:, -1] | |
| logits = self.heads[k](self.norm(yk)).float()[0] | |
| if (sticky_bias and sticky_prev is not None and k >= 1 | |
| and 0 <= sticky_prev[k] < logits.shape[-1]): | |
| logits[sticky_prev[k]] += sticky_bias | |
| if (antifan_bias and antifan_prev2 is not None | |
| and sticky_prev is not None | |
| and k in _af_levels | |
| and 0 <= antifan_prev2[k] < logits.shape[-1] | |
| and antifan_prev2[k] != sticky_prev[k]): | |
| logits[antifan_prev2[k]] -= antifan_bias | |
| if k == 0 and cb0_mask is not None: | |
| logits[cb0_mask] = float("-inf") | |
| base_logits = logits.clone() if ras_tau else None | |
| rp = rep_penalty | |
| if rp is not None and not isinstance(rp, (int, float)): | |
| rp = rp[k] if k < len(rp) else 1.0 | |
| if rp and rp != 1.0 and rep_window: | |
| hist = rep_window[k] if k < len(rep_window) else () | |
| if hist: | |
| idx = torch.as_tensor(sorted({int(c) for c in hist | |
| if 0 <= c < logits.shape[-1]}), | |
| device=logits.device, dtype=torch.long) | |
| if idx.numel(): | |
| s = logits[idx] | |
| logits[idx] = torch.where(s < 0, s * rp, s / rp) | |
| t = level_temps[k] if level_temps else 0.0 | |
| if t and t > 0: | |
| lk = logits / t | |
| if top_k and top_k > 0: | |
| kth = torch.topk(lk, min(top_k, lk.shape[-1]))[0][..., -1] | |
| lk = lk.masked_fill(lk < kth, float("-inf")) | |
| if top_p and 0.0 < top_p < 1.0: | |
| sl, si = torch.sort(lk, descending=True) | |
| cum = torch.cumsum(F.softmax(sl, dim=-1), dim=-1) | |
| cut = cum - F.softmax(sl, dim=-1) > top_p | |
| sl = sl.masked_fill(cut, float("-inf")) | |
| lk = torch.full_like(lk, float("-inf")).scatter(-1, si, sl) | |
| ck = int(torch.multinomial(F.softmax(lk, dim=-1), 1).item()) | |
| if ras_tau and rep_window: | |
| rt = ras_tau | |
| if not isinstance(rt, (int, float)): | |
| rt = rt[k] if k < len(rt) else 0.0 | |
| if rt and rt > 0: | |
| hist = rep_window[k] if k < len(rep_window) else () | |
| win = list(hist)[-ras_win:] if ras_win else list(hist) | |
| if win and sum(1 for c in win if c == ck) >= max( | |
| 1, int(round(len(win) * rt))): | |
| ck = int(torch.multinomial( | |
| F.softmax(base_logits / t, dim=-1), 1).item()) | |
| else: | |
| ck = int(logits.argmax(-1).item()) | |
| out.append(ck) | |
| if amax is not None: | |
| amax.append(int(logits.argmax(-1).item())) | |
| if k < self.num_levels - 1: | |
| idx = torch.tensor([ck], device=h.device) | |
| if self.depth_cond == "prefix": | |
| nxt = self.shared_embed(idx) + self.level_embed[k + 1] | |
| else: | |
| nxt = self.code_embeds[k](idx) + hp | |
| steps.append(nxt.unsqueeze(1)) | |
| return (out, amax) if return_argmax else out | |
| class AttentionMixer(nn.Module): | |
| def __init__(self, d_model: int, head_dim: int = 64, | |
| rope_base: float = 10000.0, device=None, dtype=None, | |
| layer_idx=None): | |
| super().__init__() | |
| assert d_model % head_dim == 0 | |
| kw = {"device": device, "dtype": dtype} | |
| self.num_heads = d_model // head_dim | |
| self.head_dim = head_dim | |
| self.layer_idx = layer_idx | |
| self.in_proj = nn.Linear(d_model, 3 * d_model, bias=False, **kw) | |
| self.out_proj = nn.Linear(d_model, d_model, bias=False, **kw) | |
| nn.init.xavier_uniform_(self.in_proj.weight) | |
| nn.init.xavier_uniform_(self.out_proj.weight) | |
| inv_freq = 1.0 / (rope_base ** ( | |
| torch.arange(0, head_dim, 2, device=device).float() / head_dim)) | |
| self.register_buffer("inv_freq", inv_freq, persistent=False) | |
| def _rope(self, x, positions): | |
| freqs = torch.outer(positions.float(), self.inv_freq) | |
| cos = freqs.cos()[None, None, :, :].to(x.dtype) | |
| sin = freqs.sin()[None, None, :, :].to(x.dtype) | |
| x1, x2 = x[..., 0::2], x[..., 1::2] | |
| out = torch.empty_like(x) | |
| out[..., 0::2] = x1 * cos - x2 * sin | |
| out[..., 1::2] = x1 * sin + x2 * cos | |
| return out | |
| def forward(self, hidden_states, inference_params=None, **kwargs): | |
| B, S, D = hidden_states.shape | |
| qkv = self.in_proj(hidden_states) | |
| q, k, v = qkv.chunk(3, dim=-1) | |
| shape = (B, S, self.num_heads, self.head_dim) | |
| q = q.view(shape).transpose(1, 2) | |
| k = k.view(shape).transpose(1, 2) | |
| v = v.view(shape).transpose(1, 2) | |
| if inference_params is None: | |
| pos = torch.arange(S, device=hidden_states.device) | |
| q = self._rope(q, pos) | |
| k = self._rope(k, pos) | |
| attn = F.scaled_dot_product_attention(q, k, v, is_causal=True) | |
| else: | |
| if self.layer_idx is None: | |
| raise RuntimeError("AttentionMixer.layer_idx is required for the KV cache " | |
| "(enable_hybrid_attention sets it)") | |
| off = int(inference_params.seqlen_offset) | |
| pos = torch.arange(off, off + S, device=hidden_states.device) | |
| q = self._rope(q, pos) | |
| k = self._rope(k, pos) | |
| cache = (inference_params.key_value_memory_dict.get(self.layer_idx) | |
| if off > 0 else None) | |
| if cache is not None: | |
| k = torch.cat([cache[0], k], dim=2) | |
| v = torch.cat([cache[1], v], dim=2) | |
| inference_params.key_value_memory_dict[self.layer_idx] = (k, v) | |
| if S == 1: | |
| attn = F.scaled_dot_product_attention(q, k, v, is_causal=False) | |
| elif off == 0: | |
| attn = F.scaled_dot_product_attention(q, k, v, is_causal=True) | |
| else: | |
| L = k.shape[2] | |
| mask = torch.ones(S, L, dtype=torch.bool, device=q.device).tril(diagonal=L - S) | |
| attn = F.scaled_dot_product_attention(q, k, v, attn_mask=mask) | |
| attn = attn.transpose(1, 2).reshape(B, S, D) | |
| return self.out_proj(attn) | |
| class ContinuousLatentHead(nn.Module): | |
| def __init__(self, d_model: int, latent_dim: int = 512, cb0_size: int = 2048, | |
| d_hidden: int = 1024, n_layers: int = 4, device=None, dtype=None): | |
| super().__init__() | |
| kw = {"device": device, "dtype": torch.float32} | |
| self.latent_dim = latent_dim | |
| self.d_hidden = d_hidden | |
| self.h_proj = nn.Linear(d_model, d_hidden, **kw) | |
| self.cb0_embed = nn.Embedding(cb0_size, d_hidden, **kw) | |
| self.x_in = nn.Linear(latent_dim, d_hidden, **kw) | |
| self.t_embed = nn.Sequential( | |
| nn.Linear(1, d_hidden, **kw), nn.SiLU(), | |
| nn.Linear(d_hidden, d_hidden, **kw)) | |
| self.blocks = nn.ModuleList([ | |
| nn.Sequential(nn.Linear(d_hidden, d_hidden, **kw), nn.SiLU(), | |
| nn.Linear(d_hidden, d_hidden, **kw)) | |
| for _ in range(n_layers)]) | |
| self.norm = RMSNorm(d_hidden, device=device, dtype=torch.float32) | |
| self.out = nn.Linear(d_hidden, latent_dim, **kw) | |
| nn.init.zeros_(self.out.weight) | |
| nn.init.zeros_(self.out.bias) | |
| def _cond(self, h, cb0): | |
| return self.h_proj(h.float()) + self.cb0_embed(cb0.clamp(min=0)) | |
| def _velocity(self, x_t, t, cond): | |
| z = self.x_in(x_t) + self.t_embed(t) + cond | |
| for blk in self.blocks: | |
| z = z + blk(z) | |
| return self.out(self.norm(z)) | |
| def flow_loss(self, h, cb0, target): | |
| with torch.autocast(device_type="cuda", enabled=False): | |
| target = target.float() | |
| cond = self._cond(h, cb0) | |
| t = torch.rand(target.shape[0], 1, device=target.device) | |
| noise = torch.randn_like(target) | |
| x_t = (1.0 - t) * noise + t * target | |
| v = self._velocity(x_t, t, cond) | |
| return F.mse_loss(v, target - noise) | |
| def sample(self, h, cb0, steps: int = 16, noise_scale: float = 1.0): | |
| with torch.autocast(device_type="cuda", enabled=False): | |
| cond = self._cond(h, cb0) | |
| x = torch.randn(h.shape[0], self.latent_dim, device=h.device) * noise_scale | |
| dt = 1.0 / steps | |
| for i in range(steps): | |
| t = torch.full((h.shape[0], 1), i * dt, device=h.device) | |
| x = x + dt * self._velocity(x, t, cond) | |
| return x | |
| class MultiTokenPrediction(nn.Module): | |
| def __init__( | |
| self, | |
| d_model: int, | |
| vocab_size: int, | |
| num_crh_heads: int = 0, | |
| crh_codebook_size: int = 2048, | |
| num_spec_heads: int = 0, | |
| shared_spec_weights: bool = True, | |
| device: str = "cpu", | |
| dtype: torch.dtype = torch.float32, | |
| ): | |
| super().__init__() | |
| self.num_crh_heads = num_crh_heads | |
| self.num_spec_heads = num_spec_heads | |
| self.shared_spec_weights = shared_spec_weights | |
| self.d_model = d_model | |
| self.vocab_size = vocab_size | |
| self.crh_codebook_size = crh_codebook_size | |
| self.num_heads = num_crh_heads + num_spec_heads | |
| if num_crh_heads > 0: | |
| self.crh_norms = nn.ModuleList([ | |
| RMSNorm(d_model, device=device, dtype=dtype) | |
| for _ in range(num_crh_heads) | |
| ]) | |
| self.crh_heads = nn.ModuleList([ | |
| nn.Linear(d_model, crh_codebook_size, bias=False, device=device, dtype=dtype) | |
| for _ in range(num_crh_heads) | |
| ]) | |
| if num_spec_heads > 0: | |
| if shared_spec_weights: | |
| self.spec_shared_norm = RMSNorm(d_model, device=device, dtype=dtype) | |
| self.spec_shared_head = nn.Linear( | |
| d_model, vocab_size, bias=False, device=device, dtype=dtype | |
| ) | |
| else: | |
| self.spec_norms = nn.ModuleList([ | |
| RMSNorm(d_model, device=device, dtype=dtype) | |
| for _ in range(num_spec_heads) | |
| ]) | |
| self.spec_heads = nn.ModuleList([ | |
| nn.Linear(d_model, vocab_size, bias=False, device=device, dtype=dtype) | |
| for _ in range(num_spec_heads) | |
| ]) | |
| self._init_weights() | |
| def _init_weights(self): | |
| if self.num_crh_heads > 0: | |
| for head in self.crh_heads: | |
| nn.init.normal_(head.weight, std=0.02) | |
| if self.num_spec_heads > 0: | |
| if self.shared_spec_weights: | |
| nn.init.normal_(self.spec_shared_head.weight, std=0.02) | |
| else: | |
| for head in self.spec_heads: | |
| nn.init.normal_(head.weight, std=0.02) | |
| def compute_loss( | |
| self, | |
| hidden_states: torch.Tensor, | |
| labels: torch.Tensor, | |
| speech_mask: torch.BoolTensor, | |
| residual_codes: Optional[torch.Tensor] = None, | |
| crh_gamma: float = 0.85, | |
| spec_gamma: float = 0.8, | |
| ) -> torch.Tensor: | |
| total_loss = torch.tensor(0.0, device=hidden_states.device, dtype=hidden_states.dtype) | |
| total_loss = total_loss + self._compute_crh_loss(hidden_states, speech_mask, residual_codes, crh_gamma) | |
| total_loss = total_loss + self._compute_spec_loss(hidden_states, labels, speech_mask, spec_gamma) | |
| return total_loss | |
| def _compute_crh_loss(self, hidden_states, speech_mask, residual_codes, gamma) -> torch.Tensor: | |
| if self.num_crh_heads == 0 or residual_codes is None: | |
| return torch.tensor(0.0, device=hidden_states.device, dtype=hidden_states.dtype) | |
| B, T, D = hidden_states.shape | |
| loss_fct = nn.CrossEntropyLoss(ignore_index=-100, reduction="mean") | |
| total = torch.tensor(0.0, device=hidden_states.device, dtype=hidden_states.dtype) | |
| for k in range(self.num_crh_heads): | |
| target_k = residual_codes[:, k, :] | |
| masked_target = target_k.clone() | |
| masked_target[~speech_mask] = -100 | |
| if (masked_target != -100).sum() == 0: | |
| continue | |
| h_normed = self.crh_norms[k](hidden_states) | |
| logits_k = self.crh_heads[k](h_normed) | |
| loss_k = loss_fct( | |
| logits_k.reshape(-1, self.crh_codebook_size), | |
| masked_target.reshape(-1), | |
| ) | |
| discount = gamma ** (k + 1) | |
| total = total + discount * loss_k | |
| return total | |
| def _compute_spec_loss(self, hidden_states, labels, speech_mask, gamma) -> torch.Tensor: | |
| if self.num_spec_heads == 0: | |
| return torch.tensor(0.0, device=hidden_states.device, dtype=hidden_states.dtype) | |
| B, T, D = hidden_states.shape | |
| loss_fct = nn.CrossEntropyLoss(ignore_index=-100, reduction="mean") | |
| total = torch.tensor(0.0, device=hidden_states.device, dtype=hidden_states.dtype) | |
| for m_idx in range(self.num_spec_heads): | |
| offset = m_idx + 2 | |
| if offset >= T: | |
| continue | |
| h = hidden_states[:, :T - offset, :] | |
| target = labels[:, offset:] | |
| target_speech_mask = speech_mask[:, offset:] | |
| valid_mask = target_speech_mask & (target != -100) | |
| if not valid_mask.any(): | |
| continue | |
| if self.shared_spec_weights: | |
| h_normed = self.spec_shared_norm(h) | |
| logits_m = self.spec_shared_head(h_normed) | |
| else: | |
| h_normed = self.spec_norms[m_idx](h) | |
| logits_m = self.spec_heads[m_idx](h_normed) | |
| masked_target = target.clone() | |
| masked_target[~valid_mask] = -100 | |
| loss_m = loss_fct( | |
| logits_m.reshape(-1, self.vocab_size), | |
| masked_target.reshape(-1), | |
| ) | |
| discount = gamma ** (m_idx + 1) | |
| total = total + discount * loss_m | |
| return total | |
| def predict_offset_2(self, hidden_state: torch.Tensor) -> torch.Tensor: | |
| assert self.shared_spec_weights, ( | |
| "predict_offset_2 requires shared_spec_weights=True" | |
| ) | |
| assert self.num_spec_heads > 0, "No speculative heads configured" | |
| if hidden_state.dim() == 1: | |
| hidden_state = hidden_state.unsqueeze(0) | |
| h_normed = self.spec_shared_norm(hidden_state) | |
| logits = self.spec_shared_head(h_normed) | |
| return logits.argmax(dim=-1) | |
| def speculative_draft( | |
| self, | |
| hidden_state: torch.Tensor, | |
| num_draft_tokens: int = 1, | |
| ) -> torch.Tensor: | |
| if num_draft_tokens > 1: | |
| import warnings | |
| warnings.warn( | |
| "speculative_draft(num_draft_tokens > 1) is unsupported with " | |
| "shared_spec_weights — returning the same offset-2 draft " | |
| f"replicated {num_draft_tokens} times. Use predict_offset_2 " | |
| "for the single-draft architecture. Multi-draft requires " | |
| "Medusa or EAGLE-style retraining.", | |
| DeprecationWarning, | |
| stacklevel=2, | |
| ) | |
| single = self.predict_offset_2(hidden_state) | |
| return single.unsqueeze(-1).expand(single.size(0), num_draft_tokens) | |
| class MambaCoTModel(nn.Module): | |
| def __init__( | |
| self, | |
| model_name: str = "state-spaces/mamba2-1.3b", | |
| device: str = "cuda", | |
| dtype: torch.dtype = torch.bfloat16, | |
| mtp_num_heads: int = 3, | |
| mtp_gamma: float = 0.8, | |
| shared_spec_weights: bool = True, | |
| num_crh_heads: int = 0, | |
| crh_codebook_size: int = 2048, | |
| crh_gamma: float = 0.85, | |
| backbone_kind: str = "mamba", | |
| ): | |
| super().__init__() | |
| self.device = device | |
| self.dtype = dtype | |
| self.mtp_gamma = mtp_gamma | |
| self.crh_gamma = crh_gamma | |
| from backbones import load_backbone | |
| self.backbone_kind = backbone_kind | |
| print(f"Loading {backbone_kind} backbone: {model_name}...") | |
| self.backbone = load_backbone(backbone_kind, model_name, | |
| device=device, dtype=dtype) | |
| self.tokenizer = load_base_tokenizer() | |
| num_added = expand_tokenizer(self.tokenizer) | |
| print(f"Added {num_added} new tokens to tokenizer (vocab: {len(self.tokenizer)}).") | |
| self._resize_embeddings() | |
| self.token_registry = TokenRegistry(self.tokenizer) | |
| d_model = self.backbone.config.d_model | |
| vocab_size = self.backbone.config.vocab_size | |
| self.mtp_module = MultiTokenPrediction( | |
| d_model=d_model, | |
| vocab_size=vocab_size, | |
| num_crh_heads=num_crh_heads, | |
| crh_codebook_size=crh_codebook_size, | |
| num_spec_heads=mtp_num_heads, | |
| shared_spec_weights=shared_spec_weights, | |
| device=device, | |
| dtype=dtype, | |
| ) | |
| self.use_speaker_conditioning = False | |
| self.use_speaker_input = False | |
| self.use_prosody_input = False | |
| self.use_plan_injection = False | |
| self.use_delay_pattern = False | |
| self.use_depth_module = False | |
| self.use_flow_head = False | |
| self.use_hybrid_attention = False | |
| self.hybrid_replaced_layers = [] | |
| def enable_delay_pattern(self, num_levels: int = 8, codebook_size: int = 2048, | |
| codebook_sizes=None): | |
| sizes = (list(codebook_sizes) if codebook_sizes | |
| else [codebook_size] * num_levels) | |
| emb = self.backbone.backbone.embedding | |
| if not isinstance(emb, DelayEmbedding): | |
| self.backbone.backbone.embedding = DelayEmbedding( | |
| emb, num_levels=num_levels, codebook_size=codebook_size, | |
| codebook_sizes=codebook_sizes, | |
| ) | |
| ref = self.backbone.lm_head.weight | |
| d_model = ref.shape[1] | |
| self.delay_norms = nn.ModuleList([ | |
| RMSNorm(d_model, device=ref.device, dtype=ref.dtype) | |
| for _ in range(num_levels) | |
| ]) | |
| self.delay_heads = nn.ModuleList([ | |
| nn.Linear(d_model, sizes[k], bias=False, | |
| device=ref.device, dtype=ref.dtype) | |
| for k in range(num_levels) | |
| ]) | |
| for h in self.delay_heads: | |
| nn.init.normal_(h.weight, std=0.02) | |
| self.delay_num_levels = num_levels | |
| self.delay_codebook_size = codebook_size | |
| self.use_delay_pattern = True | |
| def forward_delay( | |
| self, | |
| input_ids: torch.Tensor, | |
| codes: torch.Tensor, | |
| labels: Optional[torch.Tensor] = None, | |
| speaker_ids: Optional[torch.Tensor] = None, | |
| prosody_vecs: Optional[torch.Tensor] = None, | |
| semantic_weight: float = 1.0, | |
| eos_weight: float = 1.0, | |
| plan_bins: Optional[torch.Tensor] = None, | |
| plan_schedule: Optional[torch.Tensor] = None, | |
| ) -> dict: | |
| speaker_emb = None | |
| inject = None | |
| if self.use_speaker_conditioning and speaker_ids is not None: | |
| speaker_emb = self.speaker_encoder(speaker_ids) | |
| if self.use_speaker_input: | |
| inject = self.speaker_input_proj(speaker_emb) | |
| if self.use_prosody_input and prosody_vecs is not None: | |
| ref = next(self.backbone.parameters()) | |
| p = self.prosody_input_proj( | |
| self.prosody_encoder(prosody_vecs.to(device=ref.device, | |
| dtype=ref.dtype))) | |
| inject = p if inject is None else inject + p | |
| if inject is not None: | |
| self.backbone.backbone.embedding.set_speaker(inject) | |
| self._stage_plan(plan_bins, plan_schedule) | |
| self.backbone.backbone.embedding.set_codes(codes) | |
| hidden_states = self.backbone.backbone(input_ids) | |
| if speaker_emb is not None: | |
| hidden_states = self.adaln(hidden_states, speaker_emb) | |
| logits = self.backbone.lm_head(hidden_states) | |
| text_loss = None | |
| audio_loss = None | |
| total_loss = None | |
| if labels is not None: | |
| loss_fct = nn.CrossEntropyLoss(ignore_index=-100) | |
| shift_logits = logits[..., :-1, :].contiguous() | |
| shift_labels = labels[..., 1:].contiguous() | |
| if eos_weight != 1.0: | |
| per_tok = nn.functional.cross_entropy( | |
| shift_logits.view(-1, shift_logits.size(-1)), | |
| shift_labels.view(-1), ignore_index=-100, reduction="none") | |
| flat = shift_labels.view(-1) | |
| tw = torch.ones_like(per_tok) | |
| reg = self.token_registry | |
| stop_ids = torch.tensor([reg.audio_end_id, reg.eos_id], | |
| device=flat.device) | |
| tw[torch.isin(flat, stop_ids)] = eos_weight | |
| valid = flat != -100 | |
| text_loss = (per_tok * tw)[valid].sum() / tw[valid].sum() | |
| else: | |
| text_loss = loss_fct( | |
| shift_logits.view(-1, shift_logits.size(-1)), | |
| shift_labels.view(-1), | |
| ) | |
| audio_loss = torch.tensor( | |
| 0.0, device=hidden_states.device, dtype=torch.float32) | |
| h = hidden_states[:, :-1, :] | |
| tgt = codes[:, 1:, :] | |
| for k in range(self.delay_num_levels): | |
| tk = tgt[..., k].masked_fill(tgt[..., k] < 0, -100) | |
| if (tk >= 0).sum() == 0: | |
| continue | |
| lk = self.delay_heads[k](self.delay_norms[k](h)).float() | |
| w = semantic_weight if k == 0 else 1.0 | |
| audio_loss = audio_loss + w * loss_fct( | |
| lk.reshape(-1, lk.shape[-1]), | |
| tk.reshape(-1), | |
| ) | |
| total_loss = text_loss + audio_loss | |
| return { | |
| "loss": total_loss, | |
| "main_loss": audio_loss, | |
| "mtp_loss": text_loss, | |
| "logits": logits, | |
| "hidden_states": hidden_states, | |
| } | |
| def enable_depth_module(self, num_levels: int = 8, codebook_size: int = 2048, | |
| d_depth: int = 1024, depth_layers: int = 2, | |
| depth_feedback: str = "all", cross_frame: int = 0, | |
| codebook_sizes=None, depth_arch: str = "gru", | |
| depth_heads: int = 8, level_decay: float = 1.0, | |
| depth_cond: str = "add"): | |
| emb = self.backbone.backbone.embedding | |
| if not isinstance(emb, DelayEmbedding): | |
| self.backbone.backbone.embedding = DelayEmbedding( | |
| emb, num_levels=num_levels, codebook_size=codebook_size, | |
| codebook_sizes=codebook_sizes, | |
| ) | |
| ref = self.backbone.lm_head.weight | |
| self.depth_module = DepthModule( | |
| d_model=ref.shape[1], num_levels=num_levels, | |
| codebook_size=codebook_size, d_depth=d_depth, | |
| num_layers=depth_layers, cross_frame=cross_frame, | |
| codebook_sizes=codebook_sizes, | |
| depth_arch=depth_arch, depth_heads=depth_heads, | |
| level_decay=level_decay, depth_cond=depth_cond, | |
| device=ref.device, dtype=ref.dtype) | |
| self.depth_num_levels = num_levels | |
| self.depth_codebook_size = codebook_size | |
| self.depth_level_sizes = self.depth_module.level_sizes | |
| self.depth_feedback = depth_feedback | |
| self.depth_cross_frame = cross_frame | |
| self.use_depth_module = True | |
| def enable_flow_head(self, latent_dim: int = 512, cb0_size: int = 2048, | |
| d_hidden: int = 1024, n_layers: int = 4, | |
| flow_weight: float = 1.0): | |
| ref = self.backbone.lm_head.weight | |
| self.flow_head = ContinuousLatentHead( | |
| d_model=ref.shape[1], latent_dim=latent_dim, cb0_size=cb0_size, | |
| d_hidden=d_hidden, n_layers=n_layers, device=ref.device) | |
| self.flow_weight = flow_weight | |
| self.use_flow_head = True | |
| def enable_hybrid_attention(self, top_k: int = 4, head_dim: int = 64, | |
| rope_base: float = 10000.0): | |
| layers = self.backbone.backbone.layers | |
| n = len(layers) | |
| assert 0 < top_k < n, f"top_k={top_k} out of range for {n} layers" | |
| ref = self.backbone.lm_head.weight | |
| replaced = list(range(n - top_k, n)) | |
| for i in replaced: | |
| layers[i].mixer = AttentionMixer( | |
| d_model=ref.shape[1], head_dim=head_dim, rope_base=rope_base, | |
| device=ref.device, dtype=ref.dtype, layer_idx=i) | |
| self.hybrid_replaced_layers = replaced | |
| self.use_hybrid_attention = True | |
| return replaced | |
| def forward_depth( | |
| self, | |
| input_ids: torch.Tensor, | |
| codes: torch.Tensor, | |
| labels: Optional[torch.Tensor] = None, | |
| speaker_ids: Optional[torch.Tensor] = None, | |
| prosody_vecs: Optional[torch.Tensor] = None, | |
| semantic_weight: float = 1.0, | |
| eos_weight: float = 1.0, | |
| target_latents: Optional[torch.Tensor] = None, | |
| loss_weights: Optional[torch.Tensor] = None, | |
| pause_exit_weight: float = 1.0, | |
| pause_codes: Optional[torch.Tensor] = None, | |
| plan_bins: Optional[torch.Tensor] = None, | |
| plan_schedule: Optional[torch.Tensor] = None, | |
| ) -> dict: | |
| speaker_emb = None | |
| inject = None | |
| if self.use_speaker_conditioning and speaker_ids is not None: | |
| speaker_emb = self.speaker_encoder(speaker_ids) | |
| if self.use_speaker_input: | |
| inject = self.speaker_input_proj(speaker_emb) | |
| if self.use_prosody_input and prosody_vecs is not None: | |
| ref = next(self.backbone.parameters()) | |
| p = self.prosody_input_proj( | |
| self.prosody_encoder(prosody_vecs.to(device=ref.device, | |
| dtype=ref.dtype))) | |
| inject = p if inject is None else inject + p | |
| if inject is not None: | |
| self.backbone.backbone.embedding.set_speaker(inject) | |
| self._stage_plan(plan_bins, plan_schedule) | |
| feed = codes | |
| if self.depth_feedback == "semantic": | |
| feed = codes.clone() | |
| feed[..., 1:] = -1 | |
| self.backbone.backbone.embedding.set_codes(feed) | |
| hidden_states = self.backbone.backbone(input_ids) | |
| if speaker_emb is not None: | |
| hidden_states = self.adaln(hidden_states, speaker_emb) | |
| logits = self.backbone.lm_head(hidden_states) | |
| text_loss = None | |
| depth_loss = None | |
| total_loss = None | |
| if labels is not None: | |
| shift_logits = logits[..., :-1, :].contiguous() | |
| shift_labels = labels[..., 1:].contiguous() | |
| if eos_weight != 1.0 or loss_weights is not None: | |
| per_tok = F.cross_entropy( | |
| shift_logits.view(-1, shift_logits.size(-1)), | |
| shift_labels.view(-1), ignore_index=-100, reduction="none") | |
| flat = shift_labels.view(-1) | |
| tw = torch.ones_like(per_tok) | |
| if eos_weight != 1.0: | |
| reg = self.token_registry | |
| stop_ids = torch.tensor([reg.audio_end_id, reg.eos_id], | |
| device=flat.device) | |
| tw[torch.isin(flat, stop_ids)] = eos_weight | |
| if loss_weights is not None: | |
| tw = tw * loss_weights[..., 1:].contiguous().view(-1).to(tw.dtype) | |
| valid_t = flat != -100 | |
| text_loss = (per_tok * tw)[valid_t].sum() / tw[valid_t].sum().clamp(min=1e-8) | |
| else: | |
| text_loss = F.cross_entropy( | |
| shift_logits.view(-1, shift_logits.size(-1)), | |
| shift_labels.view(-1), ignore_index=-100) | |
| h = hidden_states[:, :-1, :] | |
| tgt = codes[:, 1:, :] | |
| prev = codes[:, :-1, :] | |
| valid = (tgt >= 0).all(dim=-1) | |
| flow_loss = None | |
| if valid.any(): | |
| h_v = h[valid] | |
| t_v = tgt[valid] | |
| p_v = prev[valid] if getattr( | |
| self.depth_module, "cross_frame", 0) > 0 else None | |
| fw = None | |
| if pause_exit_weight != 1.0 and pause_codes is not None: | |
| pc = pause_codes.to(t_v.device) | |
| prev0 = prev[valid][:, 0] | |
| exit_ = torch.isin(prev0, pc) & ~torch.isin(t_v[:, 0], pc) | |
| fw = torch.where(exit_, torch.full_like(prev0, 0, dtype=torch.float32) | |
| + float(pause_exit_weight), torch.ones_like(prev0, dtype=torch.float32)) | |
| depth_loss = self.depth_module.forward_loss( | |
| h_v, t_v, semantic_weight=semantic_weight, prev_codes=p_v, | |
| frame_weights=fw) | |
| if self.use_flow_head and target_latents is not None: | |
| tl = target_latents[:, :, 1:].permute(0, 2, 1) | |
| flow_loss = self.flow_head.flow_loss( | |
| h_v, t_v[:, 0], tl[valid]) | |
| else: | |
| depth_loss = torch.tensor( | |
| 0.0, device=hidden_states.device, dtype=torch.float32) | |
| total_loss = text_loss + depth_loss | |
| if flow_loss is not None: | |
| total_loss = total_loss + self.flow_weight * flow_loss | |
| return { | |
| "loss": total_loss, | |
| "main_loss": depth_loss, | |
| "mtp_loss": text_loss, | |
| "flow_loss": flow_loss, | |
| "logits": logits, | |
| "hidden_states": hidden_states, | |
| } | |
| def enable_speaker_conditioning(self, speaker_dim: int = 256, num_speakers: int = 1000, | |
| input_injection: bool = False): | |
| d_model = self.backbone.config.d_model | |
| ref = next(self.backbone.parameters()) | |
| self.speaker_encoder = SpeakerEncoder( | |
| num_speakers=num_speakers, speaker_dim=speaker_dim | |
| ).to(device=ref.device, dtype=ref.dtype) | |
| self.adaln = AdaLN(d_model, speaker_dim=speaker_dim).to( | |
| device=ref.device, dtype=ref.dtype | |
| ) | |
| self.use_speaker_conditioning = True | |
| self.use_speaker_input = bool(input_injection) | |
| if input_injection: | |
| self.speaker_input_proj = nn.Linear(speaker_dim, d_model).to( | |
| device=ref.device, dtype=ref.dtype) | |
| nn.init.zeros_(self.speaker_input_proj.weight) | |
| nn.init.zeros_(self.speaker_input_proj.bias) | |
| def enable_plan_injection(self, pitch_bins: Optional[int] = None, | |
| duration_bins: Optional[int] = None, | |
| energy_bins: Optional[int] = None, | |
| d_plan: int = 256, | |
| scaffold_dropout: float = 0.3): | |
| emb = self.backbone.backbone.embedding | |
| if not isinstance(emb, DelayEmbedding): | |
| raise RuntimeError( | |
| "enable_plan_injection() requires the DelayEmbedding input " | |
| "channel — call enable_depth_module() (or enable_delay_pattern" | |
| "()) first. Without it there is no per-position hook and the " | |
| "plan would be silently ignored.") | |
| from tokenizer import PITCH_BINS, DURATION_BINS, ENERGY_BINS | |
| ref = next(self.backbone.parameters()) | |
| self.plan_injector = PlanInjector( | |
| d_model=self.backbone.config.d_model, | |
| n_pitch_bins=PITCH_BINS if pitch_bins is None else pitch_bins, | |
| n_duration_bins=DURATION_BINS if duration_bins is None else duration_bins, | |
| n_energy_bins=ENERGY_BINS if energy_bins is None else energy_bins, | |
| d_plan=d_plan, scaffold_dropout=scaffold_dropout, | |
| device=ref.device, dtype=ref.dtype) | |
| self.plan_bin_sizes = self.plan_injector.bin_sizes | |
| self.use_plan_injection = True | |
| def _stage_plan(self, plan_bins, plan_schedule): | |
| if plan_bins is None and plan_schedule is None: | |
| return | |
| if not self.use_plan_injection: | |
| _warn_plan_without_injection() | |
| return | |
| if plan_bins is None or plan_schedule is None: | |
| raise ValueError( | |
| "plan_bins and plan_schedule must be supplied together " | |
| f"(got plan_bins={'set' if plan_bins is not None else 'None'}, " | |
| f"plan_schedule=" | |
| f"{'set' if plan_schedule is not None else 'None'}) — see " | |
| "model.build_plan_schedule.") | |
| self.backbone.backbone.embedding.set_plan( | |
| self.plan_injector(plan_bins, plan_schedule)) | |
| def enable_prosody_conditioning(self, prosody_dim: int = 6, | |
| prosody_hidden: int = 256): | |
| d_model = self.backbone.config.d_model | |
| ref = next(self.backbone.parameters()) | |
| self.prosody_encoder = nn.Sequential( | |
| nn.Linear(prosody_dim, prosody_hidden), nn.SiLU(), | |
| ).to(device=ref.device, dtype=ref.dtype) | |
| self.prosody_input_proj = nn.Linear(prosody_hidden, d_model).to( | |
| device=ref.device, dtype=ref.dtype) | |
| nn.init.zeros_(self.prosody_input_proj.weight) | |
| nn.init.zeros_(self.prosody_input_proj.bias) | |
| self.use_prosody_input = True | |
| def _resize_embeddings(self): | |
| current_vocab_size = self.backbone.backbone.embedding.weight.shape[0] | |
| new_vocab_size = len(self.tokenizer) | |
| pad_multiple = getattr(self.backbone.config, "pad_vocab_size_multiple", 16) | |
| if new_vocab_size % pad_multiple != 0: | |
| new_vocab_size += pad_multiple - (new_vocab_size % pad_multiple) | |
| if new_vocab_size == current_vocab_size: | |
| print("No embedding resize needed.") | |
| return | |
| print(f"Resizing embeddings: {current_vocab_size} -> {new_vocab_size}") | |
| d_model = self.backbone.config.d_model | |
| n_copy = min(current_vocab_size, new_vocab_size) | |
| old_emb = self.backbone.backbone.embedding | |
| new_emb = nn.Embedding( | |
| new_vocab_size, d_model, device=self.device, dtype=self.dtype | |
| ) | |
| with torch.no_grad(): | |
| new_emb.weight[:n_copy] = old_emb.weight[:n_copy] | |
| if new_vocab_size > current_vocab_size: | |
| mean_embed = old_emb.weight.mean(dim=0) | |
| noise = torch.randn( | |
| new_vocab_size - n_copy, d_model, | |
| device=self.device, dtype=self.dtype, | |
| ) * 0.02 | |
| new_emb.weight[n_copy:] = mean_embed + noise | |
| self.backbone.backbone.embedding = new_emb | |
| new_head = nn.Linear( | |
| d_model, new_vocab_size, bias=False, device=self.device, dtype=self.dtype | |
| ) | |
| with torch.no_grad(): | |
| new_head.weight[:n_copy] = old_emb.weight[:n_copy] | |
| if new_vocab_size > current_vocab_size: | |
| new_head.weight[n_copy:] = torch.randn( | |
| new_vocab_size - n_copy, d_model, | |
| device=self.device, dtype=self.dtype, | |
| ) * 0.02 | |
| self.backbone.lm_head = new_head | |
| if hasattr(self.backbone.config, "tie_embeddings"): | |
| self.backbone.config.tie_embeddings = False | |
| self.backbone.config.vocab_size = new_vocab_size | |
| def forward( | |
| self, | |
| input_ids: torch.Tensor, | |
| labels: Optional[torch.Tensor] = None, | |
| speech_mask: Optional[torch.BoolTensor] = None, | |
| loss_weights: Optional[torch.Tensor] = None, | |
| residual_codes: Optional[torch.Tensor] = None, | |
| speaker_ids: Optional[torch.Tensor] = None, | |
| codes: Optional[torch.Tensor] = None, | |
| prosody_vecs: Optional[torch.Tensor] = None, | |
| semantic_weight: float = 1.0, | |
| eos_weight: float = 1.0, | |
| target_latents: Optional[torch.Tensor] = None, | |
| plan_bins: Optional[torch.Tensor] = None, | |
| plan_schedule: Optional[torch.Tensor] = None, | |
| pause_exit_weight: float = 1.0, | |
| pause_codes: Optional[torch.Tensor] = None, | |
| ) -> dict: | |
| if codes is not None: | |
| if self.use_depth_module: | |
| return self.forward_depth( | |
| input_ids, codes, labels=labels, speaker_ids=speaker_ids, | |
| prosody_vecs=prosody_vecs, | |
| semantic_weight=semantic_weight, eos_weight=eos_weight, | |
| target_latents=target_latents, loss_weights=loss_weights, | |
| pause_exit_weight=pause_exit_weight, pause_codes=pause_codes, | |
| plan_bins=plan_bins, plan_schedule=plan_schedule, | |
| ) | |
| return self.forward_delay( | |
| input_ids, codes, labels=labels, speaker_ids=speaker_ids, | |
| prosody_vecs=prosody_vecs, | |
| semantic_weight=semantic_weight, eos_weight=eos_weight, | |
| plan_bins=plan_bins, plan_schedule=plan_schedule, | |
| ) | |
| if plan_bins is not None or plan_schedule is not None: | |
| raise RuntimeError( | |
| "plan injection requires the delay/depth path (`codes` in the " | |
| "batch + enable_depth_module()/enable_delay_pattern()); the " | |
| "flat forward has no per-position input channel.") | |
| hidden_states = self.backbone.backbone(input_ids) | |
| if self.use_speaker_conditioning and speaker_ids is not None: | |
| speaker_emb = self.speaker_encoder(speaker_ids) | |
| hidden_states = self.adaln(hidden_states, speaker_emb) | |
| logits = self.backbone.lm_head(hidden_states) | |
| main_loss = None | |
| mtp_loss = None | |
| total_loss = None | |
| if labels is not None: | |
| shift_logits = logits[..., :-1, :].contiguous() | |
| shift_labels = labels[..., 1:].contiguous() | |
| if loss_weights is not None: | |
| loss_fct = nn.CrossEntropyLoss(ignore_index=-100, reduction="none") | |
| per_token_loss = loss_fct( | |
| shift_logits.view(-1, shift_logits.size(-1)), | |
| shift_labels.view(-1), | |
| ).view(shift_logits.size(0), shift_logits.size(1)) | |
| shift_weights = loss_weights[..., 1:].contiguous() | |
| weighted_loss = per_token_loss * shift_weights | |
| main_loss = weighted_loss.sum() / shift_weights.sum().clamp(min=1.0) | |
| else: | |
| loss_fct = nn.CrossEntropyLoss(ignore_index=-100) | |
| main_loss = loss_fct( | |
| shift_logits.view(-1, shift_logits.size(-1)), | |
| shift_labels.view(-1), | |
| ) | |
| if speech_mask is None: | |
| speech_mask = self.token_registry.build_speech_mask(labels) | |
| mtp_loss = self.mtp_module.compute_loss( | |
| hidden_states=hidden_states, | |
| labels=labels, | |
| speech_mask=speech_mask, | |
| residual_codes=residual_codes, | |
| crh_gamma=self.crh_gamma, | |
| spec_gamma=self.mtp_gamma, | |
| ) | |
| total_loss = main_loss + mtp_loss | |
| return { | |
| "loss": total_loss, | |
| "main_loss": main_loss, | |
| "mtp_loss": mtp_loss, | |
| "logits": logits, | |
| "hidden_states": hidden_states, | |
| } | |
| def generate(self, input_ids: torch.Tensor, max_length: int = 2000, **kwargs): | |
| return self.backbone.generate(input_ids, max_length=max_length, **kwargs) | |