Pupbani는 샘플의 이름 즉 타깃값을 모르는 상태에서의 분류를 하는 방법을 고민하다가 방법을 찾아 냈다.
바로 K-평균 군집 알고리즘이다.
- 평균값을 자동으로 찾아준다.
- 평균값은 군집의 중심에 존재한다.
- 이를 "클러스터 중심(Cluster Center)" 또는 "센트로이드(Centroid)" 라고 부른다.
K-평균을 사용해서 비지도학습 모델을 만들어보자.
K-Means
동작 방식은 다음과 같다.
- 무작위로 K갱의 클러스터 중심을 정한다.
- 각 샘플에서 가장 가까운 클러스터 중심을 찾아 해당 클러스터의 샘플로 지정한다.
- 클러스터에 속한 샘플의 평균값으로 클러스터 중심을 변경한다.
- 클러스터 중심에 변화가 없을 때까지 2번으로 돌아가 반복한다.
이제 K-Means 모델을 직접 만들어보자.
먼저 300개의 과일 데이터를 준비하여 모델에 사용할 수 있게 전처리를 해준다.
import numpy as np
fruits = np.load('fruits_300.npy')
# K-Means 모델은 2차원 배열 형태의 데이터가 필요함.
# (샘플 개수, 너비, 높이) -> (샘플 개수, 너비 x 높이)
fruits_2d = fruits.reshape(-1, 100*100)
K-Means 모델을 생성하고 학습한다.
- sklearn.cluster 모듈아래 KMeans 클래스에 구현되어 있다.
from sklearn.cluster import KMeans
km = KMeans(n_clusters=3, random_state=42) # 클러스터 3개
km.fit(fruits_2d) # 과일 데이터 군집별 분류
print(km.labels_) # 분류된 결과, labels_ 속성에 저장됨
분류된 결과를 좀더 살펴보자.
numpy의 unique 메서드를 통해 각 레이블 별 샘플 개수를 확인해보자.
result = np.unique(km.labels_, return_counts=True)
print(f"0번 샘플 개수: {result[1][0]}개")
print(f"1번 샘플 개수: {result[1][1]}개")
print(f"2번 샘플 개수: {result[1][2]}개")
이제 각 클러스터별로 어떤 과일들을 모았는지 이미지로 확인해보자.
import matplotlib.pyplot as plt
def draw_fruits(arr, ratio=1):
n = len(arr) # n은 샘플 개수입니다
# 한 줄에 10개씩 이미지를 그립니다. 샘플 개수를 10으로 나누어 전체 행 개수를 계산합니다.
rows = int(np.ceil(n/10))
# 행이 1개 이면 열 개수는 샘플 개수입니다. 그렇지 않으면 10개입니다.
cols = n if rows < 2 else 10
fig, axs = plt.subplots(rows, cols,
figsize=(cols*ratio, rows*ratio), squeeze=False)
for i in range(rows):
for j in range(cols):
if i*10 + j < n: # n 개까지만 그립니다.
axs[i, j].imshow(arr[i*10 + j], cmap='gray_r')
axs[i, j].axis('off')
plt.show()
0번 레이블 - 111개
draw_fruits(fruits[km.labels_==0])
1번 레이블 - 98개
draw_fruits(fruits[km.labels_==1])
2번 레이블 - 91
draw_fruits(fruits[km.labels_==2])
클러스터 중심
K-Means로 분류한 클러스터의 중심(센트로이드)은 cluster_centers_ 속성에 저장되어 있다.
중심을 이미지 형태로 출력하려면 2차원에서 3차원으로 변환해줘야 한다.
draw_fruits(km.cluster_centers_.reshape(-1, 100, 100), ratio=3)
훈련 데이터 샘플에서 클러스터 중심까지 거리로 변환해주는 transform() 메서드를 지원함.
2차원 데이터를 넣어야한다.
반환된 배열은 [[0번 레이블 까지의 거리, 1번 레이블 까지의 거리, 2번 레이블 까지의 거리]] 형태 이다.
print(km.transform(fruits_2d[100:101]))
배열 값에 따르면 이 데이터는 0번 레이블에 속한다.
진짜 그러한가 알아보자.
print(km.predict(fruits_2d[100:101]))
draw_fruits(fruits[100:101])
0번 레이블에 속하는 것을 알 수 있다.
앞에서 말했듯 K-Means 알고리즘이 클러스터 중심을 찾는 과정은 반복적으로 클러스터 중심을 옮기면서 최적의 중심을 찾는다.
이 옮기는 횟수는 n_iter_ 속성에 저장된다.
print(km.n_iter_)
최적의 K 찾기
K-Means의 단점 중 하나는 클러스터 개수를 사전에 지정해야 한다는 것이다.
실전에는 몇개의 클러스터가 있는지 알 수 없다.
어떻게 하면 적절한 클러스터 개수를 찾을 수 있을까?
대표적인 방법은 엘보우(elbow) 방법이다.
엘보우 방법은 클러스터 개수를 늘려나가면서 이너셔의 변화를 관찰하여 최적을 클러스터 개수를 찾는 방법이다.
- 이너셔(inertia) : 클러스터 중심과 클러스터에 속한 샘플 사이의 거리의 제곱 합이다.
- 이너셔는 클러스터의 샘플이 얼마나 가깝게 있는지 나타내는 값이다.
- 클러스터 개수를 증가 시키면서 이너셔를 그래프로 그리면 감소하는 속도가 꺾이는 지점이 있는데 이 지점 부터는 클러스터의 개수를 늘려도 클러스터에 잘 밀집된 정도가 개선되지 않는다.
- 이 꺾이는 지점에서의 클러스터 개수가 최적의 클러스터 개수이다.
inertia = []
for k in range(2, 7):
km = KMeans(n_clusters=k, random_state=42)
km.fit(fruits_2d)
inertia.append(km.inertia_)
plt.plot(range(2, 7), inertia)
plt.title("최적의 클러스터 찾기")
plt.xlabel("클러스터 개수")
plt.ylabel("이너셔셔")
plt.show()
그래프에서 보면 꺾이는 지점이 뚜렷하진 않지만 K가 3일 때 기울기가 조금 바뀌었다.
최적의 K개수는 3이라고 볼 수 있다.
Pupbani는 K-Means를 통해 과일을 분류하는 비지도 학습모델을 만들었다.
이제 마켓팅 팀에 전달하러 가보자.
'AI > 기계학습(Machine Learning)' 카테고리의 다른 글
[기계학습/ML]16. SVM(Support Vector Machine) 서포트 벡터 머신 (0) | 2022.12.10 |
---|---|
[기계학습/ML]15. 비지도학습 - 주성분 분석 (0) | 2022.12.05 |
[기계학습/ML]13. 비지도학습 - 군집(Clustering) (0) | 2022.12.05 |
[기계학습/ML]12. 앙상블 학습 - 랜덤 포레스트, 엑스트라 트리, 그레이디언트 부스팅 (0) | 2022.10.24 |
[기계학습/ML]11. 검증 세트 - 교차 검증, 그리드 서치 (0) | 2022.10.24 |