군집화란?
군집을 사용한 이미지 분할
여기서는 k-평균을 사용하는 간단한 색상 분할 작업에 중점을 둬서 진행해 본다.
◆ 이미지 분할(image segmentation)
이미지 분할은 이미지를 여러 개의 세그먼트로 분할하는 작업이다.
- 색상 분할: 동일한 색상을 가진 픽셀을 같은 세그먼트에 할당. 예. 인공위성 사진을 분석하여 한 지역의 전체 산림 면적이 얼마나 되는지 측정
- 시맨틱 분할: 동일한 종류의 물체에 속한 모든 픽셀을 같은 세그먼트에 할당. 예. 자율 주행 자동차의 비전 시스템에서 보행자 이미지를 구성하는 모든 픽셀을 '보행자' 세그먼트에 할당
- 인스턴스 분할: 개별 객체에 속한 모든 픽셀을 같은 세그먼트에 할당. 위의 예에서 각 보행자는 다른 세그먼트가 됨.
▶ 시맨틱 분할 또는 인스턴스 분할에서 최고 수준의 성능을 내려면 합성곱 신경망 기반의 복잡한 모델을 사용해야 한다.
[무당벌레 이미지를 활용한 이미지 분할 실습]
1. 이미지 다운로드 및 로드 (+라이브러리)
먼저 무당벌레 이미지를 다운로드 한 다음,
파이썬 이미지 처리 라이브러리인 PIL의 후속 버전인 Pillow 패키지를 import 한 다음, 이를 사용해 ladybug.png 이미지를 로드한다 (파일은 filepath 경로에 있다고 가정)
from pathlib import Path
import urllib.request
IMAGES_PATH = Path() / "images" / "unsupervised_learning"
IMAGES_PATH.mkdir(parents=True, exist_ok=True)
homl3_root = "https://github.com/ageron/handson-ml3/raw/main/"
filename = "ladybug.png"
filepath = IMAGES_PATH / filename
if not filepath.is_file():
print("Downloading", filename)
url = f"{homl3_root}/images/unsupervised_learning/{filename}"
urllib.request.urlretrieve(url, filepath)
import PIL
image = np.asarray(PIL.Image.open(filepath))
image.shape
▶ 이 이미지는 3D 배열로 표시되는데,
- 첫 번째 차원: 높이
- 두 번째 차원: 너비
- 세 번째 차원: 색상 채널의 수 (3 - RGB)
※ 색상 채널은 각 픽셀에 대해 0에서 255 사이의 부호 없는 8비트 정수로 빨강, 초록, 파랑의 강도를 담고 있는 3D 벡터를 의미하며, 일부 이미지에는 채널 수가 적을 수 있으며 (흑백), 일부 이미지에는 채널 수가 많을 수 있다.
2. k-평균 사용
그리고 배열의 크기를 바꿔 긴 RGB 색상 리스트로 만든 다음 k-평균을 사용하여 8개의 클러스터로 모은다.
각 픽셀에 대해 가장 가까운 클러스터 중심(=각 픽셀 클러스터의 평균 색상)을 포함한 segmented_img 배열을 생성하고 마지막으로 이 배열을 원래 이미지로 바꾼다.
그리고 고급 넘파이 인덱싱을 사용하는데, 예를 들어 kmeans.labels_의 처음 10개의 레이블이 1이면 segmented_img의 처음 10개의 색상은 kmeans.cluster_centers_[1]과 동일하다.
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)
3. 클러스터 개수에 따른 이미지 출력
클러스터 개수를 여러 개로 바꿔 클러스터 별 이미지를 출력한다.
segmented_imgs = []
n_colors = (10, 8, 6, 4, 2)
for n_clusters in n_colors:
kmeans = KMeans(n_clusters=n_clusters, n_init=10, random_state=42).fit(X)
segmented_img = kmeans.cluster_centers_[kmeans.labels_]
segmented_imgs.append(segmented_img.reshape(image.shape))
plt.figure(figsize=(10, 5))
plt.subplots_adjust(wspace=0.05, hspace=0.1)
plt.subplot(2, 3, 1)
plt.imshow(image)
plt.title("원본 이미지")
plt.axis('off')
for idx, n_clusters in enumerate(n_colors):
plt.subplot(2, 3, 2 + idx)
plt.imshow(segmented_imgs[idx] / 255)
plt.title(f"{n_clusters} 색상")
plt.axis('off')
plt.show()
▶ 8개보다 클러스터를 작게 하면 무당벌레의 빨간색이 독자적인 클러스터를 만들지 못하고 주위 색에 합쳐지는데, 이는 k-평균이 비슷한 크기의 클러스터를 만드는 경향이 있기 때문이다. 무당벌레는 이미지의 나머지 부분보다 훨씬 작기 때문에 화려한 색을 가지고 있더라도 k-평균이 무당벌레를 하나의 클러스터로 만들지 못한다.
군집을 사용한 준지도 학습
군집은 준지도 학습에서도 사용할 수 있는데, 레이블이 없는 데이터가 많고 레이블이 있는 데이터는 적을 때 사용한다.
여기서는 숫자 0에서 9까지를 나타내는 8*8 흑백 이미지 1,797개로 구성된 MNIST와 유사한 간단한 숫자 데이터셋을 사용해본다.
1. 데이터셋 로드 및 분할
from sklearn.datasets import load_digits
X_digits, y_digits = load_digits(return_X_y=True)
X_train, y_train = X_digits[:1400], y_digits[:1400]
X_test, y_test = X_digits[1400:], y_digits[1400:]
2. 훈련 세트 훈련
50개 샘플에 대한 레이블만 있다고 가정하고,
기준 성능을 얻기 위해 레이블이 있는 이 50개 샘플에서 로지스틱 회귀 모델을 훈련
from sklearn.linear_model import LogisticRegression
n_labeled = 50
log_reg = LogisticRegression(max_iter=10_000)
log_reg.fit(X_train[:n_labeled], y_train[:n_labeled])
3. 테스트 세트에서 정확도 측정
테스트 세트에서 이 모델의 정확도를 측정한다. (테스트 세트는 레이블이 있어야 함)
log_reg.score(X_test, y_test)
▶ 정확도가 75.8%이 나왔다.
정확도가 비교적 낮으므로 개선이 필요해 보인다.
4. 정확도 개선 작업 - 1
먼저 훈련 세트를 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]
# 대표 이미지 출력
plt.figure(figsize=(8, 2))
for index, X_representative_digit in enumerate(X_representative_digits):
plt.subplot(k // 10, 10, index + 1)
plt.imshow(X_representative_digit.reshape(8, 8), cmap="binary",
interpolation="bilinear")
plt.axis('off')
plt.show()
5. 정확도 개선 작업 - 2
이미지를 보고 수동으로 레이블을 할당한다.
y_representative_digits = np.array([
8, 0, 1, 3, 6, 7, 5, 4, 2, 8,
2, 3, 9, 5, 3, 9, 1, 7, 9, 1,
4, 6, 9, 7, 5, 2, 2, 1, 3, 3,
6, 0, 4, 9, 8, 1, 8, 4, 2, 4,
2, 3, 9, 7, 8, 9, 6, 5, 6, 4
])
이제 레이블된 50개의 샘플로 이루어진 데이터셋이 준비되었다. 이 이미지들은 랜덤으로 고른 샘플이 아니며 각 클러스터를 대표하는 이미지이다.
6. 정확도 개선 작업 -3
성능이 개선되었는지 확인해본다.
log_reg = LogisticRegression(max_iter=10_000)
log_reg.fit(X_representative_digits ,y_representative_digits)
log_reg.score(X_test, y_test)
▶ 50개 샘플로 모델을 훈련했음에도 정확도가 향상되었다.
샘플에 레이블을 부여하는 것은 비용이 많이 들고 어려운데, 이러한 경우 랜덤 샘플 대신 대표 샘플에 레이블을 할당하는 것이 좋은 방법이 될 수 있다.
[정확도 개선 방법2: 레이블 전파]
대표 샘플의 레이블을 동일한 클러스터에 있는 모든 샘플로 전파하는 레이블 전파 방식을 이용하여 정확도를 향상시킬 수 있다.
※ 레이블 전파 알고리즘은 이전에 레이블이 지정되지 않은 데이터 포인트에 레이블을 할당하는 반지도 기계 학습 알고리즘이다. 기계 학습에서 이 알고리즘을 사용하려면 예제의 극히 일부에만 레이블이나 분류가 있다. 이러한 레이블은 알고리즘의 모델링, 피팅 및 예측 프로세스 중에 레이블이 지정되지 않은 데이터 포인트로 전파한다.
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]
모델을 다시 훈련하고 성능을 확인해본다.
log_reg = LogisticRegression()
log_reg.fit(X_train, y_train_propagated)
log_reg.score(X_test, y_test)
▶ 정확도가 이전보다 약 3.5% 향상되었다.
[정확도 개선 방법3: 이상치 제거]
클러스터 중심에서 가장 먼 1%의 샘플을 무시하면 더 나은 결과를 얻을 수 있는지 살펴본다. 이렇게 하면 일부 이상치가 제거될 것이다.
아래는 각 샘플에서 가장 가까운 클러스터 중심까지의 거리를 계산한 다음 각 클러스터에 대해 가장 큰 1%의 거리를 -1로 설정하고, -1 거리로 표시된 샘플이 없는 데이터셋을 생성한다.
percentile_closest = 99
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]
부분적으로 전파한 이 데이터셋에서 모델을 다시 훈련하고 성능 확인
log_reg = LogisticRegression(max_iter=10_000)
log_reg.fit(X_train_partially_propagated, y_train_partially_propagated)
log_reg.score(X_test, y_test)
▶ 서적과는 달리, 오히려 정확도가 비슷한 수준인데, 이상치가 제거된 레이블의 정확도가 낮은지를 확인해 본다.
(y_train_partially_propagated == y_train[partially_propagated]).mean()
▶ 이상치가 제거된 레이블이 96.1%의 정확도를 보인다.
이상치를 제거했음에도 정확도가 떨어지는 이유에 대해 챗gpt에 물어본 결과,
레이블의 정확도가 96.1%로 높은 상황에도 불구하고 이상치를 제거한 후의 정확도가 낮은 이유는 여러 가지 복합적인 요소들이 작용했기 때문입니다. 중요한 정보를 가진 이상치를 제거했을 가능성, 데이터의 양 감소로 인해 학습 부족, 모델의 민감도와 데이터 전처리 방법의 문제 등이 모두 영향을 미쳤을 수 있습니다.
이를 보완하기 위해 다음과 같은 방법을 고려할 수 있습니다:
- 신중한 이상치 탐지 및 제거: 도메인 전문가의 조언을 받아 이상치를 신중하게 탐지하고 제거하세요.
- 교차 검증: 여러 방법을 사용하여 충분한 교차 검증을 통해 가장 적합한 모델을 찾아내세요.
- 모델 평가 지표 다양화: 정확도뿐만 아니라 다른 지표들(F1-score, precision, recall 등)을 사용하여 모델 성능을 다면적으로 평가하세요.
[사이킷런에서의 레이블 전파]
사이킷런은 레이블을 자동으로 전파할 수 있는 두 개의 클래스를 제공한다.
sklearn.semi_supervised 패키지에 있는
- LabelSperading
- LabelPropagation
두 클래스 모두 모든 샘플 간에 유사도 행렬을 구축하고 레이블이 지정된 샘플에서 레이블이 지정되지 않은 비슷한 샘플로 레이블을 반복적으로 전파한다.
또한 sklearn.semi_supervised 패키지에는 SelfTrainingClassifier 라는 클래스도 있는데, 이 클래스에 기본 분류기(예. 랜덤 포레스트)를 제공하면 레이블이 지정된 샘플에서 훈련한 다음 이를 사용하여 레이블이 지정되지 않은 샘플의 레이블을 예측한다. 그런 다음 가장 확신하는 레이블로 훈련 세트를 업데이트하고 더 이상 레이블을 추가할 수 없을 때까지 이 훈련과 레이블 지정 프로세스를 반복한다.
LabelSperading 클래스를 사용한 레이블 전파 및 성능 측정 코드
from sklearn.semi_supervised import LabelSpreading
# 모든 레이블 초기화
y_train_semi_supervised = np.full(y_train.shape, -1) # -1은 레이블이 없음을 의미함
y_train_semi_supervised[representative_digit_idx] = y_representative_digits
# 반지도 학습 (LabelSpreading)
label_spreading = LabelSpreading(kernel='knn', alpha=0.2) # alpha는 정규화 정도
label_spreading.fit(X_train, y_train_semi_supervised)
#성능 측정
label_spreading.score(X_test, y_test)
▶ 정확도가 94.4%로 크게 향상되었다.
능동 학습(active learning)
모델과 훈련 세트를 지속적으로 향상시키기 위해 다음 단계로 능동 학습을 몇 번 반복할 수 있다. 이 방법은 전문가가 학습 알고리즘과 상호 작용하여 알고리즘이 요청할 때 특정 샘플의 레이블을 제공한다.
능동 학습 중 가장 널리 사용되는 것은 불확실성 샘플링(uncertainty sampling)이며, 작동 방식은 다음과 같다.
- 수집한 레이블된 샘플에서 모델 훈련. 이 모델을 사용해 레이블되지 않은 모든 샘플에 대한 예측을 만든다.
- 모델이 가장 불확실하게 예측한 샘플(추정 확률이 낮은 샘플)을 전문가에게 보내 레이블을 붙인다.
- 레이블을 부여하는 노력만큼의 성능이 향상될 때까지 반복
다른 능동 학습 전략은
- 모델을 가장 크게 바꾸는 샘플
- 모델의 검증 점수를 가장 크게 덜어 뜨리는 샘플
- 여러 개의 모델이 동일한 예측을 내지 않는 샘플
에 대해 레이블을 요청하는 것이다.
다음 내용
[출처]
핸즈 온 머신러닝
https://www.php.cn/ko/faq/514056.html
'[파이썬 Projects] > <파이썬 머신러닝>' 카테고리의 다른 글
[머신러닝] 도커(Docker) 설치하기 (1) | 2024.12.07 |
---|---|
[머신러닝] 텐서플로 모델 훈련과 배포 (0) | 2024.12.07 |
[머신러닝] 군집: k-평균 알고리즘 훈련하기 (0) | 2024.11.16 |
[머신러닝] 머신러닝 기반 분석 모형 선정 (1) | 2024.11.16 |
[머신러닝] 차원 축소: 랜덤 투영, 지역 선형 임베딩 (3) | 2024.11.15 |