BpeTokenizer: mask_token_id sobrecarrega <unk> em span-MLM #25

Open
opened 2026-05-14 02:13:45 -03:00 by navigator · 0 comments
Owner

TL;DR

A Phase EPSILON adicionou tokenização BPE 32K pros baselines SOTA (scripts/bpe_corpus.py). O sentencepiece BPE não tem um piece dedicado pra <mask>. Quando o sweep usa mixed-objective com span-MLM (20%), o apply_objective_to_batch precisa de um mask_token_id. Hoje o código usa unk_id() (= 1) como fallback — ou seja, e compartilham o mesmo id. Isso confunde dois eventos linguisticamente distintos (token mascarado deliberadamente vs token fora-do-vocab) em uma única classe.

Por que isso importa

Em span-MLM o modelo é treinado a:

  1. Reconstruir o token original onde o input mostra <mask>
  2. Aceitar que significa "palavra inexistente, mantém como está"

Com os dois compartilhando id=1, o modelo aprende sinais ambíguos: às vezes id=1 significa "tem alguma palavra aqui, prevê qual", às vezes "isso é uma palavra que não existe no vocab". Esperado: aprendizado de span-MLM enfraquecido em BPE.

Em escala nano + 256K tokens × 20% span-MLM = ~50K tokens de treino com a confusão — pouco, mas é um vies estrutural conhecido.

Onde mexer

Duas opções:

Opção A — retreinar BPE tokenizer com <mask> dedicado

scripts/train_bpe_tokenizer.py adiciona um user-defined symbol <mask> à sentencepiece config. Vocab vira 32_001 ou reserva-se um id já existente. Tokens de mask deixam de colidir com .

spm.SentencePieceTrainer.Train(
    ...,
    user_defined_symbols=["<mask>"],
)

Custo: 1 retreino do tokenizer (~60s), reprocessamento de qualquer parquet pre-tokenizado.

Opção B — usar último id como mask (sem retreinar)

BpeTokenizer.mask_id = vocab_size - 1. Mas esse id é uma piece BPE real, então mascarar com ele introduz outro vies (modelo tem que aprender a tratar o piece final como mask).

Pior que A.

Opção C — desligar span-MLM em sweeps BPE

--objective-mix "0.7,0.3,0" em vez de "0.5,0.3,0.2" só pros sweeps BPE. Evita o problema mas reduz a comparabilidade do regime.

Recomendação

Opção A — retreinar BPE 32K com <mask> dedicado. ~60 segundos de compute, fix definitivo. O EPSILON sweep atual (em andamento) é OK com a sobrecarga <unk>, mas qualquer sweep BPE futuro com span-MLM deveria usar a versão correta.

Critério de aceite

  • BPE 32K v2 tokenizer com <mask> dedicado, salvo em data/processed/bpe_32k_v2.model.
  • BpeTokenizer expõe atributo .mask_id apontando pro novo piece.
  • _run_one em run_ablation_word.py lê tokenizer.mask_id sem fallback pro <unk>.

Não-bloqueante (por enquanto)

O EPSILON sweep que está rodando vai produzir números úteis com <unk> como mask. Não invalida a comparação BPC porque ambos os baselines BPE (vanilla / deepseek / llama) usam a mesma sobrecarga. O viés é constante entre eles, então paired-t intra-BPE permanece justo. Só afeta a comparação absoluta BPE vs word-level (que tem um <mask> real).

Referências

  • scripts/bpe_corpus.py — BpeTokenizer
  • scripts/train_bpe_tokenizer.py — training script
  • scripts/run_ablation_word.py linha ~230 — fallback de mask_token_id
  • masha/training/mixed_objective.py — apply_objective_to_batch
## TL;DR A Phase EPSILON adicionou tokenização BPE 32K pros baselines SOTA (`scripts/bpe_corpus.py`). O sentencepiece BPE não tem um piece dedicado pra `<mask>`. Quando o sweep usa mixed-objective com span-MLM (20%), o `apply_objective_to_batch` precisa de um `mask_token_id`. Hoje o código usa `unk_id()` (= 1) como fallback — ou seja, **<mask> e <unk> compartilham o mesmo id**. Isso confunde dois eventos linguisticamente distintos (token mascarado deliberadamente vs token fora-do-vocab) em uma única classe. ## Por que isso importa Em span-MLM o modelo é treinado a: 1. **Reconstruir** o token original onde o input mostra `<mask>` 2. **Aceitar** que <unk> significa "palavra inexistente, mantém como está" Com os dois compartilhando id=1, o modelo aprende sinais ambíguos: às vezes id=1 significa "tem alguma palavra aqui, prevê qual", às vezes "isso é uma palavra que não existe no vocab". Esperado: aprendizado de span-MLM enfraquecido em BPE. Em escala nano + 256K tokens × 20% span-MLM = ~50K tokens de treino com a confusão — pouco, mas é um vies estrutural conhecido. ## Onde mexer Duas opções: ### Opção A — retreinar BPE tokenizer com `<mask>` dedicado `scripts/train_bpe_tokenizer.py` adiciona um user-defined symbol `<mask>` à sentencepiece config. Vocab vira 32_001 ou reserva-se um id já existente. Tokens de mask deixam de colidir com <unk>. ```python spm.SentencePieceTrainer.Train( ..., user_defined_symbols=["<mask>"], ) ``` Custo: 1 retreino do tokenizer (~60s), reprocessamento de qualquer parquet pre-tokenizado. ### Opção B — usar último id como mask (sem retreinar) `BpeTokenizer.mask_id = vocab_size - 1`. Mas esse id é uma piece BPE real, então mascarar com ele introduz outro vies (modelo tem que aprender a tratar o piece final como mask). Pior que A. ### Opção C — desligar span-MLM em sweeps BPE `--objective-mix "0.7,0.3,0"` em vez de "0.5,0.3,0.2" só pros sweeps BPE. Evita o problema mas reduz a comparabilidade do regime. ## Recomendação **Opção A** — retreinar BPE 32K com `<mask>` dedicado. ~60 segundos de compute, fix definitivo. O EPSILON sweep atual (em andamento) é OK com a sobrecarga `<unk>`, mas qualquer sweep BPE futuro com span-MLM deveria usar a versão correta. ## Critério de aceite - BPE 32K v2 tokenizer com `<mask>` dedicado, salvo em `data/processed/bpe_32k_v2.model`. - `BpeTokenizer` expõe atributo `.mask_id` apontando pro novo piece. - `_run_one` em `run_ablation_word.py` lê tokenizer.mask_id sem fallback pro `<unk>`. ## Não-bloqueante (por enquanto) O EPSILON sweep que está rodando vai produzir números úteis com `<unk>` como mask. **Não invalida** a comparação BPC porque ambos os baselines BPE (vanilla / deepseek / llama) usam a mesma sobrecarga. O viés é constante entre eles, então paired-t intra-BPE permanece justo. Só afeta a comparação **absoluta** BPE vs word-level (que tem um `<mask>` real). ## Referências - `scripts/bpe_corpus.py` — BpeTokenizer - `scripts/train_bpe_tokenizer.py` — training script - `scripts/run_ablation_word.py` linha ~230 — fallback de mask_token_id - `masha/training/mixed_objective.py` — apply_objective_to_batch
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
pop/MASHA#25
No description provided.