사진 파일 데이터가 넘파이 배열의 기본 저장 포맷인 npy
로 저장되어있어서
fruits = np.load('fruits_300.npy)
해주고
넘파이 배열로 저장된 이미지를 쉽게 그려주는 함수가 맷플롯립의 imshow()
함수이다!
plt.imshow(fruits[0], cmap ='gray')
plt.show()
cmap에 gray를 입력해서 흑백사진이 나오게끔 하였고 0에 가까울 수록 검게 나타나고 높은 값은 밝게 표시된다 !
cmap = gray_r
으로 설정하면 흑과 백을 반전시켜서 보여준다
plt.imshow(fruits[0], cmap = 'gray_r')
plt.show()
ex) 바나나랑 파인애플 이미지 출력하기
fig, axs = plt.subplots(1,2)
axs[0].imshow(fruits[100], cmap = 'gray_r')
axs[1].imshow(fruits[200], cmap='gray_r')
plt.show()
subplots()
함수를 사용하면 여러개의 그래프를 배열처럼 쌓을 수 있게 해줌!
그리고 이 함수의 두 매개변수는 행과 열을 지정해준다 ~
평균값을 hist()
함수로 히스토그램으로 표현하고 맷플롯립의 legend()
함수를 써서 어떤 과일의 히스토그램인지 범례 만들기
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()
ㄴ 바나나 사진의 평균값은 40 아래에 있음 but 사과&파인애플은 90~100 사이에 많이 분포 -> 바나나는 사진에서 차지하는 영역이 작아서 평균값이 작음 ! & 바나나는 픽셀 평균값만으로 사과나 파인애플과 확실히 구분된다 ~
bar()
함수 = 픽셀 n개에 대한 평균값을 막대그래프로 그려줌
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()
픽셀 평균값을 이제 100 x 100으로 바꿔서 이미지 처럼 출력해서 그래프랑 비교하자
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()
평균값과 가까운 사진 고르는건 절댓값 오차
를 쓰면 좋다!!
ㄴ np.mean(fruits 매열에 있는 모든 샘플
- apple_mean
)
abs_diff = np.abs(fruits-apple_mean)
abs_mean = np.mean(abs_diff, axis = (1,2))
print(abs_mean.shape) # (300,)
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()
각 샘플의 오차 평균을 구해보고 가장 작은 순서대로 100개를 골라서 (apple_mean과 오차가 가장 작은 샘플 100개 고르기) 그래프를 그려본것
np.argsort()
함수는 작은 것에서 큰 순서대로 나열한 abs_mean
배열의 인덱스를 반환해주는 함수!
정리
비슷한 샘플끼리 그룹으로 모으는 작업 : 군집
군집 알고리즘에서 만든 그룹 : 클러스터
정의 : k-평균 군집 알고리즘으로 평균값을 자동으로 구할 수 있음
ㄴ 이 평균값이 클러스터의 중심에 위치해서 클러스터 중심 or 센트로이드 라고 함
k-평균 알고리즘 작동 방식
1. 무작위로 k개의 클러스터 중심을 정함
2. 각 샘플에서 가장 가까운 클러스터 중심을 찾아 해당 클러스터의 샘플로 지정
3. 클러스터에 속한 샘플의 평균값으로 클러스터 중심을 변경
4. 클러스터 중심에 변화가 없을 때 까지 2번으로 돌아가서 반복
k-평균 모델을 훈련하려면 2차원으로 변경해야함
import numpy as np
fruits = np.load('fruits_300.npy')
fruits_2d = fruits.reshape(-1,100*100) # 3차원 -> 2차원
훈련하기
from sklearn.cluster import KMeans
km = KMeans(n_clusters = 3, random_state = 42) # 3으로 저장해서 labels_ = 0,1,2 중 하나
km.fit(fruits_2d)
훈련하면 군집 결과는 labels_
속성에 저장되고 이 길이는 샘플의 개수와 같음
print(km.labels_)
output
[2 2 2 2 2 0 2 2 2 2 2 2 2 2 2 2 2 2 0 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
2 2 2 2 2 0 2 0 2 2 2 2 2 2 2 0 2 2 2 2 2 2 2 2 2 0 0 2 2 2 2 2 2 2 2 0 2
2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 0 2 2 2 2 2 2 2 2 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 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 0 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 0 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]
ㄴ 0,1,2랑 레이블 순서는 의미 없고 0,1,2가 어떤 과일 사진을 주로 모았는지 알아보는게 중요
print(np.unique(km.labels_, return_counts = True))
output
(array([0, 1, 2], dtype=int32), array([111, 98, 91]))
ㄴ 0이 111개, 1이 98개, 2가 91개 모았다는 소리 !
각 클러스터가 어떤 이미지를 나타냈는지 그림으로 출력해봐야함 !! -> 이를 draw_fruits()
함수를 써서 만들어본다~
import matplotlib.pyplot as plt
def draw_fruits(arr, ratio=1):
n=len(arr) # n = 샘플 개수
rows = int(np.ceil(n/10)) # 한줄에 10개 이미지씩 그리려고 10 나누기
cols = n if rows<2 else 10 # 행이 1개면 열의 개수 = 샘플 개수 아니면 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])
ㄴ km.labels == 0 이렇게 쓰면 km.labels 배열에서 값이 0인 위치는 True, 그 외는 False가 됨
난 근데 왜 이따구로 나오지?
draw_fruits(fruits[km.labels_ == 1])
draw_fruits(fruits[km.labels_ == 2])
책에선 ==0 쓰면 내 ==2 이미지로 나오네...왜지...!!!
KMeans 클래스가 찾은 클러스터 중심은 cluster_centers_
속성에 저장되어 있다
이 배열은 fruits_2d
샘플의 클러스터 중심이어서 이미지로 출력하려면 100 x 100 크기의 2차원 배열로 바꿔야 함
draw_fruits(km.cluster_centers_.reshape(-1, 100,100), ratio = 3)
transform()
메서드 = 훈련 데이터 샘플에서 클러스터 중심까지 거리로 변환해줌
print(km.transform(fruits_2d[100:101])) # [[3393.8136117 8837.37750892 5267.70439881]]
첫 번째 클러스터까지의 거리가 가장 작음. 이 샘플은 레이블 0에 속한듯
print(km.predict(fruits_2d[100:101])) # [0]
ㄴ 역시 0으로 예측했당 ㅋㅋ
draw_fruits(fruits[100:101])
print(km.n_iter_) # 4
ㄴ 반복 횟수는 n_iter_
에 저장됨
실전에서는 몇개의 클러스터가 있을 지 모르는데 k-평균 알고리즘의 단점은 클러스터 개수를 지정해야된다는 것이다 !!
: 클러스터 개수를 늘려가면서 이너셔의 변화를 관찰하여 최적의 클러스터 개수를 찾는 방법
이너셔 : 거리의 제곱 합 (클러스터에 속한 샘플이 얼마나 가깝게 모여있는지를 나타내는 값), 클러스터 개수 늘어나면 클러스터 개개의 크기는 줄어들어서 이너셔도 줄어든다
inertia = []
for k in range(2,7): # 클러스터 개수 k를 2~6까지 바꿔가며 5번 훈련함
km = KMeans(n_clusters = k, random_state = 42)
km.fit(fruits_2d)
inertia.append(km.inertia_) # inertia_속성에 저장된 이너셔 값을 inertia 리스트에 추가
# 그래프로 출력
plt.plot(range(2,7), inertia)
plt.xlabel('k')
plt.ylabel('inertia')
plt.show()
ㄴ k=3
에서 그래프의 기울기가 좀 바뀌었다 !!
특성(차원)이 많으면 선형 모델의 성능이 높아지고 훈련 데이터에 쉽게 과대적합됨. 그래서 차원 축소를 해야하고 이는 데이터를 가장 잘 나타내는 일부 특성을 선택해서 데이터 크기를 줄이고 지도 학습 모델의 성능을 향상시킬 수 있당
분산이 큰 방향 = 데이터를 잘 표현하는 어떤 벡터
분산이 가장 큰 방향의 벡터를 주성분이라고 함. 주성분 벡터의 원소 개수 = 원본 데이터셋에 있는 특성 개수
원본 데이터는 주성분을 사용해서 차원을 줄일 수 있다 !
ex) s(4,2)를 주성분에 직각으로 투영해서 1차원 데이터 p(4,5) 만들기
사진 데이터 다운받아서 넘파이 배열로 적재
!wget https://bit.ly/fruits_300_data -O fruits_300.npy
import numpy as np
fruits = np.load('fruits_300.npy')
fruits_2d = fruits.reshape(-1,100*100)
n_components
에 주성분의 개수 지정
from sklearn.decomposition import PCA
pca = PCA(n_components = 50)
pca.fit(fruits_2d)
ㄴ PCA 클래스가 찾은 주성분은 components_ 속성에 저장되어 있음
배열 크기 확인
print(pca.components_.shape) # (50, 10000)
n_components = 50
으로 지정했기 때매 첫번째 차원이 50 & 두 번째 차원은 항상 원본 데이터의 특성 개수와 같은 10,000임
draw_fruits()
함수를 써서 주성분을 그림으로 그리기
draw_fruits(pca.components_.reshape(-1,100,100))
transform()메서드로 차원 축소하기
print(fruits_2d.shape) # (300, 10000)
fruits_pca = pca.transform(fruits_2d)
print(fruits_pca.shape) # (300, 50)
10,000 -> 50 으로 줄임
데이터를 다시 원상복구 할 수도 있음
ㄴ 이를 inverse_transform()
메서드로 쓸 수 있다
fruits_inverse = pca.inverse_transform(fruits_pca)
print(fruits_inverse.shape) # (300, 10000)
ㄴ 10,000으로 복원됐당
이걸 100 x 100 으로 바꿔서 100개씩 나눠서 출력할거임
fruits_reconstruct = fruits_inverse.reshape(-1,100,100)
for start in [0,100,200]:
draw_fruits(fruits_reconstruct[start:start+100])
print('\n')
: 주성분이 원본 데이터의 분산을 얼마나 잘 나타내는지 기록한 값
explained_variance_ratio_
에 각 주성분의 설명된 분산 비율이 기록돼있음
print(np.sum(pca.explained_variance_ratio_)) # 0.9215611769400113
분산을 그래프로 출력
plt.plot(pca.explained_variance_ratio_)
plt.show()
from sklearn.linear_model import LogisticRegression
lr = LogisticRegression()
target = np.array([0]*100 + [1]*100 + [2]*100)
원본 데이터로 cross_validate()
써서 교차 검증 수행하기
from sklearn.model_selection import cross_validate
scores = cross_validate(lr, fruits_2d, target)
print(np.mean(scores['test_score'])) # 0.9966666666666667
print(np.mean(scores['fit_time'])) # 2.30128927230835
fit_time
에는 훈련 시간이 기록되어 있음 (2.3초)
축소된 fruits_pca를 사용하면
scores = cross_validate(lr, fruits_pca, target)
print(np.mean(scores['test_score'])) # 1.0
print(np.mean(scores['fit_time'])) # 0.020006752014160155
정확도는 100% 이고 훈련 시간도 매우 감소함
PCA 클래스는 지정된 비율에 도달할 떄 까지 자동으로 주성분을 찾음
분산의 50%에 달하는 주성분 찾도록 PCA 모델 만들기
pca = PCA(n_components=0.5)
pca.fit(fruits_2d)
print(pca.n_components_) # 2
ㄴ 2개의 특성만으로 원본 데이터에 있는 분산의 50%를 표현할 수 있다
원본데이터 변환하기
fruits_pca = pca.transform(fruits_2d)
print(fruits_pca.shape) # (300, 2) -> 주성분이 2개여서 2
교차 검증 결과는?
scores = cross_validate(lr, fruits_pca, target)
print(np.mean(scores['test_score'])) # 0.9933333333333334
print(np.mean(scores['fit_time'])) # 0.024647998809814452
차원 축소된 데이터를 써서 k-평균 알고리즘으로 클러스터 찾기
from sklearn.cluster import KMeans
km = KMeans(n_clusters = 3, random_state = 42)
km.fit(fruits_pca)
print(np.unique(km.labels_, return_counts = True))
output
(array([0, 1, 2], dtype=int32), array([110, 99, 91]))
KMeans가 찾은 레이블을 써서 과일 이미지 출력하기
for label in range(0,3):
draw_fruits(fruits[km.labels_ == label])
print('\n')
훈련 데이터의 차원을 줄이면 시각화를 할 수 있다는 장점이 있음
for label in range(0,3):
data = fruits_pca[km.labels_ == label]
plt.scatter(data[:,0], data[:,1])
plt.legend(['apple','banana','pineapple'])
plt.show()
아까 사과와 파인애플이 몇개가 같이 나온것도 여기서 보면 사과와 파인애플 클러스터의 경계가 가깝게 붙어있어서 그런 것이다 ~~