The Relationship between news and stocks 15

2022. 8. 3. 23:47Project/뉴스기사로 인한 주가 등락 예측

728x90
반응형

LSTM

- LSTM에서는 출력, 입력, 삭제 게이트라는 3개의 게이트가 존재했

- GRU는 LSTM보다 학습 속도가 빠르다고 알려져있지만 여러 평가에서 GRU는 LSTM과 비슷한 성능을 보인다고 알려져 있음

- GRU와 LSTM 중 어떤 것이 모델의 성능면에서 더 낫다라고 단정지어 말할 수 없으며, 기존에 LSTM을 사용하면서 최적의 하이퍼파라미터를 찾아낸 상황이라면 굳이 GRU로 바꿔서 사용할 필요는 없음

- 데이터 양이 적을 때는 매개 변수의 앵이 적은 GRU가 조금 더 낫고, 데이터 양이 더 많으면 LSTM이 더 낫다고도 함

양방향 RNN모델 구성

- 출력값에 대한 손실을 최소롸하는 과정에서 모든 파라미터를 동시에 학습되는 종단간 학습 가능

- 단어와 구간 유사성을 입력벡터에 내재화하여 성능 개선

- 데이터 길이가 길어도 성능이 저하되지 않음

코드

필요 라이브러리 설치

%pip install gensim --upgrade
%pip install -U keras-tuner
%pip install pymysql

라이브러리 불러오기

import IPython
import keras_tuner as kt
from tensorflow import keras
from tensorflow.keras.models import Sequential, load_model
from tensorflow.keras.layers import Embedding, Dense, LSTM, Bidirectional
from tensorflow.keras.callbacks import EarlyStopping, ModelCheckpoint
from tensorflow.keras.preprocessing.text import Tokenizer
from tensorflow.keras.preprocessing.sequence import pad_sequences
import tensorflow as tf
import pandas as pd
import numpy as np
import pymysql

MySQL 연동

conn = pymysql.connect(
                        user    = 'stocks',
                        passwd  = 'Stocks!',
                        host    = "-",
                        port    = 3306,
                        db      = 'Data',
                        charset = 'utf8'
        )

데이터 불러오기

sql = 'SELECT stock_id, text, date, token, label FROM Token'
news = pd.read_sql(sql, conn)
news

데이터 전처리 - token열을 Str -> List로 변환

import re

def str_to_list(d):
  text = re.sub(r'[\[\'\]]', '', d)
  return text.split(", ")

news["token"] = news.token.apply(str_to_list)

데이터 전처리 - 불용어 처리

from tqdm import tqdm
def stopword(x):
  stopword = [r'상승.*', r'하락.*', r'급등.*', r'급락.*', '상승세', '하락세', '폭등', '폭락', '오름세', '약세', '강세', '의', '가', '이', '은', '들', '는', '좀', '잘', '걍', '과', '도', '를', '으로', '자', '에', '와', '한', '하다', '에', '은', '는', '하']
  return [i for i in x if i not in stopword and not i.isdigit()]
tqdm.pandas()
news["token"] = news.token.progress_apply(stopword)
news

모델링 - 데이터 셋 나누기

test = news.loc[news["date"] >= '2022-07-01 00:00:00']
train = news.loc[news["date"] < '2022-07-01 00:00:00']

X_train = train['token']
y_train = train['label']
X_test = test['token']
y_test = test['label']

모델링 - Tokenizer

tokenizer = Tokenizer()
tokenizer.fit_on_texts(X_train)
threshold = 4
words_cnt = len(tokenizer.word_index)
rare_cnt = 0
words_freq = 0
rare_freq = 0

for key, value in tokenizer.word_counts.items():
  words_freq += value

  if value < threshold:
    rare_cnt +=1
    rare_freq += value

print("전체 단어 수", words_cnt)
print("빈도가 {} 이하인 희귀 단어 수: {}".format(threshold-1, rare_cnt))
print("희귀 단어 비율: {}".format((rare_cnt / words_cnt) * 100))
print("희귀 단어 등장 빈도 비율: {}".format((rare_freq / words_freq) * 100))

'''
전체 단어 수 62463
빈도가 3 이하인 희귀 단어 수: 18802
희귀 단어 비율: 30.101019803723805
희귀 단어 등장 빈도 비율: 0.07036167954849475
'''
vocab_size = words_cnt - rare_cnt + 2

tokenizer = Tokenizer(vocab_size, oov_token='OOV')
tokenizer.fit_on_texts(X_train)
X_train = tokenizer.texts_to_sequences(X_train)
X_test = tokenizer.texts_to_sequences(X_test)

y_train = np.array(y_train)
y_test = np.array(y_test)

drop_train = [index for index, sentence in enumerate(X_train) if len(sentence) < 1]

X_trian = np.delete(X_train, drop_train, axis=0)
y_train = np.delete(y_train, drop_train, axis=0)
print('리뷰 최대 길이:', max(len(l) for l in X_train))
print('리뷰 평균 길이:', sum(map(len, X_trian)) / len(X_train))


'''
리뷰 최대 길이: 5759
리뷰 평균 길이: 489.9088054952696
'''
import matplotlib.pyplot as plt
plt.style.use('seaborn-white')

plt.hist([len(s) for s in X_train], bins=50)
plt.xlabel('Length of Samples')
plt.ylabel('Number of Samples')
plt.show()

모델링 - Padding

max_len = 800

X_train = pad_sequences(X_train, maxlen=max_len)
X_test = pad_sequences(X_test, maxlen=max_len)

모델링 - BI-LSTM

model = Sequential()
model.add(Embedding(vocab_size, 800))
model.add(Bidirectional(LSTM(32, return_sequences = True, dropout = 0.4)))
model.add(Bidirectional(LSTM(224)))
model.add(Dense(1, activation='sigmoid'))
model.compile(optimizer = keras.optimizers.Nadam(learning_rate = 0.001),
              loss = keras.losses.BinaryCrossentropy(), 
              metrics = ['accuracy'])
model.summary()

es = EarlyStopping(monitor = 'val_loss', mode = 'min', verbose = 1, patience = 3)
mc = ModelCheckpoint('BiLSTM_best_model.h5', monitor = 'val_accuracy', mode = 'max', verbose = 1, save_best_only = True)
history = model.fit(X_train, y_train, epochs=10, validation_split = 0.2, callbacks=[es, mc], batch_size = 128)
loaded_model = load_model('BiLSTM_best_model.h5')
loaded_model.evaluate(X_test, y_test)

 

728x90
반응형