네이버 영화 평점 데이터를 기반으로 분석을 해보기로 한다.
일반적으로 한글의 언어처리는 '띄어쓰기'와 '다양한 조사'로 인해 처리가 어렵다고 한다. 띄어쓰기를 잘못하면 의미가 왜곡되어버리기 쉽상이고, 조사는 경우의 수가 너무 맘ㄶ기 때문에 어근 추출 등의 전처리 시 제거하기가 까다롭다.
파이썬의 대표적인 한글 형태소 패키지다.
형태소의 사전적인 의미는 '단어로서 의미를 가지는 최소 단위'로 정의할 수 있으며, 형태소 분석이란 일반적으로 말뭉치를 이러한 형태소 어근 단위로 쪼개고 각 형태소에 품사 태그(POS tagging)을 부착하는 작업을 지칭한다.
pip install konlpy
KoNLPy의 설치가 완료 되었다면 이를 이용해 네이버 영화 평점 데이터를 기반으로 감성 분석을 수행해보자. 데이터는 깃허브에서 확인 및 다운로드 받으면 된다. 사용하는 파일은 ratings_train과 ratings_test 텍스트 파일이다.
import pandas as pd
train_df = pd.read_csv('https://raw.githubusercontent.com/e9t/nsmc/master/ratings_train.txt', sep='\t')
train_df[:3]

train_df['label'].value_counts()
0 75173
1 74827
Name: label, dtype: int64
0은 부정 감성, 1은 긍정 감성을 의미하는데, 이 비율이 어느 한 쪽으로 치우치지 않고 거의 균등하게 분포하고 있음을 확인했다.
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
데이터는 150,000 x 3 의 크기이며, 1번 컬럼인 document에 일부 결측이 있는 것으로 보인다. 또한 문자가 아닌 숫자의 경우에는 '단어적인 의미'를 가지기 어렵기 때문에 이들을 정규 표현식 모듈인 re를 이용해 공백으로 변환시켜주자
import re
# document 결측 공백으로 변환
train_df = train_df.fillna(" ")
# 정규 표현식으로 숫자를 공백으로 변경 (정규 표현식에서 \d는 숫자를 의미)
train_df["document"] = train_df["document"].apply(lambda x: re.sub(r"\d+", " ", x))
# test set 동일 작업
test_df = pd.read_csv('https://raw.githubusercontent.com/e9t/nsmc/master/ratings_test.txt', sep='\t')
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)
twitter.morphs() 메서드를 이용하면 입력 인자로 들어온 문장을 형태소 단어 형태로 토큰화해 list 객체로 반환해준다.
from konlpy.tag import Okt
twitter = Okt()
def tw_tokenizer(text):
# 텍스트를 형태소 단어로 토큰화 후 리스트로 반환
tokens_ko = twitter.morphs(text)
return tokens_ko
from sklearn.feature_extraction.text import TfidfVectorizer
# Twitter 객체의 morphs() 객체를 이용한 tokenizer를 사용
# ngram_range는 (1, 2)
tfidf_vect = TfidVectorizer(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'])
# 로지스틱 회귀를 이용해 감성 분석 분류 수행
from sklearn.linear_model import LogisticRegression
lg_clf = LogisticRegression(random_state=0, solver='liblinear')
# 파라미터 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)
print(grid_cv.best_params_, round(grid_cv.best_score_, 4))
Fitting 5 folds for each of 5 candidates, totalling 25 fits
{'C': 3.5} 0.8628
C가 3.5일때 최고 약 0.86의 정확도
from sklearn.metrics import accuracy_score
# 학습 데이터를 적용한 TfidVectorizer를 이용해 테스트 데이터를 TF-IDF 값으로 피처 변환
tfidf_matrix_test = tfidf_vect.transform(test_df['document'])
# classifier는 GridSearchCV에서 최적 파라미터로 학습된 클래스 피어를 그대로 이용
best_estimator = grid_cv.best_estimator_
preds = best_estimator.predict(tfidf_matrix_test)
print(f'정확도 : {accuracy_score(test_df['label'], preds)}')
정확도 : 0.86172