[혼공머신] 5주차-1_비지도학습 갑자기 난이도 급상승?!

06-1 군집 알고리즘

타깃을 모르는 비지도 학습

비지도 학습(unsupervised learning) : 타깃이 없을 때 사용하는 머신러닝 알고리즘

 

과일 사진 데이터 준비하기

Jupyter Notebook이나 Google Colab 같은 환경에서 사용되는 shell 명령어

! : Jupyter Notebook에서 셸 명령어를 실행하기 위한 접두사

-O : 저장할 파일 이름을 지정

 

이 배열의 첫 번째 차원(300) : 샘플의 개수

두 번째 차원(100) : 이미지 높이

세 번때 차원(100) : 이미지 너비

이미지 크기는 100×100 (각 픽셀은 넘파이 배열의 원소 하나에 대응)

 

우리가 보는 것과 컴퓨터가 처리하는 방식이 다르기 때문에 종종 흑백 이미지를 이렇게 반전하여 사용합니다.

관심 대상의 영역을 높은 값으로 바꾸었지만 맷플롯립으로 출력할 때 바탕이 검게 나오므로 cmap 매개변수를 ‘gray_r’로 지정하여 다시 반전하여 보기 좋게 출력합니다.

 

밝은 부분이 0에 가깝고 짙은 부분이 255에 가까운 값입니다.

 

fig : 전체 그림(figure)을 의미

axs : 2개의 서브플롯을 담고 있는 배열(리스트처럼 생긴 객체)

plt.subplots(1, 2) : 1행 2열의 서브플롯을 생성하는 함수

cmap='gray_r' : 색상맵을 반전된 흑백으로 지정

 

픽셀값 분석하기

넘파일 배열을 나눌 때 100×100 이미지를 펼쳐서 길이가 10,000인 1차원 배열로 만들면 이미지로 출력하긴 어렵지만 배열을 계산할 때 편리합니다.

axis=0 → “열 단위로 계산” (행을 따라 내려가면서)

  • 즉, 각 열(column)의 값을 기준으로 계산합니다.

  • 예: 평균을 구하면, 각 열의 평균이 나옵니다.

axis=1 → “행 단위로 계산” (열을 따라 가로로)

  • 즉, 각 행(row)의 값을 기준으로 계산합니다.

  • 예: 평균을 구하면, 각 행의 평균이 나옵니다.

 

사과와 파인애플은 많이 겹쳐있어서 픽셀값만으로는 구분하기 쉽지 않음

→ 샘플의 평균값이 아니라 픽셀별 평균값을 비교

각 샘플의 평균값 : 각 이미지를 한 장의 사진 → 한 개의 숫자로 만드는 방식

픽셀별 평균값 : 모든 이미지에서 같은 위치의 픽셀끼리 평균을 냄

 

여기서 axs는 matplotlib의 subplot(여러 개의 그래프 영역)을 한 번에 만들었을 때, 각 그래프 영역(axes 객체)**를 담고 있는 배열

 

 

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

 

abs_diff의 크기 (300, 100, 100)

  • 300 → 사진이 300장 있음 (샘플 개수)
  • 100 → 한 장의 사진 높이 (픽셀 수)
  • 100 → 한 장의 사진 너비 (픽셀 수)

즉, 300장의 흑백 사진이 있고, 각 사진은 100×100 픽셀로 되어 있음.
그래서 전체 데이터는 3차원 배열 (사진 개수, 높이, 너비) 모양이 됨.

axis=(1,2) 의미

  • axis=1 → 사진의 “높이 방향”
  • axis=2 → 사진의 “너비 방향”
  • (1, 2)를 같이 지정하면 → 각 사진의 모든 픽셀을 평균낸다는 뜻.

 abs_mean의 크기 (300,)

 

  • 사진 한 장(100×100) → 평균을 내면 숫자 1개가 됨.
  • 300장의 사진 → 숫자 300개가 나옴.
  • 그래서 (300,) → 길이가 300인 1차원 배열이 됨.

 

apple_index = np.argsort(abs_mean)[:100]

  • abs_mean에는 각 사진이 사과 평균 사진과 얼마나 다른지(차이 평균값)가 들어 있음
  • np.argsort(abs_mean) → 차이가 작은 순서대로 사진의 인덱스(번호)를 정렬
  • [:100] → 그 중에서 가장 사과에 가까운 사진 100장만 선택

apple_index = apple_index.reshape(10, 10)

  • 100장의 사진을 10×10 형태로 바꿈 (나중에 그래프를 10행 10열에 맞춰서 그림)

fig, axs = plt.subplots(10, 10, figsize=(10,10))

  • 10행 10열짜리 빈 그림판(axs)을 만듦
  • figsize=(10,10) → 전체 그림 크기를 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')

  • 10×10 반복문을 돌면서 각 위치(axs[i, j])에 사과에 가장 가까운 사진을 차례대로 그림
  • cmap='gray_r' → 흑백(역상)으로 표시
  • [:100] → 그 중에서 가장 사과에 가까운 사진 100장만 선택

 

군집(clustering) : 비슷한 샘플끼리 그룹으로 모으는 작업

클러스터(cluster) : 군집 알고리즘에서 만든 그룹

 

06-2 k-평균

k-평균(k-means) 군집 알고리즘 이 평균값을 자동으로 찾아줍니다.

이 평균값이 클러스터의 중심에 위치하기 때문에 클러스터 중심(cluster center) 또는 센트로이드(centroid)라고 부릅니다.

 

k-평균 알고리즘 소개

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

 

KMeans 클래스

사이킷런의 k-평균 알고리즘은 sklearn.cluster 모듈 아래 KMeans 클래스에 구현되어 있습니다.

이 클래스에서 설정할 매개변수는 클러스터 개수를 지정하는 n_clusters입니다.

레이블값 0,1,2와 레이블 순서에는 어떤 의미도 없습니다.

실제 레이블 0,1,2가 어떤 과일 사진을 주로 모았는지 알아보려면 직접 이미지를 출력하는 것이 최선입니다.

 

.labels_ 속성 → 각 데이터 포인트가 어느 클러스터에 속하는지 나타내는 배열

np.unique() → 배열 안에서 고유값(중복 없는 값)을 찾아서 정렬해 반환

return_counts=True → 각 고유값이 몇 번 등장했는지도 함께 반환

첫 번째 배열 → 클러스터 번호 목록

두 번째 배열 → 각 클러스터에 속한 샘플 개수

 

draw_fruits(arr, ratio=1)

  • arr 배열(예: 여러 장의 과일 이미지)을 받아서 여러 이미지를 그리드(격자) 형태로 예쁘게 한 번에 보여주는 함수
  • (샘플 개수, 너비, 높이)의 3차원 배열을 입력받으며 가로로 10개씩 이미지를 출력
  • ratio는 각 이미지 크기 조절에 쓰임.

n = len(arr)

  • arr에 들어있는 이미지 개수를 저장

rows = int(np.ceil(n/10))

  • 한 행에 최대 10개씩 놓는다고 가정
  • np.ceil(n/10)n을 10으로 나눈 후 올림 (예: 35 → 3.5 → 4행 필요)
  • int()는 정수형으로 변환
  • 즉, 필요한 행(row) 개수 계산

cols = n if rows < 2 else 10

  • 만약 이미지 개수가 10개 이하면 → 한 행에 다 놓음 (cols = n)
  • 10개 넘으면 → 열은 무조건 10개 (cols = 10)

fig, axs = plt.subplots(rows, cols, figsize=(cols*ratio, rows*ratio), squeeze=False)

  • plt.subplotsrows x cols 크기의 빈 그래프판 만들기
  • figsize → 전체 그림 크기 설정 (가로 cols * ratio, 세로 rows * ratio)
  • squeeze=Falseaxs를 항상 2차원 배열로 만들어서 인덱싱 편하게 함

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')

  • 2중 for문으로 rows행, cols열만큼 반복
  • i*10 + j는 현재 그리드에서 몇 번째 이미지를 그릴지 계산
  • 만약 인덱스가 이미지 개수 n보다 작으면 → axs[i, j].imshow()로 해당 위치에 이미지 그림
  • axs[i, j].axis('off') → 축 눈금이나 테두리 표시 안 함

 

불리언 인덱싱(Boolean Indexing) :  배열(예: NumPy 배열)에서 조건에 맞는 원소들만 선택해서 추출하는 방법

배열에서 True 또는 False 값을 가진 같은 크기의 불리언 배열을 만들어서,

True인 위치에 해당하는 원소들만 골라내는 방식

 

 

 

 

 

댓글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다