jvscribe-ptbr-zipformer-ctc-64m

ASR de português brasileiro que roda em tempo real sobre CPU, sem GPU e sem chamada de rede. Zipformer-CTC de 64M parâmetros, quantizado int8, com cabeça de fonema auxiliar.

A aposta é especialização: um modelo que só faz PT-BR cabe em dezenas de milhões de parâmetros onde um multilíngue de 600M não fecha real-time em CPU.

Resultados

[MEDIDO] 2026-07-30 — FLEURS pt_br test[0:100], 2.552 palavras, decode greedy CTC, ONNX Runtime int8, 4 threads, i7 12-core com load average < 1.

métrica valor
WER 15,99%
CER 7,30%
RTFx 40,0×

RTFx é a razão entre duração do áudio e tempo de processamento — 40× significa que 40 segundos de áudio são transcritos em 1 segundo. O requisito do produto é ≥ 6×.

Como este peso foi escolhido

Dois candidatos foram medidos no mesmo conjunto, com a mesma régua de normalização:

peso WER CER RTFx
model.int8.onnx (média de 112k+124k) 15,99% 7,30% 40,0×
alternates/ckpt124k.int8.onnx (checkpoint único) 17,32% 7,39% 34,3×

Bootstrap pareado de 5.000 reamostragens: IC95% do delta = [−2,25, −0,43] pp. O intervalo não cruza zero — a vantagem do checkpoint averaging é real, não ruído amostral.

O model_card.json é a autoridade legível por máquina sobre qual arquivo é o canônico. Não deduza o peso oficial pelo nome do arquivo — neste projeto, escolher por nome já entregou o modelo pior em silêncio.

Uso

import onnxruntime as ort

sess = ort.InferenceSession("model.int8.onnx", providers=["CPUExecutionProvider"])
tokens = [l.split()[0] for l in open("tokens.txt", encoding="utf-8")]

# entrada: log-mel fbank 80-bin, kaldi/HTK, 16 kHz  ->  (N, T, 80)
logits = sess.run(None, {"x": feats, "x_lens": lens})[0]   # (N, T, 500)

# decode: CTC greedy — argmax, colapsa repetições, remove o blank (id 0)

O pipeline completo (fbank, segmentação, decode, lote) está em jvscribe:

python3 jvscribe/batch/batch_transcribe.py --input-dir audios/ --out-dir saida/

Arquivos

arquivo tamanho papel
model.int8.onnx 68 MB modelo oficial — WER 15,99%
tokens.txt 5 KB 500 tokens BPE emitíveis; fingerprint SHA-256 9fcb45e4…
model_card.json 1 KB contrato legível por máquina (arquivo canônico, sha256, fingerprint do vocabulário, WER e condição da medição)
alternates/ckpt124k.int8.onnx 68 MB checkpoint único, WER 17,32%
alternates/ckpt124k.fp32.onnx 249 MB fp32 do acima — ponto de partida para requantizar
finetune/ 2,6 GB tudo para retomar o treino — ver finetune/README.md

O par (modelo, vocabulário) não é intercambiável. Dois artefatos deste projeto têm 500 tokens cada e 492 dos 500 ids mapeiam para tokens diferentes: trocar o tokens.txt produz texto plausível e errado, sem erro algum. Valide pelo vocab_fingerprint do model_card.json, nunca pela contagem de tokens.

Arquitetura

encoder Zipformer, 6 stacks
num-encoder-layers 2,2,3,4,3,2
feedforward-dim 512,768,1024,1536,1024,768
encoder-dim 192,256,384,512,384,256
encoder-unmasked-dim 192,192,256,256,256,192
decoder CTC greedy (sem transducer)
cabeça auxiliar CTC de fonema (--use-phoneme-ctc 1)
tokenização SentencePiece BPE, 500 unidades, PT-BR dedicado
features log-mel fbank 80-bin, kaldi/HTK, 16 kHz
parâmetros ~64M
quantização int8 dinâmica (ONNX Runtime)

A cabeça de fonema é auxiliar de treino — ela regulariza o encoder e sai do grafo de inferência. Ganho medido em M4: −4,74% relativo de WER (IC95% [2,79, 6,72]).

Treino

Receita icefall zipformer sobre egs/commonvoice/ASR. Corpus: TAGARELA (8.972 h, 91% PT-BR, pseudo-rotulado) com finetune em CORAA. Suíte de avaliação do projeto inclui Common Voice 21.0 (CC0) e MLS-PT (CC-BY-4.0).

Para retomar o treino, ver finetune/README.md. Ele traz os comandos reais, as quatro flags de arquitetura sem as quais o checkpoint não carrega, e as armadilhas que já custaram runs inteiros neste projeto.

Limitações — leia antes de usar em produção

  • Telefonia 8 kHz não foi medida. Todos os números aqui são banda larga 16 kHz. A literatura indica penalidade de 2–3× em canal telefônico; para este modelo isso é [DESCONHECIDO].

  • Fala espontânea de call center não foi medida. FLEURS é leitura de notícias, um regime mais fácil que conversa real.

  • Só português brasileiro. Não há suporte multilíngue nem code-switching validado.

  • Overfitting ao professor. O finetune de M5 apresenta overfitting aos pseudo-rótulos do Whisper presentes no TAGARELA — o gargalo é qualidade de dado, não capacidade do encoder.

  • Este modelo NÃO é streaming. É não-causal: a atenção enxerga o contexto inteiro nos dois sentidos. Três confirmações independentes — o metadado do artefato diz comment: "non-streaming zipformer2 CTC"; o grafo ONNX não tem entrada nem saída de estado (só x, x_lenslog_probs, log_probs_len); e os scripts de treino nunca passam --causal 1, cujo default no icefall é False.

    Consequência prática: dá para usá-lo ao vivo com janela deslizante + LocalAgreement-2 (é o que jvscribe/realtime/ faz, técnica do Whisper-Streaming), mas cada atualização reprocessa a janela inteira. Medido: 121 ms para reprocessar 6 s contra 11 ms que custaria processar só os 0,5 s novos — 10,6× de trabalho redundante por hop, que é o piso de latência do desenho. Um modelo com cache de estado remove esse termo, e isso exige retreinar com --causal 1 e reexportar com entradas de estado; não é ajuste de runtime.

  • avg-124k-112k.pt foi reconstruído. O .pt original chegou truncado da nuvem; ver finetune/README.md § Incidente. A equivalência bit a bit com o original não foi verificada.

Licença

otheros termos de redistribuição são herdados dos corpora de treino e ainda não foram formalmente apurados. O TAGARELA carrega pseudo-rótulos gerados por Whisper; CORAA, Common Voice e MLS-PT têm licenças próprias e distintas entre si. Este repositório é privado justamente porque essa apuração está pendente.

Não redistribua nem use comercialmente sem antes revisar a licença de cada corpus de origem.

Citação

@misc{jvscribe2026,
  title  = {jvscribe: ASR PT-BR em tempo real sobre CPU},
  author = {Paulo Henrique},
  year   = {2026},
  note   = {Zipformer-CTC 64M, int8, WER 15,99\% em FLEURS pt_br}
}
Downloads last month

-

Downloads are not tracked for this model. How to track
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support

Evaluation results