参考书目:陈允杰.TensorFlow与Keras——Python深度学习应用实战.北京:中国水利水电出版社,2021
本系列基本不讲数学原理,只从代码角度去让读者们利用最简洁的Python代码实现深度学习方法。
循环神经网络最初是用来处理文本数据的,自然语言都是时序数据,股价也是。循环神经网络可以专门用来预测时间序列数据,虽然股价预测是很难在实际中去应用,预测的也是不太准,因为股市的数据波动超级大,波动因素也不确定(还有一些别的原因下面数据处理时会介绍).....但是对于具有固定规律的时间序列数据预测还是不错的,比如天气,用电量负荷等等,本次采用Google股价数据作为循环神经网络的案例演示,别的时间序列数据也是通用。由于是预测价格,相当于是回归问题。文本数据处理起来有点麻烦,因为涉及到词向量之类,文本要处理成矩阵才能送入神经网络运算,之后会介绍文本的情感分类问题。本次就当时间序列数据的回归问题案例。
循环神经网络最经典的就是RNN,LSTM,GRU三种,三种其实用法一样,下面都会介绍。
一般来说预测效果是LSTM>=GRU>RNN。
使用循环神经网络预测Google股价
Google股价可以在雅虎财经下载,或者去同花顺亿牛网东方财富网等随便下一支A股股价数据都行,一样用。
导入包,读取数据,查看数据长什么样子
import numpy as np
import pandas as pd
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import Dense, Dropout,LSTM,SimpleRNN,GRU
np.random.seed(10) # 指定乱数种子
# 载入Google股价的训练数据集
df_train = pd.read_csv("GOOG_Stock_Price_Train.csv",index_col="Date",parse_dates=True)
df_train
有每天的开盘价,最高价,最低价,收盘价,除权价,成交量,我们一般采用收盘价作为股价,本次使用调整的收盘价,也就是Adj Close
数据标准化,然后分割X和y,训练集和测试集(X是前60天股价,y是第61天股价,然后滑动窗口,得到所有的X和y)
X_train_set = df_train.iloc[:,4:5].values # Adj Close欄位
#特征标准化 - 正规化
sc = MinMaxScaler()
X_train_set = sc.fit_transform(X_train_set)
#取出几天前股价来建立成特征和标签数据集
def create_dataset(ds, look_back=1):
X_data, Y_data = [],[]
for i in range(len(ds)-look_back):
X_data.append(ds[i:(i+look_back), 0])
Y_data.append(ds[i+look_back, 0])
return np.array(X_data), np.array(Y_data)
look_back = 60
print("回看天数:", look_back)
# 分割成特征数据和标签数据
X_train, Y_train = create_dataset(X_train_set, look_back)
print(X_train)
可以直接查看一下X和y的形状。由于是循环神经网络,要三维的数据输入,下面把X改为三维张量,查看形状。
# 转换成(样本数, 时步, 特征)张量
X_train = np.reshape(X_train, (X_train.shape[0], X_train.shape[1], 1))
print("X_train.shape: ", X_train.shape)
print("Y_train.shape: ", Y_train.shape)
1198为样本个数,60是时间步长,1是一个特征,即单变量,只有股价一个特征变量。预测就相当于是前60天股价预测第61天股价。
定义RNN模型
首先使用最普通的循环神经网络——RNN
# 定义模型
model = Sequential()
model.add(SimpleRNN(50, return_sequences=True, input_shape=(X_train.shape[1], 1)))
model.add(Dropout(0.2))
model.add(SimpleRNN(50))
model.add(Dropout(0.2))
model.add(Dense(1))
model.summary() # 显示模型摘要资讯
#编译模型
model.compile(loss="mse", optimizer="adam")
#训练模型
model.fit(X_train, Y_train, epochs=100, batch_size=32)
这里堆叠了两层的RNN,每层50个神经元,训练100轮
RNN训练起来特别慢,下面使用LSTM(如果想用GRU,直接把所有的LSTM改成GRU就行,其他的位置不需要任何改动,很方便)
定义LSTM(GRU)模型
# 定义模型
model = Sequential()
model.add(LSTM(50, return_sequences=True,
input_shape=(X_train.shape[1], 1)))
model.add(Dropout(0.2))
model.add(LSTM(50, return_sequences=True))
model.add(Dropout(0.2))
model.add(LSTM(50))
model.add(Dropout(0.2))
model.add(Dense(1))
model.summary() # 显示模型摘要资讯
#编译模型
model.compile(loss="mse", optimizer="adam")
#训练模型
model.fit(X_train, Y_train, epochs=100, batch_size=32)
这里堆叠了3层LSTM层,训练结果如下
只看损失感觉很小,进一步和真实值对比
# 使用模型预测股价 - 2017年1~3月预测 4 月份股价
df_test = pd.read_csv("GOOG_Stock_Price_Test.csv")
X_test_set = df_test.iloc[:,4:5].values
# 产生标签数据
_, Y_test = create_dataset(X_test_set, look_back)
#特征数据和标准化
X_test_s = sc.transform(X_test_set)
X_test,_ = create_dataset(X_test_s, look_back)
# 转换成(样本数, 时步, 特征)张量
X_test = np.reshape(X_test, (X_test.shape[0], X_test.shape[1], 1))
X_test_pred = model.predict(X_test)
# 将预测值转换回股价
X_test_pred_price = sc.inverse_transform(X_test_pred)
画图查看对比
# 绘出股价图表
import matplotlib.pyplot as plt
plt.plot(Y_test, color="red", label="Real Stock Price")
plt.plot(X_test_pred_price, color="blue", label="Predicted Stock Price")
plt.title("2017 Google Stock Price Prediction")
plt.xlabel("Time")
plt.ylabel("Google Time Price")
plt.legend()
plt.show()
看上去还不错.....但其实差的有点远,股价预测840,和真实值的830差的不多,但是真的实盘操作这10块很致命,可能就是赚钱和亏损的区别。
而且最严重的问题是,这是单步预测,即采用前60天的真实数据去预测61天的数据,下一个样本X也是用的真实数据。也就是说如果不知道真实数据的话,只能预测一步,只能预测到明天的价格,不能预测后天的价格。
当然解决办法是把预测出来的y作为下一个X放进去然后再预测,但是预测出出来的值和真实值是有差异的,X有误差了,那么估计的y也会有误差,并且滑动窗口越多,误差会累计得越来越大.....所以做长期的预测不准确的原因在这里。
当然,对于具有明显的周期性或者是趋势性的时间序列数据来说,循环神经网络的效果还是不错的,股价预测也只能是当案例看看了,只靠这种单一的价格模型去预测股价是很困难的。