{"id":80,"date":"2026-06-19T22:00:00","date_gmt":"2026-06-19T22:00:00","guid":{"rendered":"https:\/\/rpi.temporiti.net\/wordpress\/?p=80"},"modified":"2026-06-20T06:21:21","modified_gmt":"2026-06-20T04:21:21","slug":"whisper-medium-trascrizione","status":"publish","type":"post","link":"https:\/\/rpi.temporiti.net\/wordpress\/?p=80","title":{"rendered":"Trascrizione audio difficile con faster-whisper medium"},"content":{"rendered":"<p>Per il 90 percento dell&#8217;audio che mi capita di trascrivere, il modello small di Whisper basta e avanza. Poi c&#8217;\u00e8 quell&#8217;altro 10 percento: il webinar tecnico ripreso con un microfono da pochi euro montato sul laptop del relatore, la sessione con un secondo relatore in collegamento remoto su una linea ballerina che disturba per met\u00e0 del tempo, la tavola rotonda dove tre persone si parlano sopra e nessuno tiene il microfono fermo. In quei casi il small comincia ad arrancare, e i refusi non sono pi\u00f9 refusi: sono frasi inventate che sembrano plausibili. \u00c8 il momento del medium.<\/p>\n<p>Il medium di faster-whisper pesa pi\u00f9 o meno tre volte il small (intorno a 1,5 GB), e richiede pi\u00f9 o meno tre volte la CPU per produrre lo stesso minuto di trascrizione. Vale la pena tirarlo fuori solo quando l&#8217;audio \u00e8 davvero compromesso, perch\u00e9 altrimenti il guadagno qualitativo non giustifica l&#8217;attesa.<\/p>\n<h2>Setup con faster-whisper<\/h2>\n<p>L&#8217;installazione \u00e8 identica a quella che uso per il small: stesso virtualenv, stesso runtime, cambia solo il nome del modello che chiedo a <code>WhisperModel<\/code>. Su Debian 13 la base \u00e8 quella di sempre.<\/p>\n<pre><code class=\"language-bash\">\nsudo apt install -y ffmpeg python3-venv\npython3 -m venv ~\/.venvs\/whisper\nsource ~\/.venvs\/whisper\/bin\/activate\npip install --upgrade pip faster-whisper\n<\/code><\/pre>\n<p>Lo script che mi tengo per i casi tosti ha qualche parametro in pi\u00f9 rispetto al fratello minore. Aumento <code>beam_size<\/code> per dare al decoder margine di esplorazione, e attivo <code>condition_on_previous_text=False<\/code> per evitare che un errore si propaghi sui segmenti successivi (\u00e8 il classico effetto &#8220;loop di hallucination&#8221; che Whisper fa quando il VAD non aiuta).<\/p>\n<pre><code class=\"language-python\">\nfrom faster_whisper import WhisperModel\n\nmodel = WhisperModel(\n    \"medium\",\n    device=\"cpu\",\n    compute_type=\"int8\",\n    cpu_threads=8,\n    num_workers=1,\n)\n\nsegments, info = model.transcribe(\n    \"webinar-rumoroso.mp4\",\n    language=\"it\",\n    beam_size=8,\n    best_of=5,\n    vad_filter=True,\n    vad_parameters=dict(min_silence_duration_ms=500),\n    condition_on_previous_text=False,\n)\n\nwith open(\"webinar-rumoroso.txt\", \"w\") as f:\n    for seg in segments:\n        f.write(f\"[{seg.start:7.2f}] {seg.text.strip()}\\n\")\n<\/code><\/pre>\n<p>Solita regola: <code>cpu_threads=8<\/code>. Sul portatile a 12 core posso lasciare lavorare il modello in background mantenendo la macchina reattiva per quello che faccio in primo piano. Saturare tutti i thread mi ha gi\u00e0 regalato due episodi di thermal throttling, e da allora la regola \u00e8 ferma.<\/p>\n<p>Per file molto lunghi conviene spezzare a monte con <code>ffmpeg<\/code> in chunk di 10-15 minuti: il decoder lavora meglio su segmenti gestibili, e in caso di crash si recupera solo la parte interrotta.<\/p>\n<pre><code class=\"language-bash\">\nffmpeg -i webinar-rumoroso.mp4 -f segment -segment_time 900 -c copy chunk-%03d.mp4\n<\/code><\/pre>\n<h2>Un esempio reale<\/h2>\n<p>Due settimane fa ho recuperato un webinar tecnico di 70 minuti su osservabilit\u00e0 di cluster Kubernetes. Il relatore aveva fatto la sessione dal salotto di casa, microfono integrato del laptop, riverbero della stanza, condizionatore acceso in sottofondo, e per giunta i 12 minuti centrali con audio mono sbilanciato sul canale destro perch\u00e9 aveva mosso le cuffie. Una bestia.<\/p>\n<p>Il small su quel file mi aveva prodotto una trascrizione piena di frasi inventate, intere sezioni dove Whisper si era impuntato a ripetere la stessa parola per dieci secondi e poi era ripartito a caso. Il medium con i parametri sopra ci ha messo circa 40 minuti di elaborazione, ma il risultato era usabile. Ho dovuto correggere a mano qualche nome di tool (Loki diventava ogni tanto &#8220;loki&#8221; lowercase, OpenTelemetry diventato &#8220;open telemetri&#8221; per qualche secondo) e una manciata di sigle, ma le frasi erano frasi vere, i timestamp tornavano, e ho potuto estrarre con <code>grep<\/code> tutti i passaggi dove il relatore parlava di cardinality limits sui label di Prometheus che era esattamente quello che mi serviva per gli appunti.<\/p>\n<h2>Cosa fa bene<\/h2>\n<p>Audio sporco, voci sovrapposte, riverbero, microfoni mediocri: il salto qualitativo rispetto al small \u00e8 netto. Gestisce meglio le interruzioni e le ripartenze di frase. Sui termini tecnici inglesi pronunciati con accento italiano ha pi\u00f9 tenuta. Quando il relatore cambia tono o velocit\u00e0 improvvisamente, il decoder si adatta senza perdere i pezzi.<\/p>\n<h2>Cosa fa meno bene<\/h2>\n<p>Tempi di elaborazione che diventano scomodi: su CPU a 8 thread, su una macchina di qualche anno, conta pi\u00f9 o meno 30-40 minuti di calcolo per ogni ora di audio. Se non \u00e8 la macchina su cui lavoro in primo piano va benissimo; se \u00e8 il portatile su cui devo continuare a scrivere durante la giornata, lo lancio la sera. Sui nomi propri continua a inventare quando l&#8217;audio \u00e8 davvero brutto, ma \u00e8 limite intrinseco del modello, non un suo bug.<\/p>\n<h2>Privacy &#8211; vantaggio del modello locale<\/h2>\n<p>Stessa storia del fratello small, e qui ci tengo ancora di pi\u00f9: per principio l&#8217;audio che do in pasto al medium non passa mai per un&#8217;API esterna. Sul mio host parte la trascrizione, finita la trascrizione il file resta sull&#8217;host, fine.<\/p>\n<p>Cache pesi in <code>~\/.cache\/huggingface\/hub\/models--Systran--faster-whisper-medium\/<\/code>, eliminabile con <code>rm -rf<\/code> quando voglio fare spazio. Confronto con l&#8217;API Whisper di OpenAI: il file audio andrebbe caricato, e la policy di retention dipende dal piano contrattuale; qui non c&#8217;\u00e8 nessun caricamento e nessun contratto.<\/p>\n<p>Licenze: il modello Whisper \u00e8 MIT (OpenAI), faster-whisper come libreria \u00e8 MIT, runtime CTranslate2 MIT. Stack permissivo, riutilizzabile dentro qualsiasi tooling interno senza vincoli copyleft.<\/p>\n<h2>In pratica<\/h2>\n<p>Il medium \u00e8 la mia rete di sicurezza per i file che il small non riesce a domare. Lo tengo nel virtualenv accanto al small, lo invoco cambiando solo una stringa nel codice, lo lascio macinare la sera. Per i webinar tecnici brutti e rumorosi \u00e8 quello che fa la differenza fra una trascrizione inutilizzabile e una bozza da cui posso ricostruire i contenuti senza riascoltare nulla.<\/p>\n<hr>\n<blockquote>\n<p>Immagine generata con ComfyUI Mac M1 \/ RealVisXL V5 Lightning.<\/p>\n<\/blockquote>\n","protected":false},"excerpt":{"rendered":"<p>Per il 90 percento dell&#8217;audio che mi capita di trascrivere, il modello small di Whisper basta e avanza. Poi c&#8217;\u00e8 quell&#8217;altro 10 percento: il webinar tecnico ripreso con un microfono da pochi euro montato sul laptop del relatore, la sessione con un secondo relatore in collegamento remoto su una linea ballerina che disturba per met\u00e0 [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":81,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[6],"tags":[],"class_list":["post-80","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-ai-locale"],"_links":{"self":[{"href":"https:\/\/rpi.temporiti.net\/wordpress\/index.php?rest_route=\/wp\/v2\/posts\/80","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/rpi.temporiti.net\/wordpress\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/rpi.temporiti.net\/wordpress\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/rpi.temporiti.net\/wordpress\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/rpi.temporiti.net\/wordpress\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=80"}],"version-history":[{"count":6,"href":"https:\/\/rpi.temporiti.net\/wordpress\/index.php?rest_route=\/wp\/v2\/posts\/80\/revisions"}],"predecessor-version":[{"id":396,"href":"https:\/\/rpi.temporiti.net\/wordpress\/index.php?rest_route=\/wp\/v2\/posts\/80\/revisions\/396"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/rpi.temporiti.net\/wordpress\/index.php?rest_route=\/wp\/v2\/media\/81"}],"wp:attachment":[{"href":"https:\/\/rpi.temporiti.net\/wordpress\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=80"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/rpi.temporiti.net\/wordpress\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=80"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/rpi.temporiti.net\/wordpress\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=80"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}