Notice
Recent Posts
Recent Comments
Link
«   2025/05   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
Archives
Today
Total
관리 메뉴

악어새와 좀개구리밥

RNN으로 주가 예측하기 본문

DataScience

RNN으로 주가 예측하기

tiobi 2021. 11. 11. 11:11
728x90

RNN으로 주가 예측하기

GitHub링크

0. 글의 목적

데이터 사이언스 공부를 하다 보면 관성적으로 거치는 과정이 있다. 데이터 전처리부터 기계학습까지, 내가 기본적으로 거치는 과정을 간단하게 기록하려고 한다.

1. 주제 선정

나는 주로 이미지 학습이나 클래시피케이션 문제를 많이 풀어봤는데, 캐글이나 데이콘 문제를 보니 시계열 문제도 정말 많은 것 같다. 이번에는 시계열 예측 공부를 해보기 위해 텐서플로의 RNN 모델 두 가지를 사용해서 주가 예측을 해보기로 했다. 같은 입력과 구조를 가진 두 모델 중 어느 모델이 더 정확도가 높은지도 확인해보겠다.

최근 운영 미숙과 과도한 과금 정책, 기대에 못 미치는 신작 등으로 주가가 8개월만에 반토막이 나버린 엔씨소프트의 주식 데이터를 불러와 간단한 기계학습을 통해 주가 예측을 해보겠다.

2. 데이터 준비

엔씨소프트의 최근 10년 주가 그래프(출처:네이버증권)을 살펴보자. 주가는 몇 년동안 완만한 성장세를 보이다가 코로나19사태로 게임사의 매출이 급증하면서 2020년 주가가 급등했고, 최근 주가하락사태로 급락도 기록했다. 엔씨소프트의 주가는 완만한 상승, 하강, 급등, 급락이 모두 있는 자료라고 생각해서 시계열 예측 공부 하기에는 아주 적합하다고 생각한다.

3, 데이터 로드, 전처리

3-1. 데이터 로드

주가 데이터를 불러오기 위해 파이썬의 Finance Data Reader 라이브러리를 사용했다. Finance Data Reader는 Pandas Data Reader을 주식 데이터 위주로 다루기 위해 만들어진 라이브러리로, 웹 상의 데이터를 크롤링해서 데이터프레임을 반환한다.

3-1 데이터 로드 코드

# 라이브러리 호출
import FinanceDataReader as fdr
import numpy as np
import pandas as pd

# 데이터프레임 저장.
# 036570은 엔씨소프트의 종목코드이고, 2011년부터 2022년(최근)까지의
# 데이터를 불러왔다.
df = fdr.DataReader('036570', '2011', '2022')

데이터가 잘 불러와진 것을 확인할 수 있다.

3-2. 데이터 전처리

불러온 데이터는 학습 과정을 거치기 이전에 학습에 적합한 데이터인지 확인을 한 이후에 약간의 전처리 과정을 거쳐야 한다. 먼저, 결측 데이터가 있는지 확인해야 한다.

3-2-1. 결측 데이터 확인

# 데이터 전처리와 시각화 라이브러리
import missingno as msno
import matplotlib.pyplot as plt
import seaborn as sns

plt.style.use('seaborn')

# 결측 데이터 시각화
msno.matrix(df)

# 결측 데이터 수치화
for col in df.columns:
    print("Column: {:>10}. Number of NaN data: {:5}".
    format(col, df[col].isnull().any())

결측 데이터가 있는지 확인을 해보았지만, 아무런 결측 데이터가 없다는 것을 확인할 수 있었다. 결측 데이터가 존재한다면, 다양한 방법을 통해서 결측 데이터를 메꾸거나 학습 과정에서 제외해야 한다. 예를 들어, 수치 데이터는 중간값, 중앙값을 사용할 수 있고, 문자열 데이터는 최빈값 등으로 대체할 수 있다.

4. 데이터 시각화

# 2x2 그래프 생성
fig, ax = plt.subplots(2,2, figsize = (18, 20))

df['High'].plot.line(ax = ax[0][0], c = 'r')
ax[0][0].set_title("Stock Price High")

df['Low'].plot.line(ax = ax[0][1], c = 'b')
ax[0][1].set_title("Stock Price Low")

df['Open'].plot.line(ax = ax[1][0], c = 'g')
ax[1][0].set_title("Open Stock Price")

df['Close'].plot.line(ax = ax[1][1], c = 'y')
ax[1][1].set_title("Close Stock Price")

데이터의 특성을 확인하기 위해 시각화를 진행했다. 각 데이터 사이에서 큰 자이는 존재하지 않는 것으로 확인이 되므로, 어떤 데이터를 사용해도 괜찮다는 판단 하에, 종가 데이터를 사용해서 모델 학습을 진행해보기로 했다.

5. 입력 데이터 전처리

5-1. 데이터 정규화

기계학습 과정 이전에 입력 데이터를 모델에 가장 알맞은 형태로 변환을 해야 한다. 먼저, 사이킷런 라이브러리를 활용해서 데이터를 정규화 했다.

# 입력 데이터 전처리 라이브러리.
from sklearn.preprocessing import MinMaxScaler

# 데이터 정규화
scaler = MinMaxScaler(feature_range=(0, 1))
stock_close_data_scaled = scaler.fit_transform(stock_close_data)
print("stock close data shape: ", stock_close_data_scaled.shape)

5-2. 데이터 윈도우 표준화

다음, 데이터를 작은 배치 단위로 표준화할 수 있도록 윈도우 표준화 과정을 거쳤다.

input_data = []
input_y = []

# 윈도우 크기
timestamps = 50

# 표준화 진행
for i in range(timestamps, len(stock_close_data_scaled)):
    input_data.append(stock_close_data_scaled[i - timestamps:i,0])
    input_y.append(stock_close_data_scaled[i,0])

# np.array로 변환
input_data = np.array(input_data)
input_y = np.array(input_y)

print("input data shape: ", input_data.shape)
print("input y shape: ", input_y.shape) 

이 과정에서 윈도우의 크기를 조금 크게 한 것이 아닌가 하는 생각이 든다. 윈도우의 크기가 크면 학습 과정에서는 유리하게 작용할 수 있지만, 실제 데이터를 처리하기에는 조금 힘들지 않을까 싶다. 이 부분은 조금 더 공부를 해보아야겠다.

5-3. 입력 변환, 데이터셋 구분

마지막으로, 데이터를 모델 입력 형식으로 변환하고, 학습, 검증, 테스트 데이터로 구분했다. 각 데이터는 일반적으로 많이 사용되는 6:4:4 비율을 따랐다.

input_data = input_data.reshape(input_data.shape[0], input_data.shape[1], 1)
input_y = input_y.reshape(input_y.shape[0], 1)

print("input data shape: ", input_data.shape)
print("input y shape: ", input_y.shape)

train_data = input_data[:1674]
train_y = input_y[:1674]

validation_data = input_data[1674:2174]
validation_y = input_y[1674:2174]

test_data = input_data[2174:2674]
test_y = input_y[2174:2674]

print("train data shape: ", train_data.shape)
print("train y shape: ", train_y.shape)

print("validation data shape: ", validation_data.shape)
print("validation y shape: ", validation_y.shape)

print("test data shape: ", test_data.shape)
print("test y shape: ", test_y.shape)

6. 기계 학습

6-1. 모델 구성

데이터 전처리 과정이 모두 끝났으므로, 모델을 구성해서 학습 과정을 거쳐야 한다. 이 실험에서는 서로 다른 두 개의 RNN모델을 활용해서 그 두 모델간의 성능 차이를 비교해보기 위해 두 개의 모델을 만들었다. 입력 데이터의 크기가 크지 않고, 차원의 수도 작으므로, 모델은 최대한 단순하게 만들어 과적합을 방지했다.

# 기계 학습 라이브러리
from keras.layers import Dense, SimpleRNN, LSTM
import tensorflow
# SimpleRNN 모델
model = tensorflow.keras.models.Sequential()
model.add(SimpleRNN(units = 10, activation = 'tanh', input_shape = (50, 1)))
model.add(Dense(1))
model.compile(optimizer = 'adam', loss = 'mse', metrics=['mae'])
# LSTM 모델
model2 = tensorflow.keras.models.Sequential()
model2.add(LSTM(units = 10, activation = 'tanh', input_shape = (50, 1)))
model2.add(Dense(1))
model2.compile(optimizer = 'adam', loss = 'mse', metrics=['mae'])

6-2. 학습 진행

두 개의 모델이 모두 준비되었으므로, 각 모델에 대한 학습을 진행한다.

6-2-1. SimpleRNN 모델

# SimpleRNN 모델
hist = model.fit(
    train_data, train_y,
    batch_size = 16, epochs = 1000,
    validation_data = (validation_data, validation_y)
)

6-2-2. LSTM 모델

# LSTM 모델
hist2 = model2.fit(
    train_data, train_y,
    batch_size = 16, epochs = 1000,
    validation_data = (validation_data, validation_y)
)

7. 예측 결과 확인

학습이 완료되었으므로, 최근 데이터를 입력으로 하는 모델 예측을 진행한다. SimpleRNN과 LSTM 모델에 동일한 데이터를 입력하고, 시각화를 통해 어떤 모델의 성능이 더 좋은지 확인해본다.

7-1. 예측 결과 저장

# 모델 예측 결과
prediction_rnn = model.predict(test_data)
prediction_lstm = model2.predict(test_data)

7-2. 예측 결과 시각화

# 예측 결과 시각화
plt.figure(figsize=(12, 8))
plt.plot(test_y, label = "Actual Data")
plt.plot(prediction_rnn, label = "RNN Predictions")
plt.plot(prediction_lstm, label = "LSTM Predictions")
plt.legend()

8. 결론

Tensorflow의 RNN 모델들을 통해 주가의 변동을 어느 정도 정확하게 예측할 수 있다는 것을 확인했다. SimpleRNN봐 LSTM의 성능이 조금 더 정확한 것을 확인할 수 있었는데, 실제 기계학습에서도 SimpleRNN보다는 GRU나 LSTM을 더 많이 사용하는 것으로 알려져 있다. 데이터 전처리 과정에서 윈도우 크기를 조금 크게 한 것이 아닌가라는 생각도 든다. 이 부분은조금 더 공부를 해보고, 실험을 통해 보완하겠다.

GitHub링크

'DataScience' 카테고리의 다른 글

[Keras] 입력 데이터 변환 및 저장  (0) 2021.11.18
Comments