Конвертировать формат NER SpaCy в формат IOB - PullRequest
1 голос
/ 14 января 2020

У меня есть данные, которые уже помечены в формате SpaCy. Например:

("Who is Shaka Khan?", {"entities": [(7, 17, "PERSON")]}),
("I like London and Berlin.", {"entities": [(7, 13, "LOC"), (18, 24, "LOC")]})

Но я хочу попробовать обучить его любой другой модели NER, такой как BERT-NER, для которой вместо этого требуется пометка IOB. Есть ли какой-либо код преобразования из формата данных SpaCy в IOB?

Спасибо!

Ответы [ 2 ]

2 голосов
/ 14 января 2020

Это тесно связано и в основном скопировано с { ссылка }, см. Также комментарии в комментариях:

import spacy
from spacy.gold import biluo_tags_from_offsets

TRAIN_DATA = [
    ("Who is Shaka Khan?", {"entities": [(7, 17, "PERSON")]}),
    ("I like London and Berlin.", {"entities": [(7, 13, "LOC"), (18, 24, "LOC")]}),
]

nlp = spacy.load('en_core_web_sm')
docs = []
for text, annot in TRAIN_DATA:
    doc = nlp(text)
    tags = biluo_tags_from_offsets(doc, annot['entities'])
    # then convert L->I and U->B to have IOB tags for the tokens in the doc
0 голосов
/ 14 января 2020

Боюсь, вам придется написать свое собственное преобразование, потому что кодирование IOB зависит от того, какую токенизацию будет использовать предварительно обученная модель представления (BERT, RoBERTa или любая другая предварительно обученная модель по вашему выбору).

Формат SpaCy определяет диапазон символов объекта, то есть

"Who is Shaka Khan?"[7:17]

вернет "Shaka Khan". Вам необходимо сопоставить это с токенами, используемыми предварительно обученной моделью.

Вот примеры того, как разные модели токенизируют пример предложения, когда вы использовали Трансформаторы Huggingface .

  • BERT: ['Who', 'is', 'S', '##hak', '##a', 'Khan', '?']
  • Роберт: ['Who', '_is', '_Sh', 'aka', '_Khan', '?']
  • X LNet: ['▁Who', '▁is', '▁Shak', 'a', '▁Khan', '?']

Зная, как работает токенизатор, вы можно реализовать преобразование. Нечто подобное может работать для токенизации BERT.

entities = [(7, 17, "PERSON")]}
tokenized = ['Who', 'is', 'S', '##hak', '##a', 'Khan', '?']

cur_start = 0
state = "O" # Outside
tags = []
for token in tokenized:
    # Deal with BERT's way of encoding spaces
    if token.startswith("##"):
        token = token[2:]
    else:
        token = " " + token

    cur_end = cur_start + len(token)
    if state == "O" and cur_start < entities[0][0] < cur_end:
        tags.append("B-" + entitites[0][2])
        state = "I-" + entitites[0][2]
    elif state.startswith("I-") and cur_start < entities[0][1] < cur_end:
        tags.append(state)
        state = "O"
        entities.pop(0)
    else:
        tags.append(state)
    cur_start = cur_end

Обратите внимание, что фрагмент может сломаться, если один токен BERT будет содержать конец одной сущности и начало следующей. Маркер также не различает guish сколько пробелов (или других пробелов) было в исходной строке, это также потенциальный источник ошибок.

...