• Aucun résultat trouvé

3.2 Difficultés liées au moteur de règles

La représentation décidée, les transformations statistiques et algorithmiques que nous avons présen-tées ne posent pas de difficultés d’implémentation particulières. Le moteur de règles possède des par-ticularités intéressantes à étudier au niveau du matching. La performance globale du système dépend fortement de sa capacité à trouver rapidement quelles expressions régulières étendues sont applicables à un endroit spécifique dans la représentation. La méthode traditionnelle pour prendre en compte des expressions régulières est de les transformer en automates finis déterministes, assurant ainsi un temps de recherche linéaire en fonction de la taille du document. Cependant les extensions rendent cet exer-cice particulièrement difficile : la présence de bornes sur les nombres de répétitions peut déclencher des explosions combinatoires sur le nombre d’états dans l’automate. Les automates ne sont pas ca-pables de tenir compte des répétitions shy ou greedy. Ils ne peuvent pas non plus indiquer quelles zones sont couvertes par les parenthèses de substitution. Et enfin les lookaheads négatifs ou arrières sont totalement en dehors de leurs capacités. Cette transformation n’est donc pas utilisable en l’état.

3.2.1 Matching d’expression régulières par interprétation de patterns

Nous avons pris l’option de considérer les expressions régulières comme étant un langage de patterns pour lequel nous écrivons un interpréteur récursif. Pour chaque type de nœud que l’on peut trouver dans l’arbre syntaxique d’une de nos expressions, par exemple matching de mot simple, matching de classe, concaténation, alternative, répétition, lookahead, il est possible d’associer deux opérateurs match et next. match doit, étant donné une position dans la représentation, indiquer si un premier matching est possible à cet endroit et la zone correspondante. next indique ensuite à chaque appel si un matching supplémentaire est possible. Ces deux opérateurs peuvent être écrits en fonction de contenu de la représentation pour les nœuds feuilles et en fonction de ces mêmes opérateurs sur le ou les descendants pour les autres, d’où la récursivité. En cas d’échec un backtrace se produit où les appels récursifs sont remontés jusqu’à ce qu’un matching alternatif soit trouvé ou que le matching global soit abandonné. Un intérêt supplémentaire de cette approche est que les opérateurs sont re-tournables, permettant de faire un matching de droite à gauche dans le document en partant de la fin de l’expression régulière, ce qui rend immédiate l’implémentation des lookaheads arrières mais aussi des recherches récursives partant de la droite.

Une telle approche est bien évidemment plus lente qu’un automate. Elle peut cependant devenir très performante si l’on arrive à limiter deux facteurs : d’une part la quantité de travail par nœud (temps passé par instruction), et d’autre part le nombre d’essais se révélant a posteriori inutiles (nombre d’instructions exécutées).

3.2.2 Limitation de la quantité de travail par nœud et gestion de la mémoire

Limiter la quantité de travail par nœud est un problème très lié à l’implémentation. La transformation des mots et catégories en nombres aide déjà, permettant d’effectuer les matchings simples de mots par comparaison d’entiers et les matchings de classe par lookup dans un vecteur de bits de la taille du vocabulaire. Une difficulté reste cependant : la gestion de la mémoire. En effet l’opérateur next néces-site d’avoir assez d’informations stockées pour pouvoir calculer le matching suivant. Or la solution évidente, stocker ces informations dans le nœud, n’est pas aussi simple qu’il y paraît. En présence de macros, plusieurs matchings peuvent être actifs pour le même nœud en même temps. Dupliquer les nœuds, ou en d’autres termes instancier les macros, donne lieu à une explosion combinatoire. Stocker plusieurs informations par nœud pose ses propres difficultés. Il est en effet possible de les organi-ser en pile, la recherche par backoff assurant qu’un matching organi-sera terminé avant qu’un précédent soit continué. Cela garantit qu’un appel next agit sur l’état situé en haut de la pile. Cependant vider correc-tement les piles après un matching réussi complet d’une expression est complexe et coûteux. En effet il n’est pas possible de simplement supprimer le contenu des piles. Les lookaheads et les descentes dans les arbres sont en pratique des matchings complets de sous-expressions où seul le premier nous intéresse et qui donc nécessitent une passe de vidage après usage. Mais ces sous-expressions peuvent, via là encore les macros, avoir des nœuds communs avec l’expression principale qui est elle non-terminée. Il ne faut donc enlever des piles que les parties du haut correspondant à la sous-expression. Cela se révèle en pratique assez coûteux.

Cependant une approche alternative permet de régler ce problème. La clé est de noter qu’à l’exception des répétitions sans borne supérieure, chaque type de nœud a besoin d’une place de taille fixe pour son stockage, taille calculable au moment de la lecture des règles, à laquelle il faut ajouter la place nécessaire pour ses descendants. Même le cas des répétitions illimitées peut rentrer dans ce cadre en ayant comme information de taille fixe un pointeur vers une zone mémoire allouée dynamiquement au besoin. Chaque nœud possède ainsi une zone mémoire avec laquelle travailler. Il passe à ses des-cendants leur propre zone qui est une sous-partie de la sienne. Cette structuration en zones mémoire incluses les unes dans les autres récursivement sépare naturellement les multiples matchings pouvant s’appliquer sur le même nœud via les macros. Il n’y a alors plus besoin d’action spécifique après un matching complet réussi. Nous avons constaté que même avec nos règles les plus complexes cette zone mémoire ne fait pas plus que quelques kilo-octets, rendant cette solution très performante et l’utilisation mémoire du système après initialisation minimale.

3.2.3 Limitation du nombre de tests inutiles

Limiter le nombre d’essais inutiles se fait d’abord au niveau des règles elles-mêmes, et donc de ce qu’a écrit l’expert linguiste. En effet, comme dans tout langage de programmation, des modifications subtiles peuvent avoir de gros effets sur le nombre de tests à effectuer. Il est cependant possible de l’aider avec un outil de profiling lui indiquant quelles règles prennent le plus de temps, et même dans ces règles quelles macros sont les plus coûteuses.

3.2. DIFFICULTÉS LIÉES AU MOTEUR DE RÈGLES 55 Le moteur lui-même peut aussi agir sur deux points pour limiter le nombre de tentatives de mat-ching inutiles. Le premier point est un filtrage des règles. Il est possible d’obtenir un sur-ensemble de l’ensemble des mots ou des catégories pouvant apparaître en première position dans toutes les suites de mots acceptées par une règle donnée. L’estimation de l’ensemble ne peut être exacte à cause de l’existence des lookaheads arrières. Ces sur-ensembles calculés permettent alors de construire une table permettant de savoir immédiatement pour un emplacement donné dans un document quelles règles peuvent potentiellement s’appliquer.

Le second point concerne les alternatives. Une part significative de leur utilisation concerne la recon-naissance de listes d’expressions multi-mots, comme des noms de villes, de départements, de pays, pouvant avoir des centaines de milliers d’entrées. Essayer les alternatives une par une est dans ce genre de cas très inefficace. Il est donc intéressant d’utiliser une approche de filtrage similaire à celle utilisée sur les règles. Filtrer uniquement sur le premier mot possible s’est cependant révélé insuffi-sant, et nous filtrons actuellement sur un arbre de préfixes possibles. Nous pensons généraliser cela en une version limitée de la transformation en automates. Appliquer un arbre de préfixes ou un auto-mate a un coût similaire, mais la meilleure couverture de variantes d’un autoauto-mate permet d’obtenir un meilleur résultat au niveau du filtrage. N’utiliser les automates que pour un tel filtrage local, qui n’a pas besoin d’être parfait, permet d’éviter les difficultés dues aux extensions proposées. L’interpré-tation des patterns est toujours effectuée et permet de prendre en compte les lookaheads, répétitions limitées et autres problèmes. Les automates permettent de filtrer efficacement les cas où les expres-sions multi-mots ont un fort taux de mots optionnels ou de variantes possibles dont la combinatoire est explosive pour un filtrage par préfixes.

Chapitre 4

Évaluation

Évaluer un moteur d’analyse tel que celui que nous proposons n’est pas une tâche aisée. En effet on ne peut, comme pour un système d’analyse, annoter un corpus de test avec les informations que l’on veut obtenir et mesurer la qualité de la sortie. Un moteur d’analyse, et en particulier un mettant en avant l’écriture de règles, est similaire à un langage de programmation : le résultat est en grande partie dépendant de l’ensemble de règles.

Se pose donc la question de sur quels critères pouvons-nous évaluer un moteur ?. L’analogie avec un langage de programmation est utile : un moteur d’analyse est performant si, dans le cadre où il a été conçu, il aide effectivement l’expert linguiste en charge de l’analyse (le programmeur) à obtenir les résultats recherchés. Une grande partie de l’évaluation prend donc la forme de cas d’usage. Nous en présentons trois, qui donneront une idée de ce que le moteur nous a permis de construire :

– L’analyse pour Ritel, un système interactif multimodal de recherche d’informations en domaine ouvert en français.

– L’adaptation de la sous-partie spécifique Question-Réponse de l’analyse de Ritel à l’espagnol et à l’anglais.

– L’exploration de corpus.

Cependant, même pour un langage de programmation, mesurer la vitesse d’exécution des programmes écrits est une information pertinente. Nous présentons donc un certain nombre de mesures que nous avons effectuées sur l’analyseur développé pour Ritel, qui est le plus complet que nous ayons déve-loppé.

4.1 Ritel : un système interactif de recherche d’informations en