ML 세션 - Week6.비지도 학습
Aug 26, 2024
»
AI
정답
- 0, 클러스터 중심까지의 거리가 가장 가까워서
- n_clusters=k, inertia, k=3
- pca.transform(fruits_2d), pca.inverse_transform(fruits_pca)
- 주성분이 원본 데이터의 분산을 얼마나 잘 나타내는지 기록한 값, n_components=0.5
- 110,99,91, 비슷하다
비지도 학습
타겟 데이터 없을 때 사용하는 머신러닝 알고리즘
- 군집이란 비슷한 샘플끼리 그룹으로 모으는 작업
- 대표적인 비지도 학습 작업
- 클러스터란 군집 알고리즘에서 만든 그룹
데이터 준비
-
!wget https://bit.ly/fruits_300_data -O fruits_300.npy import numpy as np import matplotlib.pyplot as plt fruits = np.load('fruits_300.npy') # 넘파이에서 npy 파일 로드 print(fruits.shape) # (300, 100, 100) -> 100 * 100 이미지 300개 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] ''' # 0에 가까울 수록 검은 색 높을수록 흰색 plt.imshow(fruits[0], cmap='gray') # imshow()함수로 넘파이 배열로 저장된 이미지 그리기 # 흑백 이미지라 cmap 매개변수 gray plt.show() 

- 컴퓨터는 흰색에 집중함 -> 바탕을 검은색, 물질을 희게 반전

-
plt.imshow(fruits[0], cmap='gray_r') # gray_r로 다시 반전 plt.show() 
-
fig, axs = plt.subplots(1, 2) # subplots()는 여러개의 그래프 배열처럼 쌓기 가능 -> 이미지 한 번에 표시 axs[0].imshow(fruits[100], cmap='gray_r') axs[1].imshow(fruits[200], cmap='gray_r') plt.show() 
픽셀값 분석
- 사용하기 쉽게 데이터 가공

-
# apple pineapple banana로 데이터 나누기 # reshape()로 두 번째 차원과 세 번째 차원 합치기 # 첫 번째 차원 -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() 메서드 이용
-
print(apple.mean(axis=1)) # axis=0이면 행을 따라 계산, 1이면 열을 따라 계산 # 가로로 값 나열해서 axis=1로 
- 사과, 파인애플, 바나나 히스토그램 겹쳐 보기
-
plt.hist(np.mean(apple, axis=1), alpha=0.8) # alpha<1 로 투명도 주기 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']) # legend()로 범례 만들기 plt.show() 
- 사과랑 파인애플 구분이 어려움
- 픽셀별 평균값 비교
- 과일의 모양에 따라 픽셀값이 높은 위치가 다를 것
- axis=0으로 픽셀 평균 계산
-
fig, axs = plt.subplots(1, 3, figsize=(20, 5)) axs[0].bar(range(10000), np.mean(apple, axis=0)) # bar()로 막대그래프 그리기 axs[1].bar(range(10000), np.mean(pineapple, axis=0)) axs[2].bar(range(10000), np.mean(banana, axis=0)) plt.show() 
- 사과는 사진 아래쪽으로 갈수록 값이 높아짐
- 파인애플은 비교적 고르면서 높음
- 바나나는 중앙 픽셀값이 높음
- 픽셀 평균값 이미지로 출력해서 비교하기
-
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 배열 샘플 - 평균
-
abs_diff = np.abs(fruits - apple_mean) # abs()로 절댓값 계산 abs_mean = np.mean(abs_diff, axis=(1,2)) apple_index = np.argsort(abs_mean)[:100] # argsort()는 작은 것에서 큰 순서대로 나열한 인덱스 반환 -> 평군과 오차가 가장 작은 샘플 100개 고름 fig, axs = plt.subplots(10, 10, figsize=(10,10)) # 그래프가 많아서 figsize으로 전체 그래프 크기를 조금 크게 지정 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') # axis('off')로 좌표축 생략 # 10 * 10 격자 그래프 그리기 plt.show() 
k-means 알고리즘
- 무작위로 k개의 클러스터 중심 정하기
- 각 샘플에서 가장 가까운 클러스터 중심 찾아 해당 클러스터 샘플로 지정
- 클러스터에 속한 샘플의 평균값으로 클러스터 중심 변경
- 클러스터 중심에 변화가 없을 때까지 2번으로 돌아가 반복

-
fruits_2d = fruits.reshape(-1, 100*100) from sklearn.cluster import KMeans km = KMeans(n_clusters=3, random_state=42) # n_clusters로 클러스터 개수 지정 km.fit(fruits_2d) print(km.labels_) # labels: 샘플이 어떤 레이블에 속하는지 print(np.unique(km.labels_, return_counts=True)) # (array([0, 1, 2], dtype=int32), array([111, 98, 91])) # 0: 91개, 1: 98개, 2: 111개 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() # 3차원 배열 입력 받아 가로로 10개씩 이미지 출력 # figsize는 ratio에 비례하여 커짐 draw_fruits(fruits[km.labels_==0]) # 값이 0인 위치는 true, 그 외는 false draw_fruits(fruits[km.labels_==1]) draw_fruits(fruits[km.labels_==2]) 


클러스터 중심
- cluster_centers_ 속성에 저장
-
draw_fruits(km.cluster_centers_.reshape(-1, 100, 100), ratio=3) 
-
print(km.transform(fruits_2d[100:101])) # transform()은 훈련 데이터 샘플에서 클러스터 중심까지 거리로 변환 # [[3393.8136117 8837.37750892 5267.70439881]] : 세 번째 클러스터까지의 거리가 가장 작음 -> 레이블 2 print(km.predict(fruits_2d[100:101])) # 2 draw_fruits(fruits[100:101]) # 파인애플임 print(km.n_iter_) # n_iter_에 클러스터 중심을 옮기면서 반복한 횟수 저장
최적의 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() 
- k=3에서 엘보우
- 이너셔: 클러스터 중심과 샘플 사이의 거리의 제곱 합
차원과 차원 축소
- 차원 = 특성
- 차원 축소
- 비지도 학습 작업 중 하나
- 데이터를 가장 잘 나타내는 일부 특성을 선택해 데이터 크기를 줄이고 지도 학습 모델의 성능 향상
- PCA(주성분 분석)
PCA
- 데이터에 있는 분산이 큰 방향 찾기
- 분산이란 데이터가 퍼져있는 정도

- 데이터의 분포를 가장 잘 표현하는 방향 벡터 = 주성분
- 주성분 벡터의 원소 개수는 원본 데이터셋에 있는 특성 개수와 같음
- 주성분은 원본 차원과 같고 주성분으로 바꾼 데이터는 차원이 줄어듦
- 주성분 찾기
- 두 번째 주성분(주성분 벡터에 수직이고 분산이 가장 큰 다음 방향) 찾기
-
from sklearn.decomposition import PCA pca = PCA(n_components=50) # n_components로 주성분 개수 지정 pca.fit(fruits_2d) print(pca.components_.shape) # (50, 10000) # 50개의 주성분, 1000개의 원본 데이터 특성 개수 draw_fruits(pca.components_.reshape(-1, 100, 100)) # 주성분 그림으로 그려보기 
-
print(fruits_2d.shape) # 원본 데이터 (300, 10000) fruits_pca = pca.transform(fruits_2d) # transform()으로 차원 축소 print(fruits_pca.shape) # (300, 50) 10000->50으로 차원 축소
원본 데이터 재구성
- inverse_transform()으로 원본 데이터 재구성
-
fruits_inverse = pca.inverse_transform(fruits_pca) print(fruits_inverse.shape) # (300, 10000) fruits_reconstruct = fruits_inverse.reshape(-1, 100, 100) # 100 * 100 크기로 변환 for start in [0, 100, 200]: draw_fruits(fruits_reconstruct[start:start+100]) # 100개씩 나눠서 출력 print("\n") 
설명된 분산
- 주성분이 원본 데이터의 분산을 얼마나 잘 나타내는지 기록한 값
- explained_variance_ratio_에 기록
- 첫번째 주성분의 설명된 분산이 가장 큼
-
print(np.sum(pca.explained_variance_ratio_)) # 0.9215651897863715 plt.plot(pca.explained_variance_ratio_) # 설명된 분산의 비율 그래프로 그려보기
- 처음 10개의 주성분이 대부분의 분산 표현
다른 알고리즘과 함께 사용하기
-
from sklearn.linear_model import LogisticRegression # 로지스틱 회귀 모델 lr = LogisticRegression() target = np.array([0] * 100 + [1] * 100 + [2] * 100) # 타깃 데이터 from sklearn.model_selection import cross_validate scores = cross_validate(lr, fruits_2d, target) # 원본 데이터로 교차 검증 수행 print(np.mean(scores['test_score'])) print(np.mean(scores['fit_time'])) # 각 교차 검증 폴드의 훈련 시간 ''' 0.9966666666666667 1.819899892807007 ''' scores = cross_validate(lr, fruits_pca, target) # 차원 축소 데이터로 교차 검증 print(np.mean(scores['test_score'])) print(np.mean(scores['fit_time'])) # 훈련 시간 감소 ''' 1.0 0.032833099365234375 ''' - 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) scores = cross_validate(lr, fruits_pca, target) print(np.mean(scores['test_score'])) # 0.9933333333333334 print(np.mean(scores['fit_time'])) # 0.03713240623474121 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") 
- 차원 줄이면 시각화 하기도 쉬움
-
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() 