spaCy - токенизация переносимых слов - PullRequest
0 голосов
/ 25 сентября 2019

Добрый день, ТАК,

Я пытаюсь пост-обрабатывать дефисные слова, которые токенизируются в отдельные токены, когда они предположительно были одним токеном.Например:

Example:

Sentence: "up-scaled"
Tokens: ['up', '-', 'scaled']
Expected: ['up-scaled']

На данный момент мое решение состоит в том, чтобы использовать средство сопоставления:

matcher = Matcher(nlp.vocab)
pattern = [{'IS_ALPHA': True, 'IS_SPACE': False},
           {'ORTH': '-'},
           {'IS_ALPHA': True, 'IS_SPACE': False}]

matcher.add('HYPHENATED', None, pattern)

def quote_merger(doc):
    # this will be called on the Doc object in the pipeline
    matched_spans = []
    matches = matcher(doc)
    for match_id, start, end in matches:
        span = doc[start:end]
        matched_spans.append(span)
    for span in matched_spans:  # merge into one token after collecting all matches
        span.merge()
    #print(doc)
    return doc

nlp.add_pipe(quote_merger, first=True)  # add it right after the tokenizer
doc = nlp(text)

Однако это приведет к ожидаемой проблеме ниже:

Example 2:

Sentence: "I know I will be back - I had a very pleasant time"
Tokens: ['i', 'know', 'I', 'will', 'be', 'back - I', 'had', 'a', 'very', 'pleasant', 'time']
Expected: ['i', 'know', 'I', 'will', 'be', 'back', '-', 'I', 'had', 'a', 'very', 'pleasant', 'time']

Есть ли способ, где я могу обрабатывать только слова, разделенные дефисами, которые не имеют пробелов между символами?Таким образом, такие слова, как «масштабированный», будут сопоставлены и объединены в один токен, но не «… назад - я ..»

Большое спасибо

РЕДАКТИРОВАТЬ: У меня естьпопробовал решение, опубликованное: Почему spaCy не сохраняет дефисы внутри слова во время токенизации, как это делает Stanford CoreNLP?

Однако я не использовал это решение, потому что оно привело к неправильному токенизациислова с апострофами (') и числа с десятичными числами:

Sentence: "It's"
Tokens: ["I", "t's"]
Expected: ["It", "'s"]

Sentence: "1.50"
Tokens: ["1", ".", "50"]
Expected: ["1.50"]

Именно поэтому я использовал Matcher вместо того, чтобы пытаться редактировать регулярное выражение.

1 Ответ

1 голос
/ 26 сентября 2019

Matcher не совсем подходящий инструмент для этого.Вместо этого вы должны изменить токенизатор.

Если вы хотите сохранить способ обработки всего остального и изменить поведение дефисов, вам следует изменить существующий шаблон инфикса и сохранить все остальные параметры.Текущее английское определение шаблона инфикса здесь:

https://github.com/explosion/spaCy/blob/58533f01bf926546337ad2868abe7fc8f0a3b3ae/spacy/lang/punctuation.py#L37-L49

Вы можете добавлять новые шаблоны без определения пользовательского токенайзера, но нет способа удалить шаблон без определения пользовательского токенизатора.Итак, если вы закомментируете шаблон дефиса и определите пользовательский токенайзер:

import spacy
from spacy.tokenizer import Tokenizer
from spacy.lang.char_classes import ALPHA, ALPHA_LOWER, ALPHA_UPPER, CONCAT_QUOTES, LIST_ELLIPSES, LIST_ICONS
from spacy.util import compile_infix_regex

def custom_tokenizer(nlp):
    infixes = (
        LIST_ELLIPSES
        + LIST_ICONS
        + [
            r"(?<=[0-9])[+\-\*^](?=[0-9-])",
            r"(?<=[{al}{q}])\.(?=[{au}{q}])".format(
                al=ALPHA_LOWER, au=ALPHA_UPPER, q=CONCAT_QUOTES
            ),
            r"(?<=[{a}]),(?=[{a}])".format(a=ALPHA),
            #r"(?<=[{a}])(?:{h})(?=[{a}])".format(a=ALPHA, h=HYPHENS),
            r"(?<=[{a}0-9])[:<>=/](?=[{a}])".format(a=ALPHA),
        ]
    )

    infix_re = compile_infix_regex(infixes)

    return Tokenizer(nlp.vocab, prefix_search=nlp.tokenizer.prefix_search,
                                suffix_search=nlp.tokenizer.suffix_search,
                                infix_finditer=infix_re.finditer,
                                token_match=nlp.tokenizer.token_match,
                                rules=nlp.Defaults.tokenizer_exceptions)


nlp = spacy.load("en")
nlp.tokenizer = custom_tokenizer(nlp)
print([t.text for t in nlp("It's 1.50, up-scaled haven't")])
# ['It', "'s", "'", '1.50', "'", ',', 'up-scaled', 'have', "n't"]

Вам необходимо указать текущие настройки префикса / суффикса / token_match при инициализации нового токенизатора, чтобы сохранить существующее поведение токенайзера.См. Также (для немецкого языка, но очень похоже): https://stackoverflow.com/a/57304882/461847

Отредактировано, чтобы добавить (так как это кажется излишне сложным, и вы действительно должны быть в состоянии переопределить шаблоны инфиксов без загрузкисовершенно новый пользовательский токенизатор):

Если вы только что загрузили модель (для v2.1.8) и еще не вызвали nlp(), вы также можете просто заменить infix_re.finditer, не создавая пользовательский токенизатор:

nlp = spacy.load('en')
nlp.tokenizer.infix_finditer = infix_re.finditer

Существует ошибка кеширования, которая, мы надеемся, должна быть исправлена ​​в v2.2, которая позволит правильно работать в любой точке, а не только с недавно загруженной моделью.(В противном случае поведение очень запутанное, поэтому создание пользовательского токенизатора было лучшей рекомендацией общего назначения для v2.1.8.)

...