Automatic Speech Recognition
Transformers
Safetensors
English
voxtral
audio
speculative-decoding
neuron
trainium
distillation
Instructions to use jburtoft/Voxtral-Mini-3B-2507-draft-4layer with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use jburtoft/Voxtral-Mini-3B-2507-draft-4layer with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("automatic-speech-recognition", model="jburtoft/Voxtral-Mini-3B-2507-draft-4layer")# Load model directly from transformers import AutoProcessor, AutoModelForMultimodalLM processor = AutoProcessor.from_pretrained("jburtoft/Voxtral-Mini-3B-2507-draft-4layer") model = AutoModelForMultimodalLM.from_pretrained("jburtoft/Voxtral-Mini-3B-2507-draft-4layer", device_map="auto") - Notebooks
- Google Colab
- Kaggle
| """Build a naive layer-pruned Voxtral draft from Voxtral-Mini-3B-2507. | |
| Loads the full model, keeps a subset of the 30 Llama decoder layers, saves | |
| the pruned model to disk with a corrected config so HF `.from_pretrained()` | |
| reloads it as a smaller `VoxtralForConditionalGeneration`. | |
| Design: | |
| - Full model: 30 decoder layers in `model.language_model.model.layers`. | |
| - Keep layers by 0-indexed positions (default: [0, 6, 12, 18, 24, 29]). | |
| - Preserve everything else unchanged: audio encoder, multi_modal_projector, | |
| embed_tokens, norm, lm_head. This ensures the draft shares the audio path | |
| and vocab with the target (both required for HF `assistant_model=`). | |
| - Save to `--out-dir` so HF can reload with `.from_pretrained()`. | |
| Usage: | |
| python build_draft.py --keep 0,6,12,18,24,29 --out-dir /mnt/drafts/voxtral-mini-3B-draft-6of30 | |
| python build_draft.py --keep 0,10,20,29 --out-dir /mnt/drafts/voxtral-mini-3B-draft-4of30 | |
| Then load in a benchmark script: | |
| target = VoxtralForConditionalGeneration.from_pretrained("mistralai/Voxtral-Mini-3B-2507", ...) | |
| draft = VoxtralForConditionalGeneration.from_pretrained("/mnt/drafts/voxtral-mini-3B-draft-6of30", ...) | |
| outputs = target.generate(..., assistant_model=draft) | |
| """ | |
| from __future__ import annotations | |
| import argparse | |
| import json | |
| import os | |
| import shutil | |
| from pathlib import Path | |
| import torch | |
| from transformers import ( | |
| AutoProcessor, | |
| VoxtralForConditionalGeneration, | |
| ) | |
| def parse_args() -> argparse.Namespace: | |
| p = argparse.ArgumentParser() | |
| p.add_argument("--source", default="mistralai/Voxtral-Mini-3B-2507") | |
| p.add_argument("--out-dir", required=True, type=Path) | |
| p.add_argument( | |
| "--keep", | |
| default="0,6,12,18,24,29", | |
| help="Comma-separated 0-indexed layer indices to keep from the 30 " | |
| "Llama decoder layers. Must be sorted ascending, must include " | |
| "at least the first and last for stability.", | |
| ) | |
| p.add_argument("--dtype", default="bfloat16", choices=["bfloat16", "float16", "float32"]) | |
| p.add_argument("--device", default="cuda") | |
| return p.parse_args() | |
| def main() -> int: | |
| args = parse_args() | |
| keep_layers = [int(x) for x in args.keep.split(",")] | |
| n_keep = len(keep_layers) | |
| assert keep_layers == sorted(keep_layers), "--keep must be ascending" | |
| assert n_keep >= 2, "Keep at least 2 layers" | |
| dtype = {"bfloat16": torch.bfloat16, "float16": torch.float16, "float32": torch.float32}[args.dtype] | |
| print(f"[build_draft] Loading {args.source} on {args.device} dtype={args.dtype}") | |
| model = VoxtralForConditionalGeneration.from_pretrained( | |
| args.source, | |
| torch_dtype=dtype, | |
| low_cpu_mem_usage=True, | |
| ) | |
| processor = AutoProcessor.from_pretrained(args.source) | |
| # Introspect layer container. transformers 5.x path: | |
| # VoxtralForConditionalGeneration | |
| # .model (VoxtralModel) | |
| # .language_model (LlamaModel) | |
| # .layers (ModuleList of 30 LlamaDecoderLayer) | |
| # .lm_head | |
| # transformers 4.5x path had .language_model directly under the top-level | |
| # object (no intermediate .model). Support both. | |
| if hasattr(model, "model") and hasattr(model.model, "language_model"): | |
| decoder = model.model.language_model | |
| elif hasattr(model, "language_model"): | |
| lm = model.language_model | |
| decoder = lm.model if hasattr(lm, "model") else lm | |
| else: | |
| raise AttributeError( | |
| "Cannot find decoder. Model has: " + ", ".join(sorted(dict(model.named_children()).keys())) | |
| ) | |
| assert hasattr(decoder, "layers"), ( | |
| f"Decoder container {type(decoder).__name__} has no .layers. " | |
| f"Children: {list(dict(decoder.named_children()).keys())}" | |
| ) | |
| layers = decoder.layers | |
| n_full = len(layers) | |
| print(f"[build_draft] Full model has {n_full} decoder layers") | |
| assert max(keep_layers) < n_full, f"--keep {keep_layers} exceeds n_full={n_full}" | |
| # Rewrite layer_idx on each kept layer so RoPE / KV cache accounting still works | |
| kept = torch.nn.ModuleList([layers[i] for i in keep_layers]) | |
| for new_idx, layer in enumerate(kept): | |
| if hasattr(layer, "self_attn") and hasattr(layer.self_attn, "layer_idx"): | |
| layer.self_attn.layer_idx = new_idx | |
| decoder.layers = kept | |
| # Update text_config so config.json reflects the new depth | |
| tcfg = model.config.text_config | |
| tcfg.num_hidden_layers = n_keep | |
| # Some HF paths look for the num_hidden_layers on the top config too. | |
| if hasattr(model.config, "num_hidden_layers"): | |
| model.config.num_hidden_layers = n_keep | |
| # Save | |
| args.out_dir.mkdir(parents=True, exist_ok=True) | |
| print(f"[build_draft] Saving pruned model to {args.out_dir}") | |
| model.save_pretrained(args.out_dir, safe_serialization=True) | |
| processor.save_pretrained(args.out_dir) | |
| # Sanity print | |
| with open(args.out_dir / "config.json") as f: | |
| saved = json.load(f) | |
| print(f"[build_draft] saved text_config.num_hidden_layers = " | |
| f"{saved.get('text_config', {}).get('num_hidden_layers')}") | |
| print(f"[build_draft] Done.") | |
| return 0 | |
| if __name__ == "__main__": | |
| raise SystemExit(main()) | |