Apuntes / Procesamiento de Lenguaje Natural / Apunte 3 de 8

Read in English

El modelo de espacio vectorial

Cómo un buscador encuentra el capítulo del que trata una consulta, convirtiendo palabras en pesos, capítulos en vectores y la relevancia en un ángulo, y cómo BM25 refina los pesos.

Los lectores recuerdan los capítulos del Quijote por sus aventuras: los molinos, los galeotes, los leones, el retablo de maese Pedro. Un buscador solo tiene las palabras. Dadas unas pocas, debe encontrar los capítulos de los que tratan, entre los 126 del libro, 52 de la primera parte y 74 de la segunda. El modelo de espacio vectorial convierte cada capítulo, y también la consulta, en un vector con un número por cada palabra del vocabulario, y ordena los capítulos según cuánto apuntan en la misma dirección que la consulta:

  • molinospuntuaciónI.VIIII.IIII.III

    ¿Qué capítulo tiene los molinos?

  • 7010700000187

    Cada capítulo, una columna de conteos

  • Capítulos ordenados por su ángulo con la consulta

La sección 1 prepara las palabras, la sección 2 arma los vectores, la sección 3 les da pesos, la sección 4 ordena los capítulos por ángulo y la sección 5 cambia los pesos por los de BM25, que los buscadores todavía usan como estándar.

1Stopwords y el pipeline

Las palabras más frecuentes del libro, la cabeza de la curva de Zipf del primer apunte, son las invariables del segundo: que, y, de, a, junto con pronombres y verbos auxiliares. Aparecen en todos los capítulos, así que no dicen nada de cuál es cuál. Un buscador las descarta como stopwords. La lista de Snowball para el castellano tiene 308, y 262 aparecen en el libro: cerca del 1% de sus palabras distintas, pero el 52% de sus tokens. Sin ellas, el texto conserva algo menos de la mitad de su largo y no pierde casi ninguna de las palabras que distinguen un capítulo de otro.

Quitar las stopwords es un paso de un pipeline que convierte texto crudo en un vocabulario: dividirlo en oraciones, luego en tokens, pasarlos a minúsculas, quitar las stopwords y llevar cada palabra a su lema. Cada paso después de los tokens es optativo, y la figura te deja activarlos y desactivarlos, en la primera línea del capítulo primero y en el libro entero.

Activa y desactiva los pasos con los botones. Una palabra quitada queda tenue y tachada.

Las stopwords no siempre son ruido. En la línea de arriba, sin ellas, de cuyo nombre no quiero acordarme queda en cuyo nombre quiero acordarme: se fue el no, y con él el sentido de la línea. Una búsqueda de una frase exacta, o una tarea en la que importa la negación, las conserva.

2La matriz término-documento

Tras el pipeline, cada capítulo es una bolsa de palabras con sus conteos. Escribe los conteos en una tabla con una fila por cada palabra del vocabulario y una columna por cada capítulo: es la matriz término-documento. Cada columna reúne los conteos de un capítulo, un vector

dj = (w1,j, …, wV,j),

donde wi,j es el peso de la palabra i en el capítulo j, por ahora su conteo. En el modelo de espacio vectorial cada palabra del vocabulario es una dimensión y cada capítulo un punto en ese espacio, aquí de 22 146 dimensiones. Se pueden dibujar de a dos: elige un par de palabras, y cada capítulo se vuelve un punto cuyas coordenadas son sus conteos de ambas.

Haz clic en un punto, o pasa de un capítulo a otro con las flechas.

La flecha de un capítulo tiene una dirección y un largo. La dirección dice qué palabras dominan: los capítulos del gobierno de Sancho se inclinan hacia sancho. El largo depende sobre todo de cuán largo es el capítulo, porque un capítulo largo lo repite todo más. La sección 4 ordena por lo primero e ignora lo segundo.

3Pesos

El conteo es un mal peso, por dos razones. Una palabra que está en todos los capítulos no distingue a ninguno, por más que aparezca; y una palabra usada diez veces no dice diez veces más que una usada una vez. Los pesos de siempre corrigen ambas cosas. El primero escala por la palabra más frecuente del capítulo, o amortigua el conteo con un logaritmo:

tfi,j = fi,jmaxl fl,j, tflogi,j = 1 + log10 fi,j (si fi,j > 0),

donde fi,j cuenta la palabra i en el capítulo j: 31 usos pesan 2.49, no 31. El segundo mira la colección entera, N capítulos, de los cuales ni contienen la palabra, y premia lo raro:

idfi = log10 Nni, wi,j = tfi,j · idfi.

Una palabra que está en todos los capítulos recibe idfi = log10 1 = 0 y se anula sola; el producto, tf-idf, es alto para una palabra frecuente en este capítulo y rara en el resto. La figura muestra las diez palabras de más peso de cinco capítulos con cada función de pesos; aquí tf-idf usa el tf logarítmico.

Elige un capítulo y una función de pesos, y luego una palabra para ver su cálculo.

Con el conteo los capítulos se parecen, encabezados por don, quijote y dijo. Con tf-idf cada uno muestra su propia historia, su gente, sus objetos y sus lugares, y las palabras que están en los 126 capítulos, capítulo, así y si, pesan 0.

4Ordenar por ángulo

Una consulta es un texto corto, así que se vuelve un vector como cualquier capítulo, con un peso para cada una de sus palabras y 0 para el resto. ¿Qué capítulos están más cerca de ella? La distancia es una mala respuesta: un capítulo largo tiene un vector largo, lejos de una consulta corta aunque apunte en la misma dirección. El ángulo ignora el largo. Su coseno es el producto punto dividido por ambos largos:

cos(q, d) = q · d‖q‖ ‖d‖ = ∑i qi di√∑i qi2 √∑i di2.

Vale 1 cuando los dos vectores apuntan igual y 0 cuando no comparten ninguna palabra; con pesos que nunca son negativos, nunca baja de 0. La figura vuelve al plano de la sección 2, donde la consulta es un punto como cualquier capítulo, ubicado según cuántas veces usa cada una de las dos palabras: el punto rojo parte en (6, 2), como si la consulta dijera sancho seis veces y quijote dos. Arrástralo, y compara los capítulos más cercanos en ángulo con los más cercanos en distancia.

Arrastra el punto rojo de la consulta, o muévelo con las flechas. De cerca se ve la esquina junto al origen, donde está la mayoría de los capítulos.

Por ángulo, los capítulos más cercanos son los que usan las dos palabras en la proporción de la consulta, sea cual sea su largo. Por distancia, son los capítulos tan cortos como la consulta, sea cual sea su tema. Un buscador ordena por el coseno en todas las dimensiones a la vez, con pesos tf-idf tanto para la consulta como para los capítulos.

5BM25

BM25 es una función de pesos que los buscadores todavía usan como estándar. El puntaje de un capítulo para una consulta es la suma de sus pesos para las palabras de la consulta:

wi,j = fi,j (k1 + 1)k1 [(1 − b) + b l(dj) / lavg] + fi,j · log10 Nni,

donde l(dj) es el largo del capítulo en palabras y lavg el promedio, 1420 aquí. El primer factor se satura: en un capítulo de largo promedio, con k1 = 1.2, un uso pesa 1, dos pesan 1.38 y veinte apenas 2.08, bajo un techo de k1 + 1 = 2.2. El corchete corrige por largo: un capítulo largo necesita más usos para pesar lo mismo, y b fija cuánto, desde 0, sin corrección, hasta 1, corrección completa. Los valores b ≈ 0.75 y k1 ≈ 1.2 vienen de experimentos, no de una teoría: ordenan bien en colecciones donde hay personas que juzgaron qué documentos responden a qué consultas. Muchas implementaciones cambian log(N/ni) por una forma suavizada, log((N − ni + 0.5)/(ni + 0.5)), con la misma idea.

La figura busca en los 126 capítulos del libro. Arma una consulta con las palabras, y compara el coseno de la sección 4, sobre tf-idf, con BM25.

k1
b

Elige una o más palabras para la consulta, y luego un orden.

Busca leones. El coseno pone primero el capítulo XXX de la segunda parte, corto, donde leones aparece 7 veces, todas en el nuevo título de don Quijote, el Caballero de los Leones. BM25 pone primero el capítulo XVII, la aventura de los leones misma, con 18 usos en un capítulo el doble de largo. El coseno divide por el largo del vector completo del capítulo, lo que favorece a los capítulos cortos; la saturación de BM25 y su corrección por largo, más suave, dejan que los 18 usos cuenten.

6Resumen

Las ideas del apunte en NumPy. f es la matriz término-documento de conteos, una fila por palabra y una columna por capítulo, l los largos de los capítulos, w_q el vector de la consulta y q las filas de sus palabras.

tf normalizado
tfi,j = fi,j / maxl fl,j
tf = f / f.max(axis=0)
tf logarítmico
1 + log10 fi,j
g = np.maximum(f, 1)
tf_log = 1 + np.log10(g)
tf_log *= f > 0
idf
log10(N/ni)
N = f.shape[1]
n = (f > 0).sum(axis=1)
idf = np.log10(N / n)
tf-idf
wi,j = tfi,j · idfi
w = tf_log * idf[:, None]
Coseno
cos(q, d) = q · d / (‖q‖ ‖d‖)
norm = np.linalg.norm
cos = w.T @ w_q / (
    norm(w, axis=0)
    * norm(w_q))
BM25
f (k1 + 1)k1 [1 − b + b l/lavg] + f · idf
r = l / l.mean()
k = k1 * (1 - b + b * r)
bm = f * (k1+1) / (k+f)
s = bm[q] * idf[q, None]
score = s.sum(axis=0)

Los datos del apunte: las stopwords son las listas de Snowball, y los lemas vienen de las listas de lematización de Michal Měchura (ODbL).