2021. 9. 3. 07:48ㆍ개인 프로젝트/게임 여론 분석
[T 게임의 <언더월드> 이벤트 여론 분석 - 군집화 편]
6. 군집화(클러스터링)
1) 군집화 목표
마지막으로 T 게임의 <언더월드> 이벤트에 대한 여론을 분석하고자, 클러스터링을 시도했다. 전처리가 완료된 데이터들을 이용해 여러 군집으로 나누어, 각 군집이 지니는 대표적인 여론을 파악하고자 한다.
2) 데이터 토큰화
클러스터링을 진행할 때도 마찬가지로 데이터를 토큰화해야 한다. 즉 불필요한 단어는 지우고 필요한 단어만 남기는 작업을 해야 한다. 모든 데이터를 사용할 생각이기에, 먼저는 전처리한 모든 데이터를 합치는 작업을 진행했다.
import pandas as pd
import numpy as np
from konlpy.tag import Okt
from gensim.models import Word2Vec, KeyedVectors
import warnings
warnings.filterwarnings('ignore')
#logging은 선택사항: Word2Vec 모델 학습 시 진행상황을 알 수 있음
import logging
logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', level=logging.INFO)
okt = Okt()
먼저 클러스터링에 필요한 여러 라이브러리를 불러온다.
#전처리가 완료된 데이터 불러오기
runner_data = pd.read_csv("runner_final.csv")
cafe_data = pd.read_csv("cafe_final.csv")
tweet_data = pd.read_csv("tweet_final.csv")
#위 3개의 데이터 중 게시글(본문) 데이터 하나로 합치기
total_data = pd.concat([runner_data['body'], cafe_data['body'], tweet_data['tweet']])
total_data = pd.DataFrame(total_data)
total_data.reset_index(inplace=True) #인덱스가 섞여 있어 리셋 필요
total_data.drop("index", axis=1, inplace=True) #인덱스를 리셋했을 때 자동으로 생기는 'index' 열 제거
total_data.columns=['text'] #열 이름을 'text로 변경
위 3개의 게시글(본문) 데이터를 합쳐서, 다음의 248개의 데이터를 얻게 되었다.
저번과 같이 텍스트 데이터 한 행마다 불필요한 단어를 제거하고 필요한 단어만 남기는 작업을 진행할 때, 불용어를 지정해주어야 한다. 따라서 계속해서 사용하는 한국어 불용어 사전 텍스트 파일을 이용했으며, 임의의 단어들을 추가했다.
#한국어 불용어 사전 텍스트 파일 불러오기
stopwords= []
with open("stopwords_korean.txt", encoding='utf-8') as f:
for item in f.readlines():
stopwords.append(item[:-1])
#임의의 단어 추가
stopwords.extend(['테일즈런너', '테런', '언더월드', '이벤트', '하다'])
이제 위에서 만든 불용어사전과, Okt 분석기를 사용해 각 행마다 텍스트 데이터를 토큰화해야한다. 다음의 코드를 사용해 각 데이터를 토큰화했다.
token_sentence = [] #토큰화된 데이터를 담는 리스트
for line in total_data['text']:
result = okt.pos(str(line), norm=True, stem=True) #행 별 형태소 분석
token = []
for word in result:
if (len(word[0]) > 1) and ((word[1] == "Noun") or (word[1] == "Adjective") or (word[1] == "Verb")) and (word[0] not in stopwords):
token.append(word[0]) #단어가 두 글자 이상이며, 명사/형용사/동사 중 하나에 속하고, 불용어 사전에 포함하지 않는 경우 token 리스트에 넣는다
token_sentence.append(token) #token 리스트를 token_sentence 리스트에 넣어 이중 리스트 완성
여기서 매 행마다 리셋되는 token 리스트는 한 행을 토큰화한 데이터를 담는 리스트이며, token_sentence는 모든 행의 토큰화된 데이터를 담는 이중리스트다.
3) Word2Vec 모델 학습
클러스터링을 할 때, 단어를 벡터화해야 한다. 이때 Word2Vec 모델을 이용하기로 한다. 따라서 위에서 토큰화한 데이터를 이용해 Word2Vec 모델을 학습했다.
w2v_model = Word2Vec(token_sentence, size=100, window=5, min_count=3, sg=1)
위 한 문장으로 학습이 끝났다.
4) 단어 벡터화
이제 Word2Vec 모델을 이용해 단어들을 벡터화하기로 한다. 이를 워드 임베딩(Word Embedding)이라 부른다.
vectorized = [] #모든 텍스트 데이터를 벡터화한 리스트
for tokens in token_sentence: #token: 한 행(한 문장이라 이해하면 쉽다)
zero_vector = np.zeros(w2v_model.vector_size) #Word2Vec 모델의 벡터 크기(차원)에 맞는 영벡터 생성
vectors = [] #한 행(한 문장)의 여러 단어들에 대한 벡터를 담는 리스트
for token in tokens: #한 행(한 문장)의 단어들에 대해
if token in w2v_model.wv: #단어가 Word2Vec 모델에 포함되면
try:
vectors.append(w2v_model.wv[token]) #vectors에 넣는다
except KeyError:
continue #그렇지 않으면 pass
if vectors: #한 행(한 문장)에서 한 단어라도 Word2Vec 모델에 포함되어 vectors 리스트에 값이 들어있다면
vectors = np.asarray(vectors)
avg_vec = vectors.mean(axis=0) #vectors의 평균(한 문장 내 여러 단어들의 평균) 구한다
vectorized.append(avg_vec) #한 문장을 대표하는 값(여러 단어들의 평균)을 vectorized에 넣는다
else:
vectorized.append(zero_vector) #한 행(한 문장)에서 한 단어라도 Word2Vec 모델 단어에 포함되지 않으면 영벡터를 넣는다
token_sentence 리스트에는 여러 행이 담겨있고, 이 행(쉽게 말해 한 문장) 내에서도 여러 단어들이 존재한다. 먼저는 한 문장 내 여러 단어들을 벡터화한 후, 해당 단어들의 평균 벡터를 구해 이것을 해당 문장을 대표하는 벡터로 보는 것이다.
5) 군집화(클러스터링)
문장들을 벡터로 바꾸었기 때문에, 이제 클러스터링을 적용해보기로 한다.
from sklearn.metrics import silhouette_samples, silhouette_score
from sklearn.cluster import KMeans
from scipy.spatial.distance import cdist
다음이 클러스터링을 할 때 필요한 라이브러리다. K-Means 알고리즘을 이용해 시도해봤다.
먼저는 군집화를 하기 전, 몇 개의 군집으로 시도를 해야 최적의 군집화가 가능할지를 봐야 한다. 이는 'Elbow Method'를 통해 가능하다. 'Elbow Method'란, WCSS 즉 '군집의 중심과 군집 요소 간의 거리의 제곱의 합'의 값이 크게 낮아지는 k값을 선택하는 것이다. WCSS가 작다는 것은, 그만큼 각 군집들이 서로 옹기종기 모여있음을 의미하는 것이다. 그 뜻은 다시 말해 군집화가 잘 이루어졌다는 것을 뜻한다.
해당 방법을 사용할 때, 이 사이트의 코드를 참고했다: K- 평균 클러스터링 Python 예제 (ichi.pro)
wcss = []
for k in range(1, (len(vectorized)//10)+1):
kmeans = KMeans(n_clusters = k)
kmeans.fit(vectorized)
wcss.append(kmeans.inertia_)
#그래프 그리기
plt.figure(figsize=(20, 12))
plt.plot(range(1, len(wcss)+1), wcss, marker='o')
plt.title("Elbow Method", fontsize=20)
plt.ylabel("WCSS", fontsize=20)
plt.xticks(fontsize=20)
plt.yticks(fontsize=20)
plt.show()
각 군집 개수에 대한 WCSS를 구하고 싶다면, 'inertia_'라는 메소드를 사용하면 쉽게 구할 수 있다. 위 프로젝트에서는 전체 데이터의 10분의 1(즉 약 25개)까지의 결과를 구하고, 각 군집 개수에 대한 WCSS를 그래프로 그렸다.
여기서 WCSS가 가장 큰 폭으로 낮아지는 x값을 선택하면 된다. 조금 애매모호한 상황이나, 위 그래프를 통해 군집의 개수를 5개로 지정했다.
최적의 군집 개수를 정했으니 실제로 클러스터링을 진행하면 된다. 클러스터링 코드는 아래 사이트의 코드를 참고했다.
How to Cluster Documents Using Word2Vec and K-means (dylancastillo.co)
def kmeans_clusters(X, k):
kmeans = KMeans(n_clusters=k).fit(X)
print(f"For n_clusters = {k}")
sample_silhouette_values = silhouette_samples(X, kmeans.labels_)
print(f"Silhouette values:")
silhouette_values = []
for i in range(k):
cluster_silhouette_values = sample_silhouette_values[kmeans.labels_ == i]
silhouette_values.append((i, cluster_silhouette_values.shape[0], cluster_silhouette_values.mean(), cluster_silhouette_values.min(), cluster_silhouette_values.max()))
silhouette_values = sorted(silhouette_values, key=lambda tup: tup[2], reverse=True)
for s in silhouette_values:
print(f" Cluster {s[0]}: Size:{s[1]} | Avg:{s[2]:.2f} | Min:{s[3]:.2f} | Max: {s[4]:.2f}")
return kmeans, kmeans.labels_
clustering, cluster_labels = kmeans_clusters(X=vectorized, k=5)
위 코드는 각 클러스터 집단에 대해 사이즈(데이터 개수), 벡터 평균, 최소, 최대를 구해주는 함수다. 군집의 개수(k)를 5로 지정하고, 위 함수를 실행하면 다음과 같이 나온다.
비록 첫번째 군집에는 6개의 데이터밖에 없지만, 그래도 나름 잘 분류된 것으로 볼 수 있을 것 같다(?).
각 군집에 대해 세부적으로 살펴보기로 한다.
군집0에 포함되는 글들이다. 예상보다 글들에 대한 공통성이 없어 보였다.
군집1에 포함되는 글들이다. 토큰화했을 때 아무 결과도 안 나오거나, 부정적인 반응을 보이는 짧은 글들이 포함되어 있다.
군집2에 포함되는 글들이다. 나름 '언더월드' 이벤트 OST가 좋다는 글들이 아래에 보이지만, 전반적으로는 큰 공통점이 없어 보인다.
군집3에 포함되는 글들이다. 역시 전반적으로 큰 공통점이 없어 보인다.
마지막으로 군집4에 포함되는 글들이다. 너무 많은 데이터가 포함되는 탓에 공통점을 찾기 어렵다.
6) 군집화 결과
군집화(클러스터링)을 시도한 결과는 생각보다 무의미했다. 지난 편에 했던 감성분석보다, 여론을 제대로 분석할 수 없었다. 문서 유사도를 구하는 코드를 이용해 여론 분석을 시도하려 했으나, 데이터 정제가 제대로 안 되어 있거나 아직은 머신러닝 툴을 다루는 기술이 부족하여 결과가 제대로 안 나온 것으로 보인다.
-여담-
텍스트 데이터에 대해 클러스터링을 시도하는 것은 이번이 3번째이지만, 계속해서 시도를 해봐도 텍스트 데이터를 군집화한다는 것은 굉장히 어려운 작업인 것 같다. 역시 머신러닝에 대한 지식과, 자연어처리에 대한 지식을 더 쌓아서 조금 더 좋은 결과를 만들 수 있도록 노력해야겠다는 생각이 들었다.
'개인 프로젝트 > 게임 여론 분석' 카테고리의 다른 글
[파이썬] T 게임의 <언더월드> 이벤트 여론 분석 - 결론 편 (0) | 2021.09.14 |
---|---|
[파이썬] T 게임의 <언더월드> 이벤트 여론 분석 - 감성분석 편 (0) | 2021.09.03 |
[파이썬] T 게임의 <언더월드> 이벤트 여론 분석 - 데이터 시각화 편 (2) | 2021.08.29 |
[파이썬] T 게임의 <언더월드> 이벤트 여론 분석 - 데이터 전처리 편 (0) | 2021.08.28 |
[파이썬] T 게임의 <언더월드> 이벤트 여론 분석 - 데이터 수집 편 (풀버전) (0) | 2021.07.23 |