Sistemas de recomendación : Filtrado Colaborativo basado en el usuario usando N Nearest Neighbors

Ashay Pathak
Ashay Pathak

Follow

Feb 25, 2019 – 9 min read

Ashay Pathak, Chatana Mandava, Ritesh Patel

El filtrado colaborativo es una técnica muy utilizada en los sistemas de recomendación y es un área de investigación que avanza rápidamente. Los dos métodos más utilizados son los basados en memoria y los basados en modelos.

En este post, sólo nos centraremos en (User-Based Collaborative Filtering) UB-CF que es un método basado en memoria. La idea principal detrás de UB-CF es que las personas con características similares comparten gustos similares. Por ejemplo, si estamos interesados en recomendar una película a nuestro amigo Bob, supongamos que Bob y yo hemos visto muchas películas juntos y las calificamos de forma casi idéntica. Tiene sentido pensar que en el futuro también nos seguirán gustando películas similares y utilizar esta métrica de similitud para recomendar películas.

Intentemos implementar UB-CF y generar una lista de películas que nuestro amigo Bob a.k.a usuario activo podría estar interesado en ver. La motivación para escribir este post es profundizar en el algoritmo y entender cómo funciona realmente UB-CF. La mayor parte del contenido de este post está inspirado en un Curso en Coursera.

Filtrado Colaborativo Usuario-Usuario

El método identifica a los usuarios que son similares al usuario consultado y estima que la calificación deseada es la media ponderada de las calificaciones de estos usuarios similares.

Filtrado Colaborativo : fuente aquí

Estaremos haciendo las recomendaciones sobre el conjunto de datos MovieLens. El lenguaje de programación utilizado es python y el trabajo de análisis de datos se realiza principalmente utilizando la biblioteca pandas. IDE utilizado es jupyter notebook.

Así que antes de empezar me gustaría dar la lista de bibliotecas utilizadas :

  1. Pandas
  2. Numpy
  3. sklearn

Así que vamos a avanzar y entender los conceptos detrás de las recomendaciones. He adjuntado algunos fragmentos de código y salidas en el blog para una mejor comprensión. El archivo ipynb completo se adjunta al final del blog.

Función de puntuación

Es fácil idear una función para el filtrado colaborativo no personalizado (es decir, no tenemos en cuenta los gustos, disgustos y valoraciones del usuario activo en el pasado) que devuelva una puntuación tomando como parámetros de entrada el usuario u y el artículo i. La función devuelve una puntuación que cuantifica la intensidad con la que al usuario u le gusta/prefiere el artículo i.

Por lo tanto, esto se suele hacer utilizando las valoraciones de otras personas similares al usuario. Todo esto se discutiría en detalle más adelante. Por ahora la fórmula que he utilizado es,

función de puntuación

Donde ‘s’ es la puntuación predicha, ‘u’ es el usuario, ‘i’ es el ítem, ‘r’ es la calificación dada por el usuario y ‘w’ es el peso.

En este caso nuestra puntuación es igual a la suma de las valoraciones que cada usuario dio a ese ítem restando la valoración media de ese usuario multiplicada por algún peso que es de lo mucho que este usuario es similar o se supone que contribuye a las predicciones de otro usuario. Este es el peso entre el usuario u y v. La puntuación oscila entre 0 y 1, donde 0 es bajo y 1 es alto. Todo parece perfecto, entonces ¿por qué restamos las valoraciones medias de cada usuario y por qué utilizamos la media ponderada en lugar de la media simple?

El problema está en los tipos de usuarios que estamos manejando. Comienza con el hecho de que la gente califica a menudo en escalas muy diferentes. Yo puedo ser un usuario positivo y optimista que valore la película que me ha gustado con un 4 sobre 5, pero otro usuario menos optimista o con un nivel de exigencia muy alto puede valorar su película favorita con un 2 sobre 5. Aquí su 2 es mi 4. En este caso, su 2 es mi 4. Para mejorarlo, podemos aumentar la eficacia de este algoritmo si normalizamos la puntuación del usuario. Una forma de hacerlo es decir que vamos a calcular s(u,i), es decir, la puntuación como la calificación media que el usuario da a cada elemento más una desviación y la desviación va a ser lo mucho que este elemento es mejor o peor que la media.

He utilizado la similitud coseno para calcular el peso dado en la fórmula anterior. También he utilizado la noción de vecindad que se discutiría en este blog a medida que avanzamos.

Para normalizar los datos de la manera anterior, se requiere algún análisis de datos en pandas. Usted puede obtener el código completo al final. Para el blog, me centraré en los conceptos importantes.

import pandas as pdmovies = pd.read_csv("movies.csv",encoding="Latin1")
Ratings = pd.read_csv("ratings.csv")
Tags = pd.read_csv("tags.csv",encoding="Latin1")Mean = Ratings.groupby(by="userId",as_index=False).mean()
Rating_avg = pd.merge(Ratings,Mean,on='userId')
Rating_avg=Rating_avg-Rating_avg
Rating_avg.head()

Calificación normalizada

Así que ahora hemos terminado de calcular la calificación normalizada para un usuario. Los datos anteriores se utilizarían para calcular la puntuación final del usuario más tarde.

A partir de aquí nos centraremos en algunos conceptos importantes relacionados con los sistemas de recomendación.

Semejanza del coseno

Para la fórmula anterior necesitamos encontrar los usuarios que tienen pensamientos similares. Esto suena muy interesante para encontrar un usuario que tenga gustos y disgustos similares. Pero la pregunta es ¿cómo encontramos la similitud?

Para responder a esto, vamos a utilizar la similitud del coseno y ver cómo los usuarios son similares. Normalmente se calcula sobre las valoraciones que ambos usuarios han hecho en el pasado.

En nuestro ejemplo, he utilizado la función cosine_similarity de sklearn para calcular la similitud. Pero antes de eso tenemos que realizar algunos pre-procesamiento y limpiar los datos.

from sklearn.metrics.pairwise import cosine_similarityfinal=pd.pivot_table(Rating_avg,values='adg_rating',index='userId',columns='movieId')

Tabla dinámica

Esta contiene algunos lotes de valor NaN ya que cada usuario no ha visto todas las películas y esa es la razón por la que este tipo de matriz se llama matriz dispersa. Métodos como la factorización de la matriz se utilizan para hacer frente a esta dispersión, pero no nos centraremos en ella en este blog. El siguiente paso y uno de los pasos importantes es reemplazar estos valores NaN.

Hay dos métodos comúnmente utilizados para esto :

  1. Utilizar el promedio del usuario sobre la fila.
  2. Utilizar el promedio de la película sobre la columna.

He utilizado ambos métodos y se puede obtener en el código de abajo. Pero para explicarlo yo utilizaría el método de la media de la película.

# Replacing NaN by Movie Average
final_movie = final.fillna(final.mean(axis=0))

Reemplazar los valores NaN por la media de la película

Ahora, el siguiente paso es calcular la similitud entre los usuarios.

# user similarity on replacing NAN by item(movie) avg
cosine = cosine_similarity(final_movie)
np.fill_diagonal(cosine, 0 )
similarity_with_movie =pd.DataFrame(cosine,index=final_movie.index)
similarity_with_movie.columns=final_user.index
similarity_with_movie.head()

Similaridad coseno usuario

¡Comprobemos si lo que hemos calculado tiene realmente sentido!

a = get_user_similar_movies(370,86309)
a = a.loc]
a.head()

Películas similares

Desde la imagen anterior podemos ver que la similitud que generamos es cierta ya que ambos usuarios (370,86309) tienen casi las mismas valoraciones y gustos.

Así que hemos terminado con el cálculo de las similitudes entre los usuarios, pero todavía no estoy contento. Discutiré la razón en el siguiente paso.

Vecindad para el usuario (K)

Así que desde arriba podemos ver que hemos calculado las similitudes para todos los usuarios. Pero como soy un estudiante de Big Data, la complejidad del problema siempre me impulsa. Con esto quiero decir que el sistema de recomendación trabaja con los datos enormes y por lo tanto se vuelve muy importante para mantener y capturar sólo los aspectos más importantes y necesarios de los datos.

Para explicar esto usando nuestro ejemplo del sistema de recomendación de películas, la matriz que obtuvimos anteriormente es (862*862), ya que hay 862 usuarios únicos en los datos. Este número sigue siendo pequeño en comparación con los datos con los que trabajaría el sistema original. Pensemos en Amazon. Tendría más de millones de usuarios en su base de datos, por lo que al calcular la puntuación de cualquier artículo no sería una buena solución o método mirar a todos los demás usuarios todo el tiempo. Por lo tanto, para superar esto, generamos una noción de vecindad. Esto incluye sólo el conjunto de (K) usuarios similares para un usuario en particular.

Ahora vamos a dar más pasos para implementar la idea. En nuestro ejemplo he tomado el valor de k como 30. Así que tendríamos 30 vecinos más cercanos para todos los usuarios.

He utilizado mi función personalizada find_n_neighbours que toma la matriz de similitud y el valor de n como entrada y devuelve los n vecinos más cercanos para todos los usuarios. Usted puede encontrar el código en el cuaderno dado al final del blog.

# top 30 neighbours for each user
sim_user_30_m = find_n_neighbours(similarity_with_movie,30)
sim_user_30_m.head()

top 30 vecinos para los usuarios

Así que ahora si usted piensa entonces hemos reducido seriamente el número de cálculos innecesarios. Ahora, estamos listos para calcular la puntuación de un elemento ahora.

Generando la puntuación final S(u,i)

¡Vaya! hemos terminado con el proceso. Sé que hemos pasado por cosas realmente técnicas, pero el tiempo invertido merece la pena ya que hemos llegado al último paso.

Aquí intentaremos predecir la puntuación de la película que el usuario dado no ha visto.

score = User_item_score(320,7371)
print(score)

Puntuación prevista

Así que nuestro sistema predijo que la puntuación sería de 4,25, lo cual es realmente bueno. Creo que al usuario (370) le podría gustar la película con id (7371).

Ahora vamos a darle un toque final a nuestro sistema. Creo que la mayoría de nosotros habría utilizado Netflix o Hotstar. Así que cuando abrimos la aplicación se muestra el elemento que hace como.

Siempre me fascina la recomendación ya que resultan ser las películas que me gustan. Todo esto sucede a través del propio sistema de recomendación. Ahora haremos lo mismo e intentaremos predecir las 5 mejores películas que le pueden gustar al usuario dado.

La lógica no es tan difícil. Ya hemos generado la puntuación para un elemento. Del mismo modo podemos generar la puntuación para los otros elementos con el mismo usuario.

Pero, ahora de nuevo teniendo en cuenta Big Data ¿tiene sentido para generar la puntuación para todos los otros elementos?

Creo que NOOOOO!!!

Consideremos el usuario (U) y el usuario (K). Ahora supongamos que K no está en la vecindad de U ,es posible que al usuario U le guste una película que K ha visto y calificado. La mayoría de las veces no.

Creo que has pillado el truco. Sí, sólo nos interesa calcular las puntuaciones de los artículos que han visto sus usuarios vecinos.

Tenemos mucho éxito. Hemos reducido el cálculo de 2500 a N ( donde N es el conjunto de películas que le han gustado a mi vecindario ) y que es muy inferior a 2500.

Así que finalmente vamos a recomendar las películas.

User_item_score1 es mi función personalizada que utiliza nuestra discusión anterior para calcular las predicciones

user = int(input("Enter the user id to whom you want to recommend : "))
predicted_movies = User_item_score1(user)
print(" ")
print("The Recommendations for User Id : ",user)
print(" ")
for i in predicted_movies:
print(i)

Por fin!!!! Lo hemos conseguido. Tenemos nuestro propio sistema de recomendación.

La representación del código de abajo puede no ser muy fácil de leer, así que por favor ve a mi repositorio de GitHub para acceder al código.

Los Datasets necesarios se pueden obtener desde aquí.

Espero que os haya gustado el blog. Para cualquier consulta pueden enviarme un correo. Estén atentos a más blogs ya que esta es una de mis áreas de interés y definitivamente publicaría algunas cosas nuevas.

¡Gracias…!!

Deja una respuesta

Tu dirección de correo electrónico no será publicada.