Machine Learning, NLP

«Нормальная» подготовка текстов к ML

Время прочтения: 3 мин.

Для работы с текстом методами ML и NLP нам нужно провести предобработку исходных данных. Методов для этого существует несколько, расскажем о лемматизации или приведении словоформы к лемме — её нормальной (словарной) форме.

Задача лемматизации может решаться несколькими путями, основные из них:

  1. Алгоритмический. Может привести к нормальной форме любое слово, но не исключены ошибки. Пример: алгоритмы, использующие стеммер Портера.
  2. С использованием базы данных лемм и словоформ. Исключает ошибки, но может не найти слово и вернуть пустой результат.
  3. Комбинированный. Самый эффективный, в случае если слова нет в базе, алгоритм пытается вычислить лемму сам.

Для языка python разработано несколько библиотек для лемматизации. К сожалению, большинство из них не поддерживают русский язык, а если и поддерживают, то дают неудовлетворительный результат. К таким библиотекам можно отнести nltk(nltk.stem.snowball), spacy (spacy.lang.ru).

Более подробно мы разберём две библиотеки, хорошо работающие с русским языком комбинированным методом, а именно: открытую pymorphy2 и закрытую разработку яндеса pymystem3.

pymorphy2 написан полностью на python, а pymystem3 является исполняемым файлом с консольным интерфейсом, для работы с ним в python будем использовать обёртку Mystem. Такие различия в архитектуре наталкивают на мысли о разнице в производительности с различными объемами текста и на различных ОС. Проверим наши догадки пролемматизируя книгу Анджея Сапковского «Ведьмак: Последнее желание»!

Считаем книгу, уберем знаки препинания и слова короче 4-х букв:

book = []
with open('./sapkovskiy_andzhey_poslednee_zhelanie.txt', 'r', encoding='utf-8', newline='\n', errors='ignore') as resf:
    line = resf.readline()
    while line:
        line = re.sub(r'[^\w\s]', '', line)
        line = re.sub(r'[\s]', ' ', line)
        line = line.split()
        line = list(filter(lambda x: len(x) > 3, line))
        if len(line) > 0:
            book.append(line)
        line = resf.readline()

Пример замера скорости лемматизации:

start_time = datetime.now()
res_book = []
morph = pymorphy2.MorphAnalyzer()
for line in book:
    res_line = []
    for word in line:
        res_line.append(morph.parse(word)[0].normal_form)
    res_book.append(' '.join(res_line))

print(str(datetime.now() - start_time))
open('./pymorphy2.txt', 'w', encoding='utf-8').writelines("\n".join(res_book))

В качестве тестового стенда будет выступать ПК с intel i5-4430, 16 Gb Ram, Windows 10. В качестве Linux будем использовать WSL на этом же ПК.

Сравнение скорости работы:

 Windows 10Linux (WSL)
pymorphy2 (по словам)2с.9 с.
pymystem3 (по словам)9 мин. 11 с.7 с.
pymystem3 (по предложениям)25 с.4 с.
pymystem3 (по 50000 слов)0,007 с.0,003 с.

Обратите внимание на изменение времени выполнения pymystem3 от обрабатываемого объёма за один вызов, это связанно с архитектурой решения, требующей запуск консольного приложения, что очень дорого по времени.

pymorphy2, в свою очередь, умеет работать только с одним словом за один вызов, поэтому в таблице представлен только этот вариант.

Сравнение результатов:

Оригиналpymorphy2pymystem3
Она пришла под утро.прийти утроприходить утро
– Я бы хотел здесь.хотеть здесьхотеть здесь
– Что подать?податьподавать

Выводы

Обе библиотеки хороши в работе, но если у вас на входе достаточно большой объем данных лучше будет обратить внимание на pymystem3 и, по возможности, использовать Linux, если объём не большой, то хорошим выбором станет pymorphy2.

Советуем почитать