한글 텍스트 분류를 위해 KoNLPy를 사용한다.
한글 문제는 데이터도 많지 않고 관련 자료도 많지 않다. 몇 가지 데이터가 있는데 그중 하나가
Naver sentiment movie corpus v1.0로 네이버 영화 리뷰 데이터이다.
이 데이터는 네이버 영화의 리뷰를 영화당 100개씩 모아서 만든 데이터다. 영어와 같은 리뷰 데이터 구조며 감정의 경우 긍정 혹은 부정 값을 가진다. 따라서 이전 문제와 같지만 언어 특성과 데이터 구조적 특성으로 데이터 전처리 과정이 다르다.
위 URL에서 데이터를 받는다.
여기서는 train과 test 데이터를 사용하는데, 두 비율을 바꾸려면 ratings를 사용하여 분리한다. 두 파일을 기존에 영어에 쓰던 data_in 폴더에 옮긴다
import numpy as np
import pandas as pd
import os
import matplotlib.pyplot as plt
import seaborn as sns
from wordcloud import WordCloud
%matplotlib inline
DATA_IN_PATH='./data_in/'
print("파일 크기 : ")
for file in os.listdir(DATA_IN_PATH):
if 'txt' in file:
print(file.ljust(30)+str(round(os.path.getsize(DATA_IN_PATH+file)/1000000,2))+'MB')
이제 판다스로 학습데이터를 확인해보면
train_data=pd.read_csv(DATA_IN_PATH+'ratings_train.txt', header=0, delimiter='\t', quoting=3)
train_data.head()
데이터는 3개의 열로 각 데이터의 인덱스, 리뷰데이터인 document, 긍정/부정을 나타내는 라벨값을 가진다. 이제 학습데이터를 분석한다.
영어 데이터와 비슷한 구조이기에 동일한 순서로 진행한다. 데이터 개수를 다시 한 번 보면,
print('전체 학습 데이터의 개수: {}'.format(len(train_data)))
이제 각 데이터에 대해 리뷰 길이를 확인한다. 데이터프레임의 apply 함수를 사용하여 길이 값을 추출한다.
train_length=train_data['document'].astype(str).apply(len)
train_length.head()
길이를 보면 19, 33, 17로 각 데이터의 길이 값을 가진다. 이제 이 변수를 사용해 전체 데이터의 길이에 대한 히스토그램을 그린다.
# 그래프에 대한 이미지 크기 선언
# figsize: (가로, 세로) 형태의 튜플로 입력
plt.figure(figsize=(12,5))
# 히스토그램 선언
# bins : 히스토그램 값에 대한 버킷 범위
# range : x축 값의 범위
# alpha : 그래프 색상 투명도
# color : 그래프 색상
# label : 그래프에 대한 라벨
plt.hist(train_length, bins=200, alpha=0.5, color='r', label='word')
plt.yscale('log',nonposy='clip')
# 그래프 제목
plt.title('Log-Histogram of length of review')
# 그래프 x축 라벨
plt.xlabel('Length of review')
# 그래프 y축 라벨
plt.ylabel('Number of review')
길이를 보면 고르게 분포돼 있다. 조금 특이한 부분은 보통 20자 이하에 많다가 길이가 길어질수록 점점 적어지는데, 140자 부근에서 갑자기 많아진다. 해당 데이터의 경우 140자 제한이 있는 데이터이기에 최대 글자수에 모여있는 형태다.
print('리뷰 길이 최대값: {}'.format(np.max(train_length)))
print('리뷰 길이 최대값: {}'.format(np.min(train_length)))
print('리뷰 길이 평균값: {:.2f}'.format(np.mean(train_length)))
print('리뷰 길이 표준편차: {:.2f}'.format(np.std(train_length)))
print('리뷰 길이 중간값: {}'.format(np.median(train_length)))
# 사분위의 대한 경우는 0~100 스케일로 돼 있음
print('리뷰 길이 제1사분위: {}'.format(np.percentile(train_length, 25)))
print('리뷰 길이 제3사분위: {}'.format(np.percentile(train_length, 75)))
길이 최대값은 158이다. 최대 글자 수가 140이지만 특수 문자 등으로 인해 더 긴 데이터도 포함되었다. 최솟값은 1이고 평균은 35정도이고, 중간값은 27자로 평균보다 작다. 이제 박스 플롯을 그리면,
plt.figure(figsize=(12,5))
# 박스 플롯 생성
# 첫 번째 파라미터: 여러 분포에 대한 데이터 리스트를 입력
# labels : 입력학 데이터에 대한 라벨
# showmeans : 평균값을 마크함
plt.boxplot(train_length, labels=['counts'], showmeans=True)
그림을 보면 대부분 일부 길이가 긴 데이터가 꽤 있다. 중간값과 평균값은 전체 데이터로 보면 아래쪽에 위치한다.
이제 데이터에서 자주 사용된 어휘를 보기 위해 어휘 빈도 분석을 한다. (워드 클라우드로 진행)
우선 데이터 안의 문자열이 아닌 데이터를 모두 제거한다. 어휘 빈도 분석은 문자열에만 진행가능하기에 다른 형식이 있다면 오류 발생가능성이 있다.
train_review=[review for review in train_data['document'] if type(review) is str]
문자열만 남은 리뷰 데이터를 워드 클라우드에 적용하면 되는데 워드 클라우드가 기본이 영어라이브러리라서 한글 폰트를 설정해야한다. 한글 폰트 아무거나 다운받아서 data_in 폴더로 옮긴다. 폰트는 워드클라우드 생성 때 설정한다.
wordcloud=WordCloud(font_path=DATA_IN_PATH+'nanumgothic.TTF').generate(' '.join(train_review))
plt.imshow(wordcloud, interpolation='bilinear')
plt.axis('off')
plt.show()
'영화', '너무', '진짜' 등의 어휘가 많고 영어 데이터와는 달리 HTML 태그나 특수 문자가 적다. 이제 긍정, 부정 라벨값 비율을 확인한다.
fig, axe=plt.subplots(ncols=1)
fig.set_size_inches(6,3)
sns.countplot(train_data['label'])
거의 반반이다. 실제 값을 보자.
print("긍정 리뷰 개수: {}".format(train_data['label'].value_counts()[1]))
print("부정 리뷰 개수: {}".format(train_data['label'].value_counts()[0]))
차이가 약 300개지만 거의 비슷해 그대로 사용해도 무방하다. 이제 각 리뷰의 단어 수를 확인한다.
우선 각 데이터를 띄어쓰기 기준으로 나눠 그 개수를 하나의 변수로 할당해, 그 값으로 히스토그램을 그린다.
길이의 경우 대부분 5개 정도고 이후로는 고르게 분포되다가 30개 이상부터는 수가 급격히 줄어든다. 실제 값을 확인하면,
print('리뷰 단어 개수 최댓값: {}'.format(np.max(train_word_counts)))
print('리뷰 단어 개수 최솟값: {}'.format(np.min(train_word_counts)))
print('리뷰 단어 개수 평균값: {:.2f}'.format(np.mean(train_word_counts)))
print('리뷰 단어 개수 표준편차: {:.2f}'.format(np.std(train_word_counts)))
print('리뷰 단어 개수 중간값: {}'.format(np.median(train_word_counts)))
# 사분위의 대한 경우는 0~100 스케일로 돼 있음
print('리뷰 단어 개수 제1사분위: {}'.format(np.percentile(train_word_counts, 25)))
print('리뷰 단어 개수 제3사분위: {}'.format(np.percentile(train_word_counts, 75)))
보면 평균 7~8개 단어 개수를 가지고 중간값은 6정도의 단어를 가진다. 글자 수 제한이 있어 영어 데이터에 비해 길이가 짧은 편이다. 이 경우 모델에 적용할 최대 단어 수를 6개가 아닌 7개로 설정해도 무리없다.
마지막으로 특수문자 유무를 확인하는데, 리뷰에 자주 사용되는 특별한 특수문자는 없으므로 일반적인 마침표와 물음표만 확인한다.
# 물음표가 구두점으로 쓰임
qmarks=np.mean(train_data['document'].astype(str).apply(lambda x: '?' in x))
# 마침표
fullstop=np.mean(train_data['document'].astype(str).apply(lambda x: '.' in x))
print('물음표가 있는 질문: {:.2f}%'.format(qmarks*100))
print('마침표가 있는 질문: {:.2f}%'.format(fullstop*100))
영화 리뷰 데이터기에 물음표는 거의 없지만 마침표의 경우 절반 정도가 있다. 이제 분석한 결과로 데이터를 전처리 한다.
[ 출처 : 책 ( 텐서플로와 머신러닝으로 시작하는 자연어 처리) ]
[ NLP ] 한글 텍스트 분류 (3) (0) | 2021.01.21 |
---|---|
[ NLP ] 한글 텍스트 분류 (2) (0) | 2021.01.13 |
[ NLP ] 영어 텍스트 분류 (7) (0) | 2021.01.10 |
[ NLP ] 영어 텍스트 분류 (6) (0) | 2020.12.30 |
[ NLP ] 영어 텍스트 분류 (5) (0) | 2020.12.22 |