Instructions to use lemuriandezapada/VibeVoice-Embed with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use lemuriandezapada/VibeVoice-Embed with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("feature-extraction", model="lemuriandezapada/VibeVoice-Embed", trust_remote_code=True)# Load model directly from transformers import AutoModel model = AutoModel.from_pretrained("lemuriandezapada/VibeVoice-Embed", trust_remote_code=True, device_map="auto") - VibeVoice
How to use lemuriandezapada/VibeVoice-Embed with VibeVoice:
import torch, soundfile as sf, librosa, numpy as np from vibevoice.processor.vibevoice_processor import VibeVoiceProcessor from vibevoice.modular.modeling_vibevoice_inference import VibeVoiceForConditionalGenerationInference # Load voice sample (should be 24kHz mono) voice, sr = sf.read("path/to/voice_sample.wav") if voice.ndim > 1: voice = voice.mean(axis=1) if sr != 24000: voice = librosa.resample(voice, sr, 24000) processor = VibeVoiceProcessor.from_pretrained("lemuriandezapada/VibeVoice-Embed") model = VibeVoiceForConditionalGenerationInference.from_pretrained( "lemuriandezapada/VibeVoice-Embed", torch_dtype=torch.bfloat16 ).to("cuda").eval() model.set_ddpm_inference_steps(5) inputs = processor(text=["Speaker 0: Hello!\nSpeaker 1: Hi there!"], voice_samples=[[voice]], return_tensors="pt") audio = model.generate(**inputs, cfg_scale=1.3, tokenizer=processor.tokenizer).speech_outputs[0] sf.write("output.wav", audio.cpu().numpy().squeeze(), 24000) - Notebooks
- Google Colab
- Kaggle
VibeVoice-Embed
The acoustic encoder of microsoft/VibeVoice-1.5B, fully separated into a standalone voice-embedding model: 24 kHz audio in, one 64-dimensional speaker vector out.
- 344M parameters (the encoder alone β no language model, no diffusion head, no decoder)
- 64-d embeddings, mean-pooled over causal-VAE latent frames at 7.5 Hz
- Deterministic: the encoder's distribution mean is used, never a sample β the same clip always produces the same vector
- Loads in seconds, runs at >100Γ realtime on a modest GPU slice (~2.7 GB at fp32)
How it was made
Upstream VibeVoice pairs this encoder with a decoder as a reconstruction VAE feeding a
1.5B LM. The weights here are the 276 model.acoustic_tokenizer.encoder.* tensors,
copied bit-for-bit (bfloat16, exactly as stored) out of the source checkpoint and
renamed to encoder.*.
One encoder covers the whole family
The 1.5B and 7B (Large) VibeVoice checkpoints ship the same acoustic encoder.
Measured against a 7B export (fp16 storage): 111/276 tensors bit-identical, the rest
differ only by bf16-vs-fp16 storage rounding (β€5Γ10β»βΆ of tensor max, concentrated in
elements small enough to hit fp16's subnormal range); embeddings of identical clips
agree to ~2Γ10β»β΅ relative and speaker-separation metrics are identical (dβ² +3.61,
AUC 0.997 for both). So this export is the voice embedder for either model size. The modeling code is likewise mechanically extracted from
upstream modular_vibevoice_tokenizer.py with the decoder classes removed β only
VibeVoiceEmbedModel (construction, pooling, padding handling) is new. Provenance,
including the exact source revision, is recorded machine-readably under
vibevoice_embed_metadata in config.json.
Does a reconstruction VAE make a speaker embedder?
Measured on a controlled set β 4 speakers Γ 4 utterances, all different text, each clip cloned from one fixed reference so identity is pinned (24 same-speaker pairs, 96 different-speaker pairs):
| representation / metric | dβ² | AUC |
|---|---|---|
| mean-pooled, cosine on unit vectors | +3.61 | 0.997 |
| mean-pooled, L2 on raw vectors | +2.80 | 0.975 |
| βnorm gapβ alone | +0.15 | 0.562 |
| purpose-built speaker embedder (CosyVoice, 192-d), cosine | +3.47 | 0.990 |
So mean-pooled VibeVoice latents separate speakers on par with a purpose-trained speaker-verification embedder on this set, despite coming from an encoder trained for reconstruction.
Honest caveats:
- Every test clip is synthetic output of one TTS engine, so recording channel is matched in a way that flatters the encoder. Not yet validated on real, diverse-channel diarized audio.
- No metric achieved a positive worst-case margin β same/different distributions overlap slightly, so this supports ranking and clustering, not a single global accept/reject threshold.
- Pooling is mean-only for a measured reason: std-pooling carries no speaker information here (std vectors of different clips have cosine 0.97β0.98), and concatenating it degrades the embedding (its norm is 3β4Γ the mean's).
Distance metric
Vectors are returned unnormalised (raw preserves information; normalising is one line). But the per-clip magnitude is noise β scored alone it is at chance (AUC 0.562) β so ranking by L2 on raw vectors is measurably worse than cosine. L2-normalise before indexing, or configure your vector store for cosine. On unit vectors, cosine, Euclidean and dot product produce identical rankings.
Usage
import torch, torchaudio
from transformers import AutoModel
model = AutoModel.from_pretrained(
"lemuriandezapada/VibeVoice-Embed",
torch_dtype=torch.float32, # recommended: pooling is precision-sensitive
trust_remote_code=True,
).eval().to("cuda")
wav, sr = torchaudio.load("clip.wav")
wav = wav.mean(0, keepdim=True) # mono
if sr != model.config.sampling_rate:
wav = torchaudio.functional.resample(wav, sr, model.config.sampling_rate)
with torch.no_grad():
out = model(wav.to("cuda"))
embedding = out.pooler_output[0] # (64,), float32, unnormalised
unit = torch.nn.functional.normalize(embedding, dim=0) # for cosine indexing
frames = out.last_hidden_state # (1, frames, 64) latents at 7.5 Hz
Batching variable-length clips
The encoder is causal, so right-padding cannot corrupt a clip's real frames β but the
frames emitted for the padding must not be averaged in. Pass padding_mask and the
model pools mask-aware:
batch = torch.nn.utils.rnn.pad_sequence([wav_a[0], wav_b[0]], batch_first=True)
mask = torch.zeros_like(batch, dtype=torch.bool)
mask[0, : wav_a.shape[-1]] = True
mask[1, : wav_b.shape[-1]] = True
out = model(batch.to("cuda"), padding_mask=mask.to("cuda"))
A clip embedded in a batch equals the same clip embedded alone.
Serving
serving/openai_embeddings_server.py is a self-contained OpenAI-compatible
/v1/embeddings server (FastAPI): audio goes in the input field as base64 or a
data: URI, optionally "normalize": true for unit vectors.
python serving/openai_embeddings_server.py --model lemuriandezapada/VibeVoice-Embed --port 8080
There is no vLLM plugin, deliberately: this is a pure convolutional encoder β no attention, no KV cache, no tokens β so vLLM's engine has nothing to schedule for it, and at >100Γ realtime the serving bottleneck is transport, not compute.
Repository layout
model.safetensorsβ encoder weights, bfloat16, keysencoder.*config.jsonβ encoder hyperparameters (verbatim from the source checkpoint),auto_mapfortrust_remote_code, andvibevoice_embed_metadataprovenanceconfiguration_vibevoice_embed.py/modeling_vibevoice_embed.pyβ standalone modeling code, no dependency on the VibeVoice packageserving/openai_embeddings_server.pyβ reference server
Upstream references
- Code: https://github.com/microsoft/VibeVoice (community fork: https://github.com/vibevoice-community/VibeVoice)
- Base model: https://huggingface.co/microsoft/VibeVoice-1.5B
- Related export from this workspace: VibeVoice-ASR-awq-int4
Notes
- This is a derivative export of the upstream checkpoint's acoustic encoder, not a fine-tune; no weights were modified.
- Base model licensing and usage terms follow the upstream VibeVoice release (MIT).
- Downloads last month
- -
Model tree for lemuriandezapada/VibeVoice-Embed
Base model
microsoft/VibeVoice-1.5B