[ML] 9. Unsupervised Learning (2)

앙버터·2023년 11월 10일
0

ML

목록 보기
10/10

1. Clustering for image segmentation

  • 이미지를 여러 개의 segment로 분할
  • Semantic segmentation : 동일한 종류의 물체에 속한 모든 픽셀 = 같은 세그먼트 (5명의 사람 = 모두 1개의 segment)
  • Instance segmentation : 동일한 종류의 물체이더라도 개개인을 구분 (5명의 사람 = 5개의 segment)

1.1 색상 분할

비슷한 색상의 픽셀을 같은 세그먼트에 할당

  1. 데이터 불러오기
import PIL

image = np.asarray(PIL.Image.open(filepath))
image.shape
>> (533, 800, 3) 
  • 3D array로 표현되는 배열 : (높이, 너비, 컬러 채널 개수)
    첫번째 차원이 높이, 두번째 차원이 너비, 세번째 차원이 컬러 채널 개수
  • 컬러 채널 개수 = 3 : RGB 채널
    ➡ 각 픽셀에 대해 R,G,B의 강도를 담은 3D 벡터가 있음
  1. 이 이미지를 표현하는 3D 배열array을 RGB 색상의 긴 리스트로 변경 후 k-means를 사용해 색상을 클러스터로 모음
X = image.reshape(-1, 3) # [R, G, B] * (데이터개수/3) 차원
kmeans = KMeans(n_clusters=8, random_state=42).fit(X) #8개 색상
segmented_img = kmeans.cluster_centers_[kmeans.labels_]
segmented_img = segmented_img.reshape(image.shape)
  1. 모든 초록색을 하나의 클러스터로 만든다
  2. 그 클러스터 내의 각 색상에 대해, 클러스터의 평균 컬러를 찾는다
  3. 모두 평균 색으로 변경한다
  4. 만들어진 긴 색상의 리스트를 원본 이미지와 동일하게 reshape

  • 8개보다 색상을 작게 하면 무당벌레가 독자적인 클러스터를 만들지 못함
    : k-means가 비슷한 크기의 클러스터를 만드는 경향이 있기 때문
    (클러스터들의 크기가 비슷할 때 더 좋은 성능이 나온다.)

2. Clustering for preprocessing

차원 축소dimentionally reduction(입력 특성 개수를 감소시키는 것)에 클러스터링을 사용 ➡ 학습 속도를 높이는 효과
supervised learning을 위한 전처리

  • simple MNIST-like dataset : 1,797개, 8x8 이미지 (0~9)

모든 데이터셋을 사용하여 학습

#load dataset
from sklearn.datasets import load_digits
X_digits, y_digits = load_digits(return_X_y=True)

# training set과 test set으로 분할
X_train, X_test, y_train, y_test= train_test_split(X_digits, y_digits)

# Logistic regression 모델 훈련
from sklearn.linear_model import LogisticRegression
log_reg = LogisticRegression()
log_reg.fit(X_train, y_train)

# test dataset에 대한 성능평가
log_reg.score(X_test, y_test)
>> 0.906801007556675
# 전체 데이터셋에 대해 지도학습을 한 결과이므로, 
#	클러스터링으로 전처리를 하면 더 좋은 결과가 나와야 한다.

k-means 사용한 전처리
: 훈련 세트를 k개의 클러스터로 그룹화하고, 각 이미지를 k개 클러스터까지의 거리로 환산한 새로운 데이터셋을 만드는 과정

  • 훈련 세트를 50개의 클러스터로 모으고, 이미지를 50개 클러스터까지의 거리로 바꾼다. 그리고 로지스틱 회귀 적용
from sklearn.pipelineimport make_pipeline
pipeline = make_pipeline(KMeans(n_clusters=50),
						LogisticRegression(max_iter=10000))
pipeline.fit(X_train, y_train)

pipeline.score(X_test, y_test)
>> 0.924433249370277

클러스터링 진행 후 비슷한 모양의 숫자로만 모여있는 상태에서 로지스틱 회귀를 진행한다.

  • 클러스터의 개수 조절 (그리드 서치 사용)
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_
>> {'kmeans__n_clusters' : 83}

✔ 그리드 서치의 범위를 넓히면 더 좋은 결과가 나올수도 있으나, 샘플 수가 적기 때문에 클러스터 개수가 너무 많아지면 한 클래스에 너무 적은 데이터가 들어가 의미없는 결과를 얻게될수도 있다.

pipeline = make_pipeline(
                KMeans(n_clusters=83, n_init=10, random_state=42),
                LogisticRegression(max_iter=10000))
pipeline.fit(X_train, y_train)
pipeline.score(X_test, y_test)
>> 0.926521410579346

3. Clustering for Semi-supervised learning

  • 50개의 레이블링된 데이터로 로지스틱 회귀 훈련
n_labeled = 50
log_reg = LogisticRegression(max_iter=10_000)
log_reg.fit(X_train[:n_labeled], y_train[:n_labeled])
# 레이블링된 데이터로만 학습

log_reg.score(X_test, y_test) # test data로 성능측정
>> 0.7481108312342569

전체 대상으로 한 학습보다 정확도는 낮지만, 레이블링된 데이터가 적게 필요함

  • 각 클러스터(k=50)에서 센트로이드와 가장 가까운 샘플을 하나씩 선택하여 50개 샘플을 선택 ➡ 각 클러스터의 대표 이미지
k = 50
kmeans = KMeans(n_clusters=k, random_state=42)
# X_digits_dist : 각 샘플과 센트로이드와의 거리를 담고있는 배열
X_digits_dist = kmeans.fit_transform(X_train) # y_train 없이 진행
# 거리가 가장 짧은 샘플의 인덱스
representative_digit_idx = X_digits_dist.argmin(axis=0) 
X_representative_digits = X_train[representative_digit_idx] # 50개 샘플

# 수동으로 레이블링
y_representative_digits = np.array([
    1, 3, 6, 0, 7, 9, 2, 4, 8, 9,
    5, 4, 7, 1, 2, 6, 1, 2, 5, 1,
    4, 1, 3, 3, 8, 8, 2, 5, 6, 9,
    1, 4, 0, 6, 8, 3, 4, 6, 7, 2,
    4, 1, 0, 7, 5, 1, 9, 9, 3, 7
])

# 로지스틱 회귀 훈련
log_reg = LogisticRegression(max_iter=10_000)
log_reg.fit(X_representative_digits, y_representative_digits)
log_reg.score(X_test, y_test)
>> 0.8488664987405542

✔ 무작위로 선택한 샘플 대신, 대표 샘플에 레이블을 할당하는 것이 더 좋은 방법이다.

3.1 label propagation

  • 대표 샘플의 레이블을 동일한 클러스터에 있는 모든 샘플로 전파
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(max_iter=10_000)
log_reg.fit(X_train, y_train_propagated)
log_reg.score(X_test, y_test)
>> 0.8967254408060453
  • 클러스터 경계에 있는 샘플들에는 아마 레이블이 잘못 부여되었을 것이다.
  • 센트로이드와 가까운 99%의 샘플에만 레이블을 전파
  • 1%는 무시하여 outlier 제거

    레이블이 전파되는 샘플의 퍼센트가 적을수록 센트로이드와 더 가까운 샘플만 레이블링하므로 outlier가 더 적어진다. 하지만 그 수를 너무 낮추면 훈련 샘플의 개수가 너무 적어져 정확도가 낮아질 수 있다.

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)
>> 0.9093198992443325

전체 데이터셋에서 훈련한 성능과 가깝다.
✔ 적은 labeled data 만으로도 좋은 성능을 낼 수 있는 준지도학습

np.mean(y_train_partially_propagated == y_train[partially_propagated])
>> 0.9755555555555555
# 2.5%의 샘플에 대해 잘못 레이블링되어있음

레이블 전파된 데이터가 97% 실제 데이터와 같음을 확인할 수 있다.

4.1 semi-supervised learning in scikit-learn

sklearn.semi_supervised package

  • LabelSpreading and LabelPropagation (앞서 배운 방식)
    : 모든 샘플 사이의 유사도 행렬similarity matrix 생성
    반복적으로 레이블을 비슷한 unlabeled 샘플에 전파

  • SelfTrainingClassifier

  1. 레이블이 있는 일부 데이터로 base classifier 훈련
  2. 1번의 모델로 레이블이 없는 데이터의 레이블을 예측
  3. 예측 결과를 레이블로 하여 훈련 세트를 업데이트
  4. 1,2,3 의 훈련과 레이블링 과정을 반복하여 모든 샘플이 레이블링될때까지 진행

4.2 Active learning

  • 전문가(사람)이 개입
  • 알고리즘이 레이블을 요구할 때 제공
  • Uncertainty sampling
  1. 지금까지 수집한 레이블된 샘플에서 모델을 훈련
    이 모델을 사용해 레이블되지 않은 모든 샘플에 대한 예측 생성
  2. 모델이 가장 불확실하게 예측한 샘플(추정 확률이 낮은 샘플)을 전문가에게 보내 레이블을 붙임
    (레이블링을 했을 때 가장 오차가 클 수 있는 데이터를 제거)
  3. 레이블을 부여하는 노력만큼의 성능이 향상되지 않을 때까지 반복
    ✅ 더 적은 반복으로 속도를 높일 수 있다.
  • 다른 방법

    • 모델을 가장 크게 바꾸는 샘플
    • 모델의 검증 점수를 가장 크게 떨어뜨리는 샘플
    • 여러 개의 모델이 동일한 예측을 내지 않는 샘플
      에 대해 레이블을 요청

    💜 끝 💜

profile
그래도 해야지 어떡해

0개의 댓글