Классификация текстов – одна из основных задач в области обработки естественного языка (NLP – от англ. Natural Language Processing). Она имеет широкий спектр применений: определение тональности отзывов, категоризация новостей, фильтрация спама и т.д. В этой статье вы узнаете, как реализовать классификатор текстов при помощи библиотеки spaCy, а также несколько полезных лайфхаков, которые помогут ускорить обработку. Эта публикация будет полезна всем, кто начал изучать машинное обучение и мало знаком со spaCy .
spaCy – это open-source NLP-библиотека с поддержкой более 70 языков. Она написана на Cython и эффективно справляется с обработкой больших объемов данных.
Установка
Начнем с установки библиотеки и одной из предобученных моделей. Для каждого языка в spaCy доступны несколько моделей. В большинстве случаев есть три варианта: малая, средняя и большая – они обозначаются суффиксами «_sm», «_md», «_lg» соответственно. Также есть модели с суффиксом «_trf», которые поддерживают пайплайны трансформеров, но для русского языка они пока не доступны.
В качестве примера загрузим среднюю модель для русского языка:
pip install spacy
python -m spacy download ru_core_news_md
Подробнее об установке читайте в документации.
Инициализация модели
Модель spaCy – это последовательность компонентов обработки. Когда вы передаете в модель текст, spaCy токенизирует его и возвращает объект Doc. Затем этот Doc последовательно обрабатывается компонентами модели, такими как лемматизатор, синтаксический анализатор или распознаватель именованных сущностей. Каждый компонент возвращает обработанный Doc, который затем передается следующему компоненту.
Обычно переменную с моделью называют «nlp». Посмотреть последовательность компонентов можно при помощи следующего кода:
import spacy
nlp = spacy.load("ru_core_news_md")
print(nlp.pipe_names)
>>>['tok2vec', 'morphologizer', 'parser', 'attribute_ruler', 'lemmatizer', 'ner']
Описание компонентов можно найти здесь.
Соответственно, чем больше компонентов задействовано в пайплайне, тем больше потребуется времени на обработку данных. Например, для решения задач классификации в большинстве случаев достаточно токенизации и лемматизации. При этом незадействованные компоненты лучше убрать. Это позволит значительно ускорить обработку.
Исключить ненужные компоненты можно на этапе инициализации модели. Ниже пример кода загрузки модели, которая только токенизирует текст и позволяет получить леммы токенов.
nlp = spacy.load('ru_core_news_md', exclude=['tok2vec', 'morphologizer',
'parser', 'attribute_ruler', 'ner'])
print(nlp.pipe_names)
>>>['lemmatizer']
Если вам необходима только токенизация, то необязательно исключать компоненты модели. Лучшим решением будет использовать метод nlp.make_doc, который токенизирует текст, но не передает его дальше компонентам пайплайна.
Text2Doc
Spacy не работает напрямую со строками, поэтому все тексты необходимо предварительно преобразовать в объекты Doc.
doc = nlp('some text')
print(type(doc))
>>>spacy.tokens.doc.Doc
При решении задач классификации, обычно мы имеет дело с больших количеством текстов. При последовательной обработке данных разработчики spaCy рекомендуют использовать метод nlp.pipe. Он обрабатывает тексты как поток и возвращает объекты Doc. Этот способ гораздо быстрее, чем вызывать nlp для каждого текста отдельно.
Важно! nlp.pipe - это генератор, поэтому, чтобы получить список документов, не забудьте вызвать метод list вокруг него.
docs = list(nlp.pipe(LOTS_OF_TEXTS, n_process=3, batch_size=1000))
Метод nlp.pipe поддерживает многопоточность, и вы можете задать значения n_process и batch_size. Оптимальная настройка будет зависеть от компонентов пайплайна, длины текстов, количества доступных потоков и объема памяти. Согласно документации, для небольших датасетов оптимальным решением будет меньшее количество потоков, но с бОльшим размером батча. По умолчанию batch_size равен 256.
Предобработка
Ниже пример функции, которая очищает текст от стоп-слов, пунктуации, лишних пробелов, чисел и возвращает лемматизированный текст.
def lemmatize(doc):
words = []
for token in doc:
if (token.is_stop != True) and (token.is_punct != True) and\
(token.is_space != True) and (token.is_digit != True):
words.append(token.lemma_)
return ' '.join(words)
Минимальную предобработку можно реализовать одной короткой функцией. В одном из проектов я классифицировал пресс-релизы. Сами тексты были высокого качества, поэтому такой функции было достаточно. К сожалению, чаще приходится работать с куда более «грязными» данными. В этих случаях, конечно, потребуется дополнительная предобработка при помощи регулярных выражений - например, чтобы убрать из текста эмодзи.
Поскольку все манипуляции проходят с объектами Doc, лемматизация нескольких тысяч текстов может занять пару секунд.
Сплит данных и DocBin
Теперь данные готовы для разбивки на обучающую, валидационную и тестовую выборки. Это можно сделать, например, при помощи срезов или метода train_test_split из библиотеки sklearn. Но прежде, чем передать их в модель, данные нужно преобразовать в DocBin(файл с расширением .spacy), который spaCy использует для обучения.
Ниже пример функции для создания файлов DocBin.
def create_docbin(data, outfile):
db = DocBin()
categories = data['category'].unique()
for i in range(data.shape[0]):
doc = nlp.make_doc(data['clear_text'][i])
doc.cats = {category: 0 for category in categories}
doc.cats[data['category'][i]] = 1
db.add(doc)
db.to_disk(outfile)
В коде выше атрибуту cats присваивается значение 0 для всех категорий, кроме правильной, которая получает значение 1.
Подготовка к обучению
Порядок обучения моделей spaCy отличается от привычных «фит-предикт». Во-первых, перед запуском обучения необходимо сгенерировать конфигурационный файл с гиперпараметрами. Во-вторых, обучение запускается через CLI (интерфейс командной строки).
Конфигурационный файл
Сгенерировать и скачать базовый конфигурационный файл можно на сайте spaCy.
Здесь необходимо выбрать язык и обучаемый компонент, в данном случае texcat для классификации. В этом примере будет использоваться простейшая из доступных архитектур bag-of-words.
После получения base_config.cfg, нужно выполнить команду init fill-config, чтобы сгенерировать основной конфигурационный файл. Значения гиперпараметров будут заполнены по умолчанию. Как уверяют разработчики spaСy, параметры по умолчанию уже включают в себя оптимальные значения для эффективного решения NLP-задач, но вы всегда можете внести любые корректировки.
Выполним генерацию основного конфигурационного файла при помощи следующей команды:
python -m spacy init fill-config base_config.cfg config.cfg
Файл config.cfg определяет архитектуру и гиперпараметры модели. Здесь стоит обратить внимание на алгоритм прекращения обучения. Модель останавливает обучение, если в течение 1 600 итераций скор не улучшается, либо общее количество итераций достигает 20 000. При этом по умолчанию количество эпох установлено на 0 и игнорируется.
После завершения обучения будут сохранены последняя и лучшая обученные модели.
Обучение
Для запуска обучения необходимо выполнить следующую команду:
python -m spacy train config.cfg --paths.train ./train.spacy --paths.dev ./dev.spacy --output model
Если все сделано правильно, вы увидите примерно такой вывод, где:
E - эпохи;
# - итерации обучения;
LOSS TEXTCAT - функция потерь;
CATS_SCORE - макро-F1;
SCORE - CATS_SCORE, нормализованный от 0 до 1.
Оценка модели
В spaCy есть встроенный скрипт оценки качества модели. При помощи Evaluate можно узнать ROC-AUC, точность (P), полноту (R) и F1. Подробный отчет с метриками будет сохранен в JSON-файле, путь к которому вы укажете при запуске оценки.
python -m spacy evaluate model/model-best/ --output metrics.json ./test.spacy
Предикт
Чтобы получить предсказание классов, передайте в обученную модель текст и затем обратитесь к атрибуту cats объекта Doc.
Заключение
В этой статье мы рассмотрели одну из доступных в spaCy архитектур классификации текстов. Узнать больше о возможностях этой библиотеки можно в документации. Также советую пройти мини-курс на сайте библиотеки.
Jupyter notebook с кодом доступен по ссылке.