El Portal de las Tecnologías para la Innovación

Cómo usar NetworkX, Jaccard Similarity y cuGraph para predecir tu próxima película favorita

A medida que aumenta la cantidad de datos disponibles para todos en el mundo, la capacidad de un consumidor para tomar decisiones informadas se vuelve cada vez más difícil.

Afortunadamente, los grandes conjuntos de datos son un componente beneficioso para los sistemas de recomendación, que pueden facilitar enormemente la toma de decisiones que a veces resultan abrumadoras.

Los gráficos son excelentes opciones para modelar las relaciones inherentes a los datos que alimentan los sistemas de recomendación, y NetworkX es una opción muy popular a la que recurren muchos científicos de datos para el análisis de gráficos en Python. NetworkX es fácil de aprender y usar, está equipado con una amplia gama de algoritmos de gráficos, cuenta con el respaldo de una comunidad grande y amigable y tiene abundantes ejemplos disponibles en cuadernos, documentos, Stack Overflow y su LLM favorito. Sin embargo, y para decepción de innumerables desarrolladores que se adentraron en el análisis de gráficos con o incluso gracias a NetworkX, es bien sabido que su rendimiento se queda corto en las escalas utilizadas por los sistemas de recomendación típicos.

Esto plantea la pregunta: ¿es posible escribir un sistema de recomendaciones basado en gráficos eficaz en unas pocas líneas de Python? En términos más generales, ¿es posible que los desarrolladores y los científicos de datos tengan análisis de gráficos fáciles de usar y de alto rendimiento?

La respuesta a ambas preguntas es “sí”.

NVIDIA GTC2025

Continúe leyendo para descubrir cómo puede crear un sistema de recomendación simple y efectivo en Python usando NetworkX, un conjunto de datos de 33 millones de reseñas de películas, el algoritmo Jaccard Similarity y el back-end NVIDIA cuGraph, que proporciona la aceleración de más de 250X necesaria para los datos de gráficos modernos a gran escala.

El conjunto de datos MovieLens

Empecemos por la parte más importante del sistema: los datos. El conjunto de datos 1 de MovieLens está disponible para su descarga al público y se describe con más detalle en el archivo README . El conjunto completo incluye alrededor de 331.000 usuarios anónimos que han reseñado 87.000 películas, lo que da como resultado 34 millones de valoraciones.

Imagen que muestra cómo los datos de MovieLens se pueden mostrar como un gráfico.
Figura 1. Los datos de MovieLens se pueden representar como un gráfico, donde las calificaciones individuales se asignan fácilmente a los bordes entre los nodos de usuario y de película.

Extracción de recomendaciones a partir de los datos: gráficos bipartitos y similitud de Jaccard

El tipo de gráfico que hemos creado a partir de los datos de MovieLens es un gráfico bipartito porque solo hay dos tipos de nodos: películas y usuarios, y las reseñas (bordes) solo pueden ocurrir entre un usuario y una película. Esto hace que sea particularmente fácil aplicar el algoritmo de similitud de Jaccard para encontrar similitudes entre películas. Jaccard Similarity compara pares de nodos y calcula un coeficiente de similitud utilizando sus relaciones en el gráfico. En este caso, las películas se relacionan entre sí en función de cómo los usuarios han elegido verlas y reseñarlas.

Imagen que muestra cómo se puede utilizar el algoritmo de similitud Jaccard para encontrar similitudes entre películas.
Figura 3. La similitud de Jaccard calcula un coeficiente de similitud utilizando los tamaños de los conjuntos de vecinos de los dos nodos que se comparan. Según las preferencias de visualización de los usuarios, podemos ver que m3 es más similar a m2 que a m1, y las películas m4 y m1 no son similares en absoluto. Este sistema recomendaría m2 a alguien a quien le gusta m3, y no recomendaría m1 a alguien a quien le gusta m4.

NetworkX lo hace fácil… para gráficos más pequeños

No es de sorprender que NetworkX admita el tipo de análisis que hemos descrito anteriormente, y es bastante fácil comenzar a ver resultados con solo unas pocas líneas de Python. Pero, como veremos, el rendimiento se convierte en una limitación para gráficos de mayor tamaño (como los necesarios para nuestro sistema de recomendación de películas) cuando se utiliza NetworkX sin el backend cuGraph acelerado por GPU.

A continuación veremos las piezas clave del sistema de recomendación, pero el código fuente completo está disponible aquí .

Como el algoritmo de similitud de Jaccard que utilizamos no tiene en cuenta los pesos de los bordes, considerará que todas las reseñas son iguales. No queremos que se recomienden películas con reseñas bajas, por lo que filtraremos todas las reseñas que estén por debajo de un cierto umbral, lo que tiene el efecto secundario de hacer que el gráfico también sea más pequeño.

# Create a separate DataFrame containing only "good" reviews (rating >= 3).good_ratings_df =ratings_df[ratings_df["rating"] >=3]good_user_ids =good_ratings_df["userId"].unique()good_movie_ids =good_ratings_df["movieId"].unique()

Si imprimimos los tamaños de los datos con los que estamos trabajando, vemos que nuestro gráfico de buenas críticas tiene aproximadamente 330k nodos y 28M aristas, con un grado promedio (número de vecinos por nodo) de 84:

total number of users: 330975
total number of reviews: 33832162
average number of total reviews/user: 102.22
total number of users with good ratings: 329127
total number of good reviews: 27782577
average number of good reviews/user: 84.41

Como se mencionó anteriormente, los gráficos de este tamaño suelen representar un desafío para NetworkX, pero la aceleración de GPU mediante el backend cuGraph elimina las limitaciones de rendimiento que suelen asociarse con esta cantidad de datos. Sin embargo, continuaremos con un entorno solo de CPU para demostrar el rendimiento predeterminado.

Nota: Todos los ejemplos a continuación se ejecutaron en una estación de trabajo que utiliza NetworkX 3.4.2 y un Intel(R) Xeon(R) Platinum 8480CL a 2,0 GHz con 2 TB de RAM.

Utilizando un gráfico de NetworkX creado a partir de usuarios y buenas críticas de películas, escojamos un usuario, busquemos una de sus películas mejor calificadas y usemos Jaccard Similarity para encontrar otras películas similares.

# Pick a user and one of their highly-rated moviesuser =good_user_ids[321]user_reviews =good_user_movie_G[user]highest_rated_movie =max(    user_reviews,    key=lambdan: user_reviews[n].get("rating", 0))

Cuando buscamos el ID del nodo en el mapa de nombres de películas, vemos que una de las películas mejor calificadas de este usuario es la película animada “Mulan”:

highest rated movie for user=289308 is Mulan (1998), id: 1907, rated: {'rating': 5.0}

Ahora podemos usar Jaccard Similarity para recomendar una película según las preferencias del usuario y su historial de visualización:

%%time# Run Jaccard Similarityjacc_coeffs =list(nx.jaccard_coefficient(good_user_movie_G, ebunch))

CPU times: user 2min 5s, sys: 15.4 ms, total: 2min 5s
Wall time: 2min 14s

El cálculo de similitud de Jaccard con la implementación predeterminada de NetworkX duró más de dos minutos. Con estos resultados, ahora podemos ofrecer una recomendación.

# Sort by coefficient value, which is the 3rd item in the tuplesjacc_coeffs.sort(key=lambdat: t[2], reverse=True) # Create a list of recommendations ordered by "best" to "worst" based on the# Jaccard Similarity coefficients and the movies already seenmovies_seen =list(good_user_movie_G.neighbors(user))recommendations =[mid for(_, mid, _) injacc_coeffs                  ifmid notinmovies_seen]

 Ahora podemos simplemente imprimir la primera película en la lista ordenada de recomendaciones:

User ID 289308 might like Tarzan (1999) (movie ID: 2687)

El código es fácil y los resultados parecen buenos, pero el rendimiento nos frena.

Como podemos ver, la recomendación parece razonable; alguien a quien le gusta “Mulan” probablemente también disfrutará de la película animada de Disney de 1999 “Tarzán”.

Sin embargo, si nuestro objetivo fuera proporcionar un servicio o analizar cientos o miles de películas, la duración de dos minutos nos haría empezar a buscar una alternativa a NetworkX. Vemos que encontrar similitudes entre otras películas que utilizan este sistema no es más rápido:

%%time# 1196: "Star Wars: Episode V - The Empire Strikes Back (1980)"print_similar_movies(1196)

movies similar to Star Wars: Episode V - The Empire Strikes Back (1980):
movieId=260, Star Wars: Episode IV - A New Hope (1977)
movieId=1210, Star Wars: Episode VI - Return of the Jedi (1983)
movieId=1198, Raiders of the Lost Ark (Indiana Jones and the Raiders of the Lost Ark) (1981)
CPU times: user 13min 47s, sys: 71.8 ms, total: 13min 47s
Wall time: 11min 30s

%%time# 318: "Shawshank Redemption, The (1994)"print_similar_movies(318)

movies similar to "Shawshank Redemption, The (1994)":
movieId=296, Pulp Fiction (1994)
movieId=593, "Silence of the Lambs, The (1991)"
movieId=356, Forrest Gump (1994)
CPU times: user 28min 28s, sys: 172 ms, total: 28min 28s
Wall time: 16min 49s

La calidad de las recomendaciones que se obtienen es impresionante, teniendo en cuenta que este sistema se compone de tan solo unas pocas líneas de código. Sin embargo, el rendimiento en tiempo de ejecución lo hace prácticamente inutilizable. Como se ha visto anteriormente, encontrar recomendaciones basadas en “Cadena perpetua (1994)” lleva casi 17 minutos.

NVIDIA cuGraph lo hace mucho más rápido

El algoritmo gráfico en el flujo de trabajo anterior es prohibitivamente costoso, pero al usar el backend NVIDIA cuGraph y una GPU compatible, podemos mejorar drásticamente el rendimiento sin cambiar el código.

La similitud de Jaccard es compatible con la versión 25.02 o posterior de nx-cugraph. La versión 25.02 está disponible en las compilaciones nocturnas y formará parte de las futuras versiones estables a finales de este mes. Las instrucciones para instalar nx-cugraph, así como otros paquetes de RAPIDS, tanto desde los canales nocturnos como estables mediante conda o pip, están disponibles en la Guía de instalación de RAPIDS .

Una vez instalado, nx-cugraph se puede habilitar simplemente configurando una variable de entorno:

NX_CUGRAPH_AUTOCONFIG=True 

cuGraph utiliza la GPU para acelerar drásticamente las búsquedas de vecinos y las comparaciones de conjuntos necesarias para el cálculo de similitud de Jaccard. Además, a medida que el gráfico se escala y aumenta la cantidad de películas y reseñas por película, el rendimiento permanece casi constante.

La mejor parte del sistema, la simplicidad del código, no cambia y los resultados son idénticos, pero el rendimiento aumenta más de 250 veces para la ejecución que antes tomaba casi 17 minutos, reduciéndola a menos de 4 segundos.

Gráfico de barras que muestra cómo se acelera la similitud de Jaccard con cuGraph sobre NetworkX para recomendaciones de películas.
Figura 4. Gráfico que muestra la aceleración de cuGraph en comparación con NetworkX para el cálculo de similitud de Jaccard para varias películas.

Software: NetworkX 3.4.2, cuGraph/nx-cugraph 25.02
CPU: Intel(R) Xeon(R) Platinum 8480CL a 2,0 GHz, 2 TB de RAM
GPU: NVIDIA Quadro RTX 8000, 48 GB de RAM

Conclusión

La publicación del blog abordó un sistema de recomendaciones simple y eficaz que es fácil de escribir en Python con NetworkX. Aunque existen muchos otros enfoques que se pueden adoptar (como los que se describen aquí ), pocos igualarían el bajo esfuerzo que se requiere para comenzar a explorar los datos que ofrece el análisis de gráficos con NetworkX. Sin embargo, la exploración de datos productiva y significativa requiere una respuesta rápida, y NetworkX tradicionalmente ha tenido dificultades para adaptarse a problemas más grandes y del mundo real.

El backend NVIDIA cuGraph para NetworkX acelera la API NetworkX, que es conocida y flexible, para que también funcione a escala, generando resultados en segundos en lugar de decenas de minutos, lo que le permite concentrarse y ser productivo. Los usuarios ahora pueden seguir usando NetworkX, la biblioteca de análisis de gráficos más popular, sin preocuparse por problemas de escalabilidad simplemente agregando una GPU y el backend cuGraph a su entorno.

Para obtener más información sobre el análisis de gráficos acelerado con NetworkX y NVIDIA cuGraph, visita https://rapids.ai/nx-cugraph . NVIDIA Blog. R. R. Traducido al español

Artículos relacionados

Scroll al inicio