El tokenizer no es solo "dividir palabras". Es BPE real, tokens especiales, padding, truncation y la attention mask que el modelo necesita para saber qué ignorar.
AutoTokenizer.from_pretrained(). Hoy vemos exactamente qué hace esa línea y por qué el tokenizer es específico de cada modelo.
En el curso del Transformer estudiaste que la tokenización convierte texto en IDs y que cada ID tiene un vector asociado en la tabla de embeddings. La conexión es directa: el ID 1492 se convierte en el vector de la fila 1492 de la matriz E.
Ahora piensa en esto: si cambias el tokenizer (otro vocabulario, otros IDs), el ID 1492 puede corresponder a una palabra completamente diferente. El vector de la fila 1492 de la matriz E que aprendió el modelo ya no tiene nada que ver con lo que llega. El modelo recibiría basura.
bert-base-uncased como modelo,
debes usar bert-base-uncased como tokenizer. HuggingFace te obliga a esto
implícitamente: cuando cargas el modelo con su nombre, el tokenizer del mismo nombre ya
tiene el vocabulario correcto.
Vocabulario de BERT: ID 1492 → token "gato" ID 2103 → token "perro" Vocabulario de LLaMA (diferente): ID 1492 → token "§código" ← completamente distinto ID 2103 → token "##ing" ← completamente distinto Si tokenizas "gato" con BERT (ID 1492) pero pasas ese ID a LLaMA, LLaMA busca en su tabla el embedding de "§código" — información incorrecta. El modelo produce basura.
En el curso del Transformer viste BPE como algoritmo. Ahora lo vemos funcionando de verdad
con un tokenizer de producción. Usaremos bert-base-uncased — uno de los
más usados del mundo y con 30.522 tokens en su vocabulario.
La palabra "uncased" en el nombre significa "sin distinción de mayúsculas" — el modelo convierte todo a minúsculas antes de tokenizar. Lo contrario sería "cased" (distingue "Casa" de "casa").
from transformers import AutoTokenizer
tok = AutoTokenizer.from_pretrained("bert-base-uncased")
# Tokenizar una frase simple
texto = "I love machine learning!"
resultado = tok.tokenize(texto)
print("Tokens:", resultado)
ids = tok.convert_tokens_to_ids(resultado)
print("IDs: ", ids)
Ahora con una palabra más rara — "embeddings" — que BPE dividirá en partes:
print(tok.tokenize("embeddings"))
print(tok.tokenize("unbelievable"))
print(tok.tokenize("supercalifragilístico"))
En BERT, los tokens que empiezan con ## son CONTINUACIONES de una palabra. Es decir, van pegados al token anterior sin espacio. "embeddings" → ['em', '##bed', '##ding', '##s'] Significa: em + bed + ding + s = embedding + s Sin ##: el token empieza una nueva palabra (hay espacio antes). Con ##: el token continúa la palabra anterior (no hay espacio). Cuando reconstruyes el texto: 'em' + '##bed' + '##ding' + '##s' → 'embeddings' ✓
Esta convención (##) es específica de BERT. Otros modelos usan otras convenciones:
GPT-2 usa un espacio al principio del token para indicar inicio de palabra (Ġ), y LLaMA
usa un símbolo diferente. La idea es la misma pero el formato varía.
Cuando tokenizas texto con tok(texto) (en lugar de tok.tokenize(texto)),
el tokenizer añade tokens especiales automáticamente:
# tok(texto) = tokenize + añadir tokens especiales + convertir a IDs
encoded = tok("I love learning")
print(encoded["input_ids"])
# Para ver los tokens como texto (no como IDs):
print(tok.convert_ids_to_tokens(encoded["input_ids"]))
[CLS] — ID 101 — "Classification" — siempre va al principio
El vector del [CLS] al final del modelo captura el "resumen"
de toda la frase. Se usa para clasificación de texto.
[SEP] — ID 102 — "Separator" — siempre va al final (o entre dos frases)
Le dice al modelo dónde termina una frase (o comienza la segunda).
[PAD] — ID 0 — "Padding" — relleno cuando hay textos de distinto largo
Veremos esto en detalle en la siguiente sección.
[UNK] — ID 100 — "Unknown" — para caracteres que no están en el vocabulario
En BPE entrenado con bytes este nunca aparece, pero en BERT sí puede.
[MASK] — ID 103 — "Mask" — usado en el entrenamiento BERT (rellenar huecos)
Cuando ves "el perro [MASK] pescado", el modelo debe predecir "come".
Los tokens especiales varían entre modelos. GPT-2 solo usa <|endoftext|> al
final. LLaMA usa <s> al principio y </s> al final.
El tokenizer de HuggingFace sabe cuáles añadir para cada modelo.
Los modelos procesan textos en lotes — grupos de varios textos a la vez — porque es mucho más eficiente que procesarlos uno a uno. Pero hay un problema: una matriz (el lote) necesita que todos los textos tengan la misma longitud. Y los textos naturales tienen longitudes distintas.
Ambas filas tienen 8 columnas — el modelo puede procesarlas en un lote. Los [PAD] son relleno sin significado.
# Tokenizar dos frases de distinto largo en un lote
frases = [
"I love machine learning!", # larga
"Hi there", # corta
]
encoded = tok(
frases,
padding=True, # añade [PAD] hasta igualar al más largo del lote
truncation=True, # corta si supera max_length
max_length=512, # longitud máxima (BERT acepta hasta 512)
return_tensors="pt", # devuelve tensores de PyTorch (pt = PyTorch)
)
print("input_ids shape:", encoded["input_ids"].shape)
print(encoded["input_ids"])
print(encoded["attention_mask"])
El shape [2, 8] significa: 2 frases, 8 tokens cada una. La primera frase tiene
texto real en las 8 posiciones; la segunda tiene texto real en 4 y relleno en las últimas 4
(ID 0 = [PAD]).
Viste en el output anterior que junto con input_ids siempre aparece
attention_mask. Vamos a entenderla desde cero.
El modelo Transformer calcula atención entre todos los tokens — cada token
"mira" a todos los demás (lo estudiaste en el curso del Transformer). El problema es que los
tokens de padding ([PAD]) no contienen información real: son solo relleno para
igualar los tamaños. Si el modelo les prestara atención, estaría "distrayéndose" con basura.
Frase 1: "I love machine learning!"
input_ids: [101, 1045, 2293, 3698, 4083, 999, 102, 0]
attention_mask: [ 1, 1, 1, 1, 1, 1, 1, 0]
↑
[PAD] — ignorar
Frase 2: "Hi there"
input_ids: [101, 7632, 2045, 102, 0, 0, 0, 0]
attention_mask: [ 1, 1, 1, 1, 0, 0, 0, 0]
↑ ↑ ↑ ↑
cuatro [PAD] — todos ignorados
Resultado: el modelo atiende solo donde hay 1.
Cada fila del lote puede tener distintas longitudes reales de forma eficiente.
return_tensors — en qué formato devuelve los datos
El parámetro return_tensors controla el tipo de objeto que devuelve el tokenizer:
| Valor | Devuelve | Cuándo usarlo |
|---|---|---|
"pt" | Tensores de PyTorch (torch.Tensor) | Siempre que vayas a pasar al modelo de HuggingFace |
"tf" | Tensores de TensorFlow | Si usas TensorFlow en vez de PyTorch |
"np" | Arrays de NumPy | Para procesamiento o visualización |
None (default) | Listas Python | Para depurar o explorar |
# Ejemplo con None — devuelve listas Python (más fácil de leer)
encoded_lista = tok("Hello world", return_tensors=None)
print(encoded_lista)
Ves tres claves: input_ids (los IDs), attention_mask (el filtro de
padding que ya conoces) y token_type_ids — este último es específico de BERT
cuando procesa dos frases a la vez (0 = primera frase, 1 = segunda). Para textos simples
es siempre 0 y la mayoría de los modelos modernos no lo usan.
El tokenizer también sabe ir al revés: de IDs a texto. Esto lo usarás cuando el modelo genere texto (los modelos generativos producen IDs, no texto directamente):
# De texto a IDs (encode)
ids = tok.encode("I love learning")
print("IDs:", ids)
# De IDs a texto (decode)
texto = tok.decode(ids)
print("Texto:", texto)
# Sin tokens especiales
texto_limpio = tok.decode(ids, skip_special_tokens=True)
print("Limpio:", texto_limpio)
skip_special_tokens=True elimina [CLS], [SEP] y [PAD] del resultado final.
En generación de texto siempre querrás esta opción para obtener texto limpio.
Escribe texto y ve cómo lo tokenizaría BERT (simulación con vocabulario reducido):
## en BERT para indicar
continuación). El tokenizer añade tokens especiales automáticamente ([CLS], [SEP]).
padding=True rellena con [PAD] para igualar longitudes en un lote.
truncation=True corta textos demasiado largos. La attention mask
tiene 1 donde hay texto real y 0 donde hay padding — el modelo la usa para ignorar el relleno.
return_tensors="pt" devuelve tensores de PyTorch listos para el modelo.
En la próxima lección entendemos el problema central: por qué el modelo sabe mucho pero no lo que tú necesitas, y la diferencia entre pre-entrenamiento, fine-tuning e instrucción — que es lo que justifica todo el resto del curso.