비지도 학습이란?
실제로 사용할 수 있는 데이터는 대부분 레이블이 없습니다.
레이블이 없는 데이터를 이용해서 데이터가 어떻게 구성되어있는지를 알아내는 문제의 범주에 속합니다.
이번 포스트에서는 몇 가지 비지도 학습과 알고리즘을 알아보겠습니다.
- 군집 (clustering)
- 이상치 탐지 (outlier detection)
- 밀도 추정 (density estimation)
군집 (clustering)
비슷한 샘플을 구별해 하나의 클러스터 또는 비슷한 샘플의 그룹으로 할당하는 작업입니다.
분류처럼 각 샘플은 하나의 그룹에 할당됩니다.
하지만 분류와 달리 군집은 비지도학습입니다.
위 그림을 보면 왼쪽에는 각 샘플의 클래스가 구분되어 나타나 있습니다.
이 데이터셋은 레이블이 되어있습니다.
오른쪽은 동일한 데이터셋이지만 레이블이 없습니다.
따라서 분류 알고리즘을 이용할 수 없어 군집 알고리즘이 필요한 경우입니다.
이와 같은 상황에 쓰이는 군집 알고리즘은 다음과 같은 다양한 애플리케이션에 사용됩니다.
- 고객 분류
- 데이터 분석
- 차원 축소 기법
- 이상치 탐지
- 준지도 학습
- 검색 엔진
- 이미지 분할
클러스터에 대한 보편적 정의는 없고 실제로 상황에 따라 다릅니다.
알고리즘이 다르면 다른 종류의 클러스터를 감지합니다.
먼저 유명한 군집 알고리즘인 k-평균과 DBSCAN을 살펴보고 비선형 차원 축소, 준지도 학습, 이상치 탐지와 같은 애플리케이션을 알아보겠습니다.
k-평균
k-평균은 반복 몇 번으로 데이터셋을 빠르고 효율적으로 클러스터로 묶을 수 있는 간단한 알고리즘입니다.
아래 코드를 참고하세요.
k-평균 알고리즘
이런 알고리즘은 어떻게 작동하는 걸까요?
센트로이드가 주어진다면 모든 샘플에 가장 가까운 센트로이드의 클러스터를 할당할 수 있습니다.
반대로 모든 샘플의 레이블이 주어진다면 각 클러스터에 속한 샘플의 평균을 계산하여 모든 센트로이드를 쉽게 구할 수 있습니다.
레이블이나 센트로이드가 주어지지 않았을 때가 우리가 논의해야 할 사안입니다.
처음에는 센트로이드를 처음에는 램덤하게 설정하고,
그다음 샘플에 레이블을 할당하고 센트로이드를 업데이트하고,
다시 샘플에 레이블을 할당하고 센트로이드를 업데이트하는 식으로 센트로이드에 변화가 없을 때까지 반복합니다.
이 알고리즘은 제한된 횟수 안에 수렴하는 것을 보장합니다.
일반적으로 매우 작은 횟수 안에 수렴하고, 무한하게 반복되지 않습니다.
그림으로 보면 아래와 같은 방식으로 진행됩니다.
이 알고리즘이 수렴하는 것은 보장되지만 그것이 적절한 솔루션인지는 모릅니다.
이 여부는 센트로이드 초기화에 달려 있습니다.
센트로이드 초기화를 개선하여 이런 위험을 줄여야합니다.
센트로이드 초기화 방법
센트로이드 위치를 근사하게 알 수 있다면 (예를 들어 다른 군집 알고리즘을 먼저 실행합니다.)
init 매개변수에 센트로에드 리스트를 담은 넘파이 배열을 전달하고 n_init을 1로 설정할 수 있습니다.
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)
또 다른 방법은 랜덤 초기화를 다르게 하여 여러 알고리즘을 실행하고 가장 좋은 솔루션을 선택하는 것입니다.
랜덤 초기화 횟수는 n_init 매개변수로 조절합니다.
기본값은 10인데, fit() 메소드를 호출 시 전체 알고리즘이 10번 실행된다는 말입니다.
이 때도 뭐가 최적인지를 판단할 기준이 있어야겠죠?
이 때 사용하는 성능 지표는 각 샘플과 가장 가까운 센트로이드 사이의 평균 제곱 거리이며,
모델의 이너셔(inertia)라고 부릅니다.
KMeans 클래스는 알고리즘을 n_init 번 실행한 후 이너셔가 가장 낮은 모델을 반환합니다.
이 값을 확인하고 싶다면 inertia_ 인스턴스 변수를 확인해보면 됩니다.
kmeans.inertia_
score() 메소드는 이너셔의 음숫값을 반환합니다.
예측기의 score() 메소드가 사이킷런의 큰 값이 좋은 것이다 라는 규칙을 따라야하기 때문입니다.
데이비드 아서와 세르게이 바실비츠키가 2006년 논문에서 제안한
k-평균 알고리즘을 향상시킨 k-평균++ 알고리즘이 있습니다.
이 논문에서 다른 센트로이드와 거리가 먼 센트로이드를 선택하는 똑똑한 초기화 단계를 소개했습니다.
이 방법은 k-평균 알고리즘이 최적이 아닌 솔루션으로 수렴할 가능성을 크게 낮춥니다.
k-평균++ 초기화 알고리즘은 다음과 같습니다.
- 데이터셋에서 무작위로 균등하게 하나의 센트로이드 \(c^{(i)}\)를 선택합니다.
- \( D(x^{(i)})^{2} / \sum_{j=1}^{m} D(x^{(j)})^{2} \)의 확률로 샘플 \(x^{(i)}\)를 새로운 센트로이드 \(c^{(i)}\)로 선택합니다. 여기서 \(D(x^{(i)})\)는 샘플 \(x^{(i)}\)와 이미 선택된 가장 가까운 센트로이드까지의 거리입니다. 이 확률 분포는 이미 선택한 센트로이드에서 멀리 떨어진 샘플을 다음 센트로이드로 선택할 가능성을 높입니다.
- k개의 센트로이드가 선택될 때까지 이전 단계를 반복합니다.
KMeans 클래스는 기본적으로 이 초기화 방법을 사용합니다.
원래 방식을 쓰고싶다면 init 매개변수를 'random'으로 지정하면 됩니다. (그럴 필요성은 거의 없습니다.)
k-평균 속도 개선과 미니배치 k-평균
2013년 찰스 엘칸의 논문에서 k-평균 알고리즘에 대해 또 다른 중요한 개선을 제안했습니다.
불필요한 거리 계산을 많이 피함으로써 알고리즘의 속도를 상당히 높일 수 있습니다.
엘칸은 이를 위해 삼각 부등식을 사용했습니다. (두 점 사이의 직선은 항상 가장 짧은 거리가 됩니다.)
그리고 샘플과 센트로이드 사이의 거리를 위한 하한, 상한선을 유지합니다.
이 알고리즘은 KMeans 클래스에서 기본적으로 사용합니다.
원래 방식을 쓰고싶다면 algorithm 매개변수를 'full'로 지정합니다. (그럴 필요성 역시 거의 없습니다.)
또한 2010년 데이비드 스컬리의 논문에서 k-평균 알고리즘의 또 다른 변종이 제시되었습니다.
전체 데이터셋을 사용해 반복하지 않고 이 알고리즘은
각 반복마다 미니배치를 사용해 센트로이드를 조금씩 이동합니다.
이는 알고리즘의 속도를 일반적으로 3~4배 높입니다.
또한 메모리에 들어가지 않는 대량의 데이터셋에 군집 알고리즘을 적용할 수 있습니다.
사이킷런의 MiniBatchKMeans 클래스로 이를 이용할 수 있습니다.
KMeans 클래스처럼 이 클래스를 이용할 수 있습니다.
from sklearn.cluster import MiniBatchKMeans
minibatch_kmeans = MiniBatchKMeans(n_clusters=5)
minibatch_kmeans.fit(X)
데이터셋이 메모리에 들어가지 않을 때 쓰는 가장 간단한 방법은 점진적 PCA에서 봤던것처럼 memmap클래스를 이용하는 것입니다. 또는 MiniBatchKMeans 클래스의 partial_fit() 메소드에 한 번에 하나의 미니배치를 전달할 수 있습니다. 하지만 이 방식은 이것저것 할 일이 많습니다.
미니배치 k-평균 알고리즘이 일반 k-평균 알고리즘보다 훨씬 빠릅니다.
k가 증가할수록 속도 격차는 더더욱 벌어집니다.
하지만 미니배치 k-평균 알고리즘이 일반 k-평균 알고리즘보다 이너셔는 일반적으로 더 나쁩니다.
특히 클러스터의 개수가 증가할 때 그렇습니다.
최적의 클러스터 개수 찾기
k를 어떻게 설정할지는 어렵습니다.
k가 너무 작으면 별개의 클러스터를 합치고, k가 너무 크면 하나의 클러스터가 여러 개로 나뉩니다.
가장 작은 이너셔를 가진 모델을 선택하는 것도 올바른 해결방안이 아닙니다.
이너셔는 k가 증가함에 따라 점점 작아지므로 k를 선택할 때 좋은 선택 지표가 아닙니다.
그림에서 보듯이 이너셔는 k가 4까지 증가할 때 빠르게 줄어듭니다.
하지만 k가 더 증가하면 천천히 줄어듭니다.
이 그래프를 팔의 형태로 보면 k=4 지점이 팔꿈치처럼 포이므로 엘보라고 합니다.
k에 대한 정답을 모를 때 이 지점은 좋은 선택이 됩니다.
그런데 이 방법은 너무 엉성합니다.
더 정확한 방법은 실루엣 점수를 이용하는 것입니다.
이 값은 모든 샘플에 대한 실루엣 계수의 평균입니다.
각 샘플의 실루엣 계수는 \( (b - a) / max(a, b) \)로 계산합니다.
여기서 a는 동일한 클러스터에 있는 다른 샘플까지 평균 거리이고,
b는 현재 속한 클러스터를 제외하고 가장 가까운 클러스터까지 평균 거리입니다.
실루엣 계수는 -1에서 +1 사이의 값을 갖고,
+1에 가까우면 자신의 클러스터에 잘 속해 있고 다른 클러스터와는 멀리 있다는 것이고,
0에 가까우면 클러스터 경계에 위치한다는 것이고,
-1에 가까우면 샘플이 잘못된 클러스터에 할당됐다는 것입니다.
실루엣 점수는 사이킷런의 silhouette_score() 함수를 사용해서 구할 수 있습니다.
from sklearn.metrics import silhouette_score
silhouette_score(X, kmeans.labels_)
k가 4나 5일 때 성능이 가장 좋고, 오히려 k가 더 증가하면 실루엣 점수가 더 나빠짐을 알 수 있습니다.
이는 이너셔를 비교했을 때는 드러나지 않았던 정보입니다.
모든 샘플의 실루엣 계수를 할당된 클러스터와 계숫값으로 정렬해 그리면 더 많은 정보를 얻을 수 있습니다.
이를 실루엣 다이어그램이라고 합니다.
이 그래프의 높이는 클러스터가 포함하고 있는 샘플의 개수를 의미하고,
너비는 이 클러스터에 포함된 샘플의 정렬된 실루엣 개수를 나타냅니다.
수직 파선은 각 클러스터 개수에 해당하는 실루엣 점수를 나타냅니다.
한 클러스터의 샘플 대부분이 이 점수보다 낮은 계수를 가지만 클러스터의 샘플이 다른 클러스터랑 너무 가깝다는 것을 의미하므로 나쁜 클러스터입니다.
위 그래프에서는 k가 4일 때나 5일 때 대부분의 샘플이 파선을 넘어 뻗어있고 1.0에 근접합니다.
그런데 k가 4일 때는 인덱스 1의 클러스터가 너무 큽니다.
반면 k가 5일 때는 모든 클러스터의 크기가 비슷합니다.
따라서 k가 4일 때 전반적인 실루엣 점수가 k가 5일때보다 조금 높더라도
비슷한 크기의 클러스터를 얻을 수 있는 k=5를 선택하는 것이 좋습니다.
k-평균의 한계
k-평균에서는 최적이 아닌 솔루션을 피하려면 알고리즘을 여러 번 실행해야 하고 클러스터 개수를 지정해야 합니다.
꽤 번거로운 작업입니다.
또한 클러스터의 크기나 밀집도가 서로 다르거나 원형이 아닐 경우 잘 작동하지 않습니다.
군집을 사용한 이미지 분할
이미지 분할은 이미지를 세그먼트 여러 개로 분할하는 작업입니다.
시맨틱 분할에서는 동일한 종류의 물체에 속한 모든 픽셀은 같은 세그먼트에 할당됩니다.
시맨틱 또는 인스턴스 분할에서 최고 수준의 성능을 내려면 CNN을 사용한 복잡한 모델을 사용해야 합니다.
여기선 훨씬 쉬운 방법인 색상 분할을 수행하겠습니다.
동일한 색상을 가진 픽셀을 같은 세그먼트에 할당할 것입니다.
군집을 사용한 전처리
군집은 지도 학습 알고리즘을 적용하기 전에 전처리 단계로 사용할 수 있습니다.
군집을 사용한 준지도 학습
군집을 사용하는 또 다른 사례는 준지도 학습입니다.
레이블이 없는 데이터가 많고 레이블이 있는 데이터는 적을 때 사용합니다.
모델과 훈련 세트를 계속 향상하기 위해 능동 학습을 몇 번 반복할 수 있습니다.
여러 전략이 있는데 가장 널리 사용되는 것 중 하나는 불확실성 샘플링입니다.
작동 방식은 아래와 같습니다.
- 지금까지 수집된 레이블된 샘플에서 모델을 훈련합니다. 이 모델을 사용해 레이블되지 않은 모든 샘플에 대한 예측을 만듭니다.
- 모델이 가장 불확실하게 예측한 샘플(또는 모델을 가장 크게 바꾸는 샘플이나 모델의 검증 점수를 가장 크게 떨어뜨리는 샘플)을 전문가에게 보내 레이블을 붙입니다.
- 레이블을 부여하는 노력만큼의 성능 향상이 나오지 않을 때까지 반복합니다.
DBSCAN
이 알고리즘은 밀집된 연속적 지역을 클러스터로 정의합니다.
아래와 같은 방식으로 작동합니다.
- 알고리즘이 각 샘플에서 작은 거리인 \(\epsilon\) 내에 샘플이 몇 개 놓여 있는지 셉니다. 이 지역을 샘플의 \(\epsilon-이웃\)이라고 합니다.
- 자기 자신을 포함해 (\epsilon-이웃\) 내에 적어도 min_samples개 샘플이 있다면 이를 핵심 샘플로 간주합니다. 즉 핵심 샘플은 밀집된 지역에 있는 샘플입니다.
- 핵심 샘플의 이웃에 있는 모든 샘플은 동일한 클러스터에 속합니다. 이웃에는 다른 핵심 샘플이 포함될 수 있습니다. 따라서 다른 핵심 샘플의 이웃의 이웃은 계속해서 하나의 클러스터를 생성합니다.
- 핵심 샘플이 아니고 이웃도 아닌 샘플은 이상치로 판단합니다.
이 알고리즘은 모든 클러스터가 충분히 밀집되어 있고 밀집되지 않은 지역과 잘 구분될 때 좋은 성능을 냅니다.
사이킷런의 DBSCAN 클래스를 쓰면 간편하게 이 알고리즘을 이용할 수 있습니다.
다른 군집 알고리즘
사이킷런에는 아래와 같은 여러 군집 알고리즘들이 더 있습니다.
- 병합 군집
- BIRCH
- 평균-이동
- 유사도 전파
- 스펙트럼 군집
가우시안 혼합
가우시안 혼합 모델(GMM)은 밀집도 추정, 군집, 이상치 탐지에 사용합니다.
GMM은 샘플이 파라미터가 알려지지 않은 여러 개의 혼합된 가우시안 분포에서 생성되었다고 가정하는 확률 모델입니다. 쉽게 설명하면, 아래와 같은 분포를 가진 데이터가 있다고 하겠습니다.
위 분포를 하나의 덩어리라고 생각할 수도 있지만, 아래와 같이 여러 가우시안 분포가 결합된 형태라고도 생각할 수 있습니다.
하나의 가우시안 분포에서 생성된 모든 샘플은 하나의 클러스터를 형성합니다.
우리는 이 모델을 이용해서 가우시안 분포의 수가 k개라고 했을 때
전체 분포에 대한 하위집단의 분포의 비율인 가중치 벡터 \(\phi\)와
하위집단 각각의 분포의 파라미터 \(\mu^{(1)}\)에서 \(\mu^{(k)}\)까지와 \(\Sigma^{(1)}\)에서 \(\Sigma^{(k)}\)까지를 추정합니다.
사이킷런의 GaussianMixture 클래스를 이용해보겠습니다.
가우시안 혼합을 이용한 이상치 탐지
가우시안 혼합 모델을 이상치 탐지에 사용하는 방법은 매우 간단합니다.
밀도가 낮은 지역에 있는 모든 샘플을 이상치로 볼 수 있습니다.
이렇게 하기 위해선 사용할 밀도 임곗값을 정해야 합니다.
다음은 네 번째 백분위수를 밀도 임곗값으로 사용하여 이상치를 구분하는 코드입니다.
densities = gm.score_samples(X)
density_threshold = np.percentile(densities, 4)
anomalies = X[densities < density_threshold]
아래 그림에서 이상치가 별 모양으로 표시돼 있습니다.
클러스터 개수 선택하기
k-평균처럼 GaussianMixture 알고리즘은 클러스터의 개수를 지정해야 합니다.
가우시안 혼합에서는 정의된 BIC나 AIC와 같은 이론적 정보 기준을 최소화하는 모델을 찾습니다.
\[ BIC = log(m)p - 2log(\hat{L}) \]
\[ AIC = 2p - 2log(\hat{L}) \]
위 식에서 \(m\)은 샘플의 개수이고, \(p\)는 모델이 학습할 파라미터 개수이고, \(\hat{L}\)은 모델의 가능도 함수의 최댓값입니다.
BIC와 AIC는 모두 학습할 파라미터가 많은(즉 클러스터가 많은) 모델에게 벌칙을 가하고 데이터에 잘 학습하는 모델에게 보상을 더합니다.
확률과 가능도가 헷갈릴 수 있으므로 한 번 설명하고 넘어가겠습니다.
확률은 파라미터 \(\theta\)인 확률 모델이 주어지면 미래 출력 \(x\)가 얼마나 그럴듯한지 설명합니다.
가능도는 출력 \(x\)를 알고 있을 때 특정 파라미터 값 \(\theta\)가 얼마나 그럴듯한지 설명합니다.
bic()와 aic() 메소드를 사용해 BIC와 AIC를 계산할 수 있습니다.
gm.bic(X)
gm.aic(X)
여러 가지 클러스터 개수 k에 대한 AIC와 BIC를 보여줍니다.
여기서 볼 수 있듯이 k=3이 최선의 선택으로 보입니다.
베이즈 가우시안 혼합 모델
최적의 클러스터 개수를 수동으로 찾지 않고
불필요한 클러스터의 가중치를 0으로 가깝게 만드는 BayesianGaussianMixture 클래스를 쓸 수 있습니다.
이 때 클러스터 개수 n_components를 최적의 클러스터 개수보다 크다고 믿을 만한 값으로 지정합니다.
가우시안 혼합 모델은 타원형 클러스터에 잘 작동합니다.
하지만 다른 모양을 가진 데이터셋에 훈련하면 나쁜 결과를 얻게 될 것입니다.
예를 들어 반달 데이터셋을 군집하기 위해 베이즈 가우시안 혼합 모델을 사용하면 아래와 같은 상황이 벌어집니다.
이 알고리즘은 필사적으로 타원을 찾습니다.
2개가 아니라 8개의 클러스터를 찾았습니다.
밀도 추정은 너무 나쁘지 않아서 이 모델은 이상치 감지를 위해서는 쓸 수 있습니다.
이상치 탐지와 특이치 탐지를 위한 알고리즘
사이킷런에는 이상치 탐지와 특이치 탐지를 위한 몇 가지 알고리즘이 더 있습니다.
- PCA(그리고 inverse_transform() 메소드를 가진 다른 차원 축소 기법)
- Fast-MCD
- 아이솔레이션 포레스트
- LOF
- one-class SVM
'DATA > 머신 러닝' 카테고리의 다른 글
[머신 러닝] 다층 퍼셉트론 (0) | 2022.01.22 |
---|---|
[머신 러닝] 퍼셉트론 (0) | 2022.01.21 |
[머신 러닝] 차원 축소 (0) | 2022.01.18 |
[머신 러닝] 서포트 벡터 머신 (support vector machine) (0) | 2022.01.17 |
[머신 러닝] 경사 하강법 (Gradient descent) (0) | 2022.01.16 |