Whistle: speech-to-text in 16,9 MB — e le barre del benchmark che nessuno ha etichettato
- Capire cosa c'è davvero dentro i 16,9 MB — e quale parte del titolo «9x più piccolo» è architettura e quale è una scelta di quantizzazione che il confronto non tiene costante
- Leggere il grafico WER del vendor comprese le barre dei concorrenti che non etichetta mai: Whistle vince 5 confronti diretti su 8 e perde i tre che somigliano di più alla vita reale
- Sapere perché 30 secondi sono un muro nell'encoder e non un'impostazione, e costruire la segmentazione VAD che ne consegue
- Trovare l'API di streaming che la discussione di lancio ha concluso non esistere, e capire perché la sua latenza reale non è gli 11,1 ms al primo token
- Usare il keyword biasing come leva principale di accuratezza, non come un optional — è la differenza tra il 41% e il 96% su un vocabolario chiuso
- Scegliere tra Whistle, Whisper, Parakeet e un'API cloud per il tuo audio specifico
L'8 ottobre 2026 un post di blog intitolato "Whistle: Speech to Text in 16.9 MB" ha raggiunto 768 punti su Hacker News. Il numero nel titolo è tutto il pitch, ed è vero: l'intero modello di riconoscimento vocale multilingue è un singolo file da 16,9 MB che gira su una CPU, senza GPU e senza dipendenze.
La discussione che ne è seguita si è divisa nettamente in due. Chi ha letto il grafico dei benchmark è rimasto colpito. Chi ha puntato un microfono contro il modello no — un tester ha riportato 70 risposte corrette su 170 enunciati. Entrambi i gruppi avevano ragione, e il motivo è stampato nel grafico stesso del vendor. Sta solo nelle barre che non portano alcun numero.
La versione in una frase
Whistle compra le sue dimensioni quantizzando a 2–4 bit un piccolo modello ASR encoder–decoder e limitando in banda il suo front end a 250–3500 Hz, il che gli costa quasi nulla sul parlato con microfono ravvicinato e un solo parlante — dove batte davvero Whisper base — e gli costa moltissimo sul parlato in campo lontano, sovrapposto o con accento, dove è il peggiore dei tre modelli del suo stesso confronto.
Cosa c'è davvero nei 16,9 MB
| Componente | Dettaglio |
|---|---|
| Front end | finestra da 25 ms, hop da 10 ms, 80 bin log-mel, limitato in banda a 250–3500 Hz |
| Stem | convoluzionale a 128 canali, sottocampiona a un frame ogni 80 ms |
| Encoder | 8 blocchi Simple Attention, MLP Monarch Hadamard |
| Decoder | 8 blocchi Simple Attention «a scala» (laddered), larghezza 512, 8 query head per 2 KV head |
| Vocabolario | 8.192 pezzi di testo più 7 token di lingua |
| Tetto di output | 320 token per clip |
| Tetto di input | 480.000 campioni — 30 s a 16 kHz mono |
| Contenitore | .cact, Cactus Quants, 2–4 bit |
| Licenza | Apache-2.0 |
Tre di queste righe lavorano più delle altre, e due sono quelle che la gente salta.
Il titolo «9x» sulle dimensioni non tiene costante la precisione
Il confronto che tutti citano è 16,9 MB contro i 145,3 MB di Whisper base. Cioè un nono delle dimensioni — ma la nota a piè di pagina del vendor stesso dice che Whistle è misurato a 2–4 bit mentre Whisper è fp32 in memoria sulla CPU. Sono 8–16 volte di distanza in bit per peso prima ancora che venga presa una singola decisione architetturale.
La riga onesta, a parità di condizioni, nello stesso grafico è Moonshine tiny v2 a 41,9 MB in int8. Whistle è 2,5 volte più piccolo, non 9. Il 2,5x rimanente è reale ed è dove l'architettura si guadagna il pane — ma se ripeti il numero 9x stai ripetendo soprattutto una scelta di quantizzazione.
È la stessa trappola trattata in come leggere un benchmark di IA senza farsi ingannare: il numero non è sbagliato, è il confronto a non essere tenuto costante.
Limitare la banda a 3500 Hz è una decisione di design con una classe di errore prevedibile
Il front end tiene 250–3500 Hz e butta via tutto il resto. È più o meno la banda telefonica, ed è una scelta sensata per un modello pensato per girare su un dispositivo indossabile — la maggior parte dell'energia vocale sta sotto.
La maggior parte, non tutta. La letteratura di fonetica acustica è precisa su
questo: 3–4 kHz bastano per vocali intelligibili ma non per la maggior parte
delle fricative sorde, i cui spettri sono concentrati sopra i 4 kHz. La s
inglese ha la massima concentrazione di energia nell'intervallo 5–10 kHz circa,
con picco vicino agli 8 kHz — più del doppio del taglio di Whistle — e la sh
è centrata vicino ai 4 kHz con energia che si estende fino a 8.
Tagliare a 3500 Hz non rende quei suoni inudibili. Elimina la banda che li separa più nettamente l'uno dall'altro.
Quindi prima ancora di eseguire un singolo test puoi prevedere la classe di errore: confusioni e terminazioni di parola perse sulle fricative, peggiori sui parlanti il cui ritmo o accento dà a quelle consonanti meno durata con cui compensare. I resoconti dal campo nel thread di lancio — output in spagnolo con «parole inesistenti e terribili refusi», accenti messicani e venezuelani non riconosciuti, accenti marcati che degradano nettamente — sono quella previsione che si avvera puntuale.
Le barre del benchmark che nessuno ha etichettato
Ecco la parte che vale la pagina. Il grafico di Whistle riporta il tasso di errore sulle parole su nove set di test per tre modelli. Stampa un numero su ogni barra di Whistle e su nessuna delle altre.
Le barre però ci sono, disegnate in scala lineare. Le etichette stampate di Whistle calibrano quella scala con esattezza — 6,867 pixel per punto di WER, coerente a ±0,01 su tutti e nove — quindi i valori dei concorrenti si ricavano dall'altezza delle barre. Farlo produce la tabella che il grafico si ferma un attimo prima di darti.
| Set di test | Cos'è | Whistle | Whisper base ≈ | Moonshine tiny v2 ≈ |
|---|---|---|---|---|
| LibriSpeech test-clean | Audiolibri letti, puliti | 4.31 | 4.89 | 4.51 |
| LibriSpeech test-other | Audiolibri letti, più difficili | 10.49 | 11.00 | 11.71 |
| SPGISpeech | Chiamate finanziarie | 7.65 | — | 7.70 |
| Earnings-22 | Chiamate sugli utili, con accento | 19.01 | — | 21.25 |
| AMI | Riunioni, campo lontano, sovrapposizioni | 26.07 | 21.50 | 22.78 |
| AMI cleaned | Idem, riferimenti ripuliti | 22.87 | — | — |
| TED-LIUM | Conferenze | 7.61 | 4.99 | 5.64 |
| FLEURS | Parlato letto multilingue | 21.39 | 24.50 | — |
| MLS | Audiolibri multilingue | 24.90 | 23.10 | — |
Più basso è meglio; il grassetto indica il vincitore di ogni riga. La colonna di Whistle riporta i valori stampati dal vendor. Le altre due colonne sono lette dalle barre del grafico — il vendor dichiara che sono i numeri pubblicati da Whisper e Moonshine, e si rifiuta di stamparli.
Whistle vince cinque delle otto righe in cui esiste un confronto. Per un modello 2,5 volte più piccolo del rivale a precisione più vicina è un risultato genuino, e la riga FLEURS — 3,1 punti meglio di Whisper base sul parlato letto multilingue — è la cosa più impressionante del grafico.
Ora guarda cosa perde:
- AMI, di 4,57 punti contro Whisper base. Microfoni in campo lontano, più parlanti, sovrapposizioni, interruzioni. Whistle non è solo indietro, è il peggiore dei tre, e perde contro un modello 8,6 volte più grande.
- TED-LIUM, di 2,62 punti. Un solo parlante, ma una sala grande, un microfono da palco e un eloquio non scritto.
- MLS, di 1,80 punti. Multilingue a lunga durata, mediato su sei lingue non inglesi.
Lo schema non è sottile una volta che i numeri sono affiancati. Ogni riga che Whistle vince è con microfono ravvicinato e, di fatto, un solo parlante. Ogni riga che perde ha distanza tra bocca e microfono oppure più di una persona nella stanza. Gli audiolibri letti e il parlato dettato sono esattamente il profilo che un front end limitato in banda a 16 kHz con un encoder minuscolo gestisce bene. Riverbero e parlanti sovrapposti sono esattamente ciò che non può permettersi di avere la capacità di districare.
Quest'unica frase concilia le due metà del thread di lancio. Chi ha provato la dettatura in un telefono tenuto vicino al viso l'ha apprezzato. Chi lo ha puntato su una clip di un film, una riunione o una stanza no. Nessuno stava misurando male.
4,31 e 26,07 sono lo stesso modello con gli stessi pesi. Se di Whistle ricordi un solo valore di WER, ricorda la forbice, non il titolo.
Una nota sul risultato 70/170
Il risultato dal campo più citato nel thread — 70 corrette su 170, contro 168/170 di un modello ASR Qwen molto più grande — è una misura reale, ma è la misura delle impostazioni di default su un vocabolario chiuso. Lo stesso tester ha riportato 164/170 dopo la messa a punto. Cosa è cambiato è l'argomento della prossima sezione, ed è la funzione più sottovalutata della scheda del modello.
Il keyword biasing è la leva principale di accuratezza
Whistle decodifica con cinque beam valutati da una log-probabilità normalizzata per lunghezza, e accetta un elenco di parole che ti aspetti di sentire e le favorisce durante quella ricerca, con corrispondenza tramite un automa di Aho-Corasick.
In un modello ASR generalista questa è una comodità. In un modello da 16,9 MB con un vocabolario di 8.192 pezzi è più vicina a un elemento portante. Il modello ha pochissima capacità da spendere in un modello linguistico implicito, quindi nomi propri, nomi di prodotto, toponimi e gergo di dominio sono esattamente ciò che sbaglia — ed esattamente ciò che una lista di keyword corregge. Passare da 70/170 a 164/170 significa dal 41% al 96% con gli stessi pesi.
Trascrivi con i due argomenti che contano
import needle r = needle.transcribe( "clip.wav", keywords=["Kubernetes", "Grafana", "Prometheus", "Aoife", "Nguyen"], word_timestamps=True, language="en", # skip detection when you already know ) print(r["text"]) for w in r["words"]: print(w["word"], w["start"], w["end"], w["probability"])
Tre cose in quello snippet vanno dette chiaramente:
keywordsnon è un filtro di post-elaborazione. Cambia la ricerca a beam, quindi può recuperare una parola che il decoder altrimenti non avrebbe mai collocato.language="en"elimina una modalità di fallimento, non solo latenza. Il rilevamento automatico su sette lingue in una clip breve o rumorosa è un'altra cosa che può andare storta. Se la tua app conosce la lingua, dichiarala.- Ogni parola porta la propria
probability. È la tua soglia di confidenza — vedi il pattern di passaggio di consegne più sotto.
- Una lista di keyword troppo lunga diluisce il bias e comincia a spingere il decoder verso parole che nessuno ha detto. Tienila al vocabolario che i tuoi utenti producono davvero in quel contesto — la schermata corrente, la rubrica corrente, il documento corrente — non l'intero glossario del prodotto.
Il muro dei 30 secondi è nell'encoder, non in un file di configurazione
30 secondi a 16 kHz sono 480.000 campioni. Dopo lo stem convoluzionale è un frame ogni 80 ms, quindi 375 frame dell'encoder, e il decoder si ferma a 320 token di output. Sono forme fisse in un file di tensori da 16,9 MB. Non esiste un flag che le alzi, e un tester nel thread di lancio che ha registrato oltre il limite ha ottenuto un errore di runtime anziché una trascrizione troncata.
Quindi qualsiasi clip più lunga di 30 secondi significa che la segmentazione è tua — e la segmentazione è dove vive davvero l'accuratezza dell'ASR a lunga durata, perché un taglio a metà di una parola o di una proposizione è un errore da cui il modello non si può riprendere.
- 16 kHz mono non è un consiglio, è il contratto di input. Un file stereo a 44,1 kHz va convertito prima che raggiunga il modello; l'installazione base gestisce WAV a 16 kHz e campioni grezzi, e l'extra [mic] aggiunge ricampionamento e acquisizione dal microfono.
- Esegui un rilevatore di attività vocale e taglia nelle pause. Fette fisse da 30 secondi prima o poi taglieranno a metà di una parola, e quell'errore finisce per sempre nella tua trascrizione. Punta a segmenti da 10–25 secondi per avere margine per una proposizione finale lunga.
- Dai ai passaggi consecutivi una regione condivisa, così puoi ricucire sulla base dell'accordo invece di fidarti di un confine netto. Il costo è ridecodificare un secondo di audio; a 1.319 token/s di decodifica non è il tuo collo di bottiglia.
- Ogni parola torna con un inizio e una fine, quindi deduplica la regione di sovrapposizione per tempo anziché con il confronto di stringhe. È l'unico modo affidabile per eliminare una parola doppia senza eliminare una parola legittimamente ripetuta.
- Fai la media delle probabilità per parola su ogni segmento. I segmenti a bassa confidenza sono la tua coda per un modello più grande — sul dispositivo gratis, fuori dal dispositivo quando serve. Non rieseguire tutto.
Il silenzio è gestito; il parlato a basso SNR no
La scheda del modello fa un'affermazione precisa e gradita: silenzio e rumore stazionario restituiscono una trascrizione vuota anziché una frase inventata, perché il rilevamento del silenzio gira prima della ricerca a beam. È una correzione diretta del difetto più noto di Whisper — allucinare con sicurezza sottotitoli sul nulla.
Leggi l'affermazione con precisione. Copre silenzio e rumore stazionario. Non copre il parlato basso, distante o sovrapposto, e il thread contiene proprio quel fallimento: un tester ha riportato che il modello emetteva «Thank you.» in ripetizione per oltre un minuto mentre veniva riprodotto un dialogo reale, e un altro ha trovato una negazione ribaltata — «can handle» al posto di «can't handle» — più una parola inventata di sana pianta.
Sono i classici artefatti dell'addestramento su sottotitoli di video web, dove
«Thanks for watching» occupa una quota enorme del testo, e sopravvivono al
filtro del silenzio perché il filtro non era mai stato pensato per loro. Se un
n't perso cambierebbe ciò che fa la tua applicazione, un controllo sulla
probabilità per parola non è facoltativo.
Lo streaming esiste — il thread di lancio ha concluso di no
Una lamentela ricorrente nella discussione era che Whistle non può fare
trascrizione dal vivo, perché la demo non mostrava testo parziale durante il
parlato. La demo no; la libreria sì. needle.stream(chunks) prende input dal
microfono in tempo reale e conferma le parole su cui passaggi consecutivi
sovrapposti concordano.
Quella politica — decodificare ripetutamente una finestra sovrapposta, emettere solo il prefisso su cui due passaggi concordano — è la famiglia LocalAgreement dell'ASR in streaming, e saperlo ti dice ciò che la scheda tecnica non dirà:
La latenza percepita non è gli 11,1 ms al primo token. È il ritardo di accordo: quanto una parola deve aspettare prima che un secondo passaggio la confermi. Il TTFT è una misura di velocità di decodifica su una clip completa da 10 secondi. In un ciclo di streaming il numero visibile all'utente è fissato dal passo della tua finestra e da quanto sei disposto ad aspettare per la stabilità, e saranno centinaia di millisecondi, non undici.
Veloce resta veloce — 11,1 ms contro i 73,2 ms di Whisper base e i 22,8 ms di Moonshine, e 1.319 token/s di decodifica contro 266 e 262, misurati su 10 s di audio su una CPU Apple M4 Pro. Solo, non promettere a un product manager 11 ms di sottotitoli dal vivo.
Due funzioni silenziosamente più interessanti delle dimensioni
Il decoder è «a scala», quindi un file è una famiglia
Ogni profondità da due layer in su è un modello distribuibile, selezionato
al caricamento con --audio-depth. Scarichi un unico blob da 16,9 MB e
scegli il tuo punto accuratezza/latenza sul dispositivo, che è il posto giusto
dove sceglierlo — un orologio e un telefono possono eseguire lo stesso
artefatto a profondità diverse. Per dare la scala, il modello di testo Needle
fratello decodifica a circa 4.000 token/s con due layer e 400 con venti su un
Raspberry Pi 5.
embed() ti dà il riconoscimento vocale senza alcuna trascrizione
L'output dell'encoder è disponibile direttamente: una riga per frame da 80 ms,
tramite needle.Whistle().embed(audio). È rilevamento di wake word, matching di
comandi, deduplicazione audio e «trova dove in questa registrazione qualcuno ha
detto questo» — senza decodificare testo, senza un modello linguistico e senza
le classi di errore discusse sopra, perché non gli chiedi mai di scrivere
nulla.
Per molti compiti embedded («l'utente ha detto una di queste undici cose?») questo è lo strumento giusto e la trascrizione è la deviazione.
Eseguilo dalla CLI, compreso il confronto a tre integrato
pip install cactus-needle # microphone playground needle whistle playground # put Whistle next to Whisper and Moonshine on YOUR audio needle whistle compare my-clip.wav # air-gapped / embedded: fetch a platform engine and the weights needle download macos-arm64 needle download whistle ./macos-arm64/needle --model whistle.cact --audio clip.wav --audio-word-timestamps
Il sottocomando compare merita una menzione: il vendor distribuisce lo
strumento che verifica le sue stesse affermazioni sul tuo audio. Data quanto la
forbice di WER sopra dipende dalle condizioni di registrazione, eseguirlo su una
clip rappresentativa del tuo audio vale più di qualsiasi tabella di questa
pagina.
Una proprietà di riproducibilità da copiare
Il motore non legge alcuna variabile d'ambiente. Ogni comportamento è un default compilato o un flag esplicito. Significa che lo stesso blob con gli stessi flag dà lo stesso punteggio su qualsiasi macchina, una proprietà che la maggior parte degli stack di inferenza non può vantare e che conta moltissimo quando cerchi di capire se è regredita la valutazione della settimana scorsa o la shell della settimana scorsa.
Scegliere: Whistle, Whisper, Parakeet o un'API cloud
| La tua situazione | Scegli | Perché |
|---|---|---|
| Dettatura su telefono o dispositivo indossabile, microfono ravvicinato, offline, 7 lingue supportate | Whistle | È il centro di progetto. 16,9 MB, niente GPU, timestamp per parola, e batte Whisper base proprio su questo profilo audio |
| Wake word o insiemi fissi di comandi su un microcontrollore | Whistle embed() | Salta del tutto la trascrizione; confronta i frame dell'encoder |
| Registrazioni di riunioni, più parlanti, microfoni da stanza | Non Whistle | Il suo stesso grafico lo mette ultimo su AMI. Usa un Whisper a dimensione piena, Parakeet o un ASR cloud con diarizzazione |
| Qualsiasi lingua fuori dalle sette | Non Whistle | Solo inglese, tedesco, francese, spagnolo, italiano, olandese e polacco. Niente cinese, nonostante sia stata l'aggiunta più richiesta nel thread di lancio |
| Miglior accuratezza open-weight per secondo su GPU, inglese | Parakeet | parakeet-tdt-0.6b-v2 di NVIDIA era il favorito dei professionisti nel thread; CC-BY-4.0 |
| Audio che non puoi inviare da nessuna parte, e l'accuratezza conta più delle dimensioni | Whisper large, in locale | Resta il riferimento di accuratezza. Scambi un ingombro ~100 volte maggiore per averla |
| Trascrizioni mediche, legali o finanziarie a valore di registro | ASR cloud con un umano nel ciclo | La modalità di fallimento del ribaltamento della negazione descritta sopra è da sola squalificante |
Un ibrido è di solito la risposta giusta, e ha la stessa forma di ogni altra divisione locale/remoto di questo sito: esegui il modello piccolo su tutto, usa le probabilità per parola come soglia, fai salire la frazione incerta. Vedi Claude plus local models per il pattern generale e a private local AI stack per capire dove si colloca in esso un passaggio di trascrizione.
Cosa ci dice questo sull'ondata on-device in generale
Whistle è una buona istanza di uno schema che compare sempre più tra i modelli piccoli: la capacità è reale e l'involucro operativo è stretto, e solo la prima metà sta in un titolo. La quantizzazione a 2 bit non è un trucco — il modello funziona davvero — ma compra le sue dimensioni spendendo la capacità che avrebbe gestito l'audio difficile, e il grafico del vendor mostra esattamente dove cade il conto etichettando solo le righe in cui non cade.
La regola pratica si generalizza oltre l'ASR: quando un modello piccolo pubblica una forbice ampia di benchmark, la forbice è la specifica. Trova la riga in cui va peggio, capisci cosa ha quel set di test che gli altri non hanno e verifica se il tuo input reale somiglia a quella riga. Per Whistle la risposta è distanza e sovrapposizione. Per un modello linguistico quantizzato di solito è il contesto lungo — vedi cosa nasconde «98,2% di retention».
Check yourself
0/6Fonti e approfondimenti
- Cactus-Compute/whistle — Hugging Face model card — architettura, contratto di input, argomenti dell'API, grafico di benchmark, licenza e citazione
- Whistle: Speech to Text in 16.9 MB — Cactus Compute blog — il post di lancio: struttura dei blocchi, strategia di decodifica, metodologia dei benchmark e note sulla precisione
- cactus-compute/needle — GitHub — il pacchetto Python
cactus-needlee il sorgente del motore, compresineedle.stream(),transcribe()eembed() - Cactus-Compute/needle3 — Hugging Face — il motore condiviso e le sue cartelle per piattaforma (binario
needle,libneedle.a,needle.h) - Needle supported devices — Cactus Compute — le 13 cartelle di piattaforma, i flag del runner, il componente WASI e la configurazione air-gapped
- "Whistle: Speech to Text in 16.9 MB" — Hacker News — 768 punti, 153 commenti: i resoconti 70/170 e 164/170, l'errore di runtime dei 30 secondi, le lamentele su accenti e output in spagnolo, il «Thank you.» in ripetizione e la negazione ribaltata
- Robust Speech Recognition via Large-Scale Weak Supervision — Radford et al. (arXiv 2212.04356) — il paper di Whisper; le Tabelle 9, 10 e 13 sono la fonte delle cifre di Whisper nel grafico di Whistle
- Moonshine: Speech Recognition for Live Transcription and Voice Commands — Jeffries et al. (arXiv 2410.15608) — il paper di Moonshine, Tabella 3
- nvidia/parakeet-tdt-0.6b-v2 — Hugging Face — il favorito per accuratezza per secondo tra i professionisti nel thread di lancio, CC-BY-4.0
- Turning Whisper into Real-Time Transcription System — Macháček et al. (arXiv 2307.14743) — la politica di streaming LocalAgreement che
needle.stream()implementa, e da dove viene davvero la sua latenza - Classification of Fricative Consonants for Speech Enhancement in Hearing Devices — PLOS ONE — perché 3–4 kHz di banda bastano per le vocali ma non per le fricative sorde, la cui energia sta sopra i 4 kHz
- The perceptual significance of high-frequency energy in the human voice — Monson et al., PMC — cosa scarta un front end limitato in banda, e cosa perdono gli ascoltatori con esso
Prossimi passi
- Full-duplex voice AI: why voice agents suddenly got real — l'altra metà del ciclo: cosa cambia quando un modello ascolta e parla contemporaneamente, e perché l'alternanza dei turni umana fissa il budget di latenza
- A private local AI stack — dove si colloca un passaggio di trascrizione offline accanto a tutto il resto che esegui sul tuo hardware
- Ternary quantization in practice: Bonsai 2 27B in 5.95 GB — la stessa lezione un livello più su: cosa nasconde una percentuale di retention, e perché un loader standard può emettere nonsenso sicuro di sé