完整的工作代码可在github.com/lilianweng/stock-rnn找到。
RNN实战part1:股市预测
在第2部分的教程中,将继续探讨股票预测的话题,在第一部分我增添了一个循环神经网络(RNN),并赋予它应对多个股票价格预测的能力。为了区分与不同价格序列相关联的模式,我用股票符号嵌入向量作为输入的一部分。
数据集
在搜索过程中,我找到了用来查询雅虎!金融API的库。如果雅虎没有关闭获取历史数据的API,那么这个库是有用的。在本文中,我选择了谷歌财经的一个链接,其中提供了一些可以被下载股票历史价格信息的免费数据资源。
获取数据的代码可以写成如下简单形式:
1 2 3 4 5 6 7 8 |
importurllib2 fromdatetimeimport datetime BASE_URL ="https://www.google.com/finance/historical?" "output=csv&q={0}&startdate=Jan+1%2C+1980&enddate={1}" symbol_url = BASE_URL.format( urllib2.quote('GOOG'), # Replace with any stock you are interested. urllib2.quote(datetime.now().strftime("%b+%d,+%Y"), '+') ) |
当获取内容时,记得添加try-catch语句块,以防链接失败或者提供的股票代码失效。
1 2 3 4 5 6 |
try: f = urllib2.urlopen(symbol_url) withopen("GOOG.csv", 'w') as fin: print>> fin, f.read() except urllib2.HTTPError: print"Fetching Failed: {}".format(symbol_url) |
获取完整的数据采集实现代码请点这里。
模型构建
该模型预期用来学习不同股票的价格序列。由于不同的底层模式,我要明确地告诉模型它正在处理哪只股票。嵌入(Embedding)比one-hot编码更受欢迎,原因是:
1.给出的训练集包含N个股票,one-hot编码将引入N个(或N-1个)额外的稀有特征维度。一旦每个股票代码被映射到一个小得多的长度为k的映射向量,k<<N,那么结果就是采用一个更多的压缩形式和更小的数据集。
2.因为映射向量是用来学习的变量,所以类似的股票可以与类似的映射相关联,并且可以帮助预测其它的股票,比如“GOOG”和“GOOGL”,之后将在下图中可以看到。
在循环神经网络中,取一个时间点t处,输入向量包括input_size(标记为w)第一批股票每日价格值 (pi,tw,pi,tw+1,…,pi,(t+1)w−1)。股票代码被唯一的嵌入到一个长度向量embedding_size(标记为k)(ei,0,ei,1,…,ei,k)。如下图所示,价格向量与映射向量关联在一起,然后传送到长短期记忆网络(LSTM)单元。
另一种选择是将嵌入向量与长短期记忆网络(LSTM)单元相关联,并且在输出层学习新的权重W和偏差b。然而,在这种方式下,LSTM单元就只能得出一个股票的价格而无法得出其它的价格了,并且它的能力将受到很大程度的限制。
图.具有股票符号映射的RNN(股票价格预测循环神经网络)模型的架构。
RNNConfig中添加了两个新的配置环境变量:
·embedding_size 控制每个嵌入向量的大小;
·stock_symbol_size指数据集中单股的数量。
这两个环境变量共同定义了嵌入矩阵的大小,以满足模型必须学习embedding_size×stock_symbol_size结果的格外变量,来与Part1的模型进行比较。
1 2 3 4 |
classRNNConfig(): # … old ones embedding_size =8 stock_symbol_size =100 |
定义图形
(1)如Part1所讲的:定义图形,首先让我们定义一个名为lstm_graph的tf.Graph()和一组张量(tensors),以同样的方式保存输入数据,inputs,targets和learning_rate。另一个用来定义的占位符是一个与输入价格相关的股票代码列表。在用标签编码(label encoding)之前,这些股票代码已经分别被映射成唯一的整数。
1 2 |
# Mapped to an integer. one label refers to one stock symbol. stock_labels = tf.placeholder(tf.int32, [None, 1]) |
(2)然后我们需要建立一个嵌入矩阵作为一个查找表,表中包含了所有股票的嵌入向量。矩阵用-1到1之间的随机数进行初始化,并在训练数据期间更新。
# Don’t forget: config = RNNConfig() # Convert the integer labels to numeric embedding vectors. embedding_matrix = tf.Variable( tf.random_uniform([config.stock_symbol_size, config.embedding_size], -1.0, 1.0) ) |
(3)重复股票标签num_steps的次数以在训练期间来匹配RNN(循环神经网络)的展开版本和inputs张量的形状。转换操作tf.tile获得一个基础张量,并通过多次)重复以确定的维度来创建一个新的张量;严格的来说就是输入张量的第i个维度乘以变量multiples[i]的倍数。例如,如果变量stock_labels 为 [[0],[0],[2],[1]],那么与[1,5]做相关计算,则产生 [[0 0 0 0 0],[0 0 0 0 0]) [2 2 2 2 2],[1 1 1 1 1 1]]。
1 |
stacked_stock_labels = tf.tile(stock_labels, multiples=[1, config.num_steps]) |
(4) 然后根据查找表embedding_matrix将符号映射到映射向量
|
(5)最后,将价格的值与嵌入向量结合起来。tf.concat的操作沿着维度变量axis与张量列表连接。在这个例子中,我们希望保持批量和步骤的数量不变,但只扩展输入向量的变量input_size的长度以包含映射特征。
# inputs.get_shape() = (?, num_steps, input_size) # stock_label_embeds.get_shape() = (?, num_steps, embedding_size) # inputs_with_embed.get_shape() = (?, num_steps, input_size + embedding_size) inputs_with_embed = tf.concat([inputs, stock_label_embeds], axis=2) |
其余代码运行动态RNN,提取LSTM单元的最后状态,并控制输出层中的权重和偏差,详情请参阅Part1。
训练会话
如果你不知道在TensorFlow中如何运行一个训练会话,请阅读Part1:开始训练会话。
向图形提供数据之前,股票符号应被转换成具有独立标签编码的整数。
1 2 3 |
fromsklearn.preprocessingimportLabelEncoder label_encoder=LabelEncoder() label_encoder.fit(list_of_symbols) |
对每个个股来说,需要保持训练/测试分流比不变,90%用于训练,10%用于测试。
可视化图形
用代码定义图片后,让我们检查下Tensorboard 中的可视化情况以确保组件的构造是正确的。从本质上看,它非常像图1中的架构图解。
图二,以上定义了图形的Tensorboard可视化。“train”和“save”两个模块已经从主图中删除了。
不同于展现图形结构或者及时跟踪变量,Tensorboard 也支持映射可视化。为了将映射值传送到Tensorboard 中,我们需要增加适当的跟踪日志。
(0)在我的映射可视化中,我想用行业领域来用颜色区分每个股票。元数据应该存储在CSV文件中。此文件有股票标识和行业领域两列。CSV文件有没有标题不重要,重要的是股票列表顺序必须与label_encoder.classes一致。
1 2 3 4 5 6 |
importcsvembedding_metadata_path=os.path.join(your_log_file_folder,'metadata.csv')withopen(embedding_metadata_path,'w')asfout: csv_writer=csv.writer(fout) # write the content into the csv file. # for example, csv_writer.writerows(["GOOG", "information_technology"]) |
(1)在训练的tf.Session变量中首先设置摘要作者。
1 2 3 4 |
fromtensorflow.contrib.tensorboard.pluginsimportprojectorwithtf.Session(graph=lstm_graph)assess: summary_writer=tf.summary.FileWriter(your_log_file_folder) summary_writer.add_graph(sess.graph) |
(2)将图lstm_graph中定义的张量embedding_matrix添加到projector配置变量中,并附上元数据CSV的文件路径。
1 2 3 4 5 6 |
projector_config=projector.ProjectorConfig() # You can add multiple embeddings. Here we add only one. added_embedding=projector_config.embeddings.add() added_embedding.tensor_name=embedding_matrix.name # Link this tensor to its metadata file. added_embedding.metadata_path=embedding_metadata_path |
(3)这行代码是在your_log_file_folder文件夹下新建了文件projector_config.pbtxt。TensorBoard 将在启动时读取该文件。
1 |
projector.visualize_embeddings(summary_writer,projector_config) |
结果集
该模型对标准普尔500指数中市值最大的前100只股票数据进行训练。
使用了以下配置:
input_size=10
num_steps=30
lstm_size=256
num_layers=1,
keep_prob=0.8
batch_size = 200
init_learning_rate = 0.05
learning_rate_decay = 0.99
init_epoch = 5
max_epoch = 500
embedding_size = 8
stock_symbol_size = 100
价格预测
作为对预测质量的简要概述,图三绘制了对“KO”, “AAPL”, “GOOG” 和 “NFLX”这些测试数据的预测结果。总体趋势在实际价格和预测值之间相匹配。考虑预测任务是如何设计的,该模型依赖所有的历史数据测试点来预测接下来5(input_size)天的数据。参数input_size比较小的时候,模型完全不需要担心长期的增长曲线。但是一旦我们增大了input_size,预测就会困难多了。
图3.显示了在测试数据集中KO,AAPL,GOOG和NFLX的真实和预测股票价格。
嵌入可视化
Tensorboard支持的非常好的一种常见的技术是t-SNE(Maaten和Hinton, 2008),用以在嵌入空间来可视化集群。t-SNE是“t-分布随机邻域嵌入算法”的缩写,它是随机相邻嵌入算法 (Hinton and Roweis, 2002)的一种演变,但是成本函数修改后更易于优化了。
1、类似于SNE,t-SNE首先将数据点之间的高维欧氏距离转换为表示相似性的条件概率。
2、t-SNE在低维空间的数据点上定义了类似的概率分布,依照地图上的点的位置,它最小化了两种分布之间的kullback-leibler divergence。
在t-SNE可视化中查看这一讲,了解如何调整参数,困惑度和训练效率(ε)。
图四,用t-SNE实现股票映射的可视化,不同颜色的标签代表着不同行业领域的股票。
在嵌入空间中,我们可以通过检验映射向量之间的相似程度来度量两种股票之间的相似性。例如,GOOG在训练映射中与GOOGL基本类似(见图5)。
图五,当我们在Tensorboard的映射表中搜索“GOOG”时,其他类似的股票会随着相似度的下降颜色从暗到亮变得突出。
本教程中的完整代码请点击github.com/lilianweng/stock-rnn.
文章原标题《Predict Stock Prices Using RNN: Part 2》
作者:Lilian Weng
译者:奥特曼,审校:袁虎。
文章为简译,更为详细的内容,请查看原文