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

Keras 를 이용한 텍스트마이닝 with Python

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

지난 포스트(2020/09/04 - [텍스트마이닝 with R & Python] - MySQL 데이터베이스 연동하기 with Python)에 이어서 지금부터는 데이터베이스에 저장된 종목코드와 종목을 불러와서 모든 종목들의 뉴스를 크롤링한 뒤 이를 토대로 텍스트마이닝을 수행해보자. 

 

아래의 코드를 살펴보면 데이터베이스에 저장된 종목명을 가져와서 뒤에 주가전망이라는 키워드를 조합한다. 예를 들면 "삼성전자 주가전망"에 대해서 뉴스를 크롤링하고 정렬은 유사도순으로, 한번에 100건의 검색을 수행해준다. 다만 유의할 점은 종목이 대략 2천여개 정도 되기 때문에 그냥 경로에 집어넣으면 폴더가 상당히 지저분해진다. 따라서 파이썬 파일이 있는 경로에 textfile 이라는 새로운 폴더를 하나 생성해주고 거기다가 데이터들을 저장하자. 만드는 김에 models(학습신경망 저장경로) 라는 새폴더도 만들어주자. 

 

# 네이버 검색 API예제는 블로그를 비롯 전문자료까지 호출방법이 동일하므로 blog검색만 대표로 예제를 올렸습니다.
# 네이버 검색 Open API 예제 - 블로그 검색
import os
import sys
import urllib.request
from bs4 import BeautifulSoup

import pymysql
import numpy as np

#MySQL 아이디와 비밀번호는 본인의 환경에 맞게 설정
conn= pymysql.connect(host='localhost', user='', password='', db='stock_data', charset='utf8')
curs = conn.cursor()
sql = """select name, code from stock_master order by code"""
curs.execute(sql)
res = curs.fetchall()   #fetchall 실행결과얻기
gamma_x = np.asarray(res) #행렬로 가져오기
print(gamma_x)
word=[]
code=[]

for i in range(len(gamma_x)):
    word.append(gamma_x[i][0])
    code.append(gamma_x[i][1])
    #네이버 API 아이디와 키값은 본인의 값으로 입력
    client_id = ""
    client_secret = ""
    encText = 'sort=sim&start=1&display=100&query='+urllib.parse.quote(str(word[i])+" 주가전망")
    #정렬은 유사도 순으로 한번에 출력하는 갯수는 100개로 설정
    #url = "https://openapi.naver.com/v1/search/blog?query=" + encText # json 결과
    url = "https://openapi.naver.com/v1/search/news.xml?" + encText # xml 결과
    print(url)

    request = urllib.request.Request(url)
    request.add_header("X-Naver-Client-Id",client_id)
    request.add_header("X-Naver-Client-Secret",client_secret)
    response = urllib.request.urlopen(request)
    rescode = response.getcode()

    file = open("./textfile/"+str(code[i])+".txt", "w", encoding='utf-8') 

    if(rescode==200):
        response_body = response.read()
        xmlsoup = BeautifulSoup(response_body, 'html.parser')
        items = xmlsoup.find_all('item')
        #print(items)
        for item in items:
            file.write(item.description.get_text(strip=True) + '\n')

        print("Success")
    else:
        print("Error Code:" + rescode)

    file.close()

 

세팅을 완료하고 스크립트를 실행하면 textfile 폴더 안에 약 2천 여개의 종목 뉴스데이터가 저장이 된다. 다 돌아갈 때까지 커피한잔 먹고 오자. (10분 정도 시간이 흘렀다.) 짜잔! 폴더 안에 text 파일들이 종목코드별로 예쁘게 저장이 되어 있다. 자 이제는 이 데이터들을 Keras의 유틸과 신경망(LSTM)을 이용해서 문장에 대한 학습을 수행할 텐데 아래의 코드를 이해하기 위해서는 우선 RNN(Recurrent Neural Network)에 대한 이해가 필요하다. 음...설명을 이어나가려면 내용이 너무 길어진다. 이 신경망에 대한 상세한 설명은 다음 포스트로 우선 미루자.

 

다만 일반적인 DNN(Deep Neural Network)와 중요한 차이점은 바로 시퀀셜 데이터 즉 시간적으로 연관성이 있는 경우에 사용하는 신경망이라는 점이다. 예를 들어 "나는 점심을 먹는다."라는 문장은 주어 -> 목적어 -> 동사의 순서로 구성되어 있다. 만약 순서를 고려하지 않게 되면 컴퓨터는 "나는 너는 그는" 와 같이 의미를 추측하기 어려운 문장을 조합할 수도 있다는 뜻이다. 따라서 머신러닝을 수행할 때도 그 상황에 맞는 신경망 선택이 필요하다는 점만 짚고 넘어가자. 

 

케라스에서는 다양한 툴도 제공하고 있는데 아래의 코드에 나와있는 케라스문서 URL에 가보면 한국어로도 상세하게 설명이 나와있으니 참고하길 바란다. 간단하게 아래의 학습원리를 설명하면 

 

  • 삼성전자에 대한 뉴스데이터를 전부 수집한다.
  • 한 문장의 최대 단어는 20개 정도라고 가정을 해준다. (더 줄여도 상관은 없다.)
  • 케라스의 텍스트마이닝 도구를 이용해서 불필요한 특수문자를 제거하고 단어의 구분은 띄어쓰기로 잘랐다.
  • 이렇게 변환된 단어를 정수형의 인덱스로 변환한다.
  • LSTM 신경망에 입력하기 위한 차원을 변환하여 학습을 수행한다.

이와 같은 방식으로 학습을 수행하고 나면 "삼성전자 주가전망"으로 검색된 뉴스데이터 대해서 가장 빈도수가 높은 단어(예 : 상승 모멘텀 유력 등)들의 조합으로 미래에 올 문장을 예측해주는 챗봇을 만들 수 있을 것이다.

import requests
import random
import tensorflow as tf
import pymysql
import numpy as np

from keras.layers import *
from keras.models import *

from keras.utils import *
from keras.preprocessing.text import Tokenizer
from keras.preprocessing.sequence import pad_sequences

#MySQL 아이디와 비밀번호는 본인의 환경에 맞게 설정
conn= pymysql.connect(host='localhost', user='', password='', db='stock_data', charset='utf8')
curs = conn.cursor()
sql = """select name, code from stock_master order by code"""
curs.execute(sql)
res = curs.fetchall()   #쿼리에 대한 결과값 저장
gamma_x = np.asarray(res) #행렬로 변환

word=[]
code=[]
for i in range(len(gamma_x)):
    word.append(gamma_x[i][0])
    code.append(gamma_x[i][1])

max_word = 20

for i in range(len(gamma_x)):
    
    file="./textfile/"+str(code[i])+".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)

    with tf.device("/cpu:0"):        
        model = Sequential()

        model.add( LSTM(128, return_sequences=True, input_shape=(max_word,1))) 
        model.add( LSTM(256, return_sequences=True))
        model.add( Dense(len(token.word_index)+1, activation="softmax"))
        model.summary()
        model.compile( loss="categorical_crossentropy", optimizer="adam", metrics=["accuracy"])
        
        path=str("./models/")+str(code[i])
        saved=path+'.hdf5'

        # 모델평가 : 테스트 모드에서의 모델의 손실 값과 측정항목 값을 반환합니다.
        # 즉 학습한 모델에 대한 정확도를 평가하는 값으로 간주(loss 와 accuracy)
        # 케라스문서 : https://keras.io/ko/models/model/          
        score = model.evaluate(X, Y, verbose=1)

        maxepoch=0
            
        while score[1]:

            history = model.fit(X, Y, batch_size=1, epochs=1)#학습할 양도 얼마되지 않으니 배치사이즈는 1로 해주자.
            score = model.evaluate(X, Y, verbose=1)
            model.save_weights(saved)
            maxepoch+=1

            if score[1] > 0.95: # 정확도가 95% 만 넘어가도 그냥 나와라.(학습중단) -> 정확도도 80% 정도면 괜찮지 않을까한다.
                break
            if maxepoch > 50: # 95% 못넘기고 50 번 이상 반복하면 그냥 나와라.(학습중단) -> 횟수는 임의 조정이 가능하다.
                break 

 

물론 위의 예시는 뉴스의 조합으로 만들어진 문장들이기 때문에 연설문이나 소설처럼 짜임새 있는 문장들의 조합이 아니다. 따라서 아주 매끄러운 문장을 조합하는 챗봇이 되기는 어렵다. 그러나 우리는 봇이 아니라 사람이기때문에 최근 검색된 뉴스들의 조합(빈도수가 높은 단어들의 조합)을 통해 참고자료를 얻는데에는 크게 무리가 없을 듯 하다.

728x90
반응형

댓글