Instructions to use igorktech/femto-asr with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use igorktech/femto-asr with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("automatic-speech-recognition", model="igorktech/femto-asr", trust_remote_code=True)# Load model directly from transformers import AutoModelForCTC model = AutoModelForCTC.from_pretrained("igorktech/femto-asr", trust_remote_code=True, device_map="auto") - Notebooks
- Google Colab
- Kaggle
femto-asr
2.39M parameters. 9.2 MB in fp32, 2.5 MB as int8. An English speech recognizer sized for a microcontroller with 400 KB of SRAM and no floating-point unit.
It will not beat Whisper. It is for the case where the alternative is no speech recognition at all.
Results
LibriSpeech, greedy CTC, no language model, no rescoring, full utterances:
| split | WER % | CER % |
|---|---|---|
| dev-clean | 10.80 | 4.14 |
| dev-other | 24.60 | 11.53 |
| test-clean | 10.95 | 4.09 |
| test-other | 25.36 | 11.94 |
For command-and-control use, a lexicon-constrained decoder over a known phrase list takes error rates far lower (~0.28 → ~0.05 WER on a 198-utterance phrase set) — but that is a decoder choice, not this model.
Architecture
audio 16 kHz mono
│
▼
log-mel 80 bins @ 100 fps ── global CMVN
│
▼
┌─────────────────────────────────────────────┐
│ conv stem 80→128, k5 s2 │ 4x downsample
│ 128→128, k5 s1 │ 100 fps → 25 fps
│ depthwise k5 s2 + 1x1 │
└─────────────────────────────────────────────┘
│ [B, T, 128]
▼
╔═════════════════════════════════════════════╗
║ E-Branchformer block × 12 ║
║ ║
║ ┌── LN → linear GQA-latent attention ──┐ ║ branch 0
║ │ ├──╫─► w₀·attn + w₁·cgmlp
║ └── LN → conv-gating MLP (k31) ────────┘ ║ (learned 2-vector)
║ ║
║ LN → SwiGLU FFN, rank-64 ─────────────╫─► branch 1
╚═════════════════════════════════════════════╝
│ ▲
│ each branch reads a learned mix of earlier
│ residual snapshots, not just the running sum
│ (depth-wise linear attention, 24 steps)
▼
linear head → 1025 CTC classes (0 = blank)
Per block: 4 query heads over 2 KV groups, both reached through 16-wide latents; SwiGLU hidden width 512 factored to rank 64; depthwise kernel 31.
Four choices carry the model, and each is load-bearing:
- Linear attention, not softmax. Keys and values collapse into one
[32, 32]state per group, so cost is linear in time and noT×Tmatrix ever exists — which is what makes 400 KB of SRAM feasible. The feature map isrelu²rather thanelu + 1because an exp is a soft-float call per element on a chip with no FPU. - Low-rank FFN. All three SwiGLU projections are rank-64 factorizations. Training the same model at rank 32 costs ~11% relative WER, and truncating a trained rank-64 to 48 roughly triples it.
- NoRA (arXiv:2608.31036): each low-rank down-projection is L2-normalized along the rank dimension. ~9% relative WER for no parameters and no compute — it folds into the weights at export.
- Fixed-point RMSNorm. The depth mixer rounds its rsqrt scale to 16 fractional bits, so an integer Newton-Raphson rsqrt on-device reproduces training numerics exactly. Active at inference, not a training-only trick.
Training
960 hours of LibriSpeech, 35 epochs, one RTX 4090, 16 hours.
Trained from scratch with CR-CTC consistency regularization, SpecAugment on a ramp, an auxiliary CTC head at an intermediate layer (dropped at export), the Muon optimizer, a Noam schedule with a terminal linear cooldown, and EMA weights — all reported numbers are the EMA. Speed perturbation between 0.85x and 1.3x was applied from the first step rather than bolted on afterwards; that ordering matters (see Speaking rate).
Training loss rises between epochs 7 and 14 (79 → 118) while validation keeps improving. That is the SpecAugment ramp increasing augmentation strength, not divergence — the model is being shown harder inputs, so its loss on them grows even as it generalizes better. Validation WER is the curve to read.
The last 7 epochs of cooldown are worth ~3% relative, and the final checkpoint beat the best-validation one (epoch 32) on all four splits, so epoch 35 is what ships.
Usage
import soundfile as sf
from transformers import AutoModelForCTC, AutoFeatureExtractor, AutoTokenizer
REPO = "igorktech/femto-asr"
model = AutoModelForCTC.from_pretrained(REPO, trust_remote_code=True).eval()
fe = AutoFeatureExtractor.from_pretrained(REPO, trust_remote_code=True)
tok = AutoTokenizer.from_pretrained(REPO, trust_remote_code=True)
wav, sr = sf.read("clip.wav") # 16 kHz mono
out = model(**fe(wav, sampling_rate=sr))
ids = model.greedy_decode(out.logits, out.output_lengths)
print(tok.decode(ids[0]))
With the pipeline:
from transformers import pipeline
pipe = pipeline("automatic-speech-recognition", model=REPO, trust_remote_code=True)
print(pipe("clip.wav")["text"])
Batching — ragged inputs are padded and masked, and batched results match single-clip results exactly:
batch = fe([wav1, wav2, wav3], sampling_rate=16000)
out = model(**batch)
texts = [tok.decode(x) for x in model.greedy_decode(out.logits, out.output_lengths)]
Fine-tuning — pass labels as CTC class indices and you get a CTC loss back:
labels = torch.tensor([tok.encode_text("HELLO WORLD")]) # already class indices
loss = model(**fe(wav, sampling_rate=16000), labels=labels).loss
loss.backward()
trust_remote_code=True is required: this is a custom architecture, so the
modeling code lives in the repo.
Notes for anyone modifying this repo
Padding. The feature extractor adds 0.3 s of silence to both ends. Frames at either edge have context on one side only: without the leading pad the first word is frequently dropped, without the trailing pad the last token truncates. On a live device, start the encoder ~0.3 s before the button and keep it running ~0.3 s after release.
tokenizer_class must stay out of tokenizer_config.json. In transformers
5.x, AutoTokenizer.from_pretrained returns early when that key is present and
never consults auto_map, which breaks custom-code tokenizers with a confusing
NoneType has no attribute from_pretrained. save_pretrained will put the key
back — remove it again, or loading fails.
Id convention. Tokenizer ids are CTC class indices (0 = blank, BPE token i at
i + 1), following Wav2Vec2 so the pipeline works unmodified. decode does the
full CTC reduction. Use encode_bpe_ids() if you need raw BPE ids for the C
implementation.
Limitations
- English read speech. Trained on LibriSpeech audiobooks. Accents, spontaneous and conversational speech, children's voices, and far-field or noisy audio are all out of distribution. LibriSpeech is not demographically representative; evaluate on your own audio and speakers before relying on this.
- Fast speech degrades sharply. See below.
- No language model. Greedy CTC gets homophones and rare words wrong in ways an LM or a constrained decoder would fix. Proper nouns are particularly weak — it will not know a name it never saw.
- Uppercase only, no punctuation, no casing, no digits.
- 16 kHz mono only.
- Not streaming as published. Attention is full-context. The architecture supports chunk-causal operation but these weights were trained and evaluated offline.
- Sensitive to spectral smearing. Reverberant rooms are a plausible failure mode; a phase-vocoder artifact alone cost 2.5x WER in testing.
Speaking rate
dev-clean, time-compressed with a pitch-preserving phase vocoder:
| rate | WER |
|---|---|
| 1.0x | 0.115 |
| 1.3x | 0.308 |
| 1.6x | 0.471 |
Training with perturbation from step one beat adapting a converged model afterwards at every rate including 1.0x, whereas the retrofit bought fast-speech robustness by paying 2–4% at normal speed.
If you benchmark this yourself: librosa.effects.time_stretch at its defaults
uses a 2048-sample window, which is 128 ms at 16 kHz and smears speech
transients badly. That alone cost 2.5x WER and made fast speech look far worse
than it is. Use a shorter window.
Licence
Apache 2.0, covering the code and the model weights.
Two things it does not cover. The logo depicts a character owned by its respective rights holders and is not part of the licence grant. And the model was trained on LibriSpeech, which is CC BY 4.0 — credited below, and not a restriction on use of these weights.
Credits
Combines E-Branchformer (Kim et al., 2022), MLA-style latent attention (DeepSeek-V2), grouped-query attention, linear attention with a positive feature map, Fast Conformer's depthwise-separable subsampling (arXiv:2305.05084), CR-CTC, and NoRA (arXiv:2608.31036).
- Downloads last month
- 41
Dataset used to train igorktech/femto-asr
Papers for igorktech/femto-asr
Normalized Low-Rank Adaptation
Fast Conformer with Linearly Scalable Attention for Efficient Speech Recognition
Evaluation results
- Test WER (greedy on LibriSpeech (clean)test set self-reported10.950
- Test CER on LibriSpeech (clean)test set self-reported4.090
- Test WER (greedy on LibriSpeech (other)test set self-reported25.360
- Test CER on LibriSpeech (other)test set self-reported11.940