Construire l'index inversé et les autres données relatives à l'indexation l'ensemble de documents CACM.
On considère la base CACM et les documents filtrés d'après obtenus avec le filtrage appliqué au TP sur Zipf.
1. Écrire une fonction qui reprend la question 5 du TP Zipf: il ouvre les fichiers CACM-XX.tok un à un et qui applique l'anti-dictionnaire qui est dans le fichier common-words (cf. TP Zipf) sur les mots “en minuscule” (en ne prenant pas en compte les termes de ces fichiers qui y apparaissent). Le résultat du filtrage sera mis dans le dictionnaire créé en question 5 du TP Zipf.
Indication: On utilise un dictionnaire python pour stocker l'anti-dictionnaire en mémoire, en associant à chaque mot de l'anti-dictionnaire la valeur 1. Le filtrage se fera en regardant si un terme des fichiers CACM-XX.tok est une clé de ce dictionnaire ou non. **TRUC : le fichier common-words est un fichier généré sous Windows, il contient donc les caractères '\r\n' à la fin de chaque ligne. Il faut donc que vous lisiez ligne par ligne ce fichier (par readline), et que vous appliquiez la fonction ".strip()" à la chaîne lue avant de la stocker dans l'anti-dictionnaire.**
2. Reprendre la question 1 en ajoutant, à la suite du traitement de l'anti-dictionnaire, le fait que l'on tronque les termes en utilisant la troncature de Porter de la librarie nltk :
On importe le stemmer par from nltk.stem.porter import * On l'utilise avec : stemmer = PorterStemmer() # creation d'un objet stemmer racine = stemmer.stem(mot) # genere la racine du mot qui est une chaine de caractères
3. Construire le vocabulaire associé à cette collection en considérant tous les termes (tronqués) qui apparaissent au moins une fois dans le corpus de documents. Le résultat sera stocké dans un fichier “vocabulaire.json”. Utilisez encore un dictionnaire python qui, pour chaque terme (la clé), stocke la valeur 0 (on est ici assez proche du TP sur la loi de Zipf). On a deux solutions pour ce problème : soit on passe en revue les fichiers (comme pour le TP Zipf), soit on reprend la structure générée à la question 2.
4. Calculer le df_i pour chaque terme t_i du vocabulaire (en modifiant le code de la questuion 3) et stocker le résultat dans le fichier “vocabulaire.json”. Par rapport à la question précédente, il suffit de stocker le df_i (c'est-à-dire le nombre de documents dans lequel il apparaît) dans la valeur associée au terme dans le dictionnaire au lieu de 0. Ici, une solution est de créer un “petit” vocabulaire (dictionnaire python) lié au document que l'on traite, en mettant uniquement 1 pour valeur, puis une fois le document traité on passe en revue ce petit dictionnaire et on met à jour le grand dictionnaire créé en question 3.
Un exemple : le df de "report" est égal à 100
4'. A partir du dictionnaire qui contient les df_i en question 4, on le met à jour pour stocker les valeurs idf_i. Pour calculer l'idf_i, qui utilise les df_i des termes c'est à dire qu'on applique la formule “idf_i = ln(N/df_i)”.
Un exemple : l'idf de "report" est égal à 3,467
ATTENTION : Dans la suite, on opte dans le projet pour une approche un peu différente de ce qui a été vu en cours pour la construction de l'index inversé : 1. on construit les vecteurs des documents, puis 2. on construit l'index inversé à partir de ces vecteurs. On “zappe” les couples (terme,doc) des transparents 27/28 du cours pour créer les vecteurs. Cette simplification des étapes nous permet de vérifier si on fait des erreurs, elle n'est possible que lorqu'on a peu de documents !
5. Écrire une fonction qui construit la représentation vectorielle (au sens RI) de chaque document, d'après le modèle vectoriel de Salton vu en cours. Le type de représentation choisi est : tf.idf. (on pose donc que nd=1 (cf. transparent 23)). Ici en fait on modifie votre code de la question 5 du TP de Zipf en générant un dictionnaire par document.
La différence par rapport à la question 5 du TP Zipf est que l'on stocke pour chaque document un dictionnaire de couples (terme, tf.idf du terme dans d). Ici encore, l'idée est de passer document par document : 1.) calculer le tf de tous les termes présents (c'est la question 5 du TP Zipf), et 2.) multiplier ces tf par l'idf stockés dans le vocabulaire (cf. question 4').
Nous obtenons donc, un “gros” dictionnaire qui stocke tous les “vecteurs” au sens recherche d'information (des dictionnaires python) de documents, avec l'identifiant de document comme clé et la représentation de son vecteur comme valeur.
Par exemple, si le vocabulaire contient 5 termes de t1 à t5 et qu'un document "CACM_123" 'contient 3 fois le terme "t3" et 2 fois le terme "t1", en supposant le l'idf de "t1" est 2.6 et "t3" est 7.61 . Dans le cas où on opte pour une représentation tf.idf, le "vecteur" (dictionnaire) associé au document "CACM_123" est le suivant : [ "t1":5.2 ; "t3":22.83 ] et dans le dictionnaire de vecteurs on a : [ "CACM_123": [ "t1":5.2 ; "t3":22.83 ]]
Répétons que je vous suggère d'utiliser un dictionnaire pour représenter le vecteur d'un document.
TRUC : vérifier sur un “petit” document et sur un terme que la valeur tf.idf est correcte. Par exemple le document CACM-2439 et le terme “termin”.
6. Écrire une fonction qui, à partir du vocabulaire et des vecteurs des documents, construit l'index inversé de ses termes (cas statique), puis le sauvegarde sur disque.
Indication : utiliser encore une fois un dictionnaire pour stocker cet index en mémoire, et le sauver dans un fichier json (utiliser la fonction dump de la library json). Dans ce cas, le dictionnaire a comme clé le terme, et comme contenu un autre dictionnaire ayant comme clé l'identifiant du document et la valeur tf.idf du terme dans le documemt. On a donc un dictionnaire de : [terme: [ document: valeur tf.idf]] . L'intérêt d'un tel dictionnaire est qu'il permet de se passer du tri des paires (terme,doc) (cf. transparent 28 du cours).
Exemple : le poids du terme "penni" pour le document CACM-847 est 14,7 . Ce terme apparaît 2 fois et son idf est de 7.37
7. Pour préparer le traitement des requêtes, il vous est demandé de rajouter, lors du traitement de chaque document le calcul de la norme du vecteur suivant la pondération tf.idf : il est calculé par la racine carrée de la somme des carrées des valeurs (c'est-à-dire tf.idf) de chaque dimension du vecteur (se baser sur la question 5). Ici encore, un dictionnaire est utilisable, avec comme clé l'identifiant de document, et en valeur la norme calculée. On calcule donc bien une valeur par document. Vous devrez stocker ce fichier sur disque dans un autre fichier au format json. Se baser sur la représentation construite en question 5.
Exemple : la norme de CACM-1022 est 12,05
Sauvegarde/écriture de variables python dans des fichiers json. On importe en début de programme la librairie python json :
import json
On sauve une variable V dans un fichier “monfichier.json” en écrivant :
with open("monfichier.json", 'w') as fp: json.dump(V, fp) fp.close()
On lit une variable V d'un fichier “monfichier.json” en écrivant :
with open('monfichier.json', 'r') as fp: V = json.load(fp) fp.close()