Boîte à outil 3 : Extraction de patrons syntaxiques

Objectif

La troisième partie de ce projet consiste à extraire de notre corpus des séries de mots en fonction des catégories grammaticales auxquelles ils appartiennent. On s'intéresse par exemple à toutes les suites de noms suivis d'un adjectif ou encore de tous les noms suivis d'une préposition puis d'un autre nom.

Les méthodes mises en oeuvre

Grâce à l'étiquetage du corpus effectué à l'étape précédente, nous pouvons extraire ces patrons syntaxiques de plusieurs manières. À partir du résultat de l'étiquetage au format XML obtenu à la BAO2 avec TreeTagger, nous avons prévu des feuilles de style XSLT qui sélectionnent à l'aide d'XPath un patron syntaxique. Nous avons mis en oeuvre une seconde méthode à base d'expressions régulières qui extrait dans le résultat au format CNR de Cordial® un ou plusieurs patrons syntaxiques.

Méthode 1 : Une feuille de style XSLT

Puisque le fichier de sortie obtenu avec TreeTagger est au format XML, il est possible de lui associer une feuille de style XSLT qui sélectionne parmi les élements étiquetés ceux remplissant les critères du patron syntaxique choisi.

Lorsqu'on travaille à partir des fichiers par rubrique, on a l'arborescence suivante :

Pour extraire le patron syntaxique NOM ADJ, on indiquera dans la feuille de style XSLT au moyen d'une règle sur toutes les balises <element> qu'on souhaite afficher pour un <element> le contenu de la troisième balise <data> à condition que cet élément possède aussi une balise <data> contenant la chaîne de caractère NOM et que le permier <element> suivant possède une balise <data> contenant la chaîne de caractère ADJ :

<xsl:template match="element">
<xsl:choose>
<xsl:when test="(./data[contains(text(),'NOM')]) and (following-sibling::element[1][./data[contains(text(),'ADJ')]])">
<xsl:value-of select="./data[3]"/>
</xsl:when>
</xsl:choose>
</xsl:template>

Mais cette règle est incomplète car elle n'affichera que les noms suivis d'un adjectif et pas l'adjectif en question. On prévoit donc aussi d'afficher pour un element le contenu de la troisième balise <data> à condition que cet élément possède aussi une balise <data> contenant la chaîne de caractère ADJ et que le premier <element> précédent possède une balise <data> contenant la chaîne de caractère NOM. Grâce à ce couple de condition, le processeur XSLT ou le navigateur affichera bout à bout tous les noms suivis d'un adjectif ainsi que les adjectifs précédés d'un nom. Pour améliorer la lecture du résultat, il faut prévoir d'insérer un espace entre un nom et un adjectif ainsi qu'un retour à la ligne après cet adjectif :

<xsl:template match="element">
<xsl:choose>
<xsl:when test="(./data[contains(text(),'NOM')]) and (following-sibling::element[1][./data[contains(text(),'ADJ')]])">
<xsl:value-of select="./data[3]"/>
<xsl:text> </xsl:text>
</xsl:when>
<xsl:when test="(./data[contains(text(),'ADJ')]) and (preceding-sibling::element[1][./data[contains(text(),'NOM')]])">
<xsl:value-of select="./data[3]"/>
<br/>
</xsl:when>
</xsl:choose>
</xsl:template>

Pour que cette règle s'applique à toutes les balises <element> du document XML, il faut lui indiquer dans la règle principale de la feuille de style XSLT le chemin vers ces balises :

<xsl:apply-templates select="./rubrique/article/*/element"/>

C'est à dire qu'on part de l'élément racine <rubrique> puis on descend l'arborescence jusqu'au fils <element>. Comme la balise <article> peut contenir soit <titre> soit <resume>, on indique dans le chemin XPath ces deux possibilités (ou plus) au moyen du symbole *.

On procédera de la même manière pour n'importe quel patron syntaxique. Pour un patron à plus de deux élements comme NOM PRP NOM, on continuera simplement la numérotation des éléments frères (preceding-sibling::element[2], following-sibling::element[2]...). Lorsqu'on prévoit une transformation du document XML d'origine au format HTML, on peut améliorer la présentation des résultats dans un tableau avec des couleurs par catégorie grammaticale.

Voici les feuilles de style XSLT pour l'extraction des patrons NOM ADJ et NOM PRP NOM dans les fichiers XML par rubrique produits dans la BAO2 par TreeTagger : patron-NOM-ADJ.xsl patron-NOM-PRP-NOM.xsl

Vous pouvez consulter le résultat obtenu avec ces feuilles de styles appliquées sur les rubriques Europe et Culture :

On s'apperçoit évidemment que la qualité des résultats dépend surtout de la qualité de l'étiquetage. Si on prend l'exemple de l'extraction des noms suivis d'une préposition puis d'un nom dans la rubrique Europe, on trouve le cas suivant :

contrat du rap des années 2000. Rencontre

La suite de caractères "2000." a mal été segmentée et identifiée par TreeTagger. Pour les cas du français ou une préposition et un article sont amalgamés, TreeTagger produit la glose PRP:det. Cette glose contient bien la chaîne de caractère "PRP" qui est une des conditions pour l'affichage du patron NOM PRP NOM dans notre feuille de style XSLT. Il est possible de restreindre le nombre des résultats au cas où on a uniquement une préposition et non l'amalgame d'une préposition et d'un article en précisant dans les règles qu'on ne souhaite que les cas ou la balise <data> est égale à PRP :

<xsl:template match="element">
<xsl:choose>
<xsl:when test="(./data[contains(text(),'NOM')]) and (following-sibling::element[1][./data='PRP']) and (following-sibling::element[2][./data[contains(text(), 'NOM')]])">
<xsl:value-of select="./data[3]"/>
<xsl:text> </xsl:text>
</xsl:when>
<xsl:when test="(./data='PRP') and (preceding-sibling::element[1][./data[contains(text(),'NOM')]]) and (following-sibling::element[1][./data[contains(text(),'NOM')]])">
<xsl:value-of select="./data[3]"/>
<xsl:text> </xsl:text>
</xsl:when>
<xsl:when test="(./data[contains(text(),'NOM')]) and (preceding-sibling::element[1][./data='PRP']) and (preceding-sibling::element[2][./data[contains(text(), 'NOM')]])">
<xsl:value-of select="./data[3]"/>
<br/>
</xsl:when>
</xsl:choose>
</xsl:template>

Cette nouvelle feuille de style ainsi que le résultat obtenu sur le fichier XML de la rubrique Europe sont disponibles ci-dessous :

Méthode 2 : Extraction de patrons par expressions régulières

À partir de l'étiquetage produit par Cordial®, nous avons mis au point un script Perl à base d'expressions régulières permettant l'extraction de patrons syntaxiques choisis par l'utilisateur. Pour fonctionner, notre script à besoin d'un fichier de sortie Cordial® et d'un fichier texte contenant la liste des patrons syntaxiques à extraire. Ce fichier contient un patron par ligne comme par exemple :

NC ADJ
NC PREP NC
DETDMS NCMS DETDMS NCMS

Voici les tâches effectuées par ce script :

  • Pour chaque ligne du fichier contenant les patrons, on reconstitue l'expression régulière qui permettera d'extraire le patron recherché dans le fichier de Cordial®.
  • On concatène sur une même ligne le contenu de chaque phrase étiquetée dans le fichier Cordial®.
  • On extrait de ces phrases le patron recherché au moyen de l'expression régulière.
  • On enregistre dans un fichier de sortie le résultat de l'extraction (un fichier par patron)

Concernant la reconstitution de l'expression régulière, prenons l'exemple du patron NC ADJ. Une fois que les données de Cordial® sont conacténées sur une ligne par phrase, un nom suivi d'un adjectif se présente sous cette forme :

$ligne="les le DETDPIG chapeaux chapeau NCMP bleus bleu ADJMP sont être VINDP3P grands grand ADJMP"

C'est à dire, n'importe quelle suite de caractères sauf une tabulation(il s'agit de la forme du nom) suivie d'une tabluation puis de n'importe quelle suite de caractères sauf une tabulation(il s'agit du lemme du nom) puis de la glose NCMP suivie d'une tabulation puis de n'importe quelle suite de caractères sauf une tabulation(il s'agit de la forme de l'adjectif) suivie d'une tabulation puis de n'importe quelle suite de caractères sauf une tabulation(il s'agit du lemme de l'adjectif) suivie de la glose ADJMP.

On peut ainsi extraire dans cet exemple les formes du nom suivi de son adjectif au moyen de cette expression régulière :

$ligne=~/([^\t]+)\t[^\t]+\tNCMP\t([^\t]+)\t[^\t]+\tADJ/g
$nom=$1;
$adj=$2;

Pour convertir n'importe quel patron en une expression régulière qui pourra être exploitable dans la suite du script, il faut donc :

  • prendre en compte la précision des gloses de Cordial®. On doit par exemple prévoir les cas où on cherche simplement un adjectif (ADJ) et pas uniquement un adjectif masculin pluriel (ADJMP) ou féminin singulier (ADJFS). Pour cela, on indique dans l'expression régulière qu'après chaque élément du patron peut se trouver zéro ou plusieurs caractères alphanumériques [0-9A-Z]* (ex: VINDP3P, ADJSIG, PPER3S)
  • remplacer chaque espace entre les éléments du patron par [0-9A-Z]*\t([^\t]+)\t[^\t]+\t
  • insérer au début du patron l'expression pour récupérer la première forme du patron : ([^\t]+)\t[^\t]+\t
  • prévoir à la fin du dernier élément du patron la présence ou non d'un ou plusieurs caractères alphanumériques : [0-9A-Z]*

Une des difficultés dans l'exploitation de ces expressions régulières est que la longueur du patron syntaxique saisi par l'utilisateur peut être de taille différente (NC ADJ, NC PREP NC...). Par conséquent, le nombre de variables permettant de récupérer le contenu textuel extrait au moyen des expressions régulières peut varier ($2,$3...). Heureusement, il est possible en Perl de récupérer automatiquement dans un tableau la liste des valeurs renvoyées par une recherche par expression régulière :

my @matches = ($phrase=~m/$match/g)
# Si $phrase contient la phrase étiquetée par Cordial® et $match l'expression régulière d'extraction d'un patron, @matches contiendra alors toutes les valeurs renvoyées par la recherche

Pour afficher les résultats à partir de ce tableau, il est possible de connaître la longueur de la liste qu'il contient :

$lenmatches = @matches;
# Si la recherche par expression régulière a retourné 3 valeurs alors $lenmatches vaut 3

Le script complet mis au point avec ses commentaires est accessible ici : cordialmatch.pl

Voici quelques-uns des résultats obtenus à partir de ce script sur les fichiers rubriques Europe et Société :

Là encore la qualité des résultats dépend de la qualité de l'étiquetage. Comme les titres des articles ne se terminent pas toujours par un point, il arrive qu'un patron syntaxique soit extrait entre la fin d'un titre et le début d'un résumé. On aurait pu régler ce problème en prévoyant dès la BAO1 de vérifier que chaque titre se termine par un symbole de ponctuation forte.

Un autre problème réside dans l'utilisation des expressions régulières pour l'extraction de patrons syntaxiques. Nous avons fait le constat suivant à partir de ce petit script test :

#!/usr/bin/perl -w
my $texte="abababa";
while($texte=~/aba/g){print "Trouvé !\n";}

Voici le résultat affiché dans le terminal :

Cela signifie que la chaîne de caractères "aba" n'a été identifiée que deux fois dans "abababa" et non trois fois comme nous l'aurions souhaité. Par conséquent, le script que nous avons mis en place ne permet pas d'extraire la totalité des données lorsqu'un patron syntaxique se repète successivement dans un énoncé. Si une phrase contient la suite NOM PREP NOM PREP NOM comme dans certains cas mis en évidence par les feuilles de style XSLT (ex: "politique d' assainissement des comptes") la première suite NOM PREP NOM sera reconnue puis la recherche se poursuivra sur les éléments suivants. C'est pourquoi avec notre script seul "politique d' assainissement" est présent dans les résultats d'extraction de la rubrique Europe. Une meilleure connaissance de l'emploi des expressions régulières nous aurait peut-être permi d'améliorer ce point...