Introduction
Les étapes du projet :
- parcours de l'arborescence, extraction des titres et descriptions, nettoyage des fichiers obtenus
- etiquetage morpho-syntaxique et lemmatisation
- filtrage sur la base de patrons morpho-syntaxiques
- visualisation des résultats sous forme de graphes
- analyse des résultats obtenus
Le corpus de départ :
- les fils RSS du site de presse Le Monde pour toute l'année 2016
- récupérés automatiquement et mis à disposition par notre enseignant Serge Fleury
Les outils utilisés :
- Atom : éditeur de code
- Oxygen : éditeur XML qui permet de visualiser les résultats des requêtes XPATH, valider des fichiers XML ou des DTD, visualiser un fichier XML sous forme d'arbre, et bien d'autres choses...
- Treetagger et Cordial : deux étiqueteurs morpho-syntaxiques (aussi appelé POS tagger).
- Le fichier executable patron2graphe.exe : permet de visualiser les résultats sous forme de graphes.
- Sans oublier les scripts perls : treetagger2xml-utf8.pl, qui permet d'obtenir les résultats en sortie de treetagger au format xml, et tokenise-utf8.pl pour segmenter les textes en tokens.
La cerise sur le gateau :
- Perl
Première boîte à outil (BàO 1)
Pour cette première boîte à outils, nous souhaitons écrire un script en perl qui soit capable de parcourir notre arborescence et, qui pour une rubrique donnée en argument, produise deux types de sorties: une sortie en texte brut et une sortie XML. Ces sorties contiennent les titres et descriptions de nos fils RSS.
La fonction parcoursArborescenceFichiers est récursive, c'est à dire qu'elle se rapelle elle-même tant qu'une condition n'est pas satisfaite. Ici, chaque fois que "l'objet" rencontré est un dossier, nous rappelons la fonction afin de descendre dans l'arbre constitué par l'architecture de nos dossiers.
A l'intérieur de ce premier script, plusieurs traitements sont mis en place afin de supprimer les doublons, nettoyer les contenus textuels et remplacer certaines entités par le caractère approprié.
Pour produire la sortie XML, nous utilisons deux scripts perl et le programme Treetagger que nous intégrons dans la chaîne de traitements à l'aide de la commande perl system(). En ce qui concerne les scripts perl, tokenise-utf8.pl nous permet de segmenter le texte à traiter en tokens, tandis que treetagger2xml-utf8.pl nous permet d'obtenir une version xml de la sortie de Treetagger.
#!/cygdrive/C/Strawberry/perl/bin
use strict;
<<DOC;
COURTIN Marine
cree le 03/05/2017
usage : perl script.pl repertoire-a-parcourir rubrique
sortie :
- un fichier rubrique.xml avec l'etiquetage en POS et lemme des mots de chaque titre et description
- un fichier rubrique.txt avec le titre et la description de chaque item
DOC
#--------------------- Verification des arguments ------------------------------
my $ChaineUsage="\nUtilisation: perl script.pl repertoire rubrique\n";
if (@ARGV!=2) {
die $ChaineUsage;
}
#-------------------------- Programme principal --------------------------------
# 1. recuperation des arguments passes en lignes de commande
my ($directory, $rubrique) = @ARGV;
$directory =~ s/[\/]$//;
# 2. pour eviter les problèmes d'encodage...
my $encodage = 'utf-8';
binmode(STDIN,":$encodage");
binmode(STDOUT,":$encodage");
# 3. initilisation des sorties
my $output_directory = "SORTIES";
mkdir $output_directory;
open (my $txt, ">", ".\\$output_directory\\$rubrique.txt") || die "erreur a l'ouverture du fichier de sortie en .txt";
open (my $xml, ">", ".\\$output_directory\\$rubrique.xml") || die "erreur a l'ouverture du fichier de sortie en .xml";
print $xml "<?xml version=\"1.0\" encoding=\"$encodage\" ?>\n\n<PARCOURS>\n<NOM>Courtin Marine</NOM>\n<FILTRAGE>\n\n";
# 4. initlialisation des variables globales
my %titres;
my $i=1;
#------------- lancement de la fonction de parcours ---------------------------
&parcoursArborescenceFichiers($directory);
close $txt;
print $xml "\n</FILTRAGE>\n\n</PARCOURS>\n";
close $txt;
exit;
#----------------------- fin du programme --------------------------------------
#-------------------- fonctions crées ------------------------------------------
sub parcoursArborescenceFichiers {
my $dir = shift(@_);
opendir(my $path, $dir) || die "erreur a l'ouverture de $dir: $!";
my @files = readdir($path);
closedir($path);
foreach my $file (@files) {
next if $file =~ /^\.\.?$/;
next if $file =~ /^\._/;
$file = $dir."/".$file;
if (-d $file) {
&parcoursArborescenceFichiers($file)
}
if (-f $file) {
if ($file =~ /$rubrique.+\.xml$/) {
print $i++, "\t$file\n";
open (my $input, "<", $file) || die "erreur a l'ouverture de $file : $!";
print $xml "<file><name>$file<\/name><items>\n";
my $texte="";
while (my $ligne=<$input>) {
chomp $ligne;
$ligne =~ s/\r//g;
$texte .= $ligne;
}
close $input;
$texte =~ s/> +</></g;
$texte =~ s/&#39;/'/g;
while ($texte =~ /<item>(?:<link>(?:.+?)<\/link>)?<title>(.+?)<\/title>(?:<link>(?:.+?)<\/link>)?<description>(.+?)<\/description>/g){
my $titre=$1.".";
my $description=$2;
$description =~ s/<.+?>$//g;
if (!(exists $titres{$titre})) {
my ($titre_tagged, $description_tagged) = (&etiquetagePOS("titre", $titre), &etiquetagePOS("description",$description));
print $txt $titre."\n".$description."\n\n";
print $xml "<item><title>".$titre_tagged."<\/title><description>".$description_tagged."<\/description></item>";
$titres{$titre}=1;
}
}
print $xml "<\/items><\/file>\n"
}
}
}
}
sub etiquetagePOS {
my ($var, $contenu) = @_;
# 1.1 stockage du titre|descrption dans un fichier temporaire
open (my $tmp, ">", ".\\TMP\\$var.txt") || die "erreur a l'ouverture du fichier temporaire : $!";
print $tmp $contenu;
close $tmp;
#1.2 tokenisation, etiquetage en POS, mise au format xml du fichier tagge
system("perl tokenise-utf8.pl -f .\\TMP\\$var.txt | tree-tagger.exe -token -lemma -no-unknown french-utf8.par > .\\TMP\\tagged_$var.txt");
system("perl treetagger2xml-utf8.pl .\\TMP\\tagged_$var.txt utf8");
# 1.3 concatenation de l'etiquetage pour un mm fichier
open (my $tmp_tagged, "<", ".\\TMP\\tagged_$var.txt.xml") || die "erreur a l'ouverture du fichier temporaire : $!";
my $t ="";
while (my $ligne = <$tmp_tagged>) {
chomp $ligne;
next if $ligne =~ /<\?xml version="1.0" encoding="utf-8" standalone="no"\?>/;
$ligne =~ s/\r//g;
$t.=$ligne;
}
close $tmp_tagged;
return $t;
}
Une fois le script lancé sur notre arborescence, nous obtenons ainsi deux fichiers pour chaque rubrique: $rubrique.txt et $rubrique.xml. Le contenu du fichier XML est déjà étiqueté morpho-syntaxiquement et contient également l'annotation du lemme de chaque token. En effet, il se présente comme ceci:
<?xml version="1.0" encoding="utf-8" ?>
<PARCOURS>
<NOM>Marine Courtin</NOM>
<FILTRAGE>
<!-- autant d'items qu'il y a de titres différents pour une même rubrique-->
<item>
<titre>
<document>
<article>
<!-- autant d'éléments qu'il y a de tokens dans le titre-->
<element><data type="type">DET:ART</data><data type="lemma">le</data><data type="string">les</data></element>
<element><data type="type">NOM</data><data type="lemma">licorne</data><data type="string">licornes</data></element>
<element><data type="type">PRP</data><data type="lemma">sous</data><data type="string">sous</data></element>
<element><data type="type">NOM</data><data type="lemma">pression</data><data type="string">pression</data></element>
</article>
</document>
</titre>
<description>
<document>
<article>
<!-- même type de contenu que pour les titres-->
</article>
</document>
</description>
</item>
</FILTRAGE>
</PARCOURS>
En ce qui concerne la sortie en texte brut, nous n'avons pour l'instant aucun type d'annotation. A partir de l'entrée suivante nous souhaitons obtenir un étiquetage morpho-syntaxique. Pour y parvenir, nous utilisons le logiciel Cordial. Le résultat produit en sortie est un fichier texte brut, sur lequel chaque ligne contient les informations relatives à un token. Séparé par des tabulations nous y retrouvons le token, son lemme et l'étiquette morpho-syntaxique.
Brique après brique, Lego construit son réseau.
Le fabricant de jouets danois a ouvert son premier Lego Store à Paris, dans le nouveau Forum des Halles.
Brique brique ADJINV
après après PREP
brique brique NCFS
, , PCTFAIB
Lego lego NCMS
construit construire VINDP3S
son son DETPOSS
réseau réseau NCMS
. . PCTFORTE
Les fichiers contenant le texte extrait à partir des fils RSS sont disponibles dans cette archive.
Les fichiers xml et cnr (texte brut) contenant les textes annotés morpho-syntaxiquement sont disponibles dans cette archive.
Nos fichiers ont été traités, et sont prêts à nous révéler tous leurs secrets. Pour voir comment se déroule l'extraction des motifs morpho-syntaxiques, rendez-vous sur l'onglet BàO 2...
Deuxième boîte à outils (BàO 2)
Cette deuxième boîte à outils concerne l'extraction de patrons. Pour ce faire, nous allons commencer par choisir des motifs à extraire. Ces motifs sont écrits sous la forme d'expressions régulières. C'est une étape crucial car le choix des motifs influencera significativement les résultats de notre extraction.
Au cours de ce projet, nous avons cherché des motifs morpho-syntaxiques permettant de reconnaître les entités nommées. Les types d'entités nommées que nous avons cherché à reconnaître sont les suivantes: nom de personne, entreprise ou institution, produit culturel, nom de lieu, date.
Pour extraire une entité nommée, il est courant de chercher à repérer l'élément qui l'introduit. Ces éléments suivent eux-même des motifs qui varient selon le type d'entité nommée, le registre de langue, ou encore le genre du texte. Au cours de ce projet, nous avons cherché à:
- 1. vérifier que l'introducteur varie selon le type d'entité nommée: est-ce que les personnes et les entreprises sont effectivement introduites de manières différentes ? Si cette condition est vérifiée, il nous sera utile d'identifier les motifs discriminants nous permettant de désambiguiser le type de l'entité nommée qui est identifiée.
- 2. observer comment l'élément introducteur d'entité nommée varie selon la rubrique étudiée. Un même type d'entité est-il introduit différemment selon la rubrique. Si oui, quelles conclusions pouvons-nous en tirer ?
Les motifs choisis sont les suivants:
- DET NC PREP (NC|NP)+ -> le scandale de corruption Petrobras. Dans ce patron, de nombreux types d'entités nommées peuvent apparaître. En effet, celles-ci peuvent faire référence aux personnes, entreprises, pays, produits culturels, équipes sportives lorsque ceux-ci sont les thèmes de la phrase analysée. Une fois le motif capturé, l'élément qui nous intéresse se trouve le plus souvent dans la partie droite de l'expression.
- (NOM|DET) NUM NOM NUM -> le 14 avril 2014. Ce patron permet de repérer des dates, il a été choisi pour sa faible ambiguïté. Ici, c'est l'expression entière qui constitue l'entité nommée.
Nous pourrons préciser des conditions sur les caractères utilisés (par exemple une majuscule en début de mot, une préposition spécifique pour introduire l'entité...) dans une étape ultérieure, où nous utiliserons des motifs lexicaux pour créer les graphes.
Il serait également possible d'adapter les motifs selon les rubriques parcourues. En effet, les entités nommées de type personne que nous trouveront probablement en grand nombre dans les rubriques culture et international ne seront pas nécessairement introduites par les mêmes types de motifs. De plus, les relations qu'entretiennent ces entitées nommées avec leur contexte (et éventuellement avec d'autres entités nommées) varieront peut-être en fonction de la rubrique à laquelle le texte appartient. Pour étudier ce phénomène, nous avons choisi trois rubriques sur lesquelles nous concentrer: International, Culture et Sport.
Nous observons déjà des différences dans l'étiquetage qui affecteront sans doute le repérage des entités nommées. En ce qui concerne les dates, Treetagger repère les numéraux, contrairement à Cordial, ce qui devrait nous faciliter leur identification. Treetagger et Cordial différencient tous deux les noms propres des noms communs. Favoriser les noms propres nous permettrait d'améliorer notre précision, en revanche la liste des noms propres n'étant pas fermée, nous risquons de passer à côté de nombreuses entités nommées si nous choisissons de nous concentrer sur les tokens repérés comme noms propres.
- Feuille de style XSLT
Nous commençons par repérer des motifs morpho-syntaxiques en utilisant une feuille de style XSLT appliquée au document XML étiqueté par Treetagger. La feuille de style permet de sélectionner uniquement les éléments et contenus qui nous intéressent et de les mettre en avant sur une page html, ou dans un autre type de document. Ici nous avons associé une feuille de style au fichier appartenant à la rubrique "A la une", afin d'identifier les éléments qui suivent un motif (DET|NOM) NUM NOM NUM pour repérer des dates. Comme nous pouvons le voir, il ne s'agit que d'un motif et les requêtes XPATH sont déjà très longues. Nous estimons que ce moyen d'extraire nos motifs n'est pas le plus adapté, et poursuivons notre route vers les scripts perl qui nous permettront d'automatiser nos recherches de motifs.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html" indent="no"/>
<xsl:variable name="vUpper" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'"/>
<xsl:variable name="vLower" select="'abcdefghijklmnopqrstuvwxyz'"/>
<xsl:variable name="vDigits" select="'0123456789'"/>
<xsl:template match="/">
<html>
<head>
<style>
body {
background-color: #22222D;
color: #BEC4CD;
}
</style>
</head>
<body>
<table align="center" width="80%" bordercolor="#BEC4CD" border="1">
<tr>
<td width="90%" valign="top">
<h1>Extraction des motifs <font color="#E06C62">NOM NUM NOM NUM </font></h1>
</td>
</tr>
<tr>
<td>
<blockquote>
<xsl:apply-templates select=".//element"/>
</blockquote>
</td>
</tr>
</table>
</body>
</html>
</xsl:template>
<xsl:template match="element">
<xsl:choose>
<xsl:when
test="(./data[(contains(text(),'NOM')) or (contains(text(),'DET'))]) and (following-sibling::element[1][./data[contains(text(),'NUM')]]) and (following-sibling::element[2][./data[contains(text(),'NOM')]]) and (following-sibling::element[3][./data[contains(text(),'NUM')]])">
<font color="#E06C62">
<xsl:value-of select="./data[3]"/>
</font>
<xsl:text> </xsl:text>
</xsl:when>
<xsl:when
test="(./data[contains(text(),'NUM')]) and (preceding-sibling::element[1][./data[(contains(text(),'NOM') or contains(text(), 'DET'))]]) and (following-sibling::element[1][./data[contains(text(),'NOM')]]) and (following-sibling::element[2][./data[contains(text(),'NUM')]])">
<font color="#E06C62">
<xsl:value-of select="./data[3]"/>
</font>
<xsl:text> </xsl:text>
</xsl:when>
<xsl:when
test="(./data[contains(text(),'NOM')]) and (preceding-sibling::element[2][./data[(contains(text(),'NOM') or contains(text(), 'DET'))]]) and (preceding-sibling::element[1][./data[contains(text(),'NUM')]]) and (following-sibling::element[1][./data[contains(text(),'NUM')]])">
<font color="#E06C62">
<xsl:value-of select="./data[3]"/>
</font>
<xsl:text> </xsl:text>
</xsl:when>
<xsl:when
test="(./data[contains(text(),'NUM')]) and (preceding-sibling::element[3][./data[(contains(text(),'NOM') or contains(text(), 'DET'))]]) and (preceding-sibling::element[2][./data[contains(text(),'NUM')]]) and (preceding-sibling::element[1][./data[contains(text(),'NOM')]])">
<font color="#E06C62">
<xsl:value-of select="./data[3]"/>
</font>
<br/>
</xsl:when>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
- Script de Jean-Michel Daube
Notre premier script permet de traiter la sortie en texte brut de Cordial. Lorsqu'une ligne ne correspond pas au format classique avec trois annotations séparées par des tabulations, où qu'elle contient une étiquette morpho-syntaxique de ponctuation, nous l'ignorons car nous ne la considérons pas pertinente.
Dans ce script, nous créons une liste de tokens et une liste d'etiquettes morpho-syntaxiques. Les informations relatives à un token apparaîtront au même index dans chaque liste. Nous stockons dans une variable le nombre d'espaces présents avant le motif, et dans le motif lui-même afin d'être en mesure de ne conserver qu'une tranche de la liste des tokens: celle qui contient l'élément lexical à extraire.
Ce script a un avantage certain par rapport à la feuille de style XSLT précédente. Une fois nos motifs trouvés, nous n'avons qu'à les écrire dans une fichier texte brut et à lancer le script. Les joies du traitement automatique !
#!/cygdrive/C/Strawberry/perl/bin
#
# --> Extraction de patrons de POS dans un fichier étiqueté par Cordial
#
# 1er argument : fichier etiqueté par CORDIAL
# 2eme argument: fichier de motif
#
#############
use utf8;
# binmode STDOUT, ":utf8";
open(FIC, "<:encoding(iso-8859-1)", $ARGV[0]);
open(MOTIF, "<:encoding(iso-8859-1)", $ARGV[1]);
open my $output, ">:encoding(iso-8859-1)", "extractionMotifs.txt";
# open my $output, ">", "extractionMotifs.txt";
my @motifs = <MOTIF>;
# print @motifs;
close(MOTIF);
my @token =();
my @pos = ();
while (my $ligne = <FIC>) {
next if ($ligne!~/^[^\t]+\t[^\t]+\t[^\t]+/);
$ligne =~ s/\r//g;
# si la ligne ne contient pas pctforte
if ($ligne !~/^[^\t]+\t[^\t]+\tPCTFORTE/) {
chomp($ligne);
my @LISTE=split(/\t/, $ligne);
# print "LISTE\t@LISTE\n";
# rajoute l'elt $LISTE[0] à la fin de la liste @token
push(@token, $LISTE[0]);
push(@pos, $LISTE[2]);
# print "token\t@token\n";
# print "pos\t@pos\n";
}
else {
&extract;
@token=();
@pos=();
}
}
close FIC;
close $output;
# utf-8 = miam
system("iconv -f ISO-8859-1 -t UTF-8 extractionMotifs.txt > extractionMotifs_utf8.txt");
sub extract {
my $tokenstring = join(' ', @token);
my $posstring = join(' ', @pos);
foreach my $motif (@motifs) {
chomp($motif);
my $nbEspacesAvant =0;
while ($posstring =~ /$motif/g)
{
# my $avant ligne des catégories avant que le motif n'intervienne
my $avant = $`;
while ($avant =~ / /g)
{
$nbEspacesAvant++;
}
# print "espaces avant\t$nbEspacesAvant\n";
my $nbEspacesMotif =0;
while ($motif =~ / /g) {
$nbEspacesMotif++;
}
my $total = $nbEspacesAvant + $nbEspacesMotif;
$contenu="$motif\t@token[$nbEspacesAvant..$total]\n";
print $output $contenu;
}
}
}
Le motif : (DET[^\s]+ )?NC[^\s]+? PREP (NC|NP)[^\s]+? NP
Les fichiers extraits (utf-8):
rubrique international |
rubrique culture |
rubrique sport
- Script de Serge Fleury
Dans ce script, nous parcourons notre fichier étiqueté par Cordial (le premier argument donné en ligne de commande) ligne par ligne. Nous donnons également une séquence d'étiquettes morpho-syntaxiques séparées par un espace en argument. Cette séquence constitue le motif que nous cherchons à reconnaître.
Quand l'étiquette morpho-syntaxique du token correspond à celle demandée par le motif, nous stockons le contenu de la première colonne (le token) dans une variable scalaire. Nous continuons d'y concaténer la suite des tokens, tant que leur étiquette morpho-syntaxique respecte le motif. Si l'ensemble du motif est reconnu, la longueur du segment reconnu est égale à l'index du dernier argument $#ARGV. Chaque fois que cette condition est respectée nous écrivons le segment dans le fichier de sortie.
L'écriture des motifs en ligne de commande rend ce script très maniable. Nous pouvons jongler avec les requêtes sans avoir à revenir sur notre interface graphique. De plus, sa syntaxe nous semble plus limpide et facile à adapter pour traiter les fichiers en XML.
En revanche, la prise en argument des étiquettes morpho-syntaxiques une à une ne permet pas d'utiliser tout le potentiel des expressions régulières. Si notre motif est simple, cela ne pose pas de problème, nous avons toujours la possibilité de faire plusieurs requêtes et d'en combiner les résultats. Cependant, dès lors que nos motifs se rallongent et surtout se complexifient (lorsque nous laissons le choix entre plusieurs catégories pour un argument par exemple), il devient difficile d'utiliser ce script. En effet nous ne pouvons pas faire de requête où un argument serait de ce type : (DET|NOM).
#!/cygdrive/C/Strawberry/perl/bin
# 1. encodage
my $encodage = 'utf-8';
binmode(STDIN,":$encodage");
binmode(STDOUT,":$encodage");
# 2. lecture du fichier d'entrée
open(my $file,"$ARGV[0]") || die "erreur a l'ouverture du fichier d'entrée etiqueté par Cordial";
my @lignes=<$file>;
close($file);
# 3. initilisation des sorties
my $output_directory = "EXTRACTION";
mkdir $output_directory;
open (my $output, ">", ".\\$output_directory\\motifs_extraits_cordial_2.txt") || die "erreur a l'ouverture du fichier de sortie en .txt";
while (@lignes) {
my $ligne=shift(@lignes);
chomp $ligne;
my $sequence="";
my $longueur=0;
if ( $ligne =~ /^([^\t]+)\t[^\t]+\t$ARGV[1]/) {
$sequence.=$1;
$longueur++;
my $indice=1;
my $stop=1;
while (($indice < $#ARGV) and ($stop == 1)) {
my $nextligne=$lignes[$indice-1];
if ( $nextligne =~ /^([^\t]+)\t[^\t]+\t$ARGV[$indice+1]/) {
$sequence.=" ".$1;
$longueur++;
}
else {
$stop=0;
}
$indice++;
}
if ($longueur == $#ARGV) {
print $output $sequence,"\n";
}
}
}
close($output);
Le premier motif : DET:... NOM PRP NAM NAM
Les fichiers extraits (utf-8):
rubrique international |
rubrique culture |
rubrique sport
Le deuxième motif : DET:... NOM PREP NOM NAM
Les fichiers extraits (utf-8):
rubrique international |
rubrique culture |
rubrique sport
- Script d'extraction depuis les fichiers XML
Ce script fonctionne à l'identique du précédent, le seul changement concerne la ligne d'expression régulière à tester. Nous nous servons désormais du format structuré d'XML pour viser l'étiquette morpho-syntaxique afin de vérifier notre condition, et extrayons le contenu textuel de l'élément data de type string pour obtenir notre token.
#!/cygdrive/C/Strawberry/perl/bin
# 1. encodage
my $encodage = 'utf-8';
binmode(STDIN,":$encodage");
binmode(STDOUT,":$encodage");
# 2. lecture du fichier d'entrée
open(my $file,"$ARGV[0]") || die "erreur a l'ouverture du fichier d'entrée en .xml";
my @lignes=<$file>;
close($file);
# 3. initilisation des sorties
my $output_directory = "EXTRACTION";
mkdir $output_directory;
open (my $output, ">", ".\\$output_directory\\motifs_extraits_treetagger.txt") || die "erreur a l'ouverture du fichier de sortie en .txt";
while (@lignes) {
my $ligne=shift(@lignes);
chomp $ligne;
my $sequence="";
my $longueur=0;
if ( $ligne =~ /<element><data type=\"type\">$ARGV[1]<\/data><data type=\"lemma\">[^<]+<\/data><data type=\"string\">([^<]+)<\/data><\/element>/) {
$sequence.=$1;
$longueur++;
my $indice=1;
my $stop=1;
while (($indice < $#ARGV) and ($stop == 1)) {
my $nextligne=$lignes[$indice-1];
if ( $nextligne =~ /<element><data type=\"type\">$ARGV[$indice+1]<\/data><data type=\"lemma\">[^<]+<\/data><data type=\"string\">([^<]+)<\/data><\/element>/) {
$sequence.=" ".$1;
$longueur++;
}
else {
$stop=0;
}
$indice++;
}
if ($longueur == $#ARGV) {
print $output $sequence,"\n";
}
}
}
close($output);
Le premier motif : DET:... NC PREP NAM NAM
Les fichiers extraits (utf-8):
rubrique international |
rubrique culture |
rubrique sport
Le deuxième motif : DET:... NOM PRP NOM NAM
Les fichiers extraits (utf-8):
rubrique international |
rubrique culture |
rubrique sport
Après comparaison des résultats de l'extraction depuis les fichiers XML et txt, nous constatons que l'étiquetage effectué par Cordial retourne généralement un plus grand nombre d'entrées. C'est très visible notamment pour le fichier de la rubrique Sport, lorsque nous cherchons deux noms propres en fin d'expression. Néanmoins, cette observation nous paraît trop isolée pour nous permettre d'affirmer que Cordial reconnaît mieux les noms propres et donc certaines entités nommées. Il faudrait pour cela effectuer davantage de tests et comparer les deux logiciels en terme de précision, rappel et f-mesure.
Nous disposons désormais de tous les fichiers nécessaires pour passer à la dernière étape : l'extraction de motifs lexicaux depuis nos fichiers et la visualisation des résultats sous forme de graphes.
Analyse des résultats
Une fois les patrons morpho-syntaxiques extraits, nous nous attelons à la dernière partie de ce projet. Il nous faut choisir des motifs lexicaux, produire les graphes de cooccurrence à l'aide de l'outil patron2graph.exe et comparer les résultats obtenus selon les rubriques.
Le fichier executable requière trois arguments:
- l'encodage utilisé
- le fichier contenant nos entrées, c'est à dire le résultat du filtrage morpho-syntaxique des fichiers en texte brut.
- un fichier contenant notre motif lexical sur la base duquel nous filtrons les entrées (facultatif)
- le motif \bà\b retournera essentiellement des noms de lieux et peut-être quelques noms de personnes.
- le motif \ben\b retournera lui aussi des noms de lieux.
- le motif \bde\b retournera des noms de personnes surtout pour les rubriques Culture et Sport. Il retournera également des noms de lieux, d'équipes sportives et d'entreprises.
- le motif \bentre\b fera le lien entre deux entités nommées de même type: personnes, Etats..
- le motif \bpar\b attribuera l'autorité d'une action à une entité de type personne.
- International → introduit majoritairement un nom de personne (soutien à X), quelques noms de lieu, un nom d'entreprise (réduire son exposition à X)
- Culture → là encore, la préposition est utilisé avec des noms de personne majoritairement, mais aussi des noms d'entreprise et groupes de musique
- Sport → cette fois-ci, les entités introduits sont différentes: lieu géographique, nom de compétition sportive, nom d'équipe sportive/club
- International → lorsque nous cherchons deux noms propres: nous retrouvons des noms de politiciens ou figures publiques (régime de X, gouvernement de Y, procès de Z). Lorsqu'il est associé à par il y a un phénomène de mise en exergue du responsable de l'action (au licenciement par X d'Y); nom commun puis nom propre: lieu (au coeur de X)
- Culture → nom de personne ou encore lieu ou se tient un évênement culturel (au festival de X)
- Sport → deux noms propres: nom d'équipe (face au X); nom commun puis nom propre: nom de compétition
- International → deux noms propres: politicien (course en tête de X, mise en examen de Y); nom commun suivit d'un nom propre: lieu (un nauvrage en mer Egée)
- Culture → noms d'artistes (le X de Y Z, avec X: profession, Y: domaine artistique, Z:nom)
- Sport → nom de compétition sportive, nom d'équipe sportive
- International → responsable politique derrière une loi, identification de l'auteur d'un discours (l'annonce par X)
⤷ peut indiquer un rapport de force, l'entité nommée se trouve en situation de pouvoir/contrôle sur quelqu'un ou quelque chose - Culture → adaptation par X de Y (où X est un artiste et Y un oeuvre)
- Sport → sportif dont on souhaite mettre en avant une action (égalisation du score par X)
- International → noms de politicien, d'Etats, met en relation deux entités, la relation est présentée comme équilibrée (à l'inverse de quand par est employé) (conversation entre X et Y, affrontement entre X et Y...)
- Culture → nom de personne (artiste, politicien), met également en relation deux entités
- Sport → équipe sportive/club, relation entre deux groupes adversaires
- International → sélection / isolement d'un responsable politique ou d'une figure publique (le gouvernement de X, la démission de Y, la dictature de Z, l'extradition de W). Mise en évidence d'une continuité, filiation (le gendre de X, les partisans de Y, le successeur de Z). Quelques lieux et entreprises.
- Culture → Nom d'artiste (de X à Y), panorama sur un mouvement artistique. Entreprise ou nom d'artiste, désignés comme auteurs/producteurs d'une oeuvre, autorité sur la production artistique (les créateurs de X). Oeuvre, identifiée par son créateur plutôt qu'en elle même (l'oeuvre de X).
- Sport → Lieu (la pelouse de Y) où se déroule un évênement sportif. Attribution d'un exploit sportif, d'une défaite avec sélection de son responsable à travers le pronom défini (le X de Y). Effacement du groupe au profit d'un individu (l'équipe de X, les joueuses de Y)
Nous choisissons plusieurs motifs lexicaux qui devraient nous permettre de retrouver nos entités nommées et leurs cooccurrents. Voici les hypothèses que nous avons avant de commencer:
Il ne nous reste plus qu'à passer à la présentation des résultats :
à :

au :

en :

par:

entre:

de :

Les autres graphes de visualisation sont disponibles dans cette archive.
Nous avions commencé avec la problématique suivante : comment varient les introducteurs d'entités nommées selon le type d'entité et la rubrique considérée ?
Les résultats obtenus nous permettent de faire quelques observations :
- Les prépositions introductrices varient bien plus que nous n'en avions fait l'hypothèse. Une même préposition peut être employée pour presque tous les types d'entités nommées, et les relations créées entre l'entité et son contexte peuvent être très nombreuses pour une même préposition. Ce comportement rend le repérage de motifs discriminants pour un type d'entité très difficile.
- Il existe effectivement des variations dans l'introduction des entités selon les rubriques. La rubriques sport en particulier présente des emplois que nous n'avons pas relevé pour les deux autres rubriques, comme l'utilisation de "en" et "à" pour introduire un évênement (en l'occurence une compétition sportive).
- Pour une même préposition d'introduction, les cooccurrents et les connotations qui les accompagnent varient significativement. Nous remarquons le cas de "par" qui introduit une action originale et potentiellement controversée dans la rubrique International et une adaptation, c'est à dire la réinterprétation et la réappropriation de ce qui existe déjà, dans la rubrique Culture.
L'analyse des graphes montre ses limites, en effet pour identifier les relations entre l'entité nommée et son contexte, nous nous rendons compte de la necessité de faire des allers-retours entre les réseaux de cooccurrents mis en évidence par les graphes et le texte extrait dans la BàO1. Il serait sans doute judicieux d'intégrer à l'avenir un concordancier à ce type d'analyse, afin de faciliter cette démarche. Pour obtenir ce concordancier, nous pouvons écrire un script perl en utilisant les variables prédéfinies $`, $& et $' ou encore utiliser un logiciel comme Le Trameur .
En conclusion
Ainsi s'achève ce projet encadré qui nous a occupé pendant le deuxième semestre du M1 TAL. Nous y avons appris les éléments suivants :
- écriture de scripts en perl afin d'extraire du contenu textuel depuis un document XML
- utilisation des programmes Cordial et Treetagger pour l'étiquetage morpho-syntaxique
- filtrage de motifs à l'aide d'expressions régulières
- écriture de feuilles de styles XSLT afin de transformer un document XML
- révisions sur HTML et CSS pour construire ce site
Bonne journée et à bientôt