개발자식

[머신러닝] 감성분류 모델링, XGBoost 본문

AI/Machine Learning

[머신러닝] 감성분류 모델링, XGBoost

밍츠 2022. 5. 20. 22:01

TF-IDF와 XGBoost를 활용하여 네이버 리뷰 감정분석 모델을 학습시키고 구글 드라이브에 저장해보자


 

 

1. Install & Import Libraries

!pip install konlpy==0.5.2

import konlpy
import sklearn

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

import os
import re
import tqdm
import urllib.request
from collections import Counter

from konlpy.tag import Okt

2. Load Data

urllib.request.urlretrieve("https://raw.githubusercontent.com/bab2min/corpus/master/sentiment/naver_shopping.txt", filename="ratings_total.txt")
reviews_df = pd.read_table('ratings_total.txt', names=['ratings', 'reviews'])

3. Preprocess the data

 

1. 중복 체크 & 중복 제거

reviews_df[ reviews_df['reviews'].duplicated(keep=False) == True ].sort_values('reviews')

- keep=False 옵션으로 중복된 항목들 모두 꺼낸다. 

- keep = False 일 때 개수 : 184 rows

- keep 옵션 x 일 때 개수 : 92 rows

 

중복된 항목 keep="first" (by default)로 설정한다.

 

중복 제거

df = df.drop_duplicates('Sentence') 
df = df.reset_index(drop=True)​

 

2. 리뷰 별점을 기준으로 하여 Label 열을 생성한다. (긍정 ==1, 부정 ==0)

reviews_df['label'] = reviews_df['ratings'].apply(lambda x : 1 if x > 3 else 0)

3. 형태소 분석 ( +불용어 제거)

 

def tokenize_and_stemming(sentence, tokenizer):

    sentence = re.sub("[^\s0-9a-zA-Zㄱ-ㅎㅏ-ㅣ가-힣]", "", sentence) 
    
    raw_pos_tagged = tokenizer.pos(sentence, stem=True) 

    sentence_tokenized = []
    for token, pos in raw_pos_tagged: 
        if (len(token) != 1) & (pos in ["Noun", "VerbPrefix", "Verb", "Adverb", "Adjective", "Conjunction", "KoreanParticle"]):
            sentence_tokenized.append(token)  

    return sentence_tokenized

- 정규식을 이용하여 [ whitespaces, 숫자, 영문 알파벳, 한글(+자모음) ]이 아닌 것을 공백으로 치환 (특수문자 제거)

- pos 기준 제거 대상 : ["Josa", "Eomi", "Punctuation", "Foreign", "Number", "Suffix", "Determiner"]

- 리뷰 텍스트 상 형태소의 순서를 고려하는 경우 의미를 갖게 됨 : "VerbPrefix" (잘/안/못), "Conjunction"(하지만/그러나/그리고), "KoreanParticle"(ㅋㅋㅋ/ㅎㅎㅎ/ㅠㅠ)

 

 

okt = Okt()
tokenized_sentences = []


# tqdm 사용
for sentence in tqdm.tqdm(reviews_df['reviews']):
    try:
        tokenized_sentences.append(tokenize_and_stemming(sentence, okt))
    except:
        print("Error occured at :", sentence)
        tokenized_sentences.append([])

reviews_df['reviews'] = tokenized_sentences
# tqdm 미사용
reviews_df['reviews'] = reviews_df['reviews'].apply(tokenize_and_stemming, tokenizer=okt)

- tqdm : 진행이라는 의미를 담고 있는 아랍어, 현재 실행 중인 프로그램이 얼마나 진행되고 있는지 확인할 수 있다.

 

 

 

 

긍정 리뷰에 많이 포함되었던 형태소를 체크해보면

tokens_positive = []

for tokens_list in reviews_df[reviews_df['label'] == 1]['reviews']:
    tokens_positive.extend(tokens_list)

tokens_positive_counted = Counter(tokens_positive)
tokens_positive_counted.most_common(20)

- list.append(x)는 리스트 끝에 x를 그대로 넣고 list.extend(iterable)은 리스트 끝에 가장 바깥쪽 iterable의 모든 항목을 넣는다.

 

결과 :

 

 

4. 전처리 적용 후 데이터 중간 저장

 

colab 런타임 초기화를 대비해 구글 드라이브에 현재까지 처리를 마친 데이터를 저장

from google.colab import drive
drive.mount('/gdrive')

data_path = '/gdrive/MyDrive/colab_data/temp_data/' 

if not os.path.exists(data_path): 
    os.makedirs(data_path)

reviews_df.to_csv(data_path + 'data_reviews_tokenized.csv', encoding='utf-8', index=False)

- 구글 드라이브 내 data_path 경로의 폴더가 존재하지 않을 시 새로이 생성

 

+ Load

data_path = '/gdrive/MyDrive/colab_data/temp_data/' 
reviews_df = pd.read_csv(data_path + 'data_reviews_tokenized.csv', encoding='utf-8')
reviews_df['reviews'] = reviews_df['reviews'].apply(eval)
reviews_df

 

4. Modeling -XGBOOST

 

from sklearn import model_selection
from sklearn.feature_extraction.text import TfidfVectorizer 
from sklearn.metrics import accuracy_score

from xgboost import XGBClassifier

train_x, test_x, train_y, test_y = model_selection.train_test_split(reviews_df['reviews'], reviews_df['label'], 
                                                                    test_size=0.3, 
                                                                    random_state=42) 
                                                                    
def list_to_list(doc):
    return doc
    
vectorizer = TfidfVectorizer(tokenizer=list_to_list, token_pattern=None, lowercase=False) 
vectorizer.fit(train_x)

train_x_vector = vectorizer.transform(train_x)
test_x_vector = vectorizer.transform(test_x)


model_xgb = XGBClassifier(max_depth=4, 
                          n_estimators=1500, 
                          random_state=42) 

model_xgb.fit(train_x_vector, train_y, 
              early_stopping_rounds=10, 
              eval_metric=["error", "logloss"], 
              eval_set=[(train_x_vector, train_y), (test_x_vector, test_y)])

- error == binary classification error

- logloss == cross-entropy

results = model_xgb.evals_result() 
epochs = len(results['validation_0']['error'])
x_axis = range(0, epochs)

fig, ax = plt.subplots(dpi=100, figsize=(10, 5))
ax.plot(x_axis, results['validation_0']['logloss'], label='Train')
ax.plot(x_axis, results['validation_1']['logloss'], label='Test')
ax.legend()
ax.grid()
plt.ylabel('Log-Loss (Cross-entropy)')
plt.title('XGBoost Log-Loss')
plt.show()

fig, ax = plt.subplots(dpi=100, figsize=(10, 5))
ax.plot(x_axis, results['validation_0']['error'], label='Train')
ax.plot(x_axis, results['validation_1']['error'], label='Test')
ax.legend()
ax.grid()
plt.ylabel('Classification-Error')
plt.title('XGBoost Classification-Error')
plt.show()

 

 

+ Model & Vectorizer Save /Load , Predict on a new sentence

import joblib
from google.colab import drive

drive.mount('/gdrive')

data_path = '/gdrive/MyDrive/colab_data/temp_data/' 
if not os.path.exists(data_path): 
    os.makedirs(data_path)

joblib.dump(model_xgb, data_path+'model_xgb_tfidf_v1.pkl', compress=True) 
joblib.dump(vectorizer, data_path+'vectorizer_tfidf.pkl', compress=True)
import joblib
from google.colab import drive

drive.mount('/gdrive')
data_path = '/gdrive/MyDrive/colab_data/temp_data/' 

model_xgb = joblib.load(data_path+'model_xgb_tfidf_v1.pkl')
vectorizer = joblib.load(data_path+'vectorizer_tfidf.pkl')
new_sentence = "아주좋아요 바지 정말 좋아서2개 더 구매했어요 이가격에 대박입니다. 바느질이 조금 엉성하긴 하지만 편하고 가성비 최고예요."

okt = Okt()
tokenized_sentences = []
tokenized_sentences.append(tokenize_and_stemming(new_sentence, okt))

new_sentence_vector = vectorizer.transform(tokenized_sentences) 

print(model_xgb.predict_proba(new_sentence_vector))
print(model_xgb.predict(new_sentence_vector))

결과 :

 

new_sentences = ["아주좋아요 바지 정말 좋아서2개 더 구매했어요 이가격에 대박입니다. 바느질이 조금 엉성하긴 하지만 편하고 가성비 최고예요.",
                "너무 맛있어요! 4명이 먹을거라 넉넉하게 샀다했는데 순식간에 없어집니다! 꼭 드셔보세요!", 
                "후기 좋아서 샀는데 진짜 별로에요... 재질두 비니루같은 재질....", 
                "내용물이 안나와요 ㅜㅜ 무용지물 ㅜㅜ 여러개 샀는데 ㅜㅜ"
                ]

okt = Okt()
tokenized_sentences = []

for sentence in new_sentences:
    tokenized_sentences.append(tokenize_and_stemming(sentence, okt))

new_sentence_vector = vectorizer.transform(tokenized_sentences) 

print(model_xgb.predict_proba(new_sentence_vector))
Comments