voidful's picture
Add fail-closed stable speaker inference
7e7df2a
Raw History Blame
2.48 kB
"""Projection adapter between Barbet hidden space and VoxCPM2 LM hidden space.
Barbet's hidden states are not drop-in compatible with the space VoxCPM2's
pretrained downstream modules (FSQ layer, RALM fusion, DiT projections, stop
head) were trained in. The adapter is the explicit bridge:
barbet_hidden (H_b) --RMSNorm--Linear--> H_v --[zero-init residual MLP]*N--> H_v
The residual MLP blocks have their output projections zero-initialized, so at
init the adapter is exactly RMSNorm + Linear. Training can then grow extra
capacity without perturbing the warm-start behaviour.
"""
from __future__ import annotations
import torch
import torch.nn.functional as F
from torch import nn
from .config import AdapterConfig
class RMSNorm(nn.Module):
def __init__(self, hidden_size: int, eps: float = 1.0e-6) -> None:
super().__init__()
self.weight = nn.Parameter(torch.ones(hidden_size))
self.eps = eps
def forward(self, x: torch.Tensor) -> torch.Tensor:
variance = x.float().pow(2).mean(dim=-1, keepdim=True)
x = x.float() * torch.rsqrt(variance + self.eps)
return x.to(dtype=self.weight.dtype) * self.weight
class ResidualSwiGLUBlock(nn.Module):
def __init__(self, dim: int, ffn_mult: float, eps: float) -> None:
super().__init__()
inner = int(dim * ffn_mult)
self.norm = RMSNorm(dim, eps)
self.gate_proj = nn.Linear(dim, inner, bias=False)
self.up_proj = nn.Linear(dim, inner, bias=False)
self.down_proj = nn.Linear(inner, dim, bias=False)
nn.init.zeros_(self.down_proj.weight)
def forward(self, x: torch.Tensor) -> torch.Tensor:
h = self.norm(x)
return x + self.down_proj(F.silu(self.gate_proj(h)) * self.up_proj(h))
class ProjectionAdapter(nn.Module):
"""Maps Barbet hidden states into the VoxCPM2 semantic LM space."""
def __init__(self, in_dim: int, out_dim: int, config: AdapterConfig) -> None:
super().__init__()
self.norm = RMSNorm(in_dim, config.rms_norm_eps)
self.proj = nn.Linear(in_dim, out_dim)
self.blocks = nn.ModuleList(
[
ResidualSwiGLUBlock(out_dim, config.ffn_mult, config.rms_norm_eps)
for _ in range(config.num_residual_blocks)
]
)
def forward(self, x: torch.Tensor) -> torch.Tensor:
x = self.proj(self.norm(x))
for block in self.blocks:
x = block(x)
return x