본문 바로가기
컴퓨터공학/AI

기계 학습 9. Unsupervised Learning Techniques

by Jinger 2023. 11. 17.

서론

   머신 러닝 응용 프로그램은 대체로 지도 학습이다. 그러나 현실에서는 라벨이 없는 데이터가 대부분이다. 따라서 데이터를 효율적으로 활용하기 위해 비지도 학습 기술이 필요하다. 예를 들어 사진을 통한 결함 감지와 같은 작업은 데이터를 클러스터로 그룹화하여 유사한 인스턴스를 함께 묶는 군집화 방법(Clustering)을 활용할 수 있다. 이상치 탐지(Anomaly detection)는 정상 데이터의 특성을 학습하여 비정상 인스턴스를 감지하는 데 사용되며, 이를 통해 예기치 않은 문제나 결함을 사전에 파악할 수 있다. 또한 데이터 세트의 확률 밀도 함수를 추정함(Density estimation)으로써 데이터 분석 및 시각화를 수행할 수 있다. 이러한 비지도 학습 기술은 라벨이 없는 데이터에서 유용한 정보를 추출하고, 데이터의 숨겨진 패턴을 발견하는 데 도움이 된다.


Clustering

classification vs clustering

    Clustering은 유사한 인스턴스를 식별하고 이를 클러스터 또는 유사한 인스턴스 그룹에 할당하는 작업이다. 분류(classification)와 마찬가지로 각 인스턴스는 그룹에 할당된다. 그러나 분류와 달리 클러스터링은 이미 정해진 라벨이 없기에 감독되지 않는 작업(Unsupervied task)이다. 그렇기에 분류되는 기준은 사람이 아닌 목적 혹은 데이터에 맞게 그룹의 갯수를 정한다.

클러스터링 활용처

     클러스터링 응용 프로그램에는 여러 가지가 있다.

  1. For customer segmentation(고객 세분화): 고객 분류에 사용되는 경우, 웹 사이트에서의 구매 및 활동 내역을 기반으로 고객을 클러스터링할 수 있다. 이를 통해 고객 그룹을 식별하고 그룹별 특성을 파악할 수 있다. 이는 마케팅 전략을 개발하거나 제품 개발에 도움이 될 수 있다.
  2. For data analysis(데이터 분석): 데이터 분석에 활용되기도 한다. 데이터셋을 클러스터링하여 각 클러스터를 개별적으로 분석함으로써 데이터에 대한 통찰력을 높일 수 있다. 이는 복잡한 데이터셋에서 중요한 패턴이나 관계를 파악하는 데 도움이 된다.
  3. As a dimensionality reduction technique(차원 축소 기법): 클러스터링은 차원 축소 기법으로도 사용된다. 데이터셋을 클러스터링하면 각 인스턴스의 클러스터 적합성을 측정할 수 있으며, 이를 이용하여 데이터의 차원을 줄일 수 있다. 이는 머신 러닝 모델의 학습 및 실행 속도를 향상시키고 메모리 사용량을 줄일 수 있다.
  4. For anomaly detection(also called outlier detection, 이상 탐지 or 이상치 탐지): 이상치 탐지에도 클러스터링이 활용된다. 모든 클러스터에 대한 저차원의 적합성을 가진 인스턴스는 이상치로 간주될 수 있다. 이를 통해 제조업에서의 제품 결함 탐지나 금융 분야에서의 사기 탐지에 유용하게 활용될 수 있다.
  5. For semi-supervised learning(반지도 학습): 반지도 학습에도 클러스터링을 적용할 수 있다. 라벨이 지정된 데이터가 한정적인 경우, 클러스터링을 통해 더 많은 데이터에 라벨을 할당할 수 있다. 이는 모델의 성능을 향상시킬 수 있다.
  6. For search engines(검색 엔진): 검색 엔진에서도 활용될 수 있다. 일부 검색 엔진은 참조 이미지와 유사한 이미지를 검색할 수 있다. 클러스터링 모델을 활용하여 참조 이미지의 클러스터를 식별한 다음 해당 클러스터에 속한 모든 이미지를 반환할 수 있다.
  7. To segment an image: 이미지 세그멘테이션에도 활용된다. 픽셀을 색상별로 클러스터링한 다음 각 픽셀의 색상을 해당 클러스터의 평균 색상으로 대체함으로써 이미지 내의 다양한 색상 수를 줄일 수 있다. 이를 통해 객체 감지 및 추적 시에 윤곽을 더 쉽게 인식할 수 있다.

K-Means

from sklearn.cluster import KMeans
k = 5	# 임시로 하이퍼파라미터로 지정
kmeans = KMeans(n_clusters = k)
y_pred = kmeans.fit_predict(X)

y_pred	# array([4, 0, 1, ..., 2, 1, 0], dtype = int32)
y_pred is kmeans.labels_	# True
kmeans.cluster_centers
# array([[-2.80389616, 1.80117999], [0.20876306, 2.25551336], [-2.79290307, 2.79641063], [-1.46679593, 2.28585348], [-2.80037642, 1.30082566]])
X_new = np.array([[0, 2], [3, 4], [-3, 3], [-3, 2.5]])
kmeans.predict(X_new)	# array([1, 1, 2, 2], dtype = int32)

kmeans.transform(X_new)
# array([[-2.81093633, 0.32995317, 2.904244, 149439034, 2.88633901],
# [5.80730058, 2.80290755, 5.84739223, 4,4759332, 5.84236351],
# [1.21475352, 3.29399768, 0.29040966, 1.69136631, 1.71086031],
# [0.72581411, 3.21806371, 0.36159148, 1.54808703, 1.21567622]])

      K-Means 알고리즘은 데이터셋을 빠르고 효율적으로 클러스터링하는 간단한 알고리즘이다. 각 Blob(윤곽)의 중심을 찾고 각 인스턴스를 가장 가까운 Blob에 할당한다. 이때 주의할 점은 알고리즘이 찾아야 하는 클러스터 수 k를 지정해야 한다는 것이다. 클러스터링에서 인스턴스의 라벨은 알고리즘에 의해 할당된 클러스터의 인덱스를 의미하며, 이는 분류에서의 클래스 레이블과 혼동해서는 안 된다. 클러스터의 결정 경계는 각 중심을 X로 표시되며, 다른 중심과 비교하며 Blob을 만든다. 그러나 다양한 지름을 가진 Blob의 경우(hard clustering) 인스턴스가 잘못 레이블링될 수 있다. 이를 보완하기 위해 소프트 클러스터링(soft clustering)을 사용하여 각 인스턴스에 대해 클러스터와의 거리나 유사도 점수를 부여할 수 있다. KMeans 클래스의 transform() 메서드를 통해 각 인스턴스에서 모든 중심까지의 거리를 측정할 수 있다.(위 예시를 보면 kmeans.transform()의 [0][0], [0][2], [0][4]가 비슷한 값을 갖는 것을 확인할 수 있다.) 이를 이용하여 각 중심으로부터 Blob은 고르게 분포하게 나눌 수 있다.

K-Means Algorithm

K-Means Algorithm
K-Means Algorithm with Different Centroid Initialization

    K-Means 알고리즘은 먼저 중심을 임의로 배치한 다음(예: 무작위로 k개의 인스턴스를 선택하고 그 위치를 중심으로 사용), 인스턴스에 레이블을 지정하고 중심을 업데이트하는 과정을 반복하여 중심이 더 이상 움직이지 않을 때까지 반복한다. 이 알고리즘은 유한한 수의 단계(일반적으로 매우 적은 수의 단계) 내에 수렴(converge)하는 것을 보장된다. 왜냐하면 각 단계에서 인스턴스와 가장 가까운 중심 간의 평균 제곱 거리는 계속해서 감소하기 때문이다. 그러나 이 알고리즘은 수렴이 보장되지만 올바른 솔루션으로 수렴할지는 중심 초기화(centroid initialization)에 따라 다르다. 중심 초기화를 개선 혹은 반복 실행하여 이러한 리스크를 완화할 수 있다.

K-Means Algorithm: Centroid initialization methods(중심 초기화 방법)

good_init = np.array([[-3, 3], [-3, 2], [-3, 1], [-1, 2], [0, 2]])
kmeans = KMeans(n_clusters = 5, init = good_init, n_init = 1)

    첫번째 방법은 만약 중심이 어느 정도의 위치에 있을 것으로 예상되거나 알고 있다면 init 하이퍼파라미터를 NumPy 배열로 설정하고 n_init을 1로 설정할 수 있다. 다른 해결책으로는 서로 다른 무작위 초기화를 사용하여 알고리즘을 여러 번 실행하고 최상의 솔루션을 유지하는 것이다. 이때 n_init 하이퍼파라미터로 무작위 초기화의 수를 제어할 수 있으며 기본적으로 10으로 설정되어 있다. 이는 fit()을 호출할 때 앞서 설명한 전체 알고리즘이 10번 실행되고 Scikit-Learn이 가장 좋은 솔루션을 유지한다. 최적의 솔루션을 결정하기 위해 각 인스턴스와 가장 가까운 중심 간의 평균 제곱 거리인 모델의 inertia 이라는 성능 측정 항목을 사용한다. 이외에도 K-Means++ 알고리즘의 중요한 개선 사항으로는 David Arthur와 Sergei Vassilvitskii가 2006년 논문에서 제안한 K-Means++이 있다. 이들은 서로 멀리 떨어진 중심을 선택하도록 하는 아래와 같은 단계를 도입했으며, 이 개선 사항은 K-Means 알고리즘이 최적이 아닌 솔루션으로 수렴할 가능성을 훨씬 줄여주었다. KMeans 클래스는 기본적으로 이 초기화 방법을 사용한다.

더보기

    비지도학습은 정답이 없지만 비교할 대상이 없다. 이를 해결한 방안이 Inertia이다. Inertia는 K-means 클러스터링에서 각 클러스터 내 데이터 포인트와 해당 클러스터의 중심 간의 거리의 제곱의 합을 의미한다. 작은 inertia 값은 각 클러스터 내의 데이터 포인트가 중심 주변에 모여있음을 나타내며, 이는 좋은 클러스터링 결과를 나타낸다. K-means 알고리즘은 inertia를 최소화하는 방향으로 클러스터의 중심을 조정하여 최적의 클러스터링 결과를 찾는다.

  1. 데이터 세트에서 무작위로 균일하게 선택된 하나의 중심 c(1)을 선택한다.
  2. 새로운 중심 c(i)를 취하고 위 확률을 따라 인스턴스 x(i)를 선택한다. 여기서 D(x(i))는 인스턴스 x(i)와 이미 선택된 가장 가까운 중심 사이의 거리이다. 이러한 확률 분포는 이미 선택된 중심에서 더 멀리 떨어진 인스턴스가 중심으로 선택될 가능성이 훨씬 더 높다는 것을 보장한다.
  3. k개의 중심이 모두 선택될 때까지 이전 단계를 반복한다.

Accelerated K-Means and mini-batch K-Means

    다른 K-Means 알고리즘의 중요한 개선 사항으로는 Charles Elkan이 2003년 논문에서 제안한 것이 있다. 이 알고리즘은 많은 불필요한 거리 계산을 피함으로써 알고리즘을 상당히 가속화한다. 이는 삼각 부등식(Triangle inequality 즉, 두 점 사이의 최단 거리는 항상 직선이라는 것)을 활용하고 각 인스턴스와 센트로이드 사이의 거리에 대한 하한과 상한을 추적함으로써 달성된다. 마찬가지로 이 방법 또한 KMeans 클래스가 기본적으로 사용하는 알고리즘이다. 또 다른 중요한 K-Means 알고리즘의 변형으로는 David Sculley가 2010년 논문에서 제안한 것이 있다. 이 알고리즘은 각 반복에서 전체 데이터셋을 사용하는 대신 미니 배치를 사용하여 센트로이드를 조금씩만 이동시킬 수 있다. 이를 통해 알고리즘의 속도가 보통 3배에서 4배로 향상되며, 메모리에 맞지 않는 거대한 데이터셋을 클러스터링하는 것이 가능해진다. Scikit-Learn은 MiniBatchKMeans 클래스에서 이 알고리즘을 사용할 수 있다. mini-batch K-Means 알고리즘은 일반적인 K-Means 알고리즘보다 훨씬 빠르지만, 일반적으로 군집의 수가 증가함에 따라 inertia이 약간 악화되는 편이다.

Finding the optimal number of clusters(최적의 클러스터 수 찾기)

    일반적으로 k를 어떻게 설정할지 알기 쉽지 않으며, 잘못된 값으로 설정하면 결과가 매우 나쁠 수 있다. 최소 inertia를 가진 모델을 선택할 수 있지 않을까 생각할 수 있지만, inertia는 k가 증가함에 따라 계속해서 감소하기 때문에 k를 선택하는 데 좋은 성능 측정 지표가 아니다.
    최적 클러스터 수를 선택하기 위한 기법 중 하나로 'elbow'를 찾는 방법이 있다. 이 기법은 비교적 대략적인 방법이며, 보다 정확한 접근 방법으로는 silhouette score(실루엣 계수)를 사용하는 것이 있다. 이는 모든 인스턴스의 평균 실루엣 계수로 이루어진 것이다. elbow는 급격하게 inertia 값이 줄어들다가, 완만해지기 시작한 지점이다. 그러나 위 예시와 같이 명확하게 elbow를 찾는 경우는 적다.

더보기

    silhouette score(실루엣 계수)는 (b - a) / max(a, b), 여기서 a는 동일 클러스터 내 다른 인스턴스와의 평균 거리이고, b는 가장 가까운 클러스터까지의 평균 거리이다.

silhouette score

    elbow방법보다 더 개선된 실루엣 계수(silhourtte score) 방법이 있다. 실루엣 계수란 군집끼리 잘 구분되어 있는 지 평가하는 척도이다. 실루엣 계수는 -1에서 1 사이의 값을 가질 수 있다. +1에 가까운 계수는 인스턴스가 자체 클러스터 내에 잘 있고 다른 클러스터와 멀리 떨어져 있음을 나타내며, 0에 가까운 계수는 클러스터 경계에 가깝다는 것을 의미하고, -1에 가까운 계수는 인스턴스가 잘못된 클러스터에 할당될 수 있다는 것을 나타낸다. 실루엣 스코어를 계산하려면 각 인스턴스의 실루엣 계수를 모두 평균해야 한다. 이 시각화는 이전 시각화보다 k = 4가 매우 좋은 선택임을 확인할 뿐만 아니라, k = 5도 꽤 좋으며 k = 6 또는 7보다 훨씬 나은 선택임을 확인할 수 있다. 그러나 이는 inertia을 비교할 때는 알아챌 수 없다.

    각 인스턴스의 실루엣 계수를 클러스터와 계수 값에 따라 정렬하여 플롯하면 더 유익한 시각화를 얻을 수 있습니다. 이를 실루엣 다이어그램(silhouette diagram)이라고 합니다. 수직 점선은 각 클러스터 수에 대한 실루엣 스코어를 나타낸다. 위와 같은 경우 k = 5가 수직 점선을 모두 넘었으며, 면적(부피)이 모두 비슷하기에 가장 좋은 클러스터 수라는 것을 알 수 있다.

K-Means의 한계

    K-Means 알고리즘은 최적 솔루션을 얻기 위해 여러 번 실행해야 하며, 클러스터 수를 지정하는 것이 번거로울 수 있다. 또한, 클러스터의 크기, 밀도, 또는 비구 형 모양이 다양한 경우에는 K-Means이 잘 작동하지 않을 수 있다. 이러한 경우에는 데이터에 따라 다른 클러스터링 알고리즘이 더 나은 성능을 발휘할 수 있다. 또한, 입력 기능을 스케일링하지 않으면 클러스터가 늘어져 있을 수 있고, K-Means의 성능이 저하될 수 있다. 이를 방지하기 위해 입력 기능을 스케일링하는 것이 중요하며, 일반적으로 이는 성능을 향상시킬 수 있다.

더보기

    준지도학습은 정답이 없기에 매번 다른 결과가 나올 수 있다.


Clustering Applications

이미지 분할(Image Segmentatio)을 위해 클러스터링 사용

from matplotlib.image import imread # or from imageio import imread
image = imread(os.path.join("images", "unsupervised_learning", "ladybug.png"))
image.shape	#(533, 800, 3) 너비, 높이, 색상

X = image.reshape(-1, 3)
kmeans = KMeans(n_clusters = 8).fit(X)
segmented_img = kmeas.cluster_centers_[kmeans.labels_]
segmented_img = segmented_img.reshape(image.shape)

    이미지 세분화(Image segmentation)는 이미지를 여러 세그먼트로 분할하는 작업을 말한다. 시맨틱 세그먼테이션은(semantic segmentation) 동일한 객체 유형에 속하는 모든 픽셀을 동일한 세그먼트에 할당하는 반면, 인스턴스 세그먼테이션(instance segmentation)은 동일한 개별 객체에 속하는 모든 픽셀을 동일한 세그먼트에 할당한다. 이와 달리, 간단한 컬러 세그먼테이션(color segmentation)은 유사한 색상을 가진 픽셀을 동일한 세그먼트에 할당한다. 이미지를 로드하면 각 픽셀은 0.0에서 1.0 사이(혹은 0과 255 사이)의 빨강, 녹색, 파랑 (RGB)강도를 가지는 3D 벡터로 구성된다. 이를 K-Means을 이용하여 클러스터링하여 RGB 색상의 긴 목록을 얻을 수 있다.

전처리(Preprocessing)를 위해 클러스터링 사용

from sklearn.pipeline import Pipeline

pipeline = Pipeline([
   ("kmeans", KMeans(n_clusters = 50)),
   ("log_reg", LogisticRegression())
])
pipeline.fit(X_train, y_train)
pipeline.score(X_test, y_test)	# 0.9777777777

    이미지 분류에 있어 클러스터링은 지도 학습 알고리즘 전의 효과적인 전처리 단계로 활용될 수 있다. 예를 들어, 0부터 9까지의 숫자를 나타내는 1,797개의 회색조 8x8 이미지를 포함한 간단한 MNIST와 유사한 데이터셋에 대해 클러스터링을 사용하여 차원을 축소할 수 있다. 먼저 데이터셋을 로드하고 훈련 및 테스트 세트로 분할한 후 로지스틱 회귀 모델을 적용한다. 이후 전처리 단계로 K-Means을 사용하여 성능을 향상시킬 수 있다. 위에서 훈련 세트를 50개의 클러스터로 클러스터링하고 이미지를 해당 클러스터와의 거리로 대체하는 파이프라인을 생성할 수 있다. 이렇게 하면 오류율이 약 30% 감소할 수 있다.

from sklearn.model_selection import GridSearchCV

param_grid = dict(kmeans__n_clusters = range(2, 100))
grid_clf = GridSearchCV(pipeline, param_grid, cv = 3, verbose = 2)
grid_clf.fit(X_train, y_train)

grid_clf.best_params_	# {'kmeans__n_clusters': 99}
grid_clf.score(X_train, y_train) # 0.982222222

    그러나 우리는 클러스터 수 k를 임의로 선택했다. 분류 파이프라인에서 K- Means은 전처리 단계에 불과하므로 k의 적절한 값을 찾는 것은 훨씬 간단하다. 실루엣 분석을 수행하거나 inertia을 최소화할 필요가 없으며, 최상의 분류 성능을 보이는 k가 최적의 값이다. 

준지도 학습(Semi-Supervised Learning)을 위한 클러스터링 사용

n_labeled = 50
log_reg = LogisticRegression()
log_reg.fit(X_train[:n_labeled], y_train[:n_labeled])

log_reg.score(X_test, y_test)	# 0.83333333

k = 50
kmeans = KMeans(n_clusters = k)
X_digits_dist = kmeans.fit_transform(X_train)
representative_digit_idx = np.argmin(X_digits_dist, axis = 0)
X_representative_digits = X_train[representative_digit_idx]

log_reg = LogisticRegression()
log_reg.fit(X_representative_digits, y_representative_digits)
log_reg.score(X_test, y_test)	# 0.9222222

y_train_propagated = np.empty(len(X_train), dtype = np.int32)
for i in range(k):
	y_train_propagated[kmeans.labels_ == i] = y_representative_digits[i]

log_reg = LogisticRegression()
log_reg.fit(X_train, y_train_propagated)
log_reg.score(X_test, y_test)	# 0.9333333

percentile_closest = 20

X_cluster_dist = X_digits_dist[np.arange(len(X_train)), kmeans.labels_]
for i in range(k):
   in_cluster = (kmeans.labels_ == i)
   cluster_dist = X_cluster_cist[in_cluster]
   cutoff_distance = np.percentile(cluster_dits, percentile_closest)
   above_cutoff = (X_cluster_dist > cutoff_distance)
   X_cluster_dist[in_cluster & above_cutoff] = -1
   
partially_propagated = (X_cluster_dist != -1)
X_train_partially_propagated = X_train[partially_propagated]
y_train_partially_propagated = y_train_propagated[partially_propagated]

log_reg = LogisticRegression()
log_reg.fit(X_train_partially_propagated, y_train_partially_propagated)
log_reg.score(X_test, y_test)	# 0.94

    클러스터링의 또 다른 사용 사례는 레이블이 거의 없는 인스턴스가 많은 준지도 학습에서 활용된다. 예를 들어, 손글씨 숫자 데이터셋에서 50개의 레이블이 지정된 인스턴스를 샘플로 사용하여 로지스틱 회귀 모델을 훈련할 수 있다. 우선, 훈련 세트를 50개의 클러스터로 클러스터링한 다음 각 클러스터마다 중심에 가장 가까운 이미지를 찾는다. 이러한 이미지를 대표 이미지라고 부른다. 이제 50개의 레이블이 지정된 인스턴스만 있는 데이터셋이지만 무작위 인스턴스가 아닌 각각이 해당 클러스터의 대표 이미지이다. 성능이 어떻게 향상되는지 살펴보면, 83.3%의 정확도에서 92.2%로 증가하는 것을 볼 수 있다. 그러나 여전히 모델을 50개의 인스턴스로만 훈련하고 있다. 동일한 클러스터에 있는 다른 모든 인스턴스에 레이블을 전파하는 경우, 합리적인 정확도 향상을 얻을 수 있지만 놀라운 성과는 아니다. 문제는 각 대표 인스턴스의 레이블을 클러스터 내에 위치한 클러스터 경계에 가까운 인스턴스를 포함하여 모두 전파했으며, 이들은 잘못 레이블이 지정될 가능성이 높다. 이를 해결하기 위해 중심에 가장 가까운 인스턴스의 20%에만 레이블을 전파해 보면, 평균 5개의 예제만 사용하여 94.0%의 정확도를 달성할 수 있다. 이는 완전히 레이블이 지정된 손글씨 숫자 데이터셋에서 로지스틱 회귀의 성능(96.9%)과 거의 비슷하다.

DBSCAN

from sklearn.cluster import DBSCAN
from sklearn.datasets import make_moons

X, y = make_moons(n_samplese = 1000, noise = 0.05)
dbscan = DBSCAN(eps = 0.05, min_samples = 5)
dbscan.fit(X)

dbscan.labels_
# array([0, 2, -1, -1, 1, 0, 0, 0, ..., 3, 2, 3, 3, 4, 2, 6, 3])

    DBSCAN은 고밀도의 연속적인 영역을 클러스터로 정의한다. 이 알고리즘은 모든 클러스터가 충분히 밀도가 높고 낮은 밀도 영역에 의해 잘 구분된 경우에 잘 작동한다. (클러스터 인덱스가 -1인 경우 이는 이상치로 간주된다.)

  • 각 인스턴스에 대해 알고리즘은 인스턴스로부터 작은 거리 ε(엡실론) 내에 위치한 인스턴스 수를 계산한다. 이 영역을 인스턴스의 ε-neighborhood이라고 한다.
  • 인스턴스가 ε-neighborhood (자체 포함)에 최소 min_samples 인스턴스를 갖고 있는 경우 핵심 인스턴스로 간주된다. 즉, 핵심 인스턴스는 밀집된 지역에 위치한 인스턴스이다.
  • 코어 인스턴스 인근의 모든 인스턴스는 동일한 클러스터에 속한다. 이 이웃에는 다른 핵심 인스턴스가 포함될 수 있다. 따라서 인접한 코어 인스턴스의 긴 시퀀스가 단일 클러스터를 형성한다.
  • 핵심 인스턴스가 아니고 인접 인스턴스도 없는 모든 인스턴스는 예외로 간주된다.

    위 예시는 K-Means가 잘 하지 못하지만, DBSCAN이 잘할 수 있는 분포이다.

그외

      그 외 clustering 알고리즘은 아래 예시들이 있다.

  • Agglomerative clustering
  • BIRCH
  • Mean-Shift
  • Affinity propagation
  • Spectral clustering

Gaussian Mixture Model (GMM)

     가우시안 혼합 모델(Gaussian Mixture Model, GMM)은 여러 개의 가우시안 분포로부터 생성된 것으로 가정하는 확률적 모델이다. 각 클러스터는 일반적으로 타원 형태를 가지며, 다른 타원의 형태, 크기, 밀도 및 방향을 가질 수 있다. 각 인스턴스는 하나의 가우시안 분포에서 생성되었다고 가정하지만, 어떤 분포에서 생성되었는지, 그리고 해당 분포의 매개변수가 무엇인지는 알 수 없다. 가장 간단한 GMM 변형 중 하나는 GaussianMixture 클래스에 구현된 것으로, 미리 알고 있어야 하는 가우시안 분포의 수 k가 있다. 데이터셋 X는 다음의 확률적 과정을 통해 생성되었다고 가정하자. 이 확률적 과정은 여러 가우시안 분포로부터 몇 번이고 반복적으로 생성될 수 있도록 설계되었다.

  • 각 인스턴스에 대해 k개의 클러스터 중에서 무작위로 클러스터가 선택된다. j번째 클러스터를 선택할 확률은 클러스터의 가중치 𝜙에 의해 정의된다. i번째 인스턴스에 대해 선택된 클러스터의 인덱스는 z로 표시된다.
  •  i번째의 z = j이면 i번째 인스턴스가 j번째 클러스터에 할당되었음을 의미한다. 이 i번째 인스턴스의 위치 x는 평균, µ 및 j번째 공분산 행렬 Σ를 사용하여 가우스 분포에서 무작위로 샘플링된다. 이는 x~N(μ(j) , Σ(j))로 표시된다.

    위의 그림에서 원은 랜덤 변수를 나타내며, 네모 상자는 모델의 매개변수로서의 고정된 값들을 나타낸다. 큰 직사각형은 플레이트(plate)로, 그 내용물이 여러 번 반복된다는 것을 나타낸다. 플레이트 우측 하단의 숫자는 해당 플레이트 내용물이 몇 번 반복되는지를 나타낸다. 따라서 𝑚개의 랜덤 변수 𝑧𝑖 (𝑧1에서 𝑧𝑚까지)와 𝑚개의 랜덤 변수 𝑥𝑖가 있다. 또한 𝑘개의 평균(𝜇𝑗) 및 𝑘개의 공분산 행렬(𝚺𝑗)이 있다. 중요한 변수들을 담는 가중치 벡터 𝛟 가 있고, 모든 가중치(𝜙1에서 𝜙𝑘까지)를 포함하는 하나의 가중치 벡터(𝛟)가 있다. 각 변수 𝑧𝑖는 가중치 벡터 𝛟에 의해 가중치가 지정된 범주형 분포에서 추출된다. 각 변수 𝑥𝑖는 해당 클러스터 𝑧𝑖에 의해 정의된 평균 및 공분산 행렬을 사용하여 정규 분포에서 추출된다. 실선 화살표는 조건부 종속성을 나타낸다. 예를 들어 각 랜덤 변수 𝑧𝑖의 확률 분포는 가중치 벡터 𝛟에 의존한다. 화살표가 플레이트 경계를 넘어가면 해당 플레이트의 모든 반복에 적용된다는 것을 의미한다. 예를 들어, 가중치 벡터 𝛟은 모든 랜덤 변수 𝑥1에서 𝑥𝑚까지의 확률 분포를 조건부로 지정한다. 랜덤 변수 𝑧𝑖에서 𝑥𝑖로의 구불구불한 화살표는 스위치를 나타낸다. 즉, 𝑧𝑖의 값에 따라 인스턴스 𝑥𝑖가 다른 가우시안 분포에서 추출된다. 예를 들어, 𝑧𝑖 = 𝑗이면 𝑥𝑖 ~ 𝑁(𝜇𝑗, 𝚺𝑗)이다. 음영 처리된 노드는 해당 값이 알려져 있다는 것을 나타낸다. 따라서 이 경우에는 랜덤 변수 𝑥𝑖만이 알려진 변수(observed variables)로 간주되며, 알려지지 않은 랜덤 변수 𝑧𝑖는 잠재 변수(latent variables)로 간주된다. 데이터셋 𝑋가 주어졌을 때, 주로 가중치(𝛟) 및 분포 매개변수(𝜇1에서 𝜇𝑘 및 𝚺1에서 𝚺𝑘)의 추정을 시작하고자 한다. Scikit-Learn의 GaussianMixture 클래스를 사용하면 이를 매우 쉽게 수행할 수 있다.

from sklearn.mixture import GaussianMixture

gm = GaussianMixture(n_components = 3, n_init = 10)
gm.fit(X)

gm.weights_
# array([0.20965228, 0.4000662, 0.39028152])
gm.means_
# array([[3.39909717, 1.05933727],
#	[-1.40763984, 1.42710194],
#	[0.05135313, 0.07524095]])
gm.covariances_
# array([[[ 1.14807234, -0.03270354],
#	[-0.03270354, 0.95496237]],
#	[[0.63478101, 0.72969804],
#	[0.72969804, 1.1609872]],
#	[[0.68809572, 0.79608475],
#	[0.79608475, 1.21234145]]])

    위는 EM(Expectation Maximization) 알고리즘으로 가중치, 평균 및 공분산과 같은 클러스터 매개변수를 추정한다. 클러스터 매개변수를 무작위로 초기화한 다음 수렴될 때까지 두 단계를 반복한다. 첫 번째는 인스턴스를 클러스터에 할당하는 단계(Expectation Step)이며, 두 번째는 클러스터를 업데이트하는 단계(Maximization Step)이다. 이는 K-Mean과 유사하다. 그러나 클러스터링 맥락에서 EM은 클러스터 중심(𝛍1에서 𝛍𝑘까지)뿐만 아니라 그 크기, 모양, 방향(𝚺1에서 𝚺𝑘까지), 상대적 가중치(𝜙1에서 𝜙𝑘까지)를 찾는 K-평균의 일반화로 볼 수 있다.

    K-Mean과 달리 EM은 소프트 클러스터 할당 방식을 사용한다. 기대값 단계에서 각 인스턴스는 현재 클러스터 매개변수를 기반으로 해당 클러스터에 속할 확률을 추정한다. 이후 최대화 단계에서는 모든 데이터 인스턴스를 사용하여 각 클러스터를 업데이트하며, 각 인스턴스의 소속 확률에 따라 가중치를 부여한다. 이러한 확률은 클러스터가 해당 인스턴스에 대해 얼마나 책임져야 하는지를 나타낸다. EM은 여러 번 실행하여 최적의 솔루션을 찾아내야 하며, 이를 위해 n_init을 조절합니다. 주의할 점은 기본적으로 n_init이 1로 설정되어 있다.

 

gm.predict(X)
# array([2, 2, 1,..., 0, 0, 0])
gm.predict_proba(X)
# array([[2.32389467e-02, 6.77397850e-07, 9.76760376e-01],
#	[1.64685609e-02, 6.75361202e-04, 9.82856078e-01],
#	[2.0153533e-06,	9.99923053e-01, 7.49319577e-05],
#	...,
#	[9.99999571e-01, 3.13946075e-26, 4.28788333e-07],
#	[1.00000000e+00, 1.46454409e-41, 5.12459171e-16],
#	[1.00000000e+00, 8.02006365e-41, 2.27626238e-15]])

X_new, y_new = gm.sample(6)
X_new
# array([[2.95400315, 2.63680992],
#	[-1.16654575, 1.62792705],
#	[-1.39477712, -1.48511338],
#	[0.27221525, 0.690366],
#	[0.54095936, 0.48591934],
#	[0.38064009, -0.56240465])
y_new
# array([0, 1, 2, 2, 2, 2])
gm.score_samples(X)
# array([-2.60782346, -3.57106041, -3.33003479,..., -3.51352783, -4.39802535, -3.80743859])

     그러나 K-Means와 마찬가지로 EM도 나쁜 솔루션으로 수렴할 수 있으므로 여러 번 실행하여 최고의 솔루션을 유지해야 한다. 가우시안 혼합 모델은 각 클러스터의 위치, 크기, 모양, 방향 및 상대적 가중치를 추정한 후 모델이 각 인스턴스를 가장 가능성 있는 클러스터에 할당하거나 특정 클러스터에 속할 확률을 추정할 수 있다. 또한 이 모델은 새로운 인스턴스를 샘플링하거나 모델이 주어진 위치에서의 밀도를 추정할 수 있다. 모델은 올바른 클러스터 수를 알려준 경우에 훌륭한 솔루션을 찾은 것으로 나타났다. 그러나 차원이 많거나 클러스터가 많거나 인스턴스가 적은 경우 EM은 최적 솔루션으로 수렴하기 어려울 수 있다. 이를 해결하기 위한 방법 중 하나는 알고리즘이 학습해야 하는 매개변수의 수를 제한하는 것이다. 이는 클러스터가 가질 수 있는 형태 및 방향의 범위를 제한함으로써 달성될 수 있다. covariance_type 하이퍼파라미터를 사용하여 공분산 행렬에 대한 제약 조건을 설정할 수 있다.

  • spherical: 모든 클러스터가 구 형태이지만 크기가 다를 수 있음을 의미한다.
  • diag: 클러스터가 어떤 크기든지 가질 수 있지만 타원체의 축은 좌표 축에 평행해야 함을 의미한다.
  • tied: 모든 클러스터가 동일한 타원체 모양, 크기 및 방향을 가져야 함을 의미한다.
  • full: 각 클러스터가 제한 없는 모양, 크기 및 방향을 가질 수 있음을 의미한다. 기본적으로 covariance_type은 디폴트로 설정되어 있다.

Anomaly Detection Using Gaussian Mixtures

densities = gm.score_samples(X)
density_threshold = np.precentile(densities, 4)
anomalies = X[densities < density_threshold]

     이상 감지(Anomaly detection or outlier detection)는 주어진 데이터에서 일반적인 패턴에서 크게 벗어나는 인스턴스를 감지하는 작업을 의미한다. 이러한 이상치는 일반적으로 이상치 또는 이상값으로 지칭되며, 주어진 환경에서 일반적인 동작에서 벗어나는 것으로 간주된다.
     가우시안 혼합 모델을 사용한 이상 감지의 경우, 저밀도 영역에 위치한 인스턴스를 이상치로 간주한다. 즉, 데이터의 밀도가 낮은 지역에 있는 경우 해당 인스턴스를 이상치로 식별한다. 사용자는 어떤 밀도 임계값을 사용할지 정의해야 하며, 임계값 조절을 통해 잘못된 양성 또는 음성 결과를 최적화할 수 있다. 너무 많은 정상 제품이 불량으로 표시되는 경우 임계값을 낮출 수 있고, 반대로 불량 제품이 놓치는 경우 임계값을 높일 수 있다. 이러한 방식으로 가우시안 혼합 모델은 이상치를 식별하고 시스템의 정확성을 조절하는 데 활용된다.

Likelihood Function

     가능도 함수(Likelihood Function)는 확률과 가능도(우도)라는 용어가 영어에서 종종 혼용되지만, 통계학에서는 이 둘의 개념이 다르다. 어떤 통계 모델이 주어진 경우, '확률'은 미래 결과 x가 매개 변수 값 θ를 알고 있는 경우 얼마나 가능한지를 설명하고, '가능도'는 결과 x가 알려진 후에 특정 매개 변수 값 θ가 얼마나 가능한지를 설명한다. 예를 들어, 두 개의 평균이 각각 -4와 +1인 1차원 가우시안 혼합 모델을 고려할 때, 확률 밀도 함수(PDF)는 x의 함수로 고정된 θ로 정의되며, 가능도 함수는 θ의 함수로 고정된 x로 정의된다. 중요한 점은 가능도 함수가 확률 분포가 아니라는 것이다. 확률 분포를 x의 모든 가능한 값에 대해 적분하면 항상 1이 나오지만, 가능도 함수를 θ의 모든 가능한 값에 대해 적분하면 양의 어떤 값이 나올 수 있다.
    가능도 함수를 최대화하는 것은 로그 가능도 함수를 최대화하는 것과 동일하다. 게다가 로그는 엄격히 증가하는 함수이므로, 파라미터 θ가 로그 가능도를 최대화하면 우도도 최대화된다. 이러한 이유로 로그 가능도를 최대화하는 것이 훨씬 간편하다. 예를 들어 여러 독립적인 인스턴스를 관찰한 경우, 각 개별 우도 함수의 곱을 최대화해야 할 것이지만, 로그의 특성으로 인해 로그 가능도 함수의 합을 최대화하여 간단하게 처리할 수 있다. 추정된 모델 파라미터인 θ̂을 사용하여 우도 함수를 최대화하면, 이후 AIC와 BIC를 계산하는 데 사용되는 값인 𝐿 = ℒ(θ̂, 𝐗)를 얻을 수 있다. 이는 모델이 데이터에 얼마나 잘 맞는지를 평가하는 중요한 지표로 사용된다.

Selecting the Number of Clusters

     클러스터의 수를 선택하는 것은 K-Means와 같이 가우시안 혼합 모델 알고리즘에서도 필요한 작업이다. 그렇다면 어떻게 적절한 클러스터 수를 찾을 수 있을까? K-Means에서는 관성 또는 실루엣 점수를 사용하여 적절한 클러스터 수를 선택할 수 있었다. 그러나 가우시안 혼합 모델의 경우 클러스터가 구형이 아니거나 크기가 다를 때 이러한 지표들이 신뢰성이 떨어진다. 대신, 이론적인 정보 기준인 베이지안 정보 기준(Bayesian information criterion, BIC) 또는 아카이케 정보 기준(Akaike information criterion, AIC)을 최소화하는 모델을 찾아볼 수 있다. BIC와 AIC는 더 많은 학습 매개변수(예: 더 많은 클러스터)를 갖는 모델을 처벌하고 데이터를 잘 맞추는 모델을 장려하는 특성이 있다.

    보다시피 k=3일 때 BIC와 AIC 모두 가장 낮으므로 최선의 선택일 가능성이 높다.

Bayesian Gaussian Mixture Models

from sklearn.mixture import BayesianGaussianMixture
bgm = BayesianGaussianMixture(n_components = 10, n_init = 10)
bgm.fit(X)
np.round(bgm.weights_, 2)
# array([0.4, 0.21, 0.4, 0., 0. , 0. , 0. , 0. , 0. , 0. , 0. ])

    베이지안 가우시안 혼합 모델(Bayesian Gaussian Mixture Models)은 클러스터의 최적 수를 수동으로 찾는 대신 BayesianGaussianMixture 클래스를 활용하여 최적이 아닌 클러스터에 대해 가중치를 거의 0으로 할당할 수 있다. 이 클래스는 클러스터의 수를 일정 값 이상으로 설정하고 알고리즘이 불필요한 클러스터를 자동으로 제거하도록한다. 결과적으로 알고리즘은 자동으로 필요한 클러스터 수를 탐지하며, 이때의 클러스터는 이전의 결과와 거의 동일하다. 이 모델에서 클러스터 매개변수는 더 이상 고정된 값이 아니라 숨겨진 랜덤 변수로 취급된다. 따라서 이제 z는 클러스터 매개변수와 클러스터 할당을 모두 포함하게 된다.

    Beta 분포는 특정 범위 내에 있는 랜덤 변수를 모델링하는 데 사용되며, 스틱 브레이킹 프로세스(Stick-Breaking Process, SBP)는 클러스터 할당을 모델링한다. SBP는 새로운 인스턴스가 큰 클러스터에 더 자주 가입할 가능성이 있는 데이터 세트에 적합한 모델이다.

    베이지안 통계에서는 사전 확률 분포(prior)를 사용하여 잠재 변수에 대한 사전 지식을 인코딩할 수 있다. 예를 들어, 클러스터 수에 대한 사전 신념은 weight_concentration_prior 하이퍼파라미터를 통해 조절될 수 있다. 또한, 베이즈 정리는 데이터를 관측한 후 잠재 변수에 대한 확률 분포를 업데이트하는 방법을 제공한다. 그러나 가우시안 혼합 모델에서는 이러한 확률 분포의 분모를 계산하는 것이 어려워진다. 이를 극복하기 위한 한 가지 방법은 변이추론(Variational Inference)이다. 이 방법은 군집에 대한 근사치를 찾기 위해 자체 변이 매개변수를 가진 분포군을 선택하고 이를 최적화하여 근사치를 향상시키는 것이다.

    가우시안 혼합 모델은 일반적으로 타원형 형상의 클러스터에 잘 작동하지만 데이터셋의 형상이 다르면 예상치 못한 결과가 발생할 수 있다. 이 모델은 데이터셋에서 타원체를 찾아 8개의 서로 다른 클러스터를 생성하려고 노력했으며 두 개의 달을 식별하지 못한다.


Other Algorithms for Anomaly and Novelty Detection

     임의의 모양의 클러스터를 처리할 수 있는 몇 가지 클러스터링 알고리즘들 여러가지 존재한다.

  • PCA (및 inverse_transform() 메서드를 사용한 기타 차원 축소 기술)
  • Fast-MCD (minimum covariance determinant)
  • Isolation Forest
  • Local Outlier Factor (LOF)
  • One-class SVM

주섬주섬

  • Likelihood function는 클 수록 cluster 갯수는 작은 수록 좋다. 이 둘은 트레이드 오프 관계이다.
  • 종종 범위에서 벗어난 데이터가 있는 데, 의미가 없는 데이터를 anomaly 혹은 noise라고 한다.
반응형

댓글