一、背景
近年来,股票预测还处于一个很热门的阶段,因为股票市场的波动十分巨大,随时可能因为一些新的政策或者其他原因,进行大幅度的波动,导致自然人股民很难对股票进行投资盈利。因此本文想利用现有的模型与算法,对股票价格进行预测,从而使自然人股民可以自己对股票进行预测。
理论上,股票价格是可以预测的,但是影响股票价格的因素有很多,而且目前为止,它们对股票的影响还不能清晰定义。这是因为股票预测是高度非线性的,这就要预测模型要能够处理非线性问题,并且,股票具有时间序列的特性,因此适合用循环神经网络,对股票进行预测。
虽然循环神经网络(RNN),允许信息的持久化,然而,一般的RNN模型对具备长记忆性的时间序列数据刻画能力较弱,在时间序列过长的时候,因为存在梯度消散和梯度爆炸现象RNN训练变得非常困难。Hochreiter 和 Schmidhuber 提出的长短期记忆( Long Short-Term Memory,LSTM)模型在RNN结构的基础上进行了改造,从而解决了RNN模型无法刻画时间序列长记忆性的问题。
综上所述,深度学习中的LSTM模型能够很好地刻画时间序列的长记忆性。
二、主要技术介绍
1、RNN模型
在传统的RNN(循环神经网络)中,所有的w都是同一个w,经过同一个cell的时候,都会保留输入的记忆,再加上另外一个要预测的输入,所以预测包含了之前所有的记忆加上此次的输入。所有RNN都具有一种重复神经网络模块的链式的形式。在标准的RNN中,这个重复的模块只有一个非常简单的结构,例如一个tanh层。
当权中大于1时,反向传播误差时,误差将会一直放大,导致梯度爆炸;当权中小于1时,误差将会一直缩小,导致梯度消失,进而导致网络权重更新缓慢,无法体现出RNN的长期记忆的效果,使得RNN太过健忘。RNN模型的结构如图:
2、LSTM模型
长短期记忆模型(long-short term memory)是一种特殊的RNN模型,是为了解决反向传播过程中存在梯度消失和梯度爆炸现象,通过引入门(gate)机制,解决了RNN模型不具备的长记忆性问题,LSTM模型的结构如图:
具体来说,LSTM模型的1个神经元包含了1个细胞状态(cell)和3个门(gate)机制。细胞状态(cell)是LSTM模型的关键所在,类似于存储器,是模型的记忆空间。细胞状态随着时间而变化,记录的信息由门机制决定和更新。门机制是让信息选择式通过的方法,通过sigmoid函数和点乘操作实现。sigmoid取值介于0~1之间,乘即点乘则决定了传送的信息量(每个部分有多少量可以通过),当sigmoid取0时表示舍弃信息,取1时表示完全传输(即完全记住)[2]。
LSTM 拥有三个门,来保护和控制细胞状态:遗忘门(forget gate)、更新门(update gate)和输出门(output gate)。
细胞状态类似于传送带。直接在整个链上运行,只有一些少量的线性交互。信息在上面流传保持不变会很容易。
如图:
3、控制门工作原理
遗忘门
更新门
输出门
四、代码实现
UI
demo.py
import tensorflow as tf
import numpy as np
import tkinter as tk
from tkinter import filedialog
import time
import pandas as pd
import stock_predict as pred
def creat_windows():
win = tk.Tk() # 创建窗口
sw = win.winfo_screenwidth()
sh = win.winfo_screenheight()
ww, wh = 800, 450
x, y = (sw - ww) / 2, (sh - wh) / 2
win.geometry("%dx%d+%d+%d" % (ww, wh, x, y - 40)) # 居中放置窗口
win.title('LSTM股票预测') # 窗口命名
f_open =open('dataset_2.csv')
canvas = tk.Label(win)
canvas.pack()
var = tk.StringVar() # 创建变量文字
var.set('选择数据集')
tk.Label(win, textvariable=var, bg='#C1FFC1', font=('宋体', 21), width=20, height=2).pack()
tk.Button(win, text='选择数据集', width=20, height=2, bg='#FF8C00', command=lambda: getdata(var, canvas),
font=('圆体', 10)).pack()
canvas = tk.Label(win)
L1 = tk.Label(win, text="选择你需要的 列(请用空格隔开,从0开始)")
L1.pack()
E1 = tk.Entry(win, bd=5)
E1.pack()
button1 = tk.Button(win, text="提交", command=lambda: getLable(E1))
button1.pack()
canvas.pack()
win.mainloop()
def getLable(E1):
string = E1.get()
print(string)
gettraindata(string)
def getdata(var, canvas):
global file_path
file_path = filedialog.askopenfilename()
var.set("注,最后一个为label")
# 读取文件第一行标签
with open(file_path, 'r', encoding='gb2312') as f:
# with open(file_path, 'r', encoding='utf-8'