본문 바로가기
실전머신러닝

Keras 를 이용한 텍스트마이닝으로 뉴스예측하기 with Python

by 다빈치스탁 2020. 9. 17.
728x90
반응형

지난 포스트 (2020/09/04 - [머신러닝 with Python/텍스트마이닝 with Python] - Keras 를 이용한 텍스트마이닝 with Python)에서 학습했던 모델을 이용하여 삼성전자의 뉴스를 학습한 뒤 "삼성전자의 주가 전망"에 대한 예측을 진행해보았다. 아래 첨부된 뉴스크롤링 파일(005930.txt)를 다운받아 파이썬 스크립트의 같은 경로에 저장하고 아래의 코드를 돌리면 삼성전자의 주가 전망은 "초 긍정적인 긍정적인 상황이라고 미 2일 현지시간 보도했다 보도했다 보도했다" 라고 예측된 문장(아래의 코드에 predict함수)이 나오게 된다. 

 

문맥이 상당히 매끄럽지 못하다. 뭐 얼핏 봐서는 그런데로 향후 주가전망은 좋을 것이다라는 점을 추측할 수는 있겠지만 동사를 여러번 반복했다. 이것의 가장 큰 원인은 뭘까? 그렇다. 저기서 max_word를 10단어로 한정했기 때문이다. 물론 이 외에도 뉴스를 크롤링하다보면 쓸데없는 특수문자, 기호 등이 많이 포함되어 있는 점도 중요한 원인 중의 하나일 것이다. 

 

그렇다면 이러한 것들을 해결할 수 있는 방법이 없을까? 있다. 일반적으로는 RNN은 시퀀스의 길이가 정해져 있다고 가정하고 분석한다. 그러나 문장이 매번 10개의 단어로 딱 떨어질 수 없다. 그래서 padding 처리를 해서 가중치를 확 없앤다거나 했지만 이것 역시 완벽한 솔루션은 아니다. 

 

그래서 이것을 해결하기 위해 최근에는 Dynamic RNN 신경망을 이용해서 이러한 문제를 해결하기 위한 시도를 했고 그 결과 역시 꽤나 의미있는 성과를 거두고 있다. 이 부분은 다음 포스트에서 한번 다루어보자.

 

005930.txt
0.03MB

import matplotlib.pyplot as plt
import requests
import random
import tensorflow as tf
import pymysql
import numpy as np
import os

from keras.layers import *
from keras.models import *
from keras.callbacks import *
from keras.utils import *
from keras.preprocessing.text import Tokenizer
from keras.preprocessing.sequence import pad_sequences

from collections import Counter
from wordcloud import WordCloud
    
max_word = 10
stock_code = "005930"
stock_name = "삼성전자"

for i in range(1):
    
    file="./"+str(stock_code)+".txt"
    text=""
    textarr=[]
    with open(file, encoding="utf8") as f:
        lines=f.readlines()
        text = text.join(lines)

    for t in text.split("\n"):
        textarr.append(t)

    textarr=np.array(textarr)
    
    # 케라스 문서 : https://keras.io/ko/preprocessing/text/
    # 각 텍스트를 (딕셔너리 내 하나의 정수가 한 토큰의 색인 역할을 하는) 정수 시퀀스로,
    # 혹은 단어 실셈이나 tf-idf 등을 기반으로 각 토큰의 계수가 이진인 벡터로 변환하여 말뭉치를 벡터화할 수 있도록 해줍니다.
    # filter : 쓸데없는 특수기호는 다 제거한다. 
    token=Tokenizer(lower=False,  split=' ', filters='!@#$%^&*()[]<>,.?\"\'■…▶·◆‘’◇“”ⓒ【】=@<b></b>quot;apos')
    # 각 단어의 인덱스를 구축
    token.fit_on_texts(textarr)    

    # 문자열을 정수 인덱스의 리스트로 변환
    seq = token.texts_to_sequences(textarr)
    
    # 문장 길이를 동일하게 만들기 위한 패딩
    seq = pad_sequences(seq,maxlen=max_word)
    print(seq.shape)

    # 신경망에 입력하기 위한 차원변환
    X = seq
    Y = np.vstack((X[1:],X[0]))

    X = X.reshape(-1,max_word,1)
    Y = Y.reshape(-1,max_word,1)

    # 원/핫 인코딩으로 변환
    Y = to_categorical(Y)

    model = Sequential()

    model.add( LSTM(128, return_sequences=True, input_shape=(max_word,1))) # it can be changed by LSTM
    model.add( LSTM(256, return_sequences=True))
    model.add( Dense(len(token.word_index)+1, activation="softmax")) # len(token.word_index) -> Depending on the time step, an error may occur cause of eliminate special character
    model.summary()
    
    model.compile( loss="categorical_crossentropy", optimizer="adam", metrics=["accuracy"])
    
    path=str("./models/"+str(stock_code))
    saved=path+'.hdf5'

    if os.path.isfile(saved):
        print("ok")
        model.load_weights(saved)
        

    textpredict=[]

    textpredict.append(str(stock_name) + " 주가 전망")
    prearr=np.array(textpredict)
    #allocate numeric number instead of text in textarray
    seqpre = token.texts_to_sequences(prearr)
    #Padding to make sentence lengths equal
    seqpre = pad_sequences(seqpre,maxlen=max_word)

    X_pre = seqpre
    X_pre = X_pre.reshape(-1,max_word,1)

    pred=model.predict(X_pre)
    print(pred) # 1-hot encoding result

    pred=np.argmax(pred,axis=2)
    print(pred)

    idx_word={}

    for w in token.word_index:
        idx_word[token.word_index[w]]=w

    word_list=[]
    temp =""
    for l in pred:
        for w in l:
            if w !=0:
                temp += idx_word[w]
                word_list.append(idx_word[w])
                temp += " "

    print(temp)
    print(word_list)
    print(Counter(word_list))

    wordcloud = WordCloud(font_path="c:/windows/fonts/malgun.ttf",
                          background_color="white",
                          width=500,
                          height=300)

    print(dict(Counter(word_list)))
    cloud = wordcloud.generate_from_frequencies(dict(Counter(word_list)))
    plt.figure(figsize=(10,8))
    plt.axis("off")
    plt.imshow(cloud)
    plt.show()

728x90
반응형

댓글