Aller au contenu principal

IA et opac : mettre en place un moteur de recommandation dans un opac – 3/4 : Expérimentation sur les données du SCD de l’Université de Nice

21/11/2019

Source des données

Nous utilisons au SCD de Nice le SIGB Aleph 500 (v23) de la société Ex Libris, qui est livré avec un module statistique (baptisé « Arc ») à partir duquel on extrait :

  • l’ensemble des notices bibliographiques de monographies d’une des bibliothèques du SCD, précisément la BU Saint-Jean d’Angely qui contient des collections d’économie-gestion, sociologie et médecine, soit environ 33 000 notices  (après les traitements réalisés sur les données décrits ci-dessous) ;
  • tous les évènements de prêts sur des exemplaires de cette même bibliothèque depuis 2015, soit un peu plus de 26 000 lignes de prêts (également après nettoyage), comportant des éléments sur la date du prêt et sur les données bibliographiques (titre, ppn, identifiant interne Aleph), d’exemplaires (indices de cote) et de lecteurs (identifiant anonymisé, catégorie lecteur L1, L2 etc…).

En production un système de recommandations porterait évidemment sur l’ensemble des collections  (un peu plus de 300 000 notices de monographies dans notre cas) et des données d’usage (environ 400 000 prêts depuis 2011), qui alimenteraient qui plus est le modèle en flux continu.

A noter également que l’étape préliminaire d’anonymisation des lecteurs a remplacé les identifiants système par une chaîne non signifiante person_1, person_2… On aurait pu aussi utiliser un mécanisme de hashage qui remplace l’identifiant d’un lecteur par un identifiant complètement opaque : il est impossible, à partir de l’identifiant hashé, de remonter à l’identifiant initial. Mais si ultérieurement on actualise les données en reprenant une nouvelle extraction de la même base de prêt, le même identifiant lecteur donnera le même hash.

Content-based filtering (“Vous pourriez être aussi être intéressé par…”)

Comme expliqué plus haut, ce modèle est basé sur la notion de contexte (la place de chaque document par rapport aux autres dans la collection) et fortement dépendant de la quantité et la qualité des variables prédictives utilisées (ici les champs des notices bibliographiques, indexation en premier lieu).

Manuellement, il faudrait regarder chaque notice et la comparer à chaque autre, pour évaluer la distance pour chaque paire de notice : un auteur commun les rapproche, de même qu’un sujet commun, etc.

Traduction mathématique : il faut calculer un coefficient de similarité pour chaque paire de documents à partir de leurs métadonnées bibliographiques. Pour cela, l’ensemble des notices structurées sont chargées dans un dataframe (un tableau) pour y être préparées (nettoyées et reformatées), puis transformées en représentations vectorielles de comptage d’apparition de termes, vecteurs depuis lesquels on calcule des mesures de distance cosinus (“cosine similarity”).

Tout est là : https://github.com/gegedenice/primo-recommender-system/blob/master/SimilarityModel/contentBased-data.ipynb

On ne souligne ici que quelques brèves remarques, notamment à propos du fait que s’emparer des données avec des outils dédiés à l’analyse statistique de données massives est l’occasion de porter un regard synthétique sur les données agrégées, de repérer immédiatement les anomalies ou les données manquantes dans les notices, ou encore de sortir des consolidations insolites, tout cela en quelques lignes de code aisément reproductibles

Répartition des notices par date de publication PPN manquants Titres homogènes

Concernant la vectorisation des données en fin de processus, on précise qu’il s’agit de convertir des données textuelles en données numériques pour pouvoir ensuite leur appliquer des algorithmes de calculs mathématiques. Pour cela, les champs titre-auteur-indexation déjà nettoyés sont encore reformatés grâce au package Python de text mining NLKT (pour en éliminer les mots vides notamment), puis concaténés en une liste de termes servant à étiqueter chaque document. On peut alors enfin utiliser les fonction CountVectorizer  et cosine_similarity de la librairie Scikit-learn pour :

  • en extrait un dictionnaire de mots ;
  • convertir l’ensemble des listes représentant les notices en une matrice numérique de comptage des occurrences par rapport au dictionnaire, c’est-à dire au final chaque document par une représentation vectorielle du type [1,0,1,1,1,0,1,0,0,0,1,0,0,0,0,…] ;
  • appliquer à cette matrice l’algorithme de similarité par calcul de la distance cosinus entre chaque paire de vecteurs (calcul de l’angle géométrique formé par les vecteurs pris 2 à 2)

Et pour finir construire la fonction Python de recommandations, qui prendra en entrée une variable correspondant au numéro système Aleph et fournira en sortie les numéros des 10 notices les plus « proches ». Implémentée dans un endpoint grâce à la libraire Flask, la fonction est alors appelée dynamiquement par des appels Ajax dans l’interface et produit par exemple ceci :

Collaborative-filtering (“Les lecteurs qui ont emprunté ce document ont aussi emprunté…”)

Ici ce n’est plus la notion de contexte qui importe, mais les comportements passés des utilisateurs. Les similarités entre utilisateurs et entre contenus sont uniquement déduites des usages croisés entre ces 2 ensembles, complètement indépendamment des caractéristiques intrinsèques des contenus en eux-mêmes.

Comme pour l’étape précédente, toute la phase préparatoire est déclinée ici : https://github.com/gegedenice/primo-recommender-system/blob/master/SimilarityModel/collaboraticeFiltering-data.ipynb

Et comme précédemment, c’est aussi l’occasion de manipuler, interroger et visualiser en quelques commandes simples ce que nous disent les données

Répartition des prêts par année Répartition des prêts par type de lecteurs Répartition des prêts par indice de cotes
Répartition des prêts par date de publication  Répartition des prêts par jour de la semaine Répartition des prêts par jour et type de lecteurs

A rapprocher du graphique de répartition des notices (de l’offre) par date de publication : est-ce que l’offre crée la demande ?

A priori pas de corrélation (hors week-end), à affiner sur chaque année probablement

Comme annoncé, on poursuit ensuite avec une nouvelle structuration des données dans une base de données Neo4j, sous la forme d’un graphe constitué d’entités annotées avec des attributs, et connectées entre elle par des relations elles-mêmes taggées.

L’un des gros avantages d’une modélisation de type labeled-property graph est sa nature dite schemaless qui autorise la création de n’importe quel type d’entités et de liens.

Le schéma utilisé et arbitrairement choisi  est donc le suivant :

Une fois les données chargées dans l’instance Neo4j à partir du fichier csv hebergé sur Github avec le reste du code de l’application, voici à quoi ressemble (en partie) le graphe

    • En vert : les noeuds représentant les lecteurs
    • En orange : les noeuds représentant les catégories de lecteur
    • En bleu : les noeuds représentant les documents
    • En rose : les noeuds représentant les indices de cote des documents

La requête pour retrouver, à partir de l’attribut numéro interne Aleph d’un noeud représentant un document, les autres noeuds documents ayant également un lien avec les mêmes noeuds de type lecteur que le document de départ est alors très facile à construire, et la fonction de recommandations par filtrage collaboratif sur la base des usages communs de documents pondérés par le nombre de prêts de chacun donne ceci dans l’interface

L’exploration du graphe, l’étude des métriques de graphe autant que la computation par algorithmes de graphes permet d’aller beaucoup plus loin dans l’analyse des données et la mise à jour de motifs sous-jacents dans la structure des usages d’une collection. Quelques pistes sont proposées dans le Notebook et on conclura ici avec les possibilités offertes par la méthode de projection de graphe et inférence de nouvelles relations : en effet en l’état le graphe est dit k-partite car il contient des sets de noeuds représentant des types d’entités différents (documents, lecteurs, indices de cote, catégories de lecteurs). La notion de projection de graphe permet de créer un nouveau graphe monopartite à partir du graphe initial en créant (inférant) une nouvelle relation virtuelle qui n’existe pas dans les données entre noeuds de même type (en fait, pour rebondir sur les méthodes de factorisation de matrice évoquées plus haut, c’est une autre manière de décomposer une matrice item/user en produit de matrices user/user et item/item, mais dans le cadre d’une représentation en graphe). Dans le cas qui nous occupe, on peut par exemple déduire un graphe projeté ne contenant que des noeuds de type documents reliés entre eux par des liens illustrant un usage commun les mêmes lecteurs, et sur ce graphe appliquer des algorithmes qui révèlent :

  • les ouvrages très connectés (souvent empruntés en même temps) ou à l’inverse peu ou pas connectés (peu ou jamais empruntés par les mêmes lecteurs) : algorithme de centralité degré
  • les communautés de documents (les clusters de documents qui ont un usage commun), qui permettent de dresser une cartographie disciplinaire des usages à comparer avec la cartographie disciplinaire des collections : algorithmes de détection de communauté (algorithme Louvain par exemple)
  • les documents dont l’usage créent des ponts entre les communautés détectées (qui sont des noeuds pivot qui, si on les supprimait du graphe, le rendrait moins globalement connecté) : algorithme de centralité betweeness
  • les similarités (et oui!) entre noeuds : algorithmes de similarité (algorithme cosine similarity ou algorithme Jaccard par exemple)

Tout ceci sera exploré dans un prochain billet à venir.

Ouf !

Le prototype d’interface incluant le système de recommandation est disponible ici : https://ggeoffroy.com/apps/primo-recommender-system (patience ! Le fichier du modèle étant assez lourd, la page peut mettre quelques secondes à se charger).

Le code de l’application et les notebooks sont accessibles sur Github ici : https://github.com/gegedenice/primo-recommender-system

%d blogueurs aiment cette page :