#!/usr/bin/env python3


Face Datasets Analysis with DBSCAN


# load library

from sklearn.datasets import fetch_lfw_people

import numpy as np

import matplotlib

import matplotlib.pyplot as plt

from sklearn.decomposition import PCA, NMF

from sklearn.preprocessing import MinMaxScaler

from sklearn.cluster import DBSCAN


# matplotlib 설정 

matplotlib.rc('font', family='AppleGothic')

plt.rcParams['axes.unicode_minus'] = False

people = fetch_lfw_people(min_faces_per_person=20, resize=0.7, color=False# min_faces_per_person = 겹치지 않는 최소 얼굴 수, 

image_size = people.images[0].shape


# outlier 있는지 확인 

_, bins = np.histogram(people.target, bins=100)

plt.hist(people.target, bins=bins)

plt.xticks(range(len(people.target_names)), people.target_names, rotation=90, ha='center')

plt.hlines(50, xmin=0, xmax=bins[-1], linestyles='--')# 수평선

plt.show()

각 특성별 히스토그램 

# 특성이 50개

idx = np.zeros(people.target.shape, dtype=np.bool)

for tg in np.unique(people.target):

    idx[np.where(people.target == tg)[0][:50]] = 1


# 데이터 분할

x_people = people.data[idx]

y_people = people.target[idx]


# 전처리

scaler_mms = MinMaxScaler()

x_people_scaled = scaler_mms.fit_transform(x_people)

# pca는 whiten 옵션을 True를 주면 전처리효과가 발생


# 훈련데이터를 PCA주성분 100개로 변환

pca = PCA(n_components=100, random_state=0, whiten=True).fit(x_people)

x_pca = pca.transform(x_people)


# 훈련데이터를 NMF 성분 100개로 변환

nmf = NMF(n_components=100, random_state=0).fit(x_people_scaled)

x_nmf = nmf.transform(x_people_scaled)


# DBSCAN에서 알맞은 min_sample값과 eps구하기

for min_sample in [3, 5, 7, 9]:

    for eps in [0.2, 0.4, 0.9, 1, 3, 5, 7, 9, 11, 13, 15, 17]:

        dbscan = DBSCAN(n_jobs=-1, min_samples=min_sample, eps=eps)

        labels_pca = dbscan.fit_predict(x_pca) # pca를 이용한 성분을 이용한 dbscan을 예측

        labels_nmf = dbscan.fit_predict(x_nmf) # nmf를 이용한 성분을 이용한 dbscan으로 예측

        print('︎=== min_sample:{}, eps:{} ==='.format(min_sample, eps))

        print('')

        print('pca ==> cluster 수: {}'.format(len(np.unique(labels_pca))))

        print('pca ==> cluster 크기: {}\n'.format(np.bincount(labels_pca+1)))


        print('nmf ==> cluster 수: {}'.format(len(np.unique(labels_nmf))))

        print('nmf ==> cluster 크기: {}'.format(np.bincount(labels_nmf+1)))

        print('---------------------------------------')

알맞은 min_sample과 eps를 찾는 과정

### pca : min_sample=3, eps=7일 때 가장 많은 클러스터가 생김

### nmf : min_sample=3, eps=0.9일 때 가장 많은 클러스터가 생김


# 가장 많은 클러스터가 생긴 eps와 n_sample 츨력

dbscan = DBSCAN(min_samples=3, eps=7, n_jobs=-1) # n_jobs = 코어의 갯수

labels_pca = dbscan.fit_predict(x_pca)


dbscan = DBSCAN(min_samples=3, eps=0.9, n_jobs=-1)

labels_nmf = dbscan.fit_predict(x_nmf)


# pca 주성분, DBSCAN 을 이용한 시각화

for cluster in range(labels_pca.max() + 1):

    idx = labels_pca == cluster 

    n_images = np.sum(idx) # idx중 true인 것의 갯수

    fig, axes = plt.subplots(1,n_images, figsize=(n_images*1.5, 4), # figsize=(a,b) axb 그림사이즈

                            subplot_kw={'xticks':(), 'yticks':()})

    

    for image, label, ax in zip(x_people[idx], y_people[idx], axes.ravel()):

        ax.imshow(image.reshape(image_size))

        ax.set_title(people.target_names[label].split()[-1])


plt.gray()

plt.show()

0123456789101112

PCA 주성분을 이용한 DBSCAN 





#!/usr/bin/env python3


face dataset으로 Agglomerative Algorithm 비교

1. face data load

# library import

from sklearn.decomposition import PCA, NMF

from sklearn.preprocessing import StandardScaler, MinMaxScaler

from sklearn.datasets import fetch_lfw_people

from sklearn.model_selection import train_test_split

from sklearn.cluster import DBSCAN, KMeans, AgglomerativeClustering

import numpy as np

import matplotlib.pyplot as plt

import matplotlib


# matplotlib 설정

matplotlib.rc('font', family='AppleGothic'# 한글출력

plt.rcParams['axes.unicode_minus'] = False # 축 -


# dataset

people = fetch_lfw_people(min_faces_per_person=20, resize=0.7, color=False) 

people_image = people.images[0].shape 


# 빈도수 확인

counts = np.bincount(people.target)


for i, (count, name) in enumerate(zip(counts, people.target_names)):

    print('{0:12} {1:3}'.format(count, name), end='\t')

    if i%3 == 0:

        print()


# 특성의수를 50개까지

idx = np.zeros(people.target.shape, dtype=np.bool)

for target in np.unique(people.target):

    idx[np.where(people.target == target)[0][:50]] = 1


# 데이터 분할

x_people = people.data[idx]

y_people = people.target[idx]


# preprocessing

scaler = MinMaxScaler()

x_people_scaled = scaler.fit_transform(x_people) # 전처리 메소드로 원래데이터 적용




2 . PCA와 NMF를 이용하여 DBSCAN


# PCA 주성분 분석

pca = PCA(n_components=100, whiten=True, random_state=0) 

pca.fit_transform(x_people_scaled) # 모델 fitting

x_pca = pca.transform(x_people_scaled) # 적용한 모델을 원래 데이터에 적용


# NMF 분석

nmf = NMF(n_components=100, random_state=0)

nmf.fit_transform(x_people_scaled) # 모델 fitting

x_nmf = nmf.transform(x_people_scaled) # 적용한 모델을 원래 데이터에 적용


# DBSCAN Agglomerative Algorithm

dbscan = DBSCAN() 

labels_pca = dbscan.fit_predict(x_pca) # pca를 군집화

labels_nmf = dbscan.fit_predict(x_nmf) # nmf를 군집화


print('\n레이블 종류: \npca: {}\nnmf: {}'.format(np.unique(labels_pca), np.unique(labels_nmf))) # [-1], 

### PCA, NMF 모두 레이블이 -1뿐임 ==> DBSCAN에 의해 noise point로 처리

### solution:

### 1. eps값을 크게 ==> 데이터 point의 이웃의수 증가

### 2. min_samples 값을 작게 ==> cluster에 모을 point 수를 감소



3. 알맞은 DBSCAN값 찾기

### 알맞은 eps값 확인 (-1, 0)

for eps in [0.5, 0.7, 0.9, 1, 5, 9, 15, 17]: # eps 목록

    dbscan = DBSCAN(min_samples=3, eps=eps)

    labels_pca = dbscan.fit_predict(x_pca)

    labels_nmf = dbscan.fit_predict(x_nmf)

    print('pca ==> n_samples: {}, eps: {:.3f} - {}'.format(3, eps, np.unique(labels_pca)))

    print('nmf ==> n_samples: {}, eps: {:.3f} - {}\n'.format(3, eps, np.unique(labels_nmf)))


# DBSCAN -- pca

dbscan = DBSCAN(min_samples=3, eps=15)

labels_pca = dbscan.fit_predict(x_pca)


# DBSCAN -- NMF

dbscan = DBSCAN(min_samples=3, eps=1)

labels_nmf = dbscan.fit_predict(x_nmf)


print('레이블 종류: \npca: {}\nnmf: {}'.format(np.unique(labels_pca), np.unique(labels_nmf))) # (-1, 0)

pca_count = np.bincount(labels_pca + 1) # bincount는 음수는 표현할 수없으니 1을 더함

nmf_count = np.bincount(labels_nmf + 1)


print('클러스터별 포인트 수:\npca: {}\nnmf: {}'.format(pca_count, nmf_count))


### visualization

noise_pca = x_people[labels_pca == -1] # noise 값만 추출

noise_nmf = x_people[labels_nmf == -1]


fig, axes = plt.subplots(3, 9,

                        subplot_kw={'xticks':(), 'yticks':()},

                        gridspec_kw={'hspace':0.5})


for image, ax in zip(noise_pca, axes.ravel()):

    ax.imshow(image.reshape(people_image))

plt.gray()

plt.show()

face dataset에서 DBSCAN이 noise point로 레이블한 sample(PCA)


fig, axes = plt.subplots(3, 9,

                        subplot_kw={'xticks':(), 'yticks':()},

                        gridspec_kw={'hspace':0.5})


for image, ax in zip(noise_nmf, axes.ravel()):

    ax.imshow(image.reshape(people_image))

plt.gray()

plt.show()

face dataset에서 DBSCAN이 noise point로 레이블한 sample(NMF)


face image에서 무작위로 선택한 이미지와 비교해보면 대부분 얼굴이 일부 가려져 있거나 각도가 이상, 혹은 너무 가까이거나 너무 멀리 있는 경우임


#!/usr/bin/env python3


clustering valuation(군집 모델 평가하기)


agglomerative algorithm군집 알고리즘을 적용하는데 어려운 점은 

algorithm이 잘 작동하는지 평가하거나 여러 algorithm의 출력을 비교하기가 매우 어려움


1. target value로 clustering 평가하기


agglomerative algorithm의 결과를 실제 정답 cluster와 비교하여 평가할 수 있는 지표

1. ARIadjusted rand index : 0과 1사이의 값을 제공(그러나 음수가 될 수 있음)

2. NMInormalized mutual information를 가장 많이 사용


# library import

import matplotlib

import matplotlib.pyplot as plt

import numpy as np

import mglearn

from sklearn.datasets import make_moons

from sklearn.preprocessing import StandardScaler

from sklearn.cluster import KMeans, AgglomerativeClustering, DBSCAN

from sklearn.metrics.cluster import adjusted_rand_score


# matplotlib 설정

matplotlib.rc('font', family='AppleGothic'# 한글출력

plt.rcParams['axes.unicode_minus'] = False # 축 -표시


# dataset

x, y = make_moons(n_samples=200, noise=0.05, random_state=0)


# pre-processing

scaler = StandardScaler().fit(x)

x_scaled = scaler.transform(x)


# model list

algorithms = [KMeans(n_clusters=2),

              AgglomerativeClustering(n_clusters=2),

                      DBSCAN()]

kmean_pred = algorithms[0].fit_predict(x_scaled)

# random cluster

random_state = np.random.RandomState(seed=0)

random_clusters = random_state.randint(low=0, high=2, size=len(x))


### visualization

fig, axes = plt.subplots(2, 2,

                         subplot_kw={'xticks':(), 'yticks':()})



axes[0,0].scatter(x_scaled[:, 0], x_scaled[:, 1], c=random_clusters,

                cmap=mglearn.cm3, s=60, edgecolors='k')

axes[0,0].set_title('random assign - ARI: {:.3f}'.format(

    adjusted_rand_score(y, random_clusters))) # 실제, 모델로 구한 클러스터


axes[0,1].scatter(x_scaled[:, 0], x_scaled[:, 1], c=kmean_pred,

                  cmap=mglearn.cm3, s=60, edgecolors='k')

axes[0,1].set_title('{} - ARI: {:.3f}'.format(algorithms[0].__class__.__name__, #__class__.__name__ ==> 클래스에서 이름 속성

                                              adjusted_rand_score(y, kmean_pred)))


for ax, algorithm in zip(axes.ravel()[2:], algorithms[1:]):

    clusters = algorithm.fit_predict(x_scaled)

    ax.scatter(x_scaled[:, 0], x_scaled[:, 1], c=clusters,

               cmap=mglearn.cm3, s=60, edgecolors='k')

    ax.set_title('{} - ARI: {:.3f}'.format(algorithm.__class__.__name__,

                                           adjusted_rand_score(y, clusters)))

plt.show()

two_moons 데이터셋에 적용한 random assign, KMeans, AgglomerativeClustering, DBSCAN의 ARI Score



2. no target value로 clustering 평가하기

ARI 같은 방법은 반드시 target value가 있어야하지만 실제로는 결과와 비교할 target value가 없음

따라서 ARI나 NMI 같은 지표는 algorithm을 개발할 때 도움이 많이 됨


silhouette coefficient실루엣 계수: 실제로 잘 동작하지 않음, cluster의 밀집 정도를 계산, max = 1

모양이 복잡할때는 잘 들어맞지가 않음


# library import

from sklearn.metrics.cluster import silhouette_score


# datasets

x, y = make_moons(n_samples=300, noise=0.05, random_state=0)


# pre-processing

scaler = StandardScaler().fit(x)

x_scaled = scaler.transform(x)


# random cluster

random_state = np.random.RandomState(seed=0)

random_clusters = random_state.randint(0, 2, size=len(x)) # randint(low, high, size)


# K-Means 모델

kmeans = KMeans(n_clusters=2)

kmeans_pred = kmeans.fit_predict(x_scaled)


### visualization

fig, axes = plt.subplots(2, 2,

                         subplot_kw={'xticks':(), 'yticks':()})

ax = axes.ravel()


# random cluster 그리기

ax[0].scatter(x_scaled[:, 0], x_scaled[:,1], c=random_clusters,

            cmap=mglearn.cm2, edgecolors='k', s=60)

ax[0].set_title('random assign - silhouette score: {:.3f}'.format(silhouette_score(x_scaled, random_clusters))) # silhouette_score(훈련데이터, 예측한 클러스터)


# K-Means 그리기

ax[1].scatter(x_scaled[:, 0], x_scaled[:, 1], c=kmeans_pred,

           cmap=mglearn.cm2, edgecolors='k', s=60)

ax[1].set_title('{} - silhouette score: {:.3f}'.format(kmeans.__class__.__name__,

                                                 silhouette_score(x_scaled, kmeans_pred)))


# AgglomerativeClustering과 DBSCAN 그리기

algorithms = [AgglomerativeClustering(n_clusters=2), DBSCAN()]

for ax, model in zip(ax[2:], algorithms):

    cluster = model.fit_predict(x_scaled)

    ax.scatter(x_scaled[:, 0], x_scaled[:, 1], c=cluster,

               cmap=mglearn.cm2, edgecolors='k', s=60)

    ax.set_title('{} - silhouette score: {:.3f}'.format(model.__class__.__name__,

                                                  silhouette_score(x_scaled, cluster)))

plt.show()

two_moons 데이터셋에 적용한 random_assign, KMeans, AgglomerativeClustering, DBSCAN의 silhouette score


DBSCAN의 결과가 더 보기 좋지만 K-mean의 silhouette score가 더 높음

Agglomerative Model이 매우 안정적이거나 silhouette score가 더 높더라도 cluster에 어떤 유의미한 것이 있는지 알 수가 없음


#!/usr/bin/env python3


DBSCAN



0. 살펴보기

DBSCANdensity-based spatial clustering of application with noise은 클러스터의 갯수를 미리 지정하지 않는 군집 알고리즘

DBSCAN은 병합 군집이나 k-평균보다는 다소 느리지만 비교적 큰 데이터셋에도 적용


데이터의 밀집지역이 한 클러스터를 구성하며 비교적 비어있는 지역을 경계로 다른 클러스터와 구분함


DBSCAN은 특성 공간에서 가까이 있는 데이터가 많아 붐비는 지역의 포인트를 찾음

이런 지역을 밀집 지역dense region이라 함

밀집 지역에 있는 포인트를 핵심 포인트core point라고함


핵심 포인트: min_samples, epsepsilon

한 데이터 포인트에서 eps 거리 안에 데이터가 min_samples 갯수만큼 들어 있으면 이 데이터 포인트를 핵심 포인트로 분류

eps(거리)보다 가까운 핵심 샘플은 동일한 클러스터로 분류


알고리즘

1. 무작위로 데이터 포인트를 선택


2. 그 포인트에서 eps 거리안의 모든 포인트를 찾음

2-1 eps 거리 안에 있는 데이터 포인트 수가 min_samples보다 적다면 어떤 클래스에도 속하지 않는 잡음noise로 레이블

2-2 eps 거리 안에 있는 데이터 포인트 수가 min_samples보다 많으면 핵심 포인트로 레이블하고 새로운 클러스터 레이블할당


3. 2-2의 핵심 포인트의 eps거리안의 모든 이웃을 살핌

3-1 만약 어떤 클러스터에도 아직 할당되지 않았다면 바로 전에 만든 클러스터 레이블을 할당

3-2 만약 핵심 포인트면 그 포인트의 이웃을 차례로 확인


4. eps 거리안에 더이상 핵심 포인트가 없을 때까지 진행


핵심포인트, 경계포인트(핵심 포인트에서 eps거리 안에 있는 포인트), 잡음 포인트가 핵심


DBSCAN을 한 데이터셋에 여러 번 실행하면 핵심 포인트의 군집은 항상 같고, 매번 같은 포인트를 잡음으로 레이블함

그러나 경계포인트는 한 개 이상의 핵심 포인트 샘플의 이웃일 수 있음


# library import

import mglearn

import matplotlib.pyplot as plt

from matplotlib import rc


# matplotlib 설정

rc('font', family='AppleGothic'

plt.rcParams['axes.unicode_minus'] = False


# 예제

mglearn.plots.plot_dbscan()

plt.show()


min_samples와 eps 매개변수를 바꿔가며 DBSCAN으로 계산한 클러스터


이 그래프에서 클러스터에 속한 포인트는 색을 칠하고 잡음 포인트는 하얀색으로 남겨둠

핵심포인트는 크게 표시하고 경계 포인트는 작게 나타냄

eps를 증가시키면(왼쪽 -> 오른쪽) 하나의 클러스터에 더 많은 포인트가 포함

이는 클러스터를 커지게 하지만 여러 클러스터를 하나로 합치게도 함

min_samples를 키우면(위 -> 아래) 핵심 포인트 수가 줄어들며 잡음 포인트가 늘어남




1. make_blobs(인위적 데이터)로 DBSCAN 적용

eps 매개변수가까운 포인트의 범위를 결정하기 때문에 매우 중요

eps를 매우 작게 하면 어떤 포인트도 핵심 포인트가 되지 못하고, 모든 포인트가 잡음 포인트가 될 수 있음

eps를 매우 크게 하면 모든 포인트가 단 하나의 클러스터에 속하게 됨


min_samples 설정은 덜 조밀한 지역에 있는 포인트들이 잡은 포인트가 될 것인지, 아니면 하나의 클러스터가 될 것인지를 결정

min_samples보다 작은 클러스터들은 잡음포인트가 되기 때문에 클러스터의 최소 크기를 결정


DBSCAN은 클러스터의 갯수를 지정할 필요가 없지만 eps의 값은 간접적으로 클러스터의 갯수 제어

적절한 eps 값을 찾기 위해 전처리preprocessing(StandardScaler, MinMaxScaler)로 조정하는 것이 좋음


### DBSCAN은 새로운 테스트 데이터에 대해 예측

### 할 수 없으므로(병합군집, 매니폴드학습과 동일) fit_predict를 사용

# library import

from sklearn.cluster import DBSCAN

from sklearn.datasets import make_blobs


# dataset

x, y = make_blobs(n_samples=12, random_state=0)


# 모델생성 및 클러스터 레이블

dbscan = DBSCAN()

clusters = dbscan.fit_predict(x)


# 클러스터 레이블 출력

print('clusters \n클러스터레이블: {}'.format(clusters))

### 모든 포인트에 잡은 포인트를 의미하는 '-1'이 할당

### eps와 min_samples 기본값이 작은 데이터셋에 적절하지 않음



2. 전처리 후 make_blobs(인위적 데이터)에 DBSCAN적용

# library import

from sklearn.datasets import make_moons

from sklearn.preprocessing import StandardScaler

from sklearn.preprocessing import MinMaxScaler

import numpy as np


# dataset

x, y = make_moons(n_samples=200, noise=0.05, random_state=0)


# MinMaxScaler 메소드로 전처리

scaler_MMS = MinMaxScaler().fit(x)

x_scaled_MMS = scaler_MMS.transform(x) # 전처리 메소드를 훈련데이터에 적용

dbscan = DBSCAN() # 모델생성

clusters_MMS = dbscan.fit_predict(x_scaled_MMS) # 모델 학습

print('np.unique(clusters_MMS)\n예측한 레이블: {}'.format(np.unique(clusters_MMS))) # [0]

### 예측한 레이블이 0으로 전부 하나의 클러스터로 표현

### MinMaxScaler전처리가 적합하지 않음


scaler_ss = StandardScaler().fit(x)

x_scaled_ss = scaler_ss.transform(x) 

dbscan = DBSCAN()

clusters_ss = dbscan.fit_predict(x_scaled_ss)

print('np.unique(clusters_ss)\n예측한 레이블:{}'.format(np.unique(clusters_ss))) # [0 ,1]

### 2차원 데이터셋을 0과 1로 구분했기 때문에 전처리가 잘되었음을 확인


# visualization

df = np.hstack([x_scaled_ss, clusters_ss.reshape(-1, 1)]) # x_scaled_ss 오른쪽에 1열 붙이기


df_ft0 = df[df[:,2]==0, :] # 클러스터 0 추출

df_ft1 = df[df[:,2]==1, :] # 클러스터 1 추출


# matplotlib로 그래프 그리기

plt.scatter(df_ft0[:, 0], df_ft0[:, 1], label='cluster 0', cmap='Pairs'# x, y, label, 색상

plt.scatter(df_ft1[:, 0], df_ft1[:, 1], label='cluster 1', cmap='Pairs')

plt.xlabel('feature 0')

plt.ylabel('feature 1')

plt.legend()

plt.show()

기본값 eps=0.5를 사용해 DBSCAN으로 찾은 클러스터



#!/usr/bin/env python3


병합 군집(agglomerative clustering)


0. 살펴보기


병합 군집agglomerative clustering 알고리즘은 

시작할 때 각 포인트를 하나의 클러스터로 지정하고, 그다음 종료 조건을 만족할 때까지 가장 비슷한 두 클러스터를 합침

종료조건 : 클러스터 갯수, 지정된 갯수의 클러스터가 남을 때까지 비슷한 클러스터를 합침


linkage: 옵션에서 가장 비슷한 클러스터를 측정하는 방법 지정, 이 측정은 항상 두 클러스터 사이에서 이뤄짐


ward : 기본값, 모든 클러스터 내의 분산을 가장 작게 증가시키는 두 클러스터를 합침, 크기가 비교적 비슷한 클러스터가 만들어짐


average : 클러스터 포인트 사이의 평균 거리가 가장 짤븐 두 클러스터를 합침


complete : complete연결(최대 연결)은 클러스터 포인트 사이의 최대 거리가 가장 짧은 두 클러스터를 합침


클러스터에 속한 포인트 수가 많이 다를 때(하나의 클러스터가 다른 것보다 매우 클 때 ) average나 complete가 더 나음


# library import

import mglearn

import matplotlib

import matplotlib.pyplot as plt


# matplotlib 설정

matplotlib.rc('font', family='AppleGothic')

plt.rcParams['axes.unicode_minus'] = False


# 알고리즘 설명 시각화

mglearn.plots.plot_agglomerative_algorithm()

plt.show()

두 인접 클러스터를 반복적으로 합쳐나가는 병합 군집

1. 초기에 각 포인트가 하나의 클러스터

2. 가장 가까운 두 클러스터가 합쳐짐

3. step4까지 이 방식으로 진행

4. step5에서 두 개의 포인트를 가진 클러스중 하나가 3개로 확장

5. step9까지 이 방식으로 진행

6. 3개의 클러스터를 찾는다고 지정하면 알고리즘은 종료




1. make blobs(인위적 데이터)로 병합 군집 분석


알고리즘의 작동 특성상 병합 군집은 새로운 데이터 포인트에 대해서는 예측을 할 수 없기 때문에

predict메소드가 없음(매니폴드 학습과 동일)


# library import

from sklearn.cluster import AgglomerativeClustering

from sklearn.datasets import make_blobs

import numpy as np


# dataset

x, y = make_blobs(random_state=1)


# 모델 생성 및 학습

agg = AgglomerativeClustering(n_clusters=3)

assign = agg.fit_predict(x)


# 배열 x 오른쪽에 열 한개 추가

a = assign.reshape(-1, 1)

x1 = np.hstack([x, a])


# 각 클래스별로 데이터 추출

x_0 = x1[x1[:, 2]==0, :]

x_1 = x1[x1[:, 2]==1, :]

x_2 = x1[x1[:, 2]==2, :]


# 시각화

plt.scatter(x_0[:, 0], x_0[:, 1], cmap=mglearn.cm3)

plt.scatter(x_1[:, 0], x_1[:, 1], cmap=mglearn.cm3)

plt.scatter(x_2[:, 0], x_2[:, 1], cmap=mglearn.cm3)

plt.legend(['cluster 0', 'cluster 1', 'cluster 2'], loc=2)

plt.show()

병합 군집을 사용한 세 개의 클러스터 할당




2. 계층적 군집과 덴드로그램

병합군집은 계층적 군집hierarchical clustering을 만듬. 군집이 반복하여 진행되면 모든 포인트는 하나의 포인트를 가진 클러스터에서 시작하여 마지막 클러스터까지 이동

각 중간단계는 데이터에 대한 (다른 갯수)의 클러스터를 생성


mglearn.plots.plot_agglomerative()

plt.show()

병합 군집으로 생성한 계층적 군집


이 그래프는 계층 군집의 모습을 자세히 나타내지만, 2차원 데이터일 뿐이며 특성이 셋 이상인 데이터셋에는 적용불가

dendrogram은 다차원 데이터셋을 처리가능



3. dendrogram으로 3차원 이상의 데이터 시각화


# library import

from scipy.cluster.hierarchy import dendrogram, ward[각주:1]


# dataset

x, y = make_blobs(random_state=0, n_samples=12)


### 데이터 배열 x에 ward 함수를 적용

### scipy의 ward 함수는 병합 군집을 수행할 때 생성된 거리 정보가 담긴 배열을 반환


linkage_array = ward(x)

dendrogram(linkage_array)

ax = plt.gca() # get current axes

bounds = ax.get_xbound() # x축 데이터(처음과 끝), 즉 최소/최대값을 가진 (1,2)리스트

ax.plot(bounds, [7.25, 7.25], linestyle='--', c='k'# 임의로 라인 생성

ax.plot(bounds, [4, 4], linestyle='--', c='k')

ax.text(bounds[1], 7.25, ' 두 개의 클러스터', va='center', fontdict={'size':15}) # bounds: x축 끝

ax.text(bounds[1], 4, ' 세 개의 클러스터', va='center', fontdict={'size':15})

plt.xlabel('샘플 번호')

plt.ylabel('클러스터 거리')

plt.show()

클러스터의 덴드로그램과 클러스터를 구분하는 점선


덴드로그램에서 데이터 포인트(0 ~ 11)까지는 맨 아래 나타남

이 포인트들을 잎leaf으로 하는 트리가 만들어지며 부모 노드는 두 클러스터가 합쳐질때 추가됨


가지의 길이는 합쳐진 클러스터가 얼마나 멀리 떨어져 있는지를 보여줌

빨강 그룹과 바다색 그룹이 합쳐질때 가지의 길이는 짧아짐 ==> 비교적 짧은 거리의 데이터를 병합

  1. ward : 기본값, 모든 클러스터 내의 분산을 가장 작게 증가시키는 두 클러스터를 합침, 크기가 비교적 비슷한 클러스터가 만들어짐 [본문으로]

#!/usr/bin/env python3


벡터 양자화(vector quantization)로서의 k-평균


0. 살펴보기

PCA : 데이터의 분산이 가장 큰 방향

NMF : 데이터의 극단 또는 일부분에 상응되는 중첩할 수 있는 성분

k-평균 : 클러스터의 중심으로 데이터 포인트를 표현 ==> 각 데이터 포인트가 클러스터 중심, 즉 하나의 성분으로 표현된다고 볼 수 있음

k-평균을 이렇게 각 포인트가 하나의 성분으로 분해되는 관점으로 보는 것을 벡터 양자화vector quantization이라 함


1. PCA, NMF, k-평균에서 추출한 성분을 바탕으로 시각화

# library import

from sklearn.datasets import fetch_lfw_people

from sklearn.model_selection import train_test_split

import matplotlib.pyplot as plt

import matplotlib

import numpy as np

from sklearn.decomposition import NMF

from sklearn.decomposition import PCA

from sklearn.cluster import KMeans


# matplotlib 설정

matplotlib.rc('font', family='AppleGothic')

plt.rcParams['axes.unicode_minus'] = False


# people객체 생성

people = fetch_lfw_people(min_faces_per_person=20, resize=0.7, color=False# 겹치치 않는 최소사람 수, 비율, 흑백

image_shape = people.images[0].shape 


# 특성 50개 추출(True)

idx = np.zeros(people.target.shape, dtype=np.bool) # 3023개의 False생성

for target in np.unique(people.target): 

    idx[np.where(people.target == target)[0][:50]] = 1 


# 데이터 추출

x_people = people.data[idx]

y_people = people.target[idx]


# 데이터 분할

x_train, x_test, y_train, y_test = train_test_split(

    x_people, y_people, # 분할할 데이터

    stratify=y_people, random_state=0) # 그룹, 랜덤상태


# 모델생성 및 학습

nmf = NMF(n_components=100, random_state=0).fit(x_train) 

pca = PCA(n_components=100, random_state=0).fit(x_train)

kmeans = KMeans(n_clusters=100, random_state=0).fit(x_train)


### pca.transform(x_test): 주성분 갯수만큼 차원을 줄인 데이터

### pca.inverse_transform(pca.transform(x_test)): 차원을 줄인 주성분으로 원래 데이터에 매핑

### kmeans.cluster_centers_[kmeans.predict(x_test)] k-평균 중심에서 k-평균으로 구한 라벨을 행 인덱스로 추출하여 재구성

x_reconst_nmf = np.dot(nmf.transform(x_test), nmf.components_) nmf.inverse_transform(nmf.transform(x_test)) 와 동일

x_reconst_pca = pca.inverse_transform(pca.transform(x_test))

x_reconst_kmeans = kmeans.cluster_centers_[kmeans.predict(x_test)]


# visualization

fig, axes = plt.subplots(3, 5,

                         subplot_kw={'xticks':(), 'yticks':()})

for ax, comp_nmf, comp_pca, comp_kmeans in zip(

        axes.T, nmf.components_, pca.components_, kmeans.cluster_centers_): # 행방향 순서로 그림을 채우기 때문에 Transfrom을 

    ax[0].imshow(comp_nmf.reshape(image_shape))

    ax[1].imshow(comp_pca.reshape(image_shape))

    ax[2].imshow(comp_kmeans.reshape(image_shape))


axes[0, 0].set_ylabel('nmf')

axes[1, 0].set_ylabel('pca')

axes[2, 0].set_ylabel('kmeans')

plt.gray()

plt.show()

NMF, PCA, k-평균의 클러스터 중심으로 찾은 성분의 비교

# library import 

fig, axes = plt.subplots(4, 5,

                         subplot_kw={'xticks':(), 'yticks':()}) # subplot 축 없애기

for ax, orig, rec_nmf, rec_pca, rec_kmeans in zip(

        axes.T, x_test, x_reconst_nmf, x_reconst_pca, x_reconst_kmeans):

    ax[0].imshow(orig.reshape(image_shape))

    ax[1].imshow(rec_nmf.reshape(image_shape))

    ax[2].imshow(rec_pca.reshape(image_shape))

    ax[3].imshow(rec_kmeans.reshape(image_shape))


for i, name in zip(range(4), ['원본', 'kmeans', 'pca', 'nmf']):

    axes[i, 0].set_ylabel(name)

plt.gray()

plt.show()

성분(또는 클러스터 중심) 100개를 사용한 NMF, PCA, k-평균의 이미지 재구성


k-평균을 사용한 벡터 양자화는 데이터의 차원보다 더 많은 클러스터를 사용해 데이터를 인코딩할 수 있음



2. 특성의 수보다 많은 클러스터중심을 사용한 k-평균


# library import

from sklearn.datasets import make_moons


# dataset생성

x, y = make_moons(n_samples=200, noise=0.05, random_state=0) # noise: 퍼짐정도


# 모델 생성 및 학습, 예측

kmeans = KMeans(n_clusters=10, random_state=0).fit(x)

y_pred = kmeans.predict(x)


plt.scatter(x[:, 0], x[:, 1], c=y_pred, s=60, cmap='Paired', edgecolors='black', alpha=0.7) # cmap: palette

plt.scatter(kmeans.cluster_centers_[:, 0], kmeans.cluster_centers_[:, 1], c=np.unique(kmeans.labels_), # c: group

            s=100, marker='s', linewidths=2, cmap='Paired', edgecolors='black')

plt.xlabel('특성 0')

plt.ylabel('특성 1')

plt.show()

복잡한 형태의 데이터셋을 다루기 위해 많은 클러스터를 사용한 k-평균


10개의 클러스터를 사용했기 때문에 10개로 구분

해당 클러스터에 해당하는 특성을 1, 해당하지않는 특성을 0으로 구분


큰 규모의 데이터셋을 처리할 수 있는 scikit-learn은 MiniBatchKMenas도 제공


k-평균의 단점은 클러스터의 모양을 가정(원)하고 있어서 활용범위가 비교적 제한

찾으려하는 클러스터의 갯수를 지정해야함

#!/usr/bin/env python3


k-평균 군집 알고리즘


0. 살펴보기

데이터셋을 클러스터cluster라는 그룹으로 나누는 작업

다른 클러스터의 데이터 포인트와는 구분되도록 데이터를 나누는 것이 목표


k-평균k-mean 군집은 가장 간단하고 널리 사용됨

이 알고리즘은 데이터의 어떤 영역을 대표하는 클러스터 중심cluster center를 찾음

1. 데이터 포인트를 가장 가까운 클러스터 영역에 할당

2. 클러스터에 할당된 데이터 포인트의 평균으로 클러스터 중심을 다시 지정

3. 클러스터에 할당되는 데이터 포인트에 변화가 없을 때 알고리즘 종료


# library import

import mglearn

import matplotlib

import matplotlib.pyplot as plt


# matplotlib 설정

matplotlib.rc('font', family='AppleGothic'# 한글 출력

plt.rcParams['axes.unicode_minus'] = False #  축 -


mglearn.plots.plot_kmeans_algorithm()

plt.show()

입력 데이터와 k-평균 군집 알고리즘이 세번 진행되기가지의 과정


# 경계선 그리기

mglearn.plots.plot_kmeans_boundaries()

plt.show() 

k-평균 알고리즘으로 찾은 클러스터 중심과 클러스터 경계




1. make_blobs(인위적 데이터셋)에 k-평균 알고리즘을 적용

# library import

from sklearn.datasets import make_blobs

from sklearn.cluster import KMeans

import numpy as np


# 데이터 만들기

x, y = make_blobs(random_state=1)


# 모델 생성 및 학습

kmean = KMeans(n_clusters=3) # 클러스터 수

kmean.fit(x)


print('클러스터 레이블: \n{}'.format(kmean.labels_)) 

3개의 클러스터를 지정했으므로 각 클러스터는 0 ~ 2까지 번호가 붙음


### predict메소드를 통해 새로운 데이터 클러스터 레이블을 예측할 수 있음

### 예측은 각 포인트에 가장 가까운 클러스터 영역으로 할당하는 것이며 기존 모델을 변경하지 않음

### 훈련 세트에 대해 predict 메소드를 실행하면 labels_와 같은 결과를 얻게 됨


print('예측 레이블: \n{}'.format(kmean.predict(x)))


### 클러스터는 각 데이터 포인트가 레이블을 가진다는 면에서 분류와 비슷함

### 그러나 정답을 모르고 있으며 레이블 자체에 의미가 없음

### 알고리즘이 우리에게 주는 정보는 각 레이블에 속한 데이터는 서로 비슷하다는 것





2. make_blobs 데이터로 그래프 그리기

mglearn.discrete_scatter(x[:, 0], x[:, 1], kmean.labels_, markers= 'o') # x, y, group, marker

mglearn.discrete_scatter(

    kmean.cluster_centers_[:, 0], kmean.cluster_centers_[:, 1], y=np.unique(kmean.labels_),# x, y, group

    markers='^', markeredgewidth=2) # marker, 두께

plt.show()

k-평균 알고리즘으로 찾은 세 개의 클러스터 중심과 클러스터 할당




3. 클러스터수를 변경하여 비교

fig, axes = plt.subplots(2, 2, gridspec_kw={'hspace':0.5}) # gridspec_kw: 그래프 수평간격

n_clusters_set = [2, 4, 5, 6] 

for n, ax in zip(n_clusters_set, axes.ravel()):

    kmeans = KMeans(n_clusters=n) # 클러스터 갯수

    kmeans.fit(x)

    mglearn.discrete_scatter(x[:, 0], x[:, 1], y=kmeans.labels_, ax=ax) # x, y, group, ax = plot객체

    mglearn.discrete_scatter(

        kmeans.cluster_centers_[:, 0], kmeans.cluster_centers_[:, 1], y=np.unique(kmeans.labels_), # x, y, group

        markeredgewidth=3, ax=ax, s=15) # 두께, 그림객체, 사이즈

    ax.set_title('n_cluster: {}'.format(n)) # title

    ax.set_xlabel('feature 0')

    ax.set_ylabel('feature 1')

plt.show()

k-평균 알고리즘으로 클러스터를 여러개 사용했을 때의 중심과 클러스터 할당




4. k-평균 알고리즘이 실패하는 경우

데이터셋의 클러스터 갯수를 알고있더라도 k-평균 알고리즘이 항상 이를 구분해낼 수 있는 것은 아님

각 클러스터를 정의하는 것이 중심 하나뿐이므로 클러스터는 둥근 형태로 나타남

이런 이유로 k-평균 알고리즘은 비교적 간단한 형태를 구분할 수 있음

또한 k-평균은 모든 클러스터의 반경이 똑같다고 가정 ==> 클러스터 중심 사이에 정확히 중간에 경계를 그림


4-1 클러스터의 밀도가 다를 때

x1, y1 = make_blobs(n_samples=200, cluster_std=[1, 2.5, 0.5], random_state=170) # 샘플수, 밀도, 랜덤상태

y_pred = KMeans(n_clusters=3, random_state=0).fit_predict(x1) # 모델 생성 및 훈련 후 예측


mglearn.discrete_scatter(x1[:, 0], x1[:, 1], y=y_pred) # x, y, group

plt.legend(['cluster 0', 'cluster 1', 'cluster 2']) # 범례

plt.xlabel('feature 0')

plt.ylabel('feature 1')

plt.show()

클러스터의 밀도가 다를 때 k-평균으로 찾은 클러스터 할당


4-2 클러스터가 원형이 아닐  때

x2, y2 = make_blobs(n_samples=600, random_state=170) # 샘플 갯수, 램덤상태

rng = np.random.RandomState(74).normal(size=(2,2)) # 랜덤상태, (2,2)size로 normal분포의 숫자 무작위추출

x2_reshape = np.dot(x2, rng) # np.dot 행렬곱


kmeans = KMeans(n_clusters=3).fit(x2_reshape) # 모델 생성

y2_pred = kmeans.predict(x2_reshape) # 예측


mglearn.discrete_scatter(x2_reshape[:, 0], x2_reshape[:, 1], y=kmeans.labels_, alpha=0.7) # x, y , group, 투명도

mglearn.discrete_scatter(

    kmeans.cluster_centers_[:,0], kmeans.cluster_centers_[:,1], y=np.unique(kmeans.labels_), # x, y, group

    markeredgewidth=3, s=15) # 두께, size

plt.xlabel('feature 0')

plt.ylabel('feature 1')

plt.show()

원형이 아닌 클러스터를 구분하지 못하는 k-평균 알고리즘


4-3 클러스터의 구조가 복합형태를 나타낼 때

from sklearn.datasets import make_moons


x3, y3 = make_moons(n_samples=200, noise=0.05, random_state=0) # 갯수, 퍼짐정도, 랜덤상태

kmeans = KMeans(n_clusters=2).fit(x3) # 모델 객체 생성 및 학습

y3_pred = kmeans.predict(x3) # 모델 적용


plt.scatter(x3[:, 0], x3[:, 1], c=y3_pred, s=60, edgecolors='black', cmap=mglearn.cm2) # x, y, group, 점크기 60%, 테두리색 black, palette

plt.scatter(kmeans.cluster_centers_[:, 0], kmeans.cluster_centers_[:, 1], # x, y

            marker='^', s=120, linewidth=2, edgecolors='k', c=[mglearn.cm2(0), mglearn.cm2(1)]) # marker,  점크기 120%, 두께, 테두리색, 색상 

plt.xlabel('feature 0')

plt.ylabel('feature 1')

plt.show()

반달 모양의 클러스터를 구분하지 못하는 k-평균 알고리즘


k-평균 알고리즘이 2개의 반달모양을 구분하지 못하고 원형으로 데이터를 비교






+ Recent posts