Una consulta es un documento más corto
El modelo de espacio vectorial representa cada documento y cada consulta como una fila de números. Después ordena los documentos por lo parecida que es su fila a la de la consulta.
Con él, un buscador puede poner primero un texto que no contiene todas tus palabras pero que se parece mucho a lo que escribiste.
Al final de esta lección
Nota
Puedo ordenar documentos por el coseno del ángulo entre su vector y el de la consulta.
Palabras clave
| Palabra | Qué significa |
|---|---|
| Vector | una fila de números, uno por cada palabra del vocabulario |
| Vocabulario | las palabras que cuentan para comparar |
| Producto escalar | la suma de multiplicar dos vectores posición a posición |
| Longitud | la raíz de la suma de los cuadrados de un vector |
| Similitud del coseno | el producto escalar dividido entre las dos longitudes |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
Cada texto se convierte en un vector
Se elige un vocabulario, por ejemplo cuatro palabras: biblioteca, facultad, marzo y plazo. Después se cuenta cuántas veces sale cada una en cada texto.
La fila de recuentos es el vector del texto. La consulta «plazo marzo» también es un texto, así que también tiene su vector, del mismo tamaño que los de los documentos.
Ejemplo resuelto: los vectores de los avisos
| Texto | biblioteca | facultad | marzo | plazo |
|---|---|---|---|---|
| consulta: plazo marzo | 0 | 0 | 1 | 1 |
| aviso 3: la beca de comedor tiene plazo en marzo | 0 | 0 | 1 | 1 |
| aviso 4: la biblioteca cierra tarde en marzo | 1 | 0 | 1 | 0 |
| aviso 6: plazo de pago en la facultad en marzo | 0 | 1 | 1 | 1 |
| aviso 5: bicicletas gratis en la facultad | 0 | 1 | 0 | 0 |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
Convierte un texto en un vector
# Cada aviso se convierte en una fila de cuatro recuentos.
vocab = ["biblioteca", "facultad", "marzo", "plazo"]
def make_vector(tokens):
vector = []
for word in vocab:
vector.append(tokens.count(word))
return vector
print(make_vector("plazo marzo".split()))
print(make_vector("plazo de pago en la facultad en marzo".split()))
# Cambia la consulta por "biblioteca" y ejecuta.
El coseno mide el ángulo entre dos vectores
El producto escalar multiplica dos vectores posición a posición y suma. Toma la consulta [0, 0, 1, 1] y el aviso 6, que es [0, 1, 1, 1]. La cuenta es 0, más 0, más 1, más 1: un total de 2.
La longitud de un vector es la raíz de la suma de sus cuadrados. La similitud del coseno es el producto escalar dividido entre las dos longitudes. Va de 0, sin palabras en común, a 1, la misma dirección.
Ejemplo resuelto: el coseno de cada aviso
| Aviso | Producto escalar | Longitudes | Coseno |
|---|---|---|---|
| 3 | 2 | raíz de 2 por raíz de 2 = 2 | 1,0 |
| 6 | 2 | raíz de 2 por raíz de 3 = 2,449 | 0,82 |
| 4 | 1 | raíz de 2 por raíz de 2 = 2 | 0,5 |
| 1, 2 y 5 | 0 | no se necesita | 0 |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
Calcula el coseno del aviso 6
docker exec jupyter python3 -c "print(round(2/((2**0.5)*(3**0.5)),2))"Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
El orden de los resultados sale del coseno
Para ordenar, se calcula el coseno entre la consulta y cada documento. El de coseno más alto va primero. Los que valen 0 no comparten nada con la consulta y no se muestran.
Si dos empatan, hace falta una regla fija. Aquí gana el de número menor. Un truco simple: se guardan pares (menos el coseno, número) y se ordena la lista de pares.
Ordena los avisos para «plazo marzo»
# Ordena los avisos por el coseno con la consulta "plazo marzo".
vectores = {
1: [1, 0, 0, 0],
2: [0, 1, 0, 0],
3: [0, 0, 1, 1],
4: [1, 0, 1, 0],
5: [0, 1, 0, 0],
6: [0, 1, 1, 1],
}
consulta = [0, 0, 1, 1]
def cosine(a, b):
dot = 0
la = 0
lb = 0
for n in range(len(a)):
dot = dot + a[n] * b[n]
la = la + a[n] * a[n]
lb = lb + b[n] * b[n]
if la == 0 or lb == 0:
return 0.0
return dot / ((la ** 0.5) * (lb ** 0.5))
pares = []
for numero in vectores:
c = cosine(consulta, vectores[numero])
pares.append((-round(c, 2), numero))
pares.sort()
for par in pares:
print(par[1], -par[0])
# Cambia la consulta por [0, 1, 0, 0], que es "facultad", y mira el empate.
Un error muy común
Advertencia
Mucha gente cree que gana el documento que tiene más palabras de la consulta. El coseno también mira lo demás que dice el texto: uno centrado en el tema puede ganar a otro que habla de más cosas. Por eso el aviso 3 supera al 6, que además habla de la facultad.
Para ir más lejos: usa pesos tf-idf
Los vectores de la lección usan recuentos. Prueba a poner en cada posición el peso tf-idf de la lección anterior.
Calcula otra vez el orden de los seis avisos para «biblioteca marzo». ¿Cambia el orden? Escribe por qué crees que cambia o no.