본문 바로가기

텍스트마이닝

'HelloTalk' 앱 리뷰 감성분석 | Sentiment Analysis using NRC Emotion Lexicon and GoEmotions Dataset [Part 2]

이번에는 다양한 데이터셋과 기술을 사용하여 감성 분석을 수행할 것입니다. 목표는 텍스트에서 감정적 톤을 파악하고, 리부 데이터 안에서 가장 많은 영향을 주는 감정과 토픽을 파악하는 것입니다. 이를 위해 4가지 실험을 수행하였습니다.

실험 설정

  1. 실험 1: 토픽 가중치
  2. 실험 2:  NRC Emotion Lexicon  점수 + 토픽 가중치
  3. 실험 3: GoEmotions 점수 + 토픽 가중치
  4. 실험 4: NRC Emotion Lexicon 점수 + GoEmotions scores + 토픽 가중치

저번에 데이터 전처리가 이미 완료된 데이터셋을 사용하였습니다:

데이터셋

 

이제 리뷰 점수를 바탕으로 감정 레이블을 지정하고, 긍정(1)과 부정(0)으로 표시하며, 중립(3) 점수는 제거했습니다. 

# Label encoding and labeling the reviews as "negative" and "positive" by scores

def label_encode(x):
    if x == 1 or x == 2:
        return 0
    if x == 5 or x == 4:
        return 1
    

def label2name(x):
    if x == 0:
        return "negative"
    if x == 1:
        return "positive"
        
df["label"] = df["score"].apply(lambda x: label_encode(x))
df["sentiment"] = df["label"].apply(lambda x: label2name(x))

 

저번에 준비했던 토픽 가중치 데이터셋을 불러왔어요.

df_topic_weights = pd.read_csv("hellotalk_topic_weights.csv")

df_topic_weights.head()

 

토픽 가중치 데이터셋

필요한 열만 추출하여 데이터셋을 조정했어요.

df_topic_weights = df_topic_weights[['Topic_0', 'Topic_1', 'Topic_2', 'Topic_3', 'Topic_4']]

 


실험 1

  • 토픽 가중치 및 감성 레이블
  • 실험 1은 감성 레이블과 TF-IDF 벡터화가 적용된 토픽 가중치를 포함하는 실험을 의미함
# Label encoding for "sentiment"

from sklearn.preprocessing import LabelEncoder
le = LabelEncoder()
df['label'] = le.fit_transform(df['sentiment'])

 

토픽 가중치를 sparse matrix 형식으로 변경하였어요.

from scipy.sparse import csr_matrix
topic_weights_sparse = csr_matrix(df_topic_weights.values)

 

실험 1 성능 측정

  • 이제 모든 모델에 대해 세 가지 알고리즘을 차례로 테스트하여 특성 중요도 결과를 살펴보겠습니다. 여기서 목표는 모델에서 가장 중요한 특성을 찾는 것입니다. 실험 1은 토픽만 포함하므로 가장 중요한 토픽을 찾는 것입니다.

[실험 1] 결정 트리 토픽 중요도

from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import train_test_split
import seaborn as sns
import matplotlib.pyplot as plt
import pandas as pd
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score

X_train, X_test, y_train, y_test = train_test_split(topic_weights_sparse, df['label'], test_size=0.2, random_state=2001)

clf = DecisionTreeClassifier(criterion='gini')
clf.fit(X_train, y_train)

feature_importances = clf.feature_importances_

feature_names = ['Topic_0', 'Topic_1', 'Topic_2', 'Topic_3', 'Topic_4']

sns.set(rc={'figure.figsize': (11.7, 8.27)})
sns.barplot(x=feature_importances, y=feature_names, orient='h')
plt.title("Decision Tree Feature Importances")
plt.show()

y_pred = clf.predict(X_test)
accuracy = accuracy_score(y_test, y_pred)
precision = precision_score(y_test, y_pred)
recall = recall_score(y_test, y_pred)
f1 = f1_score(y_test, y_pred)

scores_model1 = pd.DataFrame(columns=['Name', 'Accuracy', 'Precision', 'Recall', 'F1 Score'])
scores_model1 = pd.concat([scores_model1, pd.DataFrame({'Name': ['Decision Tree'], 'Accuracy': [accuracy], 'Precision': [precision], 'Recall': [recall], 'F1 Score': [f1]})], ignore_index=True)

 

실험1 결정 트리 토픽 중요도 결과

 

이 그래프에서 볼 수 있듯이, 실험 1에서 가장 중요한 토픽은 토픽 1로 나타났습니다.

 

[실험 1] 랜덤 포레스트 토픽 중요도

 

from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
import seaborn as sns
import matplotlib.pyplot as plt
import pandas as pd
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score

X_train, X_test, y_train, y_test = train_test_split(topic_weights_sparse, df['label'], test_size=0.2, random_state=2001)

clf_rf = RandomForestClassifier(n_estimators=500, max_depth=20, random_state=50)
clf_rf.fit(X_train, y_train)

feature_importances_rf = clf_rf.feature_importances_

feature_names_rf = ['Topic_0', 'Topic_1', 'Topic_2', 'Topic_3', 'Topic_4']

sns.set(rc={'figure.figsize': (11.7, 8.27)})
sns.barplot(x=feature_importances_rf, y=feature_names_rf, orient='h')
plt.title("Random Forest Feature Importances")
plt.show()

y_pred_rf = clf_rf.predict(X_test)
accuracy_rf = accuracy_score(y_test, y_pred_rf)
precision_rf = precision_score(y_test, y_pred_rf)
recall_rf = recall_score(y_test, y_pred_rf)
f1_rf = f1_score(y_test, y_pred_rf)

scores_model1 = pd.concat([scores_model1, pd.DataFrame({'Name': ['Random Forest'], 'Accuracy': [accuracy_rf], 'Precision': [precision_rf], 'Recall': [recall_rf], 'F1 Score': [f1_rf]})], ignore_index=True)

 

실험1 랜덤 포레스트 토픽 중요도 결과

 

랜덤 포레스트에서도 유사하게 토픽 1이 가장 중요한 토픽으로 나타났습니다.

 

[실험 1] XGBoost 토픽 중요도

 

from xgboost import XGBClassifier
from sklearn.model_selection import train_test_split
import seaborn as sns
import matplotlib.pyplot as plt
import pandas as pd
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score

X_train, X_test, y_train, y_test = train_test_split(topic_weights_sparse, df['label'], test_size=0.2, random_state=2001)

clf_xgb = XGBClassifier(learning_rate=0.1, n_estimators=100, random_state=50)
clf_xgb.fit(X_train, y_train)

feature_importances_xgb = clf_xgb.feature_importances_

feature_names_xgb = ['Topic_0', 'Topic_1', 'Topic_2', 'Topic_3', 'Topic_4']

sns.set(rc={'figure.figsize': (11.7, 8.27)})
sns.barplot(x=feature_importances_xgb, y=feature_names_xgb, orient='h')
plt.title("XGBoost Feature Importances")
plt.show()

y_pred_xgb = clf_xgb.predict(X_test)
accuracy_xgb = accuracy_score(y_test, y_pred_xgb)
precision_xgb = precision_score(y_test, y_pred_xgb)
recall_xgb = recall_score(y_test, y_pred_xgb)
f1_xgb = f1_score(y_test, y_pred_xgb)

scores_model1 = pd.concat([scores_model1, pd.DataFrame({'Name': ['XGBoost'], 'Accuracy': [accuracy_xgb], 'Precision': [precision_xgb], 'Recall': [recall_xgb], 'F1 Score': [f1_xgb]})], ignore_index=True)

 

실험1 XGBoost 토픽 중요도 결과

 

XGBoost에서는 가장 중요한 토픽이 토픽 4로 나타났으며, 그 다음으로 중요한 토픽은 토픽 3이었습니다.

 


실험 2

  • 토픽 기중치 + NRC 감정 점수 + 감성 레이블

각 리뷰에 대해 NRC의 7가지 감정 레이블에 대한 감정 점수를 계산합니다.

# NRCLex Emotion Lexicon "emotion_scores" calculation function

from nrclex import NRCLex

def get_emotion_scores(text):
    text_object = NRCLex(text)
    emotion_scores = text_object.affect_frequencies
    
    max_emotion = max(emotion_scores, key=emotion_scores.get)
    max_score = emotion_scores[max_emotion]
    
    #emotion_scores['max_emotion'] = max_emotion
    #emotion_scores['max_score'] = max_score
    
    return emotion_scores

 

# Getting sentiment scores for text in each row using 'content_cleaned' column
emotion_scores_df = df['content_lemmatized'].apply(get_emotion_scores).tolist()

# Creating a df of sentiment scores
emotions_df = pd.DataFrame(emotion_scores_df)

# Copying 'anticipation' column data to  'anticip' column
emotions_df['anticip'] = emotions_df['anticipation']

# Dropping the 'anticipation' column
emotions_df = emotions_df.drop(columns=['anticipation'])

# Rename 
emotions_df.rename(columns={'anticip': 'anticipation'}, inplace=True)

# Combining emotion scores df with original df
nrc_emotions = pd.concat([df.reset_index(drop=True), emotions_df.reset_index(drop=True)], axis=1)
emotion_columns = ['joy','trust', 'anticipation', 'surprise', 'anger', 'disgust', 'fear', 'sadness']

# Converting NaN values in all emotion columns to 0

nrc_emotions[emotion_columns] = nrc_emotions[emotion_columns].fillna(0)

실험 2 성능 측정

  • 실험 2는 토픽 가중치와 감정 레이블을 결합하여 NRC 감정 어휘를 사용하여 계산된 감정 점수를 사용하는 모델입니다. 따라서, 특성 중에 토픽과 감정 레이블이 함께 나타납니다.
# Label encoding

from sklearn.preprocessing import LabelEncoder
le = LabelEncoder()
nrc_emotions['label'] = le.fit_transform(nrc_emotions['sentiment'])

# Getting emotion scores

X_emotion = nrc_emotions[emotion_columns].values

# Changing the emotion scores array to sparse matrix

X_emotion_sparse = csr_matrix(X_emotion)

from scipy.sparse import hstack

X_combined = hstack((topic_weights_sparse, X_emotion_sparse))

X_combined.columns = feature_names_with_emotions

X_train, X_test, y_train, y_test = train_test_split(X_combined, nrc_emotions['label'], test_size=0.2, random_state=1)

 

[실험 2] 결정 트리 특성 중요도

 

clf = DecisionTreeClassifier(criterion='gini')
clf.fit(X_train, y_train)

feature_importances = clf.feature_importances_

sns.set(rc={'figure.figsize': (11.7, 8.27)})
sns.barplot(x=feature_importances, y=feature_names_with_emotions, orient='h')
plt.title("Decision Tree Feature Importances")
plt.show()

y_pred = clf.predict(X_test)
accuracy = accuracy_score(y_test, y_pred)
precision = precision_score(y_test, y_pred)
recall = recall_score(y_test, y_pred)
f1 = f1_score(y_test, y_pred)

 

실험 2 결정 트리 특성 중요도 결과

 

 

[실험 2] 랜덤포레스트 특성 중요도

from sklearn.ensemble import RandomForestClassifier

# Random Forest modelini oluşturun ve eğitin
rf_clf = RandomForestClassifier(n_estimators=500, max_depth=20, random_state=50)
rf_clf.fit(X_train, y_train)

rf_feature_importances = rf_clf.feature_importances_

sns.set(rc={'figure.figsize': (11.7, 8.27)})
sns.barplot(x=rf_feature_importances, y=feature_names_with_emotions, orient='h')
plt.title("Random Forest Feature Importances")
plt.show()

rf_y_pred = rf_clf.predict(X_test)
rf_accuracy = accuracy_score(y_test, rf_y_pred)
rf_precision = precision_score(y_test, rf_y_pred)
rf_recall = recall_score(y_test, rf_y_pred)
rf_f1 = f1_score(y_test, rf_y_pred)

 

실험 2 랜덤포레스트 특성 중요도 결과

[실험 2] XGBoost 특성 중요도

 

from xgboost import XGBClassifier

xgb_clf = XGBClassifier(learning_rate=0.1, n_estimators=100, random_state=51)
xgb_clf.fit(X_train, y_train)

xgb_feature_importances = xgb_clf.feature_importances_

sns.set(rc={'figure.figsize': (11.7, 8.27)})
sns.barplot(x=xgb_feature_importances, y=feature_names_with_emotions, orient='h')
plt.title("XGBoost Feature Importances")
plt.show()

xgb_y_pred = xgb_clf.predict(X_test)
xgb_accuracy = accuracy_score(y_test, xgb_y_pred)
xgb_precision = precision_score(y_test, xgb_y_pred)
xgb_recall = recall_score(y_test, xgb_y_pred)
xgb_f1 = f1_score(y_test, xgb_y_pred)

 

결과를 `scores` 데이터프레임에 추가합니다.

scores_model2 = pd.DataFrame(columns=['Name', 'Accuracy', 'Precision', 'Recall', 'F1 Score'])
scores_model2 = pd.concat([scores_model2, pd.DataFrame({'Name': ['Decision Tree'], 'Accuracy': [accuracy], 'Precision': [precision], 'Recall': [recall], 'F1 Score': [f1]})], ignore_index=True)
scores_model2 = pd.concat([scores_model2, pd.DataFrame({'Name': ['Random Forest'], 'Accuracy': [rf_accuracy], 'Precision': [rf_precision], 'Recall': [rf_recall], 'F1 Score': [rf_f1]})], ignore_index=True)
scores_model2 = pd.concat([scores_model2, pd.DataFrame({'Name': ['XGBoost'], 'Accuracy': [xgb_accuracy], 'Precision': [xgb_precision], 'Recall': [xgb_recall], 'F1 Score': [xgb_f1]})], ignore_index=True)

 


 

실험 3

  • 토픽 기중치 + GoEmotions 감정 점수 + 감성 레이블

각 리뷰에 대해 GoEmotions의 28가지 감정 레이블에 대한 감정 점수를 계산합니다.

 

from transformers import AutoTokenizer, pipeline
from optimum.onnxruntime import ORTModelForSequenceClassification

model_id = "SamLowe/roberta-base-go_emotions-onnx"
file_name = "onnx/model_quantized.onnx"

model = ORTModelForSequenceClassification.from_pretrained(model_id, file_name=file_name)
tokenizer = AutoTokenizer.from_pretrained(model_id)

onnx_classifier = pipeline(
    task="text-classification",
    model=model,
    tokenizer=tokenizer,
    top_k=None,
    function_to_apply="sigmoid",  # optional as is the default for the task
)

 

import pandas as pd

def classify_emotions(text):
    model_outputs = onnx_classifier(text)
    
    emotion_scores = {result['label']: result['score'] for result in model_outputs[0]}
    
    return emotion_scores

df['emotion_scores'] = df['content_lemmatized'].apply(classify_emotions)

df_emotion_scores = pd.json_normalize(df['emotion_scores'])

df_concat_emotions = pd.concat([df, df_emotion_scores], axis=1)

 

실험 3 성능 측정

  • 실험 2는 토픽 가중치와 감정 레이블을 결합하여 GoEmotions 감정 어휘를 사용하여 계산된 감정 점수를 사용하는 실험을 의미합니다. 따라서, 특성 중에 토픽과 감정 레이블이 함께 나타납니다.
X_combined_2 = hstack((topic_weights_sparse, go_emotions_matrix))

feature_names_with_emotions = feature_names + selected_columns

X_combined_2.columns = feature_names_with_emotions

from sklearn.preprocessing import LabelEncoder
le = LabelEncoder()
df_concat_emotions = df_concat_emotions.copy()
df_concat_emotions['label'] = le.fit_transform(df_concat_emotions['sentiment'])

X_train, X_test, y_train, y_test = train_test_split(X_combined_2, df_concat_emotions['label'], test_size=0.2, random_state=2006)

 

[실험 3] 결정 트리 특성 중요도

from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import train_test_split
import seaborn as sns
import matplotlib.pyplot as plt

clf = DecisionTreeClassifier(criterion='gini')
clf.fit(X_train, y_train)

feature_importances = clf.feature_importances_

sns.set(rc={'figure.figsize': (11.7, 8.27)})
sns.barplot(x=feature_importances, y=feature_names_with_emotions, orient='h')
plt.title("Decision Tree Feature Importances")
plt.show()

y_pred = clf.predict(X_test)
accuracy = accuracy_score(y_test, y_pred)
precision = precision_score(y_test, y_pred)
recall = recall_score(y_test, y_pred)
f1 = f1_score(y_test, y_pred)

 

실험 3 결정 트리 특성 중요도

 

[실험 3] 랜덤포레스트 특성 중요도

from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
import seaborn as sns
import matplotlib.pyplot as plt
import pandas as pd
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score

rf_clf = RandomForestClassifier(n_estimators=500, max_depth=20, random_state=50)
rf_clf.fit(X_train, y_train)

rf_feature_importances = rf_clf.feature_importances_

sns.set(rc={'figure.figsize': (11.7, 8.27)})
sns.barplot(x=rf_feature_importances, y=feature_names + selected_columns, orient='h')
plt.title("Random Forest Feature Importances")
plt.show()

rf_y_pred = rf_clf.predict(X_test)
rf_accuracy = accuracy_score(y_test, rf_y_pred)
rf_precision = precision_score(y_test, rf_y_pred)
rf_recall = recall_score(y_test, rf_y_pred)
rf_f1 = f1_score(y_test, rf_y_pred)

 

실험 3 랜덤포레스트 특성 중요도 결과

 

[실험 3] XGBoost 특성 중요도

from xgboost import XGBClassifier
from sklearn.model_selection import train_test_split
import seaborn as sns
import matplotlib.pyplot as plt
import pandas as pd
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score

xgb_clf = XGBClassifier(learning_rate=0.1, n_estimators=100, random_state=50)
xgb_clf.fit(X_train, y_train)

xgb_feature_importances = xgb_clf.feature_importances_

sns.set(rc={'figure.figsize': (11.7, 8.27)})
sns.barplot(x=xgb_feature_importances, y=feature_names + selected_columns, orient='h')
plt.title("XGBoost Feature Importances")
plt.show()

xgb_y_pred = xgb_clf.predict(X_test)
xgb_accuracy = accuracy_score(y_test, xgb_y_pred)
xgb_precision = precision_score(y_test, xgb_y_pred)
xgb_recall = recall_score(y_test, xgb_y_pred)
xgb_f1 = f1_score(y_test, xgb_y_pred)

 

실험 3 XGBoost 특성 중요도 결과

 

scores_model3 = pd.DataFrame(columns=['Name', 'Accuracy', 'Precision', 'Recall', 'F1 Score'])
scores_model3 = pd.concat([scores_model3, pd.DataFrame({'Name': ['Decision Tree'], 'Accuracy': [accuracy], 'Precision': [precision], 'Recall': [recall], 'F1 Score': [f1]})], ignore_index=True)
scores_model3 = pd.concat([scores_model3, pd.DataFrame({'Name': ['Random Forest'], 'Accuracy': [rf_accuracy], 'Precision': [rf_precision], 'Recall': [rf_recall], 'F1 Score': [rf_f1]})], ignore_index=True)
scores_model3 = pd.concat([scores_model3, pd.DataFrame({'Name': ['XGBoost'], 'Accuracy': [xgb_accuracy], 'Precision': [xgb_precision], 'Recall': [xgb_recall], 'F1 Score': [xgb_f1]})], ignore_index=True)

 

 

실험 4 성능 측정

  • 실험 4는 NRC와 GoEmotions 두 가지 감성 모델을 모두 사용하는 실험을 의미함
X_combined_4 = hstack((X_combined, go_emotions_matrix))

X_combined_4.columns = ['Topic_0', 'Topic_1','Topic_2', 'Topic_3','Topic_4', 
                        'N_joy','N_trust', 'N_anticipation', 'N_surprise', 'N_anger', 'N_disgust', 'N_fear', 'N_sadness',
                        'G_admiration', 'G_amusement', 'G_approval', 'G_caring', 'G_desire', 'G_excitement','G_gratitude', 'G_joy', 'G_love', 'G_optimism', 'G_pride', 'G_relief', 'G_sadness', 'G_fear',
                        'G_embarrassment', 'G_disapproval', 'G_disappointment', 'G_annoyance', 'G_anger', 'G_nervousness', 'G_remorse', 'G_grief', 'G_disgust', 'G_realization', 'G_surprise', 'G_curiosity', 'G_confusion', 'G_neutral']
                        
X_train, X_test, y_train, y_test = train_test_split(X_combined_4, df_concat_emotions['label'], test_size=0.2, random_state=2006)

 

[실험 4] 결정 트리 특성 중요도

from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import train_test_split
import seaborn as sns
import matplotlib.pyplot as plt

clf = DecisionTreeClassifier(criterion='gini')
clf.fit(X_train, y_train)

feature_importances = clf.feature_importances_

sns.set(rc={'figure.figsize': (11.7, 8.27)})
sns.barplot(x=feature_importances, y=X_combined_4.columns, orient='h')
plt.title("Decision Tree Feature Importances")
plt.show()

y_pred = clf.predict(X_test)
accuracy = accuracy_score(y_test, y_pred)
precision = precision_score(y_test, y_pred)
recall = recall_score(y_test, y_pred)
f1 = f1_score(y_test, y_pred)

 

 

실험4 결정 트리 특성 중요도 결과

 

 

[실험 4] 랜덤포레스트 특성 중요도

from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
import seaborn as sns
import matplotlib.pyplot as plt
import pandas as pd
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score

rf_clf = RandomForestClassifier(n_estimators=500, max_depth=20, random_state=50)
rf_clf.fit(X_train, y_train)

rf_feature_importances = rf_clf.feature_importances_

sns.set(rc={'figure.figsize': (11.7, 8.27)})
sns.barplot(x=rf_feature_importances, y=X_combined_4.columns, orient='h')
plt.title("Random Forest Feature Importances")
plt.show()

rf_y_pred = rf_clf.predict(X_test)
rf_accuracy = accuracy_score(y_test, rf_y_pred)
rf_precision = precision_score(y_test, rf_y_pred)
rf_recall = recall_score(y_test, rf_y_pred)
rf_f1 = f1_score(y_test, rf_y_pred)

 

실험4 랜덤포레스트 특성 중요도 결과

 

[실험 4] XGBoost 특성 중요도

from xgboost import XGBClassifier
from sklearn.model_selection import train_test_split
import seaborn as sns
import matplotlib.pyplot as plt
import pandas as pd
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score

xgb_clf = XGBClassifier(learning_rate=0.1, n_estimators=100, random_state=50)
xgb_clf.fit(X_train, y_train)

xgb_feature_importances = xgb_clf.feature_importances_

sns.set(rc={'figure.figsize': (11.7, 8.27)})
sns.barplot(x=xgb_feature_importances, y=X_combined_4.columns, orient='h')
plt.title("XGBoost Feature Importances")
plt.show()

xgb_y_pred = xgb_clf.predict(X_test)
xgb_accuracy = accuracy_score(y_test, xgb_y_pred)
xgb_precision = precision_score(y_test, xgb_y_pred)
xgb_recall = recall_score(y_test, xgb_y_pred)
xgb_f1 = f1_score(y_test, xgb_y_pred)

 

실험4 XGBoost 특성 중요도 결과

 

scores_model4 = pd.DataFrame(columns=['Name', 'Accuracy', 'Precision', 'Recall', 'F1 Score'])
scores_model4 = pd.concat([scores_model4, pd.DataFrame({'Name': ['Decision Tree'], 'Accuracy': [accuracy], 'Precision': [precision], 'Recall': [recall], 'F1 Score': [f1]})], ignore_index=True)
scores_model4 = pd.concat([scores_model4, pd.DataFrame({'Name': ['Random Forest'], 'Accuracy': [rf_accuracy], 'Precision': [rf_precision], 'Recall': [rf_recall], 'F1 Score': [rf_f1]})], ignore_index=True)
scores_model4 = pd.concat([scores_model4, pd.DataFrame({'Name': ['XGBoost'], 'Accuracy': [xgb_accuracy], 'Precision': [xgb_precision], 'Recall': [xgb_recall], 'F1 Score': [xgb_f1]})], ignore_index=True)

 

  • 모든 실험의 메트릭 결과를 포함하는 표는 다음과 같습니다:

Scores Table [ALL MODELS]

import plotly.graph_objects as go

# Merging all model scores together
merged_scores = pd.concat([scores_model1, scores_model2, scores_model3, scores_model4])


fig = go.Figure()

# Metrics selected
metrics = ['Accuracy', 'F1 Score']

colors = {'Accuracy': 'darkviolet', 'F1 Score': 'lightseagreen'}

for metric in metrics:
    x_values = [f'{model_name} - Model 1' for model_name in merged_scores['Name'].unique()] + \
               [f'{model_name} - Model 2' for model_name in merged_scores['Name'].unique()] + \
               [f'{model_name} - Model 3' for model_name in merged_scores['Name'].unique()] + \
               [f'{model_name} - Model 4' for model_name in merged_scores['Name'].unique()]
    
    y_values = merged_scores[metric].tolist() * 3
    
    fig.add_trace(go.Bar(x=x_values,
                         y=y_values,
                         name=metric,
                         marker_color=colors[metric]))

fig.update_layout(title='Comparison of Model Performances',
                  xaxis_title='Classifier Name & Model',
                  yaxis_title='Score Value',
                  barmode='group')
fig.show()

 

Scores Table [All Experiments]

표 2는 네 가지 실험 설정에 따른 세 가지 모델의 성능 결과를 보여줍니다. 실험 1에서는 XGB가 가장 높은 성능을 보였으며, 특히 정확도(0.7407), 재현율(0.9977), F1 점수(0.8510)에서 두드러졌습니다. 실험 2에서는 XGB가 다시 최고 성능을 기록하며 정확도(0.8043), 재현율(0.9338), F1 점수(0.8759)에서 가장 높은 값을 보였습니다. 실험 3에서는 RF가 가장 높은 정확도(0.8583), 재현율(0.9270), F1 점수(0.9065)를 기록했습니다. 마지막으로 실험 4에서는 RF가 정확도(0.8666), 정밀도(0.8956), F1 점수(0.9115)에서 가장 높은 성능을 보였고, XGB는 재현율(0.9286)에서 최고 점수를 기록했습니다. 전반적으로 실험 4가 가장 높은 성능을 보였으며, 이는 감정 모델의 내용이 확장되고 더 세분화될수록 성능이 더욱 향상된다는 것을 의미합니다.

 

Feature Importance 결과

Feature Importance 결과

 

Feature Importance 메트릭을 사용하여 앱 리뷰에서 감정 내용과 논의된 주제를 이해하는 데 각 피처의 중요성을 정량화했습니다. 표 3은 실험 4에서 수행된 피처 중요도 분석 결과를 보여줍니다.

DT에서는 Topic 3(2.11%)이 중요한 요인으로 나타났습니다. NRC 감정 중 '신뢰'(1.34%)가 두드러졌으며, GoEmotions에서는 '칭찬'(34.76%)이 가장 중요했고, 그 다음으로 '짜증'(9.56%)과 '자부심'(3.76%)이 있었습니다. RF에서는 Topic 1(1.55%)이 주목받았습니다. NRC 감정 중 '신뢰'(1.42%)가 두드러졌으며, GoEmotions에서는 '칭찬'(11.13%)이 가장 중요했고, '짜증'(6.36%)과 '감사'(6.04%)가 뒤따랐습니다. XGB에서는 Topic 1(0.81%)이 중요한 주제로 나타났습니다. NRC 감정 중 '신뢰'(2.54%)가 두드러졌으며, GoEmotions에서는 '칭찬'(36.53%)이 가장 중요한 감정으로 나타났고, '짜증'(10.45%)과 '불승인'(4.29%)이 뒤따랐습니다.

모든 모델에서 '칭찬'과 '짜증'이 가장 중요한 감정으로 나타났습니다. '칭찬'은 사용자가 앱을 긍정적으로 평가함을 나타내며, '짜증'은 사용자가 앱의 기능에 대해 불만이 있음을 나타냅니다. 이러한 문제를 해결하는 것이 앱의 전반적인 사용성을 향상시키는 데 중요합니다.

 

이 연구를 통해 HelloTalk 앱 리뷰를 분석하여 사용자가 앱에 대해 느끼는 감정을 파악하고자 하였습니다.