Реализация логистической регрессии в PyTorch для классификации настроений в данных Yelp Restaurant Review, где входной функцией является пакет слов (BOW)

Логистическая регрессия для классификации данных обзоров по различным настроениям будет реализована в среде глубокого обучения PyTorch. Это эксперимент, чтобы познакомиться с основными функциями фреймворка PyTorch, например, как определить нейронную сеть? а как настроить гиперпараметры модели в PyTorch? будет рассмотрен в этом посте. Сравнение с методами, в которых использовался Decision Tree Classifier с различными функциями ввода, такими как BOW, TF-IDF, Word2Vec и Doc2Vec в моих предыдущих сообщениях, будет сделано в конце вкратце.

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

Загрузите данные

Набор данных для обзора ресторанов Yelp можно скачать с их сайта, а формат представленных данных - JSON. Предоставленные данные на самом деле не в правильном формате json, читаемом для python. Каждая строка является словарем, но для того, чтобы это был допустимый формат json, квадратная скобка должна быть в начале и в конце файла с добавлением , в конце каждой строки. Определите INPUT_FOLDER как путь к папке в вашем локальном каталоге, где находится файл yelp review.json. Объявите OUTPUT_FOLDER как путь, куда вы хотите записать вывод следующей функции. Загрузка данных json и запись первых 100000 строк выполняется с помощью следующей функции:

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

Изучение данных

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

Вывод:

После того, как данные доступны, выполняется сопоставление звездочек с настроениями и строится распределение для каждого настроения.

Вывод:

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

  1. Положительных: 1
  2. Отрицательный: -1
  3. Нейтрально: 0

Количество строк неравномерно распределяется по этим трем настроениям. В этом посте проблема несбалансированных классов не рассматривается, поэтому написана простая функция для получения нескольких верхних записей для каждого настроения. В этом примере top_n равно 10000, что означает, что всего будет взято 30 000 записей.

Вывод:

Как предварительно обработать текстовые данные?

Предварительная обработка включает в себя множество шагов, таких как токенизация, удаление стоп-слов, выделение корней / лемматизация и т. Д. Эти часто используемые методы были подробно объяснены в моей предыдущей публикации BOW. Здесь объясняются только необходимые шаги на следующем этапе.

Зачем нужна предварительная обработка этого текста? - Не вся информация полезна при прогнозировании или классификации. Уменьшение количества слов уменьшит размер ввода вашей модели. Язык написан так, что он содержит много грамматической информации. Таким образом, при преобразовании в числовой формат специфичные для слова характеристики, такие как использование заглавных букв, знаки препинания, суффиксы / префиксы и т. Д., Являются избыточными. Очистка данных таким образом, чтобы похожие слова отображались в одно слово, и удаление грамматической информации из текста может значительно сократить словарный запас. Какие методы применять, а какие пропустить, зависит от решаемой проблемы.

1. Удаление стоп-слов

Стоп-слова - это слова, которые обычно используются и удаляются из предложения в качестве предварительного шага в различных задачах обработки естественного языка (NLP). Примеры стоп-слов: 'a', 'an', 'the', 'this', 'not' и т. Д. Каждый инструмент использует немного другой набор стоп-слов, который он удаляет, но этого метода избегают в случаях, когда фраза структура имеет значение, как и в случае с анализом настроений.

Пример удаления стоп-слов:

Вывод:

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

2. Токенизация

Токенизация - это процесс, в котором предложение / текст разбивается на массив слов, называемых токенами. Это помогает выполнять преобразования для каждого слова отдельно, а также требуется для преобразования слов в числа. Существуют разные способы токенизации. Я объяснил эти способы в моем предыдущем посте в разделе Токенизация, так что, если вам интересно, вы можете это проверить.

simple_preprocess Gensim позволяет преобразовывать текст в нижний регистр и удалять знаки препинания. Он также имеет параметры длины min и max, которые помогают отфильтровать редкие слова и наиболее часто встречающиеся слова, которые попадают в этот диапазон длин.

Здесь simple_preprocess используется для получения токенов для фрейма данных, поскольку он уже выполняет большую часть предварительной обработки за нас. Давайте применим этот метод, чтобы получить токены для фрейма данных:

Вывод:

3. Стебель

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

Для стемминга используются разные алгоритмы. PorterStammer (1979), LancasterStammer (1990) и SnowballStemmer (может добавлять собственные правила). Для реализации этих алгоритмов стемминга можно использовать пакет NLTK или Gensim. Lancaster немного медленнее, чем Porter, поэтому мы можем использовать его в соответствии с размером и требуемым временем отклика. Стеммер Snowball - это немного улучшенная версия стеммера Porter, которую обычно предпочитают последнему. Не очень ясно, какой из них даст точные результаты, поэтому нужно экспериментировать с разными методами и выбирать тот, который дает лучшие результаты. В этом примере используется Porter Stemmer, который прост и быстр. Следующий код показывает, как реализовать стемминг в фрейме данных и создать новый столбец stemmed_tokens:

Вывод:

Разделение на обучающие и тестовые наборы:

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

  • Данные обучения (подмножество данных для обучения модели машинного обучения) ~ 70%
  • Тестовые данные (подмножество данных для тестирования модели машинного обучения, обученной на основе данных поезда) ~ 30%

Постарайтесь сбалансировать количество классов в обоих наборах, чтобы результаты не были предвзятыми или одной из причин недостаточного обучения модели. Это важная часть модели машинного обучения. В реальных проблемах есть случаи несбалансированных классов, которые требуют использования таких методов, как передискретизация класса меньшинства, недостаточная выборка класса большинства (функция передискретизации из пакета scikit-learn или создание синтетических образцов с использованием функциональности SMOTE в пакете Imblearn.

В этом случае данные разделяются на две части: обучение и тестирование, при этом 70% находятся в обучении, а 30% - в тесте. При разбиении лучше иметь равное распределение классов как в обучающих, так и в тестовых данных. Здесь используется функция train_test_split из пакета scikit-learn.

Вывод:

Как видно из вышеприведенного вывода, данные распределяются для каждого класса пропорционально. Распечатывается количество строк для каждого настроения в поездке и тесте.

Начало работы с PyTorch

PyTorch - это библиотека машинного обучения с открытым исходным кодом, используемая для компьютерного зрения и обработки естественного языка и основанная на библиотеке Torch. Основными особенностями PyTorch являются тензорные вычисления с использованием графических процессоров и глубоких нейронных сетей. Тензоры определены как torch.tensor, это многомерные массивы чисел, подобные массивам Numpy, но они могут работать на графических процессорах. Вы можете пройти простое руководство здесь и познакомиться с различными типами тензоров.

Строительные блоки глубокого обучения в PyTorch

  1. Autograd PyTorch использует метод, называемый автоматическим дифференцированием. В нейронной сети вам необходимо вычислять градиенты, и это экономит количество операций, поскольку она записывает выполненные операции и воспроизводит их для вычисления градиентов.
  2. Optim Чтобы использовать torch.optim, мы должны использовать конструкцию объекта Optimizer. Обычно для этого требуется итерация, содержащая параметры модели, которые необходимо оптимизировать, и параметры, связанные с оптимизацией, такие как скорость обучения, уменьшение веса и т. Д.
  3. nn Нейронные сети могут быть построены с использованием torch.nn. nn.Module содержит слои и метод forward (ввод), который возвращает вывод.

Основные библиотеки, которые необходимо включить, и способ идентификации текущего устройства показано в следующем коде. Где загружать тензор и выполнять вычисления, определяется параметром device в различных функциях, используемых на уровнях нейронной сети. Я использовал Google Colab для эксперимента и установил для среды выполнения GPU в аппаратном ускорителе, поэтому я вижу, что torch.cuda.is_available верно в моем случае.

Вывод:

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

Различные функции, используемые в нейронной сети

Обычно на выходном слое, где вы получаете прогнозы меток, функция softmax используется с F.softmax. Другие функции доступны через torch.nn.functional. Целевая функция - это функция, минимизации которой обучается ваша сеть, которая в этом случае называется функцией потерь или функцией стоимости. Потери вычисляются в конце каждой итерации прохождения нейронной сети с обучающим экземпляром. Они используются до nn, например. nn.NLLLoss(). Для оптимизации сети используются различные алгоритмы, такие как SGD, Adam, RMSProp и т. Д. Например, чтобы использовать SGD, вам нужно будет инициализировать optim.SGD. Тогда step() функция этого инициализированного объекта - это то место, где выполняется оптимизация в сети.

Давайте погрузимся в создание нейронной сети! Мы узнаем это, построив базовую сеть с помощью логистической регрессии.

Генерация тензора ввода и метки

Первым шагом будет наличие функций, которые могут создавать входной тензор и соответствующую метку, которая является выходным тензором, которые передаются в сеть для обучения. Для логистической регрессии мы будем использовать вектор BOW в качестве входных данных, который представляет собой не что иное, как массив с размером словарного запаса в корпусе, а значения - это частоты слов в корпусе с индексом, являющимся уникальным идентификатором слова. Мы получим уникальный идентификатор из словаря, построенного с использованием corpora.Dictionary пакета Gensim. Это похоже на то, что я сделал в публикации BOW, но я добавляю еще один параметр, называемый заполнением, который будет использоваться в других задачах, таких как CNN, где вы хотите использовать встраивание слов для каждого слова в документе. В этом примере заполнение отключено.

После этого вы готовы создать векторную функцию лука следующим образом. Размер словаря 30056. Вы можете увидеть, как я назначил устройство при создании тензора:

Для создания выходного тензора необходимо выполнить отображение метки на положительные значения. В настоящее время у нас есть -1 для отрицательного значения, это невозможно в нейронной сети. Три нейрона в выходном слое дадут вероятности для каждой метки, поэтому нам просто нужно сопоставить с положительными числами. Функция следующая:

Логистическая регрессия с использованием BOW

Логистическая регрессия - это регрессионная модель, но ее можно использовать для задач классификации, когда для вероятностей, предсказанных для каждого класса, используются пороговые значения. Он использует либо сигмовидную функцию, либо функцию Softmax для получения вероятностей классов. Функция Softmax обычно используется в случае мультиклассовой классификации. Наивысшая вероятность для узла метки будет выбрана в качестве предсказанной метки класса для этого входа. Softmax принимает входной вектор i = 1, 2, .., K и выводит распределение вероятностей, состоящее из K вероятностей, пропорциональных экспонентам входных чисел. Выходные данные имеют одинаковый размер со значениями в диапазоне (0,1), и все они в сумме дают 1. К каждому элементу Xi применяется softmax, а j изменяется от 1 до K. Показан пример того, как это работает. ниже.

Вывод:

Архитектура логистической регрессии:

  • Ввод будет таким же, как и размер словаря в словаре.
  • Размер вывода будет таким же, как количество этикеток
  • Функция Forward сначала запустит линейный слой, а затем вычислит логарифм softmax значений.
  • Оптимизатор SGD обычно используется для логистической регрессии, поэтому он также используется здесь с соответствующей скоростью обучения.

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

Давайте инициализируем объект модели, функцию потерь, в которой используется функция потерь с отрицательным логарифмом правдоподобия и SGD для оптимизации. Для потерь обычно используется функция Cross Entropy Loss, и в этом случае нет необходимости отдельно рассчитывать Log Softmax. Здесь мы использовали его отдельно, чтобы вы могли узнать о компонентах каждого шага и о том, как их реализовать или изменить в других случаях, таких как двоичная классификация.

Наконец-то мы готовы обучать модель! :)

Модель логистической регрессии обучения

Следующий код обучит модель на данных поезда с эпохами, установленными на 100.

Вывод:

Тестирование модели

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

Вывод:

Отчет о классификации показывает среднюю точность, которая составляет 0.70. Это довольно хороший результат по сравнению с объемом данных, использованных для обучения. Функцию torch.argmax можно использовать для предсказанных значений вероятности, чтобы получить метку. Точность для положительных и отрицательных настроений лучше, чем для нейтральных, что имеет смысл, поскольку трудно отличить нейтральные комментарии от обычно используемых слов в положительных и отрицательных настроениях. В приведенном выше результате 0 и 1 обозначают отрицательное и положительное значение соответственно.

Эта точность лучше, чем у методов, реализованных в моих предыдущих сообщениях, где использование классификатора дерева решений использовалось для классификации на основе векторов BOW, TF-IDF, Word2Vec и Doc2Vec в качестве входных данных. Это показывает, что нейронные сети, реализующие простую логистическую регрессию, могут работать лучше с простыми векторами BOW, обученными для многих эпох. Он может улавливать отношения между словами и чувствами и лучше классифицировать. Это руководство предназначено для начала работы с PyTorch и для создания с его помощью простого классификатора.

Так что теперь вы можете легко экспериментировать с собственным набором данных с помощью этого метода! Надеюсь, это помогло вам понять, как использовать PyTorch для построения модели нейронной сети для анализа настроений на основе данных отзывов о ресторанах. Не стесняйтесь расширять этот код! Это применимо к любым другим задачам классификации текста, где существует несколько классов. Если бы я мог подумать об улучшении этой модели, я бы использовал разные скорости обучения, эпохи и попробовал бы использовать разные типы ввода, кроме просто BOW. Вы можете попробовать TFIDF, Word2Vec, Doc2Vec и т. Д. И посмотреть, какие результаты вы получите. Предварительную обработку можно изменить, чтобы использовать лемматизацию или другие алгоритмы выделения корней, чтобы увидеть, как меняются результаты.

Как всегда - Удачных экспериментов и обучения :)