Mergulho Profundo em Tokenização

Visão geral: por que a tokenização merece um “mergulho profundo”

Sistemas modernos de PLN raramente operam diretamente sobre texto bruto. Eles operam sobre IDs de tokens—inteiros que indexam um vocabulário—porque redes neurais precisam de símbolos discretos e fixos que possam ser embutidos em vetores. Tokenização (tokenization) é a etapa do pipeline que converte texto em uma sequência de tokens (frequentemente unidades de subpalavra), além do mapeamento reverso de volta para texto.

As escolhas de tokenização afetam:

  • Eficiência do modelo: os tokens determinam o comprimento da sequência, o que impacta fortemente o custo computacional em uma Arquitetura Transformer (o custo da atenção cresce aproximadamente com o quadrado do comprimento da sequência).
  • Qualidade e equidade: idiomas e sistemas de escrita podem receber “orçamentos de tokens” muito diferentes para o mesmo conteúdo.
  • Robustez: tratamento de erros de digitação, palavras raras, código, emoji ou scripts mistos.
  • Comportamento downstream: geração, recuperação, classificação e similaridade de embeddings são todos mediados por tokens (ver Representação de Texto e Modelos de Embedding).

Este artigo foca em duas famílias amplamente usadas de tokenização por subpalavras—BPE e Unigram—e nas questões multilíngues e trade-offs de projeto que importam na prática.

O que é um “token” no PLN moderno?

Um token é uma unidade produzida por um tokenizador. Dependendo do design, tokens podem corresponder a:

  • Palavras ("cat", "running")
  • Caracteres ("c", "a", "t")
  • Subpalavras ("run", "##ning" ou "▁run", "ning")
  • Bytes (modelos em nível de byte podem representar qualquer texto Unicode sem um token “desconhecido”)

A maioria dos LLMs modernos usa tokenização por subpalavras (subword tokenization): strings frequentes tornam-se tokens únicos; strings raras são decompostas em partes menores. Isso equilibra:

  • Tamanho do vocabulário (memória e custo do softmax)
  • Comprimento da sequência (computação e uso de contexto)
  • Cobertura (capacidade de representar texto arbitrário)

Duas métricas centrais são úteis ao pensar sobre tokenizadores:

  • Compressão / comprimento médio de token: quantos caracteres por token em média.
  • Fertilidade (fertility): em quantos tokens uma “palavra” se expande (importante para idiomas com palavras longas ou sem fronteiras por espaços em branco).

Requisitos desejáveis: o que torna um tokenizador “bom”?

Não existe um tokenizador universalmente melhor; você equilibra objetivos concorrentes:

  1. Cobertura: representar qualquer texto de entrada de forma confiável (evitar [UNK] quando possível).
  2. Eficiência: manter sequências curtas para reduzir computação e aumentar o contexto efetivo.
  3. Aderência estatística: alinhar tokens com padrões frequentes na distribuição dos dados de treino.
  4. Equilíbrio multilíngue: evitar penalizar certos scripts/idiomas com tokenizações excessivamente longas.
  5. Estabilidade: pequenas perturbações no texto (erros de digitação, caixa alta/baixa) não deveriam explodir a contagem de tokens.
  6. Reversibilidade: a decodificação deve reproduzir a string original (ou uma forma normalizada bem definida).

BPE e Unigram visam principalmente (2)–(3), mas considerações multilíngues (4) e robustez (1,5) muitas vezes decidem qual é melhor em um sistema.

Famílias de tokenização por subpalavras na prática

BPE (Codificação por Pares de Bytes (Byte Pair Encoding)): tokenização baseada em mesclas

A tokenização por Codificação por Pares de Bytes (Byte Pair Encoding) (como usada em muitos modelos no estilo GPT) aprende um vocabulário mesclando repetidamente os pares adjacentes de símbolos mais frequentes.

Intuição de treinamento

Comece com um vocabulário base de “símbolos”. Dependendo da implementação, eles podem ser:

  • Caracteres (codepoints Unicode)
  • Bytes (0–255) para BPE em nível de byte

Então:

  1. Conte todos os pares adjacentes de símbolos no corpus de treinamento.
  2. Mescle o par mais frequente em um novo símbolo.
  3. Repita até atingir o tamanho de vocabulário alvo.

Isso cria tokens que correspondem a substrings comuns (por exemplo, "ing", "tion", "http", "://").

Um exemplo de brinquedo (conceitual)

Suponha que nosso corpus seja:

  • low
  • lower
  • lowest

Comece a partir de caracteres:

  • l o w
  • l o w e r
  • l o w e s t

Pares frequentes incluem (l,o), (o,w), etc. Se (o,w) for o mais frequente, mescle-o em ow:

  • l ow
  • l ow e r
  • l ow e s t

Depois, talvez mescle (l,ow)low:

  • low
  • low e r
  • low e s t

Eventualmente, você pode aprender tokens como low, est, er.

Codificação com BPE

Em tempo de inferência, a codificação normalmente aplica mesclas de forma gulosa de acordo com os rankings de mescla aprendidos. Em muitas implementações, você:

  • Começa a partir dos símbolos base (caracteres/bytes)
  • Aplica mesclas em ordem de ranking para produzir a tokenização final

BPE é determinístico, dadas as regras de mescla aprendidas.

BPE em nível de byte (importante para robustez)

Uma melhoria prática importante é o BPE em nível de byte (byte-level BPE):

  • Normalize o texto para uma sequência de bytes (por exemplo, bytes UTF‑8)
  • Aprenda mesclas sobre bytes

Benefícios:

  • Não precisa de [UNK]: qualquer string Unicode é representável.
  • Robustez a caracteres incomuns, emoji ou texto corrompido.

Trade-off:

  • Alguns idiomas/scripts podem tokenizar com menor eficiência a menos que o vocabulário seja grande o suficiente e os dados de treinamento os cubram.

Unigram (Modelo de Linguagem Unigrama (Unigram Language Model)): segmentação probabilística

A tokenização Unigram (unigram tokenization) (popularizada pelo SentencePiece) adota uma abordagem diferente: em vez de construir tokens por mesclas, ela aprende uma distribuição de probabilidade sobre um conjunto fixo de subpalavras candidatas e escolhe a segmentação com a maior verossimilhança.

Intuição de treinamento

  1. Comece com um grande conjunto de tokens candidatos (frequentemente derivados de substrings frequentes).
  2. Ajuste um modelo unigrama: cada token ( t ) tem probabilidade ( p(t) ).
  3. Iterativamente pode (prune) tokens que menos contribuem para explicar os dados de treinamento, até que o vocabulário alcance o tamanho desejado.

Isso costuma ser treinado com um procedimento do tipo EM (Expectation-Maximization):

  • E-step: computar segmentações prováveis do texto usando as probabilidades atuais dos tokens
  • M-step: atualizar as probabilidades dos tokens para maximizar a verossimilhança

Codificação: melhor segmentação (Viterbi)

Dado um vocabulário fixo com probabilidades, a codificação vira um problema de programação dinâmica (dynamic programming): encontrar a sequência de tokens com máxima probabilidade (ou mínimo log-verossimilhança negativa).

Isso pode ser mais flexível do que BPE: Unigram pode “escolher” entre múltiplas segmentações possíveis, em vez de seguir um único caminho guloso de mesclas.

Por que Unigram é atraente

  • Pode produzir segmentações melhores em alguns cenários multilíngues.
  • Pode evitar certas mesclas patológicas que o BPE pode fazer cedo e nunca desfazer.
  • É frequentemente usado com o tratamento de espaços em branco do SentencePiece (ver abaixo), o que ajuda em idiomas cujo comportamento de espaços em branco difere.

Onde o WordPiece se encaixa (contexto breve)

Muitos modelos da família BERT usam WordPiece, que é similar em espírito ao BPE, mas treinado com um objetivo baseado em verossimilhança e normalmente usa um marcador de continuação como ##. Na prática, as diferenças entre BPE/WordPiece/Unigram muitas vezes importam menos do que:

  • escolhas de normalização,
  • base em bytes vs símbolos Unicode,
  • tamanho do vocabulário,
  • equilíbrio de dados multilíngues.

A metade “oculta” da tokenização: normalização e pré-tokenização

Tokenização não é apenas o algoritmo de subpalavras; também inclui:

  • Normalização Unicode (NFC/NFKC)
  • Tratamento de espaços em branco
  • Conversão para minúsculas ou preservação de caixa
  • Separação de pontuação
  • Tratamento de caracteres de controle

Espaços em branco como símbolo de primeira classe

Muitos tokenizadores tratam espaços em branco explicitamente porque eles carregam informação:

  • Separam palavras em muitos idiomas.
  • Afetam a formatação da geração.
  • Importam em código e texto estruturado.

O SentencePiece comumente substitui espaços em branco por um marcador visível como para que tokens possam codificar a informação de “fronteira de palavra” sem uma etapa separada de pré-tokenização.

Exemplo (conceitual):

  • "New York""▁New▁York" → tokens podem ser ["▁New", "▁York"]

Armadilhas da normalização Unicode

Unicode permite múltiplas formas de representar “o mesmo” glifo:

  • é pode ser um único codepoint (U+00E9) ou e + acento combinante.

Se treinamento e inferência normalizam de forma diferente, você pode obter mudanças surpreendentes na tokenização. Para corpora multilíngues, isso pode ser uma grande fonte de instabilidade.

Orientação prática:

  • Escolha uma forma de normalização (comumente NFC ou NFKC) e aplique-a de modo consistente.
  • Tenha cautela com normalização agressiva que apaga distinções importantes em alguns idiomas.

Tokenização multilíngue: por que é difícil

Um tokenizador treinado majoritariamente em inglês pode “funcionar” em outros idiomas, mas eficiência e desempenho podem degradar acentuadamente. Problemas comuns incluem:

1) Scripts e distribuições de caracteres diferentes

Um vocabulário compartilhado precisa alocar capacidade entre scripts (Latino, Cirílico, Árabe, Devanágari, caracteres Han, etc.). Se os dados de treinamento são desbalanceados, o tokenizador tende a:

  • Aprender muitos tokens para idiomas/scripts de alto recurso
  • Forçar scripts de baixo recurso a pedaços menores (mais tokens por frase)

Isso significa que, para a mesma janela de contexto, alguns idiomas têm menos palavras “à vista”.

2) Idiomas sem fronteiras de palavra por espaços em branco

Chinês e japonês tipicamente não usam espaços entre palavras; tailandês e laosiano também têm segmentação complexa. Tokenizadores de subpalavras conseguem lidar com esses idiomas, mas:

  • Podem produzir fronteiras de token que não se alinham com palavras linguísticas.
  • A eficiência depende fortemente do tamanho do vocabulário e da cobertura dos dados.

3) Idiomas morfologicamente ricos e aglutinantes

Turco, finlandês, húngaro e muitos outros constroem palavras longas com múltiplos morfemas. Um tokenizador que não tenha subpalavras relevantes pode produzir:

  • Fertilidade muito alta (muitos tokens por palavra)
  • Pior modelagem de dependências de longo alcance porque “uma palavra” fica espalhada por muitas posições

Unigram às vezes se comporta melhor aqui porque pode manter morfemas úteis como tokens se eles aumentarem a verossimilhança.

4) Diacríticos e marcas opcionais

Diacríticos árabes, niqqud hebraico e marcas combinantes em línguas índicas podem aparecer de forma inconsistente. Tokenizadores podem ficar frágeis se:

  • diacríticos forem raros no treinamento mas aparecerem na inferência,
  • a normalização remover ou alterar marcas de forma inesperada.

Abordagens em nível de byte evitam [UNK], mas ainda podem ser ineficientes.

5) Alternância de código, emojis e texto “bagunçado” da web

Texto multilíngue gerado por usuários frequentemente mistura:

  • idiomas (alternância de código),
  • scripts (Latino + numerais arábicos + emoji),
  • strings específicas de domínio (URLs, hashtags, SKUs de produtos).

Tokenização em nível de byte é robusta aqui, mas a contagem de tokens pode disparar se o tokenizador não tiver mesclas para padrões comuns como URLs.

6) Balanceamento de dados e alocação de vocabulário

Para modelos multilíngues, praticantes frequentemente usam:

  • amostragem baseada em temperatura (temperature-based sampling) (superamostrar idiomas de baixo recurso)
  • cotas explícitas por idioma/script
  • normalização ciente de script

Mesmo com balanceamento perfeito, um único vocabulário fixo não pode ser igualmente eficiente para todos os idiomas—há um trade-off real de capacidade.

Principais trade-offs ao escolher BPE vs Unigram (e ao configurar qualquer um)

Tamanho do vocabulário vs comprimento da sequência

  • Vocabulário maior → sequências mais curtas porém:

    • mais parâmetros de embedding/softmax,
    • treinamento/inferência potencialmente mais lentos devido à largura de banda de memória,
    • maior risco de “memorizar” strings idiossincráticas.
  • Vocabulário menor → sequências mais longas porém:

    • menos parâmetros,
    • melhor compartilhamento entre palavras e idiomas,
    • maior custo computacional devido a contextos mais longos.

Esse trade-off é especialmente importante para LLMs porque o custo da atenção aumenta rapidamente com o comprimento da sequência.

Robustez vs eficiência (nível de byte vs nível Unicode)

  • Nível de byte (byte-level):

    • Prós: cobertura universal, sem [UNK], bom para texto ruidoso
    • Contras: pode ser menos eficiente para scripts sub-representados nos dados de treinamento
  • Base em Unicode/caracteres:

    • Prós: pode ser eficiente se a cobertura for boa
    • Contras: pode exigir tratamento de token desconhecido; incompatibilidades de normalização podem prejudicar

Determinismo e “travamento” de mesclas (BPE) vs segmentação flexível (Unigram)

  • O procedimento de mescla do BPE é guloso durante o treinamento: mesclas iniciais nunca são desfeitas.
    • Isso pode criar tokens subótimos se padrões de frequência iniciais forem enganosos.
  • A poda do Unigram pode ser mais informada globalmente porque avalia verossimilhança e pode descartar tokens que não ajudam.

Na prática, ambos podem funcionar extremamente bem; diferenças frequentemente aparecem em casos de borda e cenários multilíngues.

Interpretabilidade e depuração

Tokens de subpalavras não são unidades linguísticas, mas:

  • BPE frequentemente cria substrings “robustas” fáceis de reconhecer.
  • Unigram pode produzir segmentações que às vezes parecem mais “morfêmicas”, mas não de forma confiável.

Para depurar problemas de prompt (por exemplo, por que um modelo se comporta de forma estranha com uma palavra), conseguir inspecionar a tokenização é valioso.

Equidade e desempenho cross-lingual

Se um idioma usa 2× ou 5× mais tokens para expressar o mesmo significado, ele efetivamente sofre:

  • redução do contexto utilizável,
  • maiores taxas de truncamento,
  • potencialmente pior qualidade de geração.

Isso não é apenas um problema de eficiência; pode se tornar um problema de disparidade de qualidade, especialmente em avaliação e comportamento de produto (ver Avaliação para PLN).

Adequação à tarefa downstream

  • Para recuperação/embeddings, a tokenização afeta quão bem unidades semânticas são representadas e quão longas as entradas podem ser antes de truncar (ver Busca Semântica).
  • Para geração, convenções de espaços em branco e pontuação podem importar muito.
  • Para texto de domínio (jurídico, médico, código), ter tokens para jargão e símbolos comuns pode reduzir muito o comprimento da sequência.

Exemplos práticos: treinando e usando tokenizadores BPE/Unigram

A seguir há exemplos pequenos e ilustrativos. Em sistemas reais, você usará corpora muito maiores e normalização cuidadosa.

Treinando um tokenizador Unigram do SentencePiece (Python)

# pip install sentencepiece
import sentencepiece as spm

# Prepare a text file "corpus.txt" with one document per line.
# Example content (very small for demonstration):
#   Hello world
#   Bonjour le monde
#   こんにちは世界

spm.SentencePieceTrainer.train(
    input="corpus.txt",
    model_prefix="toy_unigram",
    vocab_size=800,                 # tiny for demo; real models: 8k–200k+
    model_type="unigram",           # or "bpe"
    character_coverage=0.9995,      # important for multilingual scripts
    normalization_rule_name="nfc"   # consistent Unicode normalization
)

sp = spm.SentencePieceProcessor(model_file="toy_unigram.model")
text = "Bonjour le monde"
pieces = sp.encode(text, out_type=str)
ids = sp.encode(text, out_type=int)

print(pieces)
print(ids)
print(sp.decode(ids))

Principais parâmetros para entender:

  • model_type: "unigram" vs "bpe".
  • vocab_size: afeta diretamente eficiência/parâmetros.
  • character_coverage: crucial para scripts com muitos caracteres (por exemplo, japonês). Muito baixo → baixa cobertura; muito alto com vocabulário pequeno → menos espaço para mesclas/subpalavras.
  • normalization_rule_name: deve corresponder ao seu pipeline de serving.

Comparando contagens de tokens entre idiomas (diagnóstico conceitual)

Uma verificação simples de sanidade para tokenizadores multilíngues é contagem de tokens por frase em idiomas representativos:

samples = [
    ("en", "The quick brown fox jumps over the lazy dog."),
    ("tr", "Çekoslovakyalılaştıramadıklarımızdanmışsınız."),
    ("zh", "快速的棕色狐狸跳过了懒狗。"),
    ("ar", "الثعلب البني السريع قفز فوق الكلب الكسول."),
]

for lang, s in samples:
    ids = sp.encode(s, out_type=int)
    print(lang, len(ids), ids[:10])

O que observar:

  • Alguns idiomas são consistentemente muito mais longos em tokens para conteúdo de complexidade similar?
  • Certos scripts explodem em comprimento quando pontuação/diacríticos aparecem?
  • O tokenizador se comporta de forma sensata em strings de scripts mistos?

Esses diagnósticos ajudam a detectar desbalanceamento que pode mais tarde aparecer como lacunas de qualidade.

Armadilhas comuns (e como evitá-las)

Normalização inconsistente entre treinamento e inferência

Se o treinamento aplica NFC mas a inferência não (ou vice-versa), a tokenização pode derivar. Corrija:

  • tornando a normalização uma parte claramente versionada do artefato do tokenizador,
  • aplicando exatamente o mesmo pré-processamento em todos os ambientes.

Superajuste do vocabulário a um domínio

Se você treina majoritariamente em notícias, o tokenizador pode ser ineficiente em:

  • código (snake_case, chaves),
  • termos biomédicos,
  • URLs e nomes de produtos.

Mitigação:

  • incluir dados representativos,
  • ou aceitar alguma ineficiência mas usar fallback em nível de byte.

Escolher um tamanho de vocabulário sem medir comprimentos de sequência

Dois tokenizadores com o mesmo tamanho de vocabulário podem produzir distribuições diferentes de comprimento de sequência. Sempre meça:

  • p50/p95 do comprimento em tokens por documento,
  • taxas de truncamento no seu contexto máximo,
  • impacto de desempenho em tarefas-chave (ver Modelagem de Linguagem).

Tokens especiais e tratamento de fronteiras

Seja explícito sobre:

  • tokens BOS/EOS,
  • padding,
  • tokens separadores,
  • tokens de formatação de chat.

Isso importa para estabilidade do treinamento e para a construção de prompts em aplicações com LLMs.

Como escolher entre BPE e Unigram em projetos reais

Heurísticas práticas:

  • Se você precisa de máxima robustez a Unicode arbitrário e texto “bagunçado”, considere fortemente BPE em nível de byte (ou um tokenizador com fallback em bytes).
  • Se você está construindo um sistema multilíngue e pode arcar com treinamento cuidadoso do SentencePiece, Unigram costuma ser uma base forte, especialmente com boa normalização e dados balanceados por idioma.
  • Se você está limitado por computação de inferência, priorize sequências curtas, o que pode significar um vocabulário maior—mas meça memória e latência.
  • Se você se importa com equidade entre idiomas, avalie explicitamente a eficiência de tokenização por idioma e considere balanceamento de dados/vocabulário.

Em muitos pipelines modernos, tokenização não é “configurar e esquecer”. É uma alavanca de engenharia: você pode iterar no treinamento do tokenizador à medida que expande idiomas, domínios ou comprimentos de contexto.

Olhando adiante: além de vocabulários fixos de subpalavras

A pesquisa em tokenização continua a evoluir. Direções incluem:

  • Modelos totalmente em nível de byte/caractere com arquiteturas que mitigam custos de sequências longas.
  • Tokenização adaptativa ou dinâmica, na qual o modelo pode escolher granularidades.
  • Estratégias de alocação de vocabulário multilíngue que otimizam explicitamente eficiência e qualidade cross-lingual, em vez de depender de frequência bruta.

Mesmo ao usar os tokenizadores padrão atuais BPE/Unigram, tratar tokenização como uma escolha de projeto de primeira classe—especialmente para sistemas multilíngues—pode gerar ganhos substanciais tanto em desempenho quanto em experiência do usuário.