top of page
Search

Motores de Recomendación con Python (1/3)

  • Writer: Antonio Romero Camacho
    Antonio Romero Camacho
  • Feb 7, 2021
  • 9 min read

Updated: Feb 11, 2021

Las recomendaciones tienen una influencia muy importante en muchas de las decisiones que tomamos en el día a día. Algunos ejemplos de recomendaciones directas podrían ser las que nos dan nuestros amigos sobre los restaurantes a los que han ido el fin de semana o la recomendación de un determinado modelo de teléfono basada en los comentarios de una web.


Otras recomendaciones más indirectas podrían ser las que lanza Disney+ sobre las películas o programas que pueden ser más afines a nuestros gustos, o las propuestas de items que hace Amazon en relación al producto que estás comprando actualmente.

Figura 1. Motor de recomendación de Disney+

En este y en los próximos posts vamos a hablar sobre la ciencia detrás de estos motores y cómo construirlos de forma sencilla usando Python.


1. Introducción a los motores de recomendación


¿Qué son los motores de recomendación?


Los motores de recomendación son herramientas que usan el feedback de los usuarios para encontrar nuevos elementos que puedan ser afines a estos usuarios u otros, asumiendo que los usuarios con preferencias similares en el pasado probablemente tendrán las mismas preferencias en el futuro.


Estos métodos de recomendación se benefician de multiples emparejamientos entre los usuarios que dan su opinión y los elementos sobre los que opininan. De este modo, se proporcionarán mejores recomendaciones de un producto cuanto mayor sea el feedback recibido sobre el mismo. También se darán recomendaciones más personalizadas a aquellos usuarios que hayan dado más opiniones.


¿Cuándo es interesante usar los motores de recomendación?


Los motores de recomendación pretenden resolver un problema específico de Machine Learning: sugerencia de productos, servicios, entidades a un usuario en base a sus opiniones y de otros usuarios.


Lo que es importante tener claro si vamos a diseñar un motor de recomendación es que los datos son registros de preferencias de diferentes usuarios. En función de cómo se miden estas preferencias los datos se clasifican en implícitos y explícitos. Los datos explícitos contienen feedback directo de un usuario como por ejemplo su opinión sobre un producto expresada en forma de puntuación (por ejemplo: el número de estrellas con el que puntuamos un producto en Amazon). Por contra, los datos implícitos sacan información de las acciones del usuario para resumir sus preferencias (por ejemplo: histórico de reproducciones en Spotify, que te puede permitir identificar los estilos de música preferidos por ese usuario).


2. Recomendaciones basadas en contenido


Las recomendaciones pueden realizarse basadas en la opinión general. Sin embargo, este tipo de recomendaciones no son personalizadas. En este post os vamos a enseñar distintos modelos para hacer recomendaciones a un usuario basándonos en la similitud de unos items con otros que le gustaron al usuario en el pasado. Es decir, si al usuario en cuestión le gustó la película A, y mi modelo determina que las películas A y B son similares, entonces es bastante probable que al usuario le guste la película B también. En este post os mostraremos como determinar qué items son similares. Estas recomendaciones que se llevan a cabo encontrando elementos con atributos similares se denominan recomendaciones basadas en contenido.

Figura 2. Recomendaciones basadas en contenido

Atributos o características de cada elemento

Pongamos por caso que tenemos un dataset de películas. Los atributos de cada película podrían ser: título, director, fecha de estreno, género, actores protagonistas, duración, idioma, etc. Dentro de estos atributos podríamos incluir cualquier información descriptiva. La gran ventaja de usar estos atributos junto a la opinión de los usuarios es que se pueden hacer recomendaciones de cualquier elemento con atributos. Esto permite recomendar incluso nuevos items que los usuarios no tengan en el radar aún.


Los models basados en contenido usan cualquier característica disponible para construir perfiles de items que nos permitan a los científicos de datos compararlos matemáticamente. Esto nos permitirá identificar elementos similares y recomendarlos.

Figura 3. Atributos de ua película

Vectorización de atributos

La mejor manera de extraer información de estos atributos es vectorizándolos. A continuación se muestra un ejemplo donde aparecen diferentes elementos por filas y las características o atributos posibles por columnas.


Y os preguntareis, ¿por qué organizar los datos de esta manera?. Organizar la información de forma tabular nos permite calcular la distancia o similitud entre elementos de forma sencilla, lo cual es vital para hacer las recomendaciones de las que estamos hablando en este post.

Figura 4. Vectorización de los datos (formato tabular)

A continuación, aprenderemos a generar estas tablas a partir de los datos. En esta ocasión, vamos a usar un dataset de películas (movies.csv). A partir de la tabla que se muestra a continuación, queremos obtener una nueva tabla que contenga una fila por película (una película puede aparecer varias veces en el dataset original porque puede clasificarse dentro de varios géneros) con un 1 en aquellos atributos que la representen y ceros en los que no.

Figura 5. Carga del dataset original

Para transformar los datos podemos usar la función crosstab de pandas. El primer argumento que le pasemos a la función se convertirá en las filas y el segundo en las columnas. A continuación obtenemos el resultado deseado.

Figura 6. Cross_tab de películas y géneros con Pandas

Con nuestros datos en el formato adecuado estamos en disposición de comenzar a hacer comparaciones y recomendaciones. Pero para ello, tenemos que encontrar la manera de calcular el grado de similitud entre filas.


Introducción al coeficiente de similitud de Jaccard

La métrica que vamos a usar para medir el grado de similitud entre los distintos elementos de nuestra tabla "encodeada" se llama coeficiente de similitud de Jaccard. Este coeficiente es el ratio de atributos que dos elementos tienen en común, dividido por el número total de atributos de ambos. Este coeficiente toma valores entre 0 y 1, y adquiere valores más altos cuanto mayor es el número de atributos en común de los dos elementos.

Figura 7. Coeficiente de similitud de Jaccard (fórmula)

Pasamos a calcular el coeficiente de similitud de Jaccard para los datos con los que hemos empezado a trabajar. Empezaremos importando jaccard_score de la biblioteca sklearn metrics. Esta función toma dos filas y calcula el grado de similitud entre ellas.


A continuación mostramos el resultado de comparar dos películas del género animación ('Tangled' y 'WALL-E'). Puesto que pertenecen al mismo género el coeficiente de similitud de Jaccard es 1. Sin embargo, cuando comparamos dos de distinto género como 'Remember me' que es un Drama y WALL-E, el resultado es 0.

Figura 8. Cálculo del coeficiente de similitud de Jaccard con Scikit Learn

Si queremos establecer similitudes entre todos los elementos de nuestro dataset de una vez hacemos uso de dos funciones del paquete Scipy. En primer lugar, pdist (el nombre corto para pairwise distance) nos ayuda a calcular las distancias de todos los pares posibles, usando como argumento la métrica Jaccard. El resultado es una matriz que contiene todas las distancias en formato 1D array. Por ello, tendremos que usar la función squareform para transformar estos datos en 1D a la forma rectangular de matriz deseada.

Figura 9. Calculo de la distancia de Jaccard con Scipy

Nótese que el cálculo que hacemos con la función pdist es la distancia, que expresa el grado de diferencia entre cada uno de los registros. Los elementos de la diagonal, que comparan un elemento con el mismo, muestran una distancia de 0 porque son iguales y por tanto el grado de diferencia es nulo. Como estamos interesados en calcular el grado de similitud que es el complementario de la operación que acabamos de realizar, restaremos a 1 los valores de matriz_cuadrada_distancias.

Figura 10. Cálculo del coeficiente de similitud de Jaccard on Scipy

Para poder usar esta información con mayor comodidad, podemos pasar estos datos a un DataFrame. El DataFrame contendrá como argumento principal los valores de coeficiente_similitud_jaccard y como índices y columnas los nombres de las películas.

Figura 11. DataFrame con coeficientes de similitud de Jaccard

Ahora podemos buscar cómodamente la distancia entre pares.

Figura 12. Comparación del grado de similitud entre dos películas

En realidad, comparar el grado de similitud entre dos películas puede que no sea lo que más interese al usuario. Puede ser más valioso encontrar todas aquellas películas que son más parecidas a la que nos gusta. Para ello, seleccionaremos la columna de la película que nos gusta y la ordenaremos de mayor a menor. Así, Python nos mostrará en orden decreciente de similitud las películas.

Figura 13. Recomendar películas por grado de similitud con Python

3. Recomendaciones basadas en texto


Desafortunadamente, en el mundo real los problemas que tenemos que resolver no tienen etiquetas claras como los géneros para el caso particular de las películas. Por suerte, si hay texto relacionado con ese elemento entonces podemos hacer algo. Este texto puede ser un resumen, una descripción del elemento o la lista de contenidos de un libro. en estos casos usaremos "Term Frequency inverse Document Frequency" o TF-IDF para transformar el testo en información valiosa.


Estos algoritmos dividen el número de veces que una palabra aparece en un documento entre el ratio de documentos globales en que unes palabra aparece. De esta manera, se reduce la importancia de palabras comunes y se incrementa el peso de aquellas palabras que no aparecen en muchos documentos. Por ejemplo, si comparamos el texto de "Harry Potter y La Piedra Filosofal" con el resto de libros de la saga Harry Potter, la palabra Voldemort obtendrá una puntuación baja ya que aparece multitud de veces a lo largo de todos los libros. Por contra, la palabra elixir que está muy relacionada sólo al primer libro de la saga, obtendrá una puntuación alta.

Figura 14. Fórmula TF-IDF

Ahora vamos a trabajar con otro dataset de películas diferente. Este contiene los nombres de las películas y el resumen de las mismas que aparece en la página Wikipedia.

Figura 15. Cargamos el nuevo dataset con el resumen de las películas

La transformación de los datos se hace gracias a TfidfVectorizer de la biblioteca Scikit Learn. Porde efecto, esta herramienta genera una característica por cada palabra presente en un documento. Esto resulta en un gran número de características; por suerte, hay formas de reducir el número de características generador por el vectorizador.


Para ello incluiremos en primer lugar el argumento min_df y le daremos un valor de 2. De esta forma sólo se convertirán en características aquellas palabras que estén presentes en al menos dos documentos. Esto es muy útil ya que las palabras que sólo aparezcan en un documento no son muy importantes a la hora de encontrar similitudes.


Por otro lado, podemos incluir un segundo argumento max_df que elimine aquellas palabras que sean muy comunes. Si lo fijamos a 0.7, no se tendrán en cuenta aquellas palabras que aparecen en más de un 70% de los resúmenes.

Figura 16. inicializamos el vectorizador limitando el número de características a generar

Una vez iniciado el Vectorizer invocaremos al método fit_transform usando la columna resumen del dataframe. El método get_feature_names permite obtener las diferentes características. La matriz dispersa se almacena en un DataFrame que almacena por filas las películas y por columnas los nombres de las características.

Figura 17. Matriz de similitud coseno

Similitud Coseno

En este caso utilizaremos una métrica que cuantifica mejor la similitud entre elementos con mayor variabilidad. A esta métrica se la conoce como Cosine Similarity o Similitud Coseno. Sin entrar en los detalles matemáticos, se encarga de medir el ángulo entre dos documentos en el espacio métrico de multiples dimensiones. Mostramos un ejemplo de esta métrica trasladada al caso del espacio bidimensional. Toma valores entre 0 y 1, donde 1 representa similitud total.

Figura 18. Formula y representación gráfica de la similitud coseno

Esta función permite hacer el cálculo entre dos películas cualesquiera. La diferencia es que en este caso, será necesario hacer un reshape como se muestra a continuación. El grado de similitud entre los resúmenes de la primera y la segunda parte de la película Cars es de 0.38.

Figura 19. Similitud Coseno entre dos películas basado en su resumen

De forma similar y partiendo de un DataFrame, Scikit Learn es capaz de calcular de una vez la similitud coseno entre todas las filas.

Figura 20. Similitud coseno de todos los elementos

Una vez calculados esos valores construimos un nuevo DataFrame de similitudes coseno.

Figura 21. DataFrame de similitudes coseno

Esto nos permite saber qué películas son más parecidas a una en concreto basándonoslos en sus resúmenes. Si buscamos recomendaciones de películas similares a Harry Potter y el Cáliz de Fuego obtenemos sugerencias para visualizar el resto de la saga.

Figura 22. Recomendaciones de películas similares a Harry Potter y el Cáliz de Fuego

Si hacemos la consulta para el caso del Señor de los Anillos: La comunidad del anillo, vemos que nos recomienda las películas de la trilogía y el Hobbit. También nos recomienda Four Sisters and a Wedding debido a que el protagonista de la película se llama Frodo también.

Figura 23. Recomendaciones de películas similares a El Señor de los Anillos: La Comunidad del Anillo

Añadimos un último caso con la película Mary Poppins. El recomendador nos sugiere como segunda opción ver la película Saving Mr. Banks, que trata sobre la colaboración entre Walt Disney y la escritora de la novela Mary Poppins.

Figura 24. Recomendaciones de películas similares a Mary Poppins

Recomendar en base al perfil del usuario

Lo que no hemos tenido en cuenta hasta ahora es que los usuarios no son tan unidimensionales como para que les guste sólo un elemento. La realidad es que el usuario habrá visto una serie de películas y querrá que la recomendación proporcionada por los algoritmos esté alineada con su amplio gusto.


Consideremos el caso que acabamos de mencionar: un usuario que ha visto una serie de películas. La forma más directa de crear el perfil de usuario es inicialmente, creando un vector que contenga los títulos de dichas películas para conseguir a través del método .loc los vectores de características de las mismas.

Figura 25. creando el perfil del usuario

Para terminar de construir el perfil de este usuario es necesario representar todas las preferencias del usuario en una única serie. Eso lo conseguimos haciendo la media de cada característica con el método .mean.

Figura 26. Perfil del Usuario

Este perfil del usuario lo usaremos para encontrar las películas con mayor similitud que no haya visto todavía. En primer lugar, tendremos que determinar el subconjunto de películas que no ha visto todavía (eliminando del dataframe del vectorizer todas aquellas películas que están en la lista de vistas). El número de filas ha disminuido de 34886 a 34880 (6 filas).

Figura 27. resultado de eliminar las seis películas vistas por el usuario

Después calcularemos la similitud coseno entre el perfil de usuario que acabamos de crear y el DataFrame de películas que aún no ha visto el usuario. Después, almacenaremos la salida en un nuevo DataFrame y ordenaremos los resultados para poder acceder y ordenar los datos de forma sencilla.

Figura 28. Recomendación basada en el historial del usuario

Ahora si que el algoritmo ha sido capaz de recomendar en base al historial de películas vistas por nuestro usuario, y no sólo basándose en películas individuales. Los registros más arriba en la tabla son los más parecidos a los intereses del usuario en base al background de intereses que recoge su perfil.


4. Próximo Post


Hemos finalizado el post usando elementos que le gustan a un usuario para sugerirle elementos similares. Estos métodos funcionan bien cuando tenemos mucha información sobre los elementos, pero no tanto información sobre cuál es el sentimiento de las personas hacia el elemento en cuestión.


En el próximo post, entre otras cosas, encontraremos a aquellos usuarios que tienen preferencias similares al usuario al que le queremos hacer la recomendación y, basándonos en las preferencias de ese grupo se le harán las sugerencias.

 
 
 

Yorumlar


bottom of page