1 Tipos de sistemas recomendadores

Los sistemas recomendadores se agrupan en 2 grandes grupos:

  1. Content based: toman en cuenta el contexto y características tanto de los usuarios como los ítems. Ej: modelos de clasificación para saber si un usuario elegirá o no una película en base a sus características, las características de la película y el comportamiento pasado del usuario.

  2. Collaborative Filtering: modelos basados exclusivamente en el comportamiento de los usuarios con respecto a los ítems. Ej: rating dado por parte de los usuarios a las películas. Se pueden utilizar los ratings para encontrar usuarios (o películas) similares para recomendar en base a la similitud.

2 Recommenderlab

Recommenderlab es un paquete de R que entrega un framework de trabajo y evaluación de modelos recomendadores del tipo Collaborative Filtering.

En este documento se probarán los siguientes algoritmos incluidos en el paquete:

  • Random (RANDOM): Entregar recomendaciones aleatorias. Este modelo se realizará para establecer un punto de comparación para los otros modelos.
  • Popular (POPULAR): Entregar recomendaciones en base a los items más populares. A cada usuario se le recomiendan los ítems mejor rankeados que no haya rankeado previamente.
  • User based collaborative filtering (UBCF): Encontrar usuarios similares y recomendar en base a los vecinos más cercanos.
  • Item based collaborative filtering (IBCF): Encontrar similitud entre ítems y realizar recomendaciones en base a los items más similares a los preferidos por el usuario.
  • Singular Value Decomposition (SVD): Factorización de matrices para descomponer la matriz usuario-ítem en dos matrices que contienen latent features.
  • Aleternating last squares (ALS): Factorización de matrices, utiliza el algoritmo alterating last squares.

2.1 Comandos básicos

Cargamos los paquetes

library(recommenderlab)
library(dplyr)

2.1.1 Crear realRatingMatriz

El objeto que utiliza recommenderlab para entrenar los modelos son matrices usuario-ítem que pueden ser de tipo realRatingMatrix o binaryRatingMatrix

Esto se puede realizar a partir de una matriz ya creada o a partir de un data.frame con las con 3 columnas en orden: user | item | rating

df <- read.csv("datasets/ratings.csv")
df$timestamp <- NULL

head(df)
##   userId movieId rating
## 1      1       1      4
## 2      1       3      4
## 3      1       6      4
## 4      1      47      5
## 5      1      50      5
## 6      1      70      3

Para crear el objeto de clase realRatingMatrix se utiliza la función as.

ui <- df %>% as("realRatingMatrix")
ui[1:10,1:10] %>% getRatingMatrix
## 10 x 10 sparse Matrix of class "dgCMatrix"
##    [[ suppressing 10 column names '1', '2', '3' ... ]]
##                         
## 1  4.0 . 4 . . 4 . . . .
## 2  .   . . . . . . . . .
## 3  .   . . . . . . . . .
## 4  .   . . . . . . . . .
## 5  4.0 . . . . . . . . .
## 6  .   4 5 3 5 4 4 3 . 3
## 7  4.5 . . . . . . . . .
## 8  .   4 . . . . . . . 2
## 9  .   . . . . . . . . .
## 10 .   . . . . . . . . .

Una matriz realRatingMatrix se puede parsear tanto a matrix, data.frame o list.

ui %>% as("matrix") %>% .[1:10,1:10]
##      1  2  3  4  5  6  7  8  9 10
## 1  4.0 NA  4 NA NA  4 NA NA NA NA
## 2   NA NA NA NA NA NA NA NA NA NA
## 3   NA NA NA NA NA NA NA NA NA NA
## 4   NA NA NA NA NA NA NA NA NA NA
## 5  4.0 NA NA NA NA NA NA NA NA NA
## 6   NA  4  5  3  5  4  4  3 NA  3
## 7  4.5 NA NA NA NA NA NA NA NA NA
## 8   NA  4 NA NA NA NA NA NA NA  2
## 9   NA NA NA NA NA NA NA NA NA NA
## 10  NA NA NA NA NA NA NA NA NA NA
ui %>% as("data.frame") %>% head
##      user item rating
## 1       1    1      4
## 326     1    3      4
## 434     1    6      4
## 2108    1   47      5
## 2380    1   50      5
## 2860    1   70      3

2.1.2 Binarizar matriz

Se puede binarizar una matriz, con binarize y entregando un mínimo ratig para evaluar la transformación.

ui_bin <- ui %>% binarize(minRating = 4)
ui_bin[1:10,1:10] %>% as("matrix")
##        1     2     3     4     5     6     7     8     9    10
## 1   TRUE FALSE  TRUE FALSE FALSE  TRUE FALSE FALSE FALSE FALSE
## 2  FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## 3  FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## 4  FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## 5   TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## 6  FALSE  TRUE  TRUE FALSE  TRUE  TRUE  TRUE FALSE FALSE FALSE
## 7   TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## 8  FALSE  TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## 9  FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## 10 FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE

2.1.3 Operaciones matriciales

Las matrices realRatingMatrix aceptan operaciones matriciales como rowSums, rowMeans, dim, indexar, etc.

Por ejemplo,a continuación se muestra el rating promedio de la primera película:

colMeans(ui[,1])
##       1 
## 3.92093

2.1.4 Normalizar

La función normalize permite normalizar con los métodos “center” y “Z-score” tanto filas como columnas (row = FALSE).

ui %>% getRatings %>% hist(main = "Ratings")

ui %>% normalize %>% getRatings %>% hist(main = "Rating normalizados por usuario")

2.2 Ejemplo 0: recomendador de popularidad

Para ejemplificar crearemos un recomendador de popularidad utilizando los primeros 600 usuarios y predeciremos el top 5 para los siguientes 2 usuarios.

rec_pop <- Recommender(ui, "POPULAR")
pred_pop <- predict(rec_pop, ui[601:602], type = "topNList", n = 5)

Para entrenar un modelo, siempre se utiliza la función Recommender, se le entrega la data de entrenamiento y el tipo de modelo a utilizar. Para ver el listado de modelos y parámetros se pueden consultar con recommenderRegistry$grep_entries().

La función predict funciona como con cualquier otro modelo, pero además se le debe decir qué retornar (“topNList” o “ratings”). En caso de retornar topNList se requiere ingresar el número n.

Para ver los resultados, se puede desplegar una lista como a continuación:

pred_pop %>% as("list")
## $`601`
## [1] "356"  "296"  "260"  "593"  "1196"
## 
## $`602`
## [1] "2571" "2959" "1196" "858"  "1198"

2.3 Ejemplo 1: evaluación de algoritmos según rating

En la evaluación por rating, se busca predecir el rating que los usuarios asignarán a los ítems, es por esto que se mide con métricas de error para regresiones como RMSE, o el MAE.

2.3.1 Limpiar base de datos

La idea acá es no entregar mucho ruido al modelo, por lo que en primer lugar se verá cuál es el mínimo de películas rankiadas por usuario y el mínimo de ratings que tiene una película.

# Mínimo de ratings por usuario
rowCounts(ui) %>% as("matrix") %>% min
## [1] 20
# Mínimo de ratings por película
colCounts(ui) %>% as("matrix") %>% min
## [1] 1

El mínimo de ratings por usuarios es 20, mientras que el mínimo de ratings por película es 1, es decir hay al menos una película que sólo 1 usuario la rankeó. Para eliminar ruido en el modelo estableceremos el mínimo de ratings por película de 10.

ui <- ui[,colCounts(ui)>= 20]

2.3.2 Crear schema de evaluación

Con la finalidad de realizar una evaluación de modelos, se realizará un split de 90% train y 10% test.

eval_scheme <- evaluationScheme(ui, method = "split", train = 0.9, given = 5)

Debido a que se predicen ratings de ítems en base a ratings sobre otros ítems que haya realizado el usuario, se debe asumir como “conocidos” solo una parte de los ratings. Esto está dado por el parámetro given. (En caso de que given sea negativo, representa “todos menos n”)

2.3.3 Obtener data de train y test

Luego, con getData se obtienen los siguientes datos:

  • train: data de entrenamiento
  • known: data de testeo conocida. Son los ratings que se asumen dados por el usuario que se utilizarán para predecir.
  • unknown: data de testeo que se asume desconocida. Sobre esta data finalmente se calcula el error.
train <- eval_scheme %>% getData("train")
known <- eval_scheme %>% getData("known")
unknown <- eval_scheme %>% getData("unknown")

2.3.4 Entrenar modelos

Se entrenan diferentes recomendadores utilizando la data train

r1 <- Recommender(train, "RANDOM")
r2 <- Recommender(train, "UBCF")
r3 <- Recommender(train, "IBCF")
r4 <- Recommender(train, "SVD")
r5 <- Recommender(train, "ALS")

2.3.5 Predecir

En este caso a la función predict es de tipo “ratings” ya que se está prediciendo el rating con el que el usuario valorará un ítem. La predicción se debe realizar utilizando la data know.

p1 <- predict(r1, known, type = "ratings")
p2 <- predict(r2, known, type = "ratings")
p3 <- predict(r3, known, type = "ratings")
p4 <- predict(r4, known, type = "ratings")
p5 <- predict(r5, known, type = "ratings")

2.3.6 Cálculo del error

Para calcular las métricas de error (MAE, MRSE, MSE) sólo es necesario utilzar la función calcPredictionAccuracy. Para calcular el error de la prediccin se ocupa la dara unknown

error <- rbind("random" = calcPredictionAccuracy(p1, unknown),
               "ubcf" = calcPredictionAccuracy(p2, unknown),
               "ibcf" = calcPredictionAccuracy(p3, unknown),
               "svd" = calcPredictionAccuracy(p4, unknown),
               "als" = calcPredictionAccuracy(p5, unknown))
error
##             RMSE       MSE       MAE
## random 1.2843928 1.6496648 0.9736144
## ubcf   1.0972598 1.2039790 0.8512779
## ibcf   1.4058584 1.9764379 0.9993613
## svd    1.0491321 1.1006782 0.8067503
## als    0.9703985 0.9416733 0.7628789

2.4 Ejemplo 2: evaluación de algoritmos según topN

La evaluación de predicción según topN predice el top de ítems que el usuario preferirá, pero la evaluación se realiza utilizando los ratings, por lo tanto es necesario establecer un criterio para los ratings para saber si el usuario lo prefiere o no.

2.4.1 Schema de evaluación

En este ejemplo, dado que son ratings de películas de 1 a 5, se establecerá un buen rating mayor o igual a 4.

eval_scheme <- evaluationScheme(ui, method = "split", train = 0.9, given = 5, goodRating = 4)

2.4.2 Crear listado de algoritmos

algos <- list("random" = list(name = "RANDOM", param = NULL),
              "UBCF_10nn" = list(name = "UBCF", param = list(nn = 10)),
              "UBCF_50nn" = list(name = "UBCF", param = list(nn = 50)),
              "IBCF_Pearson" = list(name = "IBCF", param = list(method = "Pearson")),
              "SVD" = list(name = "SVD"),
              "ALS" = list(name = "ALS"),
              "ALS_5" = list(name = "ALS", param = list(n_factors = 5)))

2.4.3 Evaluar algoritmos

Se evaluarán los algoritmos para n = 1,3,5,10,15,20. La función eval entrena los algoritmos, predice y entrega la evaluación para todos los algoritmos.

eval <- evaluate(eval_scheme, algos, type = "topNList", n = c(1,3,5,10,15,20))
## RANDOM run fold/sample [model time/prediction time]
##   1  [0sec/0.17sec] 
## UBCF run fold/sample [model time/prediction time]
##   1  [0sec/0.55sec] 
## UBCF run fold/sample [model time/prediction time]
##   1  [0.02sec/0.67sec] 
## IBCF run fold/sample [model time/prediction time]
##   1  [18.41sec/0.03sec] 
## SVD run fold/sample [model time/prediction time]
##   1  [0.15sec/0.1sec] 
## ALS run fold/sample [model time/prediction time]
##   1  [0sec/60.04sec] 
## ALS run fold/sample [model time/prediction time]
##   1  [0sec/58.89sec]

Luego, se puede graficar la curva ROC y un gráfico Precision / Recall

plot(eval)

plot(eval,"prec/rec")

getConfusionMatrix(eval[["SVD"]])
## [[1]]
##           TP         FP       FN       TN precision     recall        TPR
## 1  0.2459016  0.7540984 56.52459 1234.475 0.2459016 0.00479640 0.00479640
## 3  0.6393443  2.3606557 56.13115 1232.869 0.2131148 0.01320815 0.01320815
## 5  1.0983607  3.9016393 55.67213 1231.328 0.2196721 0.02501414 0.02501414
## 10 1.7540984  8.2459016 55.01639 1226.984 0.1754098 0.03595916 0.03595916
## 15 2.2950820 12.7049180 54.47541 1222.525 0.1530055 0.04858423 0.04858423
## 20 2.8524590 17.1475410 53.91803 1218.082 0.1426230 0.05892988 0.05892988
##             FPR
## 1  0.0006086475
## 3  0.0019038136
## 5  0.0031398981
## 10 0.0066299250
## 15 0.0102382183
## 20 0.0138209165