Continuamos con este breve tutorial o introducción al filtrado colaborativo con Spark. Bien, la semana pasada en la entrada [Spark MLlib] Filtrado Colaborativo (I): Sistema de Recomendación construimos nuestro sistema de recomendación o más bien el generador de modelos. Esta semana tocará hacer uso de él y recomendar productos, en nuestro ejemplo películas, a nuestros usuarios a partir de sus votaciones.
Continuaremos también trabajando con la estructura de directorios y archivos ya definido, lo que vendría a ser nuestro proyecto dentro de nuestra herramienta IDE favorita. Antes de continuar, me gustaría matizar que este tutorial NO está orientado a la programación en scala ni a sus best practices a la hora de desarrollar. Todos los ejemplos son mejorables desde la primera línea. Por ejemplo, algo tan sencillo como recibir los parámetros con los que juego como argumentos de entrada, definir un único método principal, pues voy declarando funciones main por cada funcionalidad o algoritmo que trato de explicar, etc, pero lo dicho, no, no me voy a entretener ni por un instante en escribir el código correctamente, sólo pretendo dar unas pinceladas sobre cómo funcionan ciertos algoritmos o cómo alcanzar una determinada funcionalidad.
1) En esta ocasión creamos el archivo getRecommendationsByUserId.scala dentro de la misma carpeta donde creamos nuestro generador de modelos, es decir, en el directorio Carpeta_Proyecto/src/main/scala/mllib/collaborativeFiltering/
Continuaremos también trabajando con la estructura de directorios y archivos ya definido, lo que vendría a ser nuestro proyecto dentro de nuestra herramienta IDE favorita. Antes de continuar, me gustaría matizar que este tutorial NO está orientado a la programación en scala ni a sus best practices a la hora de desarrollar. Todos los ejemplos son mejorables desde la primera línea. Por ejemplo, algo tan sencillo como recibir los parámetros con los que juego como argumentos de entrada, definir un único método principal, pues voy declarando funciones main por cada funcionalidad o algoritmo que trato de explicar, etc, pero lo dicho, no, no me voy a entretener ni por un instante en escribir el código correctamente, sólo pretendo dar unas pinceladas sobre cómo funcionan ciertos algoritmos o cómo alcanzar una determinada funcionalidad.
1) En esta ocasión creamos el archivo getRecommendationsByUserId.scala dentro de la misma carpeta donde creamos nuestro generador de modelos, es decir, en el directorio Carpeta_Proyecto/src/main/scala/mllib/collaborativeFiltering/
package mllib.collaborativeFiltering
import org.apache.log4j.{Level, Logger}
import org.apache.spark.mllib.recommendation.{ALS, Rating, MatrixFactorizationModel}
import org.apache.spark.{SparkContext, SparkConf}
object getRecommendationsByUserId {
def main (args: Array[String]): Unit = {
Logger.getLogger("org.apache.spark").setLevel(Level.WARN)
Logger.getLogger("org.eclipse.jetty.server").setLevel(Level.OFF)
val sparkConfig = new SparkConf().setMaster("local[2]").setAppName("getRecommendationsByUserId")
val sc = new SparkContext(sparkConfig)
val mlRatings = sc.textFile("data/movielens/ratings.csv")
val ratings = mlRatings.map { line =>
// userId,movieId,rating,timestamp
val fields = line.split(",")
Rating(fields(0).toInt, fields(1).toInt, fields(2).toDouble)
}.cache()
val mlMovies = sc.textFile("data/movielens/movies.csv")
val movies = mlMovies.map { line =>
// movieId,title,genres
val fields = line.split(",")
(fields(0).toInt, fields(1))
}.collect.toMap
val model = MatrixFactorizationModel.load(sc, "target/tmp/myCollaborativeFilter") // Recuperando el modelo
val userId = 25560 // Seleccionamos el usuario id. Interesante pasarlo al script como argumento ;)
val moviesForUser = ratings.keyBy(_.user).lookup(userId)
println("El usuario ha votado a " + moviesForUser.size + " peliculas:")
moviesForUser.sortBy(-_.rating).take(10).map(rating => (movies(rating.product), rating.rating)).foreach(println) // Observamos que tipo de películas le gustan al usuario
val topKRecs = model.recommendProducts(userId,10) // El método para obtener las recomendaciones ya viene implementado de serie en la librería. Obtenemos el top10 de productos, en nuestro caso películas, recomendados. También podríamos pasar el número de recomendaciones, topN ;) ;)
topKRecs.map(rating => (movies(rating.product), rating.rating)).foreach(println)
}
}
2) Ejecutar la aplicación
# sbt compile
...
[success] Total time: 1 s, completed 11-mar-2016 10:14:13
# sbt run
...
Multiple main classes detected, select one to run:
[1] mllib.DecisionTrees.classificationFlightsOnTime
[2] mllib.collaborativeFiltering.getRecommendationsByUserId
[3] mllib.collaborativeFiltering.recommendation
Enter number: 2
...
El usuario ha votado a 50 peliculas:
Star Wars: Episode IV - A New Hope (1977),5.0)
(Star Wars: Episode V - The Empire Strikes Back (1980),5.0)
("Princess Bride,5.0)
(Star Wars: Episode VI - Return of the Jedi (1983),5.0)
(Life Is Beautiful (La Vita è bella) (1997),5.0)
(X-Men (2000),5.0)
(Unbreakable (2000),5.0)
("Lord of the Rings: The Fellowship of the Ring,5.0)
(Spider-Man (2002),5.0)
("Lord of the Rings: The Two Towers,5.0)
Multiple main classes detected, select one to run:
[1] mllib.DecisionTrees.classificationFlightsOnTime
[2] mllib.collaborativeFiltering.getRecommendationsByUserId
[3] mllib.collaborativeFiltering.recommendation
Enter number: 2
...
El usuario ha votado a 50 peliculas:
Star Wars: Episode IV - A New Hope (1977),5.0)
(Star Wars: Episode V - The Empire Strikes Back (1980),5.0)
("Princess Bride,5.0)
(Star Wars: Episode VI - Return of the Jedi (1983),5.0)
(Life Is Beautiful (La Vita è bella) (1997),5.0)
(X-Men (2000),5.0)
(Unbreakable (2000),5.0)
("Lord of the Rings: The Fellowship of the Ring,5.0)
(Spider-Man (2002),5.0)
("Lord of the Rings: The Two Towers,5.0)
...
("Roman Spring of Mrs. Stone,11.756657663464846)
(Babar The Movie (1989),11.171622844283377)
(Goodbye to Language 3D (2014),9.747998979226367)
(Garfield's Halloween Adventure (1985),9.736430143083723)
(Jimmy Carter Man from Plains (2007),9.72594699681999)
(Dangerous (1935),9.256450056766873)
(Bad Ronald (1974),9.089006045667578)
(End of the Line (2007),9.057430462126002)
(In the City of Sylvia (En la ciudad de Sylvia) (2007),8.963930587070152)
(Nine Dead Gay Guys (2003),8.895781428393946)
...
("Roman Spring of Mrs. Stone,11.756657663464846)
(Babar The Movie (1989),11.171622844283377)
(Goodbye to Language 3D (2014),9.747998979226367)
(Garfield's Halloween Adventure (1985),9.736430143083723)
(Jimmy Carter Man from Plains (2007),9.72594699681999)
(Dangerous (1935),9.256450056766873)
(Bad Ronald (1974),9.089006045667578)
(End of the Line (2007),9.057430462126002)
(In the City of Sylvia (En la ciudad de Sylvia) (2007),8.963930587070152)
(Nine Dead Gay Guys (2003),8.895781428393946)
...
[success] Total time: 317 s, completed 11-mar-2016 10:41:22
Muy sencillo, ¿verdad?
Entradas relacionadas:
Entradas relacionadas:
[Spark MLlib] Filtrado Colaborativo (III): Recomendación por Ítem (pendiente)