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
File size: 5,192 Bytes
f9e3832 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 | """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())
|