Como um texto vira uma lista de números — e por que o modelo não enxerga letras nem palavras, mas pedaços.
Uma rede neural é, no fundo, uma máquina de multiplicar matrizes. Matrizes são feitas de números. Logo, a primeira coisa que precisa acontecer com o seu texto é uma tradução: letras viram números. Essa etapa se chama tokenização, e ela acontece antes do modelo — não é aprendida junto com ele, é uma tabela fixa, decidida uma vez e congelada para sempre.
A palavra token é mais antiga que os modelos de linguagem, e tem outros sentidos na TI: a credencial que um servidor emite depois do login, como um JWT; a unidade que o analisador léxico de um compilador separa, como uma palavra-chave; o ativo registrado num contrato de blockchain. Neste curso, token é sempre um pedaço de texto — uma palavra, parte de palavra ou byte — com um número inteiro associado. O glossário do curso faz essa distinção para todos os termos que têm mais de um sentido.
A pergunta é: números por quê? Existem três respostas históricas, e entender por que as duas primeiras falharam é a única forma de entender por que a terceira ganhou.
Simples: monte um dicionário. "casa"→41, "cachorro"→892, e assim por diante. Funciona até o dia em que aparece uma palavra que não está no dicionário — um nome próprio, um erro de digitação, uma gíria nova, uma palavra em outro idioma. Aí o modelo escreve <UNK> ("desconhecido") e perde a informação para sempre. E o dicionário do inglês real, com nomes e variações, passa de um milhão de entradas — o que faria a última camada do modelo (que precisa ter um neurônio por palavra possível) sozinha ser maior que o modelo inteiro.
Resolve o desconhecido: são só ~100 caracteres, e qualquer texto cabe. Mas cria um problema pior. A palavra "internacionalização" vira 19 posições em vez de 1. Como o custo da atenção cresce com o quadrado do número de posições (você vai ver isso na apostila 03), textos ficam muito mais caros. E o modelo precisa gastar várias camadas só para redescobrir que c-a-s-a junto significa alguma coisa.
Aqui está a sacada. E se o vocabulário fosse feito de pedaços de palavra, escolhidos automaticamente pela estatística do texto? Palavras comuns ("the", "casa") ganham um token só. Palavras raras se decompõem em pedaços que existem ("Rafhael" → R + af + ha + el). Nada nunca é desconhecido, e o texto continua curto.
Esse é o BPE — Byte Pair Encoding. É o que o GPT-2 usa, é o que o RoBERTa usa, e variantes dele seguem em uso em muitos modelos atuais.
Pense numa loja de peças de LEGO que precisa vender o menor número possível de tipos de peça, mas ainda permitir montar qualquer coisa. Ela não vai vender só tijolinhos 1×1 (você montaria tudo, mas gastaria 10 mil peças por modelo). Também não vai vender uma peça única "Millennium Falcon montado" (só serve para um modelo). Ela vende peças médias que aparecem muito: paredes, janelas, rodas. O BPE é o algoritmo que descobre, olhando milhares de modelos já montados, quais peças médias valem a pena existir.
O BPE é um algoritmo de treino do tokenizador. Ele roda uma vez, sobre um corpus gigante, e produz duas coisas: uma lista de tokens (vocab.json) e uma lista ordenada de fusões (merges.txt). Depois disso, ele nunca mais muda.
t aparece seguido de h? E h seguido de e?t+h é o campeão, cria-se o token th, e todo t h do corpus vira th.th+e pode virar o campeão, criando the.Corpus: a palavra banana repetida 5 vezes, e bandana 2 vezes. Vamos acompanhar três fusões.
| Passo | Estado do texto | Par mais frequente | Contagem | Novo token |
|---|---|---|---|---|
| 0 | b a n a n a ×5 b a n d a n a ×2 | — | — | alfabeto: a,b,d,n |
| 1 | conta pares: an=10+4=14, na=10+2=12, ba=7… | a+n | 14 | an |
| 2 | b an an a ×5 b an d an a ×2 | an+a | 7 | ana |
| 3 | b an ana ×5 b an d ana ×2 | b+an | 7 | ban |
| fim | ban ana ×5 ban d ana ×2 | "banana" custa 2 tokens; "bandana" custa 3. | ||
O vocabulário do modelo é um retrato estatístico do corpus de treino. O GPT-2 foi treinado no WebText — 8 milhões de páginas de internet em inglês, coletadas de links votados no Reddit. Por isso o inglês é eficiente (1 token ≈ 0,75 palavra) e o português é caro (1 token ≈ 0,4 palavra): o BPE nunca teve motivo estatístico para criar o token ção. Você vai medir isso no laboratório, e vai doer um pouco.
Aqui está a contribuição específica do GPT-2, e é elegante. O BPE original (usado em tradução automática, 2016) partia de caracteres Unicode. Problema: existem mais de 149 mil caracteres Unicode. Se o alfabeto inicial já é enorme, sobra pouco espaço no vocabulário para as fusões úteis — e emojis ou ideogramas chineses ainda podiam cair fora e virar <UNK>.
A solução do GPT-2: não comece de caracteres, comece de bytes. Todo texto digital, em qualquer idioma, é uma sequência de bytes UTF-8. E bytes só existem em 256 valores possíveis. Então:
<UNK>. Não precisa.Quando você inspecionar os tokens do GPT-2, vai ver coisas como Ġthe e ĠcasaĊ. Parece estranho, mas é uma convenção simples.
Alguns bytes são caracteres de controle ou espaço em branco — imprimir eles direto quebraria os arquivos de vocabulário. Então o GPT-2 remapeia esses bytes problemáticos para caracteres Unicode visíveis e inofensivos:
| Byte real | Valor | Vira | Significa |
|---|---|---|---|
| espaço | 32 (0x20) | Ġ | "aqui começou uma palavra nova" |
| quebra de linha | 10 (0x0A) | Ċ | fim de linha |
| tabulação | 9 (0x09) | ĉ | tab |
É puramente cosmético — o Ġ é literalmente o caractere espaço com um chapéu. Mas ele carrega informação real: o espaço faz parte do token. Ġcasa (" casa", precedida de espaço) e casa (colada na palavra anterior, como em "minhacasa") são tokens diferentes, com IDs diferentes e embeddings diferentes.
Se você escrever um prompt terminando em espaço — "O Brasil é " — o modelo já recebeu o Ġ sozinho e agora precisa completar um token que começa sem espaço. Isso o joga para uma região estranha do espaço de tokens e a qualidade cai visivelmente. Nunca termine um prompt com espaço. Escreva "O Brasil é" e deixe o modelo produzir o Ġ ele mesmo.
O treino do BPE é uma sequência de escolhas gulosas. Em cada passo
Esse par é fundido num token novo merges.txt na posição
Tokenizar um texto novo não refaz contagens. Ele apenas replica as fusões, na mesma ordem em que foram aprendidas:
merges.txt é um arquivo ordenado, não um conjunto.
E o vocabulário final do GPT-2 se decompõe assim:
<|endoftext|> (ID 50256), usado para separar documentos no treino e sinalizar fim de geração.
Este é um ponto que confunde muita gente: RoBERTa usa exatamente o mesmo algoritmo do GPT-2 — byte-level BPE, 50 000 fusões. Foi uma decisão explícita dos autores do RoBERTa, que trocaram o WordPiece do BERT pelo BPE do GPT-2. Mas os tokens especiais e o corpus são diferentes, então os IDs não batem.
GPT-1 (openai-gpt) | GPT-2 (toda a família) | RoBERTa (detectores) | |
|---|---|---|---|
| Algoritmo | BPE sobre caracteres + spaCy/ftfy | BPE sobre bytes | BPE sobre bytes |
| Vocabulário | 40 478 | 50 257 | 50 265 |
| Token desconhecido | <unk> existe | não existe | não existe |
| Caixa alta | tudo minúsculo (lowercased) | preservada | preservada |
| Tokens especiais | <unk> | <|endoftext|> | <s> </s> <pad> <unk> <mask> |
| Marca de espaço | </w> no fim da palavra | Ġ no início | Ġ no início |
| Envolve a frase | não | não | <s> … </s> |
Os 8 tokens a mais do RoBERTa (50 265 vs 50 257) são justamente os especiais que ele precisa e o GPT-2 não: <pad> para completar lotes de tamanhos diferentes, <mask> para o objetivo de treino dele (apostila 10), e <s>/</s> para marcar início e fim — e o <s> vira, mais tarde, o vetor que carrega o resumo da frase inteira para a classificação (apostila 12).
# tokenizar_na_mao.py — o BPE do GPT-2 sem mágica from transformers import AutoTokenizer tok = AutoTokenizer.from_pretrained("openai-community/gpt2") texto = "O Brasil é gigante pela própria natureza." # 1) texto -> lista de IDs (o que o modelo realmente recebe) ids = tok.encode(texto) print(ids) # [46, 8290, 2634, 20692, 12427, 27638, 355, 1050, 16864, 3450, 46844, 13] # 2) cada ID de volta ao pedaço de texto que ele representa for i in ids: print(f"{i:>6} {tok.decode([i])!r:<12} {tok.convert_ids_to_tokens([i])[0]}") # o 3o campo mostra a forma crua, com o Ġ do espaço # 3) a métrica que interessa: quantos tokens por palavra? palavras = len(texto.split()) print(f"{len(ids)} tokens para {palavras} palavras = {len(ids)/palavras:.2f} tokens/palavra")
A saída da linha 3 é o momento em que a ficha cai. Em inglês, esse número fica perto de 1,3. Em português, perto de 2,4. Ou seja: o mesmo texto custa quase o dobro de tokens em português — dobro de memória, dobro de tempo, metade do contexto útil, e (em APIs pagas) o dobro do preço. Não é o modelo que "não gosta" de português; é o merges.txt que nunca viu português suficiente para criar peças boas.
cd GPT-2 .venv\Scripts\python.exe lab\01_tokenizer.py
Esse script faz quatro coisas. Observe cada uma:
Ġ. Procure onde as palavras se quebram — quase sempre no sufixo." 2024" custa 1 token (era um ano frequente na internet de 2018), mas " 1234567" vira 3 tokens, quebrados em 123+45+67 — pedaços sem nenhum significado aritmético. O modelo nunca enxerga o número inteiro. É literalmente por isso que modelos dessa geração erravam contas simples: eles não fazem aritmética sobre números, e sim sobre fragmentos arbitrários de dígitos escolhidos por frequência de texto.Por que "Rafhael" custa mais tokens que "the"; por que Ġcasa e casa são coisas diferentes; e por que um modelo que enxerga 1+000 em vez de 1000 tem dificuldade estrutural com contas.
"antidisestablishmentarianism" (28 letras, inglês)? E para "pneumoultramicroscopicossilicovulcanoconiótico" (46 letras, português)? Escreva os dois palpites, depois rode. A diferença vai ser maior do que você espera."casa", " casa" e "Casa". Quantos IDs distintos aparecem? Por quê?<|endoftext|>. Rode tok.decode([50256]). Agora explique por que ele é o último ID, e não o primeiro.As fontes primárias dos conceitos deste capítulo. Os números do texto foram medidos no laboratório; as ideias vêm daqui.
Os termos técnicos têm aqui o sentido de aprendizado de máquina. O glossário do curso mostra esse sentido ao lado dos outros que as mesmas palavras têm na TI.
Agora o texto é uma lista de inteiros. Mas um inteiro não tem significado — o ID 41 não é "mais" nem "menos" que o 40.
Na apostila 02 esses inteiros viram vetores, e é aí que o significado nasce.