촉촉한초코칩

[혼공머신] Ch06 본문

Study/PBL

[혼공머신] Ch06

햄친구베이컨 2024. 7. 24. 14:54

https://colab.research.google.com/drive/1sfIIY16vBHCjrX5VPn4HrCZR5UYLzgpy#scrollTo=D4p969zjRt35

 

Ch06. 비지도 학습

비슷한 과일끼리 모으자!
- 타깃이 없는 데이터를 사용하는 비지도 학습과 대표적인 알고리즘을 배운다.
- 대표적인 군집 알고리즘인 k-평균을 배운다.
- 대표적인 차원 축소 알고리즘인 주성분 분석(PCA)를 배운다. 

 

06-1 군집 알고리즘

흑백 사진을 분류하기 위해 여러 가지 아이디어를 내면서 비지도 학습과 군집 알고리즘을 이해한다.

문제 : 타깃을 모르는 사진을 종류별로 분류하기 

 

비지도 학습 (Unsupervised Learning)

  • 타깃이 없을 때 사용하는 머신러닝 알고리즘 

픽셀값을 이용해서 사진을 분류한다. 

#과일 사진 데이터 준비하기 
!wget https://bit.ly/fruits_300_data -o fruits_300.npy
import numpy as np
import matplotlib.pyplot as plt 
#fruits : 넘파일 배열이며 fruits_300.npy 파일에 들어 있는 모든 데이터를 담고 있다. 
#wget 실행 후 생성되는 data파일에 데이터가 담겨져 있디. 
fruits = np.load('/content/fruits_300_data')
print(fruits.shape) #(300, 100, 100) -> 샘플 개수, 이미지 높이, 이미지 너비

#첫번째 이미지의 첫번째 행 출력하기 
#마지막 인덱스는 지정하지 않거나 슬라이싱 연산자를 쓰면 첫번째 이미지의 첫번째 행을 모두 선택할 수 있다.
print(fruits[0, 0, :])
# [  1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   2   1
#    2   2   2   2   2   2   1   1   1   1   1   1   1   1   2   3   2   1
#    2   1   1   1   1   2   1   3   2   1   3   1   4   1   2   5   5   5
#   19 148 192 117  28   1   1   2   1   4   1   1   3   1   1   1   1   1
#    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]
#첫번째 이미지를 그림으로 그려서 숫자와 비교해본다. 
plt.imshow(fruits[0], cmap='gray')
plt.show()

* 넘파일 배열은 흑백 사진을 담고 있다.
사진으로 찍은 이미지를 넘파일 배열로 변환할 때 반전시켰기 때문에 흰 바탕(높은 값)은 검은색(낮은 값)으로 만들고 짙은 부분(낮은 값)은 밝은색(높은 값)으로 바뀐다. 우리가 보는 것과 컴퓨터가 처리하는 방식이 다르기 때문
0에 가까울수록 검게 나타나고 높은 값은 밝게 표시된다. 
cmap 매개변수를 gray_r로 지정하면 다시 우리가 보는 것과 동일한 이미지가 출력된다. 

plt.imshow(fruits[0], cmap='gray_r')
plt.show()

#다른 이미지도 출력해보기
#사과, 파인애플, 바나나가 100개씩 있다. 
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(행, 열) : 여러 개의 그래프를 배열처럼 쌓을 수 있도록 한다. 
* axs : 2개의 서브 그래프를 담고 있는 배열 

 

픽셀값 분석하기 

  • fruits 데이터를 사과, 파인애플, 바나나로 나눈다.
    넘파일 배열을 나눌 때 2차원 배열인 100x100 이미지를 펼쳐서 길이가 10,000인 1차원 배열로 만든다. 
    → 이미지로 출력하긴 어렵지만 배열을 계산할 때 편리하다. 
#reshape() : 두번째 차원과 세번째 차원을 10,000으로 합친다. 
#첫번째 차원을 -1로 지정하면 자동으로 남은 차원을 할당한다. 
#여기서는 첫번째 차원이 샘플 개수이다. 
apple = fruits[0:100].reshape(-1, 100*100)
pineapple = fruits[100:200].reshape(-1, 100*100)
banana = fruits[200:300].reshape(-1, 100*100)
  • 샘플마다 픽셀의 평균값을 계산해야 하므로 mean()을 사용하여 평균을 계산할 축을 지정한다. 
    → axis=0으로 하면 첫번째 축인 행을 따라 계산, 1로 지정하면 두번째 축인 열을 따라 계산
    샘플을 모두 가로로 나열했으므로 axis=1로 지정하여 평균을 계산한다. 
#각 샘플의 픽셀 평균값 계산
print(apple.mean(axis=1))
# [ 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
#   ...
#   81.9424  87.1687  97.2066  83.4712  95.9781  91.8096  98.4086 100.7823
#  101.556  100.7027  91.6098  88.8976]

#히스토그램으로 평균값 분포 보기 
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()

문제점 
바나나는 사진에서 차지하는 영역이 작기 때문에 평균값이 낮게 나온다. 
사과와 파인애플은 대체로 형태가 동그랗고 사진에서 차지하는 크기가 비슷해 픽셀만으로는 구분하기 쉽지 않다. 

해결방법
샘플의 평균값이 아니라 픽셀별 평균값을 비교한다. → 전체 샘플에 대한 각 픽셀의 평균 계산하기 

#픽셀 평균 계산하기 : axis=0 
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*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()

→ 대표 이미지와 가까운 사진을 골라 내어 구분한다. 

 

평균값과 가까운 사진 고르기 

  • fruits 배열에 있는 모든 샘플에서 과일_mean을 뺀 절댓값의 평균을 계산한다. 
#abs_diff : 각 샘플에 대한 평균을 구하기 위해 axis에 두번째, 세번째 차원을 모두 지정한다. 
#abs_mean : 각 샘플의 오차 평균으로, 크기가 (300,)인 1차원 배열이다. 
abs_diff = np.abs(fruits - apple_mean)
abs_mean = np.mean(abs_diff, axis=(1,2))
print(abs_mean.shape) #(300,)

#값이 가장 작은 순서대로 100개 고르기 -> apple_mean과 오차가 가장 적은 샘플 100개 고르기 
#np.argsort() : 작은 것에서 큰 순서대로 나열한 abs_mean 배열의 인덱스를 반환한다. 
apple_index = np.argsort(abs_mean)[:100]
#subplots() : 10*10 = 100개의 서브 그래프 만들기 
fig, axs = plt.subplots(10, 10, figsize=(10,10))
#axis : 10*10의 2차원 배열이므로 i, j를 사용하여 서브 그래프의 위치를 지정한다. 
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()

 

* 군집 (Clustering) : 비슷한 샘플끼리 그룹으로 모으는 작업
* 클러스터 (Cluster) : 군집 알고리즘에서 만든 그룹 

문제점 : 타깃값을 알고 있었기 때문에 사진 평균값을 계산해서 가장 가까운 과일을 찾을 수 있었지만 실제 비지도 학습에서는 타깃값을 모르기 때문에 샘플 평균값을 미리 구할 수 없다. 

해결 방법 : k-평균 알고리즘 사용하기 

 

정리 [비슷한 샘플끼리 모으기]

  • 문제 : 고객들이 올린 과일 사진을 자동으로 모은다. 어떤 과일 사진을 올릴지 모르기 때문에 미리 예상할 수 없어 타깃값을 준비하여 분류 모델을 훈련하기 어렵다. 
  • 비지도 학습 : 타깃값이 없을 때 데이터에 있는 패턴을 찾거나 데이터 구조를 파악하는 머신러닝 방식 → 스스로 데이터가 어떻게 구성되어 있는지 분석한다. 
  • 대표적인 비지도 학습 문제 : 군집 → 비슷한 샘플끼리 모으는 작업 

 

06-2 k-평균

k-평균 알고리즘 작동 방식을 이해하고 과일 사진을 자동으로 모으는 비지도 학습 모델을 만든다. 

 

k-평균 군집 알고리즘 : 처음에는 랜덤하게 클러스터 중심을 선택하고 점차 가까운 샘플의 중심으로 이동한다. 

k-평균 알고리즘의 작동 방식 

  1. 무작위로 k개의 클러스터 중심을 정한다. 
  2. 각 샘플에서 가장 가까운 클러스터 중심을 찾아 해당 클러스터의 샘플로 지정한다. 
  3. 클러스터에 속한 샘플의 평균값으로 클러스터 중심을 변경한다. 
  4. 클러스터 중심에 변화가 없을 때까지 2번으로 돌아가 반복한다. 

 

KMeans 클래스 KMeans

  • 비지도 학습이므로 fit() 메서드에서 타깃 데이터를 사용하지 않는다. 
  • 군집된 결과는 KMeans 클래스 객체의 labels_ 속성에 저장된다. labels_ 배열의 길이는 샘플 개수와 동일하며 배열은 각 샘플이 어떤 레이블에 해당되는지 나타낸다. 
#3차원 배열(샘플개수, 너비, 높이)을 2차원 배열(샘플개수, 너비*높이)로 변경한다. 
!wget https://bit.ly/fruits_300_data -o fruits_300.npy
import numpy as np
fruits = np.load('fruits_300_data')
fruits_2d = fruits.reshape(-1, 100*100)

#n_clusters 매개변수 : 클러스터 개수 지정 
from sklearn.cluster import KMeans
km = KMeans(n_clusters=3, random_state=42)
km.fit(fruits_2d)
print(km.labels_)
#레이블 0,1,2로 모은 샘플 개수를 확인한다. 
print(np.unique(km.labels_, return_counts=True)) 
#(array([0, 1, 2], dtype=int32), array([111,  98,  91])) -> array([]) 숫자는 각 레이블마다 몇 개의 샘플을 모았는지 나타낸다.
#각 클러스터가 어떤 이미지를 나타냈는지 그림으로 출력해본다. 
import matplotlib.pyplot as plt
def draw_fruits(arr, ratio=1):
   n = len(arr) #n : 샘플 개수
   #한줄에 10개씩 이미지를 그린다. 샘플 개수를 10으로 나누어 전체 행 개수를 계산한다.
   rows = int(np.ceil(n/10))
   #행이 1개이면 열의 개수는 샘플 개수이다. 
   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: 
        axs[i, j].imshow(arr[i*10 + j], cmap='gray_r')
      axs[i, j].axis('off')
plt.show()

draw_fruits(fruits[km.labels_==0])

draw_fruits() 

  • (샘플 개수, 너비, 높이) 3차원 배열을 입력받아 가로로 10개씩 이미지를 출력한다. 
    샘플 개수에 따라 행과 열의 개수를 계산하고 figsize를 지정한다. 
  • 2중 for 반복문을 사용하여 첫번째 행을 따라 이미지를 그린다. 
  • 이 함수를 사용하여 레이블이 0인 과일 사진을 모두 그려본다.
    km.labels_ == 0과 같이 쓰면 km.labels_ 배열에서 값이 0인 위치는 True, 그 외는 False가 된다. 

레이블이 0으로 클러스터링된 91개의 이미지를 출력했다. 

다른 두 클러스터도 출력해본다. 

draw_fruits(fruits[km.labels_==1])
draw_fruits(fruits[km.labels_==2])

  • 1은 바나나, 2는 사과로만 이루어져 있다. 
  • 하지만 0은 파인애플에 사과와 바나나가 섞여 있으므로 샘플을 완벽하게 구별해내지 못했다. 

 

클러스터 중심 

  • cluster_centers_ 속성 : KMeans 클래스가 최종적으로 찾은 클러스터 중심이 저장되어 있다. 
    fruits_2d 샘플의 클러스터 중심이기 때문에 각 중심을 이미지로 출력하려면 100*100 크기의 2차원 배열로 바꿔야 한다. 
draw_fruits(km.cluster_centers_.reshape(-1, 100, 100), ratio=3)

  • transform() : 훈련 데이터 샘플에서 클러스터 중심까지 거리로 변환해주는 함수 
#인덱스가 100인 샘플에 적용해본다. 
print(km.transform(fruits_2d[100:101])) 
#[[3393.8136117  8837.37750892 5267.70439881]] -> 하나의 샘플을 전달했기 때문에 반환된 배열 크기는 (1, 클러스터 개수)인 2차원 배열이다. 
#첫번째 클러스터 (레이블0), 두번째 클러스터(레이블1)가 각각 첫번째 원소, 두번째 원소의 값이다. 

#가장 가까운 클러스터 중심을 예측 클래스로 출력
print(km.predict(fruits_2d[100:101])) #[0] -> 파인애플 
draw_fruits(fruits[100:101]) #파인애플이 나온다.

#알고리즘이 반복한 횟수 출력하기 
print(km.n_iter_) #4

클러스터 중심을 사용해 데이터셋을 저차원으로 변환할 수 있다. 
또는 가장 가까운 거리에 있는 클러스터 중심을 샘플의 예측 값으로 사용할 수 있다. 

문제 : 타깃 정보를 알고 있었기 때문에 n_clusters를 3으로 지정했다. 클러스터 개수를 모를 때 최적의 클러스터 개수를 찾는 방법은? 

 

최적의 k 찾기 

  • k-평균 알고리즘의 단점 : 클러스터 개수를 사전에 지정해야 함 
  • 적절한 클러스터 개수 찾는 방법 : 엘보우 (elbow) 방법
    클러스터 개수를 늘려가면서 이너셔의 변화를 관찰하여 최적의 클러스터 개수를 찾는다.
    이너셔를 그래프로 그리면 감소하는 속도가 꺾이는 지점이 있다. 이 지점부터는 클러스터 개수를 늘려도 클러스터에 잘 밀집된 정도가 크게 개선되지 않는다. (= 이너셔가 크게 줄어들지 않는다.) 
  • 이너셔 (Inertia) : 클러스터 중심과 클러스터에 속한 샘플 사이의 거리의 제곱 합 → 클러스터에 속한 샘플이 얼마나 가깝게 모여 있는지 나타내는 값 

  • KMeans 클래스는 자동으로 이너셔를 계산해서 inertia_ 속성으로 제공한다.
    클러스터 개수 k를 2~6까지 바꿔가며 KMeans 클래스를 5번 훈련한다. 
    fit() 메서드로 모델 훈련한 후 inertia_ 속성에 저장된 이너셔 값을 inertia 리스트에 추가한다. 
    마지막으로 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()

  • k=3에서 그래프의 기울기가 조금 바뀌었다. 
  • 엘보우 지점보다 클러스터 개수가 많아지면 이너셔의 변화가 줄어들면서 군집 효과도 줄어든다. (위 그래프에서는 지점이 명확하진 않다.)

 

정리 [과일을 자동으로 분류하기]

  • 타깃값을 모를 때 자동으로 사진을 클러스터로 모을 수 있는 k-평균 알고리즘 사용 (KMeans 클래스)
  • k-평균 알고리즘 
    - 처음에 랜덤하게 클러스터 중심을 정하고 클러스터를 만든다. 클러스터의 중심을 이동하고 다시 클러스터를 만드는 식으로 반복해서 최적의 클러스터를 구성한다.
    transform() : 각 샘플에서 각 클러스터까지의 거리를 하나의 특성으로 활용
    predict() : 새로운 샘플에 대해 가장 가까운 클러스터를 예측값으로 출력
  • 클러스터 중심 : k-평균 알고리즘이 만든 클러스터에 속한 샘플의 특성 평균값 
  • 엘보우 방법 : 최적의 클러스터 개수를 정하는 방법 중 하나 
    클러스터 개수를 늘리면서 반복하여 KMeans 알고리즘을 훈련하고 이너셔가 줄어드는 속도가 꺾이는 지점을 최적의 클러스터 개수로 결정 
  • k-평균 알고리즘의 클러스터 중심까지 거리를 특성으로 사용할 수 있다. 
    장점 : 훈련 데이터의 차원을 크게 줄이고 데이터셋의 차원을 줄이면 지도 학습 알고리즘의 속도를 크게 높일 수 있다. 

 

06-3 주성분 분석 

차원 축소에 대해 이해하고 대표적인 차원 축소 알고리즘인 PCA(주성분 분석) 모델을 만든다. 

 

문제 : 군집이나 분류에 영향을 끼치지 않으면서 업로도된 사진 용량 줄이기 

 

차원과 차원 축소 

  • 차원 (Dimension) : 데이터가 가진 속성/특성 ≠ 배열의 차원과는 다르다. 
  • 차원 축소 (Dimensionality Reduction) : 데이터를 가장 잘 나타내는 일부 특성을 선택하여 데이터 크기를 줄이고 지도 학습모델 성능을 향상시킬 수 있는 방법 → 줄어든 차원에서 다시 원본 차원으로 손실을 최대한 줄이면서 복원할 수도 있다.   
  • 차원 축소 알고리즘 : 주성분 분석 (Principal component analysis, PCA)

 

주성분 분석(PCA) 소개 

  • 데이터에 있는 분산이 큰 방향을 찾는다. 
    * 분산 : 데이터가 널리 퍼져있는 정도 
    * 분산이 큰 방향 : 데이터를 잘 표현하는 어떤 벡터 → 이 벡터를 주성분이라고 한다. 
  • 주성분 벡터의 원소 개수 = 원본 데이터셋에 있는 특성(차원) 개수 원본 데이터는 주성분을 사용해 차원을 줄일 수 있다. 
  • 주성분 = 원본 차원
    주성분으로 바꾼 데이터 = 차원이 줄어듦 
    주성분에 투영하여 바꾼 데이터 = 원본이 가지고 있는 특성을 가장 잘 나타냄 

 

PCA 클래스 

  • n_components 매개변수 : 주성분 개수 지정
  • components_ 속성 : PCA 클래스가 찾은 주성분 저장 
!wget https://bit.ly/fruits_300_data -o fruits_300.npy
import numpy as np
fruits = np.load('fruits_300_data')
fruits_2d = fruits.reshape(-1, 100*100)

from sklearn.decomposition import PCA
pca = PCA(n_components=50)
pca.fit(fruits_2d)

print(pca.components_.shape) #(50, 10000) -> (주성분 개수, 원본 데이터의 특성 개수)

#이미지처럼 출력 
draw_fruits(pca.components_.reshape(-1,100,100))

위 주성분은 원본 데이터에서 가장 분산이 큰 방향을 순서대로 나타낸 것이다.

 

원본 데이터를 주성분에 투영하여 특성의 개수를 50개로 줄인다. 

  • fruits_2d : 10000개의 특성을 가진 300개의 이미지 
  • fruits_pca : PCA 모델을 사용해서 만든 50개 특성을 가진 데이터 
print(fruits_2d.shape) #(300, 10000)

fruits_pca = pca.transform(fruits_2d)
print(fruits_pca.shape) #(300, 50)

문제 : 데이터 차원을 줄여도 다시 원상 복구 할 수 있을까?

 

원본 데이터 재구성 

  • inverse_transform() : 50개의 차원으로 축소한 데이터를 전달해 10000개의 특성 복원하기 
fruits_inverse = pca.inverse_transform(fruits_pca)
print(fruits_inverse.shape) #(300, 10000)
  •  복원된 데이터를 100*100 크기로 바꾸어 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')

→ 50개의 특성을 10000개로 늘려 과일이 잘 복원되었다. 

 

설명된 분산 (Explained variance)

  • 주성분이 원본 데이터의 분산을 얼마나 잘 나타내는지 기록한 값 
  • PCA 클래스의 explained_variance_ratio_ : 주성분의 설명된 분산 비율 기록 → 분산 비율을 모두 더하면 50개의 주성분으로 표현하고 있는 총 분산 비율을 얻을 수 있다. 
print(np.sum(pca.explained_variance_ratio_)) #0.9215648934443857
  • 분산 비율이 높다 = 복원이 잘 된다.
  • 그래프로 그려보면 적절한 주성분의 개수를 찾는 데 도움이 된다. 
plt.plot(pca.explained_variance_ratio_)
plt.show()

  • 처음 10개의 주성분이 대부분의 분산을 표현하고 있다.
    그다음부터는 각 주성분이 설명하고 있는 분산은 비교적 작다. 

 

다른 알고리즘과 함께 사용하기

  • PCA로 차원 축소된 데이터를 사용하여 지도 학습 모델을 훈련시킨다.
    원본 데이터를 사용했을 때와 어떤 차이가 있는지 확인한다. → 로지스틱 회귀 모델 사용 
  • 지도 학습 모델은 타깃값이 필요하다. 사과 0, 파인애플 1, 바나나 2로 지정
from sklearn.linear_model import LogisticRegression
lr = LogisticRegression()

#파이썬 리스트와 정수를 곱하면 리스트 안의 원소를 정수만큼 반복한다. -> 각각 100개씩 저장되도록 한다.
target = np.array([0]*100 + [1]*100 + [2]*100)

#원본데이터(fruits_2d) 모델 돌려보기 + 교차 검증 수행 
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.230682706832886
  • 교차 검증 점수 매우 높다. → 샘플(300개)보다 특성(10000개)이 훨씬 많기 때문에 과대적합된 모델을 만들기 쉽다. 
  • cross_validate() 함수가 반환한 값 : fit_time 항목에 각 교차 검증 폴드의 훈련시간 기록
    → 이 값을 PCA로 축소한 fruits_pca를 사용했을 때와 비교해본다. 
scores = cross_validate(lr, fruits_pca, target)
print(np.mean(scores['test_score'])) #1.0
print(np.mean(scores['fit_time'])) #0.041118812561035153
  • 정확도 : 100%, 훈련시간은 20배 이상 감소 
    PCA로 훈련 데이터의 차원을 축소하면 저장공간을 줄이고 모델 훈련 속도도 높일 수 있다. 
  • n_components 매개변수 : 주성분 개수 대신 원하는 설명된 분산 비율 입력할 수 있다. 
    PCA 클래스는 지정된 비율에 도달할 때까지 자동으로 주성분을 찾는다. 
#설명된 분산의 50%에 달하는 주성분을 찾도록 PCA 모델을 만든다. 
#n_compoents : 0~1 사이의 비율을 실수로 입력한다. 
pca = PCA(n_components=0.5)
pca.fit(fruits_2d)
#몇개의 주성분을 찾았는지 출력한다. 
print(pca.n_components_) #2
  • 2개의 특성만으로 원본 데이터에 있는 분산의 50%를 표현할 수 있다.
  • 위 모델로 원본 데이터를 변환한다. 
#주성분이 2개이므로 변환된 데이터의 크기는 (300,2)가 된다. 
fruits_pca = pca.transform(fruits_2d)
  • 2개의 특성만 사용한 모델의 교차 검증 결과를 확인해본다. 
scores = cross_validate(lr, fruits_pca, target)
print(np.mean(scores['test_score'])) #0.9933333333333334
print(np.mean(scores['fit_time'])) #0.13310623168945312
  • 차원 축소된 데이터를 사용해 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)) 
#(array([0, 1, 2], dtype=int32), array([110,  99,  91])) -> 샘플 개수 (사과, 바나나, 파인애플)
#-> 원본 데이터를 사용했을 때와 결과가 비슷하다. 

#과일 이미지 출력 
for label in range(0,3):
  draw_fruits(fruits[km.labels_ == label])
  print("\n")

조금씩 섞여있다.

  • n차원으로 표현하기 → fruits_pca 데이터는 2개의 특성이 있기 때문에 2차원으로 표현해본다. 
  • km.labels_를 사용해 클러스터별로 나누어 산점도를 그려본다.
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()

각 클러스터의 산점도가 잘 구분된다.

 

정리 [주성분 분석으로 차원 축소]

  • 차원 축소 : 원본 데이터의 특성을 적은 수의 새로운 특성으로 변환하는 비지도 학습의 한 종류
    장점
    - 데이터셋 크기를 줄이고 시각화하기 쉽다. 
    - 차원 축소된 데이터는 다른 알고리즘에서 재사용하여 성능을 높이거나 훈련 속도를 빠르게 할 수 있다. 
  • 주성분 : 차원 축소 알고리즘의 하나로 데이터에서 가장 큰 분산이 큰 방향
    주성분 분석 : 주성분을 찾는 방법 원본 데이터를 주성분에 투영하여 새로운 특성을 만들 수 있다. 
  • 설명된 분산 : 주성분 분석에서 주성분이 얼마나 원본 데이터의 분산을 잘 나타내는지 기록한 것 
  • PCA 클래스
    - 자동으로 설명된 분산을 계산하여 제공한다.  
    - 주성분의 개수를 명시적으로 지정하는 대신 설명된 분산의 비율을 설정하여 원하는 비율만큼 주성분을 찾을 수 있다. 
    - 변환된 데이터에서 원본 데이터를 복원하는 메서드 제공 → 변환된 데이터가 원본 데이터의 분산을 모두 유지하고 있어야 완벽하게 복원. 하지만 적은 특성으로도 상당 부분의 디테일을 복원할 수 있다. 

'Study > PBL' 카테고리의 다른 글

[혼공머신] Ch07  (0) 2024.07.24
[혼공머신] Ch05  (0) 2024.07.23
[혼공머신] Ch04  (0) 2024.07.18
[혼공머신] Ch03  (0) 2024.07.17
[혼공머신] Ch01, 2  (1) 2024.07.12