{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Algorithme de la factorisation matricielle pour la recommandation" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Question 1. Téléchargez la base MovieLens ml-latest-small.zip décrite dans Movie Lens. Renseignez le tableau suivant en utilisant le fichier ratings.csv qui contient les identifiants des utilisateurs (userId), qui ont vu des films, identifiés par leurs movieId, et leurs donnant une note (rating) à un temps donné (timestamp). à Partir de ce fichier nous allons construire deux bases dites \"base d'apprentissage\" ($\\mathcal{R}_{app}$) et \"base de test\" ($\\mathcal{R}_{tst}$). Pour cela, triez pour chaque utilisateur, les films dans l'ordre croissant des timestamp. :\n", "\n", "| Nombre d'utilisateurs | Nombre de films | Nombre de scores ($|\\mathcal{R}|$) | Sparsité* (%) |\n", "|-----------------------|-----------------|------------------------------------|----------|\n", "| | | | |\n", "\n", "\\*Sparsité est le pourcentage d'entrées non-renseignées (non-scorées) de la matrice utilisateurs/films." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "Pour tous les utilisateurs, mettez les 80% des quadruplets correspondants à chaque utilisateur et des films les plus anciens qu'il a notés dans $\\mathcal{R}_{app}$. Pour l'utilisateur en cours de traitement, mettez le reste de ses quadruplets (correspondants aux 20% des films les plus récents notés par cet utilisateur) dans $\\mathcal{R}_{tst}$. Ainsi $\\mathcal{R}_{app}$ et $\\mathcal{R}_{tst}$ tous les utilisateurs de la base.\n", "\n", "NB: On pourra utiliser la fonction read_csv de la bibliothèque pandas pour la lecture du fichier ratings.csv" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Question 2. Combien y-a-t-il de films qui sont scorés par les utilisateurs dans la base de test et qui ne sont pas présents dans la base d'apprentissage? Combien y-a-t-il de films qui sont scorés par les utilisateurs dans la base d'apprentissage et qui ne sont pas présents dans la base de test? Remplir le tableau:\n", "\n", "| \\# de films de $\\mathcal{R}_{tst}$ non présents dans $\\mathcal{R}_{app}$ | \\# de films de $\\mathcal{R}_{app}$ non présents dans $\\mathcal{R}_{tst}$ |\n", "|------------------------------------|----------|\n", "| | |\n", "\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Filtrez les bases $\\mathcal{R}_{app}$ et $\\mathcal{R}_{tst}$ en ne gardant que les films présents dans les deux. Mettez les quadruplets de $\\mathcal{R}_{tst}$ conctenant des films qui ne sont pas présents dans $\\mathcal{R}_{app}$; dans une liste appelée coldstart (dans la suite nous allons nous intéresser qu'aux films qui sont présents dans les deux bases $\\mathcal{R}_{app}$ et $\\mathcal{R}_{tst}$).\n", "\n", "\n", "NB. Vous pouvez stocker les listes sur le disque en utilisant la fonction pickle.dump; pour lire la liste depuis ce fichier vous pouvez utiliser la fonction pickle.load." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* À quoi est dû, à votre avis, cet absence de films dans une base mais présents dans l'autre?\n", "* Quel est le nombre de films présents dans les deux bases $\\mathcal{R}_{app}$ et $\\mathcal{R}_{tst}$?\n", "* Quelle est la taille de la liste coldstart?\n", "* Quelles sont les tailles des bases $\\mathcal{R}_{app}$ et $\\mathcal{R}_{tst}$ après filtrage?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "|\\# de films présents dans $\\mathcal{R}_{app}$ et $\\mathcal{R}_{tst}$ | \\|coldstart\\| | $\\left|\\mathcal{R}_{app}\\right|$ | $\\left|\\mathcal{R}_{tst}\\right|$ |\n", "|---------------------------------------------------------------------|-----------|-------------------------|---------------------------------|\n", "| | | | |" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Nous allons dans cette partie nous intéresser à l'implémentation de l'algorithme de recommandation vu en cours. Pour rappel, cet algorithme associe à chaque utilisateur $u$ et chaque film $i$ de la $\\mathcal{R}_{app}$ des vecteurs aléatoires respectifs, $\\mathbf{U}_u\\in\\mathbb{R}^d$ et $\\mathbf{V}_i\\in\\mathbb{R}^d$ dans le but de les apprendre de façon à ce que le produit scalaire $\\mathbf{U}_u^\\top\\mathbf{V}_i$ traduise l'appétance de l'utilisateur $u$ pour le film $i$.\n", "\n", "Cet apprentissage se fait en minimisant l'erreur quadratique:\n", "$$\n", "\\mathcal{L}({R}_{app},\\mathbf{U},\\mathbf{V})=\\sum_{r_{ui}\\in {R}_{app}} (r_{ui}-\\mathbf{U}_u^\\top\\mathbf{V}_i)^2+\\lambda (\\|\\mathbf{U}_u\\|^2 + \\|\\mathbf{V}_i\\|^2)\n", "$$\n", "Où $\\mathbf{U}$ (resp. $\\mathbf{V}$) est la matrice constituée par les vecteurs représentatifs des utilisateurs (resp. des produits), $r_{ui}$ le score associé par l'utilisateur $u$ au film $i$, $\\mathcal{R}_{app}=\\{r_{ui}\\in\\mathbb{R}|~u \\text{ et } i \\text{ sont dans la base d'apprentissage}\\}$ est l'ensemble des scores donnés par les utilisateurs aux films de la base d'apprentissage; et, $\\mathbf{U}_u$ (resp. $\\mathbf{V}_i$) est le vecteur représentatif de l'utilisateur $u$ (resp. item $i$) dans l'espace latent. \n", "\n", "Pour une implémentation efficace de l'algorithme du gradient, nous allons considérer sa version stochastique qui consiste à tirer aléatoire un utilisateur $u$ et un film $i$ dans l'ensemble des films qu'il a notés et de faire la mise a jour des vecteurs représentatifs $\\mathbf{U}_u$ et $\\mathbf{V}_i$ associés en minimisant l'erreur instantanée:\n", "$$\n", "\\ell(\\mathbf{U}_u,\\mathbf{V}_i)=(r_{ui}-\\mathbf{U}_u^\\top\\mathbf{V}_i)^2+\\lambda (\\|\\mathbf{U}_u\\|^2 + \\|\\mathbf{V}_i\\|^2)\n", "$$\n", "\n", "\n", "Dans ce cas les dérivées partielles de cette fonction par rapport aux vecteurs représentatifs de l'utilisateur $u$ et du film $i$ sont:\n", "$$\n", "\\frac{\\partial \\ell(\\mathbf{U}_u,\\mathbf{V}_i)}{\\partial \\mathbf{U}_u}=-2\\left(e_{ui}\\mathbf{V}_i-\\lambda \\mathbf{U}_u \\right)\\\\\n", "\\frac{\\partial \\ell(\\mathbf{U}_u,\\mathbf{V}_i)}{\\partial \\mathbf{V}_i}=-2\\left(e_{ui}\\mathbf{U}_u-\\lambda \\mathbf{V}_i \\right)\n", "$$\n", "où $e_{ui}=(r_{ui}-\\mathbf{U}_u^\\top\\mathbf{V}_i)$.\n", "\n", "Le pseudo-code est le suivant:\n", "\n", "`input: R_app, MaxEp, eps\n", "init : U,V aléatoirement\n", "epoque <- 0\n", "Lnew <- 1\n", "Lold <- 0\n", "while epoque<=MaxEp and |Lnew-Lold|>eps\n", " Lold <- Lnew\n", " Lnew <- 0\n", " for i in range(|R_app|)\n", " choisir un utilisateur u de façon aléatoire\n", " choisir un film i dans la liste des films notés par u de façon aléatoire \n", " U_u <- U_u+eta*(e_ui*V_i - lambda*U_u)\n", " V_i <- V_i+eta*(e_ui*U_u - lambda*V_i)\n", " Lnew <- Lnew + l(U_u,V_i)\n", " Lnew <- Lnew/|R_app|\n", " epoque <- epoque+1\n", "output: U,V`\n", "\n", "\n", "\n", "Question 3. Codez l'algorithme du gradient stochastique pour apprendre les vecteurs représentatifs des utilisateurs et des films sur la nouvelle filtrée $\\mathcal{R}_{app}$, en fixant la taille de l'espace de représentation à $d=5$. On fixera $\\eta=0.01$ et $\\lambda=0.1$.\n", "\n", "NB. On pourra utiliser la bibliothèque numpy qui définit l'ensemble des opérations algébriques sur les vecteurs et les matrices" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Question 4. On considère l'erreur moyenne absolue calculée sur les notes prédites par l'algorithme pour les films de la base de test :\n", "$$\n", "MAE(\\mathcal{R}_{test},\\mathbf{U},\\mathbf{V})=\\frac{1}{|\\mathcal{R}_{test}|}\\sum_{r_{ui}\\in {R}_{test}} |e_{ui}|=\\frac{1}{|\\mathcal{R}_{test}|}\\sum_{r_{ui}\\in {R}_{test}} |r_{ui}-\\mathbf{u}^\\top\\mathbf{v}|\n", "$$\n", "Quel est l'impact de la taille des vecteurs représentatifs des utilisateurs et des films sur les prédictions. Pour cela répéter la question 7 et reporter les erreurs $MAE$ pour différentes valeurs de $d$. \n", "\n", "\n", "| $d$=5 | $d$=25 | $d$= 50| $d$=100 |\n", "|----------|--------|---------|---------|\n", "| | | | |" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.7.6" } }, "nbformat": 4, "nbformat_minor": 1 }