[파이썬 머신러닝 가이드] 군집화 - DBSCAN, 군집화 실습(고객 세그먼테이션)

목차

1. DBSCAN


2. 군집화 실습 - 고객 세그먼테이션

 

1. DBSCAN

밀도 기반 군집화의 대표적인 알고리즘, 특정 공간에서 가까이 있는 데이터가 많아 붐비는 지역(밀집지역)의 포인트를 찾음

→ 특정 데이터를 중심으로 밀도가 높은 곳에 포함된 데이터에는 클러스터를 할당함(서로 인접한 데이터들은 같은 클러스터)

 

** 특정 공간 내에 데이터 밀도 차이를 기반 알고리즘으로 하고 있어서 복잡한 기하학적 분포도를 가진 데이터 세트에 대해서도 군집화를 잘 수행함 ex) 내부의 원 모양과 외부의 원 모양 형태의 분포를 가진 데이터 셋

 

이런 형태의 데이터는 K-Means와 GMM 보다 훨씬 분류를 잘한다.

 

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

 

  • 데이터 포인트 정의
    • 핵심 포인트(Core Point) : 주변 영역 내에 최소 데이터 개수 이상의 타 데이터를 가지고 있을 경우 해당 데이터를 핵심 포인트라고 함
    • 이웃 포인트(Neighbor Point) : 주변 영역 내에 위치한 타 데이터를 이웃 포인트라 함
    • 경계 포인트(Border Point) : 주변 영역 내에 최소 데이터 개수 이상의 이웃 포인트를 가지고 있진 않지만, 핵심 포인트를 이웃 포인트로 가지고 있는 데이터
    • 잡음 포인트(Noise Point) : 최소 데이터 개수 이상의 이웃 포인트를 가지고 있지 않으며, 핵심 포인트도 이웃 포인트로 가지고 있지 않는 데이터

 

  • 과정

 

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

 

but 이웃 데이터 중에 핵심 포인트인 P2를 가지고 있음. 이처럼 자신은 핵심 포인트가 아니지만, 이웃 데이터로 핵심 포인트를 가지고 있는 데이터경계 포인트(border point)라고 함. 경계 포인트는 군집의 외곽을 형성함.

 

⇒ 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
irisDF['target'] = iris.target

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: dbscan_cluster, dtype: int64

→ 군집레이블이 -1인 것은 노이즈에 속하는 군집을 의미함. → 이 붓꽃 데이터 셋은 0과 1 두개의 군집으로 군집화된 것.

(target 값의 유형이 3가지인데, 군집이 2개가 됐다고 군집화 효율이 떨어지는 건 아님.)

← DBSCAN은 군집의 개수를 알고리즘에 따라 자동으로 지정하므로 군집의 개수 지정하는건 무의미. 오히려 군집 개수 2개인게 더 효율적.

 

** 특정 군집 개수 말고, 적절한 eps와 min_samples 를 통해 최적의 군집을 찾는 게 중요함.

→ eps 값을 크게 ⇒ 반경이 커져 포함하는 데이터가 많아지므로 노이즈 데이터 개수가 작아짐

→ min_samples 크게 (입시론 반경은 똑같고)⇒ 주어진 반경 내에서 더 많은 데이터를 포함시켜야 하므로 노이즈 데이터 개수가 커지게 됨. (데이터 밀도가 더 커져야 하는데, 매우 촘촘한 데이터 분포가 아닌 경우 노이즈로 인식하기 때문, 원은 똑같은데 포함해야하는 데이터가 많아지니 하나의 군집으로 인정되기 어려워 노이즈가 되는 것. )

 

2. 군집화 실습 - 고객 세그먼테이션

고객 세그먼테이션 : 다양한 기준으로 고객을 분류하는 기법 → 타깃 마케팅을 위해

  • RFM 기법 사용
    • Regency(R) : 가장 최근 상품 구입 일에서 오늘까지의 기간
    • Frequency(F) : 상품 구매 횟수
    • Monetary Value(M) : 총 구매 금액
# DataFrame의 groupby() 의 multiple 연산을 위해 agg() 이용 <- groupby()만 사용해서는 여러 개의 칼럼에 서로 다른 aggregation 연산(count()나 max()..)을 한 번에 수행하기 어려움
# Recency는 InvoiceDate 컬럼의 max() 에서 데이터 가공
# Frequency는 InvoiceNo 컬럼의 count() , Monetary value는 sale_amount 컬럼의 sum()
aggregations = {
    'InvoiceDate': 'max', #고객별 가장 최근 주문 일자를 먼저 구한 뒤 가공 작업 별도 수행
    'InvoiceNo': 'count',
    'sale_amount':'sum'
}
cust_df = retail_df.groupby('CustomerID').agg(aggregations) #agg()에 인자로 대상 칼럼들과 aggregation 함수명을 딕셔너리 형태로 입력 -> 칼럼 여러 개, 서로다른 aggregation 연산 쉽게 수행 가능
# groupby된 결과 컬럼값을 Recency, Frequency, Monetary로 변경
cust_df = cust_df.rename(columns = {'InvoiceDate':'Recency',
                                    'InvoiceNo':'Frequency',
                                    'sale_amount':'Monetary'
                                   }
                        )
cust_df = cust_df.reset_index()
import datetime as dt

cust_df['Recency'] = dt.datetime(2011,12,10) - cust_df['Recency'] #recency는 고객이 가장 최근에 주문한 날짜를 기반으로 함(오늘날짜를 기준으로 가장 최근 주문 일자를 뺀 날짜, 여기서 오늘 날짜를 현재날짜에 +한 날짜로 해야함)
cust_df['Recency'] = cust_df['Recency'].apply(lambda x: x.days+1)
print('cust_df 로우와 컬럼 건수는 ',cust_df.shape)
cust_df.head(3)

 

지나치게 왜곡된 데이터 셋은 K-Means와 같은 거리 기반 군집화 알고리즘에서 지나치게 일반적인 군집화 결과 도출함.

물론, 이런 특이한 데이터 셋 분리하고 도출하는 게 군집화의 목표 이기도 하지만,

비지도학습 알고리즘의 하나인 군집화의 기능적 의미는 숨어 있는 새로운 집단을 발견하는 것. (새로운 군집 내의 데이터 값을 분석하고 이해함으로써 이 집단에 새로운 의미 부여 가능)

→ 데이터 세트의 왜곡 정도를 낮추기 위해 가장 자주 사용되는 방법은 데이터 값에 로그를 적용하는 로그 변환

⇒ 실루엣 스코어가 조금 떨어지도라도 이 스코어의 절대치가 중요한게 아니라, 개별 군집이 더 균일하게 나뉠 수 있는지가 더 중요함.