Phase 10 - Lesson 01
Tokenizers: BPE, WordPiece, SentencePiece
This lesson includes a graded coding exercise that runs in your browser, unlocked with lifetime access.
Su LLM no lee inglés. Lee enteros. El tokenizador decide si esos enteros llevan significado o lo desperdician.
Tipo: Build Lenguajes: Python Prerrequisitos: Fase 05 (NLP Foundations) Tiempo: ~90 minutos
Objetivos de Aprendizaje
- Implementar los algoritmos de tokenización BPE, WordPiece y Unigram desde cero y comparar sus estrategias de fusión
- Explicar cómo el tamaño del vocabulario afecta la eficiencia del modelo: si es demasiado pequeño crea secuencias largas, si es demasiado grande desperdicia parámetros de embedding
- Analizar los artefactos de tokenización en diferentes idiomas y código, identificando dónde fallan los tokenizadores específicos
- Usar las bibliotecas tiktoken y sentencepiece para tokenizar texto e inspeccionar los IDs de token resultantes
El Problema
Su LLM no lee inglés. No lee ningún idioma. Lee números.
La brecha entre "Hello, world!" y [15496, 11, 995, 0] es el tokenizador. Cada palabra, cada espacio, cada signo de puntuación debe convertirse en un entero antes de que un modelo pueda procesarlo. Esta conversión no es neutra. Introduce suposiciones en el modelo que no se pueden deshacer más tarde.
Si se equivoca en esto, su modelo desperdiciará capacidad codificando palabras comunes con múltiples tokens. "unfortunately" se convierte en cuatro tokens en lugar de uno. Su ventana de contexto de 128K acaba de reducirse en un 75% para textos cargados de palabras de muchas sílabas. Si lo hace bien, la misma ventana de contexto contendrá el doble de significado. La diferencia entre "este modelo maneja bien el código" y "este modelo se ahoga con Python" a menudo se reduce a cómo se entrenó el tokenizador.
Cada llamada a la API que realiza a GPT-4 o Claude se cobra por token. Cada token que genera su modelo cuesta cómputo. Cuantos menos tokens se requieran para representar una salida, más rápida será la inferencia de extremo a extremo. La tokenización no es preprocesamiento. Es arquitectura.
El Concepto
Tres Enfoques Que Fallaron (y Uno Que Ganó)
Hay tres formas obvias de convertir texto en números. Dos de ellas no funcionan a escala.
La tokenización a nivel de palabra divide el texto en espacios y puntuación. "The cat sat" se convierte en ["The", "cat", "sat"]. Simple. Pero ¿qué pasa con "tokenization"? ¿O "GPT-4o"? ¿O una palabra compuesta en alemán como "Geschwindigkeitsbegrenzung"? El nivel de palabra requiere un vocabulario masivo para cubrir cada palabra en cada idioma. Si se le pasa una palabra, obtendrá el temido token [UNK], la forma en que el modelo dice "no tengo idea de qué es esto". Solo el inglés tiene más de un millón de formas de palabras. Agregue código, URLs, notación científica y otros 100 idiomas, y necesitará un vocabulario infinito.
La tokenización a nivel de caracteres va en la dirección opuesta. "hello" se convierte en ["h", "e", "l", "l", "o"]. El vocabulario es minúsculo (unas pocas cientos de caracteres). Nunca hay tokens desconocidos. Pero las secuencias se vuelven extremadamente largas. Una oración que tendría 10 tokens a nivel de palabra se convierte en 50 tokens a nivel de caracteres. El modelo debe aprender que "t", "h", "e" juntos significan "the", consumiendo capacidad de atención en algo que un humano aprende a los tres años de edad.
La tokenización de subpalabras encuentra el punto ideal. Las palabras comunes permanecen enteras: "the" es un solo token. Las palabras raras se descomponen en piezas significativas: "unhappiness" se convierte en ["un", "happi", "ness"]. El vocabulario se mantiene manejable (de 30K a 128K tokens). Las secuencias siguen siendo cortas. Los tokens desconocidos prácticamente desaparecen porque cualquier palabra se puede construir a partir de piezas de subpalabras.
Cada LLM moderno utiliza tokenización de subpalabras. GPT-2, GPT-4, BERT, Llama 3, Claude, todos ellos. La pregunta es qué algoritmo usar.
graph TD
A["Texto: 'unhappiness'"] --> B{"Estrategia de Tokenización"}
B -->|A nivel de palabra| C["['unhappiness']\n1 token si está en vocab\n[UNK] si no"]
B -->|A nivel de caracteres| D["['u','n','h','a','p','p','i','n','e','s','s']\n11 tokens"]
B -->|BPE de subpalabras| E["['un','happi','ness']\n3 tokens"]
style C fill:#ff6b6b,color:#fff
style D fill:#ffa500,color:#fff
style E fill:#51cf66,color:#fff
BPE: Byte Pair Encoding
BPE es un algoritmo de compresión codicioso (greedy) adaptado para la tokenización. La idea es lo suficientemente simple como para caber en una tarjeta de notas.
Comience con caracteres individuales. Cuente cada par adyacente en el corpus de entrenamiento. Fusione el par más frecuente en un nuevo token. Repita hasta alcanzar el tamaño de vocabulario deseado.
Aquí está BPE ejecutándose en un corpus diminuto con las palabras "lower", "lowest" y "newest":
Corpus (with word frequencies):
"lower" x5
"lowest" x2
"newest" x6
Step 0 -- Start with characters:
l o w e r (x5)
l o w e s t (x2)
n e w e s t (x6)
Step 1 -- Count adjacent pairs:
(e,s): 8 (s,t): 8 (l,o): 7 (o,w): 7
(w,e): 13 (e,r): 5 (n,e): 6 ...
Step 2 -- Merge most frequent pair (w,e) -> "we":
l o we r (x5)
l o we s t (x2)
n e we s t (x6)
Step 3 -- Recount and merge (e,s) -> "es":
l o we r (x5)
l o we s t (x2) <- 'es' only forms from 'e'+'s', not 'we'+'s'
n e we s t (x6) <- wait, the 'e' before 'we' and 's' after 'we'
Actually tracking this precisely:
After "we" merge, remaining pairs:
(l,o): 7 (o,we): 7 (we,r): 5 (we,s): 8
(s,t): 8 (n,e): 6 (e,we): 6
Step 3 -- Merge (we,s) -> "wes" or (s,t) -> "st" (tied at 8, pick first):
Merge (we,s) -> "wes":
l o we r (x5)
l o wes t (x2)
n e wes t (x6)
Step 4 -- Merge (wes,t) -> "west":
l o we r (x5)
l o west (x2)
n e west (x6)
...continue until target vocab size reached.
La tabla de fusiones (merge table) es el tokenizador. Para codificar texto nuevo, aplique las fusiones en el orden en que se aprendieron. El corpus de entrenamiento determina qué fusiones existen, y esa elección define permanentemente lo que ve el modelo.
graph LR
subgraph Training["Bucle de Entrenamiento BPE"]
direction TB
T1["Inicio: vocabulario de caracteres"] --> T2["Contar todos los pares adyacentes"]
T2 --> T3["Fusionar el par más frecuente"]
T3 --> T4["Agregar token fusionado al vocabulario"]
T4 --> T5{"¿Alcanzó el tamaño\nde vocabulario objetivo?"}
T5 -->|No| T2
T5 -->|Sí| T6["Listo: guardar tabla de fusiones"]
end
Byte-Level BPE (GPT-2, GPT-3, GPT-4)
El BPE estándar funciona con caracteres Unicode. El BPE a nivel de bytes (Byte-level BPE) opera sobre bytes crudos (0-255). Esto proporciona un vocabulario base de exactamente 256, maneja cualquier idioma o codificación y nunca produce un token desconocido.
GPT-2 introdujo este enfoque. El vocabulario base cubre cada byte posible. Las fusiones de BPE se construyen sobre eso. La biblioteca tiktoken de OpenAI implementa BPE a nivel de bytes con los siguientes tamaños de vocabulario:
- GPT-2: 50.257 tokens
- GPT-3.5/GPT-4: ~100.256 tokens (codificación cl100k_base)
- GPT-4o: 200.019 tokens (codificación o200k_base)
WordPiece (BERT)
WordPiece parece similar a BPE pero elige las fusiones de manera diferente. En lugar de la frecuencia bruta, maximiza la verosimilitud de los datos de entrenamiento:
BPE merge criterion: count(A, B)
WordPiece merge criterion: count(AB) / (count(A) * count(B))
BPE pregunta: "¿Qué par aparece con más frecuencia?" WordPiece pregunta: "¿Qué par aparece junto con más frecuencia de lo esperado por azar?" Esta sutil diferencia produce vocabularios diferentes. WordPiece favorece las fusiones donde la coocurrencia es sorprendente, no solo frecuente.
WordPiece también utiliza un prefijo "##" para subpalabras de continuación:
"unhappiness" -> ["un", "##happi", "##ness"]
"embedding" -> ["em", "##bed", "##ding"]
El prefijo "##" indica que esta pieza continúa un token anterior. BERT utiliza WordPiece con un vocabulario de 30.522 tokens. Cada variante de BERT (DistilBERT, el tokenizador de RoBERTa es en realidad BPE, pero BERT en sí es WordPiece).
SentencePiece (Llama, T5)
SentencePiece trata la entrada como una secuencia bruta de caracteres Unicode, incluyendo los espacios en blanco. Sin etapa de pretokenización. Sin reglas específicas de idioma sobre los límites de las palabras. Esto lo hace genuinamente agnóstico al idioma: funciona en chino, japonés, tailandés y otros idiomas donde los espacios no separan las palabras.
SentencePiece admite dos algoritmos:
- Modo BPE: la misma lógica de fusión que el BPE estándar, aplicada a secuencias de caracteres crudos
- Modo Unigram: comienza con un vocabulario grande y elimina iterativamente los tokens que menos afectan la verosimilitud general. Lo opuesto a BPE: poda en lugar de fusión.
Llama 2 utiliza SentencePiece BPE con un vocabulario de 32.000 tokens. T5 utiliza SentencePiece Unigram con 32.000 tokens. Nota: Llama 3 cambió a un tokenizador BPE a nivel de bytes basado en tiktoken con 128.256 tokens.
Vocabulary Size Tradeoffs
Esta es una decisión de ingeniería real con consecuencias mensurables.
graph LR
subgraph Small["Vocabulario Pequeño (32K)\nej: BERT, T5"]
S1["Más tokens por texto"]
S2["Secuencias más largas"]
S3["Matriz de embedding más pequeña"]
S4["Mejor manejo de palabras raras"]
end
subgraph Large["Vocabulario Grande (128K+)\nej: Llama 3, GPT-4o"]
L1["Menos tokens por texto"]
L2["Secuencias más cortas"]
L3["Matriz de embedding más grande"]
L4["Inferencia más rápida"]
end
Números concretos. Para un vocabulario de 128K con embeddings de 4.096 dimensiones, la matriz de embedding por sí sola tiene 128.000 x 4.096 = 524 millones de parámetros. Para un vocabulario de 32K, tiene 131 millones de parámetros. Esa es una diferencia de 400M de parámetros solo por la elección del tokenizador.
Pero los vocabularios más grandes comprimen el texto de manera más agresiva. El mismo párrafo en inglés que requiere 100 tokens con un vocabulario de 32K podría requerir 70 tokens con un vocabulario de 128K. Eso significa un 30% menos de pasadas hacia adelante (forward passes) durante la generación. Para un modelo que atiende millones de solicitudes, esto representa una reducción directa en el costo de cómputo.
La tendencia está clara: los tamaños de vocabulario están creciendo. GPT-2 usaba 50.257. GPT-4 usa ~100K. Llama 3 usa 128K. GPT-4o usa 200K.
| Modelo | Tamaño del Vocabulario | Tipo de Tokenizador | Promedio de Tokens por Palabra en Inglés |
|---|---|---|---|
| BERT | 30.522 | WordPiece | ~1,4 |
| GPT-2 | 50.257 | BPE a nivel de bytes | ~1,3 |
| Llama 2 | 32.000 | SentencePiece BPE | ~1,4 |
| GPT-4 | ~100.256 | BPE a nivel de bytes | ~1,2 |
| Llama 3 | 128.256 | BPE a nivel de bytes (tiktoken) | ~1,1 |
| GPT-4o | 200.019 | BPE a nivel de bytes | ~1,0 |
El Impuesto Multilingüe
Los tokenizadores entrenados principalmente en inglés son brutales con otros idiomas. El texto en coreano en el tokenizador de GPT-2 promedia de 2 a 3 tokens por palabra. En chino puede ser peor. Esto significa que un usuario coreano tiene efectivamente una ventana de contexto que es la mitad del tamaño de la de un usuario de habla inglesa, pagando el mismo precio por menos densidad de información.
Es por eso que Llama 3 cuadruplicó su vocabulario de 32K a 128K. Más tokens dedicados a escrituras no inglesas significan una compresión más justa entre idiomas.
Build It
Paso 1: Tokenizador a Nivel de Caracteres
Comience en la base. Un tokenizador a nivel de caracteres mapea cada carácter a su punto de código Unicode (code point). No requiere entrenamiento. Sin tokens desconocidos. Solo un mapeo directo.
class CharTokenizer:
def encode(self, text):
return [ord(c) for c in text]
def decode(self, tokens):
return "".join(chr(t) for t in tokens)
"hello" se convierte en [104, 101, 108, 108, 111]. Cada carácter es su propio token. Esta es la línea base sobre la cual mejoramos.
Paso 2: Tokenizador BPE desde Cero
La implementación real. Entrenamos en bytes crudos (como GPT-2), contamos pares, fusionamos el más frecuente y registramos cada fusión en orden. La tabla de fusiones es el tokenizador.
from collections import Counter
class BPETokenizer:
def __init__(self):
self.merges = {}
self.vocab = {}
def _get_pairs(self, tokens):
pairs = Counter()
for i in range(len(tokens) - 1):
pairs[(tokens[i], tokens[i + 1])] += 1
return pairs
def _merge_pair(self, tokens, pair, new_token):
merged = []
i = 0
while i < len(tokens):
if i < len(tokens) - 1 and tokens[i] == pair[0] and tokens[i + 1] == pair[1]:
merged.append(new_token)
i += 2
else:
merged.append(tokens[i])
i += 1
return merged
def train(self, text, num_merges):
tokens = list(text.encode("utf-8"))
self.vocab = {i: bytes([i]) for i in range(256)}
for i in range(num_merges):
pairs = self._get_pairs(tokens)
if not pairs:
break
best_pair = max(pairs, key=pairs.get)
new_token = 256 + i
tokens = self._merge_pair(tokens, best_pair, new_token)
self.merges[best_pair] = new_token
self.vocab[new_token] = self.vocab[best_pair[0]] + self.vocab[best_pair[1]]
return self
def encode(self, text):
tokens = list(text.encode("utf-8"))
for pair, new_token in self.merges.items():
tokens = self._merge_pair(tokens, pair, new_token)
return tokens
def decode(self, tokens):
byte_sequence = b"".join(self.vocab[t] for t in tokens)
return byte_sequence.decode("utf-8", errors="replace")
El bucle de entrenamiento es el núcleo de BPE: contar pares, fusionar el ganador, repetir. Cada fusión reduce el recuento total de tokens. Después de num_merges rondas, el vocabulario crece de 256 (bytes base) a 256 + num_merges.
La codificación aplica las fusiones en el orden exacto en que se aprendieron. Esto importa. Si la fusión 1 creó "th" y la fusión 5 creó "the", la codificación debe aplicar la fusión 1 primero para que se pueda formar "the" a partir de "th" + "e" en la fusión 5.
La decodificación es lo inverso: buscar cada ID de token en el vocabulario, concatenar los bytes y decodificar a UTF-8.
Paso 3: Prueba de Ida y Vuelta (Roundtrip) de Codificación y Decodificación
corpus = (
"The cat sat on the mat. The cat ate the rat. "
"The dog sat on the log. The dog ate the frog. "
"Natural language processing is the study of how computers "
"understand and generate human language. "
"Tokenization is the first step in any NLP pipeline."
)
tokenizer = BPETokenizer()
tokenizer.train(corpus, num_merges=40)
test_sentences = [
"The cat sat on the mat.",
"Natural language processing",
"tokenization pipeline",
"unhappiness",
]
for sentence in test_sentences:
encoded = tokenizer.encode(sentence)
decoded = tokenizer.decode(encoded)
raw_bytes = len(sentence.encode("utf-8"))
ratio = len(encoded) / raw_bytes
print(f"'{sentence}'")
print(f" Tokens: {len(encoded)} (from {raw_bytes} bytes) -- ratio: {ratio:.2f}")
print(f" Roundtrip: {'PASS' if decoded == sentence else 'FAIL'}")
La relación de compresión (compression ratio) le indica qué tan efectivo es el tokenizador. Una relación de 0,50 significa que el tokenizador comprimió el texto a la mitad de tokens en comparación con los bytes crudos. Cuanto menor sea, mejor. En el corpus de entrenamiento, la relación será buena. En texto fuera de distribución (out-of-distribution) como "unhappiness" (que no aparece en el corpus), la relación será peor: el tokenizador recurre a la codificación a nivel de caracteres para los patrones no vistos.
Paso 4: Comparar con tiktoken
import tiktoken
enc = tiktoken.get_encoding("cl100k_base")
texts = [
"The cat sat on the mat.",
"unhappiness",
"Hello, world!",
"def fibonacci(n): return n if n < 2 else fibonacci(n-1) + fibonacci(n-2)",
"Geschwindigkeitsbegrenzung",
]
for text in texts:
our_tokens = tokenizer.encode(text)
tiktoken_tokens = enc.encode(text)
tiktoken_pieces = [enc.decode([t]) for t in tiktoken_tokens]
print(f"'{text}'")
print(f" Our BPE: {len(our_tokens)} tokens")
print(f" tiktoken: {len(tiktoken_tokens)} tokens -> {tiktoken_pieces}")
tiktoken utiliza exactamente el mismo algoritmo pero entrenado en cientos de gigabytes de texto con 100.000 fusiones. El algoritmo es idéntico. La diferencia radica en los datos de entrenamiento y el número de fusiones. Su tokenizador entrenado en un párrafo con 40 fusiones no puede competir con las 100K fusiones de tiktoken en un corpus masivo. Pero el mecanismo es el mismo.
Paso 5: Análisis de Vocabulario
def analyze_vocabulary(tokenizer, test_texts):
total_tokens = 0
total_chars = 0
token_usage = Counter()
for text in test_texts:
encoded = tokenizer.encode(text)
total_tokens += len(encoded)
total_chars += len(text)
for t in encoded:
token_usage[t] += 1
print(f"Vocabulary size: {len(tokenizer.vocab)}")
print(f"Total tokens across all texts: {total_tokens}")
print(f"Total characters: {total_chars}")
print(f"Avg tokens per character: {total_tokens / total_chars:.2f}")
print(f"\nMost used tokens:")
for token_id, count in token_usage.most_common(10):
token_bytes = tokenizer.vocab[token_id]
display = token_bytes.decode("utf-8", errors="replace")
print(f" Token {token_id:4d}: '{display}' (used {count} times)")
unused = [t for t in tokenizer.vocab if t not in token_usage]
print(f"\nUnused tokens: {len(unused)} out of {len(tokenizer.vocab)}")
Esto revela la distribución de Zipf en su vocabulario. Dominan unos pocos tokens (espacios, "the", "e"). La mayoría de los tokens rara vez se usan. Los tokenizadores de producción se optimizan para esta distribución: los patrones comunes obtienen IDs de token cortos y los patrones raros obtienen representaciones más largas.
Use It
Su BPE desde cero funciona. Ahora vea cómo son las herramientas de producción.
tiktoken (OpenAI)
import tiktoken
enc = tiktoken.get_encoding("cl100k_base")
text = "Tokenizers convert text to integers"
tokens = enc.encode(text)
print(f"Tokens: {tokens}")
print(f"Pieces: {[enc.decode([t]) for t in tokens]}")
print(f"Roundtrip: {enc.decode(tokens)}")
tiktoken está escrito en Rust con enlaces (bindings) para Python. Codifica millones de tokens por segundo. El mismo algoritmo BPE, con una implementación de fuerza industrial.
Hugging Face tokenizers
from tokenizers import Tokenizer
from tokenizers.models import BPE
from tokenizers.trainers import BpeTrainer
from tokenizers.pre_tokenizers import ByteLevel
tokenizer = Tokenizer(BPE())
tokenizer.pre_tokenizer = ByteLevel()
trainer = BpeTrainer(vocab_size=1000, special_tokens=["<pad>", "<eos>", "<unk>"])
tokenizer.train(["corpus.txt"], trainer)
output = tokenizer.encode("The cat sat on the mat.")
print(f"Tokens: {output.tokens}")
print(f"IDs: {output.ids}")
La biblioteca tokenizers de Hugging Face también se basa en Rust por debajo. Entrena BPE en corpora a escala de gigabytes en segundos. Esto es lo que se usa al entrenar su propio modelo.
Cargar el Tokenizador de Llama
from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("meta-llama/Llama-3.1-8B")
text = "Tokenizers are the unsung heroes of LLMs"
tokens = tokenizer.encode(text)
print(f"Token IDs: {tokens}")
print(f"Tokens: {tokenizer.convert_ids_to_tokens(tokens)}")
print(f"Vocab size: {tokenizer.vocab_size}")
multilingual = ["Hello world", "Hola mundo", "Bonjour le monde"]
for text in multilingual:
ids = tokenizer.encode(text)
print(f"'{text}' -> {len(ids)} tokens")
El vocabulario de 128K de Llama 3 comprime los textos que no están en inglés significativamente mejor que el vocabulario de 50K de GPT-2. Puede verificarlo usted mismo: codifique la misma oración en varios idiomas y cuente los tokens.
Ship It
Esta lección produce outputs/prompt-tokenizer-analyzer.md, un prompt reutilizable que analiza la eficiencia de la tokenización para cualquier combinación de texto y modelo. Aliméntelo con una muestra de texto y le dirá qué tokenizador de modelo lo maneja mejor.
Ejercicios
Modifique el tokenizador BPE para imprimir el vocabulario en cada paso de fusión. Observe cómo "t" + "h" se convierte en "th", luego "th" + "e" se convierte en "the". Siga la pista de cómo se ensamblan las palabras comunes en inglés pieza por pieza.
Agregue tokens especiales (
<pad>,<eos>,<unk>) al tokenizador BPE. Asígneles los IDs 0, 1, 2 y desplace todos los demás tokens en consecuencia. Implemente un paso de pretokenización que divida en espacios en blanco antes de ejecutar BPE.Implemente el criterio de fusión de WordPiece (relación de verosimilitul en lugar de frecuencia). Entrene tanto BPE como WordPiece en el mismo corpus con el mismo número de fusiones. Compare los vocabularios resultantes: ¿cuál produce subpalabras lingüísticamente más significativas?
Construya un benchmark de eficiencia de tokenizadores multilingües. Tome 10 oraciones en inglés, español, chino, coreano y árabe. Tokenice cada una con tiktoken (cl100k_base) y mida el promedio de tokens por carácter. Cuantifique el "impuesto multilingüe" para cada idioma.
Entrene su tokenizador BPE en un corpus más grande (descargue un artículo de Wikipedia). Ajuste el número de fusiones para lograr una relación de compresión dentro del 10% de tiktoken en ese mismo texto. Esto le obligará a comprender la relación entre el tamaño del corpus, el recuento de fusiones y la calidad de la compresión.
Términos clave
| Término | Lo que dice la gente | Lo que realmente significa |
|---|---|---|
| Token | "Una palabra" | Una unidad en el vocabulario del modelo; puede ser un carácter, una subpalabra, una palabra o un bloque de varias palabras |
| BPE | "Algo de compresión" | Codificación por pares de bytes (Byte Pair Encoding): fusiona iterativamente el par de tokens adyacentes más frecuente hasta alcanzar el tamaño de vocabulario objetivo |
| WordPiece | "El tokenizador de BERT" | Similar a BPE, pero las fusiones maximizan la relación de verosimilitud count(AB)/(count(A)*count(B)) en lugar de la frecuencia bruta |
| SentencePiece | "Una biblioteca de tokenizadores" | Un tokenizador agnóstico al idioma que opera sobre Unicode crudo sin pretokenización, admitiendo los algoritmos BPE y Unigram |
| Tamaño del vocabulario | "Cuántas palabras conoce" | El número total de tokens únicos: GPT-2 tiene 50.257, BERT tiene 30.522, Llama 3 tiene 128.256 |
| Fertilidad | "No es un término de tokenización" | Promedio de tokens por palabra: mide la eficiencia del tokenizador en distintos idiomas (1,0 es perfecto, 3,0 significa que el modelo trabaja tres veces más de lo necesario) |
| BPE a nivel de bytes | "El tokenizador de GPT" | BPE que opera sobre bytes crudos (0-255) en lugar de caracteres Unicode, garantizando que no haya tokens desconocidos para ninguna entrada |
| Tabla de fusiones | "El archivo del tokenizador" | Lista ordenada de fusiones de pares aprendidas durante el entrenamiento; esto ES el tokenizador, y el orden importa |
| Pretokenización | "División en espacios" | Reglas aplicadas antes de la tokenización de subpalabras: división por espacios en blanco, separación de dígitos, manejo de puntuación |
| Relación de compresión | "Qué tan eficiente es el tokenizador" | Tokens producidos divididos por los bytes de entrada; un valor menor significa mejor compresión e inferencia más rápida |
Lectura Adicional
- Sennrich et al., 2016 -- "Neural Machine Translation of Rare Words with Subword Units" -- el artículo que introdujo BPE para NLP, convirtiendo un algoritmo de compresión de 1994 en la base de la tokenización moderna
- Kudo & Richardson, 2018 -- "SentencePiece: A simple and language independent subword tokenizer" -- tokenización agnóstica al idioma que hizo prácticos los modelos multilingües
- Repositorio tiktoken de OpenAI -- implementación de producción de BPE en Rust con enlaces para Python, utilizada por GPT-3.5/4/4o
- Documentación de Hugging Face Tokenizers -- entrenamiento de tokenizadores de nivel de producción con el rendimiento de Rust