ML & DL/파이썬 머신러닝 실전 가이드

[Python ML Guide] Section 7.5(군집화 Clustering): DBSCAN (Density-Based Spatial Clustering of Applications with Noise)

Jae. 2023. 11. 17. 05:37
728x90

https://www.inflearn.com/course/%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EB%A8%B8%EC%8B%A0%EB%9F%AC%EB%8B%9D-%EC%99%84%EB%B2%BD%EA%B0%80%EC%9D%B4%EB%93%9C

 

[개정판] 파이썬 머신러닝 완벽 가이드 - 인프런 | 강의

이론 위주의 머신러닝 강좌에서 탈피하여 머신러닝의 핵심 개념을 쉽게 이해함과 동시에 실전 머신러닝 애플리케이션 구현 능력을 갖출 수 있도록 만들어 드립니다., [사진]상세한 설명과 풍부

www.inflearn.com

 

 


1. DBSCAN

 

 

 

DBSCAN은 특정 공간 내에 데이터 밀도 차이를 기반 알고리즘으로 하고 있어서

 

복잡한 기하학적 분포도를 가진 데이터 세트에 대해서도 군집화를 잘 수행함

 

 

 

 

DBSCAN은 알고리즘이 데이터 밀도 차이를 자동으로 감지하여 군집을 생성하므로 사용자가 군집 개수를 지정할 수 없음

 

 

 

군집화 알고리즘별 비교

 

  • DBSCAN은 데이터의 밀도가 자주 변하거나, 아예 모든 데이터의 밀도가 크게 변하지 않으면 군집화 성능이 떨어짐
  • Feature의 개수가 많으면 군집화 성능이 떨어짐

 

 

 

DBSCAN 구성 요소

 

DBSCAN을 구성하는 가장 중요한 두 가지 파라미터는 입실론(epsilon)으로 표기하는 주변 영역과 이 입실론 주변 영역에 포함되는 최소 데이터의 개수 min points

 

 

  • 입실론 주변 영역(epsilon): 개별 데이터를 중심으로 입실론 반경을 가지는 원형의 영역
  • 최소 데이터 개수(min points): 개별 데이터의 입실론 주변 영역에 포함되는 타 데이터의 개수

 

 

 

 

Data Points

 

입실론 주변 영역 내에 포함되는 최소 데이터 개수를 충족시키는가 아닌가에 따라 데이터 포인트를 다음과 같이 정의

 

해당 Point를 기준으로 Epsilon 반경으로 원형의 영역을 그리고 해당 영역 내 포함되는 최소 데이터 개수를 체크

 

 

 

 

  • Core Point (핵심 포인트)
    • 주변 영역 내에 최소 데이터 개수 이상의 타 데이터를 가지고 있을 경우 해당 데이터를 핵심 포인트

 

  • Neighbor Point (이웃 포인트)
    • 주변 영역 내에 위치한 타 데이터를 이웃 포인트
    • 유동적, 일종의 역할

 

  • Border Point (경계 포인트)
    • 주변 영역 내에 최소 데이터 개수 이상의 이웃 포인트를 가지고 있지 않지만 핵심 포인트를 이웃 포인트로 가지고 있는 데이터를 경계 포인트

 

  • Noise Point (잡음 포인트)
    • 최소 데이터 개수 이상의 이웃 포인트를 가지고 있지 않으며, 핵심 포인트도 이웃 포인트로 가지고 있지 않는 데이터를 잡음 포인트

 

 

 

 

DBSCAN 절차

 

 

12개의 Data Point들에 대해 DBSCAN 군집화 진행

 

  • 1) 특정 입실론 반경 내에 포함될 최소 데이터 세트(min_points)를 6개로 가정 (자기 자신의 데이터를 포함)

 

 

 

  • 2) P1 데이터를 기준으로 입실론 반경 내에 포함된 데이터가 7개로 최소 데이터 5개 이상을 만족하므로 P1 데이터는 핵심 포인트

 

 

 

  • 3) P2 데이터를 기준으로 입실론 반경 내에 포함된 데이터가 6개로 역시 핵심 포인트

 

 

 

  • 4) 핵심 포인트 P1의 이웃 데이터 포인트 P2 역시 핵심 포인트일 경우 P1에서 P2로 연결해 직접 접근이 가능

 

 

 

 

  • 5) 특정 핵심 포인트에서 직접 접근이 가능한 다른 핵심 포인트를 서로 연결하면서 군집화를 구성. 이러한 방식으로 점차적으로 군집(Cluster) 영역을 확장해 나가는 것이 DBSCAN 군집화 방식

 

 

 

 

  • 6) P3 데이터의 경우 반경 내에 포함되는 이웃 데이터는 P2, P4 2개이므로 군집으로 구분할 수 있는 핵심 포인트가 될 수 없음. 하지만 이웃 데이터 중에 핵심 포인트인 P2를 가지고 있음.
    • 이처럼 자신은 핵심 포인트가 아니지만, 이웃 데이터로 핵심 포인트를 가지고 있는 데이터를 경계 포인트(Border Point)라고 함.
    • 경계 포인트는 군집의 외곽을 형성

 

 

 

  • 7) P5와 같이 반경 내에 최소 데이터를 가지고 있지도 않고, 핵심 포인트 또한 이웃 데이터로 가지고 있지 않는 데이터를 잡음 포인트 (Noise Point)라고 함

 

 

 

군집화의 의의

 

 

 


2. DBSCAN @ Scikit - Learn

 

 

Scikit - Learn DBSCAN

 

 

사이킷런은 DBSCAN 클래스를 통해 DBSCAN 알고리즘을 지원함

 

DBSCAN 클래스는 다음과 같은 주요한 초기화 파라미터를 가지고 있음

 

  • eps
    • 입실론 주변 영역의 반경을 의미함

 

  • min_samples
    • 핵심 포인트가 되기 위해 입실론 주변 영역 내에 포함돼야 할 데이터의 최소 개수를 의미함
    • 자신의 데이터를 포함, min_points +1

 

 

eps를 늘리고 min_samples를 줄이면 밀도가 작음에도 불구하고 군집화를 수행

 

군집화 개수를 지정할 수 없지만 군집에 해당되는 기준을 완화시킴

 

 

 


3. 실습 - DBSCAN

 

 

 

DBSCAN 적용하기 - iris DataSet

 

from sklearn.datasets import load_iris

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']

# 보다 편리한 데이타 Handling을 위해 DataFrame으로 변환
irisDF = pd.DataFrame(data=iris.data, columns=feature_names)
irisDF['target'] = iris.target
irisDF.head()

 

 

DBSCAN시 Label이 -1일 경우: Noise로 판단 (어떠한 Cluster에도 속하지 않음)

 

 

  • eps=0.6, min_samples=8로 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

iris_result = irisDF.groupby(["target"])["dbscan_cluster"].value_counts()
print(iris_result)
target  dbscan_cluster
0        0                49
        -1                 1
1        1                46
        -1                 4
2        1                42
        -1                 8
Name: count, dtype: int64

 

### 클러스터 결과를 담은 DataFrame과 사이킷런의 Cluster 객체등을 인자로 받아 클러스터링 결과를 시각화하는 함수  
def visualize_cluster_plot(clusterobj, dataframe, label_name, iscenter=True):
    if iscenter :
        centers = clusterobj.cluster_centers_
        
    unique_labels = np.unique(dataframe[label_name].values)
    markers=['o', 's', '^', 'x', '*']
    isNoise=False

    for label in unique_labels:
        label_cluster = dataframe[dataframe[label_name]==label]
        if label == -1:
            cluster_legend = 'Noise'
            isNoise=True
        else :
            cluster_legend = 'Cluster '+str(label)
        
        plt.scatter(x=label_cluster['ftr1'], y=label_cluster['ftr2'], s=70,\
                    edgecolor='k', marker=markers[label], label=cluster_legend)
        
        if iscenter:
            center_x_y = centers[label]
            plt.scatter(x=center_x_y[0], y=center_x_y[1], s=250, 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)
    if isNoise:
        legend_loc='upper center'
    else: legend_loc='upper right'
    
    plt.legend(loc=legend_loc)
    plt.show()

 

  • PCA 2개 component로 기존 Feature들을 차원 축소 후 시각화
from sklearn.decomposition import PCA

# 2차원으로 시각화하기 위해 PCA n_componets=2로 피처 데이터 세트 변환
pca = PCA(n_components=2, random_state=0)
pca_transformed = pca.fit_transform(iris.data)
# visualize_cluster_2d( ) 함수는 ftr1, ftr2 컬럼을 좌표에 표현하므로 PCA 변환값을 해당 컬럼으로 생성
irisDF["ftr1"] = pca_transformed[:, 0]
irisDF["ftr2"] = pca_transformed[:, 1]

visualize_cluster_plot(dbscan, irisDF, "dbscan_cluster", iscenter=False)

 

 

  • eps의 크기를 증가한 후 노이즈 확인
    • Noise 감소 (Cluster의 Core Point가 될 수 있는 조건들이 완화)
    • 주어진 엡실론 반경 안으로 들어오는 데이터 포인트들이 많아지기 때문

 

from sklearn.cluster import DBSCAN

dbscan = DBSCAN(eps=0.8, min_samples=8, metric="euclidean")
dbscan_labels = dbscan.fit_predict(iris.data)

irisDF["dbscan_cluster"] = dbscan_labels
irisDF["target"] = iris.target

iris_result = irisDF.groupby(["target"])["dbscan_cluster"].value_counts()
print(iris_result)

visualize_cluster_plot(dbscan, irisDF, "dbscan_cluster", iscenter=False)
target  dbscan_cluster
0        0                50
1        1                50
2        1                47
        -1                 3
Name: count, dtype: int64

 

 

  • min_samples 의 크기를 증가 후 노이즈 화인
    • Noise 증가
    • Cluster의 Core Point가 되기 위한 조건들이 더 강화

 

dbscan = DBSCAN(eps=0.6, min_samples=16, metric="euclidean")
dbscan_labels = dbscan.fit_predict(iris.data)

irisDF["dbscan_cluster"] = dbscan_labels
irisDF["target"] = iris.target

iris_result = irisDF.groupby(["target"])["dbscan_cluster"].value_counts()
print(iris_result)
visualize_cluster_plot(dbscan, irisDF, "dbscan_cluster", iscenter=False)
target  dbscan_cluster
0        0                48
        -1                 2
1        1                44
        -1                 6
2        1                36
        -1                14
Name: dbscan_cluster, dtype: int64

 

 

 

DBSCAN 적용하기 - make_circles() 데이터 세트

 

  • make_circles(): 주로 DBSCAN으로 군집화 할 때 사용
from sklearn.datasets import make_circles

X, y = make_circles(
    n_samples=1000, shuffle=True, noise=0.05, random_state=0, factor=0.5
)
clusterDF = pd.DataFrame(data=X, columns=["ftr1", "ftr2"])
clusterDF["target"] = y

visualize_cluster_plot(None, clusterDF, "target", iscenter=False)

 

 

# KMeans로 make_circles( ) 데이터 셋을 클러스터링 수행.
from sklearn.cluster import KMeans

kmeans = KMeans(n_clusters=2, max_iter=1000, random_state=0)
kmeans_labels = kmeans.fit_predict(X)
clusterDF["kmeans_cluster"] = kmeans_labels

visualize_cluster_plot(kmeans, clusterDF, "kmeans_cluster", iscenter=True)

 

 

# GMM으로 make_circles( ) 데이터 셋을 클러스터링 수행.
from sklearn.mixture import GaussianMixture

gmm = GaussianMixture(n_components=2, random_state=0)
gmm_label = gmm.fit(X).predict(X)
clusterDF["gmm_cluster"] = gmm_label

visualize_cluster_plot(gmm, clusterDF, "gmm_cluster", iscenter=False)

 

# DBSCAN으로 make_circles( ) 데이터 셋을 클러스터링 수행.
from sklearn.cluster import DBSCAN

dbscan = DBSCAN(eps=0.2, min_samples=10, metric="euclidean")
dbscan_labels = dbscan.fit_predict(X)
clusterDF["dbscan_cluster"] = dbscan_labels

visualize_cluster_plot(dbscan, clusterDF, "dbscan_cluster", iscenter=False)

728x90