def kwandoll():
check1=int(input("어떤 작업을 수행하시겠습니까?\n"+"[1]:경쟁회사 전체 데이터\n"+"[2]:경쟁회사 및 자사 데이터"))
check2=int(input("분류된 데이터를 확인하시겠습니까?\n"+"[1]:예"+"[2]:아니오"))
check3=int(input("이메일로 받으시겠습니까?\n"+"[1]:예"+"[2]:아니오"))
check4=int(input("그래프로 확인해보시겠습니까?\n"+"[1]:예"+"[2]:아니오"))
if(check1==1):
if(check2==1 and check4==1):
Main_1.classified_all_data()
df=pd.read_csv("C:\\Data\\thisweek_classified.csv")
call_Graph.wordcloud(df)
if(check3==1):
send_email.sending_classified_all_data()
else:
print("지정된 디렉토리에 저장되었습니다.")
elif(check2==1 and check4==2):
if(check3==1):
send_email.sending_classified_all_data()
else:
print("지정된 디렉토리에 저장되었습니다.")
elif(check2==2 and check4==1):
Main_1.none_classified_all_data()
df=pd.read_csv("C:\\Data\\thisweek_unclassified.csv")
call_Graph.wordcloud(df)
if(check3==1):
send_email.sending_unclassified_all_data()
else:
print("지정된 디렉터리에 저장되었습니다.")
elif(check2==2 and check4==2):
Main_1.none_classified_all_data()
if(check3==1):
send_email.sending_unclassified_all_data()
else:
print("지정된 디렉토리에 저장되었습니다.")
elif(check1==2):
if(check2==1 and check4==1):
Main_1.classified_selected_data()
df=pd.read_csv("C:\\Data\\thisweek_selected_classified.csv")
call_Graph.wordcloud(df)
if(check3==1):
send_email.sending_classified_selected_data()
else:
print("지정된 디렉토리에 저장되었습니다.")
elif(check2==1 and check4==2):
if(check3==1):
send_email.sending_classified_selected_data()
else:
print("지정된 디렉토리에 저장되었습니다.")
elif(check2==2 and check4==1):
Main_1.none_classified_selected_data()
df=pd.read_csv("C:\\Data\\thisweek_selected_unclassified.csv")
call_Graph.wordcloud(df)
if(check3==1):
send_email.sending_unclassified_selected_data()
else:
print("지정된 디렉토리에 저장되었습니다.")
elif(check2==2 and check4==2):
Main_1.none_classified_selected_data()
if(check3==1):
send_email.sending_unclassified_selected_data()
else:
print("지정된 디렉토리에 저장되었습니다.")
이후, kwandoll() 함수를 호출하면
어떤 작업을 수행하시겠습니까?
[1]:경쟁회사 전체 데이터
[2]:경쟁회사 및 자사 데이터1
분류된 데이터를 확인하시겠습니까?
[1]:예[2]:아니오1
이메일로 받으시겠습니까?
[1]:예[2]:아니오1
그래프로 확인해보시겠습니까?
[1]:예[2]:아니오1
시작 년도를 적으시오: 2021
시작 월을 적으시오: 01
시작 일을 적으시오: 01
종료 년도를 적으시오: 2021
종료 월을 적으시오: 01
종료 일을 적으시오: 13
퀄리티 벨류의 최댓값을 지정하세요: 90
퀄리티 벨류의 최솟값을 지정하세요: 10
이름을 적으세요이관형
보낼 이메일 주소를 적으세요: asd95101@naver.com
Successfully sent the mail!!!
이로써 내가 원하는 데이터는 지정 이메일로 전송이되고, 워드클라우드를 통해 그래프로 보여진다.
이제 시작이라 말주변도없고 이 데이터도 전처리를 더 해야하지만, 인턴을 통해 이 정도까지 왔다는 것도 만족한다. 계속 공부하며 글을 올려 실력을 올려보자.
def GRU_predict(new_sentence):
max_len=90
new_sentence=okt.morphs(new_sentence)
new_words=[word for word in new_sentence if not word in stopwords]
encoded=tokenizer.texts_to_sequences([new_sentence])
pad_new=pad_sequences(encoded,maxlen=max_len)
score=float(GRU_model.predict(pad_new))
percentage=score*100
return percentage,new_words
앞서 만들어본 GRU모델은 이미 h5 파일로 저장되어있다. 그러므로 이걸 불러서 사용하기만 하면 된다.
*언어는 어느 분야에서 어떤 방식으로 사용하느냐의 따라 다양한 의미로 전달된다. 이에 카메파라이 라이브와 가장 유사성 높은 어플 회사를 선택했고, 본인이 인턴하는 카메라파이라이브에서 연동시키는 어플회사 및 경쟁회사 리뷰이다.
*크롤링에 대한 매크로 및 처리는 따로 링크를 걸어두겠다.
시작하기에 앞서 사용해야 할 라이브러리를 작성한다.
from bs4 import BeautifulSoup
from selenium import webdriver
from pykospacing import spacing
import time
import csv
import os
import pandas as pd
from pandas import DataFrame
from pandas import Series
import re
import numpy as np
from sklearn.feature_extraction.text import CountVectorizer, TfidfVectorizer
from konlpy.tag import Okt
from collections import Counter
import nltk
import matplotlib.pyplot as plt
import urllib.request
from tensorflow.keras.preprocessing.text import Tokenizer
from tensorflow.keras.preprocessing.sequence import pad_sequences
from sklearn.model_selection import train_test_split
from tensorflow import keras
import pickle
#Test용 리뷰 처리
test_number_list=[]
for num in range(len(test_data)):
test_number_list.append(num)
test_data['index']=test_number_list
test_data=test_data.set_index(['index'])
test_data['내용'].nunique(),
test_data['평점'].nunique()
test_data['label'].nunique()
test_data.drop_duplicates(subset=['내용'],inplace=True)
#Test용 리뷰 문장 전처리
test_data['내용']=test_data['내용'].str.replace('[^ㄱ-ㅎㅏ-ㅣ|가-힣]','')
test_data['내용']=test_data['내용'].str.replace(' ','')
test_data['내용']=test_data['내용'].apply(spacing)
print("총 샘플의 개수: ",len(test_data))
총 샘플의 개수: 15330
훈련데이터도 같은 방식으로 진행하겠다.
#Train용 리뷰 처리
train_number_list=[]
for num in range(len(train_data)):
train_number_list.append(num)
train_data['index']=train_number_list
train_data=train_data.set_index(['index'])
train_data['내용'].nunique(),
train_data['평점'].nunique(),
train_data['label'].nunique()
train_data.drop_duplicates(subset=['내용'],inplace=True)
#Train용 리뷰 전처리
train_data['내용']=train_data['내용'].str.replace('[^ㄱ-ㅎㅏ-ㅣ|가-힣]','')
train_data['내용']=train_data['내용'].str.replace(' ','')
train_data['내용']=train_data['내용'].apply(spacing)
print("총 샘플의 개수: ",len(train_data))
총 샘플의 개수: 45774
*spacing 전희원님이 개발한 PyKoSpacing은 한국어 띄어쓰기 패키지로 띄어쓰기가 되어있지 않은 문장을 띄어쓰기를 한 문장으로 변환해주는 패키지이다. PyKoSpacing은 대용량 코퍼스를 학습하여 만들어진 띄어쓰기 딥 러닝 모델로 준수한 성능을 가지고 있다.
여기서 잠깐, 한 가지 집고 넘어가자면 본인 또한 그렇고 파이썬을 접한지 얼마 되지 않았을때 대부분 주피터노트북 저장만 해놓고 꺼버렸다. 하지만 파이썬은 휘발성 메모리? 이기 때문에 작업한 변수와 그의 맞는 데이터들은 전부 사라지게 된다. 데이터가 5만개가 아닌 10만개 100만개 된다면 처음부터 다 돌리기에는 상당한 시간이 걸릴겁니다. 이렇기에 데이터를 저장하는 습관을 들이셔야 합니다. 저는 데이터프레임을 가장 편하게 저장할 수 있는 pickle 라이브러리를 사용한다.
#전처리 내용 저장
test_data.to_pickle("C:\\data\\Test_data.pkl")
train_data.to_pickle("C:\\data\\Train_data.pkl")
# 다시 읽어오기
test_data=pd.read_pickle("C:\\data\\Test_data.pkl")
train_data=pd.read_pickle("C:\\data\\Train_data.pkl")
이 코드만 컴파일 시켜도, 원하는 장소에 저장이 되어있다.
다시 본론으로 넘어가서, 전처리를 하였으니 각 리뷰 데이터 즉 문장들을 단어(토큰)화 시켜서 불용어 처리까지 해보자.
#Train, Test, 토큰화
okt=Okt()
test_data=pd.read_pickle("C:\\data\\Test_data.pkl")
train_data=pd.read_pickle("C:\\data\\Train_data.pkl")
stopwords = ['도', '는', '다', '의', '가', '이', '은', '한', '에', '하', '고', '을', '를', '인', '듯', '과', '와', '네', '들', '듯', '지', '임', '게','요','거','로','으로',
'것','수','할','하는','제','에서','그','데','번','해도','죠','된','건','바','구','세']
test_data['tokenized']=test_data['내용'].apply(okt.morphs)
test_data['tokenized']=test_data['tokenized'].apply(lambda x: [item for item in x if item not in stopwords])
train_data['tokenized']=train_data['내용'].apply(okt.morphs)
train_data['tokenized']=train_data['tokenized'].apply(lambda x: [item for item in x if item not in stopwords])
*여기서 한가지 팁을 알려드리자면, 텍스트 마이닝에서 중요한 한 가지는 나만의 사전을 만드는 것이다. stopwords라는 변수안에 불용어처리 목록을 저장하였지만, 실제 더 하려면 직접 데이터를 읽어보고 빈도수를 분석하여 불용어처리를 하는 것이다.
fig,(ax1,ax2) = plt.subplots(1,2,figsize=(10,5))
text_len = train_data[train_data['label']==1]['tokenized'].map(lambda x: len(x))
ax1.hist(text_len, color='red')
ax1.set_title('Positive Reviews')
ax1.set_xlabel('length of samples')
ax1.set_ylabel('number of samples')
print('긍정 리뷰의 평균 길이 :', np.mean(text_len))
text_len = train_data[train_data['label']==0]['tokenized'].map(lambda x: len(x))
ax2.hist(text_len, color='blue')
ax2.set_title('Negative Reviews')
fig.suptitle('Words in texts')
ax2.set_xlabel('length of samples')
ax2.set_ylabel('number of samples')
print('부정 리뷰의 평균 길이 :', np.mean(text_len))
plt.show()
이를 통해 긍정리뷰보다는 부정리뷰가 좀 더 길게 작성되는 경향이 있다. 예상을 해본다면 불만이 있거나 요구사항이 있을 경우 말을 길게 하는 경향도 있기 때문이다.
기계는 글자를 인식하지 못한다. 기계에게 글자를 입력하기 위해서는 글자에 넘버링을 해야하는데 그 기법은 원핫인코딩, TFIDF, 분산 점수화, 임의 정수 인코딩이 있다.
본인은 정수 인코딩 기법을 사용하겠다.
X_Train=train_data['tokenized'].values
Y_Train=train_data['label'].values
X_Test=test_data['tokenized'].values
Y_Test=test_data['label'].values
tokenizer=Tokenizer()
tokenizer.fit_on_texts(X_Train)
threshold=2
total_cnt=len(tokenizer.word_index)
rare_cnt=0
total_freq=0
rare_freq=0
for key,value in tokenizer.word_counts.items():
total_freq=total_freq+value
if(value<threshold):
rare_cnt=rare_cnt+1
rare_freq=rare_freq+value
print('단어 집합(vocabulary)의 크기: ',total_cnt)
print('등장 빈도가 %s번 이하인 회귀 단어의 수: %s'%(threshold-1,rare_cnt))
print('단어 집합에서 회귀 단어의 비율:',(rare_cnt/total_cnt)*100)
print('전체 등장 빈도에서 회귀 단어 등장 빈도 비율:',(rare_freq/total_freq)*100)
단어 집합(vocabulary)의 크기: 41748
등장 빈도가 1번 이하인 회귀 단어의 수: 22682
단어 집합에서 회귀 단어의 비율: 54.330746383060266
전체 등장 빈도에서 회귀 단어 등장 빈도 비율: 2.809497444053445
단어는 약 41000개가 존재한다. 등장 빈도가 1번 이하인 단어의 수는 전체에서 약 54퍼센트를 차지하고 있다. 실제 훈련데이터에서 등장 빈도로 차지하는 비율은 2.8퍼센트 밖에 안된다. 즉, 크게 중요한 의미를 지닌 단어가 될 수 없다고 예상할 수 있다.
그러므로 등장 빈도가 1인 단어들의 수를 제외한 나머지 단어의 개수를 최대 크기로 제한해보자.
vocab_size=total_cnt-rare_cnt+2
print('단어 집합의 크기:',vocab_size)
단어 집합의 크기: 19249
이제 단어 집합의 크기는 19000여개이다. 이를 토크나이저 인자로 넘기면, 텍스트 시퀀스를 숫자 시퀀스로 변환해준다. 이 과정에서 이보다 큰 숫자가 부여된 단어들은 OOV로 변환시켜주자.
이후, 서로 다른 길이의 샘플들의 길이를 동일하게 맞춰주는 작업을 패딩이라 한다. 전체 데이터에서 가장 길이가 긴 리뷰와 길이 분포를 확인 해보자.
print('리뷰의 최대 길이: ',max(len(l) for l in X_Train))
print('리뷰의 평균 길이: ',sum(map(len,X_Train))/len(X_Train))
plt.hist([len(s) for s in X_Train], bins=50)
plt.xlabel('length of samples')
plt.ylabel('number of samples')
plt.show()
리뷰의 최대 길이: 249
리뷰의 평균 길이: 17.391602271271825
리뷰의 최대 길이는 249, 평균 길이는 17이다. 그래프를 확인해보면 대체적으로 50정도 되는 것으로 예상된다.
만약 100으로 패딩할 경우 샘플은 몇개가 온전할 지 한번 확인해 보자.
def below_threshold_len(max_len,nested_list):
cnt=0
for s in nested_list:
if(len(s)<=max_len):
cnt=cnt+1
print("전체 샘플 중 길이가 %s 이하인 샘플의 비율: %s"%(max_len,(cnt/len(nested_list))*100))
max_len=100
below_threshold_len(max_len,X_Train)
전체 샘플 중 길이가 100 이하인 샘플의 비율: 99.97865346027409
훈련 리뷰의 99.97%가 100이하의 길이를 갖는다. 그러므로 훈련 리뷰의 길이 100으로 패딩하겠다.