Séance du 18 et 25/03/2015 et du 1/04/2015: BàO 3, extractions des patrons

Désormais nous souhaitons extraire les formes que nous voulons analyser.

Lors de ces 3 séances, nous avons vu 3 manières différentes d’extraire les patrons morpho-syntaxiques de nos fichiers treetaggués et cordialisés. Un patron morpho-syntaxique correspond, par exemple, à la séquence NOM ADJ ou encore NOM PREP NOM.

Comme l’étiquetage par Treetagger et Cordial est différent, les scripts seront différents selon si l’on souhaite extraire à partir du fichier .xml ou .cnr.

La première solution que nous avons vu est celle proposée par Rachid Belmouhoub.

Elle ne traite que les fichiers XML. Elle prend en entrée 2 arguments: le fichier taggué et le fichier de motif recherché. Son programme utilise le module perl XML::LibXML. Il est composé de 2 subroutines: une pour écrire le chemin xpath qui mène au motif recherché et l’autre pour en extraire la forme correspondante. On cherche à extraire le 3ème <data> du noeud <element>, « string », mais seulement si son type correspond au motif entré. Un fichier de sortie est crée par patrons.

La seconde, celle de Jean-Michel Daube, extrait les patrons des fichiers issus de Cordial.

Comme le précédent, ce programme prend deux arguments: le fichier de patrons morphosyntaxiques et le fichier cordial. On splite chaque ligne au niveau de la tabulation et on enregistre le token et le Part-Of-Speech dans une liste. Quand on rencontre une ponctuation dans la ligne, on commence à traiter cette ligne. On transforme d’abord les listes en scalaires afin de pouvoir les comparer au motif recherché. Puis on imprime le token qui était recherché.

Enfin la dernière, proposée par M. Fleury, traite les deux formats. Les programmes ne sont pas généraux, ils correspondent à un étiqueteur (Cordial ou Treetagger) et à un patron donné. Cependant, ils sont très simples et faciles à modifier selon le patron. Ces scripts traitent ligne par ligne et vérifient que la présence du motif recherché grâce à des expressions régulières. Si oui, il l’extrait.

Nous avons testé chacune de ces solutions sur nos données.

Séance du 4 et du 11/03/2015: BàO 2, étiquetage par Cordial et Treetagger

Nous avons donc réglé le problème des doublons dans les 2 scripts (celui des regexp et celui avec le module XML::RSS) ainsi:

doublons

Les tables de hashage %dictionnairedesfils et %dictionnairedescription sont initialisées en début de programme afin d’éviter de les écraser à chaque nouvelle ouverture de fichier. On imprime alors que les $titre et $description trouvés 1 fois.

L’encodage est réglé sur iso latin 1 pour les fichiers .txt qui seront étiquetés par Cordial et en utf8 pour les .xml qui seront treetaggués:

encodage

Le logiciel Cordial ne pouvant être intégré sur nos machines et lancé en ligne de commande, nous devons étiqueter nos données en dehors du programme. Voici à quoi ressemble un fichier étiqueté par Cordial:

cordial

Dans le logiciel, il faut aller dans l’onglet syntaxe et choisir étiquetage de texte. Nous n’avons besoin que de 3 informations: la forme, le lemme et la catégorie grammaticale. On peut alors décocher tout le reste.

Quant au Treetagger, il est installé sur nos machines et nous devons lancer 3 programmes pour etiquetés correctement nos fils du Monde. Nous avons donc créer une nouvelle procédure nommée etiquetage. Comme ceci:

subetiquetage

Treetagger ne segmente pas automatiquement les données, il faut donc tokeniser avant à l’aide du programme tokenise-utf8.pl. Ce dernier crée un fichier de sortie avec un token par ligne. Ensuite on lance le parser french-utf8.par sur le fichier tokenisé qui affiche le type du token, son lemme et le token lui-même. Enfin, grâce à treetagger2xml.pl, le tout est mis au format XML. En voici un extrait:

xml

Mais tout d’abord, afin que le logiciel puisse prendre en charge nos fichiers et pour plus de cohérence dans les analyses terminologiques, il nous faut les « trier » par rubriques, afin qu’ils ne soient pas plus lourd que 2Mo. Nous avons donc ajouter une variable $rub que nous choisissons en ligne de commande.

Nous lançons désormais nos scripts sur les données complètes de l’année 2014, ce qui pour le traitement d’une seule rubrique prend environ 3-4 heures.

Séance du 18/02/2015 : dernières corrections du script et passage à la Bao 2 :

Les dernières choses restantes à corriger dans nos scripts respectifs (l’un utilisant les expressions régulières, l’autre se basant sur le module XML :: RSS ), est la suppression des doublons ainsi que l’encodage.

Il se peut, en effet, que les fils RSS d’une journée à l’autre aient les mêmes informations.

Pour éviter cela, nous allons avoir recours à un hash (ou tableau associatif) qui donnera à chaque balise une clef permettant de l’identifier. Ainsi, si une balise est déjà apparue, on l’ignore, sinon, si celle-ci est trouvée pour la première fois, on lui donne la valeur 1 et on l’enregistre. Cette solution permet ainsi de reconnaitre les balises qui apparaitraient deux fois.

Il nous reste en outre le problème de l’encodage à résoudre, nos scripts ne détectant plus l’encodage d’origine.

Une fois, ces problèmes résolus, il est prévu de travailler sur l’étiquetage des données. Nous avons donc deux fichiers de sorties :

  • Un fichier en texte brut
  • Un fichier en XML

Ces deux fichiers seront donc étiquetés de deux façons différentes :

  • Via le logiciel Cordial pour la version .txt. Toutefois, ce logiciel ne supporte pas l’utf8 et ne peut traiter des fichiers de plus de 2 Mo, ce qui signifie que le fichier devra probablement être « coupé » en plusieurs morceaux. En revanche, ce logiciel se charge lui-même de la segmentation du contenu du fichier. Le but de cette manipulation sera donc d’élaborer des patrons morphosyntaxiques, (par exemple NOM PREP NOM , tels que « conseil de sécurité », « société de production »).
  • Via le logiciel Treetagger pour la version .xml. La seule contrainte de ce logiciel est qu’il n’effectue aucune segmentation des données, que nous devrons donc faire nous-mêmes. Il faudra donc concevoir un programme ou en utiliser un (écrit en Perl) destiné à la segmentation des données. La normalisation des données analysées par Treetagger exige qu’il y ait un mot par ligne. Selon notre choix, il nous est possible d’étiqueter le fichier global ou à la volée (ce qui signifierait par exemple, traiter une balise <titre> dès que le programme tomberait dessus).

Nous avons découvert également une nouvelle commande que nous allons devoir utiliser : la commande system. Il s’agit d’une commande perl qui permet de lancer une commande externe, qui est notée entre guillemets.

Un conseil nous a été donné : essayer de ne pas produire un fichier de sortie unique, mais un fichier de sortie par rubrique afin d’organiser l’information et d’éviter de tout mettre ensemble.

Séance 4 (11/02/15) : Module perl XML::RSS

Lors de cette séance , nous avons appris à utiliser un module Perl très utile pour notre projet: XML::RSS. Il nous permet d’extraire le même contenu textuel dont nous avons besoin mais sans passer par des expressions régulières assez complexes et longues.

xmlrss

En effet, cette méthode permet d’associer à un scalaire une structure un peu plus complexe que d’habitude, dans ce cas-ci, on associe à la variable $rss une table de hashage issue du traitement du fichier XML. Ainsi avec le constructeur « new », le programme crée un nouvel objet de la classe XML::RSS, soit une nouvelle organisation des données du fichier XML sous forme de tableau associatif. Les clés du tableau correspondent aux balises du fichier xml et les valeurs à leurs contenus.

Dans ce module, parsefile ($file) est une méthode appartenant à XML::RSS qui permet de décomposer le fichier $file passé en argument et $rss-> parsefile($file) de le reconstruire sous forme de tableau. La fonction eval permet de vérifier si le fichier qui suit est bien formé. La réponse est négative si le fichier n’existe pas, si ce n’est pas un fichier xml ou bien si la structure n’est pas conforme au document rss.

Et enfin, on utilise la fonction foreach pour aller chercher dans ce tableau les éléments qui nous intéresse. Dans cette structure de données, les contenus des balises description et title sont tous dans le tableau ‘items’. On demande donc pour chaque $item de la liste que constitue le scalaire $rss, de chercher la clé ‘items’. Ensuite, on associe à $description tout le contenu de la clé item de valeur ‘description’. Idem pour la clé title, pubDate ou encoding.

Pour appliquer ce module à notre script actuel, nous avons tout d’abord du l’installer avec la commande sudo cpan install XML::RSS. Ensuite ajouter use XML::RSS en début de script et remplacer tout le traitement d’extraction à l’aide des expressions régulières par ce programme. La préparation du fichier entré qui consistait à mettre tous les fichiers xml au même format n’est plus nécessaire ici car tout est mis sous forme de tableau.

Nous avons ajouté use warnings et use strict pour éviter des erreurs de déclarations ou d’initialisation de variables.

Quelques options de nettoyage ont été ajouté à la procédure nettoyage. Pour éviter de remplacer chaque entités XML, il existe un module Perl qui le fait automatiquement : XML::Entities.

Grâce à ce module, l’extraction des données est plus simple et plus courte. Les données restent les mêmes, il n’y a que la structure qui change.

Séance 3 (04/02/15)- Perfectionnement de notre programme : le nettoyage et le parcours d’une arborescence de répertoires et de fichiers

Le premier programme Perl que nous avons élaboré fonctionne de la manière suivante :

Traitement sur un seul et unique fichier à la fois qu’il suffisait de placer en argument ($ARGV[0]) du script Perl. Désormais, grâce à un programme récursif, il nous est possible de parcourir l’arborescence d’un dossier de la manière suivante :

  • On crée une variable $path qui contiendra le premier élément de la liste @_, qui correspond, au début, au chemin du répertoire entré,
  • On ouvre ce répertoire entré en argument,
  • On crée une liste @files qui correspond à l’ensemble des éléments contenus dans le répertoire qui sont lus grâce à la fonction readdir,
  • On ferme ce répertoire,
  • Pour chaque élément de la liste @files, on passe directement au suivant si il est égal à 1 ou plusieurs points (syntaxe UNIX),
  • Si ce n’est pas le cas, on reconstruit le chemin atteint jusqu’ici, soit le nom du répertoire entré / nom du premier élément utilisable en découlant. Le programme reconstitue ainsi le chemin par rapport à notre localisation de départ,
  • Chaque fois la question suivante est posée par le programme : ai-je affaire à un répertoire ou un fichier ? S’il s’agit d’un répertoire, je l’ouvre et je continue le parcours de l’arborescence, sinon je traite les fichiers et je recommence pour un autre fichier et un autre dossier, etc..

Slide1

Nous avons aussi spécifier dans ce nouveau script que nous voulons nos fichiers de sortie en utf-8. Pour cela, nous avons ajouter au début du script: use Unicode::String qw (utf8); ainsi que if (!open (OUT1,« >:encoding(utf8) »,$output1)) à l’ouverture des fichiers de sortie pour qu’ils soient encodés en utf-8. De plus, il ne faut pas oublier de régler l’encodage de votre éditeur en utf-8 lorsque vous ouvrez ces derniers.

Slide1

Pour le traitement de nos résultats par les logiciels d’analyse textométrique, nous les imprimons dans un deuxième fichier de sortie .txt. Nous avons aussi ajouté des délimiteurs § à partir de son code héxadécimal \x{00A7} sinon il affichait ç.

Slide1

A l’heure actuelle, il nous reste les défauts suivants à corriger :

  • La suppression des doublons en ayant recours à un tableau associatif perl qui ne contient par défaut que des éléments uniques
  • s’assurer que les fichiers RSS à traiter ne sont pas vides

Séance 1 & 2 (21 et 28/01/15)-Présentation du projet, début de programme, nettoyage, filtrage

Elèves en Master 1 Traitement automatique des langues, nous menons dans le cadre du cours intitulé Programmation et projet encadré 2, dispensé par Messieurs Jean-Michel Daube, Serge Fleury et Rachid Belmouhoub, un projet dont le but final consistera en l’étiquetage lexicale de données extraites de fils RSS. Des programmes comme TreeTagger permettent déjà de tels étiquetages.

Ecrit en langage Perl, notre programme devra travailler sur des flux RSS du journal Le Monde (qui correspondent à tous les articles publiée à 19h chaque mercredi sur le journal), et les nettoyer avant d’effectuer une segmentation et un étiquetage. Ces données sont encodées au format XML qui est un langage de structuration des contenus utilisant, à la manière du HTML, des balises. L’extraction des informations nous intéressant, à savoir, le titre, la description et la date de ces contenus, se basera donc sur une recherche des balises correspondantes.

Ce projet se divise en 3 étapes. La première, appelée « Boîte à outils 1 » consiste à extraire, filtrer et nettoyer les fichiers XML afin de ne garder que ce qui nous intéresse.

Pour commencer, on testera nos programmes sur un plus petit corpus composé de quelques articles de l’année 2008.

Nous avons alors écrit un programme qui permet de ne capturer que le contenu textuel du fichier XML compris entre les balises title et description, après la première balise item.

Le traitement des fichiers contient les étapes suivantes :

  • L’extraction de l’encodage des fichiers et son affichage en ligne de commande
  • L’extraction des contenus des balises qui nous intéressent
  • Le nettoyage des données extraites : Suppression des contenus présents en double et remplacement des caractères spéciaux par les bons caractères (comme l’apostrophe par exemple ou les crochets)

Pour connaitre l’encodage du fichier entré, on l’ouvre une première fois pour relever à l’aide d’une regexp ce qui est après encoding. Puis on le ferme. Cela permettra de convertir plus tard en utf8.

On lit une deuxième fois le fichier pour le préparer. Pour cela,  on applique la fonction chomp sur les fins de lignes car tous les fichiers XML ne sont pas sur le même modèle. Ainsi on a un fichier sur une seule ligne, plus facile à gérer. On concatène toutes les lignes ensemble pour créer une chaine complète. Sur laquelle on supprime les éventuelles espaces inter-balises.

Pour prélever le contenu textuel, on a écrit une expression régulière où le texte recherché est capturé avec les (). Le contenu relevé est automatiquement enregistré dans des variables appelées $1 (pour la première paire de parenthèses) et $2 (pour la seconde). Ces dernières sont aussitôt associées aux variables $titre et $description.

On applique à ces title et description une procédure de nettoyage écrite après le programme principale qui consiste à remplacer avec la fonction $chainetrouvee=~s /motif/motif-remplacant/g toutes les erreurs d’encodage trouvées ou l’affichage d’entités sous forme XML. Cette procédure est créée grâce à la fonction sub. La variable $chainetrouvee déclarée dans cette procédure est locale du fait du « my », c’est-à-dire qu’elle n’est pas connue en dehors. On utilise une autre fonction, shift, qui permet de supprimer le premier élément d’une liste et de le renvoyer à la variable de la partie gauche. Ici la liste @_ est créée automatiquement à partir des arguments passés à la procédure, soit $titre, soit $description.

Quand la procédure est terminée, on imprime le tout dans un fichier de sortie.

En langage Perl, l’instruction if s’arrête si elle trouve une première occurrence de la chaîne recherchée et n’ira pas lire les autres. Tandis que while continuera à chercher.

L’expression .* est gourmande. Il faut donc faire attention à son utilisation sinon elle prend absolument TOUS les caractères répétés autant de fois que possible. On lui ajoute alors .*? qui permet de limiter cet effet glouton.

Enfin pour lancer le programme : perl [nom du fichier .pl] [fichier à traiter]

L’étape suivante consiste à permettre à notre script de lire la totalité d’un dossier et de traiter ainsi l’ensemble des fichiers sous-jacents.