Boîte à outils 2 : Étiquetage morpho-syntaxique
Cette étape consiste à effectuer, à partir des fichiers de sortie de la boîte à outils 1 préalablement obtenus, un étiquetage morpho-syntaxique. Ce dernier repose alors sur la segmentation en mots et en phrases. Il s'agit d'associer une étiquette morpho-syntaxique à chaque mot, c'est-à-dire identifier pour chaque mot sa classe morphosyntaxique à partir de son contexte (genre, nombre, temps…etc). Pour ce faire, nous avons utilisé deux étiqueteurs morpho-syntaxiques :
- Cordial.
- Treetagger.
Étiquetage avec Cordial
Cordial est un correcteur grammatical et étiqueteur morpho-syntaxique développé par l’éditeur de logiciels « Synapse », spécialisé dans la linguistique-informatique. Il possède une interface graphique pour lancer l'étiquetage. Cette technologie permet d'obtenir, à partir d'un texte donné en Ascii Unicode, une sortie texte fournissant pour chacun des mots du texte son lemme et sa catégorie grammaticale. Pour obtenir le format en ISO, nous avons eu au cours du programme Perl de la boîte à outil 1 de convertir l’extraction des données pour le format TXT en « ISO » en utilisant la librairie Perl « Encode qw(encode decode) ».
Après avoir lancé l'étiquatage, une boîte de dialogue s'affiche et propose de régler les paramètres de l'étiquetage.
Les unités documentaires obtenues sont au format CNR. Le programme fournit pour chaque forme, son lemme et sa catégorie grammaticale.
Les le DETDPIG
attaques attaque NCFP
de de PREP
l' le DETDMS
Eglise Eglise NPI
contre contre PREP
le le DETDMS
gouvernement gouvernement NCMS
socialiste socialiste ADJSIG
espagnol espagnol ADJMS
tendent tendre VINDP3P
la le DETDFS
campagne campagne NCFS
législative législative ADJFS
Devant devant NCMS
des de DETDPIG
centaines centaine NCFP
de de PREP
milliers millier NCMP
de de PREP
manifestants manifestant NCMP
Voici les résultats de sortie Cordial pour les deux rubriques "A La Une" et "International" respectivement :
Étiquetage avec TreeTagger
TreeTagger est un étiqueteur grammatical fondé sur l'algorithme de Helmut Schmid. L'étiqueteur se base sur une approche probabiliste pour déterminer les catégories morpho-syntaxiques des tokens d'un texte, suite à des entraînements appropriés. Il existe plusieurs versions développées pour différentes langues. L'outil permet d'annoter un texte avec des informations sur les parties du discours (genre de mots) et des informations de lemmatisation. Il est capable de segmenter automatiquement un texte et de déterminer les catégories morpho-syntaxiques des mots, TreeTagger intègre trois grandes techniques: la segmentation, la catégorisation et la lemmatisation.
Procédure TreeTagger dans le script Perl
Nous avons d'abord rassemblé l'ensemble des scripts de cours qui permettent le bon fonctionnement de la procédure d'étiquetage.
Pour ce faire, nous avons récupéré le dossier treegger-win32 sur notre machine qui contient les différents bin et lib dont nous avons besoin.
Liste des différents scripts dont notre procédure Treetagger a besoin dans l'ordre d'appel :
- ./treetagger-win32/cmd/tokenise-fr.pl : pour segmenter le fichier en tokens.
- ./treetagger-win32/bin/tree-tagger.exe : pour l'étiquetage morpho-syntaxique.
- ./treetagger-win32/lib/french.par : le fichier de langue.
- ./treetagger-win32/cmd/treetagger2xml.pl : pour réecrire le résultat au format XML.
Voici le code source Perl de la procédure Treetagger pour notre contexte de travail. Nous avons fait appel à cette procédure durant le parcours de l'arborescence des fils RSS par rubrique :
sub etiquetageavectreetagger {
my ($titre,$texte)=@_;
#----- le titre
my $codage="utf-8";
my $tmptag="texteaetiqueter.txt";
open (TMPFILE,">:encoding(utf-8)", $tmptag);
print TMPFILE $titre,"\n";
close(TMPFILE);
system("perl ./treetagger-win32/cmd/tokenise-fr.pl $tmptag |
./treetagger-win32/bin/tree-tagger.exe
./treetagger-win32/lib/french.par -lemma -token -no-unknown -sgml > treetagger.txt");
system("perl ./treetagger-win32/cmd/treetagger2xml.pl treetagger.txt");
# lecture du resultat tagge en xml :
open(OUT,"<:encoding(utf-8)","treetagger.txt.xml");
my $fistline=<OUT>;
my $titreetiquete="";
while (my $l=<OUT>) {
$titreetiquete.=$l;
}
close(OUT);
#----- le resume
open (TMPFILE,">:encoding(utf-8)", $tmptag);
#print TMPFILE $texte,"\n";
close(TMPFILE);
system("perl ./treetagger-win32/cmd/tokenise-fr.pl $tmptag |
./treetagger-win32/bin/tree-tagger.exe
./treetagger-win32/lib/french.par -lemma -token -no-unknown -sgml > treetagger.txt");
system("perl ./bao2-treetagger2xml.pl treetagger.txt");
# lecture du resultat tagge en xml :
open(OUT,"<:encoding(utf-8)","treetagger.txt.xml");
my $fistline=<OUT>;
my $texteetiquete="";
while (my $l=<OUT>) {
$texteetiquete.=$l;
}
close(OUT);
# on renvoie les resultats :
return ($titreetiquete,$texteetiquete);
}
Comme pour la boîte à outils 1, nous avons développé 3 autres procédures qui permettent de construire les différentes rubriques, nettoyer le texte et parcourir l'arborescence pour la création des différents fichiers.Dans l'odre, voici les codes sources de ces procédures :
Procédure de construction des rubriques
sub constructionrubriques {
my $path = shift(@_);
opendir(DIR, $path) or die "can't open $path: $!\n";
my @files = readdir(DIR);
closedir(DIR);
foreach my $file (@files) {
next if $file =~ /^\.\.?$/;
$file = $path."/".$file;
if (-d $file) {
&constructionrubriques($file); #recurse!
}
if (-f $file) {
if (($file=~/\.xml$/) && ($file!~/\/fil.+\.xml$/)) {
open(FILE,$file);
#print "Traitement de :\n$file\n";
my $texte="";
while (my $ligne=<FILE>) {
$ligne =~ s/\n//g;
$texte .= $ligne;
}
close(FILE);
$texte=~s/> *</></g;
# on recherche la rubrique
if ($texte=~/<channel><title>([^<]+)<\/title>/){
#print $texte,"\n";
my $rub=$1;
my $rub=$1;
$rub=~s/é/e/gi;
$rub=~s/è/e/gi;
$rub=~s/ê/e/gi;
$rub=~s/à/a/gi;
$rub=~ s/Le *Monde *\. *fr *://gi;
$rub =~ s/l'//;
$rub =~ s/://;
$rub = NFKD($rub);
$rub =~ s/\p{NonspacingMark}//g;
$rub=~ s/ / /g;
$rub=~ s/ /-/g;
$rub=lc($rub);
$dictionnairesdesrubriques{$rub}++;
}
}
}
}
}
Procédure de nettoyage du texte
sub nettoietexte {
my $texte=shift;
$texte =~ s/</</g;
$texte =~ s/>/>/g;
$texte =~ s/<a href[^>]+>//g;
$texte =~ s/<img[^>]+>//g;
$texte =~ s/<\/a>//g;
$texte =~ s/'/'/g;
$texte =~ s/"/"/g;
$texte =~ s/é/é/g;
$texte =~ s/ê/ê/g;
$texte =~ s/<[^>]+>//g;
$texte =~ s/ / /g;
$texte=~s/'/'/g;
$texte=~s/"/"/g;
$texte=~s/'/'/g;
$texte=~s/"/"/g;
return $texte;
}
Procédure de parcours de l'arborescence
sub parcoursarborescencefichierspourrepererlesrubriques {
my $path = shift(@_);
opendir(DIR, $path) or die "can't open $path: $!\n";
my @files = readdir(DIR);
closedir(DIR);
foreach my $file (@files) {
next if $file =~ /^\.\.?$/;
$file = $path."/".$file;
if (-d $file) {
&parcoursarborescencefichierspourrepererlesrubriques($file);
}
if (-f $file) {
if (($file=~/\.xml$/) && ($file!~/\/fil.+\.xml$/)) {
open(FILE, $file);
#print "Traitement de :\n$file\n";
my $texte="";
while (my $ligne=<FILE>) {
$ligne =~ s/\n//g;
$texte .= $ligne;
}
close(FILE);
$texte=~s/> *</></g;
# on recherche la rubrique
if ($texte=~/<channel><title>([^<]+)<\/title>/){
my $rub=$1;
$rub=~s/é/e/gi;
$rub=~s/è/e/gi;
$rub=~s/ê/e/gi;
$rub=~s/à/a/gi;
$rub=~ s/Le *Monde *\. *fr *://gi;
$rub =~ s/l'//;
$rub =~ s/://;
$rub = NFKD($rub);
$rub =~ s/\p{NonspacingMark}//g;
$rub=~ s/ / /g;
$rub=~ s/ /-/g;
$rub=lc($rub);
print "rubrique => $rub \n";
$texte=~/encoding ?= ?[\'\"]([^\'\"]+)[\'\"]/i;
my $encodage=$1;
print "ENCODAGE : $encodage \n";
if ($encodage ne "") {
print "Extraction dans : $file \n";
my $tmptexteXML="<file>\n";
$tmptexteXML.="<name>$file</name>\n";
my $tmptexteXMLtagger="<file>\n";
$tmptexteXMLtagger.="<name>$file</name>\n";
$texte =~ s/> *</></g;
$texte=~/<pubDate>([^<]+)<\/pubDate>/;
$tmptexteXML.="<date>$1</date>\n";
$tmptexteXML.="<items>\n";
$tmptexteXMLtagger.="<date>$1</date>\n";
$tmptexteXMLtagger.="<items>\n";
my $tmptexteBRUT="";
$out1="./SORTIE/bao2-extract".$rub.".xml";
$out2="./SORTIE/bao2-extract".$rub.".txt";
$out3="./SORTIE/bao2-extract-treetagger".$rub.".xml";
if (!open (FOUT1,">>:encoding(utf-8)", $out1)) { die "Pb a l'ouverture du fichier $out1"};
if (!open (FOUT2,">>:encoding(iso-8859-1)",$out2)) { die "Pb a l'ouverture du fichier $out2"};
if (!open (FOUT3,">>:encoding(utf-8)", $out3)) { die "Pb a l'ouverture du fichier $out3"};
my $compteurItem=0;
my $compteurEtiquetage=0;
while ($texte =~ /<item><title>(.+?)<\/title>.+?<description>(.+?)<\/description>/g) {
my $titre=$1;
my $resume=$2;
$titre = &nettoietexte($1);
$resume = &nettoietexte($2);
my $titreBRUT= encode("iso-8859-1", $titre);
my $resumeBRUT= encode("iso-8859-1", $resume);
if (uc($encodage) ne "UTF-8") {utf8($titre);utf8($resume);}
$compteurItem++;
if (!(exists($dictionnairedesitems{$resume}))) {
$compteurEtiquetage++;
print "Etiquetage (num : $compteurEtiquetage) sur item (num : $compteurItem) \n";
my ($titreetiquete,$texteetiquete)=&etiquetageavectreetagger($titre,$resume);
$tmptexteBRUT.="§ $titreBRUT \n";
$tmptexteBRUT.="$resumeBRUT \n";
$tmptexteXML.="<item><title>$titre</title><abstract>$resume</abstract></item>\n";
$tmptexteXMLtagger.="<item>\n<title>\n$titreetiquete</title>\n<abstract>\n$texteetiquete</abstract>\n</item>\n";
$dictionnairedesitems{$resume}++;
}
else {
$tmptexteXML.="<item><title>-</title><abstract>-</abstract></item>\n";
}
}
$tmptexteXML.="</items>\n</file>\n";
$tmptexteXMLtagger.="</items>\n</file>\n";
print FOUT1 $tmptexteXML;
print FOUT2 $tmptexteBRUT;
print FOUT3 $tmptexteXMLtagger;
close FOUT1;
close FOUT2;
close FOUT3;
}
else {
print "$file ==> $encodage \n";
}
}
}
}
}
}
Programme principal
#/usr/bin/perl
use Unicode::String qw(utf8);
use Encode;
use utf8;
use Unicode::Normalize;
#-----------------------------------------------------------
my $rep="$ARGV[0]";
# on s'assure que le nom du répertoire ne se termine pas par un "/"
$rep=~ s/[\/]$//;
# on initialise une variable contenant le flux de sortie
my %dictionnairedesitems=();
my %dictionnairesdesrubriques=();
&constructionrubriques($rep);
#----------------------------------------
my @liste_rubriques = keys(%dictionnairesdesrubriques);
my $output1="";
my $output2="";
my $output3="";
foreach my $rub (@liste_rubriques) {
$output1="./SORTIE/bao2-extract".$rub.".xml";
$output2="./SORTIE/bao2-extract".$rub.".txt";
$output3="./SORTIE/bao2-extract-treetagger".$rub.".xml";
if (!open (FILEOUT1,">:encoding(utf-8)", $output1)) { die "Pb a l'ouverture du fichier $output1"};
if (!open (FILEOUT2,">:encoding(iso-8859-1)",$output2)) { die "Pb a l'ouverture du fichier $output2"};
if (!open (FILEOUT3,">:encoding(utf-8)",$output3)) { die "Pb a l'ouverture du fichier $output3"};
print FILEOUT1 "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n";
print FILEOUT1 "<PARCOURS>\n";
print FILEOUT3 "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n";
print FILEOUT3 "<PARCOURS>\n";
close FILEOUT1;
close FILEOUT2;
close FILEOUT3;
}
&parcoursarborescencefichierspourrepererlesrubriques($rep);
foreach my $rub (@liste_rubriques) {
$output1="./SORTIE/bao2-extract".$rub.".xml";
$output3="./SORTIE/bao2-extract-treetagger".$rub.".xml";
if (!open (FILEOUT1,">>:encoding(utf-8)", $output1)) { die "Pb a l'ouverture du fichier $output1"};
if (!open (FILEOUT3,">>:encoding(utf-8)", $output3)) { die "Pb a l'ouverture du fichier $output3"};
print FILEOUT1 "</PARCOURS>\n";
print FILEOUT3 "</PARCOURS>\n";
close FILEOUT1;
close FILEOUT3;
}
Résultats d'exécution de programme Perl pour Treetagger
Nous présentons les différents fichiers de sortie de l'exécution de programme par rubrique
Rubrique | Format TXT | Format XML | Format XML Treetagger |
---|---|---|---|
A la Une | fichier-1-1 | fichier-1-2 | fichier-1-3 |
International | fichier-2-1 | fichier-2-2 | fichier-2-3 |
Liens vers les scripts Perl :
bao2-parcours-arborescence-fichiers-par-rubrique-avec-etiquetage.plbao2-treetagger2xml.pl