비슷한 색상의 픽셀을 같은 세그먼트에 할당
import PIL
image = np.asarray(PIL.Image.open(filepath))
image.shape
>> (533, 800, 3)
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)
- 모든 초록색을 하나의 클러스터로 만든다
- 그 클러스터 내의 각 색상에 대해, 클러스터의 평균 컬러를 찾는다
- 모두 평균 색으로 변경한다
- 만들어진 긴 색상의 리스트를 원본 이미지와 동일하게 reshape
차원 축소dimentionally reduction(입력 특성 개수를 감소시키는 것)에 클러스터링을 사용 ➡ 학습 속도를 높이는 효과
supervised learning을 위한 전처리
#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개 클러스터까지의 거리로 환산한 새로운 데이터셋을 만드는 과정
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
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
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
✔ 무작위로 선택한 샘플 대신, 대표 샘플에 레이블을 할당하는 것이 더 좋은 방법이다.
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
레이블이 전파되는 샘플의 퍼센트가 적을수록 센트로이드와 더 가까운 샘플만 레이블링하므로 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% 실제 데이터와 같음을 확인할 수 있다.
sklearn.semi_supervised
package
LabelSpreading
and LabelPropagation
(앞서 배운 방식)
: 모든 샘플 사이의 유사도 행렬similarity matrix 생성
반복적으로 레이블을 비슷한 unlabeled 샘플에 전파
SelfTrainingClassifier
다른 방법
💜 끝 💜