9. 비지도 학습
- 많은 데이터는 대부분 레이블이 없음
- 레이블을 부여하는 작업은 비용이 크며 오래걸림
=> 레이블이 없는 데이터를 바로 사용하기 위한 비지도 학습
9. 1 군집
비슷한 샘플을 구별해 하나의 클러스터 또는 비슷한 샘플의 그룹으로 할당하는 작업
분류
- 데이터셋이 레이블 되어 있음
- 로지스틱 회귀, SVM, 랜덤 포레스트 분류기 같은 분류 알고리즘이 잘 맞음
군집
- 레이블이 없음
- 대부분의 군집 알고리즘은 왼쪽 아래 클러스터를 쉽게 감지하지만, 오른쪽 위의 클러스터는 두 개의 하위 클러스터로 구성되었는지 확실하지 않음
- 모든 특성을 사용하면, 클러스터 세 개를 잘 구분할 수 있음
- 다음과 같은 어플리케이션에서 사용
-
고객 분류 (ex. 추천 시스템)
-
데이터 분석 - 군집 알고리즘으로 각 클러스터를 따로 분석
-
차원 축소 기법 - 각 클러스터에 대한 샘플의 친화성을 측정해, 각 샘플의 특성 벡터 x를 클러스터 친화성의 벡터로 바꿈. k개의 클러스터가 있다면 이 벡터는 k차원이 되고, 원본 특성 벡터보다 훨씬 저차원이며 충분한 정보를 가짐
-
이상치 탐지 - 모든 클러스터에 친화성이 낮은 샘플은 이상치일 가능성이 높음. (ex. 결함 감지, 부정 거래 감지 등)
-
준지도 학습 - 일부 레이블된 샘플을 군집을 수행해 모든 샘플에 레이블을 전파할 수 있음
-
검색 엔진 - 제시된 이미지와 비슷한 이미지를 찾아줌. 비슷한 이미지는 동일한 클러스터에 속함
-
이미지 분할 - 색을 기반으로 픽셀을 클러스터로 모아, 해당 클러스터의 평균 색으로 대체함. 이미지에 있는 색상의 종류를 크게 줄임 (ex. 물체 탐지 및 추적 시스템)
- 클러스터에 대한 보편적인 정의는 없으며, 알고리즘이 다르면 다른 종류의 클러스터를 감지
- 어떤 알고리즘은 센트로이드라 부르는 특정 포인트를 중심으로 모인 샘플을 찾음
9. 1. 1 k-평균
레이블이 없는 데이터셋 샘플 중, 덩어리로 잘 묶어지는 듯한 종류의 데이터셋을 빠르고 효율적으로 클러스터로 묶을 수 있는 알고리즘
- 각 클러스터의 중심을 찾고 가장 가까운 클러스터에 샘플을 할당함
다섯 개의 덩어리로 이루어진 데이터셋에 k-평균 알고리즘 훈련
from sklearn.cluster import KMeans
k = 5
kmeans = KMeans(n_clusters=k, random_state=42)
y_pred = kmeans.fit_predict(X
※ 알고리즘이 찾을 클러스터 개수 k를 지정하는 것은 일반적으로 쉬운 것일이 아님
군집에서 각 샘플의 레이블은 알고리즘이 샘플에 할당한 클러스터의 인덱스 (분류의 레이블과 혼동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, 2], [-3, 3], [-3, 2.5]])
kmeans.predict(X_new)
=> array([1, 1, 2, 2], dtype=int32)
- 샘플이 대부분 적절한 클러스터에 잘 할당되었지만 몇 개는 레이블이 잘못 부여됨
- k-평균 알고리즘은 클러스터의 크기가 많이 다르면 잘 작동하지 않음
-
샘플을 클러스터에 할당할 때 센트로이드까지 거리를 고려하는 것이 전부이기 때문
위와 같이 샘플을 하나의 클러스터에 할당하는 것은 하드 군집 이며,
소프트 군집은 클러스터마다 샘플에 점수를 부여함
- 이 점수는 샘플과 센트로이드 사이의 거리, 가우시안 방사기저 함수 같은 유사도 점수 등 가능
# kmean의 transform() 메서드는 샘플과 각 센트로이드 사이의 거리 반환
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]])
- X_new에 있는 첫 번째 샘플이 첫 번째 센트로이드에서 2.81, 두 번째 센트로이드에서 0.33 ... 의 거리만큼 떨어져 있음을 의미
- 고차원 데이터셋을 이런 식으로 변환하면 k-차원 데이터셋이 만들어짐
- 매우 효율적인 비선형 차원 축소 기법이 될 수 있음
k-평균 알고리즘
1. 레이블이나 센트로이드가 주어지지 않으면, 센트로이드를 랜덤하게 선정
2. 샘플에 레이블을 할당하고 센트로이드를 업데이트하고, 또 샘플에 레이블 할당하고 센트로이드 업데이트하는 식으로 센트로이드에 변화가 없을 때까지 반복
- 제한된 횟수안에 수렴하는 것이 보장됨
3. 최적으로 보이는 클러스터에 도달할 때까지 2반복
이 알고리즘이 수렴하는 것이 보장되지만, 적절한 솔루션으로 수렴하지 못할 수 있음
- 이는 센트로이드 초기화에 달려 있음 (운에 따라 다름)
센트로이드 초기화를 개선하여, 위와 같은 위험을 줄일 수 있음
센트로이드 초기화 방법
- 다른 군집 알고리즘을 먼저 실행해, 센트로이드 위치를 근사하게 파악
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)
- 또는 랜덤 초기화를 다르게 하여 여러 번 알고리즘을 실행하고 가장 좋은 솔루션을 선택
-
최선의 솔루션을 얻기위해, 성능 지표로 모델의 이너셔 사용
-
이너셔는 각 샘플과 가장 가까운 센트로이드 사이의 평균 제곱 거리
-
KMeans 클래스는 알고리즘을 n_init번 실행해 이너셔가 가장 높은 모델을 반환
kmeans.inertia_
=> 211.5985372581684
k-평균++ 알고리즘
다른 센트로이드와 거리가 먼 센트로이드를 선택하는 똑똑한 초기화 단계를 거침
- k-평균 알고리즘이 최적이 아닌 솔루션으로 수렴할 가능성을 크게 낮춤
- KMeans 클래스는 기본적으로 이 초기화 방법을 사용 (랜덤 초기화: init = "random")
1. 가지고 있는 데이터 포인트 중 랜덤하게 1개를 선택하여 첫 번째 중심점으로 지정
2. 나머지 데이터 포인트들에 대해 그 첫 번째 중심점까지의 거리를 계산
3. 두 번째 중심점은 첫 번째 중심점으로부터 최대한 먼 곳에 배치된 데이터 포인트로 지정
4. 중심점이 k개가 될 때까지 2,3 번을 반복
k-평균 속도 개선
불필요한 거리 계산을 피함으로써 알고리즘의 속도를 높임
- 삼각 부등식 사용 (두 점 사이의 직선은 항상 가장 짧은 거리가 됨)
- 샘플과 센트로이드 사이의 거리를 위한 하한선과 상한선을 유지
- KMeans 클래스에서 기본으로 사용
미니배치 k-평균
전체 데이터셋을 사용해 반복하지 않고 각 반복마다 미니배치를 사용해 센트로이드를 조금씩 이동
- 알고리즘 속도를 3~4배 정도 높임
- 메모리에 들어가지 않는 대량의 데이터셋에 군집 알고리즘을 적용할 수 있음
from sklearn.cluster import MiniBatchKMeans
minibatch_kmeans = MiniBatchKMeans(n_clusters=5)
minibatch_kmeans.fit(X)
- 일반 k-평균보다 훨씬 빠르지만, 이너셔는 조금 더 나쁨
-
특히 클러스터의 개수가 증가할 때 그런 편
최적의 클러스터 개수 찾기
클러스터 개수 k를 올바르게 지정하지 않으면, 결과가 매우 나쁠 수 있음
- 이너셔는 k가 증가함에 따라 점점 작아지므로 k 선택시 성능 지표로 사용할 수 없음
-
하지만 이너셔의 감소 폭이 크게 줄어드는 지점을 선택해 k값을 지정할 수는 있음
- 계산 비용이 많이 들지만, 실루엣 점수를 사용
- 실루엣 점수는 모든 샘플에 대한 실루엣 계수로의 평균
- 샘플의 실루엣 계수는 (b-a)/max(a, b)
-
a : 동일한 클러스터에 있는 다른 샘플까지의 평균 거리 (클러스터 내부의 평균 거리)
-
b : 가장 가까운 클러스터까지의 평균 거리 (가장 가까운 클러스터의 샘플까지 평균 거리)
- 실루엣 계수는 -1 에서 +1 까지의 바뀔 수 있음
-
+1에 가까우면 자신의 클러스터 안에 잘 속해 있고 다른 클러스터와 멀리 떨어져 있음
-
0에 가까우면 클러스터 경계에 위치한다는 의미
-
-1에 가까우면 이 샘플이 잘못된 클러스터에 할당되었다는 의미
# 실루엣 점수 계산
# 데이터셋에 있는 모든 샘플과 할당된 레이블을 전달
from sklearn.metrics import silhouette_score
silhouette_score(X, kmeans.labels_)
- 이너셔를 비교했을 때보다 많은 정보를 줌
- 실루엣 다이어그램
-
모든 샘플의 실루엣 계수를 할당된 클러스터와 계수값으로 정렬하여 그린 그래프
-
이 그래프의 높이는 클러스터가 포함하고 있는 샘플의 개수
-
너비는 이 클래스에 포함된 샘플의 정렬된 실루엣 계수를 나타냄 (넒을수록 좋음)
-
수직 파선은 각 클러스터 개수에 해당하는 실루엣 점수
- 한 클러스터의 많은 샘플이 파선의 왼쪽에서 멈추면, 클러스터의 샘플이 다른 클러스터랑 너무 가깝다는 것을 의미하므로 나쁜 클러스터임
- 실루엣 점수가 k=4 보다 조금 낮더라도, 비슷한 크기의 클러스터를 얻을 수 있은 k=5 를 선택하는 것이 좋음
-
k=5일 때는 모든 클러스터의 크기가 비슷함
9. 1. 2 k-평균의 한계
속도가 빠르고 확장이 용이하지만, 몇 가지 단점이 있음
1. 최적이 아닌 솔루션을 피하려면 알고리즘을 여러 번 실행해야 함
2. 클러스터 개수를 지정해야 함
3. k-평균은 클러스트의 크기나 밀집도가 서로 다르거나 원형이 아닐 경우 잘 동작하지 않음
크기와 밀집도, 방향이 다른 세 개의 타원형 클러스터를 가진 데이터셋을 k-평균 군집한 모습
- 두 솔루션 모두 좋지 않음
- 왼쪽이 낫지만 가운데 클러스터의 25% 정도가 오른쪽 클러스터로 할당되어 있음
- 이런 타원형 클러스터에서는 가우시안 혼합 모델이 잘 작동
k-평균을 실행하기 전에 입력 특성을 스케일을 맞추는 것이 중요함
- 클러스터가 길쭉해지는 것을 막을 수 있음
9. 1. 3 군집을 사용한 이미지 분할
이미지 분할은 이미지를 세그먼트 여러 개로 분할하는 작업
- 시맨틱 분할에서는 동일한 종류의 물체에 속한 모든 픽셀은 같은 세그먼트에 할당
- 시맨틱 / 인스턴스 분할에서 최고 성능을 내려면 합성곱 신경망을 사용한 복잡할 모델을 사용해야하지만
- 여기서는 쉬운 색상 분할을 수행해봄
동일한 색상을 가진 픽셀을 같은 세그먼트에 할당
from matplotlib.image import imread
image = imread(os.path.join(images_path, filename))
image.shape
=> (533, 800, 3)
- 이 이미지는 3D 배열로 표현됨
- 첫 번째 차원의 크기가 높이, 두 번재는 너비, 세 번째는 컬러 채널 개수(빨강, 초록, 파랑 / RGB)
=> 각 픽셀에 대해 빨강, 초록, 파랑의 강도를 담은 3D 벡터
이 배열은 RGB 색상의 긴 리스트로 변환 후 k-평균을 사용해 이 색상을 클러스터로 모으는 코드
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)
- 예를 들어, 모든 초록색을 하나의 컬러 클러스터로 만들 수 있음
- 그 후 각 색상에 대해(ex. 검은 초록) 그 픽셀의 컬러 클러스터의 평균 컬러를 찾음
- 예를 들어 모든 초록색은 모두 밝은 초록색으로 바뀔 수 있음(초록 클러스터의 평균 색이 밝은 초록일 경우)
- 마지막으로, 이 긴 색상의 리스트를 원본 이미지와 동일한 크기로 바꿈
- 클러스터 개수를 여러 개로 바꿔 테스트
- 6개 클러스터 부터, 무당벌레의 빨간색이 독자적인 클러스터를 만들지 못하고 주위 색에 합쳐짐
-> k-평균이 비슷한 크기의 클러스터를 만드는 경향이 있기 때문
- 무당벌레는 이미지의 나머지 부분보다 훨씬 작으므로 k-평균이 무당벌레를 하나의 클러스터로 만들지 못함
9. 1. 4 군집을 사용한 전처리
군집은 지도 학습 알고리즘을 적용하기 전에 전처리 단계로 사용할 수 있음
숫자 데이터셋 (0~9까지 숫자를 나타내는 8x8 크기 흑백 이미지 1797개를 담고 있음)을 이용한 예시
from sklearn.datasets import load_digits
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
X_digits, y_digits = load_digits(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(X_digits, y_digits, random_state=42)
#로지스틱 회귀 모델 훈련
log_reg = LogisticRegression(multi_class="ovr", solver="lbfgs", max_iter=5000, random_state=42)
log_reg.fit(X_train, y_train)
#테스트 세트 정확도 평가
log_reg.score(X_test, y_test)
=> 0.9688888888888889
k-평균을 전처리 단계로 사용
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(X_test, y_test)
=> 0.9777777777777777
- 오차율이 30%까지 줄어듦
군집이 데이터셋의 차원을 64에서 50으로 감소시켰지만 성능 향상은 대부분 변환된 데이터셋이 원본 데이터셋보다 선형적으로 더 잘 구분할 수 있기 때문
- 따라서 로지스틱 회귀를 사용하기에 더 좋음
클러스터 개수 k를 임의로 정했으므로, 이보다 더 좋은 결과를 얻을 수 있음
- k-평균이 분류 파이프라인의 하나의 전처리 단계이기 때문에 이전보다 좋은 k값을 찾는 일이 더 쉬움
- 가장 좋은 k값은 교차 검증에서 가장 좋은 분류 성능을 내는 값
- GridSearchCV를 사용해 최적의 클러스터 개수를 찾아봄
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_
grid_clf.score(X_test, y_test)
=> {'kmeans__n_clusters': 99}
=> 0.9822222222222222
- k=99개의 클러서를 사용할 때 정확도가 크게 향상됨
- 테스트 세트에서 98.22% 정확도 달성
9. 1. 5 군집을 사용한 준지도 학습
레이블이 없는 데이터가 많고 레이블이 있는 데이터는 적을 때 사용
숫자 데이터셋에서 레이블된 50개 샘플에 로지스틱 회귀 모델 훈련
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.8333333333333334
- 전체 데이터셋을 사용했을 때 보다 낮은 정확도
훈련 세트를 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_representative_digits = np.array([
4, 8, 0, 6, 8, 3, 7, 7, 9, 2,
5, 5, 8, 5, 2, 1, 2, 9, 6, 1,
1, 6, 9, 0, 8, 3, 0, 7, 4, 1,
6, 5, 2, 4, 1, 8, 6, 3, 9, 2,
4, 2, 9, 4, 7, 6, 2, 3, 1, 1])
이제 레이블된 50개의 샘플로 이루어진 데이터셋
- 무작위 샘플이 아닌, 각 클러스터를 대표하는 이미지
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.9222222222222223
- 83.3% -> 92.2% 로 올라간 성능
- 무작위 샘플 대신 데표 샘플에 레이블을 할당하는 것이 좋은 방법
레이블을 동일한 클러스터에 있는 모든 샘플로 전파 => 레이블 전파
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(multi_class="ovr", solver="lbfgs", max_iter=5000, random_state=42)
log_reg.fit(X_train, y_train_propagated)
log_reg.score(X_test, y_test)
=> 0.9333333333333333
- 각 대표 샘플의 레이블을 동일한 클러스터의 모든 샘플에 전파 했으므로,
- 클러스터 경계에 가깝게 위치한 샘플이 포함되어 있고, 잘못 레이블이 부여되었을 수 있음
센트로이드와 가까운 샘플의 20%에만 레이블을 전파
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_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]
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.score(X_test, y_test)
=> 0.94
- 레이블된 샘플 50개만으로(평균적으로 클래스당 5개 샘플) 94%의 정확도
- 레이블이 있는 전체 데이터셋에서 훈련한 로지스틱 회뤼 성능(96.6%)에 매우 가까움
-> 전파된 레이블이 실제로 매우 좋기 때문
- 다음 코드에서 볼 수 있듯이 99% 실제 데이터와 같음
np.mean(y_train_partially_propagated == y_train[partially_propagated])
=> 0.9896907216494846
능동 학습 - 불확실성 샘플링
1. 지금까지 수집한 레이블된 샘플에서 모델을 훈련, 이 모델을 사용해 레이블되지 않은 모든 샘플에 대한 예측을 만듦
2. 모델이 가장 불확실하게 예측한 샘플(추정 확률이 낮은 샘플)을 전문가에게 보내 레이블을 붙임
3. 레이블을 부여하는 노력만큼의 성능이 향상되지 않을 때까지 이를 반복
9. 1. 6 DBSCAN
밀집된 연속적 지역을 클러스터로 정의
1. 알고리즘이 각 샘플에서 작은 거리인 ε 내에 샘플이 몇 개 놓여 있는지 셈 -> 이 지역의 샘플을 ε-이웃이라고부름
2. 자기 자신을 포함해 ε-이웃 내에 적어도 min_samples개 샘플이 있다면 이를 핵심 샘플로 간주 -> 핵심 샘플은 밀집된 지역에 있는 샘플
3. 핵심 샘플의 이웃에 있는 모든 샘플은 동일한 클러스터에 속함. 이웃에는 다른 핵심 샘플이 포함될 수 있음. 따라서 핵심 샘플의 이웃의 이웃은 계속해서 하나의 클러스터를 형성
4. 핵심 샘플이 아니고 이웃도 아닌 샘플은 이상치로 판단
모든 클러스터가 충분히 밀집되어 있고 밀집되지 않은 지역과 잘 구분될 때 좋은 성능을 내는 알고리즘
from sklearn.datasets import make_moons
from sklearn.cluster import DBSCAN
X, y = make_moons(n_samples=1000, noise=0.05, random_state=42)
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 -> 알고리즘이 이 샘플을 이상치로 판단함
len(dbscan.core_sample_indices_)
dbscan.core_sample_indices_[:10]
bscan.components_[:3]
=> 808
=> array([ 0, 4, 5, 6, 7, 8, 10, 11, 12, 13])
=> array([[-0.02137124, 0.40618608], [-0.84192557, 0.53058695], [ 0.58930337, -0.32137599]])
- 클러스터 7개를 만들었고, 많은 샘플을 이상치로 판단했음
- eps 를 0.2 로 증가해 샘플의 이웃 범위를 넓히면 오른쪽 그래프처럼 완벽한 군집을 얻음
fit_predict()를 통한 예측
- 이 알고리즘은 새로운 샘플에 대해 클러스터를 예측할 수 없음)
- 사용자가 필요한 예측기를 선택해야함
- KNeighborsClassifier를 훈련 해 샘플 몇개를 전달 해 어떤 클러스터에 속할 가능성이 높은지 예측 후 각 클러스터에 대한 확률 추정
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)
knn.predict_proba(X_new)
=> array([1, 0, 1, 0])
=> array([[0.18, 0.82], [1. , 0. ], [0.12, 0.88], [1. , 0. ]])
이 분류기를 핵심 샘플에서만 훈련하는게 아니라, 모든 샘플을 훈련할 수도 있고, 이상치를 제외할 수도 있음
이 결정 경계를 아래 그림이 나타내고 있음
(덧셈 기호는 X_new에 있는 샘플 네개를 표시)
- 훈련 세트에 이상치가 없으므로 클러스터가 멀리 떨어져 있더라도 분류기는 항상 클러스터 한 개를 선택
- 최대 거리를 사용하면 두 클러스터에서 멀리 떨어진 샘플을 이상치로 간단히 분류할 수 있음
kneighbors() 메서드에 샘플을 전달하면 훈련 세트에서 가장 가까운 k개의 이웃의 거리와 인덱스를 반환
y_dist, y_pred_idx = knn.kneighbors(X_new, n_neighbors=1)
y_pred = dbscan.labels_[dbscan.core_sample_indices_][y_pred_idx]
y_pred[y_dist > 0.2] = -1
y_pred.ravel()
=> array([-1, 0, 1, -1])
DBSCAN은 클러스터의 모양과 개수에 상관없이 감지할 수 있는 능력이 있음
- 이상치에 안정적이며, 하이퍼파라미터가 eps, min_samples 두 개뿐임
- 하지만 클러스터 간의 밀집도가 크게 다르면 모든 클러스터를 올바르게 잡아내는 것이 불가능
9. 1. 7 다른 군집 알고리즘
사이킷런에 구현된 여러 군집 알고리즘
병합 군집
인접한 클러스터 쌍을 연결(처음에는 샘플 하나에서 시작)
- 병합된 클러스터 쌍을 트리로 모두 그리면 클러스터의 이진 트리를 얻음
- 이 트리의 리프는 개별 샘플
- 병합 군집은 대규모 샘플과 클러스터에 잘 확장되며 다양한 형태의 클러스터를 감지
- 특정 클러스터 개수를 선택하는 데 도움이 되는 유용한 클러스터 트리를 만듦
BIRCH
대규모 데이터셋을 위해 고안
- 특성 개수가 너무 많지 않다면 배치 k-평균보다 빠르고 비슷한 결과를 만듦
- 훈련 과정에서 새로운 샘플을 클러스터에 빠르게 할당할 수 있는 정보를 담은 트리 구조를 만듦
- 트리에 모든 샘플을 저장하지 않음
- 제한된 메모리를 사용해 대용량 데이터셋을 다룰 수 있음
평균-이동
1. 각 샘플을 중심으로 하는 원을 그린 후, 원마다 안에 포함된 모든 샘플의 평균을 구함
2. 원의 중심을 평균점으로 이동시킴
3. 모든 원이 움직이지 않을 때까지 이 평균-이동은 계속함
4. 평균-이동은 지역의 최대밀도를 찾을 때까지 높은 쪽으로 원을 이동시킴
동일한 지역에 안착한 원에 있는 모든 샘플은 동일한 클러스트가 됨
- DBSCAN과 유사하게 모양이나 개수에 상관없이 클러스터를 찾을 수 있으며
- 하이퍼파라미터도 매우 적음
- 국부적인 밀집도 추청제 의존
- 하지만 클러스터 내부 밀집도가 불균형할 때 여러 개로 나누는 경향이 있음
- O(m^2)으로 대규모 데이터셋에 적합하지 않음
유사도 전파
샘플은 자신을 대표할 수 있는 비슷한 샘플에 투표하며, 알고리즘이 수렴하면 각 대표와 투표한 샘플이 클러스터를 형성
- 유사도 전파는 크기가 다른 여러 개의 클러스터를 감지할 수 있음
- O(m^2)으로 대규모 데이터셋에 적합하지 않음
스펙트럼 군집
샘플 사이의 유사도 행렬을 받아 저차원 임베딩을 만듦(즉, 차원을 축소함)
그 다음, 이 저차원 공간에서 또 다른 군집 알고리즘을 사용
- 스펙트럼 군집은 복잡한 클러스터 구조를 감지하고 그래프 컷을 찾는 데 사용할 수 있음
(소셜 네트워크에서 친구의 클러스터를 찾음)
- 이 알고리즘은 샘플 개수가 많으면 잘 적용되지 않고, 클러스터의 크기가 매우 다르면 잘 작동하지 않음
[AI/Hands-on ML] - [핸즈온 머신러닝] 9장 - 비지도 학습 2 (가우시안 혼합)
'AI > Hands-on ML' 카테고리의 다른 글
[핸즈온 머신러닝] 10장 - 케라스를 사용한 인공 신경망 1 (인공 신경망 소개) (0) | 2021.02.22 |
---|---|
[핸즈온 머신러닝] 9장 - 비지도 학습 2 (가우시안 혼합) (0) | 2021.02.05 |
[핸즈온 머신러닝] 8장 - 차원 축소 (0) | 2021.01.24 |
[핸즈온 머신러닝] 핸즈온 머신러닝2 정오표 (0) | 2021.01.22 |
[핸즈온 머신러닝] 7장 - 앙상블 학습과 랜덤 포레스트 (0) | 2021.01.19 |