A complete training and inference system that converts Mimi discrete token streams (12.5 Hz, 8-codebook) into HuBERT-compatible continuous features (50 Hz, 768-dim) for use in downstream diffusion-based talking-head models such as Ditto.
Raw Audio
│
▼
[Mimi Encoder] ──→ tokens (B, T, 8) at 12.5 Hz
HuggingFace MimiModel
kyutai/moshiko-pytorch-bf16
(no moshi git install required)
│
▼
[Bridge Module]
├─ Multi-codebook Embeddings (B, T, 256)
├─ CausalUpsample ×4 (B, 4T, 512)
├─ Causal Transformer (B, 4T, 512)
└─ Output Projection (B, 4T, 1024)
│
▼
Predicted Features (B, 4T, 1024) at 50 Hz
│
▼ (during training only)
Multi-Loss vs HuBERT-large targets (facebook/hubert-large-ls960-ft, 1024-dim)
The trainer auto-detects available GPUs:
# Single GPU (default)
python train.py --config config.yaml
# DataParallel (multiple GPUs, no launcher needed)
CUDA_VISIBLE_DEVICES=0,1,2,3 python train.py --config config.yaml
# DistributedDataParallel (fastest, requires torchrun)
torchrun --nproc_per_node=4 train.py --config config.yaml
mimi_hubert_bridge/
├── config.yaml # All hyperparameters and paths
├── model.py # Bridge architecture + Discriminator
├── losses.py # All 7 loss functions + BridgeLoss manager
├── dataset.py # Data loading, Mimi/HuBERT extraction, prosody
├── trainer.py # Full training loop with AMP, logging, checkpointing
├── inference.py # Batch and streaming inference + CLI
├── preprocess.py # Data preparation scripts
├── train.py # Main training entry point
└── requirements.txt
pip install -r requirements.txt
# transformers>=4.40.0 is required for MimiModel (HuggingFace Mimi encoder).
# The moshi git repository is no longer needed.
#multiple_GPU_use
!torchrun --nproc_per_node=4 preprocess.py \
--dataset librispeech \
--root ./data/LibriSpeech/train-clean-100 \
--out_dir data \
--val_frac 0.1 \
--preextract \
--device cuda \
--num_workers 16
# From LibriSpeech
python preprocess.py \
--dataset librispeech \
--root /data/LibriSpeech/train-clean-100 \
--out_dir data \
--val_frac 0.01 \
--preextract \
--device cuda
# From any directory of audio files
python preprocess.py \
--dataset generic \
--root /data/my_audio \
--out_dir data
Outputs data/train.jsonl and data/val.jsonl. Each line:
{"audio_path": "/path/to/file.flac", "text": "optional transcript"}
Edit config.yaml. Key settings:
paths:
hubert_model: "facebook/hubert-base-ls960"
mimi_model: "kyutai/mimi"
training:
batch_size: 16
num_epochs: 50
loss_weights:
recon: 1.0
ctc: 0.3
prosody: 0.2
adv: 0.1
stat: 0.1
smooth: 0.05
alignment: 0.1
python train.py --config config.yaml
# Resume from checkpoint
python train.py --config config.yaml --resume checkpoints/bridge_best.pt
# Override hyperparameters on the fly
python train.py --config config.yaml --overrides training.batch_size=8 training.learning_rate=5e-5
Batch mode:
python inference.py \
--checkpoint checkpoints/bridge_best.pt \
--config config.yaml \
--audio input.wav \
--output features.pt
Streaming mode (causal, chunk-by-chunk):
python inference.py \
--checkpoint checkpoints/bridge_best.pt \
--config config.yaml \
--audio input.wav \
--output features.pt \
--streaming \
--chunk-size 50
Latency benchmark:
python inference.py \
--checkpoint checkpoints/bridge_best.pt \
--config config.yaml \
--benchmark
Compare Loss:
!python inference.py \
--checkpoint checkpoints/bridge_best.pt \
--config config.yaml \
--audio /content/audio.wav \
--compare
Python API:
from inference import BridgeInference, StreamingBridgeInference
# Batch
infer = BridgeInference("checkpoints/bridge_best.pt", "config.yaml")
features = infer.from_audio("speech.wav") # (T_h, 768)
# Streaming
stream = StreamingBridgeInference("checkpoints/bridge_best.pt", "config.yaml", chunk_size=50)
stream.reset()
for chunk in token_stream: # each chunk: (50, 8)
feat = stream.step(chunk) # (200, 768) — no future frames used
nn.Embedding tables (one per codebook), vocab size 2048(B, T, 256) (or concat + linear)ConvTranspose1d(stride=4, kernel=4) — clean 4× expansionLinear(512→512) → GELU → Linear(512→768)
| # | Name | Component | Default Weight |
|---|---|---|---|
| 1 | Reconstruction | MSE + cosine distance vs HuBERT | 1.0 |
| 2 | CTC | Frozen ASR head phonetic intelligibility | 0.3 |
| 3 | Prosody | Predicted F0 + energy MSE | 0.2 |
| 4 | Adversarial | Hinge GAN (delayed start at step 5000) | 0.1 |
| 5 | Statistics | Mean + variance distribution matching | 0.1 |
| 6 | Smoothness | First-order temporal difference penalty | 0.05 |
| 7 | Alignment | Frame-level phoneme cross-entropy | 0.1 |
disc_start_step (default 5000) to stabilise early training| Metric | Description |
|---|---|
recon_mse | L2 distance in HuBERT feature space |
pitch_corr | Pearson correlation of predicted vs GT F0 |
ctc | CTC loss (proxy for phonetic intelligibility) |
stat_mean/std | Distribution mismatch |
smooth | Temporal discontinuity |
The bridge output (B, T, 768) at 50 Hz is directly compatible with any model trained on HuBERT features (e.g. Ditto, SadTalker, DiffTalk). Simply replace the HuBERT feature extractor call:
# Before
hubert_features = hubert_model(audio) # slow, requires audio
# After
hubert_features = bridge(mimi_tokens) # fast, causal, streamable
| Mode | VRAM | Notes |
|---|---|---|
| Training (batch=16) | ~12 GB | Single A100/RTX 3090 |
| Training (batch=4) | ~4 GB | Consumer GPU |
| Inference (streaming) | <1 GB | CPU viable |
If you use this bridge module in your work, please cite the underlying models:
3 commits
A complete training and inference system that converts Mimi discrete token streams (12.5 Hz, 8-codebook) into HuBERT-compatible continuous features (50 Hz, 768-dim) for use in downstream diffusion-based talking-head models such as Ditto.
Raw Audio
│
▼
[Mimi Encoder] ──→ tokens (B, T, 8) at 12.5 Hz
HuggingFace MimiModel
kyutai/moshiko-pytorch-bf16
(no moshi git install required)
│
▼
[Bridge Module]
├─ Multi-codebook Embeddings (B, T, 256)
├─ CausalUpsample ×4 (B, 4T, 512)
├─ Causal Transformer (B, 4T, 512)
└─ Output Projection (B, 4T, 1024)
│
▼
Predicted Features (B, 4T, 1024) at 50 Hz
│
▼ (during training only)
Multi-Loss vs HuBERT-large targets (facebook/hubert-large-ls960-ft, 1024-dim)
The trainer auto-detects available GPUs:
# Single GPU (default)
python train.py --config config.yaml
# DataParallel (multiple GPUs, no launcher needed)
CUDA_VISIBLE_DEVICES=0,1,2,3 python train.py --config config.yaml
# DistributedDataParallel (fastest, requires torchrun)
torchrun --nproc_per_node=4 train.py --config config.yaml
mimi_hubert_bridge/
├── config.yaml # All hyperparameters and paths
├── model.py # Bridge architecture + Discriminator
├── losses.py # All 7 loss functions + BridgeLoss manager
├── dataset.py # Data loading, Mimi/HuBERT extraction, prosody
├── trainer.py # Full training loop with AMP, logging, checkpointing
├── inference.py # Batch and streaming inference + CLI
├── preprocess.py # Data preparation scripts
├── train.py # Main training entry point
└── requirements.txt
pip install -r requirements.txt
# transformers>=4.40.0 is required for MimiModel (HuggingFace Mimi encoder).
# The moshi git repository is no longer needed.
#multiple_GPU_use
!torchrun --nproc_per_node=4 preprocess.py \
--dataset librispeech \
--root ./data/LibriSpeech/train-clean-100 \
--out_dir data \
--val_frac 0.1 \
--preextract \
--device cuda \
--num_workers 16
# From LibriSpeech
python preprocess.py \
--dataset librispeech \
--root /data/LibriSpeech/train-clean-100 \
--out_dir data \
--val_frac 0.01 \
--preextract \
--device cuda
# From any directory of audio files
python preprocess.py \
--dataset generic \
--root /data/my_audio \
--out_dir data
Outputs data/train.jsonl and data/val.jsonl. Each line:
{"audio_path": "/path/to/file.flac", "text": "optional transcript"}
Edit config.yaml. Key settings:
paths:
hubert_model: "facebook/hubert-base-ls960"
mimi_model: "kyutai/mimi"
training:
batch_size: 16
num_epochs: 50
loss_weights:
recon: 1.0
ctc: 0.3
prosody: 0.2
adv: 0.1
stat: 0.1
smooth: 0.05
alignment: 0.1
python train.py --config config.yaml
# Resume from checkpoint
python train.py --config config.yaml --resume checkpoints/bridge_best.pt
# Override hyperparameters on the fly
python train.py --config config.yaml --overrides training.batch_size=8 training.learning_rate=5e-5
Batch mode:
python inference.py \
--checkpoint checkpoints/bridge_best.pt \
--config config.yaml \
--audio input.wav \
--output features.pt
Streaming mode (causal, chunk-by-chunk):
python inference.py \
--checkpoint checkpoints/bridge_best.pt \
--config config.yaml \
--audio input.wav \
--output features.pt \
--streaming \
--chunk-size 50
Latency benchmark:
python inference.py \
--checkpoint checkpoints/bridge_best.pt \
--config config.yaml \
--benchmark
Compare Loss:
!python inference.py \
--checkpoint checkpoints/bridge_best.pt \
--config config.yaml \
--audio /content/audio.wav \
--compare
Python API:
from inference import BridgeInference, StreamingBridgeInference
# Batch
infer = BridgeInference("checkpoints/bridge_best.pt", "config.yaml")
features = infer.from_audio("speech.wav") # (T_h, 768)
# Streaming
stream = StreamingBridgeInference("checkpoints/bridge_best.pt", "config.yaml", chunk_size=50)
stream.reset()
for chunk in token_stream: # each chunk: (50, 8)
feat = stream.step(chunk) # (200, 768) — no future frames used
nn.Embedding tables (one per codebook), vocab size 2048(B, T, 256) (or concat + linear)ConvTranspose1d(stride=4, kernel=4) — clean 4× expansionLinear(512→512) → GELU → Linear(512→768)
| # | Name | Component | Default Weight |
|---|---|---|---|
| 1 | Reconstruction | MSE + cosine distance vs HuBERT | 1.0 |
| 2 | CTC | Frozen ASR head phonetic intelligibility | 0.3 |
| 3 | Prosody | Predicted F0 + energy MSE | 0.2 |
| 4 | Adversarial | Hinge GAN (delayed start at step 5000) | 0.1 |
| 5 | Statistics | Mean + variance distribution matching | 0.1 |
| 6 | Smoothness | First-order temporal difference penalty | 0.05 |
| 7 | Alignment | Frame-level phoneme cross-entropy | 0.1 |
disc_start_step (default 5000) to stabilise early training| Metric | Description |
|---|---|
recon_mse | L2 distance in HuBERT feature space |
pitch_corr | Pearson correlation of predicted vs GT F0 |
ctc | CTC loss (proxy for phonetic intelligibility) |
stat_mean/std | Distribution mismatch |
smooth | Temporal discontinuity |
The bridge output (B, T, 768) at 50 Hz is directly compatible with any model trained on HuBERT features (e.g. Ditto, SadTalker, DiffTalk). Simply replace the HuBERT feature extractor call:
# Before
hubert_features = hubert_model(audio) # slow, requires audio
# After
hubert_features = bridge(mimi_tokens) # fast, causal, streamable
| Mode | VRAM | Notes |
|---|---|---|
| Training (batch=16) | ~12 GB | Single A100/RTX 3090 |
| Training (batch=4) | ~4 GB | Consumer GPU |
| Inference (streaming) | <1 GB | CPU viable |
If you use this bridge module in your work, please cite the underlying models:
3 commits
Python
100.0%