데이터분석/예시코드

LSTM으로 주식을 예측

이규승 2022. 6. 3. 11:52
728x90

LSTM으로 삼성주식을 예측해보자

 

Colab을 이용한다. 필요한 라이브러리 다운로드해준다.

!pip install finance-datareader

 

필요한 import 해주기

# LSTM으로 주식 예측
# 삼성전자 : 코드 005930

import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
import warnings
warnings.filterwarnings('ignore')
import FinanceDataReader as fdr

 

데이터 불러오기

STOCK_CODE = '005930'
stock_data = fdr.DataReader(STOCK_CODE)
print(stock_data.head(3))
print(stock_data.shape)
print(stock_data.tail(3))
print(stock_data.index)

ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ

            Open  High   Low  Close  Volume    Change
Date                                                 
1998-04-04  1072  1143  1071   1115  202157       NaN
1998-04-06  1116  1176  1092   1146  312720  0.027803
1998-04-07  1149  1261  1149   1244  480900  0.085515
(6000, 6)
             Open   High    Low  Close    Volume    Change
Date                                                      
2022-05-30  67500  67800  66900  67700  14255484  0.018045
2022-05-31  67500  67500  66700  67400  24365002 -0.004431
2022-06-02  66600  67000  66500  66600   9094972 -0.011869
DatetimeIndex(['1998-04-04', '1998-04-06', '1998-04-07', '1998-04-08',
               '1998-04-09', '1998-04-10', '1998-04-11', '1998-04-13',
               '1998-04-14', '1998-04-15',
               ...
               '2022-05-19', '2022-05-20', '2022-05-23', '2022-05-24',
               '2022-05-25', '2022-05-26', '2022-05-27', '2022-05-30',
               '2022-05-31', '2022-06-02'],
              dtype='datetime64[ns]', name='Date', length=6000, freq=None)

 

날짜 컬럼을 추가해준다

stock_data['year'] = stock_data.index.year
stock_data['month'] = stock_data.index.month
stock_data['day'] = stock_data.index.day
print(stock_data.head(3))

ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
            Open  High   Low  Close  Volume    Change  year  month  day
Date                                                                   
1998-04-04  1072  1143  1071   1115  202157       NaN  1998      4    4
1998-04-06  1116  1176  1092   1146  312720  0.027803  1998      4    6
1998-04-07  1149  1261  1149   1244  480900  0.085515  1998      4    7

 

데이터를 시각화로 보기

# 시각화
plt.figure(figsize=(10,6))
sns.lineplot(y=stock_data['Close'], x=stock_data.index)
plt.xlabel('time')
plt.ylabel('price')
plt.show()

삼성전자 주식 데이터 시각화

 

연도별 5년씩 나눠서 시각화 그래프로 보기

time_steps = [['2000','2005'],['2005','2010'],['2010','2015'],['2015','2022']]

fig, axes = plt.subplots(2,2)
fig.set_size_inches(10, 6)
for i in range(4):
  ax = axes[i//2, i%2]
  df = stock_data[(stock_data.index > time_steps[i][0]) & (stock_data.index < time_steps[i][1])]
  sns.lineplot(y=df['Close'], x=df.index, ax=ax)
  ax.set_title(f'{time_steps[i][0]} ~ {time_steps[i][1]}')
  ax.set_xlabel('time')
  ax.set_ylabel('price')

plt.tight_layout()
plt.show()

5년씩 나누어 시각화

 

데이터 전처리해준다.

MinMax 정규화 해주기

# 데이터 전처리
# 정규화
from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler()
scale_cols = ['Open','High','Low','Close','Volume']
scaled = scaler.fit_transform(stock_data[scale_cols])
print(scaled)

df = pd.DataFrame(scaled, columns=scale_cols)
print(df.head(3))

ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
[[0.01187154 0.01180785 0.01196648 0.00527877 0.00223857]
 [0.0123588  0.01214876 0.01220112 0.00562183 0.00346289]
 [0.01272425 0.01302686 0.01283799 0.00670636 0.00532522]
 ...
 [0.74750831 0.70041322 0.74748603 0.74214825 0.15785724]
 [0.74750831 0.69731405 0.7452514  0.73882827 0.26980438]
 [0.73754153 0.69214876 0.74301676 0.72997499 0.10071262]]
 
        Open      High       Low     Close    Volume
0  0.011872  0.011808  0.011966  0.005279  0.002239
1  0.012359  0.012149  0.012201  0.005622  0.003463
2  0.012724  0.013027  0.012838  0.006706  0.005325

 

대량의 데이터를 사용하기 위해 sequential dataset 이용

 

#tensorflow dataset을 이용해서 sequential dataset을 운영
import tensorflow as tf

def window_dataset_func(series, window_size, batch_size, shuffle_tf):
  series = tf.expand_dims(series, axis= -1) # 차원확대
  # print(series.shape)
  ds = tf.data.Dataset.from_tensor_slices(series) #slice
  ds = ds.window(window_size +1, shift=1, drop_remainder=True)
  ds = ds.flat_map(lambda w:w.batch(window_size+1)) # Dataset 객체를 객체화
  if shuffle_tf:
    ds = ds.shuffle(1000)
  ds = ds.map(lambda w : (w[:-1], w[-1]))
  return ds.batch(batch_size).prefetch(1) # 훈련 속도 증진, 학습 성능 향상
  # 대량의 데이터를 메모리에 로딩할 수 없으므로 batch 단위로 읽어 처리하기 위해 준비


WINDOW_SIZE = 20 # 20일 단위로 비교
BATCH_SIZE = 32

# train_data, test_data를 생성
train_data = window_dataset_func(y_train,WINDOW_SIZE,BATCH_SIZE,True)
test_data = window_dataset_func(y_test,WINDOW_SIZE,BATCH_SIZE,False) # test_data는 섞지 않는다.

for data in train_data.take(1):
  print('dataset 구성(batch_size, window_size, feature) : ', data[0].shape)
  print('dataset 구성(batch_size, window_size, feature) : ', data[1].shape)
  
  ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
dataset 구성(batch_size, window_size, feature) :  (32, 20, 1)
dataset 구성(batch_size, window_size, feature) :  (32, 1)

 

모델

# 모델
from keras.models import Sequential
from keras.layers import Dense, Conv1D, LSTM
from keras.losses import Huber # cost function의 일종으로 mse 보다 이상치에 덜 민감함
from tensorflow.keras.optimizers import Adam
from keras.callbacks import EarlyStopping, ModelCheckpoint

model = Sequential([
       # 1차원 feature map 생성
       Conv1D(filters=32, kernel_size=5, padding='causal',
              activation='relu', input_shape=[WINDOW_SIZE, 1]), # padding='casual' 모델이 시간 순서를 지켜야하느 경우의 모델 생성           
       LSTM(16, activation='tanh'),
       Dense(16, activation='relu'),
       Dense(1)
])

loss = Huber()
optimizer = Adam(learning_rate = 0.0005)
model.compile(optimizer=optimizer, loss=loss, metrics=['mse'])
print(model.summary())

ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
Epoch 14/50
150/150 - 4s - loss: 2.1294e-05 - mse: 4.2588e-05 - val_loss: 4.2450e-04 - val_mse: 8.4900e-04 - 4s/epoch - 29ms/step

 

예측

pred = model.predict(test_data)
print('pred.shape:' , pred.shape)
print('pred:',pred[:10].flatten())
print('real:',np.array(y_test)[:10])

ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ

pred.shape: (1180, 1)
pred: [0.51625794 0.51122445 0.5073114  0.5053738  0.5048668  0.5052125
 0.5059609  0.5070877  0.50846165 0.5095983 ]
real: [0.55335207 0.55556539 0.55445873 0.55954937 0.55822138 0.55578672
 0.54626945 0.54449879 0.54405613 0.52148027]

 

시각화

# 시각화
plt.figure(figsize=(10, 6))
plt.plot(np.asarray(y_test)[20:], label='real')
plt.plot(pred, label='predict')
plt.legend()
plt.show()

 

728x90