Mangaba AI · Relatório técnico

Mangaba-Base-91M: um modelo de linguagem de 91 milhões de parâmetros treinado do zero em português

Em menos de cinco horas numa única GPU, com dados públicos e tokenizer próprio, ele supera um modelo 1,8 vezes maior no seu domínio e empata no CALAME-PT. Como foi treinado, onde ganha, onde perde e por quê.

Outubro de 2026 Modelo no Hugging Face Licença Apache-2.0

Resumo

91,25Mparâmetros (3% no embedding)
2,52Btokens de treino em PT-BR
0,886bits por byte no domínio (Tucano-160m: 0,929)
4h52em uma L40S, 145 mil tokens/s
  1. Por que 91M
  2. Dados e tokenizer
  3. Arquitetura
  4. Treino
  5. Resultados
  6. Exemplos de geração
  7. Como professor de destilação
  8. Limitações e próximos passos
  9. Como usar e reproduzir

1. Por que 91M

O Base-91M nasceu com um propósito específico: ser o professor do Mangaba-Nano-1M, um modelo de 1,4 milhão de parâmetros, numa destilação de logits. Para isso, ele precisava ser bem maior que o aluno, usar o mesmo tokenizer e caber, com folga, numa noite de GPU.

Na faixa de 100 milhões de parâmetros, um modelo de linguagem já escreve frases conectadas e mantém o tema por um parágrafo. Ainda assim, roda em CPU e cabe em 97 MB no formato GGUF Q8_0. É um bom ponto de partida para ajuste fino em tarefas estreitas em português, como classificação, extração e autocompletar, e para estudar o que muda entre escalas.

2. Dados e tokenizer

Usamos só dados públicos em português, do GigaVerbo-v2:

O corpus somou 21,6 bilhões de tokens, com ~24% de texto sintético. Descartamos documentos com menos de 200 caracteres e normalizamos tudo em Unicode NFC. Os trechos de treino foram sorteados ao acaso, proporcionalmente ao tamanho de cada arquivo. Os arquivos foram tokenizados durante o próprio treino: na primeira hora, só os 2,9B tokens iniciais estavam prontos, e os demais entraram à medida que ficavam disponíveis. A validação (14 milhões de tokens) foi separada antes do treino e nunca entrou nele.

Tokenizer compartilhado

O tokenizer é um BPE byte-level de 4.096 tokens, treinado do zero no próprio corpus, com 3,4 a 3,7 caracteres por token. Esse vocabulário pequeno foi escolhido para o Nano-1M, em que cada token pesa no orçamento de parâmetros. O Base-91M o herdou porque a destilação de logits exige que professor e aluno prevejam sobre o mesmo vocabulário. Para um modelo deste porte, é um vocabulário pequeno (ver seção 8).

3. Arquitetura

É um decoder no desenho atual da indústria, o mesmo de famílias como Qwen3 e Llama: RMSNorm antes de cada bloco, RoPE, SwiGLU, atenção com consultas agrupadas (GQA), QK-norm e embeddings amarrados. Usamos a classe Qwen3ForCausalLM do Transformers só como implementação desse desenho: os pesos foram inicializados aleatoriamente e nenhum peso da Qwen é usado. Por isso o config.json mostra model_type: qwen3.

ComponenteMangaba-Base-91MMangaba-Nano-1M
Camadas × dimensão14 × 7686 × 128
FFN (SwiGLU)2.048256
Cabeças de query / key-value12 / 44 / 2
head_dim6432
Contexto512 tokens512 tokens
Parâmetros91.250.1761.411.072
Fração no embedding3%37%

4. Treino

Curva de validação do Base-91M: de 2,82 com 0,13B tokens até 2,132 com 2,52B tokens; o Nano-1M termina em 3,125.
Perda de validação. Os dois modelos usam o mesmo tokenizer e a mesma validação, então os números são diretamente comparáveis. A faixa marca o resfriamento do Base-91M, quando a perda volta a cair depois do platô.

5. Resultados

As métricas:

Base-91M contra Tucano-160m: BPB GigaVerbo 0,886 contra 0,929; Wikipédia 1,091 contra 0,955; CALAME 45,7% contra 45,6%; Story Cloze 57,8% contra 61,7%.
Os quatro benchmarks, medidos com o mesmo script (bench.py), em CPU e float32.
ModeloParâmetrosBPB GigaVerbo ↓BPB Wikipédia ↓CALAME-PT ↑Story Cloze ↑
Mangaba-Base-91M91M0,8861,09145,7%57,8%
Tucano-160m162M0,9290,95545,6%61,7%
Mangaba-Nano-1M1,41M1,2821,59317,1%56,2%

Leitura

6. Exemplos de geração

Amostragem com temperatura 0,7, top-p 0,9 e penalidade de repetição 1,15. São os trechos iniciais das cinco amostras geradas, sem escolher as melhores:

O Brasil é um país de grande riqueza, com uma população que tem crescido ao redor
do mundo. Para chegar lá, temos que nos adequar a esse novo ambiente. O que há em
termos de mudança, na realidade?

A fotossíntese é o processo pelo qual a planta realiza sua respiração, através da
luz que recebe do sol. Esse processo acontece na presença de elementos químicos
como enxofre e nitrogênio.

Era uma vez uma menina que tinha um vestido, mas não era bonito, ela dizia: “Que
mulher!”, e a menina ria.

A Constituição Federal de 1988. SOARES, Celso Antônio Bandeira de. Magistério da
Educação Infantil: uma proposta para a formação do professor em creche e pré-escola.

Para fazer um bolo de cenoura, você pode usar pedaços pequenos e então cozinhe-os
em água quente. Isso ajudará a evitar que eles se tornem grumosos demais.

As frases são gramaticais, se conectam e mantêm o tema. Os fatos, porém, não são confiáveis: a fotossíntese não é respiração e não depende de enxofre. No quarto exemplo, o modelo reproduz o formato de uma referência bibliográfica, padrão comum em textos acadêmicos da web.

7. Como professor de destilação

O Base-91M foi usado para destilar o Mangaba-Nano-1M: o aluno aprendeu a distribuição completa de probabilidades do professor para o próximo token, com perda de 30% cross-entropy e 70% divergência KL, ao longo de 4,9B tokens. O resultado no aluno foi misto:

A explicação mais provável é a lacuna de capacidade: com 65 vezes mais parâmetros, o professor gera distribuições que o aluno não consegue imitar. Um professor intermediário, de 5 a 20M de parâmetros, deve funcionar melhor para um aluno de 1M. Os detalhes estão no relatório do Mangaba-Nano-1M.

8. Limitações e próximos passos

Os próximos ganhos mais prováveis:

9. Como usar e reproduzir

from transformers import AutoModelForCausalLM, AutoTokenizer
tok = AutoTokenizer.from_pretrained("mangaba-ai/Mangaba-Base-91M")
model = AutoModelForCausalLM.from_pretrained("mangaba-ai/Mangaba-Base-91M")
ids = tok("A fotossíntese é o processo", return_tensors="pt").input_ids
out = model.generate(ids, max_new_tokens=60, do_sample=True, temperature=0.7,
                     top_p=0.9, repetition_penalty=1.15)
print(tok.decode(out[0], skip_special_tokens=True))

Com o llama.cpp: llama-completion -m Mangaba-Base-91M-Q8_0.gguf -p "O Brasil é um país" -n 60.

O repositório do modelo traz o train2.py usado no treino (dados em vários arquivos, mistura configurável, orçamento por relógio e destilação), o bench.py da suíte de benchmarks e o log.jsonl com a perda de validação ao longo do treino. O comando de treino:

python train2.py --save teacher100m --hidden 768 --layers 14 --heads 12 --kv 4 \
  --ffn 2048 --micro 32 --accum 16 --tokens 2e10 --minutes 295 \
  --lr_muon 0.02 --lr_adam 0.006 --eval_every 500