VoxCPM-BACKUP_2 / docs /voxcpm_patch_flow.md
Jiwon-Kang's picture
Add files using upload-large-folder tool
9cd59fc verified
|
Raw
History Blame Contribute Delete
22.8 kB

VoxCPM patch_size 흐름과 DiT μ•„ν‚€ν…μ²˜

λ³Έ λ¬Έμ„œλŠ” /data/mm-llm-backbone_890/personal/sirius/VoxCPM/ μ½”λ“œμ—μ„œ ν™•μΈν•œ 사싀을 기반으둜, patch_size κ°€ AudioVAE latent β†’ LM β†’ LocDiT β†’ AudioVAE.decode κΉŒμ§€ μ–΄λ–»κ²Œ 흐λ₯΄λŠ”μ§€λ₯Ό μ •λ¦¬ν•œ 것이닀. SimWhisper-Codec μžμ²΄κ°€ μ•„λ‹ˆλΌ VoxCPM 의 latent-AR + flow-matching diffusion ꡬ쑰에 λŒ€ν•œ λ…ΈνŠΈμ΄λ©°, λ³Έ SimWhisper-Codec 레포의 audiocodec/ μ™€λŠ” 직접적인 μ½”λ“œ κ³΅μœ λŠ” μ—†λ‹€.


0. ν•œ 쀄 μš”μ•½

patch_size(μ΄ν•˜ P)λŠ” DiT κ°€ ν•œ step 에 μ˜ˆμΈ‘ν•˜λŠ” AudioVAE latent frame κ°œμˆ˜λ‹€. LM/Residual LM 은 latent frame λ‹¨μœ„κ°€ μ•„λ‹ˆλΌ patch 토큰 λ‹¨μœ„(T_seq = ceil(T_vae / P))둜 λˆλ‹€. DiT λŠ” 전체 μ˜€λ””μ˜€ μ‹œν€€μŠ€λ₯Ό ν•œ λ²ˆμ— 보지 μ•Šκ³ , 각 LM step λ§ˆλ‹€ local patch [P, D] ν•œ 덩어리λ₯Ό μƒμ„±ν•œλ‹€.

wav
 -> AudioVAE.encode
 -> latent [B, D, T_vae]
 -> patching
 -> audio_feats [B, T_seq, P, D]
 -> feat_encoder -> [B, T_seq, lm_dim]
 -> base_lm  + residual_lm
 -> dit_hidden [B, T_seq, dit_dim]
 -> flatten   [(B*T_seq), dit_dim]    (ΞΌ for DiT)
 -> LocDiT (flow-matching, in_channels=D, sequence length=P)
 -> feat_pred_seq [(B*T_seq), D, P]
 -> unpatch [B, D, T_seq*P] = [B, D, T_vae]
 -> AudioVAE.decode
 -> wav

1. ν…μ„œ shape cheat-sheet

단계 shape μœ„μΉ˜
wav [B, 1, T_wav] input
latent (VAE) [B, D=1280, T_vae] (50 Hz) audio_whisper_vae.py:174
patched feats [B, T_seq, P, D] packers.py:51-68, voxcpm2.py:421-422
feat_encoder 좜λ ₯ [B, T_seq, lm_dim] local_encoder.py:7-30
base_lm hidden [B, T_seq, lm_dim] (shift-right) voxcpm2.py:325-331
residual_lm hidden [B, T_seq, lm_dim] (shift-right) voxcpm2.py:334-342
dit_hidden (ΞΌ) [(BΒ·T_seq), dit_dim] voxcpm2.py:344-345
feat_gt / feat_cond [(BΒ·T_seq), D, P] voxcpm2.py:348-352
DiT noise z [(BΒ·T_seq), D, P] unified_cfm.py:65
DiT 좜λ ₯ [(BΒ·T_seq), D, P] local_dit_v2.py:108-114
unpatch κ²°κ³Ό [B, D, T_seqΒ·P] = [B, D, T_vae] voxcpm2.py:382
recon wav [B, 1, T_wav] audio_whisper_vae.py:189

2. 전체 νŒŒμ΄ν”„λΌμΈ λ‹€μ΄μ–΄κ·Έλž¨

                β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                β”‚                        TRAINING / INFERENCE FORWARD                      β”‚
                β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

   wav  [B, 1, T_wav]                                       AudioVAE = WhisperAudioVAE
     β”‚                                                          sample_rate = 16000
     β”‚                                                          hop_length  = 320  ── 50 Hz latent
     β”‚   pad to multiple of patch_len                           chunk_size  = 320  β˜… hopκ³Ό 동일
     β”‚     train: patch_len = audio_vae.hop_length * P           latent_dim  = 1280
     β”‚              packers.py:22
     β”‚     infer: patch_len = self.patch_size * self.chunk_size
     β”‚              voxcpm2.py:416
     β–Ό
   AudioVAE.encode(wav, sr=16000)                          audio_whisper_vae.py:132-174
     β”‚   feats = Whisper.encoder(mel).transpose(1,2)
     β–Ό
   latent  z : [B, D=1280, T_vae]   (50 Hz)
     β”‚
     β”‚   ── PATCHING ──
     β”‚   train (packers + collate):
     β”‚     feat = z.transpose(1,2)                # [B, T_vae, D]
     β”‚     view β†’ [B, T_seq, P, D]
     β”‚   infer (_encode_wav):                     voxcpm2.py:421-422
     β”‚     feat.view(D, -1, P).permute(1,2,0)     # [T_seq, P, D]
     β–Ό
   audio_feats : [B, T_seq, P, D]                          T_seq = ceil(T_vae / P)

   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
   β”‚                          LM-side  (patch-token sequence, T_seq tokens)             β”‚
   β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
   β”‚                                                                                    β”‚
   β”‚   audio_feats [B, T, P, D]                                                         β”‚
   β”‚       β”‚                                                                            β”‚
   β”‚       β”‚  feat_encoder = VoxCPMLocEnc                local_encoder.py:7-30          β”‚
   β”‚       β”‚   β€’ in_proj  : Linear(D=64, H_lm)                                          β”‚
   β”‚       β”‚   β€’ prepend learnable [CLS] token                                          β”‚
   β”‚       β”‚   β€’ MiniCPMModel (bi-dir, is_causal=False)                                 β”‚
   β”‚       β”‚   β€’ take CLS:  [(B*T), P+1, H] β†’ [(B*T), H]                                β”‚
   β”‚       β–Ό                                                                            β”‚
   β”‚   feat_embed [B, T, H_lm]   β†’  enc_to_lm_proj  β†’  [B, T, lm_hidden]                β”‚
   β”‚       β”‚                                                                            β”‚
   β”‚       β”‚  combined_embed = text_maskΒ·text_embed + audio_maskΒ·feat_embed             β”‚
   β”‚       β–Ό                                                                            β”‚
   β”‚   base_lm (MiniCPM, is_causal=True)              voxcpm2.py:325-326                β”‚
   β”‚       β”‚ enc_outputs [B, T, lm_dim]                                                 β”‚
   β”‚       β”‚   ↳ fsq_layer on audio positions       voxcpm2.py:327                      β”‚
   β”‚       β”‚   ↳ shift-right β‡’ lm_hidden                                                β”‚
   β”‚       β–Ό                                                                            β”‚
   β”‚   residual path:                                voxcpm2.py:334-342                 β”‚
   β”‚     residual_inputs = fusion_concat_proj([enc_outputs, audio_maskΒ·feat_embed])     β”‚
   β”‚     residual_lm(... is_causal=True) β‡’ residual_outputs [B, T, lm_dim]              β”‚
   β”‚     shift-right β‡’ residual_hidden                                                  β”‚
   β”‚       β”‚                                                                            β”‚
   β”‚       β–Ό                                                                            β”‚
   β”‚   dit_hidden = cat( lm_to_dit_proj(lm_hidden),                                     β”‚
   β”‚                     res_to_dit_proj(residual_hidden) )   [B, T, dit_dim]           β”‚
   β”‚                                                                                    β”‚
   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                              β”‚
                       ── FLATTEN ──         voxcpm2.py:345-352
                       dit_hidden : [B, T, dit_dim] ──► [(B*T), dit_dim]            (mu)
                       feat_gt    : [B, T, P, D]   ──► [(B*T), P, D]  β†’α΅€β†’ [(B*T), D, P]
                       feat_cond  : shift-right of audio_feats, same reshape   [(B*T), D, P]
                                              β”‚
                                              β–Ό
   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
   β”‚                       DiT / Flow-matching  (local, per-patch)                     β”‚
   β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
   β”‚                                                                                    β”‚
   β”‚   UnifiedCFM.forward(mu, n_steps, P, cond, …)        unified_cfm.py:54-79         β”‚
   β”‚     z = randn((b, in_channels=D, t=P))                                            β”‚
   β”‚     iterate t_span via solve_euler                                                β”‚
   β”‚                                                                                    β”‚
   β”‚   estimator = VoxCPMLocDiT                            local_dit_v2.py:50-116      β”‚
   β”‚     x    [N, D, P]    cond [N, D, P]    mu [N, H]    t,dt [N]                     β”‚
   β”‚       β”‚                                                                            β”‚
   β”‚       β”‚ in_proj  : Linear(D=64 β†’ H)   on xα΅€  β‡’  [N, P, H]                          β”‚
   β”‚       β”‚ cond_proj: Linear(D=64 β†’ H)   on condα΅€ β‡’ [N, P, H]                         β”‚
   β”‚       β”‚ time emb : SinPosEmb(t) + SinPosEmb(dt) β†’ time_mlp  β‡’ [N, H]               β”‚
   β”‚       β”‚ mu       : view to [N, 1, H]                                              β”‚
   β”‚       β”‚ seq      : cat([mu, t_token, cond, x], dim=1)   length 1+1+P+P            β”‚
   β”‚       β”‚ MiniCPMModel(seq, is_causal=False)                                        β”‚
   β”‚       β”‚ slice last P tokens β†’ out_proj β†’ [N, P, D] β†’α΅€β†’ [N, D, P]                  β”‚
   β”‚       β–Ό                                                                            β”‚
   β”‚   feat_pred_seq : [(B*T), D, P]                                                   β”‚
   β”‚                                                                                    β”‚
   β”‚   Training loss:  UnifiedCFM.compute_loss(x1=feat_gt, mu, cond)                   β”‚
   β”‚                                                       unified_cfm.py:174-248      β”‚
   β”‚     y = (1-r)Β·z + rΒ·x1   v = x1 - z                                               β”‚
   β”‚     mse(model(y,r,t), v) , masked by tgt_mask                                     β”‚
   β”‚                                                                                    β”‚
   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                              β”‚
                       ── UNPATCH ──          voxcpm2.py:382
                       feat_pred_seq [(B*T), D, P]
                          β†’ transpose to [(B*T), P, D]
                          β†’ rearrange "(b t) p d -> b d (t p)"
                          β‡’ feat_pred : [B, D, T*P]   (= [B, D, T_vae])
                                              β”‚
                                              β–Ό
                       AudioVAE.decode(feat_pred)                audio_whisper_vae.py:176
                          feats = z.transpose(1,2).unsqueeze(2)   # [B, T, 1, D]
                          recon = generator.inference(feats)      # [B, 1, T_wav]
                                              β”‚
                                              β–Ό
                                        Ε΅av  [B, 1, T_wav]

3. LocDiT ν•œ step λ‚΄λΆ€ (token λ‹¨μœ„)

mu λŠ” ν•œ 개 토큰 으둜, timestep 도 λ³„λ„μ˜ ν•œ 개 토큰 으둜 prepend λœλ‹€. μΆ”κ°€λ‘œ 이전 patch (feat_cond) κ°€ P ν† ν°μ˜ prefix 둜 항상 ν•¨κ»˜ λ“€μ–΄κ°€μ„œ in-context 쑰건 역할을 ν•œλ‹€.

                        Local sequence (length = 1 + 1 + P + P = 2 + 2P)
                   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
   input tokens ── β”‚   ΞΌ    β”‚   tΜ‚    β”‚  cond[0..P-1]   β”‚   x[0..P-1]    β”‚
                   β”‚ [1,H]  β”‚ [1,H]  β”‚     [P,H]       β”‚     [P,H]      β”‚
                   β””β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                        β”‚       β”‚             β”‚                 β”‚
                LM hidden    sinusoidal     prefix          noisy / current
                (== ΞΌ)       time emb       (prev patch)    sample x_r
                             + Ξ”t emb

                           β”‚  bi-directional MiniCPM transformer  β”‚
                           β–Ό
                   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
   output slice ── β”‚   Β·    β”‚   Β·    β”‚       Β·         β”‚  out[0..P-1]   β”‚ β†’ out_proj
                   β””β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                                              β”‚
                                                              β–Ό
                                                       [N, P, D] β†’α΅€β†’ [N, D, P]
                                                                     = predicted velocity

4. 핡심 μ½”λ“œ 인용

4.1 Patching (latent β†’ audio_feats)

Training packer β€” VoxCPM/src/voxcpm/training/packers.py:22, 51-68

# packers.py:22
self.patch_len = audio_vae.hop_length * self.patch_size

# packers.py:51-68
def encode_audio(self, wav: torch.Tensor):
    wav = wav.unsqueeze(0).unsqueeze(1)              # [1, 1, T]
    if wav.size(-1) % self.patch_len != 0:
        padding_size = self.patch_len - wav.size(-1) % self.patch_len
        wav = torch.nn.functional.pad(wav, (0, padding_size))
    with torch.no_grad():
        z = self.audio_vae.encode(wav, self.audio_vae.sample_rate)  # [1, D, T']
        feat = z.transpose(1, 2)                                    # [1, T', D]
    return feat

Inference path β€” VoxCPM/src/voxcpm/model/voxcpm2.py:416-422

patch_len = self.patch_size * self.chunk_size
if audio.size(1) % patch_len != 0:
    padding_size = patch_len - audio.size(1) % patch_len
    pad = (padding_size, 0) if padding_mode == "left" else (0, padding_size)
    audio = torch.nn.functional.pad(audio, pad)
feat = self.audio_vae.encode(audio.to(self.device), self._encode_sample_rate).cpu()
return feat.view(self.audio_vae.latent_dim, -1, self.patch_size).permute(1, 2, 0)  # [T_seq, P, D]

4.2 feat_encoder (VoxCPMLocEnc)

VoxCPM/src/voxcpm/modules/locenc/local_encoder.py:7-30

class VoxCPMLocEnc(nn.Module):
    def __init__(self, config: MiniCPM4Config, input_dim: int = 64):
        super().__init__()
        self.special_token = nn.Parameter(torch.randn(1, 1, 1, config.hidden_size))
        self.in_proj = nn.Linear(input_dim, config.hidden_size, bias=True)
        self.encoder = MiniCPMModel(config)

    def forward(self, x):  # x: [B, T, P, D]
        B, T, P, D = x.shape
        x = self.in_proj(x)                                  # [B, T, P, H]
        special_tokens = self.special_token.expand(B, T, 1, -1)
        x = torch.cat([special_tokens, x], dim=2)
        x = rearrange(x, "b t p c -> (b t) p c")
        outputs, _ = self.encoder(x, is_causal=False)
        cls_output = outputs[:, 0, :]
        return rearrange(cls_output, "(b t) c -> b t c", b=B)

β†’ λ‹¨μˆœ Linear κ°€ μ•„λ‹ˆλΌ mini-transformer + [CLS] λ‹€.

4.3 LM + Residual LM

VoxCPM/src/voxcpm/model/voxcpm2.py:319-345

B, T, P, D = audio_feats.shape
feat_embed = self.feat_encoder(audio_feats)                  # [B, T, H_enc]
feat_embed = self.enc_to_lm_proj(feat_embed)                 # [B, T, lm_dim]

text_embed = self.base_lm.embed_tokens(text_tokens) * scale_emb
combined_embed = text_mask.unsqueeze(-1) * text_embed + audio_mask.unsqueeze(-1) * feat_embed
enc_outputs, _ = self.base_lm(inputs_embeds=combined_embed, is_causal=True)
enc_outputs = self.fsq_layer(enc_outputs) * audio_mask.unsqueeze(-1) \
              + enc_outputs * text_mask.unsqueeze(-1)
lm_hidden = torch.cat((torch.zeros_like(enc_outputs[:, 0:1, :]), enc_outputs[:, :-1, :]), dim=1)

residual_inputs = self.fusion_concat_proj(
    torch.cat((enc_outputs, audio_mask.unsqueeze(-1) * feat_embed), dim=-1)
)
residual_outputs, _ = self.residual_lm(inputs_embeds=residual_inputs, is_causal=True)
residual_hidden = torch.cat(
    (torch.zeros_like(residual_outputs[:, 0:1, :]), residual_outputs[:, :-1, :]), dim=1,
)

dit_hidden = torch.cat(
    (self.lm_to_dit_proj(lm_hidden), self.res_to_dit_proj(residual_hidden)), dim=-1
)
dit_hidden = rearrange(dit_hidden, "b t c -> (b t) c")        # [(B*T), dit_dim]

4.4 DiT μž…λ ₯ λ§Œλ“€κΈ°

VoxCPM/src/voxcpm/model/voxcpm2.py:348-358

feat_gt = rearrange(audio_feats.to(target_dtype), "b t p d -> (b t) p d")
feat_cond = torch.cat(
    (torch.zeros_like(audio_feats[:, 0:1, ...]), audio_feats[:, :-1, ...]), dim=1,
)
feat_cond = rearrange(feat_cond.to(target_dtype), "b t p d -> (b t) p d")

loss_seq_mask = loss_mask.unsqueeze(-1).repeat(1, 1, self.patch_size)
loss_seq_mask = rearrange(loss_seq_mask, "b t p -> (b t) p 1").to(target_dtype)

4.5 UnifiedCFM β€” noise/sampling

VoxCPM/src/voxcpm/modules/locdit/unified_cfm.py:54-79

@torch.inference_mode()
def forward(self, mu, n_timesteps, patch_size, cond, ...):
    b, _ = mu.shape
    t = patch_size
    z = torch.randn((b, self.in_channels, t),
                    device=mu.device, dtype=mu.dtype) * temperature
    t_span = torch.linspace(1, 0, n_timesteps + 1, device=mu.device, dtype=mu.dtype)
    t_span = t_span + sway_sampling_coef * (torch.cos(torch.pi / 2 * t_span) - 1 + t_span)
    return self.solve_euler(x=z, t_span=t_span, mu=mu, cond=cond, ...)

ν•™μŠ΅ 손싀 β€” unified_cfm.py:174-248 의 compute_loss(x1, mu, cond, tgt_mask, progress).

4.6 LocDiT (VoxCPMLocDiT)

VoxCPM/src/voxcpm/modules/locdit/local_dit_v2.py:50-116

def forward(self, x, mu, t, cond, dt):
    """
    x:    [N, C, P]   noisy / current sample
    mu:   [N, C]      LM hidden
    t:    [N]         diffusion timestep
    cond: [N, C, P]   previous-patch condition
    dt:   [N]         delta for mean velocity
    """
    x    = self.in_proj(x.transpose(1, 2).contiguous())     # [N, P, H]
    cond = self.cond_proj(cond.transpose(1, 2).contiguous())# [N, P, H]
    prefix = cond.size(1)

    t  = self.time_mlp(self.time_embeddings(t).to(x.dtype))
    dt = self.delta_time_mlp(self.time_embeddings(dt).to(x.dtype))
    t  = t + dt

    mu = mu.view(x.size(0), -1, x.size(-1))                  # [N, 1, H]
    x  = torch.cat([mu, t.unsqueeze(1), cond, x], dim=1)     # [N, 1+1+P+P, H]
    hidden, _ = self.decoder(x, is_causal=False)
    hidden = hidden[:, prefix + mu.size(1) + 1:, :]
    hidden = self.out_proj(hidden)                           # [N, P, D]
    return hidden.transpose(1, 2).contiguous()               # [N, D, P]

4.7 Unpatch

VoxCPM/src/voxcpm/model/voxcpm2.py:382

feat_pred = rearrange(
    feat_pred_seq.transpose(1, 2),
    "(b t) d p -> b d (t p)",
    b=B, p=self.patch_size,
)   # [B, D, T_vae]

슀트리밍 inference μ‹œμ—λŠ” ν•œ step λ‹¨μœ„ [B, 1, P, D] β†’ [B, D, P] (voxcpm2.py:1083), non-streaming μ‹œμ—λŠ” T_seq 만큼 λͺ¨μ€ λ’€ ν•œ λ²ˆμ— (b t) p d -> b d (t p) (voxcpm2.py:1106).

4.8 WhisperAudioVAE

VoxCPM/src/voxcpm/modules/audiovae/audio_whisper_vae.py:28-87

self.sample_rate = 16000
self.hop_length = 320              # 16000 / 50 Hz Whisper latent
self.chunk_size = 320              # β˜… VoxCPM 의 patch_len κ³„μ‚°μ—μ„œ hop_length 와 동일해야 함
self.latent_dim = 1280             # Whisper-large hidden size
self.max_audio_samples = self.sample_rate * 30   # SimWhisper extractor 30s ν•œκ³„

encode λŠ” [B, D=1280, T_lat], decode λŠ” [B, 1, T_wav] λ₯Ό λŒλ €μ€€λ‹€.


5. chunk_size vs hop_length 정리

μœ„μΉ˜ μˆ˜μ‹ μ‚¬μš© attribute
Training packer (packers.py:22) patch_len = audio_vae.hop_length * patch_size hop_length
Inference _encode_wav (voxcpm2.py:416) patch_len = self.patch_size * self.chunk_size chunk_size

β†’ 두 κ²½λ‘œκ°€ 같은 의미("VAE ν•œ frame λ‹Ή raw sample 수 Γ— P")μ—¬μ•Ό ν•˜λ―€λ‘œ chunk_size == hop_length μ—¬μ•Ό 함. WhisperAudioVAE μ—μ„œλŠ” λ‘˜ λ‹€ 320 으둜 ν•˜λ“œμ½”λ“œλ˜μ–΄ μžˆμ–΄ μ‹€νš¨μ μœΌλ‘œ 일치 (audio_whisper_vae.py:84-85).

30 초 μ œν•œμ€ chunk_size κ°€ μ•„λ‹ˆλΌ 별도 max_audio_samples = sample_rate * 30 이닀. μ˜λ―Έκ°€ λ‹€λ₯Έ 두 값을 ν•˜λ‚˜μ— λ¬Άμ§€ μ•Šλ„λ‘ 주의.


6. μ‚¬μš©μž μš”μ•½ vs μ‹€μ œ μ½”λ“œ β€” μ§šμ–΄λ‘˜ 점

  • feat_encoder λŠ” Linear κ°€ μ•„λ‹ˆλΌ mini-transformer + CLS 이닀 (VoxCPMLocEnc). patch λ‚΄λΆ€ P frame 을 μ–‘λ°©ν–₯ self-attention 으둜 μ„žμ€ λ’€ [CLS] ν† ν°λ§Œ λΉΌμ„œ [B, T, lm_dim] 을 λ§Œλ“ λ‹€.
  • Residual LM 은 base LM κ³Ό λ³„κ°œ λͺ¨λ“ˆ (voxcpm2.py:334-342). base LM 좜λ ₯ + audio embed λ₯Ό fusion_concat_proj 으둜 합쳐 λ‹€μ‹œ causal LM ν•œ 번 더 λŒλ €μ„œ dit_hidden 의 μ ˆλ°˜μ„ λ§Œλ“€κ³ , λ‚˜λ¨Έμ§€ μ ˆλ°˜μ€ base LM 좜λ ₯. 두 hidden 을 채널 μΆ•μœΌλ‘œ concat ν•˜μ—¬ μ΅œμ’… dit_dim 을 μ±„μš΄λ‹€.
  • DiT prefix λŠ” [ΞΌ, tΜ‚, cond, x] 둜 4 μ’…λ₯˜ 토큰 이 prepend λœλ‹€. ΞΌ 와 timestep 은 합쳐진 단일 토큰이 μ•„λ‹ˆλΌ λ³„κ°œμ˜ 두 토큰. μΆ”κ°€λ‘œ 이전 patch(feat_cond) κ°€ P 개 prefix ν† ν°μœΌλ‘œ λ“€μ–΄κ°€μ„œ in-context conditioning 을 μˆ˜ν–‰.
  • λ…Έμ΄μ¦ˆ z λŠ” randn((b, in_channels=D, P)) β€” DiT 의 "local time" κΈΈμ΄λŠ” μ •ν™•νžˆ patch_size 이닀.
  • chunk_size == hop_length λ™μΉ˜λŠ” WhisperAudioVAE 의 ν˜„μž¬ ν•˜λ“œμ½”λ“œ μ—μ„œλ§Œ 성립. μƒˆ AudioVAE λ₯Ό 뢙일 λ•ŒλŠ” 두 값이 λΆ„λ¦¬λ˜μ§€ μ•Šλ„λ‘ 단언문(λ˜λŠ” chunk_size = hop_length 둜 톡일)을 λ‘λŠ” 편이 μ•ˆμ „ν•˜λ‹€.