X

Обработка естественного языка с Python — NLP (Natural Language Processing)

Обработка естественного языка с Python - NLP (Natural Language Processing)

Добрый день. Сегодня мы продвинемся в изучении машинного обучения и затронем тему обработки естественного языка (NLP). Сегодня мы будем использовать множество инструментов на примере практической задачи.

Подпишись на группу Вконтакте и Телеграм-канал. Там еще больше полезного контента для программистов.

А на моем YouTube-канале ежедневно по будням проходят обучающие стримы по программированию. Подписывайся!

Задача

Имеется датасет, который вмещает в себя описание и название направления некой математической области.

Сам датасет: https://www.kaggle.com/extralime/math-lectures.

Чтобы скачать, нам необходимо зарегистрироваться на Kaggle (наверняка каждый там есть), скачать архив и распаковать его.

Нам необходимо научиться определять, про какую отросль математики идёт речь. Мы будем использовать метод векторизации, то есть представление предложения в качестве массива, который вмещает в себя кол-во определённых слов. Также присутствует визуализация данных.

Обработка естественного языка. Начало работы

Уже взглянув на список импортов, мы можем понять, что здесь не так всё просто, как в обычных задачах классификации или регрессии.

Рассмотрим самые не очевидные места:

  • string — модуль стандартной библиотеки Python для расширенной работы со строками
  • seaborn — надстройка над matplotlib (здесь нужен лишь для установки стиля графиков)
  • CountVectorizer — класс, с помощью которого мы будем проводить векторизацию

Предобработка данных

Загрузим наш датасет в объект DataFrame:

Убедимся, что у нас отсутствуют пустые значения:

Мы будем предсказывать метку класса, так что закодируем значения столбца label в численные значения:

Создаём хэш-таблицу, а также обратную к ней таблицу (позже она нам пригодится).

Series.unique() — возвращает список уникальных значений столбца Series.

Примечание: в моих фрагментах кода редко можно заметить комментарии, потому что я излагаю свои мысли в статье, однако здесь я закомментировал то, каким именно образом устроен словарь (как в C#, к примеру). Это не обязательно, однако я взял это к себе в привычку, и Вам советую. Значительно помогает облегчить работу с неизвестными словарями, повышая качество кода.

Получаем такой словарь:

С помощью метода Series.map() кодируем значения:

Series.map(dict) — заменяет значения столбца теми значениями, которые указаны в словаре.

Теперь создадим массив через векторизатор, который мы ранее импортирвали:

Также создаём обратную таблицу, дальше нам она понадобится. Векторизатор возвращает CSR-матрицу (документация здесь).

stop_words — список стоп-слов (они просто удалятся). В нашем случае мы указываем список слишком часто встречающихся английских слов. К сожалению, не все такие слова удаляются, об этом далее будет. Хотите установить свой список стоп-слов? В этом вопросе на StackOverFlow (сегодня у него особенный дизайн) описано, как это сделать.

vectorizer.get_feature_names() — получаем список всех слов, которые будут использованы при кодировании.

vectorizer.vocabulary_ — словарь со значениями, на подобии того, что мы делали выше с метками классов.

vectorizer.fit_transform() — метод «обучает» векторизатор и сразу возвращает результат для данного набора слов.

Визуализация NLP

Теперь мы визуализируем самые часто встречающиеся слова (это не совсем так) в разных «категориях» текста.

Создадим функцию, которая будет векторизировать отдельные фрагменты DataFrame’а:

В конце мы совершаем вызов функции для всего набора, чтобы понять, что мы будем получать на выходе.

Нам также понадобится функция, которая будет возвращать индексы самых часто-встречающихся N элементов (функция была выбрана из ответов на вопрос на Qaru):

Основной частью нашей визуализации станет хэш-таблица, ключём которой будет закодированное значение категории, а значениями — список часто-встречающихся слов с 90 по 100 позицию.

Почему с 90 до 100, а не с 0 до 10? Как я указал выше, не все «стоп-слова» удаляются при векторизации. Потому первые 10 слов совпадали на 70-80% для каждой категории. С 90-ой по сотую позиции слов значительно отличаются, потому мы будем использовать именно эти слова.

np.array.sum() — суммирует элемент массива, возвращая новый. В отличии от обычной функции sum, мы можем указать ось, в нашем случае для суммирования столбцов.

Никаких новых методов здесь нет, кроме вышеупомянотого, стоит только внимательно ознакомиться со всеми функциями.

Вывод таков:

Здесь разные значения «частоты» слов для разных категорий. Это объясняется тем, что датасет не равномерный, и кол-во примеров для одной категории не совпадает с кол-вом примеров для другой.

Теперь построим множество «баров»:

plt.sublots(rows, columns, figsize) — добавляет множество графиков, также можно указать значение размера общей фигуры в дюймах.

axes.flat — т.к. axes изначально имеет форму (5, 2), для облегчения работы с ними мы будем использовать одномерный массив. Получить такой массив из массива с любой формой можно с помощью аттрибута flat.

Axes.set_ylabel(string) — установка подпись для оси y.

Axes.set_xticks(array) — устанавливает нужный нам список значений по оси x.

Axes.set_xticklabels(array) — устанавливает список подписей к оси x.

Axes.set_title(string) — устанавливает заголовок для «графика».

Axes.bar() — построение столбчастой диаграммы.

Магическая команда IPython %matplotlib inline даёт нам вывод графика прямо в блокнот:

К сожалению, в статье ничего подробного не рассмотреть, выполните пример сами, чтобы получить более четкую картинку.

Модель машинного обучения

Сразу хочется сказать, что векторизация — далеко не самый лучший подход при работе с текстом, потому даже на среднюю обобщающую способность модели нам надеяться не приходится.

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

Слабый ученик в виде обычного дерева — так себе затея, лучше использовать ансамбль. Мой выбор пал на случайный лес:

Перед этим, конечно же, делим исходные данные на тестовые и тренировочные в пропорции 1:9:

Как мы можем заметить, обобщающая способность модели находится на очень низком уровне.

Но! Т.к. у нас имеются 10 классов, то вероятность чистого угадывания 0.1. Наша точность выше, чем 0.1, что говорит о том, что наша модель всё-таки вывела определённые правила для прогнозирования.

Однако, для многих классов слова часто повторяются, потому спрогнозировать нужный нам класс в такой ситуации очень сложно.

Конечно, мы можем поиграться с параметрами модели, однако значительного прироста это нам не принесёт, к сожалению.

Заключение

Сегодня мы разобрали такой метод работы с текстом, как векторизация. Мы изучили часто встречающиеся слова, визуализировали их, проанализировали некоторые «подводные» камни, а также попытались построит модель машинного обучения, однако это там не удалось в виду неактуальности подхода векторизации в этом контексте.

Весь код из статьи здесь.

Также рекомендую прочитать статью NumPy vs Python и подпишитесь на группу ВКонтакте, Телеграм и YouTube-канал. Там еще больше полезного и интересного для разработчиков.

Categories: Python
shwan :Программист .NET