Demonstrações interativas da aula de tokenização em modelos de linguagem
Cinco visualizações que acompanham os slides. As quatro primeiras usam o corpus recorrente da aula (low ×5, lower ×2, newest ×6, widest ×3, com o marcador de fim de palavra _): a granularidade caractere vs palavra vs subpalavra, o treinamento do BPE que mescla o par adjacente mais frequente por k merges, a codificação de texto novo aplicando os merges em ordem, e o critério do WordPiece score(x, y) = freq(xy) / (freq(x)·freq(y)) comparado à frequência pura. A quinta usa o corpus da seção de Unigram (low ×10, er ×10, lower ×1) para explorar o lattice de segmentações, o Viterbi e a poda. Notação dos slides: 𝒱 é o vocabulário e |𝒱| seu tamanho.
O tokenizer define o vocabulário, a segmentação e o mapeamento para IDs. Os dois extremos vistos em aula têm problemas opostos: no nível de caractere o vocabulário é mínimo (na prática ≈256 bytes, aqui os 11 caracteres do corpus) e nada fica fora do vocabulário, mas as sequências explodem; no nível de palavra as sequências são curtas, mas o vocabulário cresce sem limite e qualquer palavra nova vira [UNK] (OOV, out-of-vocabulary). A tokenização por subpalavras (aqui, BPE com os 7 merges treinados na seção 2) fica entre os dois: compõe palavras nunca vistas a partir de pedaços aprendidos. Edite o texto e compare os três regimes.
Caractere
Palavra (vocabulário fechado do corpus)
Subpalavra (BPE, k = 7 merges)
Comprimento da sequência (tokens) em cada regime
Tamanho do vocabulário |𝒱| no exemplo (caractere seria ≈256 com bytes; palavra passa de 100K em corpora reais)
💡 O texto de exemplo termina em "lowest", que nunca apareceu no corpus: no nível de palavra vira [UNK], no BPE vira low + est_. Digite "wider" e veja que o BPE com 7 merges o decompõe em caracteres (nenhum merge se aplica), sem perder a palavra. Digite algo com "z" ou "ç" para ver caracteres fora do vocabulário base em vermelho: é o problema que o byte-level BPE do GPT-2 elimina ao partir de 256 bytes.
O Byte Pair Encoding é bottom-up, guloso e baseado em frequência: começa com o vocabulário de caracteres (|𝒱| = 11 neste corpus), conta todos os pares de símbolos adjacentes ponderados pela frequência das palavras, mescla o par mais frequente em um novo símbolo e repete por k merges (hiperparâmetro). Em caso de empate, vale a primeira ocorrência na varredura do corpus, como nos slides: no passo 1, (e, s), (s, t) e (t, _) têm contagem 9 e (e, s) é escolhido. Avance os merges e observe a tabela de pares, a segmentação do corpus e o vocabulário crescendo.
Corpus segmentado (tokens do último merge em amarelo)
Vocabulário 𝒱
Contagem de pares adjacentes (mais frequentes)
Compressão do corpus: total de tokens (ciano) e |𝒱| (roxo) em função de k
💡 Siga os 7 primeiros merges e compare com os slides: es, est, est_, lo, low, ne, new. Em k = 7 o sufixo est_ é compartilhado por "newest" e "widest". Note o trade-off na curva: cada merge reduz os tokens do corpus (de 95 até 16) mas aumenta |𝒱|; em k = 15 cada palavra virou um token único e o BPE degenerou em tokenização por palavra.
Na inferência o BPE é determinístico: divide cada palavra em caracteres (com o _ final) e aplica os merges aprendidos na ordem do treinamento, nunca por frequência no texto novo. É assim que "lowest", que não existe no corpus, vira low + est_ e compartilha subpalavras com "low", "newest" e "widest". Digite um texto, escolha quantos merges usar e clique numa palavra para ver a derivação completa, como no exemplo da aula.
Tokens (clique numa palavra para ver a derivação)
💡 Deixe "lowest" selecionado e arraste k de 0 a 7: os merges 1, 2, 3, 4 e 5 disparam um a um e reconstroem a derivação dos slides (o 6 e o 7, ne e new, não se aplicam). Com k = 0 tudo degenera em caracteres. Teste "widest lowest newest" e veja o mesmo token est_ aparecer três vezes; depois escreva uma palavra em português e observe a fertilidade alta: o vocabulário foi treinado em outro corpus, o mesmo motivo pelo qual idiomas fora do inglês gastam mais tokens no GPT-2.
O WordPiece (usado no BERT) segue a mesma mecânica de merges do BPE, mas escolhe o par que maximiza a likelihood dos dados: score(x, y) = freq(xy) / (freq(x)·freq(y)). Isso favorece pares com alta informação mútua, que aparecem quase exclusivamente juntos. No passo 0 deste corpus, (e, s) tem a maior contagem (9), mas e é comum em outros contextos (freq(e) = 17) e o score cai para 0.0588; já (i, d) aparece só 3 vezes, porém i e d só existem juntos, e o score 0.3333 vence. Avance os merges e compare as duas trajetórias de treinamento.
Scores do WordPiece na segmentação atual (amarelo: escolha do WordPiece; ciano: o que a frequência pura escolheria)
BPE (frequência) após k merges
WordPiece (likelihood) após k merges
Total de tokens no corpus em função de k: BPE (ciano) comprime mais rápido que WordPiece (laranja)
💡 Em k = 1 o WordPiece já criou id (score 0.333) enquanto o BPE criou es (contagem 9): critérios diferentes, vocabulários diferentes. Avance até k = 5 e compare as segmentações de "newest" nos dois painéis. Na curva, o BPE comprime o corpus mais rápido porque ataca sempre o par mais frequente; o WordPiece prefere pares raros e exclusivos. Lembre das outras diferenças vistas em aula: o WordPiece usa o prefixo ## para continuações, decodifica por longest-match e recorre a [UNK] quando não consegue segmentar.
O Unigram LM faz o caminho oposto ao BPE: começa com um vocabulário grande (aqui, os 15 substrings do corpus low ×10, er ×10, lower ×1, com P(t) = count(t)/105) e poda os tokens menos úteis. Segmentar uma palavra é escolher um caminho no lattice: cada aresta é um token, com peso ln P(token), e a melhor segmentação é o caminho de maior soma, encontrado por programação dinâmica (Viterbi). A qualidade do vocabulário é a log-likelihood do corpus, L = Σ freq(w) · ln P(melhor segmentação de w). Clique nos tokens do vocabulário para podá-los e veja o lattice, as segmentações e L reagirem.
Vocabulário 𝒱 (clique para podar ou restaurar; caracteres nunca são podados)
Lattice de "lower": arestas cheias são tokens frequentes, tracejadas são os raros, verde é o melhor caminho (Viterbi)
Viterbi em "lower": δ(j) por prefixo
Melhor segmentação de cada palavra do corpus
💡 Reproduza o slide de poda: remova owe ou lo e note ΔL = 0, nenhum melhor caminho passa por eles. Agora remova low: "low" vira l·ow, "lower" vira o token único lower e L despenca de −49.6 para −72.3. Restaure, clique em "Podar tokens com Δ = 0" (sobram low, er e os caracteres) e ligue a renormalização: L sobe para −42.8, melhor que o vocabulário completo, porque a massa de probabilidade se concentra nos tokens que o corpus realmente usa. É este ciclo, re-estimar, medir Δ, podar, que o algoritmo repete até o vocabulário atingir o tamanho alvo.