<jupyter_start><jupyter_text>Sistemas de Recomendación - Clase II  Contenido1. [Ratings](Ratings)1. [Similitud entre usuarios y contenido](Similitud-entre-usuarios-y-contenido)1. [Algoritmos de recomendación](Algoritmos-de-recomendación)1. [Problemas con sistemas de recomendación](Problemas-con-sistemas-de-recomendación)  Ratings  ¿Qué es un rating?- Es una herramienta que establece el sentimiento de un **usuario** sobre el **contenido** que está consumiendo.- Se representa internamente como **un número en una escala** (e.g. de 0 a 5).     - Se busca que ese número pueda ser traducido a una representación gráfica para el usuario.- Pueden ser **explícitos** (dados directamente por el usuario) o **implícitos** (dados por las interacciones que realiza el usuario en una página).- Ejemplos: estrellas de Amazon, reputación de un vendedor en Mercado Libre, la cantidad de veces que uno reproduce una película en Netflix o una canción en Spotify.  Matriz de Usuario-Contenido- Es una matriz (tabla) con **una fila por cada usuario** y **una columna por cada item** de contenido (o visceversa).- El **contenido** de la matriz es el **rating** (explícito o implícito) que le asigna un usuario a cierto item de contenido.    - Desafío en el diseño de un sistema de recomendación: ¿Qué tipo de información utilizar para rellenar el contenido de la matriz?.- Es el punto de entrada para entrenar la mayoría de los algoritmos de recomendación.- El objetivo de los algoritmos de recomendación es **llenar huecos** en la matriz de usuario-contenido.  Matriz de Usuario-Contenido: Ejemplo  Matriz de Usuario-Contenido: Problemas de Dispersión- Las matrices de usuario-contenido tienden a ser **altamente dispersas**.- Las plataformas suelen tener **muchos usuarios e items**.     - Es raro encontrar a usuarios que interactúen con todos los items.- Deben poblarse con ratings (implícitos o explícitos), en caso contrario no brindan suficiente información a los algoritmos de recomendación.  Ratings explícitos vs. ratings implícitos.- Los datos para generar una matriz de usuario-contenido se obtienen de las **interacciones que el usuario hace con el sitio**.- Los **ratings explícitos** se consiguen a través de un **sistema específicamente diseñado**.    - El usuario nos provee su apreciación de un item.- Los **ratings implícitos** se consiguen a través de datos recolectados a partir del **comportamiento del usuario**.    - Se tiene que registrar lo que hace el usuario dentro del sitio. Desde compras hasta historial de revisión.- Sitios distintos tienen **distintas formas de capturar la información**. - No todas las aplicaciones se benefician de sistemas explícitos.  Ratings explícitos- Son la **forma más directa** de llenar la matriz de usuario-contenido.- Dependen de la **voluntad del usuario**.- ¿Qué tan **seguro está el usuario** de lo que le gusta?    - ¿Qué cosa en particular no le gusta? E.g. canción vs. género musical, artículo vs. tema de una noticia.- ¿Son confiables los sistemas de reputación?  Ratings implícitos- Se basan en el **monitoreo** de lo que hace el usuario.    - Dar un bajo rating a una película, pero reveerla una y otra vez.- Se busca encontrar un **número que defina la conformidad** de un usuario con un item.    - Cantidad de reproducciones de una canción, historial de navegación, compras, etc.- **No todo** lo que hace un usuario **es sinónimo de conformidad**.     - Traducir comportamiento en ratings es una tarea subjetiva, depende mucho del contexto.- El **tiempo es relevante**.    - No es lo mismo recomendar un bar de cerveza a las 9 a.m. que a las 9 p.m.    - Comprar algo que recién sale al mercado es más valioso que comprar algo más viejo.  Cálculo de ratings implícitos- Registramos las **acciones del usuario**.- Distintos **eventos** dan distintos **valores**.    - Buscar item.    - Entrar en los detalles de un item (o items de categoría similar).    - Averiguar medios de pago/envío.    - Preguntar.    - Comprar.- Buscamos llegar al rating máximo (e.g. comprar).     - Puede ser un valor continúo (e.g. porcentaje de una canción/video que el usuario reproduce)  Cálculo de ratings implícitos: fórmula$$    IR_{i,u} = w_1 \times \event_1 + w_2 \times \event_2 + \dots + w_n \times \event_n$$- $IR_{i,u}$: Rating implícito entre usuario *u* e item *i*.- $\event_i$: Número de veces que un evento ocurre.    - Pueden estar **acotados por un valor máximo**.    - Se pueden **pesar por el tiempo** desde que ocurrió el evento.- $w_1, \dots, w_n$: Pesos que se les da a los distintos eventos (basados en análisis).    - Estos pueden **calcularse mediante análisis o como parámetros de un clasificador** binario que busca encontrar la probabilidad de comprar un item.  Items poco frecuentes- Muchas veces los **eventos raros** nos dicen más sobre el usuario que cualquier otro evento.    - No es lo mismo comprar un DVD del "Señor de los Anillos" que comprar una edición de coleccionista con un poster.- Los eventos raros hacen **subgrupos de usuarios** más específicos.- Podemos **reponderar el rating** de un item (implícito o explícito) mediante esta información.$$    R^{(final)}_{i,u} = R_{i,u} \times \log\Big(\frac{N}{1+n}\Big)$$- $R_{i,u}$: Rating calculado originalmente (explícito o implícito)- $N$: Número total de usuarios.- $n$: Número de usuarios que compraron el item *i*.- $R^{(final)}_{i,u}$: Rating reponderado.  Similitud entre usuarios y contenido  Métricas de similitud- Buscamos ver que tan **cercanos están dos elementos**.- Se busca **recomendar contenido entre usuarios similares**.- Una **función (o métrica) de similitud** busca comparar dos elementos.    - $\text{sim}(i_1, i_2) = 1$ implica igualdad.    - $\text{sim}(i_1, i_2) = 0$ implica dos elementos completamente opuestos.- Las métricas de similitud **dependen del conjunto de datos**. Algunas funcionan mejor que otras.  Similitud de Jaccard- Se define **entre dos conjuntos**.- Suelen ser mejor aplicadas sobre datos binarios (e.g. un usuario compró o no un item).- Evalúa **cantidad de interacciones** en común vs. independientes.- La distancia entre dos elementos $i$ y $j$, se define formalmente como:$$    \text{Jaccard}(i, j) = \frac{|\{u:~u~\text{compró}~i \land j\}|}{|\{u:~u~\text{compró}~i \lor j\}|}$$- El término "compró" puede cambiarse por cualquier interacción que se busque modelar.  Distancia Manhattan- Se define entre **valores cuantitativos**.- Conocida también como norma $L_1$.- La distancia Manhattan se piensa como la **cantidad de cuadras que hay que hacer para llegar de un punto al otro** en Manhattan.- Se define formalmente entre dos elementos $i$ y $j$ como:$$    \text{Manhattan}(i, j) = \sum_{u=1}^{n}|r_{i,u} - r_{j,u}|$$  Distancia Euclídea- Se define entre **valores cuantitativos**.- Conocida también como norma $L_2$.- Es la **distancia recta más corta entre dos puntos**. Proviene del teorema de Pitágoras.- Se define formalmente entre dos elementos $i$ y $j$ como:$$    \text{Euclidean}(i, j) = \sqrt{\sum_{u=1}^{n}(r_{i,u} - r_{j,u})^2}$$  Similitud Coseno- Se define entre **valores cuantitativos**.- Mide el **ángulo entre vectores** que representen nuestros datos.- Se define formalmente entre dos elementos $i$ y $j$ como:$$    \text{Cosine}(i, j) = \frac{\sum_{u=1}^{n}r_{i,u}r_{j,u}}{\sqrt{\sum_{u=1}^{n}r_{i,u}^2}\sqrt{\sum_{u=1}^{n}r_{j,u}^2}}$$- En general **se normaliza utilizando promedios** (parecido a lo que se hace con el coeficiente de Pearson).    - La idea es eliminar el sesgo de los usuarios.  Coeficiente de correlación de Pearson- Mide que tan **correlacionadas están las puntuaciones** entre dos elementos (pueden ser usuarios o items).- Compara los **elementos contra el promedio** y evalúa que tan diferentes son.- Se define formalmente entre dos elementos $i$ y $j$ como:$$    \text{Pearson}(i, j) = \frac{\sum_{u=1}^{n}(r_{i,u}-\bar{r_i})(r_{j,u}-\bar{r_j})}{\sqrt{\sum_{u=1}^{n}(r_{i,u}-\bar{r_{i}})^2}\sqrt{\sum_{u=1}^{n}(r_{j,u}-\bar{r_{j}})^2}}$$  Clustering- Definidas las métricas de similitud (o distancia) podemos hacer uso de **algoritmos de clustering**.- La idea es **buscar usuarios o items cercanos y agruparlos**.- Técnicas clásicas de clustering sirven: K-Means, Jerárquico, Expectation-Maximization.  Algoritmos de recomendación  Filtrado colaborativo- Es uno de los modelos de recomendación más básicos pero **muy robusto**.- Se basa en la idea **asociar usuarios similares de acuerdo a sus gustos**, expresados en los ratings.- Puede aplicarse sobre **usuarios** (user-based collaborative filtering).    - Dados dos usuarios similares $u$ y $v$, recomendamos el item $i$ al usuario $u$ que adquirió el usuario $v$ y todavía no fue consumido por $u$.- Puede aplicarse sobre **items** (item-based collaborative filtering).    - Dados dos items similares $i$ y $j$ y un usuario $u$ que adquirió $i$, le recomendamos $j$.  Filtrado basado en usuarios vs items- Los items se consideran **más estables**.- Un sistema puede tener **pocas interacciones para cada usuario**.- **Depende de la cantidad** de usuarios o items en el sistema y **su variabilidad**.- Los items proveen menos posibilidad de variación. Se **estancan rápido**.- Los **usuarios evolucionan**, las **recomendaciones pueden ser más dinámicas**.  Pipeline de filtrado colaborativo- Partimos de la **matriz de usuario-contenido**.- Calculamos la **similitud entre los elementos**.- Ordenamos los **elementos que más se parecen** al elemento actual.- Elegimos un **vecindario sobre el cuál calcular** la predicción.- Utilizamos los **ratings del usuario y su vecindario** para calcular el rating actual.  Vecindario para calcular predicción- A la hora de calcular un rating es necesario elegir aquellos **elementos que sean los más similares**.- La opción de **clustering** nos da un vecindario en el cluster de un elemento.- Los **K** vecinos más cercanos da un número fijo de vecinos.    - Siempre nos brinda valores con los que calcular.- Un **umbral** en los ratings más cercanos dá un número variable de vecinos.    - Puede dejar el algoritmo sin datos para calcular la predicción.- El **análisis de datos y la tolerancia** sobre las predicciones nos dan idea de qué método utilizar.  Cálculo de la predicción- A partir de los datos de ratings de un usuarios, las similitudes, y el vecindario, **completamos las matriz de usuario-contenido** con el cálculo de nuevos ratings:$$    r^{(p)}_{i,u} = \bar{r_u} + \frac{\sum_{j \in N}\text{sim}(i,j)(r_{u,j}-\bar{r_j})}{\sum_{j \in N}\text{sim}(i,j)}$$- $r^{(p)}_{i,u}$: Es el valor predicho para el rating del item $i$ por el usuario $u$.- $\bar{r_u}$: Es la media de ratings dada por el usuario $u$ a todos los items que puntuó.- $j \in N$: Son todos los items $j$ del vecindario $N$.- $\bar{r_j}$: Es el rating medio del item $j$ (es opcional).- $\text{sim}(i,j)$: Es la métrica de similitud entre los items $i$ y $j$.  Surpr!se<jupyter_code>import pandas as pd

from surprise import Dataset, Reader, KNNWithMeans
from surprise.accuracy import rmse
from surprise.model_selection import cross_validate, train_test_split
ratings = pd.read_csv("../data/ml-latest-small/ratings.csv")

reader = Reader(rating_scale=(ratings.rating.min(), ratings.rating.max()))

ratings = Dataset.load_from_df(ratings[["userId", "movieId", "rating"]], reader)
ratings_train, ratings_test = train_test_split(ratings, test_size=0.2)
model = KNNWithMeans(k=5).fit(ratings_train)
predictions = model.test(ratings_test)
print("RMSE on test: {:.4f}".format(rmse(predictions, verbose=False)))<jupyter_output><empty_output><jupyter_text>Surpr!se: Cross Validation<jupyter_code>model = KNNWithMeans(k=5, verbose=False)
cross_validated_metrics = cross_validate(model, ratings, measures=['RMSE', 'MAE'], cv=5, verbose=True)<jupyter_output><empty_output><jupyter_text>Filtrado basado en contenido- En lugar de utilizar los ratings, utilizamos la **información del contenido**.    - Metadatos como tema, descripción, género, etc.- Dependen mucho del **tipo de los datos**.    - Las películas tienen directores y actores. Podemos recomendar películas similares en base a actores que tengan en común.- Requiere de tres aspectos:    1. Analizador de contenido: Crea un modelo basado en la información de los items.    2. Perfil del usuario: Genera un perfil del usuario en base al contenido consumido. E.g. una lista de películas vistas.    3. Recuperación de contenido: Recomienda contenido basado en la similitud del perfil del usuario con el perfil de los items. E.g. iterando y comparando uno a uno los elementos de la lista.  Analizador de contenido- Se busca **describir los datos mediante sus metadatos**.- Puede dividirse entre **hechos** y **etiquetas** (tags).- Los **hechos** son indiscutibles, tienen que ver con los datos en sí. E.g. director o actor de una película.- Las **etiquetas** generalmente se piensan en cómo los usuarios **categorizan los items**. E.g. una lista de Spotify para **levantar el ánimo**.- **No hay división clara**. Pero los hechos tienden a ser menos subjetivos. Las etiquetas dependen más del usuario.  Vector del contenido- El analizador de contenido busca **representar items mediante vectores**.    - En filtrado colaborativo el **vector está dado por ratings**.    - Acá el vector es **producido mediante características**.- Se lo puede pensar como una **ingeniería de atributos** de aprendizaje automático.    - Los atributos pueden ser binarios, categóricos, cuantitativos, etc.  Vector del contenido: uso del texto- Muchas veces los **metadatos están en el texto** (e.g. título o descripción).- Las **técnicas clásicas de NLP** sirven para lidiar con esto.- Buscamos representar una descripción mediante una **bolsa de palabras**.    - Requerimos de un paso previo de **tokenización**.    - Generamos un **vector donde cada dimensión representa una palabra y el valor representa el conteo**.    - Es útil eliminar las **palabras vacías**.  Vector del contenido: TF-IDF- Podemos utilizar **TF-IDF** para reponderar de acuerdo a la **importancia de los términos**.- La idea es **pesar una palabra en un metadato** de acuerdo a que tanto ocurre en todos los metadatos.$$    \text{tf-idf}(w, d) = f_{w,d} \times \text{idf}(w)$$$$    \text{idf}(w) = \frac{|D|}{|\{d \in D : w \in d\}|}$$- $w$: Es una palabra.- $d$: Es un documento.- $f_{w,d}$: Es la cantidad de veces que $w$ ocurre en $d$.- $D$: Es el conjunto de todos los documentos.  Otras técnicas: LDA, LSA, word2vec- Otras formas de representar datos es mediante otras técnicas de NLP.- LDA permite **representar los documentos mediante temas**. Los temas son representados mediante **palabras**.- LSA (o LSI) obtiene **variables latentes entre documentos**. Busca encontrar relaciones que no están a simple vista.- word2vec es un **algoritmo para representar palabras** basado en información de co-ocurrencia.  Matriz de contenido: Ejemplo  Crear el perfil de usuario- Ya habiendo creado el vector de contenido necesitamos crear **el perfil de usuario**.- Se puede hacer **sumando los vectores de contenido con los que el usuario interactuó**.    - Es sensato **normalizar este vectors**.- El vector final **sirve para encontrar contenido similar para el usuario**.  Perfil de usuario: Ejemplo- Suponiendo que el usuario haya puesto 5 estrellas para "Raiders of the Lost Ark" y 3 para "La La Land".  Ejemplo: Géneros de películas<jupyter_code>movies = pd.read_csv("../data/ml-latest-small/movies.csv")
movies['genres'] = movies['genres'].apply(lambda x: x.split("|"))
movies.head()<jupyter_output><empty_output><jupyter_text>Ejemplo: Géneros de películas - Vector de contenido<jupyter_code>genres = set(g for G in movies['genres'] for g in G)

for g in genres:
    movies[g] = movies.genres.transform(lambda x: int(g in x))
    
movie_genres = movies.drop(columns=['movieId', 'title','genres'])
movie_genres.head()<jupyter_output><empty_output><jupyter_text>Ejemplo: Géneros de película - Similitud entre géneros<jupyter_code>from sklearn.metrics.pairwise import cosine_similarity

cosine_sim = cosine_similarity(movie_genres, movie_genres)<jupyter_output><empty_output><jupyter_text>Ejemplo: Géneros de película - Sistema de recomendación<jupyter_code>def movie_finder(title):
    return movies[movies['title'].str.contains(title)]['title'].tolist()

movie_idx = dict(zip(movies['title'], list(movies.index)))
title = movie_finder('Toy Story')[0]
n_recommendations = 5

idx = movie_idx[title]
sim_scores = list(enumerate(cosine_sim[idx]))
sim_scores = sorted(sim_scores, key=lambda x: x[1], reverse=True)
sim_scores = sim_scores[1:(n_recommendations+1)]
similar_movies = [i[0] for i in sim_scores]

print("Recomendaciones para {}:".format(title))
for movie in movies['title'].iloc[similar_movies]:
    print("\t{}".format(movie))<jupyter_output><empty_output><jupyter_text>Factorización de matrices- Se busca **reducir dimensiones** de manera que se elimine el ruido.- Las dimensiones más importantes, que quedan cuando se reducen las demás, se llaman **factores latentes**.    - La idea es representar usuario y contenido a través de factores latentes.    - Nuevos usuarios (o items), con cierto contenido inicial, son fácilmente agregados.- El **algoritmo de SVD** del álgebra lineal nos brinda esa oportunidad.  Algoritmo de SVD- **M** es la matriz de ratings.- **U** es la matriz de características de usuarios.- **$\sigma$** es la matriz de valores singulares.- **V$^T$** es la matriz de características de items.  Algoritmo de SVD- Elegimos **r** valores singulares.  Algoritmo de SVD- Podemos calcular valores faltantes en base a los vectores de representación.  Ejemplo: Películas  Ejemplo: Películas  Ejemplo: Películas  Algoritmo de Simon Funk- El algoritmo de SVD **tiene algunos problemas**:    - Calcular y actualizarlo es lento.    - Necesariamente requiere valores para reemplazar los lugares vacíos.- <NAME> desarrolló [un algoritmo](https://sifter.org/~simon/journal/20061211.html) que a grandes razgos **calcula SVD iterativamente**.- La idea es definir una **función hipótesis y una función de coste**.- Una vez definidas, **se minimiza** la función de coste respecto de los parámetros de la función hipótesis.- Se puede **entrenar** como un algoritmo supervisado mediante **descenso por la gradiente**.  Algoritmo de Simon Funk: Hipótesis- Se define la función de hipótesis para un rating de un usuario a un item como:$$    \hat{r}_{i,u} = \mu + b_u + b_i + q_i^Tp_u$$- $\hat{r}_{i,u}$: Es el rating a calcular.- $\mu$: Es el promedio de todos los ratings.- $b_u$: Es el sesgo (*bias*) del usuario.- $b_i$: Es el sesgo (*bias*) del item.- $p_u$: Es el vector de factores latentes que representa el usuario $u$.- $q_i$: Es el vector de factores latentes que representa el item $i$.  Algoritmo de Simon Funk: Función de costo- Se define la función de costo como:$$    \sum_{r_{i,u} \in R_{train}} (r_{i,u} - \hat{r}_{i,u})^2 + \lambda\big(b_i^2 + b_u^2 + ||q_i||^2 + ||p_u||^2\big)$$- $r_{i,u}$: Son los ratings del conjunto de entrenamiento.- $\hat{r}_{i,u}$: Es el rating predicho.- $\mu$: Es el promedio de todos los ratings.- $b_u$: Es el sesgo (*bias*) del usuario.- $b_i$: Es el sesgo (*bias*) del item.- $p_u$: Es el vector de factores latentes que representa el usuario $u$.- $q_i$: Es el vector de factores latentes que representa el item $i$.  Surpr!se<jupyter_code>from surprise import SVD

model = SVD(n_factors=100, n_epochs=20, random_state=42).fit(ratings_train)
predictions = model.test(ratings_test)
print("RMSE on test: {:.4f}".format(rmse(predictions, verbose=False)))<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.1115717283381954, 'fraction_numerical': 0.003838476892369108, 'mean_word_length': 4.642506497256714, 'pattern_counts': {'":': 0, '<': 30, '<?xml version=': 0, '>': 30, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '7', 'dir': 'jupyter-structured-clean-dedup', 'id': '34000', 'license': 'MIT', 'n_tokens_mistral': 6657, 'n_tokens_neox': 6651, 'n_words': 2529, 'path': 'teorico/clase_2.ipynb', 'repo_name': 'guillermohilal/SistemasDeRecomendacion', 'size': '19543'}