One-hot encoding y la maldición de la dimensionalidad
22 min de lectura
La lección anterior, sobre vocabulario y frecuencia, dejó el vocabulario completamente definido: una lista cerrada de entradas, construida a partir de las palabras más frecuentes y completada con <UNK> para todo lo que queda fuera. Con eso, ya puede recibir cualquier texto. Solo queda decidir qué vector asignar a cada una de las entradas del vocabulario.
Hay una respuesta inmediata a esa pregunta, y la lección existe porque esa respuesta es correcta: cumple una por una las exigencias que la lección sobre el problema de representar el lenguaje le puso a , y aun así no basta. Tiene nombre —one-hot encoding— y lo que queda por hacer con ella es ponerle precio. Una parte del precio se ve venir, porque los vectores salen enormes, y es precisamente la parte que no importa. La otra aparece al preguntar qué puede aprender un modelo que recibe entradas así, y esa no la arregla ninguna máquina más grande.
Mira colocadas de las dos maneras las tres palabras con las que aquella lección descartó , antes de leer una sola fórmula. Sobre la recta, los índices , y dejan a gato a distancia de casa y a perro a distancia : hay un medio, y alguien lo ocupa. Con un eje por palabra hay tres ejes perpendiculares y cada palabra cae sobre el suyo, en , y . Cuenta cuántos lados distintos tiene el triángulo que forman.
Un eje para cada entrada del vocabulario
Ordena el vocabulario de una vez por todas y escríbelo como . El orden da igual —alfabético, por frecuencia, el que salga— mientras no cambie después; es el determinismo que la lección sobre el problema de representar el lenguaje exigía. Lo que ese orden aporta es una correspondencia: la entrada que ocupa la posición tendrá reservada la coordenada del vector, y ninguna otra entrada la usará. Posiciones del vocabulario y coordenadas del vector son, de aquí en adelante, la misma lista de números de a .
Queda precisar de qué está hecha esa lista, porque el resto de la lección lo da por sabido. Las entradas de son tokens, y qué sean depende de la tokenización que fijó la lección sobre tokenización: palabras enteras con un tokenizador de palabras, trozos de palabra con uno de subpalabras. Nada de lo que sigue cambia por eso, y los ejemplos usan palabras enteras porque se leen mejor.
La representación one-hot asigna a la entrada el vector de coordenadas que vale en la posición y en todas las demás. Coordenada a coordenada, escribiendo para la -ésima coordenada del vector de la entrada :
Es una como la que pedía la lección sobre el problema de representar el lenguaje, con la dimensión fijada por el propio vocabulario: y . Un texto de tokens se convierte entonces en una matriz , una fila por posición.
Ahora la propiedad por la que se elige. Toma dos entradas distintas del vocabulario, en la posición y en la posición , con . Su producto escalar recorre las coordenadas y en cada una encuentra un cero:
porque el único término que podría no anularse exigiría y a la vez. Los vectores son ortogonales, dos a dos, todos.
Y la distancia entre ellos sale de la misma cuenta. La escribimos con doble barra: es la longitud euclídea de , la raíz de la suma de sus coordenadas al cuadrado. La resta vale en la posición , en la y en el resto, de modo que
y por tanto , sean cuales sean y , y sea cual sea el tamaño del vocabulario. Ahí está lo que se buscaba, escrito en una línea: todos los pares de palabras están exactamente a la misma distancia. Ninguna queda entre otras dos, y nada de lo que la representación dice del idioma se puede leer mal, porque no dice nada. La geometría inventada de la lección sobre el problema de representar el lenguaje ha desaparecido.
Por qué las dimensiones son |V| y no |V| − 1
Aquella lección terminaba con una cuenta: puntos mutuamente equidistantes necesitan dimensiones. El mínimo son entonces dimensiones —el triángulo de arriba, con tres palabras, cabe en un plano— y el one-hot gasta una más. No es un despiste, y la diferencia se ve mirando dónde viven estos vectores.
Todos ellos cumplen la misma ecuación, porque cada uno tiene un solo :
Eso es un hiperplano de , y un hiperplano tiene una dimensión menos que el espacio que lo contiene: los vértices viven, de hecho, en dimensiones, que es el mínimo. Lo que compras con la dimensión sobrante es que las coordenadas sean legibles. Dentro del hiperplano serían combinaciones sin significado propio; en , la coordenada responde a «¿es esta la palabra ?», y por eso representar una palabra es mirar una posición en lugar de calcular nada.
Lo que cuesta no afirmar nada
El precio ya estaba escrito en la lección anterior, solo faltaba sustituir. Guardar como una tabla, una fila por entrada del vocabulario y columnas, cuesta números; con eso es
Con el vocabulario de subpalabras de la lección sobre tokenización, entre 30 000 y 50 000 entradas, son entre 900 y 2 500 millones de números: unos 10 GB con cuatro bytes cada uno. Y crece al cuadrado: duplicar el vocabulario cuadruplica la tabla.
Esa cifra pide una corrección inmediata, porque nadie paga esos 10 GB. La tabla del one-hot es la matriz identidad, y una matriz identidad no contiene información: está determinada por su tamaño. Se guarda el índice de cada entrada —un entero— y el vector se fabrica cuando hace falta, o ni siquiera se fabrica. El coste de almacenar la representación desaparece.
Lo que no desaparece es la dimensión. El vector que el sistema recibe tiene coordenadas y solo una es distinta de cero, así que la parte del vector que lleva información, con 50 000 entradas, es
Antes de seguir, un apunte a favor. La lección sobre el problema de representar el lenguaje te pidió que guardaras una observación: allí, añadir agua al vocabulario renumeraba palabras que no tenían nada que ver con ella y dejaba inservible cualquier modelo entrenado antes. El one-hot no tiene ese defecto. Añade una entrada al final y cada vector antiguo conserva sus coordenadas; solo le crece un cero. Es la estabilidad que faltaba. Es real, aunque en la práctica se congela una sola vez —para eso está <UNK>— y la ocasión casi no llega.
Por qué la ortogonalidad impide aprender
Queda la cuenta que da título a la lección, y no es la de la memoria. Un sistema que lee una ventana de tokens seguidos recibe los vectores uno detrás de otro, coordenadas en total. ¿Cuántas entradas distintas puede recibir? Cada una de las posiciones admite cualquiera de las entradas del vocabulario, con independencia de las demás, así que
Pon números de los pequeños: y una ventana de tokens, un cuarto de la frase de veinte tokens de la lección sobre vocabulario y frecuencia. Son entradas posibles. Un corpus enorme tiene del orden de tokens, y por tanto como mucho ventanas distintas, de modo que el modelo llega a ver
del espacio de entradas. Cuatro diezmillonésimas de millonésima. Y no hay corpus que arregle eso: el exponente está en , así que mirar un token más de contexto multiplica el espacio por mientras el corpus sigue donde estaba. Esta es la maldición de la dimensionalidad: el número de configuraciones distintas crece exponencialmente con la dimensión de la entrada, y cualquier cantidad de datos cubre una fracción que tiende a cero.
Dicho así suena a condena general, y no lo es. Los modelos trabajan con entradas de dimensión enorme y aprenden de todos modos, porque en esas entradas hay vecindad: dos fotografías que difieren en un píxel son parecidas, lo aprendido sobre una vale para la otra, y por eso una fracción minúscula del espacio alcanza. Ese es el argumento que salva a cualquier representación de dimensión alta, y es el que el one-hot no puede usar. La equidistancia desarrollada arriba dice, con otras palabras, que aquí no hay vecinos: la ventana el niño juega en la plaza y la ventana la niña juega en la plaza están a la misma distancia entre sí que de cualquier otra.
De modo que la virtud es el defecto, y es la misma propiedad vista dos veces. Pedimos una representación que no afirmara parecidos falsos y salió una que no afirma parecidos, punto. Cada una de esas entradas hay que aprenderla por separado, porque ninguna se parece a ninguna, y solo se van a ver .
La matriz one-hot en NumPy, y lo que pesaría
La celda construye el one-hot de un vocabulario diminuto en español y comprueba sobre él las tres afirmaciones de arriba: que los vectores son ortogonales, que todas las distancias valen y qué fracción de cada vector es distinta de cero. Después hace las cuentas de tamaño sin reservar memoria, que es la única forma de hacerlas: una tabla de 50 000 por 50 000 no cabe en el navegador, y ese es parte del resultado.
V = ["<UNK>", "de", "la", "niño", "niña", "escuela", "programación", "río"]
n = len(V)
O = np.eye(n, dtype=int) # fila i = vector one-hot de V[i]
print("|V| =", n, " -> O tiene forma", O.shape)
print(O)
print()
# Ortogonalidad: el producto de la matriz por su traspuesta es la identidad,
# es decir, todo producto escalar entre vectores distintos vale 0.
print("O @ O.T == identidad:", np.array_equal(O @ O.T, np.eye(n, dtype=int)))
# Todas las distancias entre pares valen lo mismo, y ese mismo es raíz de 2.
distancias = set()
for i in range(n):
for j in range(i + 1, n):
distancias.add(round(float(np.linalg.norm(O[i] - O[j])), 6))
print("distancias distintas entre pares:", distancias, " | raiz(2) =", round(float(np.sqrt(2)), 6))
print("no nulos por vector:", int((O[3] != 0).sum()), "de", n,
"->", round(100 / n, 3), "%")
print()
# La tabla explícita: |V| x |V| números. Calculada, NO reservada.
print(" |V| números GB (4 bytes) % no nulo")
for tam in [8, 1000, 30000, 50000]:
numeros = tam * tam
gb = numeros * 4 / 1024**3
print(str(tam).rjust(6), str(numeros).rjust(15), ("%.2f" % gb).rjust(13),
("%.5f" % (100 / tam)).rjust(11))
print()
# El espacio de entradas de una ventana de n tokens, contra un corpus grande.
T = 10**10 # tokens de un corpus enorme
tam = 30000
for ventana in [1, 2, 3, 5]:
posibles = tam ** ventana
cubierta = min(T, posibles) / posibles
print("n =", ventana, "->", ("%.1e" % posibles).rjust(8),
"ventanas posibles | fracción que cubre el corpus:", "%.1e" % cubierta)
La primera ejecución descarga el intérprete de Python (~15 MB). Después queda en la caché del navegador.
Las tres comprobaciones salen exactas y una conviene mirarla dos veces: el conjunto de distancias entre pares tiene un solo elemento. No es que se parezcan, es que no hay más de un valor. Con ocho palabras se ve de un vistazo; con 50 000 sigue siendo el problema.
La tabla de tamaños tiene una fila que sorprende y otra que no. La que no: 50 000 entradas piden 9.31 GB, y por eso nadie guarda esta tabla. La que sí: la columna de la derecha, el porcentaje de coordenadas no nulas, que ya con 30 000 entradas es 0.003 %. Y las últimas líneas son la maldición de la dimensionalidad en cuatro renglones: con una o dos palabras de contexto el corpus cubre el espacio entero, con tres cubre ya solo el 0.04 %, y con cinco la fracción se escribe con doce ceros detrás del punto. El salto ocurre entre la segunda palabra de contexto y la tercera, no en el infinito.
Una prueba de treinta segundos. Cambia V por un vocabulario de dos palabras y vuelve a ejecutar: la
distancia entre pares sigue valiendo , exactamente igual que con ocho. Ahí ves que
no entra en esa cuenta.
Comprueba tu intuición
Cuatro preguntas sobre lo que acabas de obtener: cuánto mide la distancia entre dos palabras, qué arregla el one-hot respecto de la lección sobre el problema de representar el lenguaje, qué cuesta la tabla y qué pasa con dos palabras que se parecen.
Tu vocabulario tiene entradas y representas cada una con su vector one-hot. ¿Cuánto vale la distancia euclídea entre las de dos entradas distintas y ?
Se acepta un margen de ±0.01.
Comparas el one-hot con la representación por índices de la lección sobre el problema de representar el lenguaje. ¿En qué mejora? Marca todo lo que valga.
Marca todas las opciones correctas. Se corrige todo o nada: no hay puntuación parcial.
Con , ¿cuántos números tendría la tabla que guarda explícitamente, una fila por entrada del vocabulario?
Un modelo entrenado con vectores one-hot ha visto muchas veces el niño juega y ninguna la niña juega. Lo aprendido sobre niño le ayuda algo con niña, porque las dos palabras se parecen.
El one-hot cumple las tres exigencias, es estable, y su geometría no afirma nada falso sobre el idioma. También es un callejón sin salida, y por el mismo motivo por el que cumple: una representación que no relaciona ninguna palabra con ninguna otra obliga a aprenderlo todo por separado, y no hay corpus para eso. Para salir hacen falta dos cosas a la vez, y son las dos que faltan aquí: muchas menos coordenadas que , y coordenadas que signifiquen algo, de modo que dos palabras parecidas acaben cerca.
La segunda es la difícil y ocupa el resto del bloque. Pero antes hay un cambio de pregunta que sale casi gratis desde aquí, y conviene hacerlo ahora, con los vectores one-hot todavía a la vista. Hasta ahora representa una palabra, y lo que un sistema suele necesitar es el vector de un documento entero: una reseña, un correo, una noticia. La forma más directa de obtenerlo es sumar los vectores one-hot de todos sus tokens, , donde recorre las posiciones del documento y no las entradas del vocabulario. Cada coordenada pasa entonces a contar cuántas veces sale su palabra. Eso ya no es un vector de ceros y unos, y ya no es tan mudo: la coordenada de gol distingue una crónica deportiva de una receta. Sigue teniendo dimensiones y sigue sin saber en qué orden iban las palabras. Es la bolsa de palabras, y con la corrección que la hace útil —TF-IDF— es la lección sobre la bolsa de palabras y TF-IDF.
Para profundizar1 fuente · 1 paper
De dónde sale lo de esta lección, y dónde seguir si quieres más. Nada de aquí hace falta para continuar el curso.
- A Neural Probabilistic Language Model
Plantea la maldición de la dimensionalidad para el lenguaje y propone salir de ella con vectores densos: el argumento que acabas de desarrollar. Va más allá y monta un modelo de lenguaje entero, cosa ya del bloque 2.