Выполните пошаговое руководство, в котором мы связываем пользовательские модели распознавания именованных объектов (NER) и извлечения отношений (RE) вместе, чтобы легко извлекать именованные объекты и отношения из текста.
Этот пост призван завершить серию других постов:
Модели распознавания именованных объектов
— Создание пользовательских моделей распознавания именованных объектов
— Создание пользовательских моделей распознавания именованных объектов — преобразователи
Модели извлечения отношений
— Создание пользовательских моделей извлечения отношений — Часть 1
— Создание пользовательских моделей извлечения отношений — Часть 2
Весь процесс управлялся через командную строку с помощью библиотеки extr-ds (Github Repository).
pip install extr-ds
Распознавание именованных объектов
Извлечение именованных объектов довольно просто. Мы загружаем текст в классификатор и фильтруем по показателю достоверности. Мы конвертируем ответ JSON в Entity
.
from extr import Entity, Location from extr.entities import AbstractEntityExtractor from transformers import pipeline class TransformerEntityExtractor(AbstractEntityExtractor): def __init__(self, model_output_checkpoint: str, threshold: float=.5) -> None: self._classifier = pipeline( 'ner', model=model_output_checkpoint, aggregation_strategy='simple' ) self._threshold = threshold def get_entities(self, text: str) -> List[Entity]: valid_observations = filter( lambda r: r['score'] >= self._threshold, self._classifier(text) ) entities = [] for i, response in enumerate(valid_observations): location = Location(start=response['start'], end=response['end']) entities.append( Entity( identifier=i + 1, label=response['entity_group'], text=location.extract(text), location=location ) ) return entities
Классификатор отношений
Классификатор ожидает, что наш текст будет аннотирован, e1 -> <e1:TEAM></...>, e2 -> <e2:QUANTITY></..>
. Мы можем взять извлеченные объекты и построить этот аннотированный текст для каждой комбинации пар e1, e2, что делает для нас BaseRelationLabeler
. Отсюда нам просто нужно передать этот текст в наш классификатор и отфильтровать по показателю достоверности.
Этот метод вернет все ярлыки, включая NO_RELATION
.
from extr import Entity, Relation from extr_ds.models import RelationLabel from extr_ds.labelers.relation import BaseRelationLabeler from transformers import pipeline class TransformerRelationLabeler(BaseRelationLabeler): def __init__(self, model_output_checkpoint: str, relation_builder: RelationBuilder, threshold: float=.5): super().__init__(relation_builder) self._classifier = pipeline( 'text-classification', model=model_output_checkpoint ) self._threshold = threshold def label(self, text: str, entities: List[Entity]) -> List[RelationLabel]: relation_labels = super().label(text, entities) observations = self._classifier( list(map(lambda label: label.sentence, relation_labels)) ) labels = [] for relation_label, response in zip(relation_labels, observations): if response['score'] < self._threshold: continue relation = relation_label.relation relation_label.relation = Relation( response['label'], e1=relation.e1, e2=relation.e2 ) labels.append(relation_label) return labels
Извлечение именованных объектов и отношений
RelationClassification
позаботится об остальном. Вызов .totuple(exclude=['NO_RELATION'])
наших результатов помогает отфильтровать любую связь, которая была классифицирована как несуществующая.
from extr_ds.labelers.relation import RelationBuilder, \ RelationClassification ner_model_output_checkpoint = 'transformers/nfl_pbp_token_classifier' re_model_output_checkpoint = 'transformers/nfl_pbp_relation_classifier' classifier = RelationClassification( TransformerEntityExtractor(ner_model_output_checkpoint), TransformerRelationLabeler( re_model_output_checkpoint, relation_builder=RelationBuilder(relation_formats=[ ('TEAM', 'QUANTITY', 'NO_RELATION') ]), ), additional_relation_labelers=[] ) text = '(6:51 - 1st) (Shotgun) ' + \ 'P.Mahomes scrambles right end to LAC 34 for 2 yards (S.Joseph; K.Van Noy). ' + \ 'FUMBLES (S.Joseph), and recovers at LAC 34.' results = classifier.label(text) entities, relation_labels = results.totuple(exclude=['NO_RELATION'])
Извлеченные именованные объекты
[ <Entity label="TIME" text="6:51" span=(1, 5)>, <Entity label="PERIOD" text="1st" span=(8, 11)>, <Entity label="FORMATION" text="Shotgun" span=(14, 21)>, <Entity label="PLAYER" text="P.Mahomes" span=(23, 32)>, <Entity label="EVENT" text="scrambles" span=(33, 42)>, <Entity label="DIRECTION" text="right" span=(43, 48)>, <Entity label="TEAM" text="LAC" span=(56, 59)>, <Entity label="QUANTITY" text="34" span=(60, 62)>, <Entity label="QUANTITY" text="2" span=(67, 68)>, <Entity label="PLAYER" text="S.Joseph" span=(76, 84)>, <Entity label="PLAYER" text="K.Van Noy" span=(86, 95)>, <Entity label="PLAYER" text="S.Joseph" span=(107, 115)>, <Entity label="TEAM" text="LAC" span=(134, 137)>, <Entity label="QUANTITY" text="34" span=(138, 140)> ]
Извлеченные отношения
[ <RelationLabel sentence="(6:51 - 1st) (Shotgun) P.Mahomes scrambles right end to <e1:TEAM>LAC</e1:TEAM> <e2:QUANTITY>34</e2:QUANTITY> for 2 yards (S.Joseph; K.Van Noy). FUMBLES (S.Joseph), and recovers at LAC 34." relation=<Relation e1="LAC" r="is_spot_of_ball" e2="34"> >, <RelationLabel sentence="(6:51 - 1st) (Shotgun) P.Mahomes scrambles right end to LAC 34 for 2 yards (S.Joseph; K.Van Noy). FUMBLES (S.Joseph), and recovers at <e1:TEAM>LAC</e1:TEAM> <e2:QUANTITY>34</e2:QUANTITY>." relation=<Relation e1="LAC" r="is_spot_of_ball" e2="34"> > ]