Enrichissement Teeft dans LODEX

Publié lecalendar par François Parmentier

Dans LODEX, les enrichissements sont par défaut en mode simple, et n’ont que peu de paramètres.
Une manière d’accéder à plus de paramètres est de passer en mode avancé.
On se trouve alors face à un script modifiable.
Ce script pouvant être un peu abscons, le but de cet article est d’expliquer quelles parties de ce script sont intéressantes à modifier, et pourquoi faire.

Créer un enrichissement simple

Pour créer un enrichissement, il faut avoir chargé un corpus (via l’administration de LODEX).
Nous supposerons que ce corpus dispose d’une colonne abstract contenant un résumé en anglais.
Ensuite, il faut se rendre dans le menu Données / Enrichissements :

l'interface d'enrichissement simple de LODEX

Prenons comme exemple d’enrichissement simple l’extraction de termes d’un texte avec l’algorithme Teeft.
Nommons l’enrichissement que nous créons teeft, ce sera le nom de la colonne correspondante.
Le service web à utiliser est décrit sur la page Extraction de termes d’un texte via Teeft d’Objectif TDM.
On y voit que l’URL du service web à utiliser est https://terms-extraction.services.inist.fr/v1/teeft/fr?nb=10.
Copions-la dans le champ ad hoc.

Ensuite, sélectionnons la colonne abstract puisque c’est celle que dont nous voulons extraire les termes pertinents.

formaulaire d'enrichissement simple rempli

Il reste à cliquer sur Ajouter pour créer l’enrichissement, et permettre d’accéder au script correspondant à ces paramètres.

Une fois l’enrichissement ajouté, deux nouveaux boutons apparaissent: Lancer et Supprimer.

Visualiser le script correspondant

En actionnant le bouton Mode avancé, le formulaire est remplacé par un éditeur présentant le script généré à partir de ses paramètres.

enrichissement en mode avancé

Dans notre cas, il contient :

[use]
plugin = basics
plugin = analytics

[validate]
path = id
rule = required

path = value
rule = required

# We sometimes have a stringified value (like "{id:1, name: 'Bob'}"), and we want to parse it, if possible
# This assign command's aim is to do that, it updates the abstract to JSON.parse it if possible.
# Can be simplified with the following statement : value = get('value.abstract')
[assign]
path = value
value = update("value.abstract", (item) => { try { return JSON.parse(item); } catch { return item; } }).get("value.abstract")

[expand]
size = 10
path=value

# remove the two lines below to speed up enrichment
[expand/throttle]
bySecond = 5

[expand/URLConnect]
url = https://terms-extraction.services.inist.fr/v1/teeft/fr?nb=10
timeout = 5000
noerror = false
retries = 5
Script complet généré automatiquement

 

Analyse du script

Le script affiché est écrit dans un langage de traitement de flux de données appelé ezs.

Démystifions chaque partie de ce script (dont la syntaxe est la même que les fichiers .ini qui servent de fichiers de configuration à certains programmes).
D’habitude, les crochets encadrent le nom d’une partie de configuration.
Ici, ils marquent la présence d’une instruction ezs.
Détaillons chacune de ces instructions.

use

[use]
plugin = basics
plugin = analytics

L’instruction use déclare les plugins (ou modules) du langage ezs que le script va utiliser.
Les instructions sont réparties soit dans le cœur d’ezs (nommé @ezs/core), soit dans un de ses modules.
Ici par exemple, l’instruction URLConnect appartient au module @ezs/basics.

À moins que nous ajoutions des instructions non encore présentes dans le script, nous pouvons donc ignorer cette partie.

validate

[validate]
path = id
rule = required

path = value
rule = required

L’instruction validate s’assure simplement que la structure des objets à traiter est la bonne.
Elle attend un objet JavaScript (JSON en anglais) avec au moins un champ id et un champ value.

{
    "id": "...",
    "value": "..."
}
Structure JSON valide

Ici encore, c’est LODEX qui s’assure d’envoyer des objets qui respectent cette structure.
Dans notre cas, le champ id contiendra un numéro d’ordre et le champ value tout le document en train de passer dans le flux (c’est-à-dire un objet contenant toutes les colonnes chargées).
Si d’aventure LODEX venait à ne pas respecter cette structure, une erreur s’afficherait lors du lancement de l’enrichissement.
Mais c’est très peu probable.
On peut donc aussi ignorer cette instruction.

assign

L’instruction assign ajoute un champ, ou bien modifie simplement un champ déjà existant de l’objet du flux qu’elle est en train de traiter.

# We sometimes have a stringified value (like "{id:1, name: 'Bob'}"), and we want to parse it, if possible
# This assign command's aim is to do that, it updates the abstract to JSON.parse it if possible.
# Can be simplified with the following statement : value = get('value.abstract')
[assign]
path = value
value = update("value.abstract", (item) => { try { return JSON.parse(item); } catch { return item; } }).get("value.abstract")

Ici, le commentaire explique pourquoi la ligne avec le paramètre value est si longue, et que dans les cas les plus courants, on peut se contenter d’écrire cette instruction comme suit :

[assign]
path = value
value = get("value.abstract")

Cette instruction va modifier le champ value (donc l’objet contenant toutes les colonnes) pour lui affecter uniquement sa colonne abstract.

On passera donc d’un objet du genre :

{
    "id": "001",
    "value": {
        ...
        "abstract": "Le résumé en anglais",
        ...
    }
}
Objet fourni par LODEX

à un objet ressemblant à :

{
    "id": "001",
    "value": "Le résumé en anglais"
}
Objet transformé par l'instruction assign

ce qui, si vous y avez prêté attention, est le format d’entrée nécessaire au service web teeft.

Là aussi, LODEX a tout préparé pour nous, et en principe nous n’aurons rien à y toucher.
Évidemment, quand vous maîtriserez ezs, vous pourrez vous livrer à quelques facéties, comme concaténer le contenu de deux colonnes, ou bien appauvrir un texte (en lui enlevant ses caractères accentués, ou en le passant en minuscules, voire les deux).
Ne touchons donc pas à cette instruction.

expand

[expand]
size = 10
path=value

# remove the two lines below to speed up enrichment
[expand/throttle]
bySecond = 5

[expand/URLConnect]
url = https://terms-extraction.services.inist.fr/v1/teeft/fr?nb=10
timeout = 5000
noerror = false
retries = 5

L’instruction expand est spéciale: elle fabrique un tableau de 10 éléments, tous les dix objets qu’elle reçoit, et exécute une sorte de sous-script.

Ici, le sous-script contient les instructions throttle et URLConnect.

Si nous négligeons throttle et considérons que URLConnect (dont nous verrons les paramètres plus bas) sert à appeler le service web, nous voyons que cette partie du script sert à envoyer plusieurs abstracts d’un coup au web service, réduisant ainsi le nombre de fois auquel on y fait appel.
Sachant que la rapidité du script dépend beaucoup du nombre d’appels au service web, il peut être salutaire de réduire ce nombre d’appels.

Cela peut se faire simplement en augmentant le paramètre size de expand :

[expand]
size = 100
path = value

En le mettant à 100, nous faisons des paquets de 100 abstracts à envoyer au service web (divisant par 10 le nombre de requêtes).
Cependant, restons conscients que suivant la lourdeur du traitement à effectuer, augmenter size ne permettra pas forcément au service web d’être plus rapide.
En particulier, passer sa valeur à plus que 100 n’aura que rarement un effet positif.
Mais c’est un des paramètres qu’on peut se permettre de faire varier si l’enrichissement est trop long.

throttle

# remove the two lines below to speed up enrichment
[expand/throttle]
bySecond = 5

throttle signifie littéralement « mettre au ralenti ».
Cette instruction sert à éviter l’engorgement du serveur du service web qu’on interroge.
En effet, il limite le nombre de requêtes à faire à 5 par seconde (ce qui, si on envoie 10 abstracts par requête, représente quand même 50 abstracts dont on extrait des termes par seconde).
Si on veut profiter de toute la puissance du serveur, on peut supprimer cette instruction.
Mais il faut rester conscient que cela signifie pratiquement s’accaparer les capacités du serveur.
Ça peut être acceptable si l’enrichissmenent est court, mais peut gêner d’autres utilisateurs s’il est long (il y a plus de chances que des enrichissements venant d’autres utilisateurs soient demandés pendant un enrichissement qui dure plus longtemps).

Pour être moins extrême, on peut aussi augmenter la limite de requêtes par seconde, en modifiant le paramètre bySecond, dans le but d’aller plus rapidement.
Gardons en tête que ce n’est pas le seul paramètre susceptible d’accélérer les traitements.
On lui préférera le paramètre size de l’instruction expand.

URLConnect

[expand/URLConnect]
url = https://terms-extraction.services.inist.fr/v1/teeft/fr?nb=10
timeout = 5000
noerror = false
retries = 5

Le premier paramètre de URLConnect est évidemment l’url à utiliser.
Il y a ici un argument qu’on peut faire varier: nb. Il n’affecte pas les performances du service, mais seulement le nombre de termes à renvoyer.

Rappel: on ne peut pas vérifier que cette URL est la bonne simplement en la collant dans le navigateur. Techniquement, c’est la méthode GET qui serait utilisée, alors que le service attend exclusivement une méthode POST associée à l’URL.

retries représente le nombre d’essais que LODEX fera si l’appel au service échoue avant d’abandonner.
Mieux vaut ne pas l’augmenter, car il ne sert déjà que dans des cas extrêmes.
D’ailleurs, il peut expliquer des temps d’interrogation longs (au cas où l’URL ne serait pas bonne, par exemple).

De même, modifier noerror serait contre-productif: la seule autre valeur possible est true, et elle signifie qu’aucune erreur n’est signalée.

timeout en revanche, peut s’avérer utile.
Il arrive que certains services web nécessitant des calculs relativement lourds mettent plus que 5 secondes à répondre.
C’est un des cas où aucune erreur n’arrive à LODEX, car il ignorera toute réponse arrivant après le délai imparti.
C’est timeout qui fixe ce délai, dans ce script à 5000 millisecondes.
L’onglet données affichant les colonnes enrichies affichera undefined lorsque le timeout est dépassé.
C’est alors un fort indice qu’il faut augmenter la valeur de timeout.
Il pourrait arriver qu’un service prenne plus d’une minute pour un calcul (dépendant de la complexité du calcul, de la charge du serveur, …), n’hésitez donc pas à doubler la valeur de timeout quand vous obtenez des valeurs undefined dans la colonne enrichie correspondant au nom de l’enrichissement.

Lancement du script

Avant de lancer l’opération d’enrichissement, il ne faut pas oublier de cliquer sur SAUVEGARDER pour prendre en compte les modifications éventuelles du script.

Interface LODEX d'enrichissement avancé avant le lancementCliquer sur LANCER exécute le script, sur toutes les données chargées.

Enrichissement en coursOn peut ensuite aller voir le résultat en cliquant sur Données.

Affichage des données en colonnesEn cliquant sur colonnes chargées, on cache les colonnes correspondant aux données chargées initialement (sauf la colonne uri, qui est l’identifiant de chaque ligne).
On accède ainsi plus facilement aux colonnes enrichies, qui sont à droite des colonnes chargées.

Affichage de la colonne enrichieC’est à cet endroit qu’on peut repérer les valeurs undefined, pour savoir s’il faut augmenter le timeout de URLConnect, et relancer l’enrichissement (sans oublier de sauvegarder avant).

Remarque: il peut arriver que le service renvoie une réponse vide, c’est différent d’une valeur undefined, où aucune valeur n’a été retournée.
Ci-dessous, on voit des documents sans abstract, et dont le service n’a logiquement pas pu extraire de termes.

Sans abstract, pas de termes extraits par teeftConclusion

Nous venons de voir l’utilisation d’un service web dont l’usage est simple (teeft, qui traite une chaîne de caractères).
Pour des besoins nécessitant un service web prenant en entrée une structure JSON plus détaillée, voir l’article Enrichissement RNSR dans LODEX.