Можем ли мы предсказать изменения на фондовом рынке, применяя методы обработки естественного языка (NLP) к заголовкам новостей?

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

Код

Вы можете найти код этой статьи по этой ссылке на GitHub.

Введение

Как можно использовать НЛП для зарабатывания денег? Человеческий язык представляет собой представление различного содержания: эмоций, мыслей, фактов и вымысла. Точно так же язык, используемый в СМИ, может быть представителем мира в тот конкретный период времени.

Вот почему мы решили заняться проблемой Ежедневные новости для прогнозов фондового рынка, размещенной на Kaggle. Задача заключалась в том, чтобы увидеть, можно ли использовать заголовки основных новостей для прогнозирования цен на акции. Набор данных заголовков был взят из Reddit WorldNews channel (/ r / worldnews), причем важность заголовка новости измеряется количеством голосов, полученных от пользователей Reddit. Конечный набор данных представлял собой набор 25 заголовков с наибольшим количеством заголовков для каждой даты с 8 июня 2008 г. по 1 июля 2016 г. Цена акций была выбрана в качестве промышленного индекса Доу-Джонса, взятого за тот же период.

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

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

Набор данных

Набор данных состоит из 1989 точек данных, каждая из которых соответствует определенной дате в период с 8 июня 2008 года по 1 июля 2016 года. Каждая точка данных даты имеет 27 функций. Одна функция описывает точную дату, другая - двоичный код, соответствующий изменению уровня запасов в эту дату, и 25 характеристик заголовков новостей. Этот исходный набор данных хранится в наборе данных pandas с именем data.

# import data
data = pd.read_csv('Combined_News_DJIA.csv')
print('data is %d data points with %d features'%(data.shape[0],data.shape[1]))
data.head()

Предварительная обработка данных

Очистка данных

Заголовки были скопированы с сайта Reddit. Таким образом, все заголовки имеют кавычки вокруг текста заголовка, а также код HTML заголовка, такой как «b» перед текстом. Это очищается на первом этапе предварительной обработки данных.

dataClean.iloc[:,i]=dataClean.iloc[:,i].str.strip("b'")

Нормализация

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

## All punctuations found in string.punctuation were substituted with blanks
dataClean.iloc[:,i]=dataClean.iloc[:,i].str.translate(str.maketrans('', '', string.punctuation))
dataClean.iloc[:,i]=dataClean.iloc[:,i].str.lower()

Токенизация

Затем данные токенизируются словами. Каждый текст заголовка разбивается на пробелы с помощью метода str.split (). Стоп-слова идентифицируются из списка токенов и удаляются из списка слов. Стоп-слова импортируются из списка английских игнорируемых слов библиотеки NLTK. Стоп-слова хранятся в наборе словарей для более быстрого доступа и идентификации.

dataClean.iloc[:,i]=dataClean.iloc[:,i].str.split()
dataClean.iloc[:,i]=dataClean.iloc[:,i].apply(lambda sent: 'num' if isinstance(sent,float) else ['num' if token.isdigit() else token for token in sent])

## Stopwords were imported from NLTK package. The list of stopwords was stored in a set for faster accessing.
stops = set(stopwords.words('english'))

Очищенные слова для каждого заголовка на дату хранятся в наборе данных dataClean pandas.

Стеминг и лемматизация

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

if stemming:
    porter = PorterStemmer()   
    for i in range(2,dataNormal.shape[1]):
        dataNormal.iloc[:,i]=dataNormal.iloc[:,i].apply(lambda x: [porter.stem(y) for y in x])
    
elif lemmatization:
    lemmatizer = WordNetLemmatizer()
    for i in range(2,data.shape[1]):
        dataNormal.iloc[:,i]=dataNormal.iloc[:,i].apply(lambda x: [lemmatizer.lemmatize(y) for y in x])

Полученные данные сохраняются в наборе данных dataNormal.

Векторизация данных

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

Сглаживание данных

В настоящее время набор данных состоит из 1989 точек данных (даты 1989 года) с 27 объектами, соответствующими датам, меткам и 25 заголовкам. В этой модели мы не делаем различий между рейтингом заголовков, поэтому заголовку с наибольшим количеством лайков не присваивается дополнительный вес по сравнению с заголовком, который занимает 25-е место в заголовках дня. Таким образом, мы объединяем заголовки в единый список слов на дату. Сглаженные данные хранятся в наборе данных dataFlat, который содержит 1989 точек данных с тремя функциями.

Векторизация данных

Слова нельзя напрямую сравнивать друг с другом. Класс Word2Vec пакета Gensim создает числовой вектор, представляющий каждое слово. Векторы представляют значения, а также отношения между словами. Стомерное пространство использовалось для представления векторов с разумной точностью, причем вектор создавался, если слово повторялось не менее 5 раз в корпусе.

Обучение модели Word2Vec заняло 1,4 секунды, и в результате был получен словарь из 10 197 слов. Беглый взгляд на модель Word2Vector показывает, что модель довольно точна. Например, слова, векторные формы которых наиболее близки к соответствующему вектору для «войны», были «человечность», «преступность», «картель» и «ирак». Он также показал, что слова «мир» и «террор» были больше связаны со словом «война», чем со «школой», как и ожидалось.

Набор данных dataFlat векторизуется, сначала удаляются слова, которых нет в модели W2V. Остальным словам соответствуют 100-мерные векторы, которые сохраняются для каждой даты в списке words_in_date. Для векторов, сформированных за дату, берется среднее значение. Следовательно, один 100-мерный вектор теперь представляет каждую точку данных (дату) в качестве прокси для всех слов, которые использовались в заголовке этой даты и, представленных в словаре слов. . Этот средний вектор сохраняется как дополнительный столбец в dataFlat и становится dataVector.

Разделение данных

DataVector - это 1989 точек данных с четырьмя функциями: дата, метка, список слов и, наконец, 100-мерный вектор, который представляет собой среднее значение вектора на дату. Для модели классификатора его интересуют только 100 характеристик каждого вектора. Таким образом, каждое из 100 измерений раскрывается в отдельный столбец в dataModel, который представляет собой 1989 точек данных со 102 функциями. Двумя не-векторными функциями являются даты и метки изменений DJIA.

Набор данных для обучения / тестирования создается путем разделения набора данных по дате = 31 декабря 2014 г. В результате в наборе обучающих данных оказалось 1610 векторов, а 379 векторы, входящие в набор тестовых данных, примерно 80,9–19,1%.

Моделирование

Для прогнозирования бинарной классификации было построено 5 различных типов моделей. Все модели были взяты из пакета scikit-learn, с моделями: Классификатор случайного леса (RF), XG Boost (XGB), логистическая регрессия (LogReg), поддержка Векторный классификатор (SVC) и классификатор многослойного персептрона (MLP).

Результаты

Все пять моделей предсказывали результаты, близкие к случайным. Результаты были почти идентичны как метрике точности, так и метрике ROC AUC (площадь рабочей характеристики приемника под кривой), причем последняя является метрикой, которая должна быть измерена в соответствии с определением. от конкурса Kaggle.

Как ни странно, все модели LogReg, SVC и MLP предсказали «1» (т.е. DJIA будет расти) в качестве результата для всех точек тестовых данных.

Более пристальный взгляд на кривые ROC показывает, что RF имеет относительно гладкую кривую ROC, в то время как XGB, LogReg и SVC имеют более жесткие кривые ROC, а MLP имеют абсолютно линейную кривую ROC. Но ROC-кривые всех пяти моделей совпадают с наивной случайной классификационной линией, и в результате получается оценка AUC, близкая к 0,5.

Вывод

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

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

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

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

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