Ch.06 비지도 학습
비슷한 과일끼리 모으자
이번에는 고객이 사고 싶은 과일 사진을 보내면,
그중 가장 많이 요청하는 과일을 판매 품목으로 선정하고,
1위로 선정된 과일 사진을 보낸 고객 중 몇 명을 이벤트 당첨자로 선정해보자.
Ch.06-1 군집 알고리즘
군집 알고리즘이란?
비슷한 데이터를 자동으로 묶어주는 기법이다.
쉽게 말해, 서로 비슷한 성질을 가진 것끼리 그룹(군집)으로 나눠주는 것!
예시
마트에서 물건을 분류하는 것과 같다.
- 과일 코너에는 사과, 바나나, 오렌지
- 채소 코너에는 상추, 오이, 당근
대표적인 알고리즘
- K-평균(K-Means)
- DBSCAN
- 계층적 군집
어떤 곳에 많이 쓰일까?
- 고객을 성향별로 그룹화 해서 마케팅 타겟팅
- 뉴스 기사나 문서 문류
- 이상 탐지(비정상적인 데이터 탐지)
- 이미지 분할
타깃을 모르는 비지도 학습
비지도 학습이란?
정답이 없는 데이터를 학습해서, 그 안의 패턴이나 구조를 찾아내는 학습 방식
타깃을 모르는 사진을 종류별로 분류하려고 한다.
사진의 종류를 분류하는데에는 여러가지 방법이 있지만,
이번에는 사진의 픽셀값을 모두 평균 내서 비슷한 과일끼리 모이는지 확인 해보자.
그럼 먼저 샘플 데이터로 쓸 과일의 사진을 다운로드 받아보자.
wget https://bit.ly/fruits_300_data -O fruits_300.npy
https://www.kaggle.com/datasets/moltean/fruits
이미지를 잘 가지고 왔는지 확인해보자.
확인은 맷플롯립의 imshow() 함수를 사용하면 넘파이 배열로 저장된 이미지를 쉽게 그릴 수 있다.
import numpy as np
import matplotlib.pyplot as plt
fruits = np.load('fruits_300.npy')
# 맷플롯립의 imshow() 함수 사용
plt.imshow(fruits[0], cmap='gray')
plt.show()

위 이미지를 보니 아마 사과 이미지로 추측된다.
해당 이미지에서 숫자는 0에 가까울수록 검게 나타나고, 높을 수록 밝은 값을 의미한다.
여기서 잠깐?
보통 흑백 샘플 이미지는 바탕이 밝고 물체가 짙은 색으로 표현한다.
하지만 왜 위 이미지에서는 바탕이 검게 보일까?
사실 이 부분은 넘파이 배열로 변환할 때 반전 시켜 바탕이 검은색이 됐다.
그럼 왜 이 같은 과정을 거쳤을까?
우리가 보고 싶은 건 배경이 아닌, 물건 같이 지정된 무언가이다.
이미지 데이터는 각 픽셀마다 0~255의 RGB 값이 있는데, 배경을 완전히 검게 처리하게 되면 배경의 픽셀 값은 0이 되어 전체 이미지의 픽셀 분포가 단순해지면서 학습이 더 안정되고 빠르게 수렴할 수 있기 때문이다.
즉, 간단하게 정리하면 배경은 우리에게 중요하지 않은 노이즈 같은 정보이므로,
배경을 단순화 시켜 우리가 보고 싶은 물체 등에 집중하여 처리 할 수 있도록 하는 것!
다시 원상태로 이미지를 출력하고 싶다면 아래와 같이 매개변수를 주면 된다.
cmap='gray_r'
plt.imshow(fruits[0], cmap='gray_r')
plt.show()

픽셀값 분석하기
픽셀을 분석하기 쉽게 100 x 100인 이미지를 펼쳐서 길이가 10,000인 1차원 배열로 만들어보자.
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)
# 배열의 크기 확인
print(apple.shape)
# 열 방향으로 지정 후 평균 계산
print(apple.mean(axis=1))
(100, 10000)
[ 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 88.9827 100.9158 92.7823 100.9184 104.9854 88.674 99.5643 97.2495 94.1179 92.1935 95.1671 93.3322 102.8967 94.6695 90.5285 89.0744 97.7641 97.2938 100.7564 90.5236 100.2542 85.8452 96.4615 97.1492 90.711 102.3193 87.1629 89.8751 86.7327 86.3991 95.2865 89.1709 96.8163 91.6604 96.1065 99.6829 94.9718 87.4812 89.2596 89.5268 93.799 97.3983 87.151 97.825 103.22 94.4239 83.6657 83.5159 102.8453 87.0379 91.2742 100.4848 93.8388 90.8568 97.4616 97.5022 82.446 87.1789 96.9206 90.3135 90.565 97.6538 98.0919 93.6252 87.3867 84.7073 89.1135 86.7646 88.7301 86.643 96.7323 97.2604 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, label='apple')
plt.hist(np.mean(pineapple, axis=1), alpha=0.8, label='pineapple')
plt.hist(np.mean(banana, axis=1), alpha=0.8, label='banana')
plt.legend()
plt.show()

만들어진 히스토그램을 보면 바나나 사진의 평균은 40 아래에 모여있는 것을 확인 할 수 있다.
사과와 파인애플은 90~100에 분포되어있는 것을 확인할 수 있다.
위 히스토그램으로는 바나나는 확실히 구분이 가능하지만 사과, 파인애플은 구분이 어렵다.
그럼 모두를 구분할 수 있는 좋은 방법은 뭐가 있을까?
픽셀의 평균값 계산하여 확인해보자.
평균 값은 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()
< 사과, 파인애플, 바나나 >
평균 값을 나타낸 그래프 확인 시 모두 높은 구간이 다른 것을 확인 할 수 있다.
그럼 이제 평균 값을 이용하여 사진을 골라보자.
평균값과 가까운 사진 고르기
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')
abs_diff = np.abs(fruits - apple_mean)
abs_mean = np.mean(abs_diff, axis=(1, 2))
apple_index = np.argsort(abs_mean)[:100]
apple_index = apple_index.reshape(10, 10)
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, j]], cmap='gray_r')
axs[i, j].axis('off')
plt.show()
사과와 가까운 사진 100개를 골랐더니 위의 결과와 같이 모두 사과를 가져온 것을 확인 할 수 있다.
Ch.06-2 k-평균
k-평균 알고리즘의 작동 방식을 이해하고 과일 사진을 자동으로 모으는 비지도 학습 모델을 만들어 봅시다.
k-평균 알고리즘이란?
이름에서도 알 수 있듯이 비슷한 것끼리 묶어주는 알고리즘
k-평균 알고리즘의 작동 방식
- 무작위로 k개의 클러스터 중심을 정함
- 각 샘플에서 가장 가까운 클러스터 중심을 찾아 해당 클러스터의 샘플로 지정
- 클러스터에 속한 샘플의 평균값으로 클러스터 중심을 변경
- 클러스터 중심에 변화가 없을 때까지 2번으로 돌아가 반복
KMeans 클래스
KMeans 클래스는 데이터를 자동으로 비슷한 것들끼리 묶어서 그룹을 만들어주는 모델이다.
쉽게 말하자면 데이터를 특징(색깔, 크기 등)에 따라 k개의 무리로 나눠주는 모델이며,
보통 고객 소비 습관에 따라 그룹을 나누거나, 사진 속 비슷한 색을 뽑거나, 뉴스 기사들의 주제를 자동 분류할 때 등 쓰이고 있다.
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_)
[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 0 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]
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()

결과에 대한 일부 이미지를 확인해보면 대부분 파인애플이긴하나 약간의 사과와 바나나가 섞인 것을 확인 할 수 있다.
클러스터 중심
KMeans 클래스가 최종적으로 찾은 클러스터 중심은 cluster_centers_속성에 저장되어 있고,
이 배열은 fruits_2d 샘플 클러스터의 중심이기 때문에 각 중심을 이미지로 출력하기 위해서는 100 x 100 크기의 2차원 배열로 바꿔야 이미지로 출력할 수 있다.
draw_fruits(km.cluster_centers_.reshape(-1, 100, 100), ratio=3)
이전에 출력했던 사과, 바나나, 파인애플의 이미지와 비슷한 것을 확인 할 수 있다.
K-평균 알고리즘은 클러스터 중심을 옮김녀서 최적의 클러스터를 찾으며,
알고리즘이 반복한 횟수는 KMeans 클래스의 n_iter_ 속성에 저장되어 있으니 확인해보자.
print(km.n_iter_)
4
최적의 k 찾기
k-평균 알고리즘의 단점 중 하나는 클러스터 개수를 사전에 저장해야 한다는 것이다.
왜 사전에 저장해야 한다는 것이 단점이냐면,
학생들이 있는데 학생들을 반으로 나눈다고 가정해보자.
그럼 반이 몇개인지 미리 정해야한다. 내가 만약 3개로 나눈다고 정해놓고 시작하고 실제로 데이터를 보면
2개의 반이 더 적절할 수도 있고, 5개의 반이 더 적절할 수도있다.
그럼 적절한 클러스터 개수를 찾기 위한 방법은 무엇이 있을까?
대표적인 방법으로는 옐보우 방법이 있다.
옐보우는 다양한 k값으로 K-평균을 실행해보고,
각 k에서 군집 품질 점수를 계산하여 보았을때 어느 순간 감소 폭이 확 줄어드는 지점이 있는데 이 부분이 팔꿈치 모양으로 보여 옐보우 방법이라고 부른다.
Ch. 06-3 주성분 분석
차원 축소에 대해 이해하고 대표적인 차원 축소 알고리즘 중 하나인 PCA(주성분 분석) 모델을 만들어 봅니다.
사진을 업로드하여 분류하는 프로젝트는 성공으로 끝났으나, 예상하지 못한 문제가 발생했다.
바로 저장공간이 부족하다는 것!!!
군집이나 분류에 영향을 끼치지 않으면서 업로드된 사진의 용량을 줄일 수 있을까?
차원과 차원 축소
과일 사진의 경우 10,00개이 픽셀이 있다고 가정했을때, 이 과일 사진에는 10,00개이 특성이 있는 셈이고,
이런 특성을 차원 이라고도 부를수 있다.
이 차원들을 줄일 수 있다면 저장공간을 크게 절약 할 수 있게 되며, 방법으로는 차원 축소 알고리즘이 있다.
차원 축소는 이름 그대로 데이터의 중요한 특징만 남기고, 불필요한 정보를 줄이는 것이다.
주성분 분석 소개(PCA)
데이터이 분산이 가장 큰 방향을 찾아서 그 방향으로 데이터를 재표현하는 방법이다.
이 방법을 사용하면 데이터의 크기가 줄어들어 계산 속도를 높이고, 중복되거나 중요하지 않은 특성을 없앨 수 있으며, 시각화가 가능해진다.
예를들어 학생들의 성적 데이터(수학, 과학, 물리 점수)가 있다고 가정해보자.
공부 잘하는 학생은 모든 과목의 점수가 높듯이, 과목의 점수들은 서로 비슷하게 움직일 수 있다.
PCA느ㄴ 이 세 과목의 점수를 "성적 종합 점수" 같은 새로운 축을 만들어 차원을 줄여준다.
'Newb > 머신러닝' 카테고리의 다른 글
| [혼공머신] 6주차 [6/6] (8) | 2025.08.15 |
|---|---|
| [혼공머신] 4주차 [4/6] (3) | 2025.07.27 |
| [혼공머신] 3주차[3/6] (1) | 2025.07.20 |
| [혼공머신] 2주차 (11) | 2025.07.13 |
| [혼공머신] 1주차 (2) | 2025.07.06 |


