[Python 머신러닝] 08-9 한글 텍스트 처리 - 네이버 영화 평점 감성 분석
텍스트 분석
한글 텍스트 처리 - 네이버 영화 평점 감성 분석
한글 NLP 처리의 어려움
한글 NLP를 어렵게 만드는 요인들
- 띄어쓰기
- 다양한 조사
- 주어/목적어가 생략되어도 의미 전달 가능
- 의성어/의태어, 높임말 등
한글 형태소 분석
형태소의 사전적인 의미는 ‘단어로서 의밀르 가지는 최소 단위’로 정의할 수 있다.
형태소 분석(Morphological analysis)이란 말뭉치를 이러한 형태소 어근 단위로 쪼개고 각 형태소에 품사 태깅(POS tagging)을 부착하는 작업을 일반적으로 지칭한다.
KoNLPy 소개
KoNLPy는 기존의 C/C++, Java로 잘 만들어진 한글 형태소 엔진을 파이썬 래퍼(Wrapper) 기반으로 재작성한 패키지이다.
- 꼬꼬마(Kkma)
- 한나눔(Hannanum)
- Komoran
- 은전한닢 프로젝트(Mecab)
- Okt(구 Twitter)
설치 문서: https://konlpy-ko.readthedocs.io/ko/v0.6.0/install/
윈도우 환경에서 KoNLPy 설치하기
-
Jpype 설치하기
conda install-c conda-forge jpype1 -
Java 설치하고 JAVA_HOME 환경 변수 수정
-
KoNLPy 설치하기
pip install konlpy
데이터 로딩
네이버 영화 평점 데이터는 https://github.com/e9t/nsmc에서 내려받을 수 있다.
깃허브(github)에서 전체 데이터 세트인 ratings.txt, 학습 데이터 세트인 ratings_train.txt, 테스트 데이터 세트인 ratings_test.txt를 모두 내려받는다.
<실습>실습>
from konlpy.tag import Twitter
from konlpy.tag import Okt
from konlpy.tag import Kkma
import pandas as pd
import warnings
warnings.filterwarnings('ignore')
# 컬럼 분리 문자 \t
train_df = pd.read_csv('ratings_train.txt', sep='\t') # 한글 encoding시 encoding='cp949' 적용.
train_df.head(10)
| id | document | label | |
|---|---|---|---|
| 0 | 9976970 | 아 더빙.. 진짜 짜증나네요 목소리 | 0 |
| 1 | 3819312 | 흠...포스터보고 초딩영화줄....오버연기조차 가볍지 않구나 | 1 |
| 2 | 10265843 | 너무재밓었다그래서보는것을추천한다 | 0 |
| 3 | 9045019 | 교도소 이야기구먼 ..솔직히 재미는 없다..평점 조정 | 0 |
| 4 | 6483659 | 사이몬페그의 익살스런 연기가 돋보였던 영화!스파이더맨에서 늙어보이기만 했던 커스틴 ... | 1 |
| 5 | 5403919 | 막 걸음마 뗀 3세부터 초등학교 1학년생인 8살용영화.ㅋㅋㅋ...별반개도 아까움. | 0 |
| 6 | 7797314 | 원작의 긴장감을 제대로 살려내지못했다. | 0 |
| 7 | 9443947 | 별 반개도 아깝다 욕나온다 이응경 길용우 연기생활이몇년인지..정말 발로해도 그것보단... | 0 |
| 8 | 7156791 | 액션이 없는데도 재미 있는 몇안되는 영화 | 1 |
| 9 | 5912145 | 왜케 평점이 낮은건데? 꽤 볼만한데.. 헐리우드식 화려함에만 너무 길들여져 있나? | 1 |
train_df['label'].value_counts( )
0 75173
1 74827
Name: label, dtype: int64
train_df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 150000 entries, 0 to 149999
Data columns (total 3 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 id 150000 non-null int64
1 document 149995 non-null object
2 label 150000 non-null int64
dtypes: int64(2), object(1)
memory usage: 3.4+ MB
import re
train_df = train_df.fillna(' ')
# 정규 표현식을 이용하여 숫자를 공백으로 변경(정규 표현식으로 \d 는 숫자를 의미함.)
train_df['document'] = train_df['document'].apply( lambda x : re.sub(r"\d+", " ", x) )
# 테스트 데이터 셋을 로딩하고 동일하게 Null 및 숫자를 공백으로 변환.
test_df = pd.read_csv('ratings_test.txt', sep='\t') # 한글 encoding시 encoding='cp949' 적용.
test_df = test_df.fillna(' ')
test_df['document'] = test_df['document'].apply( lambda x : re.sub(r"\d+", " ", x) )
# id 컬럼 삭제 수행.
train_df.drop('id', axis=1, inplace=True)
test_df.drop('id', axis=1, inplace=True)
from konlpy.tag import Okt
okt = Okt()
def tw_tokenizer(text):
# 입력 인자로 들어온 text 를 형태소 단어로 토큰화 하여 list 객체 반환
tokens_ko = okt.morphs(text)
return tokens_ko
#tw_tokenizer('아버지가방에 들어가신다')
okt.morphs('아버지가방에 들어가신다')
['아버지', '가방', '에', '들어가신다']
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import GridSearchCV
# Okt 객체의 morphs( ) 객체를 이용한 tokenizer를 사용. ngram_range는 (1,2)
tfidf_vect = TfidfVectorizer(tokenizer=tw_tokenizer, ngram_range=(1,2), min_df=3, max_df=0.9)
tfidf_vect.fit(train_df['document'])
tfidf_matrix_train = tfidf_vect.transform(train_df['document'])
print(tfidf_matrix_train.shape)
(150000, 129276)
# Logistic Regression 을 이용하여 감성 분석 Classification 수행.
lg_clf = LogisticRegression(random_state=0, solver='liblinear')
# Parameter C 최적화를 위해 GridSearchCV 를 이용.
params = { 'C': [1 ,3.5, 4.5, 5.5, 10 ] }
grid_cv = GridSearchCV(lg_clf , param_grid=params , cv=3 ,scoring='accuracy', verbose=1 )
grid_cv.fit(tfidf_matrix_train , train_df['label'] )
print(grid_cv.best_params_ , round(grid_cv.best_score_,4))
Fitting 3 folds for each of 5 candidates, totalling 15 fits
{'C': 3.5} 0.8593
test_df.head()
| document | label | |
|---|---|---|
| 0 | 굳 ㅋ | 1 |
| 1 | GDNTOPCLASSINTHECLUB | 0 |
| 2 | 뭐야 이 평점들은.... 나쁘진 않지만 점 짜리는 더더욱 아니잖아 | 0 |
| 3 | 지루하지는 않은데 완전 막장임... 돈주고 보기에는.... | 0 |
| 4 | D만 아니었어도 별 다섯 개 줬을텐데.. 왜 D로 나와서 제 심기를 불편하게 하죠?? | 0 |
from sklearn.metrics import accuracy_score
# 학습 데이터를 적용한 TfidfVectorizer를 이용하여 테스트 데이터를 TF-IDF 값으로 Feature 변환함.
tfidf_matrix_test = tfidf_vect.transform(test_df['document'])
# classifier 는 GridSearchCV에서 최적 파라미터로 학습된 classifier를 그대로 이용
best_estimator = grid_cv.best_estimator_
preds = best_estimator.predict(tfidf_matrix_test)
print('Logistic Regression 정확도: ',accuracy_score(test_df['label'],preds))
Logistic Regression 정확도: 0.86172