Master 2 Machine Learning — Universite de Strasbourg

Systeme de recommandation de films

Construction et comparaison de trois architectures de filtrage collaboratif sur le jeu de donnees MovieLens 25M : similarite cosinus item-based, filtrage user-based et factorisation matricielle par SVD tronquee, avec visualisation de l’espace latent par UMAP.

MovieLens 25M Collaborative Filtering TruncatedSVD — k=50 UMAP Python — SciPy — scikit-learn 25 000 095 interactions

Description des donnees

Le jeu de donnees MovieLens 25M, publie par le GroupLens Research Lab de l’Universite du Minnesota, est une reference standard dans la litterature sur les systemes de recommandation. Il regroupe 25 millions d’interactions collectees aupres de 162 541 utilisateurs sur un catalogue de plus de 62 000 films. Deux fichiers CSV constituent le socle de ce projet.

162 541 Utilisateurs
209 171 Films (index max)
25 000 095 Interactions totales
99.93 % Taux de sparsité
71 Mediane notes / utilisateur
6 Mediane notes / film

La donnee structurelle la plus importante est le taux de sparsité de 99.93 % : sur les 34 milliards de paires (utilisateur, film) concevables, moins d’une sur mille a effectivement donne lieu a une note. Tout l’enjeu de la modelisation est de combler intelligemment ces vides a partir des rares interactions observees.

Structure brute

# movies.csv movieId title genres 0 1 Toy Story (1995) Adventure|Animation|Children|Comedy|Fantasy 1 2 Jumanji (1995) Adventure|Children|Fantasy 2 3 Grumpier Old Men (1995) Comedy|Romance # ratings.csv userId movieId rating timestamp 0 1 296 5.0 1147880044 1 1 306 3.5 1147868817 2 1 307 5.0 1147868828

Distribution des notes

L’echelle de notation s’etend de 0.5 a 5.0 par pas de 0.5. La distribution est asymetrique a gauche avec un pic marque sur les notes entières, en particulier 4.0, traduisant une propension des utilisateurs a evaluer positivement les films qu’ils ont choisi de voir.

Distribution des ratings
Figure 1. Distribution des notes sur l’ensemble des 25 millions d’interactions. La note 4.0 est la plus frequente ; les demi-notes (3.5, 4.5) sont systematiquement moins utilisees que les notes entières.

Distributions d'activite utilisateurs et films

Les deux distributions suivent une loi de puissance prononcee. En echelle lineaire, la masse des observations est concentree sur un petit nombre de valeurs elevees, rendant la queue droite invisible. L’echelle logarithmique revele la structure complete : la majorite des utilisateurs ont note entre 20 et 200 films, tandis que la majorite des films n’ont recu que quelques notes.

Distributions d'activite
Figure 2. Distributions lineaire (haut) et logarithmique (bas) du nombre de notes par utilisateur (gauche) et par film (droite). La mediane utilisateur est de 71 notes ; la mediane film est de seulement 6 notes. Ce decalage entre moyenne (423.4) et mediane (6.0) pour les films confirme une distribution en loi de puissance extremement asymetrique.
Implication methodologique
Les films presentant moins de 50 notes ne peuvent pas produire de vecteurs de representation fiables. Un seuil de filtrage a 50 evaluations minimales est applique aussi bien aux utilisateurs qu’aux films avant toute modelisation.
Dimensions apres filtrage : 102 492 utilisateurs, 13 176 films Sparsité apres filtrage : 98.31 %

Matrice utilisateur-item : visualisation de la sparsité

La matrice brute de dimension 162 541 x 209 171 represente theoriquement 34 milliards de cellules. Son stockage en dense serait physiquement impossible (environ 270 Go en float64). Le format CSR (Compressed Sparse Row) de SciPy ne memorise que les 25 millions d’entrees non nulles, ramenant l’empreinte memoire a quelques centaines de megaoctets.

Sparsité de la matrice
Figure 3. Gauche : heatmap des 50 premiers utilisateurs et films, illustrant la heterogeneite locale des interactions. Droite : visualisation globale de la sparsité par nuage de points ; chaque point represente une interaction existante. La concentration visible sur la diagonale superieure gauche correspond aux films populaires et aux utilisateurs actifs.
Python — Construction de la matrice sparse CSR
def build_user_item_matrix(df_ratings):
    row_indices = df_ratings['userId'].values - 1
    col_indices = df_ratings['movieId'].values - 1
    ratings     = df_ratings['rating'].values

    n_users  = df_ratings['userId'].max()
    n_movies = df_ratings['movieId'].max()

    # CSR : seules les valeurs non nulles sont stockees
    user_item_matrix = csr_matrix(
        (ratings, (row_indices, col_indices)),
        shape=(n_users, n_movies)
    )
    return user_item_matrix, n_users, n_movies

Approche methodologique

Le projet repose exclusivement sur le filtrage collaboratif, qui exploite uniquement les interactions entre utilisateurs et films sans recourir aux attributs intrinseques du contenu. Ce choix se justifie par deux raisons complementaires : MovieLens ne fournit pas de metadonnees riches au format textuel utilisable directement, et le filtrage collaboratif offre un spectre de complexite algorithmique suffisamment etendu pour une comparaison instructive.

Methode 1

Item-Based CF

Similarite cosinus entre les vecteurs d’evaluations des films dans l’espace utilisateur. Deux films sont proches s’ils sont notes de facon coherente par les memes individus.

Methode 2

User-Based CF

Identification des utilisateurs au profil de gout similaire a un utilisateur cible. Les films bien notes par ces voisins et non encore vus constituent le jeu de recommandations.

Methode 3

SVD Tronquee — k=50

Decomposition matricielle projetant utilisateurs et films dans un espace latent commun de dimension k=50. La prediction de notes passe par le produit scalaire dans cet espace compresse.

Pipeline general

ratings.csv Matrice sparse CSR Filtrage (seuil 50) Centrage utilisateur CF item-based CF user-based TruncatedSVD UMAP 2D

Films d'ancrage

Trois films sont choisis comme points d’ancrage pour evaluer qualitativement les recommandations. Leur selection est deliberement contrastee afin de couvrir des profils tres differents en termes de popularite, de genre et d’audience.

FilmmovieIdGenre dominantProfil
The Greatest Showman (2017) 180 985 Musical / Drama Grand public, forte audience
The Lion King (2019) 203 222 Animation / Adventure Blockbuster familial recent
I Want to Eat Your Pancreas 198 611 Animation japonaise Film de niche, peu note

Filtrage collaboratif item-based

L’approche item-based evalue la proximite entre deux films en comparant leurs vecteurs de notes dans l’espace des utilisateurs. Formellement, si vi designe le vecteur de ratings recus par le film i, la similarite entre deux films i et j est :

Similarite cosinus
sim(i, j) = cos(v_i, v_j) = (v_i · v_j) / (||v_i|| × ||v_j||)

La metrique cosinus est invariante a l’echelle : elle mesure l’orientation relative des vecteurs, non leur magnitude. Cela la rend robuste aux biais systematiques de notation (utilisateurs qui notent toujours haut ou toujours bas).

Python — Item-based CF, similarite cosinus
def get_top_similar_movies_item_based(item_user_matrix, anchor_movie_id, movies_df, top_k=10):
    anchor_idx    = anchor_movie_id - 1
    anchor_vector = item_user_matrix[anchor_idx]  # vecteur (1 x n_users)

    # Similarite cosinus avec l'ensemble du catalogue
    similarities = cosine_similarity(anchor_vector, item_user_matrix).flatten()
    similarities[anchor_idx] = -1  # exclusion du film lui-meme

    top_indices   = similarities.argsort()[-top_k:][::-1]
    top_movie_ids = top_indices + 1
    # ... recuperation des titres et scores

Resultats pour les trois films d'ancrage

The Greatest Showman (2017)

Les dix films les plus proches sont quasi exclusivement des sorties de 2017-2018, toutes categories confondues (comedie musicale, super-heros, animation). Ce phenomene de co-occurrence temporelle est un artefact structurel de l’item-based CF : les utilisateurs qui ont note un film recemment ont tendance a avoir note d’autres films contemporains disponibles sur les memes plateformes.

TitreGenresSimilarite
Jumanji: Welcome to the Jungle (2017)Action|Adventure|Children0.3138
Beauty and the Beast (2017)Fantasy|Romance0.2855
Black Panther (2017)Action|Adventure|Sci-Fi0.2771
Wonder (2017)Drama0.2722
Ready Player OneAction|Sci-Fi|Thriller0.2712
Murder on the Orient Express (2017)Crime|Drama|Mystery0.2632
Ant-Man and the Wasp (2018)Action|Adventure|Comedy|Fantasy|Sci-Fi0.2598
Star Wars: The Last Jedi (2017)Action|Adventure|Fantasy|Sci-Fi0.2530
Moana (2016)Adventure|Animation|Children|Comedy|Fantasy0.2525
Wonder Woman (2017)Action|Adventure|Fantasy0.2523

The Lion King (2019)

La similarite maximale avec Aladdin (2019) atteint 0.4075, sensiblement plus elevee qu’en moyenne. Ce resultat s’explique par le contexte de sortie : les deux films appartiennent a la meme serie de remakes Disney en prises de vue reelles, sortis la meme annee, avec une audience cible presque identique. Le modele capte parfaitement cette coherence comportementale.

TitreGenresSimilarite
Aladdin (2019)Adventure|Fantasy|Romance0.4075
Toy Story 4 (2019)Adventure|Animation|Children|Comedy0.3064
Dumbo (2019)Adventure|Children|Fantasy0.2580
Dark Phoenix (2019)Action|Sci-Fi0.2347
Captain Marvel (2018)Action|Adventure|Sci-Fi0.2343
Spider-Man: Far from Home (2019)Action|Adventure|Sci-Fi0.2331
Pokemon: Detective Pikachu (2019)Action|Children|Crime|Fantasy|Mystery0.2319
Mary Poppins Returns (2018)Children|Fantasy0.2313
The Secret Life of Pets 2 (2019)Adventure|Animation|Children|Comedy0.2294
Shazam! (2019)Action|Adventure|Fantasy|Sci-Fi0.2242

I Want to Eat Your Pancreas — Cas de degradation

Ce cas illustre la limite structurelle du CF en regime sparse. Les scores de similarite sont paradoxalement eleves (0.52 pour le premier voisin), mais les films recommandes n’ont aucun rapport thematique ou stylistique avec l’anime japonais d’origine. Ce phenomene resulte directement de la faible densite d’interactions : quelques utilisateurs eclectiques ayant note ce film peu populaire ont genere des co-occurrences artificielles avec des titres tout aussi marginaux.

Degradation en regime sparse
Des scores de similarite cosinus eleves calculees sur tres peu de dimensions non nulles communes sont statistiquement non fiables. Deux vecteurs de taille 160 000 dimensions partageant seulement 3 ou 4 entrees non nulles peuvent afficher une similarite cosinus de 0.98 sans que cela soit porteur d’un sens semantique reel.
TitreGenresSimilarite
The Last Trick (1964)Animation0.5237
Transfert per camera verso Virulentia (1967)Documentary0.4534
Invisible Ink (1921)Animation|Comedy0.4527
Nostalgia (2018)Drama0.4519
Octocat Adventures (2008)(no genres listed)0.4211
The External World (2010)Animation|Comedy0.4188
The Step (1985)(no genres listed)0.4004
The Easiest Way (1931)Drama|Romance0.4003
The Company’s in Love (1932)Comedy0.4003
An Episode in the Life of an Iron Picker (2013)Drama0.4002

Filtrage collaboratif user-based

Le paradigme user-based inverse la logique de comparaison : plutot que de mesurer la proximite entre films, on identifie les utilisateurs partageant le meme profil de gout que l’utilisateur cible, puis on recommande ce que ces voisins ont aime mais que la cible n’a pas encore vu.

Construction d'un utilisateur synthetique

En l’absence d’un vrai utilisateur cible avec un historique verifie, un profil synthetique est construit manuellement a partir des films d’ancrage et de quelques titres complementaires. Ce vecteur est ensuite projete dans l’espace de la matrice et compare a l’ensemble des 162 000 utilisateurs reels par similarite cosinus.

Python — Construction du profil utilisateur synthetique
fake_user_ratings = {
    180985: 5.0,  # The Greatest Showman
    203222: 4.5,  # The Lion King (2019)
    198611: 5.0,  # I Want to Eat Your Pancreas
    5690:   4.0,  # Grave of the Fireflies
    120258: 4.5,  # Shaka Zulu
    3404:   4.5,  # Titanic
}

# Projection comme vecteur sparse (1 x n_movies)
fake_user_vector = csr_matrix(
    (list(fake_user_ratings.values()),
     ([0] * len(fake_user_ratings),
      [mid - 1 for mid in fake_user_ratings.keys()])),
    shape=(1, n_movies)
)

user_similarities = cosine_similarity(fake_user_vector, user_item_matrix).flatten()
top_user_indices  = user_similarities.argsort()[-20:][::-1]

Recommandations user-based

Les dix films recommandes obtiennent tous un score predit de 5.0, ce qui constitue une anomalie methodologique. Ce plafonnement resulte du fait que les voisins identifies ont une forte tendance a noter au maximum les films de leur catalogue. En l’absence de ponderation par la similarite et de regularisation, la moyenne brute des notes des voisins converge mecaniquement vers 5.0 pour les films qui n’ont ete evalues que par les utilisateurs les plus enthousiastes.

TitreGenresScore predit
Sudden Death (1995)Action5.0
Before the Rain / Pred dozhdot (1994)Drama|War5.0
Kiss the Girls (1997)Crime|Drama|Mystery|Thriller5.0
Life Is Beautiful (1997)Comedy|Drama|Romance|War5.0
Blair Witch Project (1999)Drama|Horror|Thriller5.0
Patriot Games (1992)Action|Crime|Drama|Thriller5.0
What Lies Beneath (2000)Drama|Horror|Mystery5.0
Anatomy of a Murder (1959)Drama|Mystery5.0
The Notebook (2004)Drama|Romance5.0
The Exorcism of Emily Rose (2005)Crime|Drama|Horror|Thriller5.0

Comparaison des deux paradigmes

L’intersection entre les listes item-based et user-based est nulle : les deux methodes ne partagent aucun film commun dans leurs top-10 respectifs. Ce resultat, instructif en soi, illustre que les deux paradigmes operent sur des logiques fondamentalement differentes.

Item-Based CF (extrait)
  • Black Panther (2017)Sim: 0.277
  • Aladdin (2019)Sim: 0.407
  • Moana (2016)Sim: 0.253
  • Wonder Woman (2017)Sim: 0.252
  • Toy Story 4 (2019)Sim: 0.306
User-Based CF
  • Life Is Beautiful (1997)5.0
  • Anatomy of a Murder (1959)5.0
  • Blair Witch Project (1999)5.0
  • The Notebook (2004)5.0
  • Patriot Games (1992)5.0
Interpretation
L’item-based CF est ancre dans le contexte des films d’entree (meme periode, meme public cible), tandis que le user-based CF reflete les preferences generales des voisins sans contrainte temporelle ni thematique. Les deux approches sont complementaires plutot que substituables.

Factorisation matricielle par SVD tronquee

La decomposition en valeurs singulieres tronquee (TruncatedSVD) approche la matrice utilisateur-item R par un produit de rang reduit :

Decomposition
R ≈ U Σ VT  avec  U ∈ Rm×k, Σ ∈ Rk×k, VT ∈ Rk×n

U encode les utilisateurs dans l’espace latent, les colonnes de V les films. Le parametre k=50 controle la dimensionnalite : les 50 composantes retenues correspondent aux 50 directions de variance maximale dans les comportements collectifs de notation.

Centrage prealable par utilisateur

Avant la decomposition, les notes sont centrees par utilisateur. Cette etape corrige les biais systematiques de notation (certains utilisateurs notent structurellement plus haut que d’autres) et permet a la SVD de capturer les preferences relatives plutot que les niveaux absolus.

Python — Centrage des notes par utilisateur
for user_idx in range(n_users):
    start = user_item_matrix_centered.indptr[user_idx]
    end   = user_item_matrix_centered.indptr[user_idx + 1]
    if start < end:
        user_item_matrix_centered.data[start:end] -= user_mean_rating[user_idx]

# Verification : la moyenne centree doit etre nulle
# Moyenne centree utilisateur 0 : -0.000000

Valeurs singulieres et variance expliquee

L’analyse du spectre de valeurs singulieres et de la variance expliquee cumulee permet de comprendre comment l’information est repartie dans l’espace latent.

Valeurs singulieres
Figure 4. Decroissance des valeurs singulieres selon l’indice de la composante. La chute rapide confirme que les premiers facteurs concentrent la majorite de la structure informationnelle de la matrice.
Variance expliquee cumulee
Figure 5. Variance expliquee cumulee par les 50 composantes. Les premiers facteurs latents capturent les dimensions dominantes (popularite, genre, epoque) ; les suivants encodent des gouts de plus en plus specifiques.

Recommandations SVD pour l'utilisateur synthetique

La projection du fake user dans l’espace latent se fait par combinaison lineaire des facteurs items, ponderee par les notes centrees. Les scores predits sont ensuite calcules par produit scalaire avec l’ensemble des vecteurs films.

TitreGenresScore predit
Blair Witch Project (1999)Drama|Horror|Thriller4.501
Babe (1995)Children|Drama4.501
Independence Day (1996)Action|Adventure|Sci-Fi|Thriller4.501
Raiders of the Lost ArkAction|Adventure4.501
Twelve Monkeys (1995)Mystery|Sci-Fi|Thriller4.501
Apollo 13 (1995)Adventure|Drama|IMAX4.500
The Hangover (2009)Comedy|Crime4.500
American Beauty (1999)Drama|Romance4.500
The Wizard of Oz (1939)Adventure|Children|Fantasy|Musical4.500
Willy Wonka & the Chocolate Factory (1971)Children|Comedy|Fantasy|Musical4.500
Regression vers la moyenne
Les recommandations SVD avec k=50 tendent vers des classiques populaires des annees 1990-2000, independamment du profil specifique de l’utilisateur cible. Les k premieres composantes encodent avant tout la popularite et les grandes categories generiques, insuffisamment discriminantes pour un profil atypique comme le fake user. Augmenter k ou utiliser une regularisation adaptive permettrait de corriger ce biais.

Visualisation de l'espace latent (UMAP)

La decomposition SVD place chaque film dans un espace de 50 dimensions. Pour rendre cet espace interpretable, une reduction supplementaire a 2 dimensions est effectuee par UMAP (Uniform Manifold Approximation and Projection). UMAP est prefere a t-SNE pour sa capacite a preserver simultanement la structure locale des voisinages et la structure globale de l’espace.

Python — Projection UMAP de l'espace latent
umap_model = umap.UMAP(
    n_neighbors=15,
    min_dist=0.1,
    n_components=2,
    random_state=42
)
item_factors_2d = umap_model.fit_transform(item_factors)  # item_factors : (n_movies x 50)

Projection globale : structure de l'espace

UMAP espace latent global
Figure 6. Projection UMAP de l’ensemble des films dans l’espace latent SVD a 50 dimensions. Chaque point represente un film, colore par son genre principal. La formation de clusters distincts confirme que l’espace latent possede une structure geometrique significative, non aleatoire.

Position des films d'ancrage

UMAP avec films d'ancrage
Figure 7. Les trois films d’ancrage (points rouges annotes) sont situes dans des regions bien separees de l’espace. The Greatest Showman et The Lion King occupent des zones denses correspondant aux films grand public modernes ; I Want to Eat Your Pancreas se positionne en peripherie, dans une zone associee aux films peu evalues.

Structure geometrique par genre

Distribution par genre UMAP
Figure 8. Distribution de trois genres selectionnes dans l’espace latent 2D. Les documentaires forment les clusters les plus compacts, les romances les plus disperses. Cette difference de cohesion geometrique se traduit numeriquement par les variances moyennes ci-dessous.

Dispersion intra-genre

La variance moyenne des coordonnees UMAP par genre quantifie la coherence comportementale de chaque categorie. Un genre compact est vu par un public homogene et specifique ; un genre disperse est consomme par des profils tres heterogenes.

====================================================================== 6.3 - DISPERSION MOYENNE PAR GENRE (LATENT SPACE) ====================================================================== primary_genre Documentary 10.21 Horror 14.39 Romance 16.72 dtype: float32
GenreVariance moyenne (2D)Interpretation
Documentary 10.21 Audience tres specifique, cluster compact
Horror 14.39 Public relativement homogene
Romance 16.72 Audience heterogene, forte dispersion

Discussion et analyse critique

Ce que les resultats montrent reellement

Les trois methodes produisent des recommandations qualitativement distinctes, et cette divergence est elle-meme informationnelle. L’item-based CF capture des co-occurrences de visionnage temporellement et thematiquement coherentes pour les films populaires, mais s’effondre sur les films de niche. Le user-based CF reflete une logique de profil general plutot que de contexte specifique. La SVD, quant a elle, converge vers un ensemble de classiques populaires qui constituent le consensus collectif encode dans les premieres dimensions de l’espace latent.

Limites methodologiques

Absence d'evaluation quantitative
Ce projet ne dispose pas de protocole d’evaluation standard. Sans split train/test temporel ni calcul de RMSE, Precision@K ou nDCG@K, les jugements de qualite restent qualitatifs. C’est la limite la plus importante a adresser dans une version suivante.
Biais de popularite
Les trois methodes sur-representent les films populaires. Un film ayant recu 80 000 notes occupe une position centrale dans tout espace de representation, au detriment des films de niche potentiellement pertinents pour un utilisateur donne.
Biais temporel
L’item-based CF produit frequemment des recommandations de films contemporains du film d’ancrage. Ce n’est pas une erreur algorithmique mais un artefact de la structure des donnees : les utilisateurs qui notent un film recent ont tendance a noter d’autres films recents disponibles simultanement.

Axes d'amelioration prioritaires

AxeAction proposeeComplexite
Evaluation Split temporel + Precision@K, nDCG@K Moyenne
User-based scoring Ponderation par similarite (k-NN pondere) Faible
SVD k-tuning Recherche du k optimal par cross-validation Moyenne
Algorithme ALS ou BPR pour donnees implicites Elevee
Cold start Hybridation avec attributs de contenu (LightFM) Elevee
Temporalite Ponderation des evaluations recentes Faible

Structure du depot

recommendation-system/ ├── README.md ├── notebooks/ │ └── recommendation_system.ipynb ├── data/ │ └── README_data.md (instructions MovieLens 25M) ├── src/ │ ├── matrix_utils.py │ ├── collaborative_filtering.py │ ├── svd_recommender.py │ └── visualization.py ├── figures/ │ └── *.png └── requirements.txt
requirements.txt
numpy>=1.24
pandas>=1.5
scipy>=1.10
scikit-learn>=1.2
matplotlib>=3.6
seaborn>=0.12
umap-learn>=0.5