SpaCy: установить информацию об объекте для токена, который включен более чем в один диапазон.
Я пытаюсь использовать SpaCy для распознавания контекста сущности в мире онтологий. Я новичок в использовании SpaCy и просто играю для начала.
Я использую ENVO Ontology в качестве списка «шаблонов» для создания словаря для распознавания сущностей. Проще говоря, данные представляют собой идентификатор (CURIE) и имя объекта, которому они соответствуют, вместе с его категорией.
Снимок экрана с моими образцами данных:
Ниже приведен рабочий процесс моего исходного кода:
- Создание шаблонов и терминов
# Set terms and patterns
terms = {}
patterns = []
for curie, name, category in envoTerms.to_records(index=False):
if name is not None:
terms[name.lower()] = {'id': curie, 'category': category}
patterns.append(nlp(name))
- Настроить собственный конвейер
@Language.component('envo_extractor')
def envo_extractor(doc):
matches = matcher(doc)
spans = [Span(doc, start, end, label = 'ENVO') for matchId, start, end in matches]
doc.ents = spans
for i, span in enumerate(spans):
span._.set("has_envo_ids", True)
for token in span:
token._.set("is_envo_term", True)
token._.set("envo_id", terms[span.text.lower()]["id"])
token._.set("category", terms[span.text.lower()]["category"])
return doc
# Setter function for doc level
def has_envo_ids(self, tokens):
return any([t._.get("is_envo_term") for t in tokens])
##EDIT: #################################################################
def resolve_substrings(matcher, doc, i, matches):
# Get the current match and create tuple of entity label, start and end.
# Append entity to the doc's entity. (Don't overwrite doc.ents!)
match_id, start, end = matches[i]
entity = Span(doc, start, end, label="ENVO")
doc.ents += (entity,)
print(entity.text)
#########################################################################
- Реализуйте настраиваемый конвейер
nlp = spacy.load("en_core_web_sm")
matcher = PhraseMatcher(nlp.vocab)
#### EDIT: Added 'on_match' rule ################################
matcher.add("ENVO", None, *patterns, on_match=resolve_substrings)
nlp.add_pipe('envo_extractor', after='ner')
и конвейер выглядит так
[('tok2vec', <spacy.pipeline.tok2vec.Tok2Vec at 0x7fac00c03bd0>),
('tagger', <spacy.pipeline.tagger.Tagger at 0x7fac0303fcc0>),
('parser', <spacy.pipeline.dep_parser.DependencyParser at 0x7fac02fe7460>),
('ner', <spacy.pipeline.ner.EntityRecognizer at 0x7fac02f234c0>),
('envo_extractor', <function __main__.envo_extractor(doc)>),
('attribute_ruler',
<spacy.pipeline.attributeruler.AttributeRuler at 0x7fac0304a940>),
('lemmatizer',
<spacy.lang.en.lemmatizer.EnglishLemmatizer at 0x7fac03068c40>)]
- Установить расширения
# Set extensions to tokens, spans and docs
Token.set_extension('is_envo_term', default=False, force=True)
Token.set_extension("envo_id", default=False, force=True)
Token.set_extension("category", default=False, force=True)
Doc.set_extension("has_envo_ids", getter=has_envo_ids, force=True)
Doc.set_extension("envo_ids", default=[], force=True)
Span.set_extension("has_envo_ids", getter=has_envo_ids, force=True)
Теперь, когда я запускаю текст «культура ткани», он выдает ошибку:
nlp('tissue culture')
ValueError: [E1010] Unable to set entity information for token 0 which is included in more than one span in entities, blocked, missing or outside.
Я знаю, почему произошла ошибка. Это потому, что в базе данных ENVO есть 2 записи для фразы «культура ткани», как показано ниже:
В идеале я бы ожидал, что соответствующий CURIE будет помечен в зависимости от фразы, присутствующей в тексте. Как мне исправить эту ошибку?
Информация о моем спа-центре:
============================== Info about spaCy ==============================
spaCy version 3.0.5
Location *irrelevant*
Platform macOS-10.15.7-x86_64-i386-64bit
Python version 3.9.2
Pipelines en_core_web_sm (3.0.0)
3 ответа
В наши дни это может быть немного поздно, но, немного дополняя ответ Sofie VL , и для всех, кто может быть все еще заинтересован в этом, то, что я (еще один новичок spaCy, смеется) сделал, чтобы избавиться от перекрывающихся промежутков, идет как следует:
import spacy
from spacy.util import filter_spans
# [Code to obtain 'entity']...
# 'entity' should be a list, i.e.:
# entity = ["Carolina", "North Carolina"]
pat_orig = len(entity)
filtered = filter_spans(ents) # THIS DOES THE TRICK
pat_filt =len(filtered)
doc.ents = filtered
print("\nCONVERSION REPORT:")
print("Original number of patterns:", pat_orig)
print("Number of patterns after overlapping removal:", pat_filt)
Важно отметить, что на данный момент я использую самую последнюю версию spaCy, v3.1.1. Кроме того, он будет работать только в том случае, если вы действительно не против удаления перекрывающихся промежутков, но если вы это сделаете, то вы, возможно, захотите на взглянутьэтот поток . Подробнее о filter_spans здесь .
С наилучшими пожеланиями.
С
spacy
v3, вы можете использовать для хранения сущностей, которые могут перекрываться. Эта функция не поддерживается.
Итак, у вас есть два варианта:
- Реализуйте обратный вызов, который отфильтрует результаты сопоставления перед использованием результата для установки. Беглый взгляд на ваш код (и последующие правки) не думаю, что
resolve_substrings
на самом деле разрешает конфликты? В идеалеon_match
функция должна проверить, есть ли конфликты с существующими энтами, и решить, какие из них оставить. - Использовать
doc.spans
вместоdoc.ents
если это работает для вашего варианта использования.
Вместо загрузки модели
nlp = spacy.load("en_core_web_sm")
, попробуйте просто инициализировать объект nlp, используя
nlp = English()
и проверьте. Надеюсь, это сработает.
По сути, заменить
nlp = spacy.load("en_core_web_sm")
с участием
nlp = English()