Logo Zéphyrnet

Du texte à la connaissance: le pipeline d'extraction d'informations

Date :

extraction d'informations

Je suis ravi de présenter mon dernier projet sur lequel j'ai travaillé. Si vous avez suivi mes articles, vous savez que je suis passionné par la combinaison du traitement du langage naturel et des graphiques de connaissances. Dans cet article de blog, je présenterai ma mise en œuvre d'un pipeline de données d'extraction d'informations. Plus tard, j'expliquerai également pourquoi je vois la combinaison de la PNL et des graphiques comme l'un des chemins vers l'IA explicable.

Si ce contenu éducatif approfondi vous est utile, vous pouvez abonnez-vous à notre liste de diffusion de recherche sur l'IA d'être alerté lorsque nous publierons du nouveau matériel. 

Pipeline d'extraction d'informations

Qu'est-ce qu'un pipeline d'extraction d'informations? Pour le dire en termes simples, l'extraction d'informations consiste à extraire des informations structurées à partir de données non structurées telles que du texte.

Étapes de ma mise en œuvre du pipeline IE. Image par auteur

Ma mise en œuvre du pipeline d'extraction d'informations se compose de quatre parties. Dans la première étape, nous exécutons le texte d'entrée via un modèle de résolution de coréférence. La résolution de coréférence est la tâche de trouver toutes les expressions qui font référence à une entité spécifique. Pour faire simple, il relie tous les pronoms à l'entité référencée. Une fois cette étape terminée, il divise le texte en phrases et supprime les ponctuations. J'ai remarqué que le modèle ML spécifique utilisé pour la liaison d'entités nommées fonctionne mieux lorsque nous supprimons les ponctuations pour la première fois. Dans l'entité nommée reliant la partie du pipeline, nous essayons d'extraire toutes les entités mentionnées et de les connecter à une base de connaissances cible. La base de connaissances cible, dans ce cas, est Wikipedia. La liaison d'entités nommées est bénéfique car elle traite également la désambiguïsation des entités, ce qui peut être un gros problème.

Une fois que nous avons extrait les entités mentionnées, le pipeline IE tente de déduire des relations entre les entités qui ont un sens en fonction du contexte du texte. Les résultats du pipeline IE sont des entités et leurs relations, il est donc logique d'utiliser une base de données de graphes pour stocker la sortie. Je vais montrer comment enregistrer les informations IE dans Néo4j.

J'utiliserai l'extrait suivant de Wikipedia pour vous guider à travers le pipeline IE.

Elon Musk est un magnat des affaires, un designer industriel et un ingénieur. Il est le fondateur, PDG, CTO et concepteur en chef de SpaceX. Il est également l'un des premiers investisseurs, PDG et architecte produit de Tesla, Inc. Il est également le fondateur de The Boring Company et le co-fondateur de Neuralink. Centibillionnaire, Musk est devenu la personne la plus riche du monde en janvier 2021, avec une valeur nette estimée à 185 milliards de dollars à l'époque, dépassant Jeff Bezos. Musk est né d'une mère canadienne et d'un père sud-africain et a grandi à Pretoria, en Afrique du Sud. Il a brièvement fréquenté l'Université de Pretoria avant de déménager au Canada à l'âge de 17 ans pour étudier à l'Université Queen's. Il a été transféré à l'Université de Pennsylvanie deux ans plus tard, où il a obtenu une double licence en économie et en physique. Il a déménagé en Californie en 1995 pour fréquenter l'Université de Stanford, mais a plutôt décidé de poursuivre une carrière dans les affaires. Il a continué à co-fonder une société de logiciels Web Zip2 avec son frère Kimbal Musk.

Le texte est copié depuis https://en.wikipedia.org/wiki/Elon_Musk et est disponible sous Licence CC BY-SA 3.0.

Étape 1: résolution de coréférence

Comme mentionné, la résolution de coréférence essaie de trouver toutes les expressions dans le texte qui font référence à une entité spécifique. Dans ma mise en œuvre, j'ai utilisé le Modèle Neuralcoref de Huggingface qui fonctionne au-dessus de la SpaCy cadre. J'ai utilisé les paramètres par défaut du modèle Neuralcoref. Une chose que j'ai remarquée en cours de route est que le modèle Neuralcoref ne fonctionne pas bien avec les pronoms de localisation. J'ai également emprunté un petit code d'amélioration à l'un des Problèmes GitHub. Le code de la partie de résolution de coréférence est le suivant:

import spacy
import neuralcoref # Load SpaCy
nlp = spacy.load('en')
# Add neural coref to SpaCy's pipe
neuralcoref.add_to_pipe(nlp) def coref_resolution(text): """Function that executes coreference resolution on a given text""" doc = nlp(text) # fetches tokens with whitespaces from spacy document tok_list = list(token.text_with_ws for token in doc) for cluster in doc._.coref_clusters: # get tokens from representative cluster name cluster_main_words = set(cluster.main.text.split(' ')) for coref in cluster: if coref != cluster.main: # if coreference element is not the representative element of that cluster if coref.text != cluster.main.text and bool(set(coref.text.split(' ')).intersection(cluster_main_words)) == False: # if coreference element text and representative element text are not equal and none of the coreference element words are in representative element. This was done to handle nested coreference scenarios tok_list[coref.start] = cluster.main.text + doc[coref.end-1].whitespace_ for i in range(coref.start+1, coref.end): tok_list[i] = "" return "".join(tok_list)

Si nous exécutons notre exemple de texte via la fonction coref_resolution, nous obtiendrons la sortie suivante:

Elon Musk est un magnat des affaires, un designer industriel et un ingénieur. 
Elon Musk est le fondateur, PDG, CTO et concepteur en chef de SpaceX.
Elon Musk est également l'un des premiers investisseurs, PDG et architecte produit de Tesla, Inc. Elon Musk est également le fondateur de The Boring Company et le co-fondateur de Neuralink. Centibillionnaire, Musk est devenu la personne la plus riche du monde en janvier 2021, avec une valeur nette estimée à 185 milliards de dollars à l'époque, dépassant Jeff Bezos. Musk est né d'une mère canadienne et d'un père sud-africain et a grandi à Pretoria, en Afrique du Sud. Elon Musk a brièvement fréquenté l'Université de Pretoria avant de déménager au Canada à l'âge de 17 ans pour étudier à l'Université Queen's. Elon Musk a été transféré à l'Université de Pennsylvanie deux ans plus tard, où Elon Musk a obtenu une double licence en économie et physique. Elon Musk a déménagé en Californie en 1995 pour fréquenter l'Université de Stanford, mais a plutôt décidé de poursuivre une carrière dans les affaires. Elon Musk a continué à co-fonder une société de logiciels Web Zip2 avec le frère d'Elon Musk Kimbal Musk.

Dans cet exemple, aucune technique avancée de résolution de coréférence n'est requise. Le modèle Neuralcoref a changé quelques pronoms «He» en «Elon Musk». Bien que cela puisse sembler très simple, il s'agit d'une étape importante qui augmentera l'efficacité globale de notre pipeline IE.

Étape 2: Liaison d'entités nommées

Tout récemment, j'ai publié un article de blog utilisant Liaison d'entités nommées pour construire un graphe de connaissances. Ici, je voulais utiliser un modèle de liaison d'entités nommées différent. J'ai d'abord essayé d'utiliser le Modèle Facebook BLINK, mais j'ai rapidement réalisé que cela ne fonctionnerait pas sur mon ordinateur portable. Il a besoin d'au moins 50 Go d'espace libre, ce qui n'est pas un gros problème en soi, mais il nécessite également 32 Go de RAM. Mon ordinateur portable ne dispose que de 16 Go de RAM, et nous avons encore besoin d'autres parties du pipeline pour fonctionner. Alors je suis revenu pour utiliser le bon vieux API Wikifier, qui s’est déjà avérée utile. Et c'est totalement gratuit. Si vous souhaitez trouver plus d'informations sur l'API, consultez mon précédent article de blog ou la documentation officielle.

Avant d'exécuter notre texte d'entrée via l'API Wikifier, nous allons diviser le texte en phrases et supprimer les ponctuations. Dans l'ensemble, le code de cette étape est le suivant:

import urllib
from string import punctuation
import nltk ENTITY_TYPES = ["human", "person", "company", "enterprise", "business", "geographic region", "human settlement", "geographic entity", "territorial entity type", "organization"] def wikifier(text, lang="en", threshold=0.8): """Function that fetches entity linking results from wikifier.com API""" # Prepare the URL. data = urllib.parse.urlencode([ ("text", text), ("lang", lang), ("userKey", "tgbdmkpmkluegqfbawcwjywieevmza"), ("pageRankSqThreshold", "%g" % threshold), ("applyPageRankSqThreshold", "true"), ("nTopDfValuesToIgnore", "100"), ("nWordsToIgnoreFromList", "100"), ("wikiDataClasses", "true"), ("wikiDataClassIds", "false"), ("support", "true"), ("ranges", "false"), ("minLinkFrequency", "2"), ("includeCosines", "false"), ("maxMentionEntropy", "3") ]) url = "http://www.wikifier.org/annotate-article" # Call the Wikifier and read the response. req = urllib.request.Request(url, data=data.encode("utf8"), method="POST") with urllib.request.urlopen(req, timeout=60) as f: response = f.read() response = json.loads(response.decode("utf8")) # Output the annotations. results = list() for annotation in response["annotations"]: # Filter out desired entity classes if ('wikiDataClasses' in annotation) and (any([el['enLabel'] in ENTITY_TYPES for el in annotation['wikiDataClasses']])): # Specify entity label if any([el['enLabel'] in ["human", "person"] for el in annotation['wikiDataClasses']]): label = 'Person' elif any([el['enLabel'] in ["company", "enterprise", "business", "organization"] for el in annotation['wikiDataClasses']]): label = 'Organization' elif any([el['enLabel'] in ["geographic region", "human settlement", "geographic entity", "territorial entity type"] for el in annotation['wikiDataClasses']]): label = 'Location' else: label = None results.append({'title': annotation['title'], 'wikiId': annotation['wikiDataItemId'], 'label': label, 'characters': [(el['chFrom'], el['chTo']) for el in annotation['support']]}) return results

J'ai oublié de mentionner que l'API Wikifier renvoie toutes les classes auxquelles appartient une entité. Il regarde le EXEMPLE DE et SUBCLASS_OF classes et traverse toute la hiérarchie des classes. J'ai décidé de filtrer les entités avec des catégories qui appartiendraient à une personne, une organisation ou un lieu. Si nous exécutons notre exemple de texte via la partie Named Entity Linking du pipeline, nous obtiendrons la sortie suivante.

Une bonne chose à propos du processus de wikification est que nous obtenons également les identifiants WikiData correspondants pour les entités avec leurs titres. Avoir les identifiants WikiData prend en charge le problème de désambiguïsation des entités. Vous pourriez alors vous demander ce qui se passe si une entité n'existe pas sur Wikipédia. Dans ce cas, malheureusement, le Wikifier ne le reconnaîtra pas. Je ne m'inquiéterais pas trop à ce sujet, cependant, car Wikipedia compte plus de 100 millions d'entités si je me souviens bien.

Si vous regardez attentivement les résultats, vous remarquerez que Pretoria est classée à tort comme une organisation. J'ai essayé de résoudre ce problème, mais la hiérarchie des classes de Wikipédia est compliquée et s'étend généralement sur cinq ou six sauts. S'il y a des experts de la classe Wiki, je serai ravi d'écouter vos conseils.

Étape 3: extraction de la relation

J'ai déjà présenté tous les concepts jusqu'à ce point. Je n'ai jamais approfondi l'extraction de relations auparavant. Jusqu'à présent, nous n'avons joué qu'avec les réseaux de cooccurrence. Je suis donc ravi de présenter un processus d'extraction de relations de travail. Je passe beaucoup de temps à rechercher des modèles open source qui pourraient faire un travail décent. J'ai été ravi de tomber sur le OuvertNRE projet. Il propose cinq modèles d'extraction de relations open source qui ont été formés sur l'ensemble de données Wiki80 ou Tacred. Parce que je suis un grand fan de tout ce que Wiki, j'ai décidé d'utiliser le jeu de données Wiki80. Les modèles formés sur l'ensemble de données Wiki80 peuvent déduire 80 types de relations. Je n'ai pas essayé les modèles formés sur l'ensemble de données Tacred. Vous pourriez essayer cela par vous-même. Dans l'implémentation du pipeline IE, j'ai utilisé le wiki80_bert_softmax maquette. Comme son nom l'indique, il utilise l'encodeur BERT sous le capot. Une chose est sûre. Si vous n'avez pas de GPU, vous n'allez pas passer un bon moment.

Si nous examinons un exemple d'appel d'extraction de relation dans la bibliothèque OpenNRE, nous remarquerons qu'il déduit uniquement des relations et n'essaye pas d'extraire les entités nommées. Nous devons fournir à une paire d'entités le h et t paramètres, puis le modèle essaie de déduire une relation.

model.infer ({'text': 'Il était le fils de Máel Dúin mac Máele Fithrich, et petit-fils du grand roi Áed Uaridnach (mort en 612).', 'h': {'pos': (18, 46) }, 't': {'pos': (78, 91)}})
('père', 0.5108704566955566)

Les résultats fournissent un type de relation ainsi que le niveau de confiance de la prédiction. Mon code pas si parfait pour l'extraction de relations ressemble à ceci:

# First get all the entities in the sentence
entities = wikifier(sentence, threshold=entities_threshold)
# Iterate over every permutation pair of entities
for permutation in itertools.permutations(entities, 2): for source in permutation[0]['characters']: for target in permutation[1]['characters']: # Relationship extraction with OpenNRE data = relation_model.infer( {'text': sentence, 'h': {'pos': , source[1] + 1]}, 't': {'pos': [target[0], target[1] + 1]}}) if data[1] > relation_threshold: relations_list.append( {'source': permutation[0]['title'], 'target': permutation[1]['title'], 'type': data[0]})

Nous devons utiliser les résultats de la liaison de l'entité nommée comme entrée dans le processus d'extraction de relation. Nous itérons sur chaque permutation d'une paire d'entités et essayons de déduire une relation. Comme vous pouvez le voir par le code, nous avons également un paramètre relation_threshold pour omettre les relations avec un faible niveau de confiance. Vous verrez plus tard pourquoi nous utilisons des permutations et non des combinaisons d'entités.

Ainsi, si nous exécutons notre exemple de texte via le pipeline d'extraction de relations, les résultats sont les suivants:

L'extraction de relations est un problème difficile à résoudre, alors ne vous attendez pas à des résultats parfaits. Je dois dire que ce pipeline IE fonctionne aussi bien, sinon mieux que certaines des solutions commerciales existantes. Et évidemment, d'autres solutions commerciales sont bien meilleures.

Étape 4: Graphique des connaissances

Comme nous traitons des entités et de leurs relations, il est logique de stocker les résultats dans une base de données de graphes. j'ai utilisé Néo4j dans mon exemple.

Image par auteur

Rappelez-vous, j'ai dit que nous tenterions d'inférer une relation entre toutes les permutations de paires d'entités au lieu de combinaisons. En regardant les résultats de la table, il serait plus difficile de comprendre pourquoi. Dans une visualisation graphique, il est facile d'observer que si la plupart des relations sont déduites dans les deux sens, ce n'est pas vrai dans tous les cas. Par exemple, la relation de lieu de travail entre Elon Musk et l'Université de Pennsylvanie est supposée dans une seule direction. Cela nous amène à une autre lacune du modèle OpenNRE. La direction de la relation n'est pas aussi précise que nous le souhaiterions.

Un exemple pratique de pipeline IE

Pour ne pas vous laisser les mains vides, je vais vous montrer comment vous pouvez utiliser mon implémentation IE dans vos projets. Nous exécuterons le pipeline IE via le Ensemble de données BBC News trouvé sur Kaggle. La partie la plus difficile de l'implémentation du pipeline IE était de configurer toutes les dépendances. Je veux que vous conserviez votre santé mentale, j'ai donc créé une image de docker que vous pouvez utiliser. Exécutez la commande suivante pour le faire fonctionner:

docker run -p 5000: 5000 tomasonjo / trinityie

Lors de la première exécution, les modèles OpenNRE doivent être téléchargés, donc ne pas utiliser -rm option. Si vous souhaitez apporter des modifications au projet et créer votre propre version, j'ai également préparé un GitHub référentiel.

Comme nous allons stocker les résultats dans Neo4j, vous devrez également le télécharger et le configurer. Dans l'exemple ci-dessus, j'ai utilisé un schéma de graphique simple, où les nœuds représentent des entités et les relations représentent, eh bien, des relations. Nous allons maintenant refactoriser un peu notre schéma de graphe. Nous souhaitons stocker des entités et des relations dans le graphique, mais également enregistrer le texte d'origine. Avoir une piste d'audit est très utile dans les scénarios du monde réel car nous savons déjà que le pipeline IE n'est pas parfait.

Image par auteur

Il peut être un peu contre-intuitif de refactoriser une relation en un nœud intermédiaire. Le problème auquel nous sommes confrontés est que nous ne pouvons pas avoir de relation pointant vers une autre relation. Compte tenu de ce problème, j'ai décidé de refactoriser une relation en un nœud intermédiaire. J'aurais pu utiliser mon imagination pour produire de meilleurs types de relations et d'étiquettes de nœuds, mais c'est ce que c'est. Je voulais seulement que la direction relationnelle conserve sa fonction.

Le code pour importer 500 articles dans l'ensemble de données d'actualités de la BBC vers Neo4j est le suivant. Vous devrez exécuter le docker trinityIE pour que le pipeline IE fonctionne.

import json
import urllib
import pandas as pd
from neo4j import GraphDatabase driver = GraphDatabase.driver('bolt://localhost:7687', auth=('neo4j', 'letmein')) def ie_pipeline(text, relation_threshold=0.9, entities_threshold=0.8): # Prepare the URL. data = urllib.parse.urlencode([ ("text", text), ("relation_threshold", relation_threshold), ("entities_threshold", entities_threshold)]) url = "http://localhost:5000?" + data req = urllib.request.Request(url, data=data.encode("utf8"), method="GET") with urllib.request.urlopen(req, timeout=150) as f: response = f.read() response = json.loads(response.decode("utf8")) # Output the annotations. return response import_refactored_query = """
UNWIND $params as value
CREATE (a:Article{content:value.content})
FOREACH (rel in value.ie.relations | MERGE (s:Entity{name:rel.source}) MERGE (t:Entity{name:rel.target}) MERGE (s)-[:RELATION]->(r:Relation{type:rel.type})-[:RELATION]->(t) MERGE (a)-[:MENTIONS_REL]->(r))
WITH value, a
UNWIND value.ie.entities as entity
MERGE (e:Entity{name:entity.title})
SET e.wikiId = entity.wikiId
MERGE (a)-[:MENTIONS_ENT]->(e)
WITH entity, e
CALL apoc.create.addLabels(e,[entity.label]) YIELD node
RETURN distinct 'done' """ with driver.session() as session: params = [] for i,article in list(data.iterrows())[:500]: content = article['content'] ie_data = ie_pipeline(content) params.append({'content':content, 'ie':ie_data}) if (len(params) % 100 == 0): session.run(import_refactored_query, {'params':params}) params = [] session.run(update_query, {'params':params})

Le code est également disponible sous la forme d'un Jupyter Notebook sur GitHub. En fonction de vos capacités GPU, le pipeline IE peut prendre un certain temps. Inspectons maintenant la sortie. Évidemment, j'ai choisi des résultats qui ont du sens. Exécutez la requête suivante:

MATCH p = (e: Entité {nom: 'Enrico Bondi'}) - [: RELATION] -> (r) - [: RELATION] -> (), 
(r) <- [: MENTIONS_REL] - (s)
REVENIR *

Résultats

Résultats de l'extraction d'IE sur l'ensemble de données BBC News. Image par auteur

Nous pouvons constater qu'Enrico Bondi est un citoyen italien. Il a occupé un poste à la Chambre des députés italienne. Une autre relation a été déduite qu'il possède également Parmalat. Après une brève recherche sur Google, il semble que ces données soient plus ou moins au moins dans les domaines du possible.

Chemin vers une IA explicable

Vous pourriez vous demander ce que cela a à voir avec l'IA explicable. Je vais vous donner un exemple concret. Ce document de recherche est intitulé Réutilisation de médicaments pour COVID-19 via l'achèvement du graphique des connaissances. Je ne suis pas médecin, alors ne vous attendez pas à une présentation détaillée, mais je peux vous donner un aperçu de haut niveau. Il existe de nombreux articles de recherche médicale disponibles en ligne. Il existe également des bases de données en ligne sur les entités médicales telles que MeSH or Ensemble. Supposons que vous exécutiez un modèle de liaison d'entités nommées sur des articles de recherche biomédicale et que vous utilisiez l'une des bases de données médicales en ligne comme base de connaissances cible. Dans ce cas, vous pouvez extraire les entités mentionnées dans les articles. La partie la plus difficile est l'extraction de la relation. Parce que c'est un domaine si important, de grands esprits se sont réunis et ont extrait ces relations.

Il y a probablement plus de projets, mais je suis conscient du SemMedDB projet, qui a également été utilisé dans l'article mentionné. Maintenant que vous avez votre graphe de connaissances, vous pouvez essayer de prédire de nouvelles fins pour les médicaments existants. En science des réseaux, on parle de prédiction de lien. Lorsque vous essayez de prédire les liens ainsi que leurs types de relations, la communauté scientifique appelle cela l'achèvement de graphe de connaissances. Imaginez que nous ayons prédit de nouveaux cas d'utilisation de médicaments existants et que nous montrions nos résultats à un médecin ou à un pharmacologue. Sa réponse serait probablement, c'est bien, mais qu'est-ce qui vous fait penser que ce nouveau cas d'utilisation fonctionnera? Les modèles d'apprentissage automatique sont une boîte noire, ce n'est donc pas vraiment utile. Mais ce que vous pouvez donner au médecin, ce sont tous les liens entre le médicament existant et la nouvelle maladie qu'il pourrait traiter. Et pas seulement les relations directes, mais aussi celles qui sont à deux ou trois bonds. Je vais faire un exemple, donc cela n'a peut-être pas de sens pour un chercheur biomédical. Supposons que le médicament existant inhibe un gène corrélé à la maladie. Il peut y avoir de nombreux liens directs ou indirects entre le médicament et la maladie qui pourraient avoir un sens. Par conséquent, nous nous sommes engagés dans un pas vers une IA explicable.

Conclusion

Je suis vraiment ravi du déroulement de ce projet. J'ai bricolé avec la combinaison de graphiques PNL et de connaissances depuis environ un an, et maintenant j'ai mis toutes mes connaissances dans un seul article. J'espère que tu as aimé!

PS Si vous souhaitez apporter des modifications au pipeline IE, le code est disponible sous forme de Github référentiel. Le code de reproduction de ce billet de blog est également disponible sous forme de Jupyter Notebook.

Cet article a été publié initialement le Vers la science des données et republié sur TOPBOTS avec la permission de l'auteur.

Vous aimez cet article? Inscrivez-vous pour plus de mises à jour de recherche sur l'IA.

Nous vous informerons lorsque nous publierons plus de formation technique.

Source : https://www.topbots.com/information-extraction-pipeline/

spot_img

Dernières informations

spot_img

Discutez avec nous

Salut! Comment puis-je t'aider?