본문 바로가기

파이썬/머신러닝

k-평균 군집화 알고리즘 기본 개념과 예제

 

머신러닝은 크게 지도 학습과 비지도 학습으로 나뉩니다. 지도 학습은 정답이 정해진 것(=데이터에 레이블이 있음), 비지도 학습은 정답이 정해지지 않은 것(=데이터에 레이블이 없음)입니다. 현재 대부분의 머신러닝은 지도 학습 기반이지만 실제 현실에서 사용하는 데이터 중 대부분은 레이블이 없습니다.

 

 

비지도 학습은 크게 군집, 이상치 탐지, 밀도 추정 3가지 영역으로 나눌 수 있습니다. 이번 포스팅에서는 군집, 그 중에서도 가장 유명하고 보편적인 군집 알고리즘인 k-평균 군집분석에 대해 살펴보겠습니다.

 

 

k-평균 군집(k-means)

 

우선 군집은 비슷한 샘플을 구별해 클러스터 또는 비슷한 샘플의 그룹으로 할당하는 알고리즘입니다. 클러스터에 대한 보편적인 정의는 없으며 상황에 따라, 알고리즘에 따라 다르고 종류도 매우 다양합니다.

 

 

k-평균  군집분석은 데이터를 k개의 클러스터(군집)으로 묶는 알고리즘입니다. n개의 데이터와 k개의 중심점(센트로이드)가 주어졌을 때 각각의 데이터와 센트로이드 간 거리가 최소화될 때까지 센트로이드를 업데이트해서 k개의 군집을 생성합니다. 사이킷런 라이브러리의 KMeans를 사용하면 쉽게 구현할 수 있습니다. 일반적으로 k=5로 설정한다고 하지만 이는 데이터셋에 따라 다르고 k의 개수를 지정하는 일은 쉬운 일은 아닙니다.

 

 

추가로 군집에서 각 샘플의 레이블은 알고리즘이 샘플에 할당한 클러스터의 인덱스입니다. 분류 모델에서의 클래스 레이블과는 다르다는 점에 주의해야 합니다. 

 

 

k평균 군집 알고리즘의 프로세스

 

1. 무작위로 k개의 샘플을 뽑아 그 위치를 센트로이드로 배정

2. 각 레코드를 가장 가까운 센트로이드에 배정 (각 객체와 센트로이드 간 거리를 구함)

3. 센트로이드 업데이트

4. 센트로이드에 변화가 없을 때까지 2~3의 과정을 반복

 

 

이렇게 센트로이드를 랜덤하게 초기화하는 것은 알고리즘이 최적의 클러스터에 수렴할 수도 있지만 처음에 초기화할 때 운이 없다면 그렇지 못할 수도 있습니다. 

 

 

KMeans 주요 하이퍼파라미터

하이퍼파라미터명 default 설명
n_clusters 8 센트로이드의 갯수
init 'k-means++' 'k-means++'
: 하나의 무작위 중심점을 배정하고 두번째 중심점을 첫번째 중심점과 멀리 떨어진 곳에 배치, 세번째는 첫번째 두번째 중심점과 멀리 떨어진 곳에 배치
'random'
:  랜덤하게 중심점 배정
n_init 10 랜덤 초기화 횟수 설정 (전체 과정을 몇번 실행할 것인지)
max_iter 300 최대 반복 횟수
tol 1e-4 이너셔(inertia)가 tol만큼 줄어들지 않으면 조기 종료

 

여기서 중요한 개념이 바로 이너셔입니다. 이너셔는 각 샘플과 가장 가까운 센트로이드 사이의 평균제곱거리이며 k평균 군집 알고리즘의 성능 지표입니다. k평균 군집은 알고리즘을 n_init번 실행해서 이너셔가 가장 낮은 모델을 반환합니다. 샘플과 센트로이드 사이의 거리가 짧을수록 좋기 때문에 일반적으로 이너셔값이 작을수록 성능이 좋습니다. 사이킷런의 KMeans는 n_init의 횟수만큼 반복해서 이너셔 값이 가장 작은 최선의 솔루션을 반환합니다.

 

다음으로 init의 기본값인 k-means++은 처음에 센트로이드를 무작위로 선정할 때 이전 센트로이드와 거리가 먼 센트로이드를 선택하는 알고리즘입니다. k-means++을 선택하면 k평균 알고리즘이 최적이 아닌 솔루션으로 수렴할 가능성을 크게 낮춰줍니다. 

 

 

iris 데이터로 간단하게 코드를 짜보았습니다.

from sklearn.cluster import KMeans
from sklearn.datasets import load_iris
import matplotlib.pyplot as plt
import pandas as pd
%matplotlib inline

iris = load_iris()
X = iris.data[:,:2]
y = iris.target

#모델 fit
kmean = KMeans(n_clusters=3, random_state=42)
kmean.fit(X)

centroid = kmean.cluster_centers_
print(centroid)
print(kmean.inertia_)
print(kmean.score(X))
>>>array([[5.77358491, 2.69245283],
       [6.81276596, 3.07446809],
       [5.006     , 3.428     ]])
>>>37.0507021276596
>>>-37.050702127659605

**score( ) 메서드는 이너셔의 음수값을 반환합니다. 

 

 

최적의 k값 찾기

 

사실 최적의 클러스터 개수를 찾는 것은 굉장히 어려운 일입니다. iris 데이터는 워낙 유명하고 이미 데이터 구조를 알고 있기 때문에 클러스터 개수를 3개로 설정하는 것이 쉬웠지만 일반적으로 우리가 만나게 되는 데이터는 그렇지 않습니다. 

 

출처: 핸즈온머신러닝

 

앞에서 이너셔가 작을수록 일반적으로 성능이 좋다고 했는데 항상 그런 것은 아닙니다. 이너셔는 k가 증가함에 따라 점점 작아지기 때문에 모델의 성능을 평가할 때는 좋은 지표여도 k를 선택하는데에 좋은 지표는 아닙니다.

 

 

출처: 핸즈온머신러닝

다만 이너셔는 위의 그래프와 같이 어느 순간부터 느리게 감소하기 시작합니다. 이렇게 감소 속도가 줄어드는 지점을 elbow라고 합니다. 위 그래프에서 k가 4보다 더 커져봤자 유의미한 성능을 보이지 않기 때문에 k=4를 선택하는 것이 가장 효율적인 선택입니다.

 

 

다만 이 방법은 너무 주먹구구식이기 때문에 더 정확하게 하기 위해서는 모든 샘플에 대한 실루엣 계수의 평균값인 실루엣 점수를 사용합니다. 사이킷런의 silhouette_score( ) 함수를 사용하면 됩니다.

 

 

 

k-means 알고리즘은 속도가 빠르고 확장이 용이하다는 장점이 있지만 k개수를 정하기 어렵고 클러스터의 크기나 밀집도가 서로 다르거나 원형이 아니면 작동하기 어렵다는 단점이 있습니다.