Construa um gráfico de conhecimento biomédico com PNL

Nó Fonte: 1401511

Já demonstrei como criar um gráfico de conhecimento a partir de uma página da Wikipedia. No entanto, como o post chamou muita atenção, decidi explorar outros domínios em que o uso de técnicas de PNL para construir um gráfico de conhecimento faz sentido. Na minha opinião, o campo biomédico é um excelente exemplo em que a representação dos dados como um gráfico faz sentido, pois você geralmente analisa interações e relações entre genes, doenças, medicamentos, proteínas e muito mais.

Exemplo de subgráfico que mostra as relações do ácido ascórbico com outros conceitos biomédicos. Imagem do autor.

Na visualização acima, temos o ácido ascórbico, também conhecido como vitamina C, e algumas de suas relações com outros conceitos. Por exemplo, mostra que a vitamina C pode ser usada para tratar a gastrite crônica.

Agora, você pode ter uma equipe de especialistas de domínio mapeando todas essas conexões entre drogas, doenças e outros conceitos biomédicos para você. Mas, infelizmente, muitos de nós não podem se dar ao luxo de contratar uma equipe de médicos para fazer o trabalho para nós. Nesse caso, podemos recorrer ao uso de técnicas de PNL para extrair esses relacionamentos automaticamente. A parte boa é que podemos usar um pipeline de PNL para ler todos os trabalhos de pesquisa que existem, e a parte ruim é que nem todos os resultados obtidos serão perfeitos. No entanto, como não tenho uma equipe de cientistas pronta ao meu lado para extrair relações manualmente, recorrerei a técnicas de PNL para construir um gráfico de conhecimento biomédico próprio.

Usarei um único artigo de pesquisa nesta postagem do blog para orientá-lo em todas as etapas necessárias para construir um gráfico de conhecimento biomédico - Engenharia de Tecidos de Regeneração da Pele e Crescimento do Cabelo.

O jornal foi escrito por Mohammadreza Ahmadi. A versão em PDF do artigo está disponível sob a licença CC0 1.0. Vamos seguir os seguintes passos para construir um grafo de conhecimento:

  • Lendo um documento PDF com OCR
  • Pré-processamento de texto
  • Reconhecimento e vinculação de conceitos biomédicos
  • Extração de relação
  • Enriquecimento de banco de dados externo

Ao final deste post, você construirá um gráfico com o seguinte esquema.

Esquema gráfico biomédico. Imagem do autor.

Usaremos o Neo4j, um banco de dados de grafos que apresenta o modelo de grafo de propriedades rotuladas, para armazenar nosso grafo. Cada artigo pode ter um ou mais autores. Dividiremos o conteúdo do artigo em frases e usaremos a PNL para extrair ambas as entidades médicas e seus relacionamentos. Pode ser um pouco contra-intuitivo armazenarmos as relações entre entidades como nós intermediários em vez de relacionamentos. O fator crítico por trás dessa decisão é que queremos ter uma trilha de auditoria do texto fonte do qual a relação foi extraída. Com o modelo de gráfico de propriedade rotulado, você não pode ter um relacionamento apontando para outro relacionamento. Por esta razão, refatoramos a conexão entre conceitos médicos em um nó intermediário. Isso também permitirá que um especialista do domínio avalie se uma relação foi extraída corretamente ou não.

Ao longo do caminho, também demonstrarei aplicações do uso do gráfico construído para pesquisar e analisar informações armazenadas.

Vamos mergulhar direto nisso!

Se este conteúdo educacional aprofundado for útil para você, assine nossa lista de discussão sobre pesquisa em IA para ser alertado quando lançarmos novo material.

Lendo um documento PDF com OCR

Como mencionado, a versão em PDF do trabalho de pesquisa é acessível ao público sob a licença CC0 1.0, o que significa que podemos baixá-lo facilmente com Python. Estaremos usando o Pytesseract biblioteca para extrair texto do PDF. Até onde eu sei, a biblioteca pytesseract é uma das bibliotecas mais populares para OCR. Se você quiser acompanhar os exemplos de código, preparei um Bloco de anotações do Google Colab, para que você não precise copiar e colar o código por conta própria.

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)

Pré-processamento de texto

Agora que temos o conteúdo do artigo disponível, vamos remover os títulos das seções e as descrições das figuras do texto. Em seguida, vamos dividir o texto em frases.

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)

Vinculação de entidade nomeada biomédica

Agora vem a parte emocionante. Para aqueles novos em PNL e reconhecimento e vinculação de entidades nomeadas, vamos começar com algumas noções básicas. Técnicas de reconhecimento de entidades nomeadas são usadas para detectar entidades ou conceitos relevantes no texto. Por exemplo, no domínio biomédico, queremos identificar vários genes, drogas, doenças e outros conceitos no texto.

Extração de conceitos biomédicos. Imagem do autor.

Neste exemplo, o modelo de PNL identificou genes, doenças, drogas, espécies, mutações e vias no texto. Como mencionado, esse processo é chamado de reconhecimento de entidade nomeada. Uma atualização para o reconhecimento de entidade nomeada é a chamada vinculação de entidade nomeada. A técnica de vinculação de entidades nomeadas detecta conceitos relevantes no texto e tenta mapeá-los para a base de conhecimento de destino. No domínio biomédico, algumas das bases de conhecimento alvo são:

Por que gostaríamos de vincular entidades médicas a uma base de conhecimento de destino? A principal razão é que isso nos ajuda a lidar com a desambiguação de entidades. Por exemplo, não queremos entidades separadas no gráfico representando ácido ascórbico e vitamina C, pois especialistas do domínio podem dizer que são a mesma coisa. A segunda razão é que ao mapear conceitos para uma base de conhecimento alvo, podemos enriquecer nosso modelo de grafo buscando informações sobre os conceitos mapeados da base de conhecimento alvo. Se usarmos o exemplo do ácido ascórbico novamente, poderíamos facilmente buscar informações adicionais do banco de dados CHEBI se já soubermos sua ID CHEBI.

Dados de enriquecimento disponíveis sobre ácido ascórbico no site da CHEBI. Todo o conteúdo do site está disponível em Licença CC BY 4.0. Imagem do autor.

Estou procurando uma entidade biomédica pré-treinada de código aberto decente vinculando há algum tempo. Muitos modelos de PNL se concentram em extrair apenas um subconjunto específico de conceitos médicos, como genes ou doenças. É ainda mais raro encontrar um modelo que detecte a maioria dos conceitos médicos e os vincule a uma base de conhecimento alvo. Felizmente eu tropecei BERNA[1], um reconhecimento de entidade biomédica neural e ferramenta de normalização multi-tipo. Se entendi corretamente, é um modelo BioBert ajustado com vários modelos de vinculação de entidades nomeadas integrados para mapear conceitos para bases de conhecimento biomédicas alvo. Além disso, eles também fornecem um endpoint REST gratuito, para que não tenhamos que lidar com a dor de cabeça de fazer com que as dependências e o modelo funcionem. A visualização biomédica de reconhecimento de entidade nomeada que usei acima foi criada usando o modelo BERN, então sabemos que detecta genes, doenças, drogas, espécies, mutações e caminhos no texto.

Infelizmente, o modelo BERN não atribui IDs de base de conhecimento de destino a todos os conceitos. Então eu preparei um script que primeiro verifica se um id distinto é fornecido para um conceito e, se não for, ele usará o nome da entidade como o id. Também calcularemos o sha256 do texto das sentenças para identificar sentenças específicas mais facilmente mais tarde, quando estivermos fazendo a extração de relações.

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()})

Inspecionei os resultados da vinculação da entidade nomeada e, como esperado, não é perfeito. Por exemplo, não identifica as células-tronco como um conceito médico. Por outro lado, detectou uma única entidade chamada “coração, cérebro, nervos e rim”. No entanto, BERN ainda é o melhor modelo biomédico de código aberto que pude encontrar durante minha investigação.

Construir um gráfico de conhecimento

Antes de olhar para as técnicas de extração de relações, vamos construir um grafo de conhecimento biomédico usando apenas entidades e examinar as possíveis aplicações. Como mencionado, preparei um Bloco de anotações do Google Colab que você pode usar para seguir os exemplos de código neste post. Para armazenar nosso gráfico, usaremos o Neo4j. Você não precisa lidar com a preparação de um ambiente Neo4j local. Em vez disso, você pode usar uma instância gratuita do Neo4j Sandbox.

Caixa de areia Neo4j

Comece o Projeto em branco na sandbox e copie os detalhes da conexão para o notebook Colab.

Detalhes da conexão do Neo4j Sandbox. Imagem do autor.

Agora você pode preparar a conexão Neo4j no notebook.

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())

Começaremos importando o autor e o artigo para o gráfico. O nó do artigo conterá apenas o título.

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})

Se você abrir o navegador Neo4j, deverá ver o gráfico a seguir.

Imagem do autor.

Você pode importar as sentenças e entidades mencionadas executando a seguinte consulta Cypher:

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})

Você pode executar a seguinte consulta Cypher para inspecionar o gráfico construído:

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

Se você importou os dados corretamente, deverá ver uma visualização semelhante.

Extração de entidade armazenada como um gráfico. Imagem do autor.

Aplicativos de gráfico de conhecimento

Mesmo sem o fluxo de extração de relação, já existem alguns casos de uso para nosso gráfico.

Search engine

Poderíamos usar nosso gráfico como um mecanismo de busca. Por exemplo, você pode usar a seguinte consulta Cypher para encontrar frases ou artigos que mencionem uma entidade médica específica.

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

Resultados

Imagem do autor.

Análise de coocorrência

A segunda opção é a análise de coocorrência. Você pode definir a coocorrência entre entidades médicas se elas aparecerem na mesma frase ou artigo. Encontrei um artigo[2] que usa a rede de co-ocorrência médica para prever novas conexões possíveis entre entidades médicas.

Predição de links em uma rede de coocorrência MeSH: resultados preliminares – PubMed

Você pode usar a seguinte consulta Cypher para encontrar entidades que geralmente ocorrem simultaneamente na mesma frase.

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

Resultados

entidade 1 entidade 2 co-ocorrência
doenças de pele úlceras diabéticas 2
feridas crônicas úlceras diabéticas 2
doenças de pele feridas crônicas 2

Obviamente, os resultados seriam melhores se analisássemos milhares ou mais artigos.

Inspecione a experiência do autor

Você também pode usar este gráfico para encontrar a experiência do autor examinando as entidades médicas sobre as quais eles escrevem com mais frequência. Com essas informações, você também pode sugerir futuras colaborações.

Execute a seguinte consulta Cypher para inspecionar quais entidades médicas nosso único autor mencionou no trabalho de pesquisa.

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

Resultados

autor entidade contar
Mohammadreza Ahmadi Colágeno 9
Mohammadreza Ahmadi queimaduras 4
Mohammadreza Ahmadi doenças de pele 4
Mohammadreza Ahmadi enzimas colagenase 2
Mohammadreza Ahmadi Epidermólise bolhosa 2

Extração de relação

Agora vamos tentar extrair relações entre conceitos médicos. Pela minha experiência, a extração de relação é pelo menos uma ordem de magnitude mais difícil do que a extração de entidade nomeada. Se você não deve esperar resultados perfeitos com a vinculação de entidade nomeada, então você pode definitivamente esperar alguns erros com a técnica de extração de relação.

Eu tenho procurado por modelos de extração de relação biomédica disponíveis, mas não encontrei nada que funcione fora da caixa ou que não exija ajuste fino. Parece que o campo de extração de relações está na vanguarda, e esperamos ver mais atenção sobre isso no futuro. Infelizmente, não sou especialista em PNL, então evitei ajustar meu próprio modelo. Em vez disso, usaremos o extrator de relação zero-shot baseado no artigo Explorando o limite de tiro zero de FewRel[3]. Embora eu não recomende colocar este modelo em produção, é bom o suficiente para uma demonstração simples. O modelo está disponível em Abraçando o Rosto, para que não tenhamos que lidar com treinamento ou configuração do modelo.

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)

Com o extrator de relação zero-shot, você pode definir quais relações você gostaria de detectar. Neste exemplo, usei o associado e interage relacionamentos. Também tentei tipos de relacionamento mais específicos, como guloseimas, causas e outros, mas os resultados não foram ótimos.

Com este modelo, você deve definir entre quais pares de entidades você gostaria de detectar relacionamentos. Usaremos os resultados da vinculação da entidade nomeada como entrada para o processo de extração da relação. Primeiro, encontramos todas as frases em que duas ou mais entidades são mencionadas e as executamos pelo modelo de extração de relações para extrair quaisquer conexões. Também defini um valor limite de 0.85, o que significa que, se um modelo prever um link entre entidades com probabilidade inferior a 0.85, ignoraremos a previsão.

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})

Armazenamos os relacionamentos, bem como o texto de origem usado para extrair esse relacionamento no gráfico.

Relações extraídas armazenadas em um gráfico. Imagem do autor.

Você pode examinar os relacionamentos extraídos entre entidades e o texto de origem com a seguinte consulta Cypher:

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

Resultados

Imagem do autor.

Como mencionei, o modelo de PNL que usei para extrair relações não é perfeito e, como não sou médico, não sei quantas conexões faltou. No entanto, os que detectou parecem razoáveis.

Enriquecimento de banco de dados externo

Como mencionei antes, ainda podemos usar os bancos de dados externos como CHEBI ou MESH para enriquecer nosso gráfico. Por exemplo, nosso gráfico contém uma entidade médica Epidermólise bolhosa e também sabemos seu id MeSH.

Você pode recuperar o ID MeSH da Epidermólise bolhosa com a seguinte consulta:

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

Você pode ir em frente e inspecionar o MeSH para encontrar as informações disponíveis:

Dados vinculados MeSH

Captura de tela do autor. Os dados são cortesia da Biblioteca Nacional de Medicina dos EUA.

Aqui está uma captura de tela das informações disponíveis no site do MeSH para Epidermólise bolhosa. Como mencionado, não sou médico, então não sei exatamente qual seria a melhor forma de modelar essa informação em um gráfico. No entanto, mostrarei como recuperar essas informações no Neo4j usando o procedimento apoc.load.json para buscar as informações do endpoint MeSH REST. E então, você pode pedir a um especialista de domínio para ajudá-lo a modelar essas informações.

A consulta Cypher para buscar as informações do endpoint MeSH REST é:

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

Gráfico de conhecimento como entrada de dados de aprendizado de máquina

Como pensamento final, mostrarei rapidamente como você pode usar o gráfico de conhecimento biomédico como uma entrada para um fluxo de trabalho de aprendizado de máquina. Nos últimos anos, tem havido muita pesquisa e avanço no campo de incorporação de nós. Os modelos de incorporação de nós traduzem a topologia da rede em espaço de incorporação.

Direitos autorais © 2017 Manan Shah, Grupo SNAP. Imagem disponível sob licença MIT em https://github.com/snap-stanford/cs224w-notes.

Suponha que você construiu um gráfico de conhecimento biomédico contendo entidades e conceitos médicos, suas relações e enriquecimento de vários bancos de dados médicos. Você pode usar técnicas de incorporação de nós para aprender as representações de nós, que são vetores de comprimento fixo, e inseri-los em seu fluxo de trabalho de aprendizado de máquina. Várias aplicações estão usando essa abordagem, desde o reaproveitamento de drogas até previsões de efeitos colaterais ou adversos. Encontrei um trabalho de pesquisa que usa link previsão para tratamentos potenciais de novas doenças[4].

Conclusão

O domínio biomédico é um excelente exemplo onde os gráficos de conhecimento são aplicáveis. Existem muitos aplicativos que variam de mecanismos de pesquisa simples a fluxos de trabalho de aprendizado de máquina mais complicados. Espero que, ao ler esta postagem do blog, você tenha algumas ideias sobre como usar gráficos de conhecimento biomédico para apoiar seus aplicativos. Você pode iniciar um Sandbox Neo4j grátis e comece a explorar hoje.

Como sempre, o código está disponível em GitHub.

Referências

[1] D.Kim et al., “Uma Ferramenta Neural Named Entity Recognition and Multi-Type Normalization Tool for Biomedical Text Mining”, em Acesso IEEE, vol. 7–73729, 73740, doi: 2019/ACCESS.10.1109.

[2] Kastrin A, Rindflesch TC, Hristovski D. Previsão de link em uma rede de co-ocorrência MeSH: resultados preliminares. Stud Health Technol Informa. 2014; 205: 579-83. PMID: 25160252.

[3] Cetoli, A. (2020). Explorando o limite de tiro zero de FewRel. Dentro Anais da 28ª Conferência Internacional de Linguística Computacional (págs. 1447-1451). Comitê Internacional de Linguística Computacional.

[4] Zhang, R., Hristovski, D., Schutte, D., Kastrin, A., Fiszman, M., & Kilicoglu, H. (2021). Reaproveitamento de medicamentos para COVID-19 por meio da conclusão do gráfico de conhecimento. Jornal de Informática Biomédica, 115, 103696.

Este artigo foi originalmente publicado em Rumo à ciência de dados e republicado no TOPBOTS com permissão do autor.

Gostou deste artigo? Inscreva-se para mais atualizações de IA.

Avisaremos quando lançarmos mais educação técnica.

O posto Construa um gráfico de conhecimento biomédico com PNL apareceu pela primeira vez em TOPBOTS.

Carimbo de hora:

Mais de TOPBOTS