Chapter.6 비지도 학습
6-1 군집 알고리즘
비지도 학습은 사람이 가르쳐주지 않아도 데이터에 있는 무언가를 학습하는 머신러닝 알고리즘.
1
2
3
4
5
6
| import numpy as np
import matplotlib.pyplot as plt
fruits = np.load('fruits_300.npy')
print(fruits.shape)
# output: (300,100,100)
|
→ 이 배열의 첫번째 차원(300)은 샘플의 개수, 두번째 차원(100)은 이미지 높이, 세번째 차원(100)은 이미지 너비.
print(fruits[0,0:]): 첫번째 행에 있는 픽셀 100개에 들어 있는 값 출력
1
2
| plt.imshow(fruits[0], cmap='gray')
plt.show()
|
이 흑백 이미지는 사진의 흰 바탕을 검은색으로 만들고 실제 사과가 있어 짙은 부분은 밝은 색으로 바꾸었음.
컴퓨터는 255에 가까운 바탕에 집중한다, 픽셀값이 0이면 출력도 0이 되어 의미가 없기 때문에 픽셀값과 출력값의 의미를 부여하기 위함
1
2
| plt.imshow(fruits[0], cmap='gray_r')
plt.show()
|
1
2
3
4
5
6
| apple = fruits[0:100].reshape(-1, 100*100)
pineapple = fruits[100:200].reshape(-1, 100*100)
banana = fruits[200:300].reshape(-1, 100*100)
print(apple.shape)
# output: (100,10000)
|
→ apple, pineapple, banana 배열의 크기는 (100,10000)이라는 것을 확인할 수 있음
1
2
3
4
5
6
7
8
9
10
11
12
13
| # axis=1로 설정하여 열 방향으로 계산
apple_mean = apple.mean(axis=1)
print(apple_mean)
# Output example:
# [ 88.3346 97.9249 87.3709 98.3703 92.8705 82.6439 94.4244 95.5999
# 90.681 81.6226 87.0578 95.0745 93.8416 87.017 97.5078 87.2019
# ...
# 82.446 87.1789 96.9206 90.3135 90.565 97.6538 91.0098 93.6252
# 87.3867 84.7073 89.1135 86.7646 88.7301 86.643 96.7323 97.2604
# ...
# 101.556 100.7027 91.6098 88.8976]
|
1
2
3
4
5
6
7
| # 히스토그램으로 평균값 분포 확인
plt.hist(np.mean(apple, axis=1), alpha=0.8)
plt.hist(np.mean(pineapple, axis=1), alpha=0.8)
plt.hist(np.mean(banana, axis=1), alpha=0.8)
plt.legend(['apple', 'pineapple', 'banana'])
plt.show()
|
→ 사과와 파인애플이 많이 겹쳐있어서 구분하기가 쉽지 않음. 따라서 샘플의 평균값이 아니라 픽셀별 평균값을 비교
1
2
3
4
5
| fig, axs = plt.subplots(1, 3, figsize=(20, 5))
axs[0].bar(range(10000), np.mean(apple, axis=0))
axs[1].bar(range(10000), np.mean(pineapple, axis=0))
axs[2].bar(range(10000), np.mean(banana, axis=0))
plt.show()
|
1
2
3
4
5
6
7
8
9
| apple_mean = np.mean(apple, axis=0).reshape(100, 100)
pineapple_mean = np.mean(pineapple, axis=0).reshape(100, 100)
banana_mean = np.mean(banana, axis=0).reshape(100, 100)
fig, axs = plt.subplots(1, 3, figsize=(20, 5))
axs[0].imshow(apple_mean, cmap='gray_r')
axs[1].imshow(pineapple_mean, cmap='gray_r')
axs[2].imshow(banana_mean, cmap='gray_r')
plt.show()
|
1
2
3
4
5
6
7
| # 절댓값 오차 계산
abs_diff = np.abs(fruits - apple_mean)
abs_mean = np.mean(abs_diff, axis=(1,2))
print(abs_mean.shape)
# output: (300,)
|
이후 값이 가장 작은 순서대로 100개 선택.(apple_maen과 오차가 가장 작은 샘플 100개)
1
2
3
4
5
6
7
8
9
| apple_index = np.argsort(abs_mean)[:100]
fig, axs = plt.subplots(10, 10, figsize=(10, 10))
for i in range(10):
for j in range(10):
axs[i, j].imshow(fruits[apple_index[i*10 + j]], cmap='gray_r')
axs[i, j].axis('off')
plt.show()
|
→ apple_mean과 가장 가까운 사진 100개가 모두 사과로 출력됨.
- 이렇게 비슷한 샘플끼리 그룹으로 모으는 작업을 군집이라고 한다. 군집은 대표적인 비지도 학습으로 군집 알고리즘에서 만든 그룹을 클러스터라고 한다.
- 타깃값이 없을 때 데이터에 있는 패턴을 찾거나 데이터 구조를 파악하는 머신러닝 방식을 비지도 학습이라 한다.
6-2 k-평균
작동 방식
- 무작위로 k개의 클러스터 중심을 정한다.
- 각 샘플에서 가장 가까운 클러스터 중심을 찾아 해당 클러스터의 샘플 지정
- 클러스터에 속한 샘플의 평균값으로 클러스터 중심 변경
- 클러스터 중심에 변화가 없을 때까지 2번으로 돌아가 반복
1
2
3
4
5
6
7
8
9
10
11
| import numpy as np
fruits = np.load('fruits_300.npy')
fruits_2d = fruits.reshape(-1, 100*100)
from sklearn.cluster import KMeans
km = KMeans(n_clusters=3, random_state=42)
km.fit(fruits_2d)
print(km.labels_)
|
1
2
| print(np.unique(km.labels_, return_counts-True))
# output: (array([0,1,2], dtype=int32), array([91,98,111]))
|
→ 첫번째 클러스터가 91개 샘플을, 두번째 클러스터가 98개의 샘플을, 세번째 클러스터가 111개의 샘플을 모았다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| 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()
draw_fruits(fruits[km.labels_==0])
|
1
| draw_fruits(fruits[km.labels_==2])
|
레이블이 2인 클러스터는 파인애플에 사과와 바나나가 섞여있는 것을 볼 수 있음.
1
| draw_fruits(km.cluster_centers_.reshape(-1,100,100), ratio=3)
|
1
2
3
4
5
| print(km.transform(fruits_2d[100:101]))
# output: [[5267.70439881 8837.37750892 3393.8136117]]
print(km.predict(fruits_2d[100:101]))
# output: [2]
|
→ transform()의 결과에서 짐작할 수 있듯이 레이블 2로 예측되었음.
1
| draw_fruits(fruits[100:101])
|
실제로 출력된 그림도 파인애플로 출력
1
2
3
| # 알고리즘 반복 횟수
print(km.n_iter_)
# output: 3
|
→ 클러스터 중심을 특성 공학처럼 사용해 데이터셋을 저차원으로 변환할 수 있음.
※ 최적의 k 찾기
k-평균 알고리즘의 단점 중 하나는 클러스터 개수를 사전에 지정해야 한다는 것.
→ 적절한 클러스터 개수를 찾기 위한 대표적인 방법인 엘보우방법 활용
k-평균 알고리즘은 클러스터 중심과 클러스터에 속한 샘플 사이의 거리를 잴 수 있다. 이 거리의 제곱 합을 이너셔라고 한다.
클러스터 개수를 증가시키면서 이너셔를 그래프로 그리면 감소하는 속도가 꺾이는 지점이 있는데 그 지점이 마치 팔꿈치 모양이어서 엘보우 방법이라 부름.
1
2
3
4
5
6
7
8
9
| 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.show()
|
k=3인 지점에서 그래프의 기울기가 조금 바뀐 것을 볼 수 있음.