[혼자 공부하는 머신러닝+딥러닝] 15강. k-평균 알고리즘 작동 방식을 이해하고 비지도 학습 모델 만들기 - 이미지 평균의 자동화
https://www.youtube.com/watch?v=SBdy0nSctRM&list=PLJN246lAkhQjoU0C4v8FgtbjOIXxSs_4Q&index=15
- 14강에서는 300개의 이미지 중 사과, 파인애플, 바나나가 몇개인지 알고 있었기 때문에 비지도 학습이 아니었다.
- 아무 정보가 없는 사진 300개를 받았을 때 대표 이미지(평균값)을 자동으로 찾아주는 알고리즘 중 하나가 k-평균 알고리즘이다.
- k-평균 알고리즘의 작동 방식
1. 무작위로 k개의 '클러스터 중심'을 정한다
2. 정의된 클러스터 중심으로 부터 가장 가까운 샘플들을 하나의 클러스터로 묶는다.
- n_clusters로 생성할 클러스터 수를 정의할 수 있다.
3. 각각 묶여있는 클러스터 내 샘플들로 다시 클러스터 중심(평균값)을 계산한다.
- 클러스 중심은 클러스터 내 가장 많이 비슷한 샘플들 위치로 근접한다
4. 변경된 클러스터 중심에서 가장 인접한 샘들끼리 다시 클러스터링한다.
5. 클러스터 중심에 변화가 없을 때까지 이 과정을 반복한다.
- 유사한 샘플들끼리 있다면 클러스터 중심의 위치는 변화가 없게된다.
- 샘플, 너비, 높이 형태의 3차원 배열을 샘플, 너비 * 높이 형태의 2차원 배열로 만든다.
import numpy as np
fruits = np.load('fruits_300.npy')
fruits_2d = fruits.reshape(-1, 100*100) # 3차원 샘플을 2차원 형태로 변경한다
- fit()으로 모델 훈련을 한다. 다만 비지도 학습이라 타겟 데이터 없이 입력 데이터만 정의한다.
from sklearn.cluster import KMeans
km = KMeans(n_clusters=3, random_state=42) # 3개의 클러스터 정의
km.fit(fruits_2d) # 입력 데이터만 정의함
- 군집에 대한 결과는 .labels_ 을 이용해 확인할 수 있다.
- 0, 1, 2의 넘버링은 자동으로 생성되기 때문에 순서에 대한 의미는 없다.
- 각 숫자의 의미는 해당 샘플이 어떤 클러스터 중심에 속해있는지를 의미한다.
print(km.labels_)
[0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 2 0 2 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 2 2 0 0 0 0 0 0 0 0 2 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 2 2 2 2 2 2 2 2 2 2 2
2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1]
- 레이블 내 클러스터에 몇개의 샘플들이 존재하는지 확인해보자
- np.unique()를 이용해 유일한 값들을 확인한다.
- return_counts : 유니크한 값들에 포함되는 샘플 수를 보여준다.
print(np.unique(km.labels_, return_counts=True))
(array([0, 1, 2]), array([ 91, 98, 111], dtype=int64))
- 클러스터 내 이미지들을 직접 그려서 확인하기
import matplotlib.pyplot as plt
def draw_fruits(arr, ratio=1): # 전달값은 arr, ratio는 figsize로 사용
n = len(arr) # n은 샘플 개수입니다
# 한 줄에 10개씩 이미지를 그립니다. 샘플 개수를 10으로 나누어 전체 행 개수를 계산합니다.
rows = int(np.ceil(n/10))
# 행이 1개 이면 열 개수는 샘플 개수입니다. 그렇지 않으면 10개입니다.
cols = n if rows < 2 else 10 # 기본 한줄에 10개를 그리고 2보다 작다면 한줄에 1개만 그린다.
fig, axs = plt.subplots(rows, cols,
figsize=(cols*ratio, rows*ratio), squeeze=False)
for i in range(rows): # 한 줄마다
for j in range(cols): # 기본 10개의 샘플을 그린다.
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번에 속한 샘플 확인
draw_fruits(fruits[km.labels_==0])
- 1번에 속한 샘플들
draw_fruits(fruits[km.labels_==1])
- 2번에 속한 샘플들
draw_fruits(fruits[km.labels_==2])
- 클러스터 중심을 활용하여 새로운 샘플 검증
- fit() 을 이용한 훈련으로 찾은 "클러스터 중심(샘플 평균값)"은 .cluster_centers_에 저장되어 있다.
- 3차원 배열로 바꿔 이미지로 확인을 해보면 14강에서 직접 만들어본 평균 이미지 모습과 거의 같다.
draw_fruits(km.cluster_centers_.reshape(-1, 100, 100), ratio=3)
- k-평균의 유용한 기능으로 .transform() 메소드이다.
- 특정 샘플의 정보를 집어 넣으면, 그 샘플과 클러스트 중심들까지의 거리로 반환을 해준다.
- 100000개(픽셀)의 특성을 3개(거리)의 특성으로 차원을 줄여주는 변환기로 사용이 가능하다.
print(km.transform(fruits_2d[100:101]))
[[5267.70439881 8837.37750892 3393.8136117 ]]
- 마지막 3번째 값이 제일 낮다. 가깝다는 뜻이다. 배열은 0부터 시작하기 때문에 위치는 2번째가 된다.
- 이 거리를 이용해 예측 클래스를 정의해주는 .predict() 메소드가 있다.
print(km.predict(fruits_2d[100:101]))
[2]
- 해당 샘플의 그림을 그려서 확인을 해보자
draw_fruits(fruits[100:101])
- 클러스터 중심을 옮기면서 찾는 수행을 반복한 횟수는 n_iter_에 있다.
print(km.n_iter_)
4
- 최적의 K 찾기
- k 평균 알고리즘의 단점
- 실제 샘플의 데이터를 모르는 경우 클러스터 개수를 몇 개로 정의해야 할지 판단하기 쉽지가 않다.
- 엘보우(elbow) 방법
1) .transform()으로 구한 거리의 제곱 합을 이너셔(inertia)라 한다.
2) 이너셔 값이 작다는 것은 클러스터 중심에 샘플들이 잘 모여있다고 할 수 있다.
3) 일반적으로 클러스터 개수(k)를 늘리면 더 곳곳에 센트로이드가 자리하게 되어 이너셔가 줄어든다.
- 이 원리를 이용해 클러스터 개수를 바꿔가면서 이너셔 값의 변화를 확인하면 감소 속도가 꺽이는 지점이 있다.
- 해당 위치를 최적의 k 값으로 선택한다.
- .inertia_ : k-평균 에서 자동으로 계산한 이너셔 값
# inertia는 클러스터 중심과 거기에 속한 샘플들간 거리를 제곱하여 합한 값이다.
inertia = []
for k in range(2, 7):
km = KMeans(n_clusters=k, n_init='auto', random_state=42)
km.fit(fruits_2d)
inertia.append(km.inertia_)
plt.plot(range(2, 7), inertia)
plt.xlabel('k')
plt.ylabel('inertia')
plt.show()
- 그래프를 확인하면 꺽이는 지점이 있다. 이 위치보다 n_clusters를 늘려도 inertia가 더 안낮아짖는 경우도 있다.
- 여기서는 꺽이는 지점을 최적의 k라 정의했지만, 항상 정확한 방법은 아니다.
- 실재로 다른 지도학습이나 문제에 적용하면서 후속작업을 진행하고 그 결과를 활용해 feedback 하면서 모델을 개선하는 경우가 일반적이다.
- K-평균 알고리즘을 사용하기 위한 적절한 데이터
- k-평균 알고리즘은 클러스터 중심에 대한 샘플들의 직선거리를 이용하기에 원형에 가까운 클러스터 형태에 적합하다.
- 데이터 클러스터 형태가 타원형으로 묶이는 경우가 있다면 잘 찾지 못한다.
- 특성간 거리가 비슷한 스케일 내의 값들이어야 k-평균이 잘 분석한다.
- 표준화 등으로 스케일 전처리를 해주고 사용하는 방법들을 고려해야 한다.
- 이번 강의에서 다룬 사진 데이터의 경우 0~255 사이의 안정적인 데이터 범위로 정의되어 있어서 추가적인 전처리가 요구되지 않았다.
- 배열 슬라이싱의 이해
- a[100]은 a 배열의 101번째 요소를 반환한다.
- a[100:101]은 a배열의 101번째 요소를 슬라이싱한 결과를 반환하는데 요소를 리스트로 감싼 결과를 반환한다.
- 2차원으로 만들어진 fruits_2d 배열에서 슬라이싱을 하면 2차원 배열로 반환한다
- transform 함수는 2차원 배열을 기대한다.
# 3차원 배열 생성
a = [
[[1, 2, 3], [4, 5, 6], [7, 8, 9]],
[[10, 11, 12], [13, 14, 15], [16, 17, 18]],
[[19, 20, 21], [22, 23, 24], [25, 26, 27]]
]
# a[0]의 값 출력
print(a[0])
# 결과: [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
# a[0:1]의 결과 값 출력
print(a[0:1])
# 결과: [[[1, 2, 3], [4, 5, 6], [7, 8, 9]]]
- reference :
https://velog.io/@simon919/%ED%98%BC%EA%B3%B5%EB%A8%B8%EC%8B%A0-6-2.-k-%ED%8F%89%EA%B7%A0-8d40083s
'데이터분석-머신러닝-AI > 강의 정리' 카테고리의 다른 글
[혼자 공부하는 머신러닝+딥러닝] 17강. 인공 신경망 ▶️ 간단한 인공 신경망 모델 만들기 - 케라스 맛보기 (2) | 2023.11.20 |
---|---|
[혼자 공부하는 머신러닝+딥러닝] 16강. 주성분 분석: 차원 축소 알고리즘 PCA 모델 만들기 - 차원축소 PCA를 다른 알고리즘과 함께 사용하기 (0) | 2023.11.19 |
[혼자 공부하는 머신러닝+딥러닝] 14강. 흑백 이미지 분류 방법과 비지도 학습, 군집 알고리즘 이해하기 - 지도 학습 샘플로 군집 알고리즘 학습 (1) | 2023.11.15 |
[혼자 공부하는 머신러닝+딥러닝] 13강. 트리의 앙상블 - 다양한 앙상블 알고리즘 (1) | 2023.11.14 |
[혼자 공부하는 머신러닝+딥러닝] 12강. 교차 검증과 그리드 서치 - (1) | 2023.11.13 |
댓글