[Metacritic 포켓몬 S/S 리뷰 분석 프로젝트] 5. 감성분석
본 프로젝트의 마지막 단계는 리뷰 데이터의 감성 분석이다. 현재 데이터셋은 긍정과 부정이 극단적으로 나뉘어있는 형태이므로 감성 분석의 데이터로 적합하다. kaggle이나 dacon competition에서의 분류 알고리즘의 정확도를 높이기보다는 직접 기획부터 수집, 전처리, 분석까지 마친 데이터에 머신러닝을 통한 감성분석을 적용하는 것에 의의를 두었다.
데이터셋 전처리
이전 포스팅에서 중앙값을 기준으로 긍정과 부정으로 분류하였다. 머신러닝 알고리즘을 적용하기 전 사전 단계로 긍정, 부정 상태를 데이터에 인코딩하고 train, test 데이터셋으로 나누는 작업을 진행한다.
데이터셋 인코딩
df_pos = pd.read_csv('df_pos.csv',encoding='utf-8') # 긍정 데이터셋
df_pos = df_pos.drop("Unnamed: 0", axis=1) # csv 1열 unnamed 제거
df_neg = pd.read_csv('df_neg.csv',encoding='utf-8') # 부정 데이터셋
df_neg = df_neg.drop("Unnamed: 0", axis=1) # csv 1열 unnamed 제거
# 긍정 데이터셋을 1로 분류
df_pos['sentiment'] = 1
df_pos = df_pos.drop(['Language','Game','Grade'],axis=1)
df_pos.info()
df_pos.reset_index(drop=True, inplace=True)
df_pos
# 부정 리뷰에 0 추가
df_neg['sentiment']=0
df_neg = df_neg.drop(['Language','Game','Grade'],axis=1)
df_neg.reset_index(drop=True, inplace=True)
df_neg
긍정, 부정 데이터셋을 로드하고 sentiment 컬럼에 긍정은 1, 부정은 0으로 인코딩한 뒤 불필요한 나머지 컬럼은 삭제하였다. 또한 데이터의 불균형을 해소하기 위해 긍정과 부정 모두 1450개의 리뷰로 머신러닝을 진행한다.
df_pos = df_pos.iloc[:1450] # 긍정 데이터셋 1450개 선별
df_neg = df_neg.iloc[:1450] # 부정 데이터셋 1450개 선별
df = pd.concat([df_pos,df_neg],ignore_index=True) #concat 으로 합치기
# 클래스 레이블 무작위로 섞기 : reindex
np.random.seed(0)
df = df.reindex(np.random.permutation(df.index)) # 이 부분 정리
df.to_csv('poke_data.csv',index=False,encoding='utf-8')
총 2900개의 데이터셋을 합친 뒤 reindex로 무작위로 섞어 주었다. 이제 머신러닝을 적용할 수 있는 기초 데이터셋의 모습을 갖추게 되었다.
train, test 데이터셋 나누기
# 2900개 데이터는 train, test 데이터셋 나누기
X_train = df.loc[:1250, 'review'].values
y_train = df.loc[:1250, 'sentiment'].values
X_test = df.loc[1250:, 'review'].values
y_test = df.loc[1250:, 'sentiment'].values
보통은 라이브러리를 이용해 간단히 ,test_train_split을 할 수 있지만 위에서 사전 준비 작업을 진행하고 무작위로 데이터셋을 섞었으므로 pandas로 직접 나누어 보았다.
문서 분류를 위한 로지스틱 회귀 모델
본 프로젝트는 데이터 분석과 머신러닝을 처음 공부하던 시기에 했던 작업물이다. 다음의 머신러닝 알고리즘과 과정들은 '머신러닝 교과서 with 파이썬, 사이킷런, 텐서플로 개정 3판'에서 참고하였음을 밝힌다. 다만 책의 내용을 그대로 옮기지 않고 필자가 공부하고 이해한 방식으로 포스팅해본다.
from sklearn.pipeline import Pipeline
from sklearn.linear_model import LogisticRegression
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.model_selection import GridSearchCV
토크나이저 설정
# 포터 어간 추출 알고리즘 적용
from nltk.stem.porter import PorterStemmer
porter = PorterStemmer()
def tokenizer(text):
return text.split()
def tokenizer_porter(text):
return [porter.stem(word) for word in text.split()]
문서 분류를 위한 알고리즘이므로 nltk에서 porter 어간 추출 알고리즘을 적용하였다. 본 프로젝트에 적용한 stemmer와 lemmatization의 차이에 대해서는 NLP 관련 내용으로 별도로 포스팅한다.
링크 남겨둘것
TFIDF
tfidf = TfidfVectorizer(strip_accents=None,
lowercase=False,
preprocessor=None)
# strip_accents 악센트 제거
# lowercase 소문자로 변경
# preprocessor 전처리
간단한 머신러닝 프로젝트이므로 카운트 기반 BoW 모델인 tfidf를 적용한다. TfidfVectorizer를 활용해 벡터화까지 한번에 할 수 있다.
Grid Search 매개변수 설정
# GridSearch에 사용할 parameter
param_grid = [{'vect__ngram_range': [(1, 1)], # n-gram 설정
'vect__stop_words': [stop, None], # 불용어사전 사용
'vect__tokenizer': [tokenizer, tokenizer_porter], # 일반 tokenizer or 포터 추출기
'clf__penalty': ['l1', 'l2'], # classifier l1,l2 규제
'clf__C': [1.0, 10.0, 100.0]}, # 규제 강도
{'vect__ngram_range': [(1, 1)],
'vect__stop_words': [stop, None],
'vect__tokenizer': [tokenizer, tokenizer_porter],
'vect__use_idf':[False],
'vect__norm':[None], # 정규화
'clf__penalty': ['l1', 'l2'],
'clf__C': [1.0, 10.0, 100.0]},
]
GridSearch에 사용할 parameter를 정리하였다. 토크나이저의 경우 위에서 함수 형태로 일반 토크나이저와 포터 어간추출기를 정의 하였다. 단순히 적용하기만 할 것이라면 그냥 써도 되겠지만 데이터 사이언티스트로 커리어를 생각한다면 document를 참조하여 parameter를 공부하고 정리해 놓는 것이 좋겠다.
Logistic Regression 모델 (Pipeline)
# logistic regression tfidf 모델 : pipeline으로 간편하게
lr_tfidf = Pipeline([('vect', tfidf),
('clf', LogisticRegression(random_state=0, solver='liblinear'))])
pipeline은 Scikit learn 라이브러리에서 스케일링 등의 전처리와 복잡한 gridsearch 과정에 도움을 준다. 특히 GV를 활용해서 복합적인 모델을 적용할 때 자주 사용되는 것 같다. 자세한 내용은 참고한 블로그와 sklearn document 링크로 확인할 수있다.
[Python] sklearn.pipeline, 파이프라인(Pipeline)이란 ?
대부분의 기계 학습 데이터는 최종 모델을 만드는데 있어서 이상적인 형식이 아니다. 범주형 변수를 조작하거나 스케일링 및 정규화와 같은 많은 데이터 변환이 수행되어야 한다. Scikit-Learn은
rk1993.tistory.com
sklearn.pipeline.Pipeline
Examples using sklearn.pipeline.Pipeline: Feature agglomeration vs. univariate selection Feature agglomeration vs. univariate selection, Pipeline ANOVA SVM Pipeline ANOVA SVM, Poisson regression an...
scikit-learn.org
# gridsearch_logisticregression_tfidf
gs_lr_tfidf = GridSearchCV(lr_tfidf, param_grid, # 머신러닝 모델, 그리드서치 parameter
scoring='accuracy', # 평가지표
cv=5, # 5겹 교차검증 : crossvalidation
n_jobs=-1) # 멀티 프로세싱
gs_lr_tfidf.fit(X_train, y_train) # fitting
결론적으로 토크나이저를 설정하고 tfidf를 활용한 logistic regression 알고리즘에 5차 교차검증의 그리드 서치로 최적의 parameter를 찾는다.
print('최적의 매개변수 조합: %s ' % gs_lr_tfidf.best_params_)
그리드 서치 결과 규제강도 10, l2 규제, (1,1)의 n_gram, 불용어 적용 x, 포터 토크나이저가 최적의 매개변수 조합으로 나타났다.
print('CV 정확도: %.3f' % gs_lr_tfidf.best_score_)
최적 매개변수 조합의 5차 교차검증 최고의 정확도는 86.1% 이다.
clf = gs_lr_tfidf.best_estimator_
print('테스트 정확도: %.3f' % clf.score(X_test, y_test))
테스트셋에 대한 정확도는 84.2%로 나타났다.
정리
머신러닝 적용 결과 정확도는 84.2%로 준수한 편이나 데이터의 크기가 2900개로 매우 적어 아쉬움이 남는다. 머신러닝의 등장을 넘어 AI 플랫폼 서비스를 하는 스타트업들도 우후 죽순으로 생겨나는 요즘에도 기본적인 알고리즘에 대한 이해를 높일 수 있는 프로젝트였다. scikit-learn에 대한 공부도 할 수 있고 무엇보다 기획부터 머신러닝 적용까지 스스로 수행했다는 점에서 의미있었다.
데이터 분석 프로젝트라는 측면에서 기존 포켓몬스터 시리즈와의 매출을 비교하거나 구체적인 지표를 통해 살펴보는 과정을 추후에 추가할 예정이다. 또한 포켓몬스터 시리즈에 대해 현재 공부하고 있는 tableau로 대시보드를 만들어 보는 것도 좋은 공부가 될 것 같다.