본문 바로가기
Computer/ML·DL·NLP

[이수안컴퓨터연구소] 군집 분석 Cluster Analysis

by injeolmialmond 2021. 8. 4.

 

https://www.youtube.com/watch?v=YJSHBQj8zbU&list=PL7ZVZgsnLwEEoHQAElEPg7l7T6nt25I3N&index=3 

군집 분석(Cluster Analysis)

  • 군집 분석은 데이터의 특성에 따라 유사한 것끼리 묶음
  • 유사성을 기반으로 군집을 분류하고, 군집에 따라 유형별 특징을 분석하는 기법
  • 텍스트에 대한 군집 분석에서는 군집으로 묶여진 텍스트들끼리는 최대한 유사하고, 다른 군집으로 묶여진 텍스트들과는 최대한 유사하지 않도록 분류

 

텍스트 유사도

  • 텍스트 쌍에 대한 자카드 유사도와 코사인 유사도 계산
  • 자카드 유사도(Jaccard Similarity): 두 텍스트 문서 사이에 공통된 용어의 수와 해당 텍스트에 존재하는 총 고유 용어 수의 비율을 사용
  • 코사인 유사도(Cosine Similarity): 백터 표현 사이의 각도에 대한 코사인 값을 사용. BoW와 TF-IDF 행렬은 텍스트에 대한 백터 표현으로 활용 가능
import nltk
from nltk.metrics.distance import jaro_similarity
nltk.download('punkt')
nltk.download('wordnet')

from nltk import word_tokenize
from nltk.stem import WordNetLemmatizer
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity


def jaccard_similarity(d1, d2):
    lemmatizer = WordNetLemmatizer() 
    
    # 단어 뽑아서 lemmatize, list로
    words1 = [lemmatizer.lemmatize(word.lower()) for word in word_tokenize(d1)]
    words2 = [lemmatizer.lemmatize(word.lower()) for word in word_tokenize(d2)]
    inter = len(set(words1).intersection(set(words2))) # 두 단어 리스트 간 교집합 구하기
    union = len(set(words1).union(set(words2))) # 합집합 구하기

    return inter/union # 자카드 유사도 반환

# 예제

d1 = "Think like a man of action and act like man of thought."
d2 = "Try not to become a man of success but rather try to become of value."
d3 = "Give me liberty, or give me death."

print(jaccard_similarity(d1, d2)) # 0.2222222222222222
print(jaccard_similarity(d1, d3)) # 0.0625
print(jaccard_similarity(d2, d3)) # 0.05555555555555555


# 코사인유사도의 경우 벡터로 계산하기 때문에
# tf-idf vectorizer를 사용한 뒤 코사인유사도 계산해야 함.

tiv = TfidfVectorizer()
corpus = [d1, d2, d3]

tfidf = tiv.fit_transform(corpus).todense()

print(cosine_similarity(tfidf[0], tfidf[1])) # [[0.21078354]]
print(cosine_similarity(tfidf[0], tfidf[2])) # [[0.]]
print(cosine_similarity(tfidf[1], tfidf[2])) # [[0.]]

 

한국어 형태소 분석기 사용 : konlpy, mecab

-> 이건 지난 영상에서 이미 설치했으니 패스. 윈도우에서 mecab은 eunjeon으로 설치하면 됩니다!

 

 

데이터 전처리

import urllib.request
raw = urllib.request.urlopen("https://raw.githubusercontent.com/e9t/nsmc/master/ratings.txt").readlines()
print(raw[:5])

raw = [x.decode() for x in raw[1:10000]]

reviews = []
for i in raw:
    reviews.append(i.split("\t")[1])

print(reviews[:5])
#['어릴때보고 지금다시봐도 재밌어요ㅋㅋ', '디자인을 배우는 학생으로, 외국디자이너와 그들이 일군 전통을 통해 발
#전해가는 문화산업이 부러웠는데. 사실 우리나라에서도 그 어려운시절에 끝까지 열정을 지킨 노라노 같은 전통이있어
# 저와 같은 사람들이 꿈을 꾸고 이뤄나갈 수 있다는 것에 감사합니다.', '폴리스스토리 시리즈는 1부터 뉴까지 버릴 
#께 하나도 없음.. 최고.', '와.. 연기가 진짜 개쩔구나.. 지루할거라고 생각했는데 몰입해서 봤다.. 그래 이런게 진 
#짜 영화지', '안개 자욱한 밤하늘에 떠 있는 초승달 같은 영화.']



# 형태소 분석
from eunjeon import Mecab

tagger = Mecab()
reviews = [tagger.morphs(x) for x in reviews]

print(reviews[:5][0]) # ['어릴', '때', '보', '고', '지금', '다시', '봐도', '재밌', '어요', 'ㅋㅋ']

 

Word2Vec

from gensim.models import Word2Vec
from sklearn.manifold import TSNE
from matplotlib import font_manager as fm
from matplotlib import rc

word2vec = Word2Vec(reviews, min_count=5)
word2vec

word2vec.wv.most_similar("영화") # <gensim.models.word2vec.Word2Vec object at 0x00000266B7AF0A90>
# [('듯', 0.9414538145065308), ('작품', 0.9384720921516418), ('마음', 0.9210805892944336), ('이야기', 0.9041269421577454), ('잔잔', 0.8972564339637756), ('모습', 0.8951208591461182), ('느낌', 0.8920300006866455), ('가슴', 0.8907313346862793), ('며', 0.8872851133346558), ('따뜻', 0.8859601020812988)]

t-sne를 이용한 단어 벡터 시각화

tsne = TSNE(n_components=2) # 2차원으로 만들겠다
tsne

vocab = word2vec.wv.key_to_index
similarity = word2vec.wv[vocab]
similarity

# 보기 편하게 데이터프레임으로 변환
import pandas as pd
transform_similarity = tsne.fit_transform(similarity) # similarity를 2차원으로
df = pd.DataFrame(transform_similarity, index=vocab, columns=["x", "y"])
df[0:10]

pip install seaborn

import seaborn as sns
import matplotlib.pyplot as plt

plt.style.use("seaborn-white")

sns.lmplot("x", "y", data=df, fit_reg=False, size=8)
plt.show()

Scikit-learn, Scipy를 이용한 계층적 군집화

CodeText

  • 계층적 군집화란 개별 개체들을 유사한 개체나 그룹과 통합해 군집화를 수행하는 알고리즘
  • 비계층적 군집화와는 달리 군집 수를 지정하지 않아도 군집화를 할 수 있는 것이 장점
  • 계층적 군집화는 모든 개체간 거리나 유사도가 미리 계산되어 있어야만 하며, 계산복잡도도 비계층적 군집화보다 큼

 

Scikit-learn

  • 비계층적 군집화의 일종인 agglomerativeClustering(병합 군집)을 이용, 계층적 군집화 실습
  • 병합 군집은 각 개체들을 클러스터로 간주, 종료 조건을 만족할 때 까지 가장 비슷한 두 클러스터들을 합치며 진행
  • 병합 군집의 종료 조건에는 3가지를 지정 가능
    1. ward - 모든 클러스터 내의 분산을 가장 적게 증가시키는 두 클러스터를 합침(기본값)
    2. average - 클러스터간 평균 거리가 가장 짧은 두 클러스터를 합침
    3. complete - 클러스터간 최대 거리가 가장 짧은 두 클러스터를 합침
  • scikit-learn 사용 예제에서는 종료 조건 간의 차이를 비교
from sklearn.cluster import AgglomerativeClustering


# ward 방법
ward = AgglomerativeClustering(n_clusters=6, linkage='ward')
predict = ward.fit_predict(df)
predict

results = df
results['predict'] = predict
results[0:10] # 어떤 클러스터에 속하는지 보여줌

sns.lmplot("x", "y", data=results, fit_reg=False, size=8, hue="predict")
plt.show()

# avg 방법
avg = AgglomerativeClustering(n_clusters=6, linkage="average")
predict = avg.fit_predict(df)
predict

results = df
results['predict'] = predict
results[0:10] # 어떤 클러스터에 속하는지 보여줌

sns.lmplot("x", "y", data=results, fit_reg=False, size=8, hue="predict")
plt.show()

# complete 방법
compl = AgglomerativeClustering(n_clusters=6, linkage="complete")
predict = compl.fit_predict(df)
predict

results = df
results['predict'] = predict
results[0:10] # 어떤 클러스터에 속하는지 보여줌

sns.lmplot("x", "y", data=results, fit_reg=False, size=8, hue="predict")
plt.show()

Scipy

  • scipy를 이용한 거리 사용과 word2vec 사용을 비교
  • pdist를 이용한 각 단어간 유클리디안 거리 계산
from scipy.spatial.distance import pdist, squareform
from scipy.cluster.hierarchy import linkage, dendrogram

distmatrix = pdist(df, metric="euclidean")
row_dist = pd.DataFrame(squareform(distmatrix))
row_dist

 

  • 각 단어간 유클리디안 거리를 이용한 군집 분석 및 덴드로그램 시각화
row_clusters = linkage(distmatrix, method="complete")

plt.figure(figsize=(20,10))
dendrogram(row_clusters, leaf_rotation=50, leaf_font_size=7)
plt.show()

  • word2vec을 이용한 군집 분석 및 덴드로그램 시각화
mergings = linkage(df, method="complete")

plt.figure(figsize=(20,10))
dendrogram(mergings, leaf_rotation=50, leaf_font_size=7)
plt.show()

Scikit-learn을 이용한 비계층적 군집화

  • 비계층적 군집화는 나눌 클러스터 개수를 지정해 각 개체가 어느 클러스터에 속하는 지를 결정
  • 계층적 군집화보다 계산 복잡도가 작기 때문에 대량의 데이터에 유리하나, 클러스터 개수에 따라 군집화 성능이 크게 좌우되기 때문에 조정이 필요
  • 대표적인 비계층적 군집화 알고리즘인 kmeans를 사용해 실습

- 클러스터 개수 3개

from sklearn.cluster import KMeans

kmeans = KMeans(n_clusters=3)
predict = kmeans.fit_predict(df)
predict

results = df
results['predict'] = predict
results[0:10]

sns.lmplot("x", "y", data = results, fit_reg = False, size = 6, hue = "predict")
plt.show()

- 클러스터 개수 6개

kmeans = KMeans(n_clusters=6)
predict = kmeans.fit_predict(df)
predict

results = df
results['predict'] = predict
results[0:10]

sns.lmplot("x", "y", data = results, fit_reg = False, size = 6, hue = "predict")
plt.show()

댓글