• Aucun résultat trouvé

dictionnaires mot-mot couvre les cas de lemmatisation (mange→ manger) et, à condition de

consti-tuer les listes, les cas d’erreurs d’orthographe les plus courants (ethymologie→ etymologie) ou de

simple variantes d’écriture correctes (tsar→ tzar). L’application asymétrique de dictionnaires

1-vers-n permet de traiter les cas de sy1-vers-no1-vers-nymie (absti1-vers-ne1-vers-nce→ privation, ascétisme, jeûne, diète) sans aller

“trop loin” dans les liens. Par exemple abdiquer à pour synonyme (entre autres) renoncer, de même que s’abstenir. Cependant considérer abdiquer et s’abstenir en relation de synonymie, ce qu’une application symmétrique impliquerait, serait incorrect. Les relations d’hypéronymie et d’hyponymie

(antidépresseur ↔ médicament) peuvent être utilisées de la même façon. Le test d’inclusion, enfin,

vérifie si la valeur venant de la question est incluse dans celle venant du document ou vice-versa. Il est adapté aux comparaisons de noms de personnes et permet de retrouver Barak Obama quand l’on n’a que Obama ou l’inverse.

Définir des chaînes de transformations avec des dictionnaires bien choisis permet ainsi de retrouver de nombreuses variantes des élémentes de la question et d’y associer des indices de confiance a priori.

8.4 Sélection et classement des documents

Une fois la question analysée, l’étape suivante est la Recherche d’Informations, dont le but fondamen-tal est d’extraire de la base de documents des sous-parties pertinentes pour répondre à la question. Une grande partie de la performance finale du système est déterminée à ce niveau. Trop peu de sous-parties résultera en un système probablement rapide mais qui aura rarement les informations nécessaires pour répondre. Trop ralentira le système qui aura cependant plus de chances de répondre. Obtenir trop de texte, en plus d’un effet délétère sur la vitesse, noiera le système dans les candidats réponse possibles, diminuant sa capacité à extraire les réponses correctes. C’est donc, comme bien souvent, une question d’équilibre entre précision et rappel de l’extraction.

Deux options sont possibles : utiliser un système de recherche d’informations existant et disponible, tel que Lucene [Apache 2007], ou en créer un spécialisé. Utiliser un système existant impose de suivre ses contraintes, en particulier des recherches sur des mots simples et des formes de transformation en général limités à une lemmatisation. En conséquence, pour pouvoir utiliser nos Descripteurs De Recherche comme clé primaire de sélection, nous avons préféré développer nos propres approches. Traditionnellement, les systèmes de recherche d’informations retournent une liste de documents. Mais la définition de document est très variable. Il peut s’agir d’un des fichiers de la collection, ou d’une sous-partie d’un fichier pré-découpé au moment de l’indexation. Prendre une sous-partie permet d’avoir moins de données à traiter et potentiellement une meilleure précision. Cependant un découpage a priori n’est pas aussi précis qu’un découpage fait en fonction du contenu de la question. De plus, dans l’état actuel des performances des disques durs, le temps de lecture d’un fichier est dominé par le temps de latence : lire un fichier complet, s’il est de taille raisonnable, ne prend pas un temps significativement plus grand qu’en lire une partie. Nous avons donc décidé de procéder en

deux temps : une sélection de documents suivie par une sélection de passages pertinents.

Calcul du compte net pour un nœud function calcule_compte_net(noeud, cc)

Trie les lignes par ordre inverse de poids sort(noeud.lignes[], poids, <)

for l in noeud.lignes[] do

l.compte_net = max(0, l.compte_brut - cc) cc = cc + l.compte_net

end

Propage les comptes sur les dérivations for n in noeud.derivations[] do

calcule_compte_net(n, cc) end

end

Calcul du compte net pour les nœuds du haut for n in ddr.noeud_haut[] do

calcule_compte_net(n, 0) end

FIG. 8.8 – Algorithme de calcul des comptes nets pour les lignes du DDR à partir des comptes bruts

et de la structure arborescente.

La question à se poser est donc comment choisir les documents les plus pertinents de la collection. L’approche que nous avons choisie est de définir un score calculé par document en fonction du DDR, et ensuite de choisir les documents avec les meilleurs scores. Une idée simple pour construire un tel score est de suivre la structure arborescente du DDR. On part des comptes d’occurrences des différents éléments du DDR. Le score d’un nœud individuel est la somme des nombres d’occurrences des lignes individuelles pondérées par leurs poids plus le score de ses dérivations. Le score d’une conjonction d’éléments, ou en d’autre termes une combinaison de nœuds du même niveau, se fait par une moyenne géométrique des scores individuels. L’avantage de la moyenne géométrique est de se rendre indépendant des fréquences moyennes différentes des divers éléments. Cependant, l’analyse produit des annotations hiérarchiques et imbriquées. Il en résulte que dans certains cas, la présence d’une variante d’une entrée du DDR implique la présence des autres variantes. Par exemple, si on a (lieu, France) alors on aura (pays, France) puisque pays est une sous-catégorie de lieu. Il semble préférable d’éviter de compter plusieurs fois le même élément. Donc la première étape consiste à calculer un compte net qui prend en compte ces répétitions en suivant l’algorithme figure 8.8. Une fois les comptes nets obtenus, chaque nœud reçoit comme score associé à ses lignes la somme de ses comptes nets pondérés par les poids. De plus les nœuds secondaires ont 1 ajouté à leur score.

8.4. SÉLECTION ET CLASSEMENT DES DOCUMENTS 103 Calcul du score pour les lignes d’un nœud

function calcul_score_lignes(noeud) score = 0

for l in noeud.lignes[] do

score = score + l.poids * l.compte_net end

return score end

Calcul du score pour un ensemble de nœuds de même niveau function calcul_score_groupe(noeuds[])

score = 1

for n in noeuds[] do

score = score * calcul_score_noeud(n) end

Moyenne géométrique

score = pow(score, 1/noeuds.size()) return score

end

Calcul du score complet d’un nœud function calcul_score_noeud(noeud)

score = calcul_score_lignes(noeud)

if(noeud.derivations) score = score + calcul_score_groupe(noeud.derivations) if(noeud.secondaire) score = score + 1

return score end

Calcul du score d’un document

doc.score = calcul_score_groupe(ddr.noeud_haut)

FIG. 8.9 – Calcul d’un score de document à partir des comptes nets

L’étape finale remonte les scores jusqu’à la racine :

– Chaque nœud sans descendant a pour score total son score de lignes.

– Chaque nœud avec descendant a pour score total la somme de son score de lignes et de la moyenne géométrique des scores de ses descendants directs.

Le score final du document est le score d’un nœud racine virtuel ayant comme dérivation tous les nœuds de haut niveau. L’algorithme complet est détaillé figure 8.9. Un exemple de calcul complet est donné figure 8.10.

Ligne du DDR Comptes Score Score nœuds

Brut Net Lignes Lignes Dériv. Total

1,000 identité pers Adolf Hitler 5 5 5,000 5,000 5,000

0,700 lemme_simple pers Adolf Hitler 5 0 0,000

0,050 identité np Adolf Hitler 0 0 0,000

0,015 expansion np Adolf Hitler 0 0 0,000

1,000 identité time 30 janvier 1933 2 2 2,000 3,800 3,557 7,357

1,000 identité date_compl. 30 janvier 1933 2 0 0,000

0,700 lemme time 30 janvier 1933 2 0 0,000

0,700 lemme date_compl. 30 janvier 1933 2 0 0,000

0,600 expansion time 30 janvier 1933 5 3 1,800

0,420 exp._lemme time 30 janvier 1933 5 0 0,000

1,0 identité jour 30 6 1 1,000 1,000 1,000

0,7 lemme jour 30 6 0 0,000

1,0 identité mois janvier 8 3 3,000 3,000 3,000

0,7 lemme mois janvier 8 0 0,000

1,0 identité annee 1933 20 15 15,000 15,000 15,000

0,7 lemme annee 1933 20 0 0,000

1,000 identité action devenu 0 0 0,000 9,800 10,800

1,000 verbe_subs subs devenu 0 0 0,000

0,700 lemme action devenu 14 14 9,800

Score final 7,351

FIG. 8.10 – Calcul complet d’un score de document

Lesn documents avec le meilleur score sont ainsi sélectionnés. Ce nombre est le premier facteur de

contrôle de la vitesse du système. Mais calculer ce score demande à pouvoir efficacement trouver pour chaque ligne, et donc triplet (type, valeur, transformation), du DDR le compte d’occurrences associé dans chaque document. C’est le rôle de l’indexation.