군집을 사용한 이미지 분할

이미지 분할에는 여러 가지 변형이 있다.

  1. 색상 분할: 동일한 색상을 가진 픽셀을 같은 세그먼트에 할당. (e.g. 인공위성 사진에서 한 지역을 산림 면적을 측정.)
  2. 시맨틱 분할: 동일한 종류의 물체에 속한 모든 픽셀을 같은 세그먼트에 할당. (e.g. 보행자 식별 프로그램)
  3. 인스턴스 분할: 개별 객체에 속한 모든 픽셀을 같은 세그먼트에 할당. (e.g. 보행자 ID 인식 프로그램)

위 3가지 중 색상 분할이 k-평균을 사용한다.

다음 코드는 아래 작업을 수행한다.

  1. 사진 배열의 크기를 바꾸어 긴 RGB 색상 리스트로 만든다.
  2. k-평균을 이용해 8개의 클러스터로 모은다.
  3. 각 픽셀에 대해 가장 가까운 클러스터 중심으로 포함한 segmented_img 배열을 생성한다. 여기에는 고급 numpy 인덱싱이 사용된다.
  4. 이 배열을 원래 이미지 크기로 복원한다.
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)

k에 따른 변환의 결과는 다음과 같다. 색상의 개수에 따라 그림의 디테일이 달라진다.


준지도 학습

준지도 학습에서 군집을 사용하면 레이블을 같은 군집 내로 전파할 수 있다고 했다.

다음 코드는 train set을 50개의 cluster로 모으고, 각 cluster에서 centroid에 가장 가까운 이미지를 찾는다. 이를 대표 이미지라고 한다.

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]

그런 다음 대표 이미지에 수동으로 레이블을 할당하고, 이 레이블을 같은 cluster내로 전파하면 된다.

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

여기서 그치지 않고, cluster 중심에서 거리가 너무 먼 샘플을 무시하여(이상치로 고려하여) 훈련하면 더 높은 정확도를 얻을 수 있다. 코드에서는 거리가 먼 1%의 샘플을 거리 -1로 설정한 다음 무시하였다. 자세한 방법은 코드를 참조하자.

cf. 능동 학습(Active Learning)
전문가와 학습 알고리즘이 상호 작용하여, 알고리즘이 요청할 때 전문가가 레이블을 제공하는 학습이다. 일단 모델이 훈련한 다음, 불확실한 샘플에 대해 전문가가 레이블을 직접 부여한다.


DBSCAN

DBSCAN(Density-Based Spatial Clustering of Applications with Noise) 알고리즘은 밀집된 연속적 지역을 클러스터로 지정한다. 구제적인 방법은 다음과 같다.

  1. 각 샘플에서 작은 거리인 $\epsilon$ 안에 샘플이 몇 개 놓여 있는지 센다. 이 지역을 샘플의 $\epsilon$-이웃 이라고 한다.
  2. 자기 자신을 포함해 $\epsilon$-이웃 내에 적어도 min_samples 개의 샘플이 있다면, 이를 핵심 샘플로 간주한다. 즉, 핵심 샘플은 밀집된 지역에 있는 샘플이다.
  3. 핵심 샘플의 이웃에 있는 모든 샘플은 동일한 cluster에 속한다. 물론, 이웃에는 다른 핵심 샘플이 포함될 수도 있다. 이웃에 이웃이 꼬리를 물고 점점 클러스터가 넓어진다.
  4. 핵심 샘플도 아니고, 이웃도 아니면 이상치이다.

코드로 구현하면 다음과 같다.

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

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

# print labels of all samples
dbscan.labels_      # -1 means outlier

# index of core samples
dbscan.core_sample_indices_

# core samples
dbscan.components_



DBSCAN 클래스는 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]])      # new samples
knn.predict(X_new)
knn.predict_proba(X_new)

DBSCAN은 매우 간단하고 강력하지만, cluster 간 밀집도가 크게 다르거나 cluster 주변에 충분한 저밀도 구역이 없는 경우, 대규모 dataset이라면 잘 작동하지 않는다(계산 복잡도가 $O(m^2n)$ 이다).


다른 군집 알고리즘

  1. 병합 군집
  2. BIRCH
  3. 평균-이동
  4. 유사도 전파

코드 보러가기



별도의 출처 표시가 있는 이미지를 제외한 모든 이미지는 강의자료에서 발췌하였음을 밝힙니다.

댓글남기기