군집화
K-means 알고리즘
K-means는 군집화에서 가장 일반적으로 사용되는 알고리즘이다. K-means는 군집 중심점이라는 특정한 임의의 지점을 선택해 해당 중심에 가장 가까운 포인트들을 선택하는 군집화 기법이다.
군집 중심점은 선택된 포인트의 평균 지점으로 이동하고 이동된 중심점에서 다시 가까운 포인트를 선택, 다시 중심점을 평균 지점으로 이동하는 프로세스를 반복적으로 수행한다.
모든 데이터 포인트에서 더 이상 중심점의 이동이 없을 경우 반복을 멈추고 해당 중심점에 속하는 데이터 포인트들을 군집화 하는 기법이다.
다음 그림에서 K-means가 어떻게 동작하는지 살펴보자.
- K-means 의 특징
- 일반적인 군집화에서 가장많이 활용 되는 알고리즘
- 알고리즘이 쉽고 간결.
- 거리 기반 알고리즘으로 속성의 개수가 매우 많을 경우 군집화의 정확도가 떨어짐 -> PCA 고려
- 반복을 수행하는데, 반복 횟수가 많을 경우 수행 시간이 매우 느려진다.
- 몇 개의 군집을 선택해야 할 지 스스로 판단해야함.
사이킷런 KMeans
KMeans 클래스는 다음과 같은 초기화 파라미터를 가지고 있다.
class sklearn.cluster.KMeans(n_clusters=8, init='k-means++', n_init=10, max_iter=300, tol=0.0001,
precompute_distances='deprecated', verbose=0, random_state=None, copy_x=True, n_jobs='deprecated', algorithm='auto
이 중 중요한 파라미터는 다음과 같다.
- n_clusters : 군집화 할 개수 (군집 중심점의 갯수)
- init : 초기에 군집 중심점의 좌표를 설정할 방식을 말하며 임의로 중심을 설정하지 않고,
- 일반적으로 k-means++ 방식을 최초로 설정한다.
- max_iter : 최대 반복 횟수, 이 횟수 이전에 모든 데이터의 중심점 이동이 없으면 종료한다.
KMeans는 사이킷런의 비지도 학습 클래스와 마찬가지로 fit(데이터 셋) 또는 fit_transform(데이터 셋) 메서드를 이용해 수행하면 된다.
이렇게 수행된 KMeans 객체는 군집화 수행이 완료돼 군집화와 관련된 주요 속성을 알 수 있다. 다음은 이 주요 속성 정보이다.
- lables_ : 각 데이터 포인트가 속한 군집 중심점 레이블
- cluster_centers_ : 각 군집 중심점 좌표(Shape는 [군집개수, 피처개수])
K-Means를 이용한 붓꽃(Iris) 데이터 셋 Clustering
붓꽃 데이터를 이용해 K-평균 군집화를 수행해보자. 붓꽃의 꽃받침과 꽃잎 길이와 너비에 따른 품종을 분류하는 데이터 셋이다.
꽃받침, 꽃잎의 길이에 따라 각 데이터의 군집화가 어떻게 결졍되는지 확인해보고, 이를 분류값과 비교해 보자.
from sklearn.preprocessing import scale
from sklearn.datasets import load_iris
from sklearn.cluster import KMeans
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
%matplotlib inline
iris = load_iris()
# 보다 편리한 데이터 Handling을 위해 DataFrame으로 변환
irisDF = pd.DataFrame(data=iris.data, columns=['sepal_length','sepal_width','petal_length','petal_width'])
irisDF.head(3)
sepal_length | sepal_width | petal_length | petal_width | |
---|---|---|---|---|
0 | 5.1 | 3.5 | 1.4 | 0.2 |
1 | 4.9 | 3.0 | 1.4 | 0.2 |
2 | 4.7 | 3.2 | 1.3 | 0.2 |
붓꽃 데이터 셋을 3개 그룹으로 군집화 해보자.
이를 위해 n_cluster = 3, init = k-means++, max_iter=300로 설정한 KMeans 객체를 만들고, 여기에 fit()을 수행한다.
kmeans = KMeans(n_clusters=3, init='k-means++', max_iter=300,random_state=0)
kmeans.fit(irisDF)
KMeans(n_clusters=3, random_state=0)
fit() 을 수행해 irisDF 데이터에 대한 군집화 수행 경과가 kmeans객체 변수로 반환됐다.
kmeans의 labels 속성값을 확인해 보면 irisDF의 각 데이터가 어떤 중심에 속하는지 알 수 있다.
print(kmeans.labels_)
[1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 0 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
2 2 2 0 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 0 2 0 0 0 0 2 0 0 0 0
0 0 2 2 0 0 0 0 2 0 2 0 2 0 0 2 2 0 0 0 0 0 2 0 0 0 0 2 0 0 0 2 0 0 0 2 0
0 2]
실제 붓꽃 품종 분류 값과 얼마나 차이가 나는지 군집화과 효과적으로 됐는지 확인해 보자.
DataFrame에 group by 연산을 실제 분류 값인 target과 군집화 분류 값인 cluster 레벨로 적용해 target과 cluster값 개수를 비교할 수 있다.
irisDF['target'] = iris.target
irisDF['cluster']=kmeans.labels_
iris_result = irisDF.groupby(['target','cluster'])['sepal_length'].count()
print(iris_result)
target cluster
0 1 50
1 0 2
2 48
2 0 36
2 14
Name: sepal_length, dtype: int64
분류 타깃별로 어떻게 군집이 형성 됐는지 알 수 있다.
Target 2 값 데이터를 제외하고는 군집이 잘 형성 됐음을 알 수 있다.
이번에는 붓꽃 데이터 셋의 군집화를 시각화 해보자.
2차원 평면에 개별 데이터의 군집화를 시각적으로 표현해야하는데, 붓꽃 데이터 셋 의 속성이 4개이므로 PCA를 이용해 차원축소한 뒤 X좌표, Y 좌표로 개별 데이터를 표현하자.
from sklearn.decomposition import PCA
pca = PCA(n_components=2)
pca_transformed = pca.fit_transform(iris.data)
irisDF['pca_x'] = pca_transformed[:,0]
irisDF['pca_y'] = pca_transformed[:,1]
irisDF.head(3)
sepal_length | sepal_width | petal_length | petal_width | pca_x | pca_y | |
---|---|---|---|---|---|---|
0 | 5.1 | 3.5 | 1.4 | 0.2 | -2.684126 | 0.319397 |
1 | 4.9 | 3.0 | 1.4 | 0.2 | -2.714142 | -0.177001 |
2 | 4.7 | 3.2 | 1.3 | 0.2 | -2.888991 | -0.144949 |
# cluster 값이 0, 1, 2 인 경우마다 별도의 Index로 추출
marker0_ind = irisDF[irisDF['cluster']==0].index
marker1_ind = irisDF[irisDF['cluster']==1].index
marker2_ind = irisDF[irisDF['cluster']==2].index
# cluster값 0, 1, 2에 해당하는 Index로 각 cluster 레벨의 pca_x, pca_y 값 추출. o, s, ^ 로 marker 표시
plt.scatter(x=irisDF.loc[marker0_ind,'pca_x'], y=irisDF.loc[marker0_ind,'pca_y'], marker='o')
plt.scatter(x=irisDF.loc[marker1_ind,'pca_x'], y=irisDF.loc[marker1_ind,'pca_y'], marker='s')
plt.scatter(x=irisDF.loc[marker2_ind,'pca_x'], y=irisDF.loc[marker2_ind,'pca_y'], marker='^')
plt.xlabel('PCA 1')
plt.ylabel('PCA 2')
plt.title('3 Clusters Visualization by 2 PCA Components')
plt.show()
그래프를 살펴보면 주황색 데이터는 확실히 군집화가 잘 이루어 진 것 을 알 수 있고,
반면 초록색과 파란색은 경계 부분이 모호한 것을 알 수 있다.
군집화 알고리즘 테스트용 데이터 생성
사이킷런은 다양한 유형의 군집화 알고리즘을 테스트해 보기 위한 간단한 데이터 생성기를 제공한다.
대표적인 군집화용 데이터 생성기로는 make_blobs() 와 make_classification() API가 있다.
두 API는 비슷하게 여러 개의 클래스에 해당하는 데이터 셋을 만드는데 ,하나의 클래스에 여러 개의 군집이 분포될 수 있게 데이터를 생성할 수 있다.
둘 중에 어떤 것을 사용하든 큰 차이는 없지만, make_blobs()는 개별 군집의 중심점과 표준 편차 제어 기능이 추가돼 있으며 make_classification() 은 노이즈를 포함한 데이터를 만드는 데 유용하게 사용할 수 있다.
둘 다 분류 용도로도 테스트 데이터 생성이 가능하다. 이 외에 make_circle(), make_moon() API는 중심 기반의 군집화로 해결하기 어려운 데이터 셋을 만드는데 사용된다.
make_blobs() 의 간략한 사용법을 알아보면써 군집화를 위한 테스트 데이터 셋을 만들어보자.
make_blobs()를 호출하면 피처데이터 셋과 타깃 데이터 셋이 튜플(Tuple)로 반환도니다. make_blobs()의 호출 파라미터는 다음과 같다.
- n_smaples : 생성할 총 데이터 개수. 디폴트는 100
- n_features : 데이터의 피처 개수. 시각화를 목표로 할 경우 2개로 설정해 보통 첫 번째 피처는 x좌표 두 번째 피처는 y좌표상에 표현한다.
- centers : int 값, 예를 들어 3으로 설정하면 군집의 개수를 나타냄. ndarray형태로 표현할 경우 개별 군집 중심점의 좌표를 의미한다.
- cluster_std : 생성될 군집 데이터의 표준 편차를 의미한다.만일 0.8을 지정 하면 군집 내에서 데이터가 표준편차 0.8을 가진 값으로 만들어 진다.
[0.8 1.2 0.6] 과 같은 형태로 표현되면 3개의 군집에서 각각 의 표준편차로 군집이 형성 된다.
X,y = make_blobs(n_samples = 200, n_features = 2, centers = 3, random_state=0)을 호출하면
총 200개의 레코드와 2개의 피처가 3개의 군집화 기반 분포도를 가진 피처 데이터 셋 X와 동시에 3개의 군집화 값을 가진 타깃 데이터 셋 y가 반환된다.
Clustering 알고리즘 테스트를 위한 데이터 생성
import numpy as np
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans
from sklearn.datasets import make_blobs
%matplotlib inline
X, y = make_blobs(n_samples=200, n_features=2, centers=3, cluster_std=0.8, random_state=0)
print(X.shape, y.shape)
# y target 값의 분포를 확인
unique, counts = np.unique(y, return_counts=True)
print(unique,counts)
(200, 2) (200,)
[0 1 2] [67 67 66]
데이터 가공을 위해 위 데이터셋을 DataFrame로 변경
import pandas as pd
clusterDF = pd.DataFrame(data=X, columns=['ftr1', 'ftr2'])
clusterDF['target'] = y
clusterDF.head(3)
ftr1 | ftr2 | target | |
---|---|---|---|
0 | -1.692427 | 3.622025 | 2 |
1 | 0.697940 | 4.428867 | 0 |
2 | 1.100228 | 4.606317 | 0 |
타깃값 0, 1, 2 에 따라 마커를 다르게 해서 산점도를 그려보면 다음과 같이 3개의 구분될 수 있는 군집 영역으로 피처 데이터 셋이 만들어 졌음을 알 수 있다.
target_list = np.unique(y)
# 각 target별 scatter plot 의 marker 값들.
markers=['o', 's', '^', 'P','D','H','x']
# 3개의 cluster 영역으로 구분한 데이터 셋을 생성했으므로 target_list는 [0,1,2]
# target==0, target==1, target==2 로 scatter plot을 marker별로 생성.
for target in target_list:
target_cluster = clusterDF[clusterDF['target']==target]
plt.scatter(x=target_cluster['ftr1'], y=target_cluster['ftr2'], edgecolor='k', marker=markers[target] )
plt.show()
이번에는 이렇게 만들어진 데이터 셋에 KMeans 군집화를 수행한 뒤에 군집별로 시각화 해보자.
# KMeans 객체를 이용하여 X 데이터를 K-Means 클러스터링 수행
kmeans = KMeans(n_clusters=3, init='k-means++', max_iter=200, random_state=0)
cluster_labels = kmeans.fit_predict(X)
clusterDF['kmeans_label'] = cluster_labels
#cluster_centers_ 는 개별 클러스터의 중심 위치 좌표 시각화를 위해 추출
centers = kmeans.cluster_centers_
unique_labels = np.unique(cluster_labels)
markers=['o', 's', '^', 'P','D','H','x']
# 군집된 label 유형별로 iteration 하면서 marker 별로 scatter plot 수행.
for label in unique_labels:
label_cluster = clusterDF[clusterDF['kmeans_label']==label]
center_x_y = centers[label]
plt.scatter(x=label_cluster['ftr1'], y=label_cluster['ftr2'], edgecolor='k',
marker=markers[label] )
# 군집별 중심 위치 좌표 시각화
plt.scatter(x=center_x_y[0], y=center_x_y[1], s=200, color='white',
alpha=0.9, edgecolor='k', marker=markers[label])
plt.scatter(x=center_x_y[0], y=center_x_y[1], s=70, color='k', edgecolor='k',
marker='$%d$' % label)
plt.show()
ake_blobs() 타깃과 kmeans_label은 군집 번호를 의미하므로 서로 다른 값으로 매핑될 수 있다.
print(clusterDF.groupby('target')['kmeans_label'].value_counts())
target kmeans_label
0 0 66
2 1
1 1 67
2 2 65
1 1
Name: kmeans_label, dtype: int64
Target 0 이 cluster label 0 으로, target 1이 label 2로 , target 2 가 label 1로 거의 대부분 잘 매핑됐다.
'머신러닝' 카테고리의 다른 글
평균이동(Mean Shift) (0) | 2020.08.24 |
---|---|
군집 평가 - 실루엣 분석 (2) | 2020.08.24 |
NMF(Non - Negative Matrix Factorization) (0) | 2020.08.23 |
Truncated SVD (0) | 2020.08.23 |
SVD (0) | 2020.08.23 |