- 참고 : 핸즈온 머신러닝 2판
비지도 학습
- 지도학습에 많은 발전이 이루어졌지만 가장 잠재력이 큰 분야
- 군집
- 비슷한 샘플을 클러스터로 모음, 군집은 데이터 분석, 고객 분류, 추천 시스템, 검색 엔진, 이미지 분할, 준지도 학습, 차원 축소 등에 사용할 수 있는 도구
- 이상치 탐지
- 정상 데이터가 어떻게 보이는지를 학습, 그다음 비정상 샘플을 감지하는 데 사용
- 제조 라인에서 결함 제품을 감지하거나 시계열 데이터에서 새로운 트렌드를 찾음
- 밀도 추정
- 데이터셋 생성 확률 과정의 확률 밀도 함수(PDF)를 추정
- 밀도 추정은 이상치 탐지에 널리 사용
- 밀도가 매우 낮은 영역에 놓인 샘플이 이상치일 가능성이 높음, 데이터 분석과 시각화에도 유용
군집
- 비슷한 샘플을 구별해 하나의 클러스터 또는 비슷한 샘플의 그룹으로 할당하는 작업
- 분류와 같이 각 샘플은 하나의 그룹에 할당됨, 분류와 다르게 군집은 비지도 학습방법
-
- 왼쪽은 iris 데이터셋, 클래스 구분이 잘 되어있음
- 오른쪽은 같은 데이터 셋이지만 구분이 없는 상태
- 군집이 사용되는 예시
- 고객 분류
- 고객을 구매 이력이나 웹사이트 내 행동 기반으로 클러스터로 모을 수 있음
- 이는 고객이 누구인지, 고객이 무엇을 원하는지 이해하는 데 도움이 됨
- 고객 그룹 각각에 제품 추천, 마케팅 전략을 다르게 적용할 수 있음
- 데이터 분석
- 새로운 데이터셋을 분석할 때 군집 알고리즘을 실행하고 각 클러스터를 따로 분석하면 도움이 됨
- 차원 축소 기법
- 한 데이터셋에 군집 알고리즘을 적용하면 각 클러스터에 대한 샘플의 친화성을 측정할 수 있음
- 각 샘플의 특성 벡터 x는 클러스터 친화성의 벡터로 바꿀 수 있음
- k개의 클러스터가 있다면 이 벡터는 k차원이 됨
- 일반적으로 벡터는 원본 특성 벡터보다 훨씬 저차원이지만 이후의 분석을 위한 충분한 정보를 가질 수 있음
- 이상치 탐지
- 모든 클러스터에 친화성이 낮음 샘플은 이상치일 가능성이 큼
- 제조 분야의 결함을 감지, 부정 거래 감지 등에 활용 됨
- 준지도 학습
- 레이블된 샘플이 적다면 군집을 수행하고 동일한 클러스터에 있는 모든 샘플에 레이블을 생성할수 있음
- 이 방법을 통해 지도 학습 알고리즘에 필요한 레이블을 생성하여 성능 향상 가능
- 검색 엔진
- 제시된 이미지와 비슷한 이미지를 찾아줌, 이런 시스템을 구축하려면 먼저 데이터 베이스에 있는 모든 이미지에 군집 알고리즘을 적용
- 사용자가 찾으려는 이미지를 제공하면 훈련된 군집 모델을 사용해 이미지의 클러스터를 찾음, 이후에 이 클러스터의 모든 이미지를 반환
- 이미지 분할
- 색을 기반으로 픽셀을 클러스터, 그다음 각 픽셀의 색을 해당 클러스터의 평균 색으로 변경
- 이미지에 있는 색상의 종류를 크게 줄이고 물체의 윤곽을 감지하기 쉬워져 물체 탐지 및 추적 시스템에서 이미지 분할을 많이 활용
- 고객 분류
k-mean
- 각 클러스터의 중심을 찾고 가장 가까운 클러스터에 샘플을 할당
- 찾을 클러스터 개수 k를 지정해야 함
- 예제
- 위 center을 기반으로 그린 보로노이 다이어그램
- 위 center을 기반으로 그린 보로노이 다이어그램
from sklearn.cluster import KMeans k = 5 kmeans = KMeans(n_clusters=k, random_state=42) y_pred = kmeans.fit_predict(X) ## label 확인 kmeans.labels_ >> array([4, 0, 1, ..., 2, 1, 0], dtype=int32) ## k=5 의 중심좌표 kmeans.cluster_centers_ >> array([[-2.80389616, 1.80117999], [ 0.20876306, 2.25551336], [-2.79290307, 2.79641063], [-1.46679593, 2.28585348], [-2.80037642, 1.30082566]])
- 샘플은 대부분 적절한 클러스터에 잘 할당됨, 하지만 몇몇 샘플은 레이블이 잘못 부여
- 실제 k-평균 알고리즘은 클러스터의 크기가 많이 다르면 잘 작동하지 않음
- 샘플을 클러스터에 할당할때 센트로이드까지 거리를 고려하는 것이 전부이기 때문
- 하드군집 샘플을 클러스터에 할당하는 것보다 클러스터마다 샘플에 점수를 부여하는 것이 유용할 수 있음 이를 소프트 군집 이라고 함
- 하드 군집은 각 샘플에 대해 가장 가까운 것을 선택
kmeans.transform(X_new)
array([[2.81093633, 0.32995317, 2.9042344 , 1.49439034, 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]])
- kmeans는 transform 매서드 샘플과 각 센트로이드 사이의 거리를 반환
- 이런 방식으로 고차원 데이터셋을 변환하면 k-차원 데이터셋 생성, 효율적인 비선형 차원 축소 방법이 될수 있음
k-mean 알고리즘
KMeans
클래스는 기본적으로 최적화된 알고리즘을 적용, 아래에서 설명하는 내용은 init='random', n_init=1, algorithm='full'로 선택한 경우 원래의 k-means 설명- 처음에는 센트로이드를 랜덤하게 선정, 그다음 샘플에 레이블을 할당하고 센트로이드를 업데이트
- 샘플에 레이블을 할당하고 센트로이드를 업데이트하는 식으로 센트로이드에 변화가 없을 때까지 계속 수행
- 제한된 횟수 안에 수렴하는 것으 보장, 무한 반복하지 않음
해당 알고리즘 계산 복잡도 : 샘플 개수(m), 클러스터 개수(k), 차원 개수(n)에 선형적이지만 군집할 수 있는 구조를 가질 경우에 해당
그렇지 않는 최악의 경우 계산 복잡도는 샘플 개수가 지수적으로 급증할수 있음, 일반적으로 k-means는 빠른 군집 알고리즘
센트로이드 초기화 방법
- 센트로이드 위치를 근사하게 알 수 있다면 (다른 군집 알고리즘을 먼저 실행하여 어느정도 센트로이드를 알고 있을 경우) 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, random_state=42) kmeans.fit(X) kmeans.inertia_ >> 211.598537258168
- 랜덤 초기화를 다르게 하여 여러 번 알고리즘을 실행하고 가장 좋은 솔루션을 선택하는 것
- 랜덤 초기화 횟수는 n_init 매개변수로 조절 가능, 기본값은 10
- 이너셔를 통해 가장 최적의 솔루션을 찾는 지표로 활용
- k-means는 현재는 k-means++ 초기화 방법을 기본으로 사용
k-means 속도 개선과 미니배치 k-means
- 불필요한 거리 계산을 많이 피함으로서 알고리즘의 속도를 상당히 높일 수 있음
- 현재는 k-means에서 기본적으로 사용됨
- 전체 데이터셋을 사용해 반복하지 않고 각 반복마다 미니배치를 사용해 센트로이드를 조금씩 이동 시키는 방식이 제안됨
- 일반적으로 알고리즘 속도를 3배에서 4배 정도 높임
- 메모리에 들어가지 않는 대량의 데이터셋에 군집 알고리즘을 적용
- sklearn에서 minibatchkmeans 클래스에 구현함
- 예제
from sklearn.cluster import MiniBatchKMeans minibatch_kmeans = MiniBatchKMeans(n_clusters=5, random_state=42) minibatch_kmeans.fit(X) minibatch_kmeans.inertia_ >> 211.652398504332
- 미니배치 k-means가 일반 k-means 보다 훨씬 빠름 하지만 이너셔는 일반적으로 안좋음
- 특히 클러스터의 개수가 증가할 때 더욱 안좋아지는 경향을 보임
- 데이터 셋이 크다면 일반적인 k-means보다는 minibatchkmeans가 더 좋을수 있음
최적의 클러스터 개수 찾기
-
- 이녀셔가 급격히 작아지는 elbow 지점 선택
- 정확한 방법은 아님
- 실루엣 스코어 비교
- 실루엣 스코어는 모든 샘플에 대한 실루엣 계수의 평균
- (b-a)/max(a,b)로 계산
- a : 동일한 클러스터에 있는 다른 샘플까지의 평균 거리
- b : 가장 가까운 클러스터까지 평균 거리
- -1 ~ +1 범위
- +1 에 가까우면 잘 분리된 결과
- 0 에 가까우면 클러스터의 경계에 위치
- -1에 가까우면 잘못된 클러스터에 할당
-
from sklearn.metrics import silhouette_score silhouette_score(X, kmeans.labels_) >> 0.655517642572828
-
- 이니셔 보다 k에 대한 다양한 정보를 볼수 있음
- 모든 샘플의 실루엣 계수를 할당된 클러스터와 실루엣 값으로 정렬하여 그리면 훨씬 많은 정보를 얻을 수 있습니다. 이를 실루엣 다이어그램이라고 함
- 높이 : 클러스터가 포함한 샘플의 수, 너비 : 이 클러스터에 포함된 샘플의 정렬된 실루엣 계수
- 수직 점선 : 클러스터 개수에 해당하는 실루엣 점수
실제로 좋은 클러스터를 선택할때 실루엣에서 유심히 봐야하는 것은 3개
-
-
-
- 그래프에 음수값이 없어야 함
- 너비가 각 클러스터 마다 비슷해야 함
- 모든 클러스터가 수직점선을 넘어야 함
-
-
k-평균의 한계
- 속도가 빠르고 확장이 용이
- 최적이 아닌 솔루션을 피하기 위해 알고리즘 여러 번 실행해야 함
- 클러스터 개수도 사전에 지정해야 함
- 원형 모양의 데이터가 아닌경우 잘 작동하지 않음
-
- 두 솔루션 모두 좋지않음, 데이터에 따라 잘 수행할수 있는 군집 알고리즘이 다름
- 타원형 클러스터에서는 가우시안 혼합 모델이 잘 작동
-
k-mean 실행하기 전에 입력 특성의 스케일을 맞추는 것이 중요, 그렇지 않으면 클러스터가 길쭉해짐
일반적으로 특성의 스케일을 맞추면 잘 구분됨
군집을 사용한 이미지 분할
- 이미지 분할은 이미지를 세그먼트 여러 개로 분할하는 작업
- 시멘틱 분할에서는 동일한 종류의 물체에 속한 모든 픽셀은 같은 세그먼트에 할당
- 색상분할 예제
from matplotlib.image import imread image = imread(os.path.join(images_path, filename)) image.shape >> (533, 800, 3) X = image.reshape(-1, 3) kmeans = KMeans(n_clusters=8, random_state=42).fit(X) segmented_img = kmeans.cluster_centers_[kmeans.labels_] segmented_img = segmented_img.reshape(image.shape) segmented_imgs = [] n_colors = (10, 8, 6, 4, 2) for n_clusters in n_colors: kmeans = KMeans(n_clusters=n_clusters, random_state=42).fit(X) segmented_img = kmeans.cluster_centers_[kmeans.labels_] segmented_imgs.append(segmented_img.reshape(image.shape))
군집을 사용한 전처리
- 지도 학습 알고리즘 적용하기 전에 전처리 단계로 사용하기 좋음
- 예제
- 훈련셋과 테스트 세트로 분할
from sklearn.linear_model import LogisticRegression log_reg = LogisticRegression(multi_class="ovr", solver="lbfgs", max_iter=5000, random_state=42) log_reg.fit(X_train, y_train) log_reg_score = log_reg.score(X_test, y_test) log_reg_score >> 0.9688888888888889
- 로지스틱 회귀 모델 훈련, 테스트 세트의 정확도 평가
from sklearn.model_selection import train_test_split X_train, X_test, y_train, y_test = train_test_split(X_digits, y_digits, random_state=42)
- 클러스터를 전처리에 사용하는 예제
from sklearn.pipeline import Pipeline
pipeline = Pipeline([
("kmeans", KMeans(n_clusters=50, random_state=42)),
("log_reg", LogisticRegression(multi_class="ovr", solver="lbfgs", max_iter=5000, random_state=42)),
])
pipeline.fit(X_train, y_train)
pipeline_score = pipeline.score(X_test, y_test)
pipeline_score
0.9777777777777777
- 훈련 세트를 50개의 클러스터로 모음, 그 후에 이미지를 50개 클러스터까지 거리로 변경, 마지막에 로지스틱 회귀 모델 적용
- grid search를 통해 최적의 parameter 찾기
```python
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)
군집을 사용한 준지도 학습
- 레이블이 없는 데이터가 많고 레이블이 있는 데이터가 적을 때 사용
- 예제
- 훈련 세트를 50개의 클러스터로 모으고 그다음 각 클러스터에서 센트로이드에 가장 가까운 이미지를 찾음, 이런 이미지를 대표 이미지 라고 함
k = 50 kmeans = KMeans(n_clusters=k, random_state=42) 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] y_train[representative_digit_idx] y_representative_digits = np.array([ 0, 1, 3, 2, 7, 6, 4, 6, 9, 5, 1, 2, 9, 5, 2, 7, 8, 1, 8, 6, 3, 1, 5, 4, 5, 4, 0, 3, 2, 6, 1, 7, 7, 9, 1, 8, 6, 5, 4, 8, 5, 3, 3, 6, 7, 9, 7, 8, 4, 9])
- 대표 이미지를 출력하고 수동으로 레이블 생성
- 각 클러스터를 대표하는 이미지를 통해 학습
log_reg = LogisticRegression(multi_class="ovr", solver="lbfgs", max_iter=5000, random_state=42) log_reg.fit(X_representative_digits, y_representative_digits) log_reg.score(X_test, y_test) >> 0.09555555555555556
- 셈플에 레이블을 부여하는 것은 비용이 많이 소요, 무작위 샘플 대신 대표 샘플에 레이블을 할당하는 것이 좋음
- 레이블을 동일한 클러스터에 있는 모든 샘플로 전파하는 방법을 레이블 전파라고 함
- 아래 방법은 전체가 아닌 75번째 백분위수까지만 전파하는 코드
- 전체 전파시 이상치 생성 됨
percentile_closest = 75 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_dist[in_cluster] cutoff_distance = np.percentile(cluster_dist, 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]
n_labeled = 50 log_reg = LogisticRegression(multi_class="ovr", solver="lbfgs", random_state=42) log_reg.fit(X_train[:n_labeled], y_train[:n_labeled]) log_reg.score(X_test, y_test) >> 0.83333333334
훈련
log_reg = LogisticRegression(multi_class="ovr", solver="lbfgs", max_iter=5000, random_state=42)
log_reg.fit(X_train_partially_propagated, y_train_partially_propagated)
log_reg.score(X_test, y_test)
0.94
- 모델과 훈련 세트를 지속적으로 향상하기 위해 다음 단계로 능동 학습을 몇 번 반복할 수 있음
1. 분류기의 확신이 부족한 샘플에 수동으로 레이블을 부여합니다. 가능하면 다른 클러스터에서 샘플을 선택합니다.
2. 추가된 레이블을 사용해 새로운 모델을 훈련합니다.
### DBSCAN
- 밀집된 연속적 지역을 클러스터로 정의
- 작동방식
1. 각 샘플에서 작은 거리인 입실론 내에 샘플이 몇 개 놓여 있는지 계산, 이를 입실론 이웃
2. 자기 자신은 포함해 입실론 이웃 내에 적어도 min_samples개 샘플이 있다면 이를 핵심 샘플로 간주, 핵심샘플은 밀집된 지역에 있는 샘플
3. 핵심 샘플의 이웃에 있는 모든 샘플은 동일한 클러스터에 속함, 이웃에는 다른 핵심 샘플이 포함될 수 있음, 따라서 핵심 샘플의 이웃의 이웃은 계속해서 하나의 클러스터 형성
4. 핵심 샘플도 아니고 이웃도 아닌 샘플은 이상치
- 모든 클러스터가 충분히 밀집되어 있고 밀집되지 않은 지역과 잘 구분됨
- DBSCAN sklearn 예제
```python
from sklearn.cluster import DBSCAN
dbscan = DBSCAN(eps=0.05, min_samples=5)
dbscan.fit(X)
dbscan.labels_[:10]
>> array([ 0, 2, -1, -1, 1, 0, 0, 0, 2, 5])
- 일부 샘플의 클러스터 인덱스는 -1, 이것은 알고리즘에서 이상치로 판단했다는 의미
- 핵심 샘플의 인덱스는 인스턴스 변수 core_sample_indices_에서 확인 가능
- 핵심 샘플 자체는 인스턴스 변수 components_에 저장
len(dbscan.core_sample_indices_) >> 808 dbscan.core_sample_indices_[:10] >> array([ 0, 4, 5, 6, 7, 8, 10, 11, 12, 13]) dbscan.components_[:3] >> array([[-0.02137124, 0.40618608], [-0.84192557, 0.53058695], [ 0.58930337, -0.32137599]])
- 위 그림에서 처럼 eps 거리를 0.05 보다 0.2로 증가해 샘플의 이웃 범위를 높이면 좋은 결과를 보임
- DBSCAN은 새로운 샘플에 대해 클러스터 예측을 할수 없음
- predict() 매소드 지원안하고 fit_predict()만 지원
- KNeighborsClassifier 에 DBSCAN 예제
- 샘플 몇 개를 전달하여 어떤 클러스터에 속할 가능성이 높은지 예측하고 각 클러스터에 대한 확률 추정
from sklearn.neighbors import KNeighborsClassifier knn = KNeighborsClassifier(n_neighbors=50) knn.fit(dbscan.components_, dbscan.labels_[dbscan.core_sample_indices_]) X_new = np.array([[-0.5, 0], [0, 0.5], [1, -0.1], [2, 1]]) knn.predict(X_new) >> array([1, 0, 1, 0]) knn.predict_proba(X_new) >> array([[0.18, 0.82], [1. , 0. ], [0.12, 0.88], [1. , 0. ]])
- 해당 분류기는 DBSCAN에서 얻은 핵심샘플에서만 훈련했지만 모든 샘플에서 훈련가능
- 최대 거리를 사용하면 두 클러스터에서 멀리 떨어진 샘플을 이상치로 분류 가능
- KNeighborsClassifier의 kneighbors() 메서드 사용, 해당 메서드에 샘플은 전달하면 훈련 세트에서 가장 가까운 k개 이웃의 거리와 인덱스를 반환
- DBSCAN은 클러스터의 모양과 개수에 상관없이 감지할 수 있음
- 이상치에 안정적이고 하이퍼파라미터가 두 개뿐(eps, min_samples)
- 클러스터 간의 밀집도가 크게 다르면 모든 클러스터를 올바르게 잡아내는 것은 불가능
- 계산 복잡도는 대략 O(m log m), 샘플 개수에 대해 거의 선형적으로 증가
다른 군집 알고리즘
- 병합군집
- 클러스터 계층을 밑바닥부터 쌓아 구성하는 방식
- 병합된 클러스터 쌍을 트리로 모두 그리면 클러스터의 이진 트리를 얻을 수 있음
- 대규모 샘플과 클러스터에 잘 확장되며 다양한 형태의 클러스터를 감지할 수 있음
- 특정 클러스터 개수를 선택하는 데 도움이 되는 유용한 클러스터 트리를 생성할 수 있음
- 예제
from sklearn.cluster import AgglomerativeClustering X = np.array([0, 2, 5, 8.5]).reshape(-1, 1) agg = AgglomerativeClustering(linkage="complete").fit(X) def learned_parameters(estimator): return [attrib for attrib in dir(estimator) if attrib.endswith("_") and not attrib.startswith("_")] learned_parameters(agg) >> ['children_', 'labels_', 'n_clusters_', 'n_connected_components_', 'n_features_in_', 'n_leaves_'] agg.children_ >> array([[0, 1], [2, 3], [4, 5]])
- BIRCH
- 대규모 데이터셋을 위해 고안 됨
- 특성 개수가 너무 많지 않다면 배치 k-평균보다 빠르고 비슷한 결과를 생성
- 훈련 과정에서 새로운 샘플을 클러스터에 빠르게 할당할 수 있는 정보를 담은 트리 구조를 만듬
- 해당 트리에 모든 샘플 저장 안함
- 제한된 메모리를 사용해 대용량 데이터셋을 다룰 수 있음
- 평균-이동
- 작동방식
- 각 샘플을 중심으로 하는 원을 그림
- 원마다 안에 포함된 모든 샘플의 평균을 계산
- 원의 중심을 평균점으로 이동
- 모든 원이 움직이지 않을 때까지 평균-이동 반복
- 지역의 최대 밀도를 찾을 때까지 높은 쪽으로 원을 이동
- DBSCAN과 유사한 특징이 존재, 모양과 개수에 상관없이 클러스터를 찾을 수 있음
- 하이퍼 파라미터로 밴드워스 라는 원 반경 존재, 이외에는 없음
- 국부적인 밀집도 추정에 의존, DBSCAN과 달리 평균-이동은 클러스터 내부 밀집도가 불균형할 때 여러 개로 나누는 경향이 존재
- 이 때문에 대규모 데이터셋에는 적합하지 않음
- 작동방식
- 유사도 전파
- 투표 방식을 사용. 샘플은 자신을 대표할 수 있는 비슷한 샘플에 투표
- 알고리즘이 수렴하면 각 대표와 투표한 샘플이 클러스터를 형성
- 크기가 다른 여러 개의 클러스터를 감지할 수 있음
- 계산 복잡도 O(m2), 대규모 데이터셋에는 적합하지 않음
- 스펙트럼 군집
- 샘플 사이의 유사도 행렬을 받아 저차원 임베딩 생성 (차원을 축소함)
- 저차원 공간에서 또 다른 군집 알고리즘을 사용 (sklearn 구현은 k-mean 사용)
- 복잡한 클러스터 구조를 감지하고 그래프 컷을 찾는 데 사용할 수 있음
- 예제
from sklearn.cluster import SpectralClustering sc1 = SpectralClustering(n_clusters=2, gamma=100, random_state=42) sc1.fit(X) sc2 = SpectralClustering(n_clusters=2, gamma=1, random_state=42) sc2.fit(X) np.percentile(sc1.affinity_matrix_, 95)
가우시안 혼합(GMM)
- 샘플이 파라미터가 알려지지 않은 여러 개의 혼합된 가우시안 분포에서 생성되었다고 가정하는 확률 모델
- 하나의 가우시안 분포에서 생성된 모든 샘플은 하나의 클러스터를 형성
- GMM은 다양한 변종이 존재, 가장 간단한 버전이 GaussianMixture 클래스에 구현되어 있음
- GaussianMixture 예제
- 해당 GMM은 기댓값-최대화(EM) 알고리즘을 사용
- 해당 알고리즘은 k-mean과 공통점이 많음
- 클러스터 파라미터를 랜덤하게 초기화하고 수렴 할때까지 두 단계를 계속 반복
- 먼저 샘플을 클러스터에 할당, 이를 기대값 단계
- 기대값 단계에서는 각 클러스터에 속할 확률을 예측
- 클러스터를 업데이트 수행, 이를 최대화 단계
- 최대화 단계에서 각 클러스터가 데이터셋에 있는 모든 샘플을 사용해 업데이트
- 클러스터에 속할 추정 확률로 샘플에 가중치가 적용
- 이 확률을 샘플에 대한 클러스터의 책임 이라고 함
- 최대화 단계에서 클러스터 업데이트는 책임이 가장 많은 샘플에 크게 영향을 받음
- k-mean처럼 EM이 나쁜 솔루션으로 수렴할수 있음, 여러 번 실행해서 가장 좋은 솔루션을 선택
# 수렴여부 gm.converged_ >> True # 반복 횟수 gm.n_iter_ >> 4
- 새로운 샘플을 가장 비슷한 클러스터에 할당하는 하드 군집 또는 특정 클러스터에 속할 확률을 예측할 수 있음 소프트 군집
- 하드 군집을 위해서는 predict(), 소프트 군집을 위해서는 predict_proba() 메서드 사용
gm.predict(X) >> array([0, 0, 1, ..., 2, 2, 2]) gm.predict_proba(X) >> array([[9.76741808e-01, 6.78581203e-07, 2.32575136e-02], [9.82832955e-01, 6.76173663e-04, 1.64908714e-02], [7.46494398e-05, 9.99923327e-01, 2.02398402e-06], ..., [4.26050456e-07, 2.15512941e-26, 9.99999574e-01], [5.04987704e-16, 1.48083217e-41, 1.00000000e+00], [2.24602826e-15, 8.11457779e-41, 1.00000000e+00]])
- GMM은 생성 모델임, 즉 모델에서 새로운 샘플을 생성할 수 있음
X_new, y_new = gm.sample(6) X_new >> array([[-0.86944074, -0.32767626], [ 0.29836051, 0.28297011], [-2.8014927 , -0.09047309], [ 3.98203732, 1.49951491], [ 3.81677148, 0.53095244], [ 2.84104923, -0.73858639]]) y_new >> array([0, 0, 1, 2, 2, 2])
- 주어진 위치에서 모델의 밀도를 추정할수 있음
- 이를 위해 score_samples() 사용
- 샘플이 주어지면 이 메서드는 그 위치의 확률 밀도 함수(PDF)의 로그를 예측, 점수 높을 수록 밀도 높음
gm.score_samples(X) >> array([-2.60768954, -3.57110232, -3.32987086, ..., -3.51347241, -4.39798588, -3.80746532])
- 해당 점수들의 지숫값을 계산하면 샘플의 위치에서 PDF 값을 얻을 수 있음, 값은 하나의 확률이 아닌 확률 밀도
- 샘플이 특정 지역 안에 속할 확률을 예측하려면 그 지역에 대해 PDF를 적분해야 함
- 클러스터 평균, 결정 경계, 밀도 등고선 시각화
- 해당 GMM은 기댓값-최대화(EM) 알고리즘을 사용
from sklearn.mixture import GaussianMixture gm = GaussianMixture(n_components=3, n_init=10, random_state=42) gm.fit(X) gm.weights_ >> array([0.39025715, 0.40007391, 0.20966893]) gm.means_ >> array([[ 0.05131611, 0.07521837], [-1.40763156, 1.42708225], [ 3.39893794, 1.05928897]]) gm.covariances_ >> array([[[ 0.68799922, 0.79606357], [ 0.79606357, 1.21236106]], [[ 0.63479409, 0.72970799], [ 0.72970799, 1.1610351 ]], [[ 1.14833585, -0.03256179], [-0.03256179, 0.95490931]]])
- 특성이나 클러스터가 많거나 샘플이 적을 때는 EM이 최적의 솔루션으로 수렴하기 어려움
- 어려움을 줄이기 위해 파라미터 개수를 제한해야 함, 클러스터의 모양과 방향의 범위를 제한
- sklearn 에서는 covariance_type을 통해 제어
- "spherial" : 모든 클러스터가 원형, 지름은 다를 수 있음(분산이 다름)
- "diag" : 클러스터는 크기에 상관없이 어떤 타원형도 가능, 타원의 축은 좌표축과 나란해야 함(공분산 행렬이 대각 행렬이여야 함)
- "tied" : 모든 클러스터가 동일한 타원 모양, 크기, 방향을 가짐(모든 클러스터는 동일한 공분산 행렬을 공유)
- 기본값은 "full"
- convariance_type 시각화
가우시안 혼합을 사용한 이상치 탐지
- 이상치 탐지는 보통과 많이 다른 샘플을 감지하는 작업
- gmm을 이상치 탐지에 사용하는 방법
- 밀도가 낮은 지역에 있는 모든 샘플을 이상치로 볼 수 있음, 이를 위해 임계값 설정 필요
- 예제
- 이와 비슷한 작업은 특이치 탐지, 해당 알고리즘은 이상치로 오염되지 않은 '깨끗한' 데이터셋에서 훈련한다는 것이 이상치 탐지와 다름
- 이상치 탐지는 이런 가정을 하지 않음, 실제로 이상치 탐지는 데이터셋 정제에 사용
- 이와 비슷한 작업은 특이치 탐지, 해당 알고리즘은 이상치로 오염되지 않은 '깨끗한' 데이터셋에서 훈련한다는 것이 이상치 탐지와 다름
densities = gm.score_samples(X) density_threshold = np.percentile(densities, 4) # 네 번째 백분위수를 임계값으로 사용 anomalies = X[densities < density_threshold]
클러스터 개수 선택하기
- k-mean에서는 이너셔, 실루엣 스코어를 사용해 적절한 클러스터 개수를 선택
- 하지만 gmm에서는 해당 지표를 사용할 수 없음, 해당 지표는 클러스터가 타원형이거나 크기가 다를 때 안정적이지 않기 때문
- GMM은 BIC, AIC와 같은 이론적 정보 기준을 최소화 하는 모델을 찾음
- BIC, AIC 모두 학습할 파라미터가 많은 모델에게 벌칙을 가하고 데이터에 잘 학습하는 모델에게 보상을 더함
- 사용 예제
- AIC, BIC 모두 최소점을 선택
gm.bic(X) >> 8189.747000497186 gm.aic(X) >> 8102.521720382148
베이즈 가우시안 혼합 모델
- 최적의 클러스터 개수를 수동으로 찾지 않고 불필요한 클러스터의 가중치를 0으로 만드는 BayesianGaussianMixture 클래스를 사용할 수 있음
- 클러스터 개수 n_components를 최적의 클러스터 개수보다 크다고 믿을 만한 값으로 지정
- 해당 알고리즘은 불필요한 클러스터를 제거
- 예제
- 알고리즘이 자동으로 3개의 클러스터가 필요하다는 것을 볼수 있음
from sklearn.mixture import BayesianGaussianMixture bgm = BayesianGaussianMixture(n_components=10, n_init=10, random_state=42) bgm.fit(X) np.round(bgm.weights_, 2) >> array([0.4 , 0.21, 0.4 , 0. , 0. , 0. , 0. , 0. , 0. , 0. ])
- 클러스터가 적을 것이라는 사전 믿음(낮은 농도), 반대로 높은 농도도 존재
- 해당 사전 믿음은 weight_concentration_prior 매개변수를 통해 조절 가능
- 데이터가 많을 수록 사전 믿음은 중요하지 않음
이상치 탐지와 특이치 탐지를 위한 다른 알고리즘
- sklearn에서 제공하는 이상치탐지 및 특이치 탐지 전용으로 사용할 수 있는 알고리즘
- PCA
- 보통 샘플의 재구성 오차와 이상치의 재구성 오차를 비교하면 후자가 더 큰 값을 보임
- 이를 통해 이상치 탐지
- Fast-MCD
- EllipticEnvelope 클래스에서 구현된 이 알고리즘은 이상치 감지에 유용
- 데이터셋을 정제할 때 사용
- 보통 샘플이 하나의 가우시안 분포에서 생성되었다고 가정, 이 가우시안 분포에서 생성되지 않은 이상치로 데이터 셋이 안좋아 졌다고 가정
- 알고리즘이 가우시안 분포의 파라미터를 추정할 때 이상치로 의심되는 샘플을 무시
- 해당 기법은 알고리즘이 타원형을 잘 추정하고 이상치를 잘 구분하도록 도움
- 아이솔레이션 포레스트
- 고차원 데이터셋에서 이상치 감지를 위한 효율적인 알고리즘
- 무작위로 성장한 결정 트리로 구성된 랜덤포레스트 생성
- 각 노드에서 특성을 랜덤하게 선택한 다음 랜덤한 임곗값을 골라 데이터셋을 둘로 나눔
- 이런 식으로 데이터셋은 점차 분리되어 모든 샘플이 다른 샘플과 격리될 때까지 진행
- 이상치는 일반적으로 다른 샘플과 멀리 떨어져 있으므로 평균적으로 정상 샘플과 적은 단계에서 분리
- 고차원 데이터셋에서 이상치 감지를 위한 효율적인 알고리즘
- LOF
- 이상치 탐지에 좋음
- 주어진 샘플 주위의 밀도와 이웃 주위의 밀도를 비교
- one-class SVM
- 특이치 탐지에 사용
- 고차원 데이터셋에 잘 작동
- SVM과 같이 대규모 데이터셋으로 확장이 어려움
- PCA
'Study > Self Education' 카테고리의 다른 글
핸즈온 머신러닝 - 11 (0) | 2024.06.19 |
---|---|
핸즈온 머신러닝 - 10 (0) | 2024.06.19 |
핸즈온 머신러닝 - 8 (0) | 2024.06.18 |
핸즈온 머신러닝 - 7 (0) | 2024.06.18 |
핸즈온 머신러닝 - 6 (0) | 2024.06.18 |