[머신러닝 기초] 비지도학습(Unsupervised-learning) - 군집화(Clustering)
ㅁ 들어가기
ㅁ 군집화 알고리즘 종류
ㅁ 군집 평가
ㅁ 군집화 알고리즘 - Kmeans
ㅁ 군집화 알고리즘 - DBSCAN
ㅁ 들어가기
우리가 트럼프 카드를 군집을 만들 때 군집의 수를 과연 둘로 하는 것이 좋을까, 넷으로 하는 게 좋을까?
색깔로 하면 두 개가 될 것이고, 우리가 모양으로 하면 네 개로 나눌 수 있는데, 어떤 것이 좋다는 정답이 존재하는 것은 아니다!
군집화에 가장 직관적인 예시는 MBTI라고 볼 수 있다.
다수개의 질문을 통해 인간의 성격을 16가지로 군집을 지어 놓은 것이다.
각 군집마다 네이밍을 하고(ex. 세상의 소금형) 그 특징을 설명하고 있다.
우리가 MBTI를 하면서 본인의성격유형을 파악하고, 공감을 하고 재밌어 한다.
하지만 여기서 끝나지 않는다. 이성에게 호감을 갖는 방법, 상사와 즐겁게 회사생활을 할 수 있는 방법을 알아내고, 활용한다.
이와 같이 군집화는 특정 목적을 위해 군집의 유형을 나누고 -> 정의하고 -> 특징을 요약하여 -> 활용하게 된다.
어찌보면 군집화의 알고리즘을 배우는것 보다 "군집의 유형을 나누고 -> 정의하고 -> 특징을 요약하여 -> 활용"을 고민 하는것이 군집화의 핵심이라고 생각한다. (데이터사이언티스트의 역량이 중요한 분석이다)
성격유형은 MBTI만 있진 않다. 다른 여러가지 성격유형검사가 있으며, 군집이 있다. MBTI가 절대적으로 맞다/틀리다라고 이야기 할 수 없듯이 군집화 결과도 절대적으로 맞다/틀리다를 규정지을 수 없다. 즉, 절대적인 기준이 존재하지 않는다.
즉, 어떤 군집화가 가장 좋은 군집화인가? 라고 물어본다면,
군집을 만든 후 '인사이트를 얻고, 잘 활용할 수 있는' 이라고 말하고 싶다.
군집화는 비지도학습으로 다양한 산업군에 다양한 목적으로 사용한다.
ㅁ 군집화 알고리즘 종류
군집화 알고리즘은 다양하며, 데이터의 특성과 목적에 맞춰 적절한 알고리즘을 선택해야 한다.
위의 그래프로 알 수 있듯이 데이터 분포에 따라 군집화가 잘 되는 알고리즘과 그렇지 않은 알고리즘이 있음을 알 수 있다.
이번 장에서는 다양한 군집화 알고리즘 중에서 Kmeans 와 DBSCAN에 대해서 알아보고자 한다.
ㅁ 군집 평가
군집화는 정답이 없는 unsupervised-learning이다. 하여, 적절한 군집임을 평가 기준을 세우는 일이 어렵다.
군집이 얼마나 잘 됐느냐를 평가하는 척도는 데이터 집합을 클러스터링한 결과 그 자체를 놓고 평가하는 방식이다. 이러한 방식에서는 클러스터 내 높은 유사도 (high intra-cluster similarity) 를 가지고, 클러스터 간 낮은 유사도 (low inter-cluster similarity) 를 가진 결과물에 높은 점수를 준다. 오로지 데이터를 클러스터링한 결과물만을 보고 판단하기 때문에, 평가 점수가 높다고 해서 실제 참값 (ground truth) 에 가깝다는 것을 반드시 보장하지는 않는다는 단점이 있다.
여러 평가 지표 중 실루엣(silhouette) 지표를 살펴보겠다.
1) 실루엣 분석
- '개별데이터'가 가지는 군집화 지표
- 개별데이터가 가지는 실루엣 계수는 해당 데이터가 같은 군집 내의 데이터와 얼마나 가깝게 군집화돼 있고, 다른 군집에 있는 데이터와는 얼마나 멀리 분리돼 있는지 나타내는 지표이다.
- 실루엣계수는 -1~1사이의 값을 가지며, 1로 가까워질수록 근처의 군집과 멀리 떨어져 있음 (군집화가 잘됨)을 의미한다. -1은 아에 다른 군집에 데이터포인트가 할당 되었음을 의미한다.
- '일반적으로' 이 값이 높을 수록 군집화가 어느정도 잘 됐다고 판단할 수 있다. 무조건 이 값이 높다고 해서 군집화가 잘 됐다고 판단할 수는 없다.
좋은 군집화가 되려면 다음 기준 조건을 만족해야 한다.
1. 전체 실루엣계수의 평균값이 0~1사이의 값을 가지며, 1에 가까울수록 좋음
2. 개별 군집의 평균값의 편차가 크지 않아야 함. 즉, 개별 군집의 실루엣 계수 평균값이 전체 실루엣 계수의 평균값에 크게 벗어나지 않는 것이 중요함
- X축: 실루엣 계수 값
Y축: 개별 군집과 이에 속하는 데이터(높이로 추측) - 점선: 전체 실루엣 계수 평균
군집별 평균 실루엣 계수의 시각화를 수행하였다. 몇개의 군집이 적절할까?
1) 군집 계수 = 2 (평균 실루엣 계수: 0.704)
Cluster를 2개로 나눈 첫번째는 두 군집에 할당된 데이터수가 차이가 많다. 그러나, 개별군집의 평균값이 0보다 크고, 편차가 크지 않다.
2) 군집 계수 = 3 (평균 실루엣 계수: 0.588)
Cluster를 3개로 나눈 두번째를 보면, 2번 군집의 실루엣 계수가 마이너스(-)가 되었다. 2번 군집의 실루엣계수와 전체군집의 평균실루엣계수의 편차가 있음을 알 수 있다.
3) 군집 계수= 4 (평균 실루엣 계수: 0.65)
Cluster 4가 최적으로 보인다. 각 군집별 데이터 분포도 비슷하며, 실루엣계수 편차도 크지 않다.
하지만, 명심해야 할 것은 평균 실루엣 계수값이 높다고 해서 반드시 최적의 군집 개수로 군집화가 되었다고 볼 수 없다. 각 군집에 대한 특징을 파악하고, 해석하고, 이해가 되는 군집이 최고의 군집이다.
Sklearn API
이를 위해 sklearn에서는 API를 지원한다.
목적 | import | API |
평균실루엣계수 |
from sklearn.metrics import silhouette_score | silhouette_score() |
코드에서 보면,
from sklearn.preprocessing import scale
from sklearn.datasets import load_iris
from sklearn.cluster import KMeans
# 실루엣 분석 metric 값을 구하기 위한 API 추가
from sklearn.metrics import silhouette_samples, silhouette_score
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
%matplotlib inline
iris = load_iris()
feature_names = ['sepal_length','sepal_width','petal_length','petal_width']
irisDF = pd.DataFrame(data=iris.data, columns=feature_names)
kmeans = KMeans(n_clusters=3, init='k-means++', max_iter=300,random_state=0).fit(irisDF)
irisDF['cluster'] = kmeans.labels_
# iris 의 모든 개별 데이터에 실루엣 계수값을 구함.
score_samples = silhouette_samples(iris.data, irisDF['cluster'])
print('silhouette_samples( ) return 값의 shape' , score_samples.shape)
# irisDF에 실루엣 계수 컬럼 추가
irisDF['silhouette_coeff'] = score_samples
# 모든 데이터의 평균 실루엣 계수값을 구함.
average_score = silhouette_score(iris.data, irisDF['cluster'])
print('붓꽃 데이터셋 Silhouette Analysis Score:{0:.3f}'.format(average_score))
실루엣 계수 시각화
- 실루엣 계수를 시각화하는 함수이다.
### 여러개의 클러스터링 갯수를 List로 입력 받아 각각의 실루엣 계수를 면적으로 시각화한 함수 작성
def visualize_silhouette(cluster_lists, X_features):
from sklearn.datasets import make_blobs
from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_samples, silhouette_score
import matplotlib.pyplot as plt
import matplotlib.cm as cm
import math
# 입력값으로 클러스터링 갯수들을 리스트로 받아서, 각 갯수별로 클러스터링을 적용하고 실루엣 개수를 구함
n_cols = len(cluster_lists)
# plt.subplots()으로 리스트에 기재된 클러스터링 수만큼의 sub figures를 가지는 axs 생성
fig, axs = plt.subplots(figsize=(4*n_cols, 4), nrows=1, ncols=n_cols)
# 리스트에 기재된 클러스터링 갯수들을 차례로 iteration 수행하면서 실루엣 개수 시각화
for ind, n_cluster in enumerate(cluster_lists):
# KMeans 클러스터링 수행하고, 실루엣 스코어와 개별 데이터의 실루엣 값 계산.
clusterer = KMeans(n_clusters = n_cluster, max_iter=500, random_state=0)
cluster_labels = clusterer.fit_predict(X_features)
sil_avg = silhouette_score(X_features, cluster_labels)
sil_values = silhouette_samples(X_features, cluster_labels)
y_lower = 10
axs[ind].set_title('Number of Cluster : '+ str(n_cluster)+'\n' \
'Silhouette Score :' + str(round(sil_avg,3)) )
axs[ind].set_xlabel("The silhouette coefficient values")
axs[ind].set_ylabel("Cluster label")
axs[ind].set_xlim([-0.1, 1])
axs[ind].set_ylim([0, len(X_features) + (n_cluster + 1) * 10])
axs[ind].set_yticks([]) # Clear the yaxis labels / ticks
axs[ind].set_xticks([0, 0.2, 0.4, 0.6, 0.8, 1])
# 클러스터링 갯수별로 fill_betweenx( )형태의 막대 그래프 표현.
for i in range(n_cluster):
ith_cluster_sil_values = sil_values[cluster_labels==i]
ith_cluster_sil_values.sort()
size_cluster_i = ith_cluster_sil_values.shape[0]
y_upper = y_lower + size_cluster_i
color = cm.nipy_spectral(float(i) / n_cluster)
axs[ind].fill_betweenx(np.arange(y_lower, y_upper), 0, ith_cluster_sil_values, \
facecolor=color, edgecolor=color, alpha=0.7)
axs[ind].text(-0.05, y_lower + 0.5 * size_cluster_i, str(i))
y_lower = y_upper + 10
axs[ind].axvline(x=sil_avg, color="red", linestyle="--")
from sklearn.datasets import load_iris
iris=load_iris()
visualize_silhouette([ 2, 3, 4,5 ], iris.data)
ㅁ 군집화 알고리즘 - Kmeans
알고리즘 특징
가장 일반적으로 사용하는 알고리즘이다.
몇개로 군집화 할지(K)를 정한다.
알고리즘 매커니즘
Step 1: 군집화의 기준이 되는 중심을 구성하려는 군집 개수만큼 정함
Step 2: 각 데이터는 가장 가까운 곳에 위치한 중심점에 속함
Step 3: 소속이 결정되면 군집 중심점을 소속된 데이터의 평균 중심으로 이동함
Step 4: 기존에 속한 중심점보다 더 가까운 중심점이 있다면 해당 중심점으로 다시 소속 변경
Step 5: 다시 중심을 소속된 데이터의 평균 중심으로 이동
Step 6: 위 프로세스를 반복, 데이터의 중심점 변경이 없으면 반복 중단 및 군집화 종료
장점
- 일반적인 군집화에서 가장 많이 활용
- 알고리즘이 쉽고 간결
단점
- 거리 기반 알고리즘이기 때문에 속성의 개수가 매우 많을 경우 군집화 정확도가 감소(PCA 차원 축소가 필요할 수 있음)
- 반복 수행 횟수가 많아지면 수행 시간이 증가
- 몇 개의 군집을 선택해야할지 가이드가 어려움
Sklearn API
이를 위해 sklearn에서는 API를 지원한다.
목적 | import | API | Parameters |
|
K-means |
from sklearn.cluster import KMeans | KMeans() |
input | n_clusters=k # 군집 개수=군집 중심점의 개수 |
output | kmeans.labels_ |
코드에서 보면,
from sklearn.datasets import load_iris
from sklearn.cluster import KMeans
iris = load_iris()
# 보다 편리한 데이터 Handling을 위해 DataFrame으로 변환
irisDF = pd.DataFrame(data=iris.data, columns=['sepal_length','sepal_width','petal_length','petal_width'])
irisDF.head(3)
# 개정판 소스 코드 수정(2019.12.24)
kmeans = KMeans(n_clusters=3, init='k-means++', max_iter=300,random_state=0)
kmeans.fit(irisDF)
1) 실습 (Kmeans)
iris데이터를 활용하여, kmeans알고리즘을 적용해보자. (Link)
2) 실습 (Kmeans)
fruits_300.npy 데이터(Link)를 활용하여, kmeans 알고리즘을 적용해보고, 그 결과를 확인해보자.
import numpy as np
### 데이터 로드
fruits = np.load('fruits_300.npy')
fruits_2d = fruits.reshape(-1, 100*100)
### 시각화
import matplotlib.pyplot as plt
%matplotlib inline
def draw_fruits(arr, ratio=1):
n = len(arr) # n은 샘플 개수입니다
# 한 줄에 10개씩 이미지를 그립니다. 샘플 개수를 10으로 나누어 전체 행 개수를 계산합니다.
rows = int(np.ceil(n/10))
# 행이 1개 이면 열 개수는 샘플 개수입니다. 그렇지 않으면 10개입니다.
cols = n if rows < 2 else 10
fig, axs = plt.subplots(rows, cols,
figsize=(cols*ratio, rows*ratio), squeeze=False)
for i in range(rows):
for j in range(cols):
if i*10 + j < n: # n 개까지만 그립니다.
axs[i, j].imshow(arr[i*10 + j], cmap='gray_r')
axs[i, j].axis('off')
plt.show()
### TODO - kmeans 군집화 수행
### 군집별 데이터 확인
#draw_fruits(fruits[km.labels_==0])
3) 실습 (Kmeans, RFM)
- RFM 분석 빈칸 버전 (Link)
- 분석데이터 (Link)
- Recency (R) : 가장 최근 상품 구입 일에서 오늘까지의 기간
- Frequencey(F) : 상품 구매 횟수
- Monetary value (M) : 총 구매 금액
ㅁ 군집화 알고리즘 - DBSCAN
알고리즘 특징
특정 공간 내에 데이터 밀도 차이에 기반한 알고리즘으로 군집화 수행
- 복잡한 기하학적 분포도를 가진 데이터 세트에 대해서 군집화를 잘 수행
알고리즘 매커니즘
1) 파라미터
- 입실론 주변 영역(epsilon): 개별 데이터를 중심으로 입실론 반경을 가지는 원형의 영역
- 최소 데이터 개수(min points): 개별 데이터의 입실론 주변 영역에 포함되는 타 데이터의 개수
2) 데이터 포인트
- 핵심 포인트(Core Point): 주변 영역 내에 최소 데이터 개수 이상의 타 데이터를 가지고 있을 경우
- 이웃 포인트(Neighbor Point): 주변 영역 내에 위치한 타 데이터
- 경계 포인트(Border Point): 주변 영역 내에 최소 데이터 개수 이상의 이웃 포인트를 가지고 있지 않지만, 핵심 포인트를 이웃 포인트로 가지고 있는 데이터(P3, P4, P6, P7, P8, P11, P9, P10)
- 잡음 포인트(Noise Point): 최소 데이터 개수 이상의 이웃 포인트를 가지고 있지 않으며, 핵심 포인트도 이웃 포인트로 가지고 있지 않는 데이터 (P5)
- 일반적으로 eps <= 1, eps가 증가하면 noise 줄어듦
- 특정 군집 개수로 군집을 강제하지 말고, 파라미터를 통해 최적의 군집을 찾는 것이 중요
Sklearn API
이를 위해 sklearn에서는 API를 지원한다.
목적 | import | API |
K-means |
from sklearn.cluster import DBSCAN | DBSCAN() |
코드에서 보면,
from sklearn.cluster import DBSCAN
dbscan = DBSCAN(eps=0.6, min_samples=8, metric='euclidean')
dbscan_labels = dbscan.fit_predict(iris.data)
irisDF['dbscan_cluster'] = dbscan_labels
1) 실습 (DBSCAN)
iris데이터를 활용하여, DBSSCAN알고리즘을 적용해보자. (Link)
ㅁ Reference
- [책] 파이썬 머신러닝 완벽가이드
- [블로그] 파이썬 머신러닝 완벽가이드 정리본 (Link)
- [책] 혼자 공부하는 머신러닝+딥러닝
- [소스코드 출처] : https://github.com/wikibook/pymldg-rev