Apuntes / Procesamiento de Lenguaje Natural / Apunte 7 de 8
Read in EnglishSubwords y fastText
Cómo una palabra para la que ninguna tabla tiene vector recibe uno a partir de sus subwords, tramos de tres a seis caracteres que comparte con otras palabras, cómo los entrena fastText, con qué se entrenaron los vectores que se descargan, y cuándo compartir letras es compartir significado.
El apunte de los vectores de palabras le dio un vector a cada una de las 5259 palabras que el Quijote usa al menos cinco veces; las otras 17 346 palabras distintas, tres cuartas partes, se quedaron sin ninguno. El apunte de las leyes del texto mostró que cada nuevo tramo de texto trae palabras nunca vistas, y ni siquiera la tabla de dos millones de palabras de fastText, entrenada con la web, tiene 2845 de las palabras que deja fuera la tabla del libro, ni en minúsculas ni con mayúscula. Cuando Sancho, creyendo muerto a su amo, lo llora como «acometedor de peligros, sufridor de afrentas» (capítulo LII de la primera parte), ninguna de las dos tablas tiene una fila para acometedor: el libro usa la palabra solo dos veces, y la tabla de la web no la tiene. Quien la lee por primera vez la entiende igual, porque está hecha de pedazos que conoce. Este apunte arma el vector de una palabra con sus pedazos, tramos de unos pocos caracteres que comparten otras palabras, para que hasta una palabra que ninguna tabla tiene reciba uno; y muestra el precio: palabras que solo comparten una terminación quedan juntas.
-
Una palabra que ninguna tabla tiene
-
Un vector con sus pedazos
-
La grafía puede engañar
La sección 1 cuenta las palabras sin vector; la sección 2 corta una palabra en subwords, y la sección 3 arma su vector con los de ellos; la sección 4 muestra por qué unos pocos símbolos escriben cualquier palabra, y cómo fastText le pone tope a las filas de sus subwords; la sección 5 los entrena; la sección 6 es fastText y sus tablas publicadas; y la sección 7 muestra cuándo un subword compartido es un significado compartido.
1Palabras sin vector
Una tabla de vectores ya entrenada es fija: una palabra que no guardó queda fuera del vocabulario (OOV) y no tiene fila, así que tampoco vector. Un clasificador que la lee no le da nada, como el modelo lineal del apunte de los modelos lineales le daba peso 0 a hideperro. Estas palabras son de dos tipos: las que la tabla dejó fuera, que el texto usó de una a cuatro veces, y las que nunca se vieron. Más texto baja su parte sin acabarla nunca, como mostró el apunte de las leyes del texto: la cola de palabras raras no se agota. En la figura, entrena la tabla con el libro hasta el punto que arrastras, guardando las palabras usadas al menos cinco veces, y mira cuántos de los tokens de la décima siguiente del libro quedan sin vector.
Arrastra el punto rojo a lo largo del libro, o muévelo con las flechas del teclado.
Entrenada con la primera parte, la tabla no tiene vector para el 14.3% de los tokens de la segunda parte ni para el 82.3% de sus palabras distintas; aun guardando todas las palabras, el 6.8% de los tokens de la segunda parte son nuevos. Entre las palabras que le faltan hay otras formas de palabras que tienen vector, como dueñas y leones, verbos con un pronombre pegado, como preguntóle y púsose, nombres, y palabras que la primera parte nunca necesitó, como duquesa y vuesa; son más largas que las palabras con vector, 7.9 letras contra 6.4. De las 17 346 palabras que deja fuera la tabla del libro, a la de fastText le faltan 2845, ni en minúsculas ni con mayúscula: nombres como caraculiambro y malindrania, verbos con pronombre pegado como desvelábase y admiráronse, y grafías antiguas como quitalle y rescibió. Las otras formas de palabras conocidas son las que los pedazos de palabras pueden rescatar.
2Los subwords de una palabra
¿De qué podría estar hecha una palabra sin vector? Marca su comienzo y su final con < y >, y desliza por ella una ventana de n caracteres de ancho, de a un carácter, como el apunte de word2vec deslizaba una ventana por un texto. Cada tramo de n caracteres seguidos es un n-grama de caracteres (char n-grama), o subword; los pedazos de palabras del apunte anterior son estos subwords. Las marcas de borde distinguen un tramo del final de una palabra, como ino>, de las mismas letras dentro de una palabra. fastText toma todos los n de 3 a 6, y una palabra que tiene fila propia, una usada al menos cinco veces, cuenta esa fila como una más:
con o misma agregada cuando tiene fila propia. Una palabra de |o| caracteres tiene |o| + 3 − n tramos de largo n, así que 4|o| − 6 en total cuando |o| es al menos 3. En la figura, elige una palabra y un rango de largos.
Palabra
Haz clic en una palabra, o recorre las palabras con las flechas del teclado, y elige los largos.
Las 17 346 palabras sin vector tienen el 71% de sus subwords en palabras que sí tienen: el 95% de sus subwords de 3 caracteres, pero solo el 36% de los de 6. Los tramos cortos se comparten; los largos son propios de cada palabra. molinillo, que el libro nunca usa, comparte 10 de sus 30 subwords con molino, y 9, como linil e inillo, no están en ninguna palabra con vector.
3Una palabra como la suma de sus subwords
Cada subword recibe un vector propio, vg ∈ ℝd, una fila de la tabla, y el vector de una palabra suma los vectores de sus subwords:
Una palabra nunca vista tiene de todos modos subwords vistos dentro de otras palabras, así que recibe un vector, cerca de las palabras que los comparten. Es el pooling del apunte de los vectores de palabras, sobre los subwords de una palabra en vez de las palabras de un texto. El programa de fastText toma la media en vez de la suma, dividiendo por el número de filas, lo que, como mostró ese apunte, no cambia ninguna dirección ni ningún coseno; las figuras siguen al programa. La figura usa vectores entrenados con el libro y con subwords, como explica la sección 5. Elige una palabra y compara sus palabras más cercanas con las del apunte de los vectores de palabras, que solo guardaba palabras enteras.
Palabra
Haz clic en una palabra, o recorre las palabras con las flechas del teclado, y elige los vectores.
Una fila que ninguna palabra con vector usa conserva su comienzo al azar, tan corto que casi no suma nada: un largo de unos 0.06, contra una mediana de 2.6 para el vector de una palabra. Para una palabra con fila propia, esa fila es una entre muchas: la de escudero es 1 de sus 27, y sin ella el vector conservaría un coseno de 0.999 con el que tiene. Así cambian también las palabras más cercanas de una palabra frecuente, hacia palabras escritas como ella: escuderos y majadero para escudero, donde las palabras enteras daban asendereado.
4Pocos símbolos, todas las palabras
¿No serán nuevos también los subwords de una palabra nueva, y cuántas filas hacen falta? Un conjunto fijo de símbolos compone todas las palabras: esto es la composicionalidad. El libro escribe sus palabras con 35 caracteres (las 25 letras de la a a la z salvo la k, más la ñ y 9 vocales con tilde, acento grave o diéresis), 37 con las dos marcas, así que puede haber a lo más
subwords de 3 caracteres, donde |Σ| es el número de símbolos; solo 45 360 pueden darse, porque una marca solo abre o cierra un tramo. Los subwords de 3 caracteres crecen cada vez más despacio y quedan muy por debajo de ese techo, 4957 tras el libro entero, mientras siguen llegando palabras nuevas. Los subwords de hasta 6 caracteres siguen creciendo, así que fastText le pone un tope a la tabla: una función de hash convierte cada subword en un número, cuyo resto al dividir por 2 000 000 elige su fila, y dos subwords pueden entonces compartir una. Las palabras con fila guardan además sus filas propias, así que 𝒢o lista filas entre G = V + 2 000 000, donde V es el número de palabras con fila propia, 5259 en el libro. En la figura, arrastra el punto rojo a lo largo del texto leído; el anillo marca diez veces más texto.
Arrastra el punto rojo a lo largo del texto leído, o muévelo con las flechas del teclado.
La ley de Heaps del apunte de las leyes del texto predice unas 4.2 veces las palabras para diez veces el texto, y el libro da 3.9. Los 36 373 subwords de las palabras con vector caen en 36 046 filas, así que unos cientos de filas guardan dos subwords, y entrenar con el libro solo alcanza esas filas de los 2 000 000.
5Entrenar con subwords
¿De dónde salen los vectores de los subwords? Del entrenamiento del apunte de word2vec, con un cambio: el objetivo entra por sus subwords. Los pares (o, c) son los de ese apunte, pero el objetivo ya no es un vector one-hot: es un vector multi-hot, con un 1 por cada uno de sus subwords,
donde eg tiene un 1 en la fila g y 0 en todas las demás (no es el embedding e(w) del apunte de los vectores de palabras), y un subword repetido en una palabra, o dos subwords que comparten fila, dan un 2. Ahora Win ∈ ℝG×d mientras que Wout ∈ ℝd×V sigue igual, y también z = WoutTvo y y = softmax(z), que el entrenamiento reemplaza por el muestreo negativo. La primera capa sigue seleccionando filas de Win y sumándolas, como ese apunte dijo que hace WinTx, ponderadas por x. Un producto punto es lineal, así que el puntaje de un par es la suma de los puntajes de sus subwords:
y la pérdida es la entropía cruzada binaria de ese apunte, con h = σ(s(o, c)). La parte del gradiente de cada fila es (h − y)uc, la misma para todos los subwords del objetivo: un paso mueve todas las filas que usa el objetivo, así que una palabra que comparte algunas de esas filas se mueve sin que la entrenen. Solo se divide el lado del objetivo. Sus filas son la tabla que se guarda, como el programa original de word2vec guardaba solo Win, así que deben servir para palabras nunca vistas; Wout guarda una columna por palabra y no interviene en los vectores de palabras.
La figura sigue un ejemplo diseñado con dos números por vector, y solo con subwords de 3 caracteres, para que sea pequeño: el objetivo gigantes, con sus ocho subwords y su fila propia; el par (gigantes, molinos) del apunte de word2vec, molinos en verde, y una palabra sorteada, atrevió, en tinta; y giganta, que no está en ningún par ni tiene fila propia, y comparte cinco de sus siete subwords con gigantes. El vector del objetivo es la media de sus filas, como lo calcula fastText, y cada fila da el paso entero. Da pasos de descenso de gradiente con η = 0.4.
Siguiente da un paso de descenso de gradiente, Atrás vuelve. Teclas: → y ←.
El modelo del libro es el entrenamiento del apunte de los vectores de palabras (d = 100, un radio sorteado hasta m = 5, k = 5 palabras sorteadas con β = ¾, 20 lecturas, una frecuencia mínima de 5, cinco entrenamientos), con cada objetivo entrando por sus subwords de 3 a 6 caracteres y su fila propia, en 2 000 000 filas; con los subwords apagados, da exactamente los vectores de ese apunte. Las figuras y los números citados muestran el primer entrenamiento, y el texto solo afirma lo que se cumplió en al menos cuatro de los cinco. En nuestro programa tardó unas 2.6 veces más (98 segundos contra 38 con el libro en español); el artículo de fastText reporta que es 1.5 veces más lento que el skip-gram.
6fastText y los vectores que se descargan
fastText es el programa de Bojanowski, Grave, Joulin y Mikolov («Enriching Word Vectors with Subword Information», 2017): el skip-gram de las secciones 2 a 5. Su artículo lo entrenó con Wikipedia en nueve idiomas, con d = 300, 5 negativos sorteados por la raíz cuadrada de las frecuencias (β = ½), un radio sorteado de 1 a 5 y 5 lecturas del texto. Entre los vectores que se publicaron después están 294 idiomas entrenados con Wikipedia con ese skip-gram, y 157 idiomas entrenados con Common Crawl y Wikipedia (Grave et al. 2018), las tablas del apunte de los vectores de palabras. Esas son CBOW con pesos por posición, solo con subwords de 5 caracteres, un radio sorteado de 1 a 5, 10 negativos y d = 300: cada palabra de alrededor entra como la media de sus filas, ponderada número por número por un vector aprendido para su lugar en la ventana. En CBOW las palabras de alrededor entran por sus subwords y la palabra que se predice queda entera: en los dos modelos, el lado que entra por pedazos es la entrada, cuyas filas se guardan. En la figura, elige un objetivo en la línea de Sancho del apunte de word2vec, con radio m = 2, y un modelo.
Haz clic en una palabra de la línea para que sea el objetivo, o recorre la línea con las flechas del teclado, y elige un modelo.
Las tablas de la web sortean un radio de hasta 5, no de 2 como la figura. Su archivo .vec guarda una fila terminada por palabra, la fila propia de la palabra promediada con las de sus subwords, así que no puede armar una palabra nueva; su archivo .bin, de unos 7 GB, guarda las filas de los subwords y sí puede. Con subwords de 5 caracteres solamente, una palabra de una o dos letras que no está en la tabla no tiene ningún subword y recibe el vector cero. Como los vectores del libro, estos tienen un vector por grafía, sea cual sea la oración. La misma biblioteca también clasifica textos (Joulin et al. 2017), y los mil millones de palabras en menos de diez minutos que tanto se citan son la velocidad de ese clasificador.
7Lo que los subwords aciertan y lo que no
¿Estar cerca en la grafía es estar cerca en el significado? El corte no ve el significado: cuando los subwords compartidos son el lexema, el parecido es real, y cuando son solo la terminación, es grafía. Por la misma linealidad de la sección 5, el coseno de una palabra con otra se divide en una parte por subword:
(con la media del programa, el mismo valor). Así, el coseno tiende a subir con el número de subwords que comparten las dos palabras, |𝒢o ∩ 𝒢o′|. Si el vector de cada subword tuviera el mismo largo y formara ángulo recto con los demás, el coseno sería la parte en común, |𝒢o ∩ 𝒢o′| / √(|𝒢o| |𝒢o′|); las filas entrenadas no cumplen ninguna de las dos cosas, así que la cuenta es una tendencia, no una regla. Elige una palabra sin vector y compara su palabra más cercana que comparte su inicio con su palabra más cercana que comparte su final.
Palabra
Haz clic en una palabra, o recorre las palabras con las flechas del teclado.
Los subwords aciertan con las formas. De 734 palabras cuya forma con una s o una es agregada también tiene vector (plurales como caballeros, pero también formas verbales como tienes), los vectores de palabras enteras del apunte de los vectores de palabras ponen esa forma, en la mediana, en el puesto 1041 entre las palabras más cercanas a la palabra, y primera en ninguna; con subwords queda, en la mediana, quinta, y primera en el 18%. galeotes pasa del puesto 3330 al primero para galeote. Las analogías de forma ganan y las de significado pierden: hijo : hija :: hermano : hermana pasa del puesto 106 al primero en los cinco entrenamientos, mientras que rey : reino :: gobernador : ínsula, primera con las palabras enteras, cae al segundo puesto o al cuarto, detrás de gobernar. El artículo de fastText encontró la misma división entre analogías de forma y de significado.
Se equivocan con el significado donde la grafía engaña. Las palabras frecuentes se desvían hacia palabras escritas como ellas: de las 10 palabras más cercanas a cada una de las 883 palabras más frecuentes del libro que tienen cuatro letras o más, 4.1 comparten con ella un tramo de 4 caracteres, contra 0.23 con palabras enteras. tantos queda ahora junto a santos y apenas junto a penas, en cuatro de los cinco entrenamientos, donde el apunte de los vectores de palabras ponía apenas junto a hubo; conveniente queda junto a inconveniente en los cinco, pero por su lexema compartido, un parentesco real entre opuestos. Con mucho más texto los accidentes se desvanecen: en la tabla de la web, molinillo tiene un coseno de 0.585 con molino y de 0.237 con anillo, y tantos de 0.361 con santos. Los subwords ayudan más donde hay poco texto, y sus errores son errores de grafía; una grafía sigue teniendo un solo vector. Apuntes posteriores cortan el texto mismo en piezas elegidas por fusiones, y arman vectores que cambian con la oración.
8Resumen
Las ideas del apunte en NumPy. w_in tiene una fila por palabra con vector y luego 2 000 000 filas de subwords, y w_out una columna por palabra; idx da la fila de una palabra, rows lista las filas de la palabra o, c es la columna de una palabra de contexto, y su etiqueta y eta la tasa de aprendizaje; tokens es un texto, y V el número de palabras con vector.
s = "<" + o + ">"
subs = [s[i:i + n]
for i in range(len(s))
for n in range(3, 7)
if i + n <= len(s)]fnv = 2166136261
for b in g.encode("utf-8"):
b = b if b < 128 else b | 0xFFFFFF00
fnv = ((fnv ^ b) * 16777619) % 2**32
row = V + fnv % 2_000_000x_o = np.zeros(len(w_in))
np.add.at(x_o, rows, 1)
v_o = w_in.T @ x_o # = w_in[rows].sum(axis=0)v_o = w_in[rows].mean(axis=0)s = (w_in[rows] @ w_out[:, c]).sum()h = 1 / (1 + np.exp(-(w_out[:, c] @ v_o)))
g_in = (h - y) * w_out[:, c]
g_out = (h - y) * v_o
np.subtract.at(w_in, rows, eta * g_in)
w_out[:, c] -= eta * g_outv_o = w_in[rows].sum(axis=0)
parts = w_in[rows] @ v_o2 / (
np.linalg.norm(v_o)
* np.linalg.norm(v_o2))
cos = parts.sum()a, b = set(rows), set(rows2)
share = len(a & b) / np.sqrt(len(a) * len(b))oov = np.mean([w not in idx for w in tokens])import fasttext
m = fasttext.load_model("cc.es.300.bin")
v = m.get_word_vector("acometedor")Los datos del apunte: los libros son el Don Quijote de Project Gutenberg (#2000) y la traducción de Ormsby (#996); las cuentas de las tablas de fastText, cc.en.300 y cc.es.300 (Grave et al. 2018), salen de los archivos de fasttext.cc, con la licencia Creative Commons Atribución-CompartirIgual 3.0, y la página solo muestra lo que se calculó con ellas. Los vectores del libro con subwords se entrenaron para este apunte.