[Engenharia de ia/dados] Construindo um corpus PT-BR de 8,4M documentos com C4/FineWeb2, filtragem via SBERT e geração sintética via LLMs
Eu publiquei recentemente o Corpus PT-BR v1, um corpus em português com 8,4 milhões de documentos e ~6,3 bilhões de tokens, em Parquet, com deduplicação e schema unificado.
Link para explição completa:https://huggingface.co/datasets/Madras1/corpus-ptbr-v1
A motivação do projeto foi bem prática. O ecossistema de IA em português, especialmente no Brasil e na comunidade lusófona, ainda sofre com limitações de infraestrutura, financiamento e disponibilidade de dados. Existem algumas iniciativas relevantes, mas em volume, escala e atualização ainda são poucas.
Além disso, para muitos trabalhos de IA de verdade, como avaliação de modelos, sistemas de RAG, benchmarks, fine-tuning e continual pretraining, é difícil avançar sem uma base de dados em português. Na prática, muitas vezes o caminho acaba sendo traduzir datasets já consolidados em inglês, como SQuAD e outros semelhantes, em vez de trabalhar com uma base nativa mais ampla.
Então a ideia da v1 foi montar um corpus mais utilizável para fine-tuning, continual pretraining, RAG e também como base para derivar datasets mais específicos para aplicações futuras.
Estrutura do corpus
O corpus foi construído em duas camadas:
1. Dados reais curados
O subset real veio principalmente de C4 PT e FineWeb2 PT. Essas bases já tinham volume e cobertura razoáveis em português, mas ainda traziam muito ruído para uso direto em treino.
Para melhorar isso, eu adicionei uma etapa de quality filtering baseada em SBERT. O processo foi, em alto nível, o seguinte:
- rotular automaticamente algumas dezenas de milhares de exemplos com um modelo maior;
- usar esses rótulos para treinar um modelo SBERT menor;
- usar o SBERT como filtro semântico de qualidade em escala.
A inspiração geral dessa etapa veio do trabalho da equipe da Hugging Face no FineWeb, mas adaptada para uma execução mais viável em contexto individual e de baixo orçamento.
2. Dados sintéticos
A segunda camada foi uma etapa de geração sintética para complementar o subset real.
Aqui o objetivo não era só aumentar volume, mas tentar evitar colapso de estilo. Para isso eu misturei:
- múltiplos modelos;
- personas e system prompts diferentes;
- formatos textuais variados;
- temas diferentes;
- geração em batch.
A ideia não era simplesmente “simular um crawl”, mas gerar uma camada complementar com mais diversidade lexical, estilística e discursiva, além de manter uma qualidade média alta de conteúdo.
Engenharia de custo
A parte mais complicada do projeto não foi só a curadoria dos dados, mas o custo da geração em escala.
Se eu usasse só o caminho mais direto, fazendo tudo por API padrão, o custo subiria rápido, principalmente pelo volume de tokens sintéticos gerados(1b). Então a solução acabou sendo uma mistura de:
- inferência local quando fazia sentido, usando tanto CPU quanto GPU;
- cloud compute com arbitragem de GPU spot em diferentes provedores;
- batching pesado;
- arbitragem entre APIs, aproveitando preços mais baixos em determinados momentos e também planos mais vantajosos por volume.
Esse ponto, para mim, foi tão importante quanto a parte de modelagem. Em projetos de dados para LLM, custo e throughput acabam virando restrições de primeira ordem. Construir uma infraestrutura viável para projeto open-source, sendo solo ou com baixo orçamento, já é por si só um desafio que inviabiliza muita iniciativa logo no começo.
Formato final
O corpus foi publicado em Parquet com schema simples:
textsourcesubsetword_countchar_countlanguage
Isso deixa o dataset mais fácil de filtrar, inspecionar e reutilizar em pipelines diferentes.
Limitações
Apesar da curadoria, a v1 ainda tem limitações claras:
- o subset real ainda pode carregar ruído herdado das fontes de crawl;
- existe ruído residual de variante no português;
- a parte sintética pode conter alucinações factuais;
- eu ainda não fechei um benchmark downstream mais sólido para medir ganho de forma limpa, embora as avaliações preliminares do SBERT e as checagens manuais tenham sido positivas até aqui.
Então eu vejo essa versão mais como um primeiro release público utilizável do que como um corpus “fechado”.
Próximos passos
Os pontos que eu considero mais úteis para uma próxima iteração são:
- melhorar a separação de variante PT-BR vs PT-PT;
- fazer auditoria mais fina do subset real;
- organizar melhor os scripts e o pipeline público;
- rodar benchmarks simples para medir utilidade prática do corpus;
- em uma segunda fase, aumentar ainda mais o dataset, já que um dos meus outros projetos está caminhando para inferência local mais otimizada.