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 :

  1. Cordial.
  2. 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/&#39;/'/g;
    $texte =~ s/&#34;/"/g;
    $texte =~ s/é/é/g;
    $texte =~ s/ê/ê/g;
    $texte =~ s/<[^>]+>//g;
    $texte =~ s/ / /g;
    $texte=~s/'/'/g;
    $texte=~s/"/"/g;
    $texte=~s/&#39;/'/g;
    $texte=~s/&#34;/"/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.pl
bao2-treetagger2xml.pl