SLM-100M Português (LLaMA-style)
Small Language Model de ~100M parâmetros pré-treinado em ~2B tokens de português do FineWeb2 (subset PT). Projeto da disciplina Aprendizado Profundo II (PUCRS — Escola Politécnica).
Diferente do modelo anterior em inglês, este foi projetado especificamente para português: tokenizer BPE byte-level treinado em corpus PT, arquitetura ampliada (12 camadas, n_embd=768) e regularização adicional (z-loss).
Resultados
| Métrica | Valor |
|---|---|
| Val loss | 3.183 |
| Val perplexity | 24.12 |
| Best val_loss (durante treino) | 3.112 (ppl 22.48) |
| Train loss final | 3.143 |
| Tokens vistos | ~1.97B |
| Tempo de treino | ~10.5h em RTX 5060 Ti |
| Throughput médio | ~58K tokens/s |
Curvas completas no Weights & Biases.
Arquitetura
Decoder-only transformer (estilo LLaMA), com algumas mudanças em relação ao modelo anterior em inglês para escala maior e melhor estabilidade:
| Componente | Escolha |
|---|---|
| Camadas (n_layer) | 12 |
| Dimensão (n_embd) | 768 |
| Heads de Query | 12 |
| Heads de Key/Value | 4 (GQA, ratio 3:1) |
| Dim do FFN (SwiGLU) | 2048 (≈8/3 × n_embd) |
| Context length | 1024 |
| Vocabulário | 32.000 |
| Positional Encoding | RoPE (θ=10000) |
| Normalização | RMSNorm (pre-norm, ε=1e-5) |
| Ativação | SwiGLU |
| Embeddings | Compartilhadas com LM head (tied) |
| Z-loss | coeficiente 1e-4 (estabilidade do softmax final) |
Total de parâmetros: 100.092.672 (~100M)
Diferenças vs modelo inglês de 55M
| Aspecto | Modelo EN (55M) | Modelo PT (100M) |
|---|---|---|
| Camadas | 10 | 12 |
| n_embd | 512 | 768 |
| n_head / n_kv_head | 8 / 4 | 12 / 4 |
| Vocab | 50.304 (GPT-2 BPE) | 32.000 (BPE byte-level treinado em PT) |
| Z-loss | não usado | coef 1e-4 |
Dataset
FineWeb2 — subset português (HuggingFaceFW/fineweb-2)
Corpus web em português filtrado por qualidade, parte do pipeline FineWeb da HuggingFace. Usado em streaming até atingir ~2B tokens, divididos em:
- Train: 2.100.000.085 tokens (~4.2 GB em uint16)
- Val: 2.067.198 tokens (~4 MB)
Tokenizer
BPE byte-level treinado do zero no corpus FineWeb2-pt, com vocabulário de 32.000 tokens. Diferente do tokenizer GPT-2 usado no modelo anterior (que foi treinado em inglês e tinha eficiência ~2-3x pior em português), este tokenizer é otimizado para a distribuição de caracteres e padrões morfológicos do português brasileiro.
Comparativo de eficiência (tokens necessários para frase exemplo):
| Frase | GPT-2 BPE (50k) | PT BPE (32k) |
|---|---|---|
| "O direito tributário brasileiro é complexo." | 14 tokens | ~10 tokens |
Configuração de Treino
| Hiperparâmetro | Valor |
|---|---|
| Otimizador | AdamW (β₁=0.9, β₂=0.95) |
| Weight decay | 0.1 (seletivo: apenas em params 2D+) |
| LR pico → mínimo | 6e-4 → 6e-5 |
| Schedule | Warmup linear 750 steps + cosine decay |
| Gradient clipping | 1.0 |
| Batch size (por step) | 8 sequências |
| Gradient accumulation | 16 |
| Batch efetivo | 128 sequências = 131.072 tokens/step |
| Max steps | 15.000 |
| Mixed precision | bfloat16 (autocast) |
| Compilação | torch.compile ativado |
Como Usar
Este checkpoint requer o código do modelo do repositório: https://github.com/cjfbr/slm-pretraining
O tokenizer está incluído neste repositório HF (tokenizer.json), formato
tokenizers da HuggingFace.
import torch
from tokenizers import Tokenizer
from huggingface_hub import hf_hub_download
from slm.model import GPT
from slm.config import ModelConfig
REPO = "cjfb75/slm-100m-pt-llama-style"
# Baixar tokenizer e checkpoint
tokenizer_path = hf_hub_download(REPO, "tokenizer.json")
ckpt_path = hf_hub_download(REPO, "pretrain_final.pt")
# Tokenizer
tokenizer = Tokenizer.from_file(tokenizer_path)
# Modelo
cfg = ModelConfig(
vocab_size=32000,
n_layer=12,
n_embd=768,
n_head=12,
n_kv_head=4,
ffn_hidden_dim=2048,
block_size=1024,
)
model = GPT(cfg)
ckpt = torch.load(ckpt_path, weights_only=False, map_location="cuda")
model.load_state_dict(ckpt["model"])
model.eval().cuda()
# Geração
prompt = "A capital do Brasil é"
ids = tokenizer.encode(prompt).ids
input_ids = torch.tensor([ids], dtype=torch.long, device="cuda")
with torch.no_grad():
out = model.generate(input_ids, max_new_tokens=100, temperature=0.8, top_k=50)
print(tokenizer.decode(out[0].cpu().tolist()))
Limitações Conhecidas
- Modelo pequeno (~100M): capacidade limitada de raciocínio complexo ou conhecimento factual amplo
- Pré-treino apenas: não passou por SFT ou RLHF, então pode alucinar fatos e não segue instruções consistentemente
- Distribuição do FineWeb2: viés para texto web em geral; subdomínios como código fonte ou texto jurídico/médico podem ter qualidade reduzida
- Repetição: comportamento típico de modelos pequenos pode ocorrer em
geração longa; mitigado parcialmente por
top_ketemperatureadequados
Referências de Comparação
| Modelo | Params | Tokens | Val PPL em PT |
|---|---|---|---|
| Este modelo | 100M | 1.97B | 24.1 |
| Modelo prévio (EN→PT cross-lingual) | 55M | 524M EN | (incoerente, ver análise) |
| Sabiá-7B (referência grande) | 7B | 1.4T | ~15 |
Equipe
- Cristiano Ferrazzo (PUCRS — Escola Politécnica)
Licença
MIT. Use livre para fins acadêmicos e comerciais.
Citação
Se usar este modelo em trabalho acadêmico:
@misc{slm100m_pt_2026,
author = {Ferrazzo, Cristiano},
title = {SLM-100M Portuguese: Small Language Model for Brazilian Portuguese},
year = {2026},
publisher = {HuggingFace},
url = {https://huggingface.co/cjfb75/slm-100m-pt-llama-style}
}