PASO 1 / 10

Tokenización

Cómo un pedazo de texto ("Hola mundo") se convierte en una lista de números que el modelo puede procesar.

🎯 De un vistazo

La tokenización es el primer paso de todos: convierte el texto crudo en algo que el modelo pueda recibir.

PROPÓSITO

¿Para qué sirve?

Convertir texto ("Hola mundo") en una lista de números enteros (IDs) que el modelo pueda procesar. Es el "traductor de entrada" del Transformer.

APORTE

¿Qué aporta al modelo?

Define el vocabulario del modelo: el conjunto de "piezas" (tokens) con las que va a trabajar. Cada pieza recibe un ID único y estable.

NECESIDAD

¿Por qué es indispensable?

Las redes neuronales SOLO operan con números. No pueden recibir letras ni palabras directamente. Sin tokenización, no hay forma de meterle texto al modelo.

↓ A continuación, el detalle con ejemplos y visualizaciones ↓

¿Por qué necesitamos tokenizar?

Las redes neuronales solo entienden números — específicamente, números en arrays (vectores y matrices). No saben qué es la letra "A" ni qué es la palabra "perro". Antes de poder darle texto a un Transformer, tenemos que traducirlo a números.

Analogía de programador: Imagina que tienes una función generar_texto(input) pero input debe ser number[]. No puedes pasarle un string directo. Necesitas un convertidor: string → number[]. Ese convertidor es el tokenizer.

Un token es un pedacito de texto al que le asignamos un número (su ID). El tokenizer mantiene un vocabulario: una tabla que dice "este pedacito ↔ este número".

Intento 1: tokenizar por caracteres

La idea más simple: cada carácter es un token. "Hola" → ['H', 'o', 'l', 'a'] → [4 IDs].

🎮 Pruébalo

📜 El código que lo hace

function tokenizeChars(text) {
  // 1. Construir vocabulario: un Map de char → ID
  const vocab = new Map();
  let nextId = 0;

  // 2. Recorrer cada carácter
  const tokens = [];
  for (const ch of text) {
    if (!vocab.has(ch)) {
      vocab.set(ch, nextId++);
    }
    tokens.push({ text: ch, id: vocab.get(ch) });
  }
  return tokens;
}
⚠️ El problema: con caracteres, la frase "Hola mundo" tiene 10 tokens. Una novela tendría millones. El modelo gastaría TODO su esfuerzo procesando letras individuales en lugar de significados. Además, "perro" y "perros" se ven como cadenas distintas de letras sin relación obvia entre sí.

Intento 2: tokenizar por palabras

Solución obvia: cada palabra es un token. "Hola mundo" → ['Hola', 'mundo'] → 2 tokens.

🎮 Pruébalo

📜 El código

function tokenizeWords(text) {
  const vocab = new Map();
  let nextId = 0;

  // split por espacios
  const words = text.split(/\s+/);

  const tokens = [];
  for (const w of words) {
    if (!vocab.has(w)) vocab.set(w, nextId++);
    tokens.push({ text: w, id: vocab.get(w) });
  }
  return tokens;
}
⚠️ Nuevos problemas:
  • Vocabulario gigante: el español tiene cientos de miles de palabras (con conjugaciones, plurales, etc.). Cada palabra única necesita su propio ID.
  • Palabras nuevas: ¿qué pasa si aparece "criptomoneda" y no está en el vocabulario? El modelo se rompe.
  • No comparte info: "correr", "corriendo", "corrió" son 3 IDs distintos sin relación.

Intento 3: BPE (Byte Pair Encoding) — el que usan los LLMs reales

GPT, Claude, LLaMA y prácticamente todos los LLMs modernos usan una variante de BPE. La idea es brillante: encontrar un punto medio entre caracteres y palabras.

La intuición

BPE se construye aprendiendo del texto. El algoritmo:

  1. Empieza tratando cada carácter como un token (vocabulario inicial = caracteres únicos).
  2. Mira qué par de tokens adyacentes aparece más veces en el corpus.
  3. Junta ese par en un nuevo token y lo agrega al vocabulario.
  4. Repite hasta tener un vocabulario del tamaño deseado (típicamente 30k–100k tokens).
Resultado: palabras comunes ("el", "que", "casa") terminan siendo un solo token. Palabras raras se rompen en pedazos ("criptomoneda" → "cripto" + "moneda"). El modelo nunca se queda sin saber qué hacer, porque en el peor caso puede caer a caracteres.

Mini-BPE en vivo

Vamos a entrenar un BPE con un corpus chiquito y ver cómo aprende las fusiones (merges) paso a paso.

🎮 Entrena tu propio BPE

🔥 Merges aprendidos (en orden)

📜 El algoritmo de entrenamiento

function trainBPE(corpus, numMerges) {
  // 1. Empezamos: cada palabra = lista de caracteres
  //    "gato" → ["g","a","t","o"]
  let words = corpus.split(/\s+/)
    .map(w => w.split(""));

  const merges = [];

  for (let i = 0; i < numMerges; i++) {
    // 2. Contar pares adyacentes en todo el corpus
    const pairs = new Map();
    for (const w of words) {
      for (let j=0; j<w.length-1; j++) {
        const pair = w[j] + "|" + w[j+1];
        pairs.set(pair, (pairs.get(pair)||0)+1);
      }
    }

    // 3. Encontrar el par más frecuente
    const best = [...pairs.entries()]
      .sort((a,b) => b[1]-a[1])[0];

    // 4. Fusionar ese par en todas las palabras
    const [a,b] = best[0].split("|");
    words = words.map(w => mergePair(w, a, b));
    merges.push(a + b);
  }
  return merges;
}

Ahora usa tu BPE entrenado para tokenizar

🎮 Tokeniza con el BPE recién entrenado

Escribe algo (puede ser texto del corpus o palabras nuevas):

📜 Aplicar BPE a texto nuevo

function applyBPE(text, merges) {
  const words = text.split(/\s+/);
  const result = [];

  for (let w of words) {
    // Empezar como lista de caracteres
    let tokens = w.split("");

    // Aplicar cada merge aprendido, en orden
    for (const merge of merges) {
      tokens = applyMerge(tokens, merge);
    }
    result.push(...tokens);
  }
  return result;
}

📚 Vocabulario final

🎯 Lo que ocurre en un LLM real

Click para ver detalles del tokenizer de GPT/Claude

Los tokenizers reales (como tiktoken de OpenAI o el de Claude) son BPE entrenados sobre terabytes de texto de internet. Tienen vocabularios de ~50,000 a 200,000 tokens. Algunos detalles extra:

  • Operan sobre bytes, no caracteres. Esto permite manejar cualquier texto del mundo (emojis, chino, árabe) sin romperse.
  • Los espacios cuentan: " hola" (con espacio adelante) es un token distinto a "hola". Esto evita ambigüedad.
  • Tokens especiales: incluyen marcadores como <|endoftext|>, <|user|>, <|assistant|> que el modelo usa para estructurar conversaciones.
  • Ratio típico: ~1 token ≈ 4 caracteres en inglés, ~1 token ≈ 2-3 caracteres en español (¡el español es menos eficiente porque BPE fue entrenado mayoritariamente con inglés!).

Un dato curioso: cuando pagas por la API de un LLM, pagas por token. Por eso el español sale más caro que el inglés para la misma cantidad de información.

✅ Resumen: lo que entra al siguiente paso

Al final de la tokenización, lo que tenemos es:

// Input original:
"el gato come"
// Después del tokenizer (BPE):
[847, 2103, 1492]
// Solo números. Eso es lo que recibe el modelo.

En el Paso 2: Embeddings, esos números (que son solo IDs, sin significado matemático) se convertirán en vectores de cientos de dimensiones que SÍ capturan el significado de cada token. Ahí empieza lo interesante.