用每日新闻预测金融市场变化(进阶版)
这篇教程里,我们会使用FastText来做分类
import pandas as pd
import numpy as np from sklearn.metrics import roc_auc_score from datetime import date
监视数据
我们先读入数据。这里我提供了一个已经combine好了的数据。
data = pd.read_csv('../input/Combined_News_DJIA.csv')
这时候,我们可以看一下数据长什么样子
data.head()
其实看起来特别的简单直观。如果是1,那么当日的DJIA就提高或者不变了。如果是1,那么DJIA那天就是跌了。
分割测试/训练集
这下,我们可以先把数据给分成Training/Testing data
train = data[data['Date'] < '2015-01-01'] test = data[data['Date'] > '2014-12-31']
然后,我们把每条新闻做成一个单独的句子,集合在一起:
X_train = train[train.columns[2:]] corpus = X_train.values.flatten().astype(str) X_train = X_train.values.astype(str) X_train = np.array([' '.join(x) for x in X_train]) X_test = test[test.columns[2:]] X_test = X_test.values.astype(str) X_test = np.array([' '.join(x) for x in X_test]) y_train = train['Label'].values y_test = test['Label'].values
这里我们注意,我们需要三样东西:
corpus是全部我们『可见』的文本资料。我们假设每条新闻就是一句话,把他们全部flatten()了,我们就会得到list of sentences。
同时我们的X_train和X_test可不能随便flatten,他们需要与y_train和y_test对应。
corpus[:3]
X_train[:1]
y_train[:5]
来,我们再把每个单词给分隔开:
同样,corpus和X_train的处理不同
from nltk.tokenize import word_tokenize
corpus = [word_tokenize(x) for x in corpus] X_train = [word_tokenize(x) for x in X_train] X_test = [word_tokenize(x) for x in X_test]
tokenize完毕后,
我们可以看到,虽然corpus和x都是一个二维数组,但是他们的意义不同了。
corpus里,第二维数据是一个个句子。
x里,第二维数据是一个个数据点(对应每个label)
X_train[:2]
corpus[:2]
预处理
我们进行一些预处理来把我们的文本资料变得更加统一:
-
小写化
-
删除停止词
-
删除数字与符号
-
lemma
我们把这些功能合为一个func:
# 停止词
from nltk.corpus import stopwords
stop = stopwords.words('english') # 数字 import re def hasNumbers(inputString): return bool(re.search(r'\d', inputString)) # 特殊符号 def isSymbol(inputString): return bool(re.match(r'[^\w]', inputString)) # lemma from nltk.stem import WordNetLemmatizer wordnet_lemmatizer = WordNetLemmatizer() def check(word): """ 如果需要这个单词,则True 如果应该去除,则False """ word= word.lower() if word in stop: return False elif hasNumbers(word) or isSymbol(word): return False else: return True # 把上面的方法综合起来 def preprocessing(sen): res = [] for word in sen: if check(word): # 这一段的用处仅仅是去除python里面byte存str时候留下的标识。。之前数据没处理好,其他case里不会有这个情况 word = word.lower().replace("b'", '').replace('b"', '').replace('"', '').replace("'", '') res.append(wordnet_lemmatizer.lemmatize(word)) return res
把我们三个数据组都来处理一下:
corpus = [preprocessing(x) for x in corpus] X_train = [preprocessing(x) for x in X_train] X_test = [preprocessing(x) for x in X_test]
我们再来看看处理之后的数据长相:
print(corpus[553]) print(X_train[523])
训练NLP模型
有了这些干净的数据集,我们可以做我们的NLP模型了。
我们这里要用的是FastText。
原理,我在课件上已经讲过了,这里我们来进一步看看具体的使用。
由于这篇paper刚刚发布,很多社区贡献者也都在给社区提供代码,尽早实现python版本的开源编译(我也是其中之一)。
当然,因为Facebook团队本身已经在GitHub上放出了源代码(C++),
所以,我们可以用一个python wrapper来造个interface,方便我们调用。
首先,我们讲过,FT把label也看做一个元素,带进了word2vec的网络中。
那么,我们就需要把这个label塞进我们的“句子”中:
for i in range(len(y_train)): label = '__label__' + str(y_train[i]) X_train[i].append(label) print(X_train[49])
然后,我们把数据存成文件的形式。因为我们这里的FastText只是个python的interface。调用起来还得用C++的接口。
我们需要存三个东西:
含有label的train集
不含label的test集
label单独放一个文件
X_train = [' '.join(x) for x in X_train] print(X_train[12])
同理,test集也这样。
X_test = [' '.join(x) for x in X_test] with open('../input/train_ft.txt', 'w') as f: for sen in X_train: f.write(sen+'\n') with open('../input/test_ft.txt', 'w') as f: for sen in X_test: f.write(sen+'\n') with open('../input/test_label_ft.txt', 'w') as f: for label in y_test: f.write(str(label)+'\n')
调用FastText模块
import fasttext
clf = fasttext.supervised('../input/train_ft.txt', 'model', dim=256, ws=5, neg=5, epoch=100, min_count=10, lr=0.1, lr_update_rate=1000, bucket=200000)
训练完我们的FT模型后,我们可以测试我们的Test集了
y_scores = []
# 我们用predict来给出判断
labels = clf.predict(X_test) y_preds = np.array(labels).flatten().astype(int) # 我们来看看 print(len(y_test)) print(y_test) print(len(y_preds)) print(y_preds) from sklearn import metrics # 算个AUC准确率 fpr, tpr, thresholds = metrics.roc_curve(y_test, y_preds, pos_label=1) print(metrics.auc(fpr, tpr))
同理,这里,我们通过parameter tuning或者是resampling,可以让我们的结果更加好。
当然,因为FT本身也是一个word2vec。并且自带了一个类似于二叉树的分类器在后面。
这样,在小量数据上,是跑不出很理想的结论的,还不如我们自己带上一个SVM的效果。
但是面对大量数据和大量label,它的效果就体现出来了。