Прогнозирование настроений по новостным данным

Как анализ настроений объединяет возможности НЛП и анализа текста

Можете ли вы позволить машинному обучению понимать человеческий язык и знать, что люди чувствуют и думают?

Да, и это меняет то, как мы взаимодействуем с нашим окружением.

Этот термин называется анализом настроений и сочетает в себе возможности обработки естественного языка и анализа текста для классификации ответа пользователя / клиента на «положительный», «отрицательный» или «нейтральный».

В этом блоге я собираюсь помочь вам понять основные нормы обработки естественного языка и как проводить анализ тональности заголовка новости.

Анализ тональности сочетает в себе понимание семантики и символических представлений языка. Алгоритм будет учиться на помеченных данных и предсказывать метку новых / невидимых точек данных. Этот подход называется обучением с учителем, поскольку мы обучаем нашу модель с помощью корпуса помеченных новостей.

Почему анализ настроений?

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

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

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

До появления алгоритмов анализа настроений люди пытались оценить реакцию пользователей на основе простых приемов, таких как извлечение ключевых слов из контента и ограничение своих выводов только тем, «о чем люди говорят?».

Это никогда не поможет ответить на несколько важных вопросов: «Что люди думают о вашем продукте?»

Это означает, что только явные утверждения / обзоры или мнения будут иметь измеримый результат при старом подходе. Что бы вы сделали с большим количеством неявных комментариев?

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

В этом посте мы поделимся с вами инструментами и процессом анализа настроений для заголовка новостей и кода, который я написал.

Постановка задачи

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

Есть много проблем, которые можно решить с помощью науки о данных. Почему я выбираю это - просто потому, что я не хочу тратить свою энергию на размышления о больших проблемах, и чтобы вам было легче понять это и забыть через пару часов :).

A. Анализ проблемы

Вот несколько вопросов, на которые вам нужно ответить, прежде чем собирать набор данных:

  • Что будет входом в вашу программу?
  • Какого результата вы ожидаете?
  • Является ли это непрерывным значением (что-то вроде заданного регрессионной моделью?) Или это значение метки (например, спам или ветчина?)
  • Что это за проблема?
  • Будет ли это обучение с учителем или без учителя?
  • Это проблема классификации или кластеризации?
  • Какие особенности следует учитывать?

После того, как вы завершили анализ проблемы, вам следует сосредоточить пару дней на сборе обучающего набора данных.

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

Б. Набор данных обучения

Я нашел набор данных, поддерживаемый Kaggle - Набор данных агрегатора новостей. Я использовал только ~ 8k заголовков и разделил их на три категории:

  • 1 за положительный
  • 0 для нейтрального
  • -1 за отрицательный ответ.

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

'New government rule against people will' --> Negative
'Government offers funding opportunities for the startups' -->Positive

Что вы ожидаете, если новый вводимый текст будет

'Tech Companies in Nepal are having problem to raise funds.Nevertheless, they are doing great with customer acquisition'

Ваша модель не преподается с такой сложной структурой.

C. Инструменты

  • Язык: Python
  • Библиотека парсинга: BeautifulSoup
  • Библиотека машинного обучения: scikit-learn
  • Обработка данных: Pandas и Numpy
  • Построение: matplotlib

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

А. Обучение и тестирование

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

Б. Смещение (недостаточное соответствие) и отклонение (переоснащение)

При разработке модели машинного обучения (ML) модель обнаруживает ошибку из двух основных источников:

  • предвзятость и
  • отклонения.

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

На диаграмме выше линейная модель слева не подходит.

  • Модель не учитывает всю предоставленную информацию (высокая систематическая ошибка).
  • Если вы предоставите новые данные для этой модели, она не изменит своего поведения (формы) (низкая дисперсия)

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

  • Модель будет учитывать все точки данных (низкая систематическая ошибка).
  • Кривая будет пытаться изменить свою форму, чтобы захватить новые точки данных, теряя универсальность модели (высокая дисперсия)

С. Перекрестная проверка

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

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

Д. Кривая обучения

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

Зачем вам нужно строить кривую обучения? Это поможет вам определить правильное место на вашей кривой, где смещение и дисперсия сведены к минимуму.

Разработка модели

Я разделил весь процесс разработки модели на следующие подпроцессы:

  • Загрузка и разделение данных
  • Выбор функции
  • Выбор модели
  • Перекрестная проверка
  • Прогноз
  • Анализ

A. Загрузка и разделение данных

Этот фрагмент кода разделяет собранные данные на 80/20 для обучения и тестирования.

Вот результат:

Б. Извлечение признаков

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

Мы превращаем текстовые документы в числовое представление путем векторизации. Преобразование текста в векторную строку PIPE состоит из следующего процесса:

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

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

В приведенном выше коде CountVectorizer переводит целые слова в нижний регистр, удаляет дубликаты и удаляет отдельные символы слова, как это предлагает Regex.

Теперь давайте создадим матрицу "документ-термин". Это описывает количество терминов в документах.

Глядя на результат, мы видим, что он создает матрицу с 3 строками и 21 столбцом.

  • 3 столбца, потому что у нас всего три документа
  • И 21 строка, потому что у нас есть 21 член (изученный на шаге подгонки выше)

Вы можете видеть, что большинство значений функции ZEROs. Это почему? Это потому, что большинство документов можно представить небольшими наборами слов.

Допустим, вы собрали с сайта почти 100 отзывов (документов). Тогда может быть всего 1000 уникальных слов (корпус), а в одном обзоре (один документ) может быть 10 уникальных слов. Верно?

Хранить такое большое количество ZEROs не имеет смысла. Мы будем использовать scikit-learn, который внутренне использует пакет scipy.sparse для хранения такой матрицы в памяти, а также ускоряет операции.

Давайте проверим нашу модель на новом примере.

sample_test_data = ['Country is looking to encourage agriculture schemes']
sample_test_dtm = vec.transform(sample_test_data)

Вот результат:

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

Проблема с CountVectorizer **

Если вы просматриваете два разных документа, посвященных одной и той же теме, один немного длиннее другого, вы обнаружите, что средние значения счетчика в более длинном документе будут выше, чем в более коротком документе. Это означает, что CountVectorizer просто придает одинаковый вес каждому слову, присутствующему в вашем документе (одно слово представляет один столбец в DataFrame). И, когда у вас есть новый документ, он просто отображает 1, если слово присутствует в этом документе, в противном случае отображается на 0.

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

Давайте подробнее рассмотрим Tfidf

Предположим, у вас есть корпус о Mountains. Теперь Tfidf будет работать в трех разных частях.

Частота термина (Tf): это количество раз, когда конкретный термин встречается в одном документе. Давайте возьмем пример: слово is не является значимым ключевым словом для документа, но очень часто . В то же время слово Everest имеет смысл, и вы можете понять, что в документе речь идет о Mountains. Следовательно, нам нужно найти способ уменьшить вес слова is.

Один из методов - вычисление обратной величины значения счетчика, что минимизирует значимость слова is в документе.

Но вы сталкиваетесь с другой проблемой. Предположим, вы встретили слово в своем документе, скажем, stratovolcano, редко встречающееся в вашем документе. Если вы возьмете обратное значение этого случая, вы получите 1 (или близко к 1), что является наивысшим весом. Однако это слово не имеет особого смысла в отношении Mountains.

На данный момент мы ведем подсчет слов следующим образом:
tf("is") = 1000
tf("Everest") = 50
tf("stratovolcano") = 2

Обратная частота документов (iDF). Мы решим проблему появления редких и более частых слов с помощью iDF. Он определяет веса при уменьшении масштаба для слов, встречающихся в документах, по формуле:

iDF = журнал (общее количество документов / общее количество документов с вхождением слова)

Предположим, у нас есть 20 документов, и мы рассчитываем iDF для указанных выше слов.

iDF("is") = log(20/20) = 0 [Since 'is' occurs in all the documents ]
iDF("Everest") = log(20/5) = 0.6 [Since corpus is talking about 'Mountains']
iDF("stratovolcano") = log(10/1) = 1 [Since stratovolcano occurs in one doc]

Частота термина - обратная частота документа (TfiDF): этот вес - статистическая мера, используемая для оценки того, насколько важно слово для документа в сборник или корпус.

TfiDF = TF * iDF
Therefore,
  TfiDF("is") = 1000 * 0 = 0 
  TfiDF("Everest") = 50 * 0.6 = 30
  TfiDF("stratovolcano") = 2 * 1 = 2

Результаты для TfiDF для нашего образца данных поезда:

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

C. Выбор модели

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

def prepare_model(self):
    model = svm.SVC(kernel='linear')
    model.fit(train_vectors, train_labels)

D. Перекрестная проверка

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

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

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

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

Для этого мы использовали cross_val_score. Функция cross_val_score использует StratifiedKFold для создания k сверток данных, а затем вычисляет точность в отдельном прогоне. Для каждой из k складок:

  1. Обучение модели выполняется в k-1 складках в качестве обучающих данных.
  2. Обученная модель проверяется на оставшейся части данных.

Перекрестная проверка может использоваться для разных целей:

1. Перекрестная проверка для выбора модели
2. Перекрестная проверка для настройки параметров (гиперпараметры)
3. Перекрестная проверка для выбора функций

1. Перекрестная проверка для выбора модели

Мы хотели бы посмотреть, подходит ли наивный байесовский классификатор или SVM для наших данных.

Мы видим, что оценка перекрестной проверки для SVM составляет 0,73, поэтому он работает лучше, чем наивный байесовский классификатор.

2. Перекрестная проверка для настройки параметров

До сих пор мы обучаем данные для измерения точности нашей модели. Но есть и другие параметры, которые не входят в процесс обучения модели. Эти параметры, называемые гиперпараметрами, определяют такие области, как - сложность модели и скорость обучения модели. Гиперпараметры можно определить как регуляторы или уровни, которые мы используем для построения модели.

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

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

После наблюдения за точностью модели мы оптимизируем гиперпараметры, исследуя различные настройки значений параметров. Мы будем наблюдать вариации гиперпараметров SVM «ядро», «гамма» и «C», чтобы получить наиболее эффективные значения модели.

Мы будем использовать поиск по сетке, предоставляемый GridSearchCV в sklearn. GridSearchCV разделяет входные данные на набор генераторов поездов и перекрестной проверки (CV). Он обучает алгоритм с набором поездов для поиска лучших гиперпараметров с использованием набора cv.

Лучшая C: 10
Лучшее ядро: rbf
Лучшая гамма: 1.2589254117941673

E. Прогнозирование данных

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

clf.best_estimator_.predict(news_classifier.test_vectors)

F. Анализ

Оценка модели

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

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

Почему именно положительный? «Истинно», потому что реальный класс входных данных соответствует прогнозу, а «Положительный», как мы предположили, прогнозируя «положительный», соответствует положительному результату, указанному выше.

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

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

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

Значение GridSearchCV best_score вычисляется путем взятия среднего значения всех кратностей CV для данных обучения, которые мы скачали.

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

TP = истинно положительный, FN = ложно отрицательный

  • TP_A (563): настроения, которые наша модель считает «положительными», а на самом деле «положительными».
  • FN_A (134 + 82 = 216): настроения, которые наша модель считает «отрицательными» или «нейтральными», но они «положительными».
  • FP_A (79 + 72 = 151): настроения, которые наша модель считает «положительными», но на самом деле они «отрицательные» или «нейтральные».
  • TP_B (620): настроения, которые наша модель считает «негативными», и в действительности являются «негативными».
  • FN_B (79 + 92 = 171): настроения, которые наша модель считает «положительными» или «нейтральными», но на самом деле они «отрицательные».
  • FP_B (134 + 152 = 286): настроения, которые наша модель считает «отрицательными», но в действительности они «положительные» или «нейтральные».

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

т.е. отзыв = 563/779 = 0,7227 (TP / (TP + FN))

Точность: измеряет долю правильных положительных прогнозов из всех положительных прогнозов, сделанных нашей моделью. Из 714 прогнозируемых положительных меток только 563 являются действительно положительными.

т.е. точность = 563/714 = 0,7885 (TP / (TP + FP))

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

Если наша модель лучше работает с обучающими данными и плохо справляется с тестовым набором, это означает, что наша модель запоминает шаблон обучающих данных, но не может соответствовать или обобщать невидимые данные. Это явление идеального соответствия обучающим данным называется переобучением.

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

В этом сценарии, если вы добавите дополнительные точки (больше обучающих данных), модель будет дополнительно сглажена. Другими словами, он будет следовать за перемещением данных, а не за шаблоном данных. Чтобы определить переобучение, можно построить кривую обучения, чтобы увидеть поведение модели при увеличении размера обучающей выборки. Если мы видим, что граница между ошибкой обучения и ошибкой проверки закрывается вместе с увеличением данных обучения, ваша модель имеет шум из-за переобучения. И, в конечном итоге, говорится, что вам следует собирать больше данных для обучения. Другое решение - поиск более простого классификатора. Если у вас небольшой набор данных, выбор простой модели всегда предотвращает переоснащение.

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

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

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

Но такой подход не оптимален. Здесь только определенная часть данных способствует разработке модели. Это можно перенести с помощью перекрестной проверки - выполнение последовательности подгонок модели с использованием подмножеств, которые действуют как набор для обучения и проверки. Для этого мы использовали cross_val_score. cross_val_score использует StratifiedKFold для создания k сверток данных, а затем вычисления точности в отдельном прогоне.

Кривая валидации. Кривая валидации строится, чтобы оценить производительность модели с точки зрения ее сложности. Здесь мы пытаемся изменить параметр, который влияет на сложность модели управления (в нашем случае - «Гамма»), и вычисляем ошибку как для обучающих, так и для тестовых данных.

В сюжете выше

  1. Оценка обучения улучшается с увеличением сложности модели и превышает оценку валидации. Это означает, что модель лучше работает с обучающим набором, но не с тестовыми данными.
  2. На левой стороне кривой (более низкие значения гаммы - более низкая сложность модели - высокое смещение) оценки как обучения, так и проверки минимальны. Это означает, что модель не работает как для обучающего, так и для тестового набора. Это называется недостаточным оснащением.
  3. Правая часть кривой (более высокая сложность модели - большая дисперсия) показывает перебор. Это означает, что модель может работать лучше с обучающими данными, но не может предсказать невидимые данные.
  4. Для промежуточного значения гаммы обе оценки имеют максимальное значение. Этот уровень сложности модели показывает оптимальный компромисс между смещением и дисперсией.

Кривая обучения. Кривая обучения отображает поведение модели при увеличении количества точек данных поезда. Http://scikit-learn.org/stable/auto_examples/model_selection/plot_learning_curve.html

  • На рисунке видно, что оценка обучения достигает максимального значения независимо от увеличения размера обучающих данных.
  • Кроме того, оценка валидации увеличивается с увеличением количества экземпляров обучения.
  • Таким образом, можно сделать вывод, что ядро ​​«rbf» является средством оценки высокой дисперсии, которое превышает наши данные. Эту модель с высокой дисперсией можно улучшить, добавив больше обучающих выборок.

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

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

Для получения дополнительных технических сведений и кода перейдите по следующей ссылке:



Об авторе

Сиджан Бхандари - старший инженер-программист в Leapfrog Technology, Inc.