LSTM을 활용한 주식가격 예측

Page content

강의 홍보

LSTM과 RNN의 개요

  • RNN은 자연어처리에서 사용되는 대표적인 알고리즘
    • 순환신경망으로 표현됨
    • 활용범위: 음성 인식, 언어 모델링, 번역, 이미지 주석 생성

  • Long Short-term Memory로 1997년에 소개되었음(Hochreiter and Schmidhuber, 1997).

  • LSTM 네트워크는 recurrent neural network(RNN)의 한 종류임.

    • LSTM은 RNN의 특별한 한 종류로, 긴 의존 기간을 필요로 하는 학습을 수행할 능력을 갖고 있다.
  • 간단하게 영어 단어를 맞춰보자.

The clouds are in the ...

  • 위 빈칸에 들어갈 단어의 옵션은 많지 않다.

    • 즉, 우리는 위 단어가 clouds라는 것을 알게 된다.
  • 이번에는 조금 더 문장을 늘려나가보다.

I was born in Korea but grew up in France… Nevertheless, I speak fluent …

  • 한국어가 올지, 아니면 프랑스어가 올지 모른다.

  • 즉, 단기 기억은 RNN으로 해결할 수 있지만, 문장이 길어지고 복잡해지면 결과적으로 LSTM 알고리즘이 필요하다.

LSTM을 활용한 주식가격 예측 예제

  • 캐글 데이터를 활용하여 주식 가격 예측을 합니다.
import numpy as np # linear algebra
import pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)
import matplotlib.pyplot as plt
import seaborn as sns

(1) 데이터 불러오기

  • 데이터를 불러옵니다.
# Mount Google Drive
from google.colab import drive # import drive from google colab

ROOT = "/content/drive"     # default location for the drive
print(ROOT)                 # print content of ROOT (Optional)
drive.mount(ROOT)           # we mount the google drive at /content/drive
/content/drive
Drive already mounted at /content/drive; to attempt to forcibly remount, call drive.mount("/content/drive", force_remount=True).
from os.path import join  

MY_GOOGLE_DRIVE_PATH = 'My Drive/Colab Notebooks/ml_project' # 프로젝트 경로
PROJECT_PATH = join(ROOT, MY_GOOGLE_DRIVE_PATH) # 프로젝트 경로
print(PROJECT_PATH)
/content/drive/My Drive/Colab Notebooks/ml_project
%cd "{PROJECT_PATH}"
/content/drive/My Drive/Colab Notebooks/ml_project
stocks =  pd.read_csv('data/stock.csv', header=0)
stocks

(2) 데이터 전처리 및 시각화

  • 시각화를 위해 일자를 날짜형으로 변환한다.
stocks['일자'] = pd.to_datetime(stocks['일자'], format='%Y%m%d')
# stocks['일자'] = pd.to_datetime(stocks['일자'], format='%Y-%m-%d')
stocks['연도'] = stocks['일자'].dt.year
  • 연도는 1990년 이후로 재분류 한다.
df = stocks.loc[stocks['일자']>="1990"]
# df = stocks.loc[(stocks['일자']>="1990") & (df['column_name'] <= B)]
  • 이제 시각화를 작성한다.
  • 이 때, 거래량 & 종가를 기준으로 구분해보고, 차이점에 대해 확인해본다.
plt.figure(figsize=(16, 9))
sns.lineplot(y=df['거래량'], x=df['일자'])
plt.xlabel('time')
plt.ylabel('price')
Text(0, 0.5, 'price')

png

  • 거래량이 가장 많았던 시기는 IMF 전후로 일어났다.
    • 왜 이 시기에 거래가 많이 일어났을까?
plt.figure(figsize=(16, 9))
sns.lineplot(y=df['종가'], x=df['일자'])
plt.xlabel('time')
plt.ylabel('price')
Text(0, 0.5, 'price')

png

  • 주식 가격은 2000년대 이후로 계속적으로 우상향 하는 것을 확인 할 수 있다.
    • 삼성전자의 주식은 계속 오를까?
    • 언제까지 오를까?
    • 삼성전자의 주식이 큰폭으로 떨어지던 시기에는 무슨일이 있던 것일까?

(3) 데이터 정규화

  • 이제 데이터 정규화를 진행하도록 한다.
from sklearn.preprocessing import MinMaxScaler

df.sort_index(ascending=False).reset_index(drop=True)

scaler = MinMaxScaler()
scale_cols = ['시가', '고가', '저가', '종가', '거래량']
df_scaled = scaler.fit_transform(df[scale_cols])
df_scaled = pd.DataFrame(df_scaled)
df_scaled.columns = scale_cols

df_scaled
  • 정규화는 왜 하는 것일까?
    • 정규화는 무엇인가?
    • 정규화(MinMaxScaler)를 해주면 전체 데이터는 0, 1사이의 값을 갖도록 해준다.

(4) 시계열 데이터의 데이터셋 분리

  • 시계열 데이터의 데이터셋은 보통 window_size라고 정의한다.
    • window_size는 과거 기간의 주가 데이터에 기반하여 다음날의 종가를 예측할 것인가를 정하는 parameter이다.
    • 만약 과거 20일을 기반으로 내일 데이터를 예측한다라고 가정하면 window_size=20이 되는 것이다.
  • 실제 100일의 과거 데이터를 기반으로 데이터셋을 분리하도록 한다.
  • 이 때, make_dataset이라는 함수를 만든다.
    • feature_list & label list를 분리한다.
TEST_SIZE = 200
WINDOW_SIZE = 20

train = df_scaled[:-TEST_SIZE]
test = df_scaled[-TEST_SIZE:]
  • TEST_SIZE = 200은 학습은 과거부터 200일 이전의 데이터를 학습하게 되고, TEST를 위해서 이후 100일의 데이터로 모델이 주가를 예측하도록 한 다음, 실제 데이터와 오차가 얼마나 있는지 확인해 보도록 하겠습니다.
def make_dataset(data, label, window_size=20):
    feature_list = []
    label_list = []
    for i in range(len(data) - window_size):
        feature_list.append(np.array(data.iloc[i:i+window_size]))
        label_list.append(np.array(label.iloc[i+window_size]))
    return np.array(feature_list), np.array(label_list)
  • 이렇게 만든 함수를 이제 훈련데이터와 테스트데이터로 분리를 하도록 한다.
from sklearn.model_selection import train_test_split

feature_cols = ['시가', '고가', '저가', '거래량']
label_cols = ['종가']

train_feature = train[feature_cols]
train_label = train[label_cols]

train_feature, train_label = make_dataset(train_feature, train_label, 20)

x_train, x_valid, y_train, y_valid = train_test_split(train_feature, train_label, test_size=0.2)
x_train.shape, x_valid.shape
((6086, 20, 4), (1522, 20, 4))
test_feature = test[feature_cols]
test_label = test[label_cols]

test_feature.shape, test_label.shape
((200, 4), (200, 1))
test_feature, test_label = make_dataset(test_feature, test_label, 20)
test_feature.shape, test_label.shape
((180, 20, 4), (180, 1))

(5) 모형 학습

  • 이제 Keras 모형을 생성하여 학습을 진행합니다.
  • 먼저 모형을 만듭니다.
from keras.models import Sequential
from keras.layers import Dense
from keras.callbacks import EarlyStopping, ModelCheckpoint
from keras.layers import LSTM

model = Sequential()
model.add(LSTM(16, 
               input_shape=(train_feature.shape[1], train_feature.shape[2]), 
               activation='relu', 
               return_sequences=False)
          )

model.add(Dense(1))
  • 이제 모형을 학습합니다.
import os

model.compile(loss='mean_squared_error', optimizer='adam')
early_stop = EarlyStopping(monitor='val_loss', patience=5)

model_path = 'model'
filename = os.path.join(model_path, 'tmp_checkpoint.h5')
checkpoint = ModelCheckpoint(filename, monitor='val_loss', verbose=1, save_best_only=True, mode='auto')

history = model.fit(x_train, y_train, 
                                    epochs=200, 
                                    batch_size=16,
                                    validation_data=(x_valid, y_valid), 
                                    callbacks=[early_stop, checkpoint])
Epoch 1/200
375/381 [============================>.] - ETA: 0s - loss: 0.0089
Epoch 00001: val_loss improved from inf to 0.00013, saving model to model/tmp_checkpoint.h5
381/381 [==============================] - 3s 8ms/step - loss: 0.0088 - val_loss: 1.3481e-04
.
.
.
Epoch 42/200
380/381 [============================>.] - ETA: 0s - loss: 1.7859e-05
Epoch 00042: val_loss did not improve from 0.00002
381/381 [==============================] - 3s 7ms/step - loss: 1.7851e-05 - val_loss: 1.9842e-05

(6) 주가 예측

  • predict()를 활용하여 모형을 예측한다.
model.load_weights(filename)
pred = model.predict(test_feature)

pred.shape
(180, 1)
plt.figure(figsize=(12, 9))
plt.plot(test_label, label = 'actual')
plt.plot(pred, label = 'prediction')
plt.legend()
plt.show()

png

Reference

Lee, T. (2020, February 14). 딥러닝(LSTM)을 활용하여 삼성전자 주가 예측을 해보았습니다. Retrieved August 27, 2020, from https://teddylee777.github.io/tensorflow/LSTM으로-예측해보는-삼성전자-주가