Cómo lee un texto un modelo de lenguaje
La tokenización corta un texto en trozos llamados tokens. La codificación por pares de bytes (BPE, byte-pair encoding en inglés) es la forma habitual de elegir esos trozos. Cada frase que escribes a un asistente conversacional se corta así antes de que el modelo la vea.
Esos trozos deciden cuánto texto cabe, qué pasa con las palabras raras y qué sabe deletrear el modelo.
Al final de esta lección
Nota
Puedo cortar un texto corto en tokens fusionando pares.
Palabras clave
| Palabra | Qué significa | Ejemplo |
|---|---|---|
| Token | un trozo de texto que el modelo trata de una vez | ola |
| Vocabulario | la lista de todos los tokens que conoce el modelo | |
| Par | dos tokens que están uno al lado del otro | l a |
| Fusión | pegar un par para formar un token nuevo | |
| Codificación por pares | crear tokens fusionando una y otra vez el par más frecuente | |
| Identificador de token | el número que sustituye a un token |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
Palabras o caracteres: dos cortes sencillos
En el procesamiento del lenguaje natural se corta el texto en palabras. Un modelo de lenguaje necesita un vocabulario fijo, y las palabras lo complican. Una palabra nueva, como un nombre propio o una errata, es sencillamente desconocida.
Los caracteres resuelven ese problema, porque unas pocas decenas bastan para escribir cualquier palabra. Pero una frase se vuelve muy larga, y una entrada larga cuesta más cálculo.
Ejemplo resuelto: un texto, dos cortes
| Corte | Tokens en «ola olas las olas ola» | Vocabulario | ¿Puede escribir «sola»? |
|---|---|---|---|
| Palabras | 5 | 3: ola, olas, las | No, la palabra es desconocida |
| Caracteres | 17 | 4: o, l, a, s | Sí, con s, o, l y a |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
Contar las dos clases de tokens
text = "ola olas las olas ola"
words = text.split()
chars = []
for word in words:
for c in word:
chars.append(c)
word_vocab = []
for word in words:
if word not in word_vocab:
word_vocab.append(word)
char_vocab = []
for c in chars:
if c not in char_vocab:
char_vocab.append(c)
print("tokens de palabra:", len(words), "vocabulario:", word_vocab)
print("tokens de carácter:", len(chars), "vocabulario:", char_vocab)
print("¿Es sola una palabra conocida?", "sola" in word_vocab)
# Añade la palabra sola al texto y pulsa Ejecutar.
# ¿Qué vocabulario crece, y cuánto?
Fusionar el par más frecuente
La codificación por pares de bytes se sitúa entre las palabras y los caracteres. Se empieza por los caracteres. Se cuenta cada par de tokens vecinos dentro de una palabra. El par más frecuente se fusiona en un token nuevo, que entra en el vocabulario.
Luego se vuelve a contar y a fusionar. Cada fusión acorta las palabras frecuentes. Las palabras raras se quedan en trozos pequeños, así que nada es nunca desconocido.
Ejemplo resuelto: contar los pares
| Par | Dónde aparece | Cuenta |
|---|---|---|
| l a | ola, olas, las, olas, ola | 5 |
| o l | ola, olas, olas, ola | 4 |
| a s | olas, las, olas | 3 |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
Contar los pares en el laboratorio
docker exec jupyter python3 -c "from collections import Counter; words = 'ola olas las olas ola'.split(); print(Counter(w[i:i + 2] for w in words for i in range(len(w) - 1)).most_common(3))"Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Ejemplo resuelto: tres fusiones
| Fusión | Token nuevo | ola | olas | las |
|---|---|---|---|---|
| Inicio | ninguno | o l a | o l a s | l a s |
| 1: l a (5) | la | o la | o la s | la s |
| 2: o la (4) | ola | ola | ola s | la s |
| 3: ola s (2) | olas | ola | olas | la s |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
Hacer la primera fusión
text = "ola olas las olas ola"
words = []
for word in text.split():
words.append(list(word))
left = "l"
right = "a"
merged = []
for tokens in words:
out = []
i = 0
while i < len(tokens):
# Pegamos el par en un solo token y saltamos los dos.
if i + 1 < len(tokens) and tokens[i] == left and tokens[i + 1] == right:
out.append(left + right)
i = i + 2
else:
out.append(tokens[i])
i = i + 1
merged.append(out)
print(merged)
# Cambia el par por "o" y "l" y pulsa Ejecutar.
# ¿Cuántos tokens tiene ahora la palabra las?
Un error frecuente
Advertencia
Mucha gente piensa que un token es una palabra. Con la codificación por pares, una palabra frecuente es un token, pero una palabra rara son varios. Por eso cuenta tokens, no palabras, cuando preguntes cuánto texto cabe en un modelo.
Cada token se convierte en un número
El modelo nunca ve letras. Cada token del vocabulario recibe un identificador, su lugar en la lista. Tras las tres fusiones, el vocabulario es o, l, a, s, la, ola y olas, numerados del 0 al 6.
Así, «olas las ola» queda en olas, la, s, ola, es decir, los identificadores 6, 4, 3, 5. Son cuatro tokens en lugar de diez caracteres.
Cambiar los tokens por identificadores
docker exec jupyter python3 -c "vocab = ['o', 'l', 'a', 's', 'la', 'ola', 'olas']; print([vocab.index(t) for t in ['olas', 'la', 's', 'ola']])"Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.