第一部分:從RNN到LSTMjson
一、什麼是RNNwindows
RNN全稱循環神經網絡(Recurrent Neural Networks),是用來處理序列數據的。在傳統的神經網絡模型中,從輸入層到隱含層再到輸出層,層與層之間是全鏈接的,每層之間的節點是無鏈接的。可是這種普通的神經網絡對於不少關於時間序列的問題卻無能無力。例如,你要預測句子的下一個單詞是什麼,通常須要用到前面的單詞,由於一個句子中先後單詞並非獨立的。RNN之因此稱爲循環神經網路,即一個序列當前的輸出與前面的輸出也有關。具體的表現形式爲網絡會對前面時刻的信息進行記憶並應用於當前輸出的計算中,即隱藏層之間的節點再也不無鏈接而是有鏈接的,而且隱藏層的輸入不只包括輸入層的輸出還包括上一時刻隱藏層的輸出,以下圖所示: 網絡
傳統的神經網絡中,數據從輸入層輸入,在隱藏層加工,從輸出層輸出。RNN不一樣的就是在隱藏層的加工方法不同,後一個節點不只受輸入層輸入的影響,還包受上一個節點的影響。
展開來就是這個樣子:圖中的xt−1,xt, xt+1表明不一樣時刻的輸入,每一個x都具備input layer的n維特徵,依次進入循環神經網絡之後,隱藏層輸出st受到上一時刻st−1的隱藏層輸出以及此刻輸入層輸入xtapp
的兩方面影響。 ide
缺點:RNN利用內部的記憶來處理任意時序的輸入序列,而且在其處理單元之間既有內部的反饋鏈接又有前饋鏈接,這使得RNN能夠更加容易處理不分段的文本等。可是因爲RNN只能對部分序列進行記憶,因此在長序列上表現遠不如短序列,形成了一旦序列過長便使得準確率降低的結果。學習
二、什麼是LSTMui
長短記憶神經網絡——一般稱做LSTM,是一種特殊的RNN,可以學習長的依賴關係。 他們由Hochreiter&Schmidhuber引入,並被許多人進行了改進和普及。他們在各類各樣的問題上工做的很是好,如今被普遍使用。設計
LSTM是爲了不長依賴問題而精心設計的。 記住較長的歷史信息其實是他們的默認行爲,而不是他們努力學習的東西。code
全部循環神經網絡都具備神經網絡的重複模塊鏈的形式。 在標準的RNN中,該重複模塊將具備很是簡單的結構,例如單個tanh層。orm
LSTM也擁有這種鏈狀結構,可是重複模塊則擁有不一樣的結構。與神經網絡的簡單的一層相比,LSTM擁有四層,這四層以特殊的方式進行交互。
LSTM的關鍵是細胞狀態,表示細胞狀態的這條線水平的穿過圖的頂部。
細胞的狀態相似於輸送帶,細胞的狀態在整個鏈上運行,只有一些小的線性操做做用其上,信息很容易保持不變的流過整個鏈。
門(Gate)是一種可選地讓信息經過的方式。 它由一個Sigmoid神經網絡層和一個點乘法運算組成。
Sigmoid神經網絡層輸出0和1之間的數字,這個數字描述每一個組件有多少信息能夠經過, 0表示不經過任何信息,1表示所有經過
LSTM有三個門,用於保護和控制細胞的狀態。
LSTM的第一步是決定咱們要從細胞狀態中丟棄什麼信息。 該決定由被稱爲「忘記門」的Sigmoid層實現。它查看ht-1(前一個輸出)和xt(當前輸入),併爲單元格狀態Ct-1(上一個狀態)中的每一個數字輸出0和1之間的數字。1表明徹底保留,而0表明完全刪除。
讓咱們回到語言模型的例子,試圖根據之前的語料來預測下一個單詞。 在這樣的問題中,細胞狀態可能包括當前主題的性別,從而決定使用正確的代詞。 當咱們看到一個新主題時,咱們想要忘記舊主題的性別。
下一步是決定咱們要在細胞狀態中存儲什麼信息。 這部分分爲兩步。 首先,稱爲「輸入門層」的Sigmoid層決定了咱們將更新哪些值。 接下來一個tanh層建立候選向量Ct,該向量將會被加到細胞的狀態中。 在下一步中,咱們將結合這兩個向量來建立更新值。
在咱們的語言模型的例子中,咱們但願將新主題的性別添加到單元格狀態,以替換咱們忘記的舊對象。
如今是時候去更新上一個狀態值Ct−1了,將其更新爲Ct。簽名的步驟以及決定了應該作什麼,咱們只需實際執行便可。
咱們將上一個狀態值乘以ft,以此表達期待忘記的部分。以後咱們將獲得的值加上 it∗C̃ t。這個獲得的是新的候選值, 按照咱們決定更新每一個狀態值的多少來衡量.
在語言模型的例子中,對應着實際刪除關於舊主題性別的信息,並添加新信息,正如在以前的步驟中描述的那樣。
最後,咱們須要決定咱們要輸出什麼。 此輸出將基於咱們的細胞狀態,但將是一個過濾版本。 首先,咱們運行一個sigmoid層,它決定了咱們要輸出的細胞狀態的哪些部分。 而後,咱們將單元格狀態經過tanh(將值規範化到-1和1之間),並將其乘以Sigmoid門的輸出,至此咱們只輸出了咱們決定的那些部分。
對於語言模型的例子,因爲只看到一個主題,考慮到後面可能出現的詞,它可能須要輸出與動詞相關的信息。 例如,它可能會輸出主題是單數仍是複數,以便咱們知道動詞應該如何組合在一塊兒。
三、股票預測實戰1
在對理論有理解的基礎上,咱們使用LSTM對股票進行預測。
環境配置以下:
步驟1、導入數據:
import math
import numpy as np
import pandas as pd
class DataLoader():
"""A class for loading and transforming data for the lstm model"""
def __init__(self, filename, split, cols):
dataframe = pd.read_csv(filename)
i_split = int(len(dataframe) * split)
self.data_train = dataframe.get(cols).values[:i_split]
self.data_test = dataframe.get(cols).values[i_split:]
self.len_train = len(self.data_train)
self.len_test = len(self.data_test)
self.len_train_windows = None
def get_test_data(self, seq_len, normalise):
'''
Create x, y test data windows
Warning: batch method, not generative, make sure you have enough memory to
load data, otherwise reduce size of the training split.
'''
data_windows = []
for i in range(self.len_test - seq_len):
data_windows.append(self.data_test[i:i+seq_len])
data_windows = np.array(data_windows).astype(float)
data_windows = self.normalise_windows(data_windows, single_window=False) if normalise else data_windows
x = data_windows[:, :-1]
y = data_windows[:, -1, [0]]
return x,y
def get_train_data(self, seq_len, normalise):
'''
Create x, y train data windows
Warning: batch method, not generative, make sure you have enough memory to
load data, otherwise use generate_training_window() method.
'''
data_x = []
data_y = []
for i in range(self.len_train - seq_len):
x, y = self._next_window(i, seq_len, normalise)
data_x.append(x)
data_y.append(y)
return np.array(data_x), np.array(data_y)
def generate_train_batch(self, seq_len, batch_size, normalise):
'''Yield a generator of training data from filename on given list of cols split for train/test'''
i = 0
while i < (self.len_train - seq_len):
x_batch = []
y_batch = []
for b in range(batch_size):
if i >= (self.len_train - seq_len):
# stop-condition for a smaller final batch if data doesn't divide evenly
yield np.array(x_batch), np.array(y_batch)
i = 0
x, y = self._next_window(i, seq_len, normalise)
x_batch.append(x)
y_batch.append(y)
i += 1
yield np.array(x_batch), np.array(y_batch)
def _next_window(self, i, seq_len, normalise):
'''Generates the next data window from the given index location i'''
window = self.data_train[i:i+seq_len]
window = self.normalise_windows(window, single_window=True)[0] if normalise else window
x = window[:-1]
y = window[-1, [0]]
return x, y
def normalise_windows(self, window_data, single_window=False):
'''Normalise window with a base value of zero'''
normalised_data = []
window_data = [window_data] if single_window else window_data
for window in window_data:
normalised_window = []
for col_i in range(window.shape[1]):
normalised_col = [((float(p) / float(window[0, col_i])) - 1) for p in window[:, col_i]]
normalised_window.append(normalised_col)
normalised_window = np.array(normalised_window).T # reshape and transpose array back into original multidimensional format
normalised_data.append(normalised_window)
return np.array(normalised_data)
步驟2、定義模型
import os
import math
import numpy as np
import datetime as dt
from numpy import newaxis
from core.utils import Timer
from keras.layers import Dense, Activation, Dropout, LSTM
from keras.models import Sequential, load_model
from keras.callbacks import EarlyStopping, ModelCheckpoint
class Model():
"""A class for an building and inferencing an lstm model"""
def __init__(self):
self.model = Sequential()
def load_model(self, filepath):
print('[Model] Loading model from file %s' % filepath)
self.model = load_model(filepath)
def build_model(self, configs):
timer = Timer()
timer.start()
for layer in configs['model']['layers']:
neurons = layer['neurons'] if 'neurons' in layer else None
dropout_rate = layer['rate'] if 'rate' in layer else None
activation = layer['activation'] if 'activation' in layer else None
return_seq = layer['return_seq'] if 'return_seq' in layer else None
input_timesteps = layer['input_timesteps'] if 'input_timesteps' in layer else None
input_dim = layer['input_dim'] if 'input_dim' in layer else None
if layer['type'] == 'dense':
self.model.add(Dense(neurons, activation=activation))
if layer['type'] == 'lstm':
self.model.add(LSTM(neurons, input_shape=(input_timesteps, input_dim), return_sequences=return_seq))
if layer['type'] == 'dropout':
self.model.add(Dropout(dropout_rate))
self.model.compile(loss=configs['model']['loss'], optimizer=configs['model']['optimizer'])
print('[Model] Model Compiled')
timer.stop()
def train(self, x, y, epochs, batch_size, save_dir):
timer = Timer()
timer.start()
print('[Model] Training Started')
print('[Model] %s epochs, %s batch size' % (epochs, batch_size))
save_fname = os.path.join(save_dir, '%s-e%s.h5' % (dt.datetime.now().strftime('%d%m%Y-%H%M%S'), str(epochs)))
callbacks = [
EarlyStopping(monitor='val_loss', patience=2),
ModelCheckpoint(filepath=save_fname, monitor='val_loss', save_best_only=True)
]
self.model.fit(
x,
y,
epochs=epochs,
batch_size=batch_size,
callbacks=callbacks
)
self.model.save(save_fname)
print('[Model] Training Completed. Model saved as %s' % save_fname)
timer.stop()
def train_generator(self, data_gen, epochs, batch_size, steps_per_epoch, save_dir):
timer = Timer()
timer.start()
print('[Model] Training Started')
print('[Model] %s epochs, %s batch size, %s batches per epoch' % (epochs, batch_size, steps_per_epoch))
save_fname = os.path.join(save_dir, '%s-e%s.h5' % (dt.datetime.now().strftime('%d%m%Y-%H%M%S'), str(epochs)))
callbacks = [
ModelCheckpoint(filepath=save_fname, monitor='loss', save_best_only=True)
]
self.model.fit_generator(
data_gen,
steps_per_epoch=steps_per_epoch,
epochs=epochs,
callbacks=callbacks,
workers=1
)
print('[Model] Training Completed. Model saved as %s' % save_fname)
timer.stop()
def predict_point_by_point(self, data):
#Predict each timestep given the last sequence of true data, in effect only predicting 1 step ahead each time
print('[Model] Predicting Point-by-Point...')
predicted = self.model.predict(data)
predicted = np.reshape(predicted, (predicted.size,))
return predicted
def predict_sequences_multiple(self, data, window_size, prediction_len):
#Predict sequence of 50 steps before shifting prediction run forward by 50 steps
print('[Model] Predicting Sequences Multiple...')
prediction_seqs = []
for i in range(int(len(data)/prediction_len)):
curr_frame = data[i*prediction_len]
predicted = []
for j in range(prediction_len):
predicted.append(self.model.predict(curr_frame[newaxis,:,:])[0,0])
curr_frame = curr_frame[1:]
curr_frame = np.insert(curr_frame, [window_size-2], predicted[-1], axis=0)
prediction_seqs.append(predicted)
return prediction_seqs
def predict_sequence_full(self, data, window_size):
#Shift the window by 1 new prediction each time, re-run predictions on new window
print('[Model] Predicting Sequences Full...')
curr_frame = data[0]
predicted = []
for i in range(len(data)):
predicted.append(self.model.predict(curr_frame[newaxis,:,:])[0,0])
curr_frame = curr_frame[1:]
curr_frame = np.insert(curr_frame, [window_size-2], predicted[-1], axis=0)
return predicted
步驟3、模型訓練
import os
import json
import time
import math
import matplotlib.pyplot as plt
from core.data_processor import DataLoader
from core.model import Model
def plot_results(predicted_data, true_data):
fig = plt.figure(facecolor='white')
ax = fig.add_subplot(111)
ax.plot(true_data, label='True Data')
plt.plot(predicted_data, label='Prediction')
plt.legend()
plt.show()
def plot_results_multiple(predicted_data, true_data, prediction_len):
fig = plt.figure(facecolor='white')
ax = fig.add_subplot(111)
ax.plot(true_data, label='True Data')
# Pad the list of predictions to shift it in the graph to it's correct start
for i, data in enumerate(predicted_data):
padding = [None for p in range(i * prediction_len)]
plt.plot(padding + data, label='Prediction')
plt.legend()
plt.show()
def main():
configs = json.load(open('config.json', 'r'))
if not os.path.exists(configs['model']['save_dir']): os.makedirs(configs['model']['save_dir'])
data = DataLoader(
os.path.join('data', configs['data']['filename']),
configs['data']['train_test_split'],
configs['data']['columns']
)
model = Model()
model.build_model(configs)
x, y = data.get_train_data(
seq_len=configs['data']['sequence_length'],
normalise=configs['data']['normalise']
)
'''
# in-memory training
model.train(
x,
y,
epochs = configs['training']['epochs'],
batch_size = configs['training']['batch_size'],
save_dir = configs['model']['save_dir']
)
'''
# out-of memory generative training
steps_per_epoch = math.ceil((data.len_train - configs['data']['sequence_length']) / configs['training']['batch_size'])
model.train_generator(
data_gen=data.generate_train_batch(
seq_len=configs['data']['sequence_length'],
batch_size=configs['training']['batch_size'],
normalise=configs['data']['normalise']
),
epochs=configs['training']['epochs'],
batch_size=configs['training']['batch_size'],
steps_per_epoch=steps_per_epoch,
save_dir=configs['model']['save_dir']
)
x_test, y_test = data.get_test_data(
seq_len=configs['data']['sequence_length'],
normalise=configs['data']['normalise']
)
predictions = model.predict_sequences_multiple(x_test, configs['data']['sequence_length'], configs['data']['sequence_length'])
# predictions = model.predict_sequence_full(x_test, configs['data']['sequence_length'])
# predictions = model.predict_point_by_point(x_test)
plot_results_multiple(predictions, y_test, configs['data']['sequence_length'])
# plot_results(predictions, y_test)
if __name__ == '__main__':
main()
最終運行結果顯示: