Portulino — Modelo de linguagem iniciado do zero usando GPUs T4 gratuitas no Colab
Fabio 11 Jan 2026 colab, transformers, dataset, modelos generativos, e inteligência artificialIntrodução
Já faz algum tempo que me interesso por modelos de linguagem. Mesmo antes do surgimento dos Transformers, eu treinava alguns modelos usando cadeias de Markov e, mais tarde, redes recorrentes. Começar pelas cadeias de Markov foi um processo didático pois fornecia ótimos insights e entendimento da matemática e estatistica envolvida. O caminho natural seria então ir para as RNNs e a tecnologia mais avançada para a época eram as LSTM e as GRU, redes recorrentes mais sofisticadas que as RNNs vanilla. Porém, treinar essas redes era muito ineficiente: demorava demais, exigia grande quantidade de dados e o hardware disponível raramente ajudava.
Naquele período, o treinamento eficiente de representações vetoriais de palavras começava a surgir por meio de trabalhos como (MIKOLOV et al., 2013)1 e (PENNINGTON; SOCHER; MANNING, 2014)2.
Quando Attention Is All You Need (VASWANI et al., 2017)3 foi publicado, o cenário da pesquisa em NLP começou a mudar radicalmente. Alguns anos depois, surgiram o GPT-2 e o GPT-3 (2020), que impulsionaram a corrida por modelos próprios e, consequentemente, por datasets e infraestrutura de grande escala.
Para mim, entretanto, ainda havia muito a aprender. Passei anos sem me aprofundar de fato em Transformers. Naquela época, o interesse em Redes Neurais não era disseminado; quem se aventurava muitas vezes desistia, pois o processo de compreensão era longo e tedioso. Era preciso primeiro dominar o forward — conceito relativamente simples. Já o backward, ou cálculo dos gradientes, era inacessível para muitos, exigindo compreensão de derivadas parciais. Como eu já tinha cursado Cálculo 1 e 2, não tive grandes problemas, mas demorei bastante para organizar meu entendimento. Estudar as derivadas parciais à mão foi útil para compreender a mecânica do backpropagation, que na prática é executado automaticamente por frameworks como PyTorch e TensorFlow.
E foi que então os Transformers transformaram completamente o campo, tornando obsoletas diversas arquiteturas de redes neurais até então consagradas.
No processo de aprender redes neurais o método que mais me ajuda a compreende-las é estudar o código-fonte. É nele que consigo visualizar o fluxo dos dados, a pilha de chamadas e os cálculos envolvidos. De fato, ao tentar ler Attention Is All You Need, é comum ter a sensação de que alguns detalhes não estão completamente explicados.
O código-fonte que encontrei e que serviu como meu ponto de partida para entender e treinar modelos Transformer foi o nanoGPT do KARPATHY4. O nanoGPT é modelo decoder only minimo e didático inspirado no GPT-2, em contraste com o modelo encoder-decoder originalmente proposto por (VASWANI et al., 2017)3. Ele possuía um gerenciamento de dataset bastante rudimentar, que apenas selecionava chunks de textos aleatórios. Não havia suporte a instruct, apenas textos corridos.
Esse código simples me ajudou a compreender e treinar diversos modelos com datasets próprios e neste processo, valiosos insights se reveleram através dos erros e acertos. Ao treinar meu primeiro modelo com ele fiquei impressionado com a rapidez que ele aprende comparado às RNNs. Em poucos meses ele já respondia algumas perguntas e em menos de 5000 iterações o loss caia dos 10.0 para 3.5.
O treinamento não podia ser feito em casa pois minha GPU Nvidia era fraca. Então usava as T4 gratuitas do Colab. Ligava o treinamento praticamente todos os dias, religiosamente, conseguia 100 a 150 iterações por dia. As vezes pagava os R$ 58,00 para usar as A100 ou L4 e dar um salto no aprendizado, mas esses pagamentos eram algo raro.
Portulino
E foi que surgiu meu melhor modelo: O Portulino. Esse nome foi dado porque é um modelo treinado primáriamente em textos em pt-BR. Porém, mesmo pequeno se mostrou altamente eficiente no aprendizado devido aos avanços nos últimos anos que implementei no código original nanoGPT, a essa altura já totalmente reescrito, e a geração de datasets sintéticos de alta qualidade.
Arquitetura e configurações
Similaridades ao GPT-2
- É um modelo decoder somente seguindo o fluxo Embedding -> N x Transformer Blocks -> LM Head
- O block transformer contém Self-Attention + Feed-Forward com conexões residuais
- Autoregressivo
Diferenças e melhorias:
- Tokenizado por Tokenizers
- Usa RoPE ao invés de Learning Embeddings
- GQA (16 Q heads, 4 KV heads)
- RMSNorm
- Pre-norm
- SwiGLU
- Sem bias
- Weight Tying ( neste ponto já não sei se foi uma boa escolha )
Configurações
- Vocabulário personalizado de 32k. Foi escolhido esse valor para comprimir o dataset em 16 bits e economizar no Google Drive permitindo maior quantidade de dados
- 1024 de contexto
- 1024 de dimensão Embedded
- 20 Layers
- 16 Heads
- 4 KV Heads
- Dropout = 0.05
- Learning Rate 0.0003
O fluxo resumido segue o esquema:
Input [B, T]
↓ Embedding
[B, T, 1024]
↓ ×20 Blocks
├─ RMSNorm → GQA+RoPE → residual
└─ RMSNorm → SwiGLU → residual
↓ Final RMSNorm
↓ LM Head (weight tied)
[B, T, 32006] logits
Treinamento
O treinamento em sua grande maioria é feito quase todos os dias na disponibilidade das T4 gratuitas do Google Colab. Em geral consigo 1:20hrs a 1:40hrs de treinamento. Se pular um dia consigo 3:30hrs no máximo. Por dia o rendimento é de aproximadamente 100 a 150 iterações com batch de tamanho 9. As melhorias implementadas liberaram memória para permitir 9 de batch ao invés dos 8 que conseguia no antigo nanoGPT. O tempo de treinamento também diminui levemente, mas varia dependendo da carga do sistema de arquivos do Colab na sessão, em geral fica em 198s a 233s para rodar 5 iterações. O checkpoint é salvo a cada 100 iterações e depois a cada 15 para não perder treinamento quando está perto da sessão se desconectar. Apesar da sua maioria ser nas T4, as vezes eu pagava créditos ao Google para liberar as A100 e L4. Nas A100 eu consigo 70 de batch size e nas L4 17 de batch size. O que me faz pensar que um pagamento de A100 deve cobrir meses ( se não todos ) de T4.
Exemplo de output do treinamento:
GPU RAM TOTAL: 15095 RESERVED: 2 ALLOCATED: 0 FREE: 1
BATCH SIZE: 9 GPU RAM: 15095 SAVE_CKPT 100 TIME_SAVE 100
Resuming training from out
Resumed from iteration 32900 loss 2.1903536787525693
Compiling the model...
Done
GPTConfig(block_size=1024, vocab_size=32006, n_layer=20, n_head=16, n_kv_head=4, n_embd=1024, dropout=0.05, bias=False, tie_word_embeddings=True)
i 32905: l 2.1359, al 2.1849, lr 0.000298, ctime 10.69m, ne 61.02m 1.02h time 194.75s
i 32910: l 2.7124, al 2.2377, lr 0.000298, ctime 13.94m, ne 57.78m 0.96h time 194.77s
O otimizador utilizado é o AdamW. Comecei a implementar o Muon, mas ainda não coloquei para rodar, provavelmente será para um próximo modelo.
Dataset
O grande aprendizado nesse processo não era mais o modelo. Os modelos atuais já são ótimos para aprender e são eficientes, o que importa agora é aprender bem e para isso o dataset deveria ser a real preocupação. O modelo anterior era treinado com um dump da Wikipédia em pt-BR e alguns dumps de livros desses torrentes de ebooks. Os instructs eram praticamente todos em inglês com excessão do Cabrita e Alpaca que surgiram depois e tinham instructs em pt-BR. Porém eram todos datasets sujos, com muito caracteres aleatórios, textos mal formatados, mal codificados e instructs defeituosos. Era hora de arrumar a casa e melhorar esses datasets e conseguir mais. O Portulino foi treinado primariamente com os seguintes dataset:
Textos corridos
- Wikipédia pt-BR de 2015
- Aproximadamente 10Gb de scrapys de blogspots e wordpress limpos em pt-BR que eu mesmo fiz
- Apostilas diversas de concurseiros achadas na internet em especial nesses google drives
- Scrapys de outros sites
- Livros
- PDFs de manuais e trabalhos academicos
- Diversos datasets encontrados de pares EN-PT para tradução
- No final descobri o CulturaX que tinha melhor qualidade que meus scrapys, porém já era tarde e foram treinados no máximo 10k epochs.
Instructs
- Alpaca e Cabrita limpos
- gsm8k
- OpenAssistent
- Aproximadamente 100k de instructs que eu mesmo fiz automizados utilizando api da OpenAI e Gemini
- Aproximadamente 5k de instructs think que eu mesmo fiz automizados utilizando api do Gemini
- Obviamente alguns feitos manualmente ou batchs nas IAs web
- Tem um enorme de 400k thinking em inglês mas eu não anotei e já foi concatenado no dataset
- 5K perguntas e respostas sobre as principais legislações brasileiras feitos automizados utilizando a api Gemini
- Reddit filtrado por score
Eu tinha esperança que os datasets em ingles conseguissem ajudar o modelo a responder o que não sabia em pt-BR através da tradução interna, mas em nenhum momento eu percebi isso. Provavelmente se eu tivesse deixado treinar mais tempo essa capacidade surgiria. O dataset de think foi uma surpresa pois deu um salto no raciocino do modelo.
Vocabulário
Como treino redes a algum tempo tenho textos de mais de uma década. Isso refletiu inclusive no vocabulário personalizado treinado pelo Tokenizers. O treinamento ocorreu por volta de 2015 época do Impeachment da Dilma, logo Dilma ficou eternizado no vocabulário e hoje não se fala mais nela.
Antes tinha treinado um vocabulário de 50K mas os datasets ficam grandes pois necessitava usar 32 bits para representar um token, cogitei usar 24 bits ou até mesmo 17 bits, mas desconfiei que a manipulação de bits iria mais atrapalhar que ajudar. Dessa forma um novo vocabulário de 32K que cabe perfeitamente em 16 bits signed foi treinado. A maioria dos textos usados foram em pt-BR, porém o vocabulário tem alguns tokens em inglês.
Tokens especiais adicionados no vocabulário:
| <|query|> | Usado parar marcar o início de um instruct, query, solicitação do usuário |
| <|answer|> | Usado em par com o <|query|> para marcar o fim do instruct e início da geração da resposta pelo modelo |
| <|endoftext|> | Fim de texto |
| <|hole|> | O thinking apareceu depois que treinei o vocabulário, então usei esse token não usado como marcação para gerar o Thinking. |
| <|code|> | Pensei em usar para algo programático mas não foi para frente. |
Entre outros tokens especiais não usados mas contidos no vocabulário.
Performance
Logo nos primeiros momentos do treinamento já percebi uma melhoria considerável com essas inovações e os novos datasets limpos. Em um mês treinando o loss já caiu para 3.6 e textos inteligiveis começaram a aparecer. A inferência, em casa, era feita por CPU e quando ativei o cache KV ela ficou extremamente rápida. Eu não implementei métrica ou validação alguma. Isso gastaria meus preciosos tempos de T4. Os testes eram eu mesmo digitando coisas ao longo do tempo. Quando eu implementei o thinking fiquei impressionado com a capacidade de detectar o intent do usuário, porém a resposta final nunca foi satisfatória no thinking. Provavelmente porque ele só foi implementado quando o modelo já estava na metade do treinamento.
Obviamente é um modelo pequeno e treinado por uma pessoa só, ele tem problemas clássicos reconhecidos em IA. A inferência é pobre. Seu fraco, como é até hoje nos grandes modelos, é a ontologia e senso-comum. Ontologia e senso-comum não costuma estar escrito e se estão são em textos especificos. Um modelo grande infere e consegue responder a esses questionamentos mas um modelo pequeno e com dataset pequeno não conseguirá performar bem nessa área. Para corrigir isso o uso de RAG é encorajado, aliás é percepível que as grandes empresas de IAs estão reforçando mais o uso de RAG ao invés de confiar no conhecimento dos pesos. E faz todo sentido, melhor implementar um ótimo modelo de racíocinio, moderação e detecção de intent e deixar conhecimentos efêmeros e especificos a parte.
Anatomia Humana. Isso ficou evidente no Portulino na anatomia sexual humana. Sim, isso mesmo, ele tem sérios problemas com quem tem pênis e vagina. Eu fiquei muito tempo tentando corrigir isso o que acabou fazendo o modelo ficar sexual! Foi ai que resolvi parar de treiná-lo pois percebi que fiz uma bagunça.
Legislações. Um conhecido advogado inserido na área de IA e advocacia me sugeriu treinar meu modelo para o ramo juridico, então fiz turn-over e fiz 5k de instructs de legislação bem como reforcei textos juridicos que são até fáceis de se achar. Os resultados não foram bons. O modelo acabou confundindo intents com solicitações juridicas e confundindo as legislações. Mas ainda sim achei impressionante ele ter aprendido em tão pouco tempo a citar leis e principios.
Safe. Durante o processo de treinamento, paralelamente eu pesquisava e fazia novos datasets. Foi então que o momento de combater respostas não seguras começaram a me preocupar e interessar, devido ao conteúdo reddit e alguns blogs o modelo fornecia algumas respostas não seguras. Lembre-se que eu sou somente uma pessoa e esse modelo não é para produção, é um modelo toy, para aprendizado, então eu não perdi tempo filtrando o dataset. Tentando remediar isso eu fiz alguns datasets com respostas seguras para self-harming, crimes e outras ilegalidades. O Grok me ajudou muito porque é o único modelo que se você pedir coisas erradas ele não se recusa a fazer. Porém eu parei de fazer esses datasets quando cheguei nos canibais. Eu pedi para o Grok fazer frases que essas pessoas falariam e … eu só consegui ler algumas linhas … que horror. Não tinha estomago para ficar brincando de entender pessoas malucas. Lembre-se, que eu teria que pensar como elas … deixei isso para lá. Porém como muitos datasets instructs achados por ai tem conteúdo safe o modelo acabou pegando o viés OpenAI e Google e assim adquiriu um pouco de segurança no self-harming.
Matemática. Esqueça. Ele nem sabe quanto é 2+2. E olha que insisti nisso. Fiz 50mB de números, equações em simbolos e literais. Pelo menos ele aprendeu a contar! O anterior nem isso sabia!
Programação. Tinha no começo mas tirei. Ele nunca aprenderia a programar eficientemente, e eu estaria desperdiçando capacidade de modelo (pesos) em algo que provavelmente não daria certo.
Humanas. Ele performa bem em humanas. Tanto porque no Brasil há intenso trabalho de humanas disponível, quanto blogs de esquerda e religiosos.
Conversação. Portulino não é voltado a diálogos. Os datasets de diálogos nunca foram empilhados para ensiná-lo a continuar o diálogo.
Próximas realizações
O Portulino apesar de suas deficiências se mostrou um monstro no aprendizado. Como os datasets foram sendo limpos e melhorados ao longo do seu treinamento, ele eternizou alguns defeitos que não são mais corrigíveis; somando-se a essas deficiências, o vocabulário está datado.
Então é hora de treinar outro! Enquanto houver T4 gratuitas ficarei treinando esses modelos.
O próximo terá melhores datasets, melhores textos corridos, novas tecnologias como o otimizador Muon, um novo vocabulário com novos tokens especiais voltados para RAG, Thinking, System e outros.
Também é hora de implementar a RMT (Recurrent Memory Transformer) para aumentar o contexto e permitir finalmente conversação. De fato já estou criando e arrumando datasets dialógicos.
Link para download
Se desejar rodar o modelo ele está disponível no huggingface no link https://huggingface.co/fabiovilao/portulino
-
Word2Vec – Mikolov et al. (2013) MIKOLOV, Tomas et al. Efficient Estimation of Word Representations in Vector Space. arXiv:1301.3781, 2013. Disponível em: https://arxiv.org/abs/1301.3781 ↩
-
PENNINGTON, Jeffrey; SOCHER, Richard; MANNING, Christopher D. GloVe: Global Vectors for Word Representation. 2014. Disponível em: https://nlp.stanford.edu/pubs/glove.pdf
CARLSON, Riley; BAUER, John; MANNING, Christopher D. A New Pair of GloVes. 2025. Disponível em: https://arxiv.org/abs/2507.18103 ↩
-
VASWANI, Ashish; SHAZEER, Noam; PARMAR, Niki; USZKOREIT, Jakob; JONES, Llion; GOMEZ, Aidan N.; KAISER, Łukasz; POLOSUHKIN, Illia. Attention Is All You Need. arXiv preprint arXiv:1706.03762, 2017. Disponível em: https://arxiv.org/abs/1706.03762 ↩ ↩2
-
KARPATHY, Andrej. nanoGPT: The simplest, fastest repository for training/finetuning medium-sized GPTs. GitHub, 2025. Disponível em: https://github.com/karpathy/nanoGPT ↩