2019년 5월 4일 토요일

LSTM 예제 코드 분석 (predicting stock price)


LSTM 에 대한 이론적인 설명은 조금만 검색해봐도 알 수 있습니다. 그래서 예제를 통한 입력 형태와 출력를 살펴 보았습니다.

예제 소스는 아래 stock price predicting 주제중에 제일 마지막 아래 있는 LSTM 예제입니다. 해당 소스를 분석하여 설명을 추가 하였습니다.

https://www.analyticsvidhya.com/blog/2018/10/predicting-stock-price-machine-learningnd-deep-learning-techniques-python/

일단 실행 가능한 소스를 공유합니다. 위 링크의 소스가 부분 부분 나뉘어져 있어서 막상 실행하려고 하면 실행이 안되어 약간 수정 및 로그를 추가 하였습니다.

Source
#import packages
import pandas as pd
import numpy as np

#to plot within notebook
import matplotlib.pyplot as plt
#주피터 노트북을 사용하지 않기때문에 삭제하였습니다.
#%matplotlib inline

#setting figure size
from matplotlib.pylab import rcParams
# 차트의 기본 크기를 설정합니다. 
rcParams['figure.figsize'] = 8,4 #그림(figure)의 크기. (가로,세로) 인치 단위

#for normalizing data
from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler(feature_range=(0, 1))

#read the file
df = pd.read_csv('NSE-TATAGLOBAL11.csv')
#Date,Open,High,Low,Last,Close,Total Trade Quantity,Turnover (Lacs)
#2018-10-08,208.0,222.25,206.85,216.0,215.15,4642146.0,10062.83

#print the head
print(df.head())

#importing required libraries
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import Dense, Dropout, LSTM

#creating dataframe
# index를 sort함 sort 의미는 없음 
data = df.sort_index(ascending=True, axis=0)
print(data.head())

#빈 dataframe을 만듭니다 갯수는 df 갯수만큼
#  Date Close
#0  NaN   NaN
#1  NaN   NaN
#2  NaN   NaN
new_data = pd.DataFrame(index=range(0,len(df)),columns=['Date', 'Close'])
print(new_data.head())

# 앞에서 만든 빈 new_data 에 Date, Close만 넣습니다.
#이런식으로 안해도 될텐데...
for i in range(0,len(data)):
    new_data['Date'][i] = data['Date'][i]
    new_data['Close'][i] = data['Close'][i]
print(new_data.head())

#setting index
#index를 date로 설정합니다.
new_data.index = new_data.Date
#그러면 date는 필요없어지니 삭제합니다
new_data.drop('Date', axis=1, inplace=True)

#creating train and test sets
# numpy로 변환합니다. 1차원인 Close만 남습니다
dataset = new_data.values

train = dataset[0:987,:]
valid = dataset[987:,:]
# 각각 아래 데이터가 생성 되었습니다.
#(987, 1)
#(248, 1)
print(train.shape)
print(valid.shape)

#converting dataset into x_train and y_train
#값을 0 ~ 1 사이의 값으로 변환 합니다
scaler = MinMaxScaler(feature_range=(0, 1))
scaled_data = scaler.fit_transform(dataset)

x_train, y_train = [], []
for i in range(60,len(train)):
    x_train.append(scaled_data[i-60:i,0])
    y_train.append(scaled_data[i,0])

# x_train [0:60]=>0:59까지데이터 y_train [60]
x_train, y_train = np.array(x_train), np.array(y_train)
#(927, 60) (927,)
# x_train 60깨 input으로 가지는 데이터가 927개 들어있다.
print(x_train.shape,y_train.shape)
print(x_train)

# 입력 format에 맞게 변환 합니다.
x_train = np.reshape(x_train, (x_train.shape[0],x_train.shape[1],1))
print("input_shape")
print(x_train.shape,y_train.shape)
print(x_train)

# create and fit the LSTM network
model = Sequential()
# x_train.shape[1] 이것은 timestep이 된다
model.add(LSTM(units=50, return_sequences=True, input_shape=(x_train.shape[1],1)))
model.add(LSTM(units=50))
model.add(Dense(1))

model.compile(loss='mean_squared_error', optimizer='adam')
model.fit(x_train, y_train, epochs=1, batch_size=1, verbose=2)

# 입력 데이터중 마지막 데이터를 가지고 valid 데이터를 만듭니다.
#predicting 246 values, using past 60 from the train data
inputs = new_data[len(new_data) - len(valid) - 60:].values
inputs = inputs.reshape(-1,1)
inputs  = scaler.transform(inputs)

X_test = []
for i in range(60,inputs.shape[0]):
    X_test.append(inputs[i-60:i,0])
X_test = np.array(X_test)

X_test = np.reshape(X_test, (X_test.shape[0],X_test.shape[1],1))

closing_price = model.predict(X_test)
print(closing_price.shape)
closing_price = scaler.inverse_transform(closing_price)

rms=np.sqrt(np.mean(np.power((valid-closing_price),2)))
print(rms)

#for plotting
train = new_data[:987]
valid = new_data[987:]
valid['Predictions'] = closing_price
plt.plot(train['Close'])
plt.plot(valid[['Close','Predictions']])
plt.show()


입력으로 사용하는 NSE-TATAGLOBAL11.csv 파일은 원본 링크의 아래 부분에서 다운로드 하였습니다.
Note: Here is the dataset I used for the code: Download
https://s3-ap-south-1.amazonaws.com/av-blog-media/wp-content/uploads/2019/03/NSE-TATAGLOBAL11.csv

NSE-TATAGLOBAL11.csv 
Date,Open,High,Low,Last,Close,Total Trade Quantity,Turnover (Lacs)
2018-10-08,208.0,222.25,206.85,216.0,215.15,4642146.0,10062.83
2018-10-05,217.0,218.6,205.9,210.25,209.2,3519515.0,7407.06
2018-10-04,223.5,227.8,216.15,217.25,218.2,1728786.0,3815.79
2018-10-03,230.0,237.5,225.75,226.45,227.6,1708590.0,3960.27
2018-10-01,234.55,234.6,221.05,230.3,230.9,1534749.0,3486.05
2018-09-28,234.05,235.95,230.2,233.5,233.75,3069914.0,7162.35
2018-09-27,234.55,236.8,231.1,233.8,233.25,5082859.0,11859.95
2018-09-26,240.0,240.0,232.5,235.0,234.25,2240909.0,5248.6
2018-09-25,233.3,236.75,232.0,236.25,236.1,2349368.0,5503.9
2018-09-24,233.55,239.2,230.75,234.0,233.3,3423509.0,7999.55
2018-09-21,235.0,237.0,227.95,233.75,234.6,5395319.0,12589.59
2018-09-19,235.95,237.2,233.45,234.6,234.9,1362058.0,3202.78
...(생략)...


Output
         Date    Open    High     Low    Last   Close  Total Trade Quantity  Turnover (Lacs)
0  2018-10-08  208.00  222.25  206.85  216.00  215.15             4642146.0         10062.83
1  2018-10-05  217.00  218.60  205.90  210.25  209.20             3519515.0          7407.06
2  2018-10-04  223.50  227.80  216.15  217.25  218.20             1728786.0          3815.79
3  2018-10-03  230.00  237.50  225.75  226.45  227.60             1708590.0          3960.27
4  2018-10-01  234.55  234.60  221.05  230.30  230.90             1534749.0          3486.05
Using TensorFlow backend.
         Date    Open    High     Low    Last   Close  Total Trade Quantity  Turnover (Lacs)
0  2018-10-08  208.00  222.25  206.85  216.00  215.15             4642146.0         10062.83
1  2018-10-05  217.00  218.60  205.90  210.25  209.20             3519515.0          7407.06
2  2018-10-04  223.50  227.80  216.15  217.25  218.20             1728786.0          3815.79
3  2018-10-03  230.00  237.50  225.75  226.45  227.60             1708590.0          3960.27
4  2018-10-01  234.55  234.60  221.05  230.30  230.90             1534749.0          3486.05
  Date Close
0  NaN   NaN
1  NaN   NaN
2  NaN   NaN
3  NaN   NaN
4  NaN   NaN
         Date   Close
0  2018-10-08  215.15
1  2018-10-05   209.2
2  2018-10-04   218.2
3  2018-10-03   227.6
4  2018-10-01   230.9
(987, 1)
(248, 1)
C:\ProgramData\Anaconda3\lib\site-packages\sklearn\utils\validation.py:595: DataConversionWarning: Data with input dtype object was converted to float64 by MinMaxScaler.
  warnings.warn(msg, DataConversionWarning)
(927, 60) (927,)
[[0.50425818 0.47758853 0.51792918 ... 0.72859704 0.7492156  0.77140296]
 [0.47758853 0.51792918 0.56006275 ... 0.7492156  0.77140296 0.77364411]
 [0.51792918 0.56006275 0.57485433 ... 0.77140296 0.77364411 0.73352757]
 ...
 [0.23576871 0.24518153 0.23733752 ... 0.23330345 0.22725235 0.22277006]
 [0.24518153 0.23733752 0.22882116 ... 0.22725235 0.22277006 0.24092335]
 [0.23733752 0.22882116 0.20528911 ... 0.22277006 0.24092335 0.24585388]]
input_shape
(927, 60, 1) (927,)
[[[0.50425818]
  [0.47758853]
  [0.51792918]
  ...
  [0.72859704]
  [0.7492156 ]
  [0.77140296]]

 [[0.47758853]
  [0.51792918]
  [0.56006275]
  ...
  [0.7492156 ]
  [0.77140296]
  [0.77364411]]

 [[0.51792918]
  [0.56006275]
  [0.57485433]
  ...
  [0.77140296]
  [0.77364411]
  [0.73352757]]

 ...

 [[0.23576871]
  [0.24518153]
  [0.23733752]
  ...
  [0.23330345]
  [0.22725235]
  [0.22277006]]

 [[0.24518153]
  [0.23733752]
  [0.22882116]
  ...
  [0.22725235]
  [0.22277006]
  [0.24092335]]

 [[0.23733752]
  [0.22882116]
  [0.20528911]
  ...
  [0.22277006]
  [0.24092335]
  [0.24585388]]]
WARNING:tensorflow:From C:\ProgramData\Anaconda3\lib\site-packages\tensorflow\python\framework\op_def_library.py:263: colocate_with (from tensorflow.python.framework.ops) is deprecated and will be removed in a future version.
Instructions for updating:
Colocations handled automatically by placer.
WARNING:tensorflow:From C:\ProgramData\Anaconda3\lib\site-packages\tensorflow\python\ops\math_ops.py:3066: to_int32 (from tensorflow.python.ops.math_ops) is deprecated and will be removed in a future version.
Instructions for updating:
Use tf.cast instead.
Epoch 1/1
2019-05-04 18:53:28.235217: I tensorflow/core/platform/cpu_feature_guard.cc:141] Your CPU supports instructions that this TensorFlow binary was not compiled to use: AVX2
 - 40s - loss: 0.0040
(248, 1)
5.49749408305034
lstm_help.py:131: SettingWithCopyWarning:
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: http://pandas.pydata.org/pandas-docs/stable/indexing.html#indexing-view-versus-copy
  valid['Predictions'] = closing_price

소스 코드에 관한 상세한 설명은 주석으로 넣었습니다.
전체적인 코드 흐름은 다음과 같습니다.
1. cvs 파일 읽기
2. date, close 컬럼만 추출 (new_data), close 는 종가를 의미함
3. date를 index로 만들고 date 컬럼 삭제, close컬럼만 남음
4. train, valid 데이터 셋으로 분리
5. MinMaxScaler 를 이용하여 0~1 사이 값으로 변환
6. x_train, y_train 데이터 구성, 이때 x_train은 60개의 time_steps 값을 가짐, 즉 close의 이전 60개 값이 x_train이 되고 현재값이 y_train 의 값 1개가 된다.
7. x_train, y_train 를 numpy로 변환
8. LSTM의 입력 포맷에 맞게 변환, 3개 차원 [전체 데이터 갯수, time_steps, feature수 ] 여기에서는 time_series = 60, feature수는 'close'컬럼 하나이므로 feature수는 1이 된다.
9. LSTM 모델링
10. train
11. 마지막 데이터를 이용하여 valid 데이터를 만듭니다. 코드상은 아래 부분입니다.
inputs = new_data[len(new_data) - len(valid) - 60:].values
new_data는 전체 데이터이므로 전체 데이터-valid데이터 크기-60(time_steps 값)위치 부터 마지막까지의 데이터입니다. 60을 빼주는 이유는 valid데이터 위치를 예측 하기 위해서는 이전 60개의 데이터가 필요하기 때문입니다.
12. 위에서 만든 inputs를 이용해서 X_test 데이터를 만듭니다. predict하기 때문에 Y_test는 필요 없습니다.

train의 입력 출력이 가장 어려운 부분이라 그림으로 표시해 보았습니다.



댓글 없음:

댓글 쓰기