Исправьте POS-теги для номеров, замененных на ## в пространстве - PullRequest
6 голосов
/ 10 февраля 2020

Набор данных гигаворда - это огромный корпус, используемый для обучения моделей абстрактного суммирования. Он содержит сводки, подобные этим:

spain 's colonial posts #.## billion euro loss
taiwan shares close down #.## percent

Я хочу обработать эти сводки с помощью spacy и получить правильный тег pos для каждого токена. Проблема заключается в том, что все числа в наборе данных были заменены знаками #, которые не классифицируются как числа ( NUM ), а как другие теги.

>>> import spacy
>>> from spacy.tokens import Doc
>>> nlp = spacy.load("en_core_web_sm")
>>> nlp.tokenizer = lambda raw: Doc(nlp.vocab, words=raw.split(' '))
>>> text = "spain 's colonial posts #.## billion euro loss"
>>> doc = nlp(text)
>>> [(token.text, token.pos_) for token in doc]
[('spain', 'PROPN'), ("'s", 'PART'), ('colonial', 'ADJ'), ('posts', 'NOUN'), ('#.##', 'PROPN'), ('billion', 'NUM'), ('euro', 'PROPN'), ('loss', 'NOUN')]

Существует ли способ настройки тег POS, чтобы он классифицировал все токены, которые состоят только из знака # и знака, как числа?

Я знаю, что вы заменили тег POS Spacy своим собственным или настроили его для своего домена с дополнительными данными, но У меня нет помеченных данных об обучении, где все числа заменены на #, и я хотел бы изменить тегер как можно меньше. Я бы предпочел иметь регулярное выражение или фиксированный список токенов, которые всегда распознаются как числа.

1 Ответ

1 голос
/ 12 февраля 2020

Как насчет замены # на di git?

В первой версии этого ответа я выбрал di git 9, потому что он напоминает мне номер COBOL c полевые форматы, которые я использовал около 30 лет go ... Но затем я взглянул на набор данных и понял, что для правильной обработки НЛП нужно разобраться хотя бы в двух вещах:

  • порядковые номера (1-й, 2-й, ...)
  • даты

Порядковые цифры требуют специальной обработки для любого выбора di git, но di git 1 дает разумные даты, за исключением года (конечно, 1111 может или не может быть истолкован как действительный год, но давайте будем осторожнее). 11/11/2020 явно лучше, чем 99/99/9999 ...

Вот код:

import re

ic = re.IGNORECASE
subs = [
    (re.compile(r'\b1(nd)\b', flags=ic), r'2\1'),  # 1nd -> 2nd
    (re.compile(r'\b1(rd)\b', flags=ic), r'3\1'),  # 1rd -> 3rd
    (re.compile(r'\b1(th)\b', flags=ic), r'4\1'),  # 1th -> 4th
    (re.compile(r'11(st)\b', flags=ic), r'21\1'),  # ...11st -> ...21st
    (re.compile(r'11(nd)\b', flags=ic), r'22\1'),  # ...11nd -> ...22nd
    (re.compile(r'11(rd)\b', flags=ic), r'23\1'),  # ...11rd -> ...23rd
    (re.compile(r'\b1111\b'), '2020')              # 1111 -> 2020
]

text = '''spain 's colonial posts #.## billion euro loss
#nd, #rd, #th, ##st, ##nd, ##RD, ##TH, ###st, ###nd, ###rd, ###th.
ID=#nd#### year=#### OK'''

text = text.replace('#', '1')
for pattern, repl in subs:
    text = re.sub(pattern, repl, text)

print(text)
# spain 's colonial posts 1.11 billion euro loss
# 2nd, 3rd, 4th, 21st, 22nd, 23RD, 11TH, 121st, 122nd, 123rd, 111th.
# ID=1nd1111 year=2020 OK

Если предварительная обработка корпуса преобразует любой ди git в # в любом случае, вы не потеряете информацию с этим преобразованием. Некоторая «истинная» # станет 1, но это, вероятно, будет незначительной проблемой по сравнению с числами, которые не распознаются как таковые. Кроме того, при визуальном осмотре около 500000 строк набора данных я не смог найти ни одного кандидата для «истинного» * ​​1024 *.

Примечание: \b в приведенных выше регулярных выражениях обозначает для «границы слова», т. е. границы между символом \w (слово) и символом \W (не словом), где символом слова является любой алфавитный символ c (дополнительная информация здесь ). \1 в замене обозначает первую группу, то есть первую пару круглых скобок (дополнительная информация здесь ). При использовании \1 регистр всего текста сохраняется, что было бы невозможно при замене строк, таких как 2nd. Позже я обнаружил, что ваш набор данных нормализован для всех строчных букв, но я решил оставить его обобщенным c.

Если вам нужно получить текст с # s обратно из частей речи, это просто

token.text.replace('0','#').replace('1','#').replace('2','#').replace('3','#').replace('4','#')
...