Konstruera ett biomedicinskt kunskapsdiagram med NLP

Källnod: 1401511

Jag har redan visat hur man gör skapa en kunskapsgraf från en Wikipedia-sida. Men eftersom inlägget fick mycket uppmärksamhet, har jag bestämt mig för att utforska andra domäner där det är meningsfullt att använda NLP-tekniker för att konstruera en kunskapsgraf. Enligt min åsikt är det biomedicinska området ett utmärkt exempel där det är meningsfullt att representera data som en graf eftersom du ofta analyserar interaktioner och relationer mellan gener, sjukdomar, läkemedel, proteiner och mer.

Exempel på subgraf som visar askorbinsyrasamband med andra biomedicinska koncept. Bild av författaren.

I ovanstående visualisering har vi askorbinsyra, även känd som vitamin C, och några av dess relationer till andra koncept. Till exempel visar det att C-vitamin kan användas för att behandla kronisk gastrit.

Nu kan du få ett team av domänexperter att kartlägga alla dessa kopplingar mellan läkemedel, sjukdomar och andra biomedicinska koncept åt dig. Men tyvärr är det inte många av oss som har råd att anlita ett team av läkare för att göra jobbet åt oss. I så fall kan vi använda NLP-tekniker för att extrahera dessa relationer automatiskt. Den goda delen är att vi kan använda en NLP-pipeline för att läsa alla forskningsartiklar där ute, och den dåliga delen är att inte alla erhållna resultat kommer att vara perfekta. Men med tanke på att jag inte har ett team av forskare redo vid min sida för att extrahera relationer manuellt, kommer jag att använda NLP-tekniker för att konstruera en egen biomedicinsk kunskapsgraf.

Jag kommer att använda en enda forskningsartikel i det här blogginlägget för att gå igenom alla steg som krävs för att konstruera en biomedicinsk kunskapsgraf – Vävnadsteknik för hudregenerering och hårväxt.

Tidningen skrevs av Mohammadreza Ahmadi. PDF-versionen av artikeln är tillgänglig under licensen CC0 1.0. Vi kommer att gå igenom följande steg för att konstruera en kunskapsgraf:

  • Läsa ett PDF-dokument med OCR
  • Förbehandling av text
  • Biomedicinskt konceptigenkänning och länkning
  • Relationsextraktion
  • Extern databasanrikning

I slutet av det här inlägget kommer du att konstruera en graf med följande schema.

Biomedicinskt grafschema. Bild av författaren.

Vi kommer att använda Neo4j, en grafdatabas som innehåller den märkta egenskapsgrafmodellen, för att lagra vår graf. Varje artikel kan ha en eller flera författare. Vi kommer att dela upp artikelinnehållet i meningar och använda NLP för att extrahera både medicinska enheter och deras relationer. Det kan vara lite kontraintuitivt att vi kommer att lagra relationerna mellan entiteter som mellanliggande noder istället för relationer. Den kritiska faktorn bakom detta beslut är att vi vill ha ett granskningsspår av källtexten från vilken relationen extraherades. Med den märkta egenskapsdiagrammodellen kan du inte ha en relation som pekar på en annan relation. Av denna anledning refaktorerar vi kopplingen mellan medicinska begrepp till en mellannod. Detta kommer också att tillåta en domänexpert att utvärdera om en relation extraherades korrekt eller inte.

Längs vägen kommer jag också att demonstrera tillämpningar av att använda den konstruerade grafen för att söka och analysera lagrad information.

Låt oss dyka in i det!

Om detta fördjupade utbildningsinnehåll är användbart för dig, prenumerera på vår AI-forskningsmaillista att bli varnade när vi släpper nytt material.

Läsa ett PDF-dokument med OCR

Som nämnts är PDF-versionen av forskningsrapporten tillgänglig för allmänheten under CC0 1.0-licensen, vilket innebär att vi enkelt kan ladda ner den med Python. Vi kommer att använda pytesserakt bibliotek för att extrahera text från PDF-filen. Så vitt jag vet är pytesseract-biblioteket ett av de mer populära biblioteken för OCR. Om du vill följa med med kodexempel har jag förberett ett Google Colab-anteckningsbok, så att du inte behöver kopiera och klistra in koden själv.

import requests
import pdf2image
import pytesseract pdf = requests.get('https://arxiv.org/pdf/2110.03526.pdf')
doc = pdf2image.convert_from_bytes(pdf.content) # Get the article text
article = []
for page_number, page_data in enumerate(doc): txt = pytesseract.image_to_string(page_data).encode("utf-8") # Sixth page are only references if page_number < 6: article.append(txt.decode("utf-8"))
article_txt = " ".join(article)

Förbehandling av text

Nu när vi har artikelinnehållet tillgängligt kommer vi att ta bort avsnittsrubriker och figurbeskrivningar från texten. Därefter delar vi upp texten i meningar.

import nltk
nltk.download('punkt') def clean_text(text): """Remove section titles and figure descriptions from text""" clean = "n".join([row for row in text.split("n") if (len(row.split(" "))) > 3 and not (row.startswith("(a)")) and not row.startswith("Figure")]) return clean text = article_txt.split("INTRODUCTION")[1]
ctext = clean_text(text)
sentences = nltk.tokenize.sent_tokenize(ctext)

Biomedicinskt namngiven enhetslänkning

Nu kommer den spännande delen. För de som är nybörjare inom NLP och namngivna enheters erkännande och länkning, låt oss börja med några grunder. Namngivna enhetsigenkänningstekniker används för att upptäcka relevanta enheter eller begrepp i texten. Till exempel inom den biomedicinska domänen vill vi identifiera olika gener, läkemedel, sjukdomar och andra begrepp i texten.

Biomedicinsk begreppsextraktion. Bild av författaren.

I det här exemplet identifierade NLP-modellen gener, sjukdomar, läkemedel, arter, mutationer och vägar i texten. Som nämnts kallas denna process för namngiven enhetsidentifiering. En uppgradering till den namngivna enhetsigenkänningen är den så kallade namngivna entitetslänkningen. Den namngivna entitetslänkningstekniken upptäcker relevanta begrepp i texten och försöker mappa dem till målkunskapsbasen. Inom den biomedicinska domänen är några av målkunskapsbaserna:

Varför skulle vi vilja koppla medicinska enheter till en målkunskapsbas? Det främsta skälet är att det hjälper oss att hantera disambiguering av enheter. Till exempel vill vi inte ha separata enheter i grafen som representerar askorbinsyra och vitamin C eftersom domänexperter kan säga att det är samma sak. Det sekundära skälet är att genom att kartlägga begrepp till en målkunskapsbas kan vi berika vår grafmodell genom att hämta information om de kartlagda begreppen från målkunskapsbasen. Om vi ​​använder askorbinsyraexemplet igen kan vi enkelt hämta ytterligare information från CHEBI-databasen om vi redan vet dess CHEBI id.

Anrikningsdata finns om askorbinsyra på CHEBIs webbplats. Allt innehåll på webbplatsen finns tillgängligt under CC BY 4.0-licens. Bild av författaren.

Jag har letat efter en anständig öppen källkod förutbildad biomedicinsk namngiven enhet länkning under en tid. Många NLP-modeller fokuserar på att bara extrahera en specifik undergrupp av medicinska koncept som gener eller sjukdomar. Det är ännu sällsyntare att hitta en modell som upptäcker de flesta medicinska begrepp och länkar dem till en målkunskapsbas. Tur att jag har snubblat på BERN[1], ett verktyg för erkännande av neural biomedicinsk enhet och normalisering av flera typer. Om jag förstår det rätt är det en finjusterad BioBert-modell med olika namngivna entitetslänkningsmodeller integrerade för kartläggning av koncept till biomedicinska målkunskapsbaser. Inte bara det, de ger också en gratis REST-slutpunkt, så vi behöver inte hantera huvudvärken med att få beroenden och modellen att fungera. Den biomedicinska namngivna visualiseringen av enhetsigenkänning som jag har använt ovan skapades med BERN-modellen, så vi vet att den upptäcker gener, sjukdomar, droger, arter, mutationer och vägar i texten.

Tyvärr tilldelar BERN-modellen inte målkunskapsbas-ID till alla koncept. Så jag har förberett ett skript som först ser om ett distinkt id ges för ett koncept, och om det inte är det kommer det att använda entitetsnamnet som id. Vi kommer också att beräkna sha256 i meningstexten för att lättare identifiera specifika meningar senare när vi ska göra relationsextraktion.

import hashlib def query_raw(text, url="https://bern.korea.ac.kr/plain"): """Biomedical entity linking API""" return requests.post(url, data={'sample_text': text}).json() entity_list = []
# The last sentence is invalid
for s in sentences[:-1]: entity_list.append(query_raw(s)) parsed_entities = []
for entities in entity_list: e = [] # If there are not entities in the text if not entities.get('denotations'): parsed_entities.append({'text':entities['text'], 'text_sha256': hashlib.sha256(entities['text'].encode('utf-8')).hexdigest()}) continue for entity in entities['denotations']: other_ids = [id for id in entity['id'] if not id.startswith("BERN")] entity_type = entity['obj'] entity_name = entities['text'][entity['span']['begin']:entity['span']['end']] try: entity_id = [id for id in entity['id'] if id.startswith("BERN")][0] except IndexError: entity_id = entity_name e.append({'entity_id': entity_id, 'other_ids': other_ids, 'entity_type': entity_type, 'entity': entity_name}) parsed_entities.append({'entities':e, 'text':entities['text'], 'text_sha256': hashlib.sha256(entities['text'].encode('utf-8')).hexdigest()})

Jag har inspekterat resultaten av den namngivna enhetens länkning, och som förväntat är den inte perfekt. Till exempel identifierar den inte stamceller som ett medicinskt begrepp. Å andra sidan upptäckte den en enda enhet som heter "hjärta, hjärna, nerver och njure". Men BERN är fortfarande den bästa biomedicinska modellen med öppen källkod jag kunde hitta under min undersökning.

Konstruera en kunskapsgraf

Innan vi tittar på relationsextraktionstekniker kommer vi att konstruera en biomedicinsk kunskapsgraf med endast entiteter och undersöka möjliga tillämpningar. Som nämnts har jag förberett en Google Colab-anteckningsbok som du kan använda för att följa kodexemplen i det här inlägget. För att lagra vår graf kommer vi att använda Neo4j. Du behöver inte ta itu med att förbereda en lokal Neo4j-miljö. Istället kan du använda en gratis Neo4j Sandbox-instans.

Neo4j sandlåda

Starta Tomt projekt i sandlådan och kopiera anslutningsdetaljerna till Colab-anteckningsboken.

Neo4j Sandbox anslutningsdetaljer. Bild av författaren.

Nu kan du gå vidare och förbereda Neo4j-anslutningen i den bärbara datorn.

from neo4j import GraphDatabase
import pandas as pd host = 'bolt://3.236.182.55:7687'
user = 'neo4j'
password = 'hydrometer-ditches-windings'
driver = GraphDatabase.driver(host,auth=(user, password)) def neo4j_query(query, params=None): with driver.session() as session: result = session.run(query, params) return pd.DataFrame([r.values() for r in result], columns=result.keys())

Vi börjar med att importera författaren och artikeln till grafen. Artikelnoden kommer endast att innehålla titeln.

author = article_txt.split("n")[0]
title = " ".join(article_txt.split("n")[2:4]) neo4j_query("""
MERGE (a:Author{name:$author})
MERGE (b:Article{title:$title})
MERGE (a)-[:WROTE]->(b)
""", {'title':title, 'author':author})

Om du öppnar Neo4j-webbläsaren bör du se följande graf.

Bild av författaren.

Du kan importera meningarna och nämnda enheter genom att utföra följande Cypher-fråga:

neo4j_query("""
MATCH (a:Article)
UNWIND $data as row
MERGE (s:Sentence{id:row.text_sha256})
SET s.text = row.text
MERGE (a)-[:HAS_SENTENCE]->(s)
WITH s, row.entities as entities
UNWIND entities as entity
MERGE (e:Entity{id:entity.entity_id})
ON CREATE SET e.other_ids = entity.other_ids, e.name = entity.entity, e.type = entity.entity_type
MERGE (s)-[m:MENTIONS]->(e)
ON CREATE SET m.count = 1
ON MATCH SET m.count = m.count + 1
""", {'data': parsed_entities})

Du kan köra följande Cypher-fråga för att inspektera den konstruerade grafen:

MATCH p=(a:Article)-[:HAS_SENTENCE]->()-[:MENTIONS]->(e:Entity)
RETURN p LIMIT 25

Om du har importerat data korrekt bör du se en liknande visualisering.

Entitetsextraktion lagras som en graf. Bild av författaren.

Tillämpningar av kunskapsdiagram

Även utan relationsextraktionsflödet finns det redan ett par användningsfall för vår graf.

Sökmotor

Vi skulle kunna använda vår graf som en sökmotor. Du kan till exempel använda följande Cypher-fråga för att hitta meningar eller artiklar som nämner en specifik medicinsk enhet.

MATCH (e:Entity)<-[:MENTIONS]-(s:Sentence)
WHERE e.name = "autoimmune diseases"
RETURN s.text as result

Resultat

Bild av författaren.

Samförekomstanalys

Det andra alternativet är samförekomstanalysen. Du kan definiera samtidig förekomst mellan medicinska enheter om de förekommer i samma mening eller artikel. Jag har hittat en artikel[2] som använder nätverket för medicinsk samtidig förekomst för att förutsäga nya möjliga kopplingar mellan medicinska enheter.

Länkförutsägelse i ett MeSH-samförekomstnätverk: preliminära resultat – PubMed

Du kan använda följande Cypher-fråga för att hitta enheter som ofta förekommer samtidigt i samma mening.

MATCH (e1:Entity)<-[:MENTIONS]-()-[:MENTIONS]->(e2:Entity)
MATCH (e1:Entity)<-[:MENTIONS]-()-[:MENTIONS]->(e2:Entity)
WHERE id(e1) < id(e2)
RETURN e1.name as entity1, e2.name as entity2, count(*) as cooccurrence
ORDER BY cooccurrence
DESC LIMIT 3

Resultat

enhet1 enhet2 samförekomst
hudsjukdomar diabetessår 2
kroniska sår diabetessår 2
hudsjukdomar kroniska sår 2

Uppenbarligen skulle resultaten bli bättre om vi analyserade tusentals eller fler artiklar.

Inspektera författarens expertis

Du kan också använda denna graf för att hitta författarens expertis genom att undersöka de medicinska enheter de oftast skriver om. Med denna information kan du också föreslå framtida samarbeten.

Utför följande Cypher-fråga för att inspektera vilka medicinska enheter som vår enda författare nämnde i forskningsartikeln.

MATCH (a:Author)-[:WROTE]->()-[:HAS_SENTENCE]->()-[:MENTIONS]->(e:Entity)
RETURN a.name as author, e.name as entity,
MATCH (a:Author)-[:WROTE]->()-[:HAS_SENTENCE]->()-[:MENTIONS]->(e:Entity)
RETURN a.name as author, e.name as entity, count(*) as count
ORDER BY count DESC
LIMIT 5

Resultat

Författaren enhet räkna
Mohammadreza Ahmadi kollagen 9
Mohammadreza Ahmadi brännskador 4
Mohammadreza Ahmadi hudsjukdomar 4
Mohammadreza Ahmadi kollagenasenzymer 2
Mohammadreza Ahmadi Epidermolysis bullosa 2

Relationsextraktion

Nu ska vi försöka utvinna relationer mellan medicinska begrepp. Enligt min erfarenhet är relationsextraktionen åtminstone en storleksordning svårare än namngiven entitetsextraktion. Om du inte borde förvänta dig perfekta resultat med namngivna entitetslänkar, kan du definitivt förvänta dig några misstag med relationsextraktionstekniken.

Jag har letat efter tillgängliga biomedicinska relationsextraktionsmodeller men hittade inget som fungerar direkt eller som inte kräver finjustering. Det verkar som att området för relationsextraktion ligger i framkant, och förhoppningsvis kommer vi att se mer uppmärksamhet kring det i framtiden. Tyvärr är jag ingen NLP-expert, så jag undvek att finjustera min egen modell. Istället kommer vi att använda nollskottsrelationsextraktorn baserad på papperet Utforska nollskottsgränsen för FewRel[3]. Även om jag inte skulle rekommendera att sätta den här modellen i produktion, är den tillräckligt bra för en enkel demonstration. Modellen finns på Kramar ansikte, så vi behöver inte syssla med träning eller att sätta upp modellen.

from transformers import AutoTokenizer
from zero_shot_re import RelTaggerModel, RelationExtractor model = RelTaggerModel.from_pretrained("fractalego/fewrel-zero-shot")
tokenizer = AutoTokenizer.from_pretrained("bert-large-uncased-whole-word-masking-finetuned-squad")
relations = ['associated', 'interacts']
extractor = RelationExtractor(model, tokenizer, relations)

Med Zero-shot relation extractor kan du definiera vilka relationer du vill detektera. I det här exemplet har jag använt associerad och interagerar relationer. Jag har också provat mer specifika relationstyper som godsaker, orsaker och andra, men resultaten var inte bra.

Med den här modellen måste du definiera mellan vilka par av entiteter du vill upptäcka relationer. Vi kommer att använda resultaten av den namngivna entitetslänkningen som en input till relationsextraktionsprocessen. Först hittar vi alla meningar där två eller flera entiteter nämns och kör dem sedan genom relationsextraktionsmodellen för att extrahera eventuella samband. Jag har också definierat ett tröskelvärde på 0.85, vilket betyder att om en modell förutsäger en länk mellan enheter med en sannolikhet som är lägre än 0.85, kommer vi att ignorera förutsägelsen.

import itertools
# Candidate sentence where there is more than a single entity present
candidates = [s for s in parsed_entities if (s.get('entities')) and (len(s['entities']) &gt; 1)]
predicted_rels = []
for c in candidates: combinations = itertools.combinations([{'name':x['entity'], 'id':x['entity_id']} for x in c['entities']], 2) for combination in list(combinations): try: ranked_rels = extractor.rank(text=c['text'].replace(",", " "), head=combination[0]['name'], tail=combination[1]['name']) # Define threshold for the most probable relation if ranked_rels[0][1] &gt; 0.85: predicted_rels.append({'head': combination[0]['id'], 'tail': combination[1]['id'], 'type':ranked_rels[0][0], 'source': c['text_sha256']}) except: pass # Store relations to Neo4j
neo4j_query("""
UNWIND $data as row
MATCH (source:Entity {id: row.head})
MATCH (target:Entity {id: row.tail})
MATCH (text:Sentence {id: row.source})
MERGE (source)-[:REL]-&gt;(r:Relation {type: row.type})-[:REL]-&gt;(target)
MERGE (text)-[:MENTIONS]-&gt;(r)
""", {'data': predicted_rels})

Vi lagrar relationerna såväl som källtexten som används för att extrahera den relationen i grafen.

Extraherade relationer lagrade i en graf. Bild av författaren.

Du kan undersöka de extraherade relationerna mellan entiteter och källtexten med följande Cypher-fråga:

MATCH (s:Entity)-[:REL]->(r:Relation)-[:REL]->(t:Entity), (r)<-[:MENTIONS]-(st:Sentence)
RETURN s.name as source_entity, t.name as target_entity, r.type as type, st.text as source_text

Resultat

Bild av författaren.

Som nämnts är NLP-modellen jag har använt för att extrahera relationer inte perfekt, och eftersom jag inte är läkare vet jag inte hur många kopplingar den missade. De som upptäckts verkar dock rimliga.

Extern databasanrikning

Som jag nämnde tidigare kan vi fortfarande använda externa databaser som CHEBI eller MESH för att berika vår graf. Till exempel innehåller vår graf en medicinsk enhet Epidermolysis bullosa och vi känner också till dess MeSH-id.

Du kan hämta MeSH-id:t för Epidermolysis bullosa med följande fråga:

MATCH (e:Entity)
WHERE e.name = "Epidermolysis bullosa"
RETURN e.name as entity, e.other_ids as other_ids

Du kan gå vidare och inspektera MeSH för att hitta tillgänglig information:

MeSH länkad data

Skärmdump av författaren. Uppgifterna kommer från US National Library of Medicine.

Här är en skärmdump av tillgänglig information på MeSH-webbplatsen för Epidermolysis bullosa. Som nämnts är jag inte en läkare, så jag vet inte exakt vad som skulle vara det bästa sättet att modellera denna information i en graf. Jag ska dock visa dig hur du hämtar denna information i Neo4j med apoc.load.json-proceduren för att hämta informationen från MeSH REST-slutpunkten. Och sedan kan du be en domänexpert hjälpa dig att modellera denna information.

Cypher-frågan för att hämta informationen från MeSH REST-slutpunkten är:

MATCH (e:Entity)
WHERE e.name = "Epidermolysis bullosa"
WITH e, [id in e.other_ids WHERE id contains "MESH" | split(id,":")[1]][0] as meshId
CALL apoc.load.json("https://id.nlm.nih.gov/mesh/lookup/details?descriptor=" + meshId) YIELD value
RETURN value

Kunskapsdiagram som maskininlärningsdatainmatning

Som en sista tanke kommer jag snabbt att gå igenom hur du kan använda den biomedicinska kunskapsgrafen som en input till ett arbetsflöde för maskininlärning. Under de senaste åren har det skett mycket forskning och framsteg inom området för inbäddning av noder. Nodinbäddningsmodeller översätter nätverkstopologin till inbäddningsutrymme.

Copyright © 2017 Manan Shah, SNAP Group. Bild tillgänglig under MIT-licens i https://github.com/snap-stanford/cs224w-notes.

Anta att du konstruerat en biomedicinsk kunskapsgraf som innehåller medicinska enheter och begrepp, deras relationer och berikning från olika medicinska databaser. Du kan använda tekniker för inbäddning av noder för att lära dig nodrepresentationerna, som är vektorer med fast längd, och mata in dem i ditt arbetsflöde för maskininlärning. Olika tillämpningar använder detta tillvägagångssätt, allt från användning av läkemedel till förutsägelser av läkemedelsbiverkningar eller biverkningar. Jag har hittat ett forskningsdokument som använder länkförutsägelse för potentiella behandlingar av nya sjukdomar[4].

Slutsats

Den biomedicinska domänen är ett utmärkt exempel där kunskapsdiagram är tillämpliga. Det finns många applikationer, allt från enkla sökmotorer till mer komplicerade arbetsflöden för maskininlärning. Förhoppningsvis, genom att läsa det här blogginlägget, kom du på några idéer om hur du kan använda grafer för biomedicinska kunskaper för att stödja dina applikationer. Du kan starta en gratis Neo4j Sandbox och börja utforska idag.

Som alltid finns koden tillgänglig på GitHub.

Referensprojekt

[1] D. Kim et al,., "A Neural Named Entity Recognition and Multi-Type Normalization Tool for Biomedical Text Mining," i IEEE-åtkomstvol. 7, s. 73729–73740, 2019, doi: 10.1109/ACCESS.2019.2920708.

[2] Kastrin A, Rindflesch TC, Hristovski D. Länkförutsägelse i ett MeSH-samförekomstnätverk: preliminära resultat. Stud Health Technol Inform. 2014;205:579–83. PMID: 25160252.

[3] Cetoli, A. (2020). Utforska nollskottsgränsen för FewRel. I Proceedings of the 28th International Conference on Computational Linguistics (s. 1447–1451). International Committee on Computational Linguistics.

[4] Zhang, R., Hristovski, D., Schutte, D., Kastrin, A., Fiszman, M., & Kilicoglu, H. (2021). Läkemedelsanvändning för covid-19 via färdigställande av kunskapsdiagram. Journal of Biomedical Informatics, 115, 103696.

Den här artikeln publicerades ursprungligen den Mot datavetenskap och publiceras på nytt till TOPBOTS med tillstånd från författaren.

Tycker du om den här artikeln? Registrera dig för fler AI-uppdateringar.

Vi meddelar dig när vi släpper mer teknisk utbildning.

Posten Konstruera ett biomedicinskt kunskapsdiagram med NLP visades först på TOPPBOTS.

Tidsstämpel:

Mer från TOPPBOTS