Inflect Micro PT-BR — 20k passos de adaptação

Voz brasileira fixa construída sobre o owensong/Inflect-Micro-v2, treinada com 37,45 horas de fala em português do Brasil, um único falante, 24 kHz.

Este repositório traz o pacote PyTorch pronto para inferência, 24 áudios de teste em três configurações de prosódia, 40 amostras de validação registradas durante o treino e os arquivos de configuração usados no run.

O que este modelo é: um experimento de adaptação de voz única, com receita reproduzível e números de treino abertos. O que este modelo não é: um sistema multilocutor, um produto de clonagem de voz, ou um modelo com avaliação de inteligibilidade por MOS. Não medi MOS e não rodei avaliador de transcrição. A seção de limitações explica o que isso significa na prática.


Como ouvir antes de baixar

As três configurações usam as mesmas oito frases, para que a única variável seja a prosódia.

Configuração speed variation Duração total das 8 frases
default 1.0 0.667 19,41 s
slower 0.90 0.667 20,67 s
varied 0.95 0.80 19,99 s

Os arquivos estão em audio/prosody_tests/<config>/audio/frase-00.wav até frase-07.wav, e a lista de frases está em audio/prosody_tests/sentences.jsonl.

ID Frase
frase-00 Olá, tudo bem com você?
frase-01 Hoje está fazendo um dia muito bonito.
frase-02 O Brasil é um país de dimensões continentais.
frase-03 Você gostaria de ouvir uma história interessante?
frase-04 Não acredito que isso aconteceu!
frase-05 Que notícia maravilhosa!
frase-06 Por favor, me diga que horas são.
frase-07 Amanhã teremos uma reunião importante.

Também incluí samples/v1_mesmas_frases/ e samples/v2_frases/: são as mesmas seis frases sintetizadas pela versão de 4 mil passos e por esta de 20 mil passos. Serve para ouvir a diferença entre as duas estratégias de treino, não para declarar uma vencedora.


O problema que motivou o run

Adaptar um modelo de TTS para uma língua com pouca representação costuma seguir dois caminhos. Um deles é continuar o fine-tuning por muito tempo e aceitar o risco de degradar a inteligibilidade que o modelo base já tinha. O outro é parar cedo, ganhar sotaque e timbre rapidamente, e conviver com artefatos.

A versão anterior deste trabalho chegou a uma voz brasileira inteligível em 4 mil passos. Ela era audivelmente brasileira, mas ainda soava presa ao modelo base: prosódia previsível, pouca variação de entonação em frases longas.

A pergunta deste run foi se mais 16 mil passos, com um cronograma de descongelamento em etapas, melhorariam a naturalidade sem custar inteligibilidade. Essa é a hipótese que os números abaixo testam.


Dados

O conjunto de origem foi preparado com o inflect-adapt prepare, que valida duração, taxa de amostragem e cobertura fonética antes de qualquer treino.

Métrica Valor
Amostras totais 20.000
Treino 19.123
Validação 877
Duração total 134.807 s (37,45 h)
Duração mínima / máxima 1,50 s / 12,0 s
Taxa de amostragem 24 kHz
Falantes 1 (ID 2961)
Idioma / frontend pt-BR / espeak, com estresse

Dois detalhes do preparo merecem atenção, porque afetam o resultado.

O primeiro é a cobertura de símbolos do modelo base. Antes da adaptação, 97,6% dos símbolos do corpus já existiam no vocabulário do Inflect-Micro-v2. Os 2,4% restantes eram três símbolos: õ (1.142 ocorrências), ũ (6.783) e o til combinante ̃ (44.321). O preparo adicionou três símbolos ao inventário, que passou a ter 181 entradas. Ou seja: o vocabulário cresceu, mas o embedding base continuou sendo reaproveitado em 97,6% das posições.

O segundo é o split. Ele é agrupado por group_id e normaliza o texto antes de separar treino e validação, para que a mesma frase não apareça dos dois lados com pontuação diferente. Com 20 mil linhas, o alvo era 1.000 linhas de validação; o resultado foi 877, porque grupos inteiros foram mantidos juntos em vez de fatiados.

Esse é um trade-off que vale explicitar: validação menor, mas sem vazamento de texto entre treino e avaliação.


Configuração do treino

Parâmetro Valor
Modelo base owensong/Inflect-Micro-v2
Preset micro-12gb
Passos 20.000
Batch 4 × 2 de acumulação (efetivo 8)
Learning rate (G e D) 8e-5
Warm-up do posterior 1.500 passos
Descongelamento do decoder 6.000 passos
Precisão AMP
Hardware A100 40 GB
Seed (treino / dataset) 1234 / 42
Checkpoint / validação a cada 500 passos

O cronograma é o ponto central. Nos primeiros 1.500 passos só o posterior treina, com lr subindo de zero. O decoder permanece congelado até o passo 6.000, o que preserva a qualidade acústica do modelo base enquanto o modelo aprende o mapeamento fonético do português. Depois disso, o decoder entra com lr menor.

No fim do run, as taxas estavam em 6,95e-5 para o posterior, 3,48e-5 para a parte linguística e 6,95e-6 para o decoder. A assimetria é intencional: o decoder se move devagar para não desfazer o que o modelo base já sabia fazer.


Resultados

As perdas abaixo são médias por faixa de passos, sobre 22.404 registros de metrics.jsonl.

Passos loss_g loss_d
1–999 75,22 2,192
1.000–4.999 45,84 1,857
5.000–9.999 29,97 2,562
10.000–14.999 26,13 2,695
15.000–19.499 25,64 2,687
19.500–20.000 25,53 2,679

O loss_g cai 66% entre a primeira faixa e a última (75,22 para 25,53). É a curva esperada de adaptação: o gerador passa a produzir algo mais próximo da distribuição do falante alvo.

O loss_d faz outra coisa, e é a parte interessante. Ele cai de 2,192 para 1,857 nos primeiros 5 mil passos, porque o discriminador aprende rápido a separar o modelo base da fala brasileira. Depois sobe para 2,56 e se estabiliza perto de 2,69.

Isso não é degradação. É o equilíbrio se reajustando. Quando o gerador começa a produzir fala do falante alvo, o discriminador perde o atalho de distinguir "modelo base" de "fala real" e precisa trabalhar com diferenças mais finas. Um loss_d que ficasse muito baixo no fim seria sinal de problema: significaria que o discriminador venceu e o gerador estagnou.

A leitura conjunta é o que sustenta a conclusão: loss_g descendo de forma monotônica na média e loss_d estabilizado em torno de 2,68, sem colapso de nenhum dos dois lados.

Um aviso metodológico: essas perdas medem o ajuste ao corpus, não a qualidade perceptual. Elas não substituem um teste com ouvintes.


Prosódia: três configurações, mesma frase

O teste de prosódia usa oito frases, o mesmo seed (42) e device=cpu, variando apenas speed e variation. As três rodadas passaram na validação automática: 8/8 avaliações, zero falhas, zero clips com clipping, zero trechos silenciosos.

A comparação entre default (speed 1.0) e slower (speed 0.90) mostra que o parâmetro funciona, mas não de forma perfeitamente linear. A razão média de alongamento por frase ficou em 1,066, e a razão das durações totais em 1,065, quando 1/0,90 seria 1,111. O desvio é maior em frases curtas: "Não acredito que isso aconteceu!" alongou 2,4% em vez de 11%, enquanto a frase mais longa alongou 9,1%.

Isso diz algo prático sobre o parâmetro: ele ajusta a duração por fonema, e a duração total ainda depende de como o modelo aloca pausas. Para controle fino de tempo, o caminho é ajustar a duração alvo explicitamente, não só o multiplicador de velocidade.

O varied (speed 0.95, variation 0.80) ficou entre os dois em duração, com contorno de entonação mais solto. É a configuração que eu escolheria para narração; default soa mais previsível e serve melhor para leitura de texto técnico.


O que deu errado, e o que isso ensinou

Três problemas reais deste run. Nenhum é específico deste modelo, e todos custaram tempo.

Cinco treinos na mesma GPU. Ao reiniciar o run depois de uma queda de sessão, eu subia o processo novo sem encerrar o antigo. Em poucos ciclos havia cinco processos inflect-adapt disputando a mesma A100 e gravando no mesmo latest.pth. O sintoma não era um erro limpo: era o treino "andando para trás", porque cópias diferentes salvavam checkpoints com estados distintos de otimizador.

A lição é que reinício de treino precisa ser idempotente. O start_one.py agora mata a árvore de processos por PID e só então sobe o novo; se sobrar qualquer processo vivo, ele aborta em vez de seguir.

O eixo de passos do W&B. Um run retomado recebe passos menores do que o histórico já registrado. O W&B rejeita passos não crescentes, então cada linha nova era descartada com um aviso. O painel parecia congelado enquanto o treino corria normalmente.

A correção foi separar as duas coisas: o contador do W&B continua monotônico, e o passo real de treino vai como métrica (train/step). Assim o painel avança e o número exibido é o passo verdadeiro.

O áudio de validação que sumia. O sincronizador checava primeiro se havia linha nova em metrics.jsonl. Se não houvesse, ele retornava antes de olhar os áudios. Como métricas são gravadas a cada 25 passos e validação a cada 500, o envio de áudio só acontecia quando as duas coisas coincidiam. O resultado foram buracos irregulares no W&B que pareciam falha do modelo.

A correção foi tratar métricas e áudio como independentes no mesmo ciclo. Também passei a persistir em disco a lista de áudios já enviados, porque antes ela vivia só em memória e cada reinício reenviava os 34 anteriores.

Três bugs, uma causa comum: estado que sobrevive a um reinício e não é reconciliado. Vale checar isso antes de culpar o modelo por um número estranho.


Como usar

O pacote é autocontido. Não precisa instalar o inflect-adapt para inferir.

pip install -r requirements.txt

python inference.py --text "Bom dia, tudo bem com você?" --output saida.wav

Com controle de prosódia:

python inference.py \
  --text "A reunião começou às nove da manhã." \
  --speed 0.9 \
  --variation 0.8 \
  --output saida.wav

Em lote, um laço simples sobre as frases lidas de um jsonl:

while IFS= read -r linha; do
  id=$(echo "$linha" | python3 -c "import sys,json;print(json.load(sys.stdin)['id'])")
  txt=$(echo "$linha" | python3 -c "import sys,json;print(json.load(sys.stdin)['text'])")
  python inference.py --text "$txt" --output "audios/$id.wav"
done < frases.jsonl

O frontend é espeak com estresse e pontuação preservada. A entrada é texto normal; não é preciso passar fonemas.


Limitações

Estas são limitações medidas, não ressalvas de praxe.

Não há avaliação de inteligibilidade. O transcript evaluator estava desabilitado em todas as rodadas. Os relatórios confirmam que o áudio é válido — sem clipping, sem silêncio, duração plausível — mas não medem se um ouvinte entende o que foi dito. A afirmação "a voz é inteligível" continua sendo julgamento auditivo, não resultado medido.

Não há MOS. Nenhum teste com ouvintes foi conduzido.

Voz única. O modelo tem um falante. Não aceita speaker_id e não foi treinado para generalizar timbre.

Domínio estreito. O corpus é de fala lida, entre 1,5 e 12 segundos por amostra. Frases muito longas são divididas em partes e sintetizadas separadamente, o que pode gerar descontinuidade de prosódia nas junções.

Números podem enganar. Números escritos por extenso, siglas e termos estrangeiros dependem do frontend espeak e não foram avaliados de forma sistemática.

Licença. O pacote carrega a licença do modelo base. Verifique os termos do owensong/Inflect-Micro-v2 e do corpus de origem antes de uso comercial.


O que eu faria diferente

Duas coisas, em ordem de impacto.

A primeira é instrumentar avaliação desde o início. Um avaliador de transcrição barato, rodando a cada 2 mil passos, teria transformado "parece melhor" em uma curva. Sem ele, a decisão de ir de 4 mil para 20 mil passos se apoia em perdas e escuta, não em medida.

A segunda é separar o teste de prosódia do run principal desde o começo. As três configurações que gerei no fim teriam mostrado cedo que variation afeta pouco a duração e muito o contorno. Isso teria economizado algumas iterações.

Sobre a hipótese inicial: a naturalidade melhorou e não vi degradação de inteligibilidade nas amostras que ouvi. Mas "não vi" é diferente de "medi", e a diferença é o próximo trabalho.


Reprodução

Caminho Conteúdo
training/launch-config.yaml Configuração efetiva gravada pelo run
training/train.py Launcher com retomada e sincronização W&B
training/supervisor.py Reinício automático do último checkpoint íntegro
training/hyperparameters.yaml Hiperparâmetros
validation/ 40 pares de áudio e metadados de validação
audio/prosody_tests/ Teste de prosódia em três configurações
checksums.sha256 Checksums do pacote exportado
export_report.json Relatório de verificação do export

O treino consumiu cerca de 20 mil passos a 1,25–2,0 s/passo em uma A100 40 GB, com picos de uso de memória em torno de 3,2 GB.

A reprodução exata depende do corpus de origem, que não está incluído aqui por questões de licença. O launch-config.yaml registra os caminhos usados.


Como citar

@misc{rios2026inflectptbr,
  author = {Hugo Rios},
  title  = {Inflect Micro PT-BR: 20k steps of single-speaker adaptation},
  year   = {2026},
  url    = {https://huggingface.co/HRB25/inflect-micro-ptbr-20k}
}

Agradecimentos

Ao owenawsong pelo Inflect e pelo checkpoint Micro-v2, sobre o qual tudo isso foi construído.

Downloads last month
14
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support

Model tree for HRB25/inflect-micro-ptbr-20k

Finetuned
(7)
this model