🤖

본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.

⚠️

본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.

이미지 로딩 중...

K-Means 클러스터링 완벽 가이드 - 슬라이드 1/8
A

AI Generated

2025. 12. 17. · 4 Views

K-Means 클러스터링 완벽 가이드

데이터를 자동으로 비슷한 그룹으로 묶어주는 K-Means 클러스터링 알고리즘을 처음부터 끝까지 배웁니다. 센트로이드 개념부터 실전 활용까지 이북처럼 술술 읽히는 스타일로 정리했습니다.


목차

  1. K-Means 알고리즘 원리
  2. 센트로이드 개념
  3. KMeans 임포트 및 사용
  4. n_clusters 파라미터
  5. fit_predict() 사용법
  6. 클러스터 레이블 확인
  7. 초기화 방법 (k-means++)

1. K-Means 알고리즘 원리

어느 날 김개발 씨는 회사에서 수천 명의 고객 데이터를 분석하라는 업무를 받았습니다. "이 고객들을 비슷한 특성끼리 묶어서 마케팅 전략을 세워야 해요." 팀장님의 말씀에 김개발 씨는 막막했습니다.

수천 개의 데이터를 어떻게 그룹으로 나눈다는 걸까요?

K-Means 알고리즘은 데이터를 자동으로 비슷한 그룹으로 묶어주는 머신러닝 기법입니다. 마치 과일 가게 주인이 사과, 배, 포도를 각각의 바구니에 담듯이, 데이터의 특성을 보고 자동으로 분류합니다.

이 알고리즘을 이해하면 고객 세분화, 이미지 압축, 이상 탐지 등 다양한 문제를 해결할 수 있습니다.

다음 코드를 살펴봅시다.

import numpy as np
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans

# 샘플 데이터 생성 (고객의 나이와 구매금액)
data = np.array([
    [25, 50000], [27, 52000], [30, 60000],  # 젊고 소비가 적은 그룹
    [45, 120000], [48, 130000], [50, 125000],  # 중년 고소득 그룹
    [65, 30000], [67, 28000], [70, 32000]  # 노년 저소득 그룹
])

# K-Means로 3개 그룹으로 분류
kmeans = KMeans(n_clusters=3, random_state=42)
labels = kmeans.fit_predict(data)
print(f"각 데이터의 그룹: {labels}")

김개발 씨는 고민 끝에 선배 개발자 박시니어 씨를 찾아갔습니다. "선배님, 수천 개의 고객 데이터를 어떻게 그룹으로 나눠야 할까요?" 박시니어 씨는 웃으며 답했습니다.

"K-Means 클러스터링을 사용해보세요. 딱 이런 상황에 쓰는 알고리즘이에요." 그렇다면 K-Means 알고리즘이란 정확히 무엇일까요?

쉽게 비유하자면, K-Means는 마치 학교 운동회에서 학생들을 팀으로 나누는 것과 같습니다. 먼저 각 팀의 주장을 무작위로 정합니다.

그리고 학생들은 자신과 가장 가까운 주장이 있는 팀으로 들어갑니다. 그 다음 각 팀의 중심에 있는 학생이 새로운 주장이 됩니다.

이 과정을 반복하면 자연스럽게 균형 잡힌 팀이 만들어집니다. K-Means도 이와 똑같은 방식으로 데이터를 그룹으로 나눕니다.

K-Means 알고리즘이 없던 시절에는 어땠을까요? 개발자들은 데이터를 하나하나 육안으로 확인하며 수작업으로 분류해야 했습니다.

데이터가 수십 개라면 가능하겠지만, 수천, 수만 개가 되면 사실상 불가능합니다. 더 큰 문제는 사람마다 분류 기준이 달라서 일관성이 없다는 점이었습니다.

또한 데이터가 3차원 이상의 복잡한 특성을 가지면 사람의 눈으로는 패턴을 찾기가 거의 불가능했습니다. 바로 이런 문제를 해결하기 위해 K-Means 알고리즘이 등장했습니다.

K-Means를 사용하면 자동으로 데이터의 패턴을 찾아 그룹으로 나눌 수 있습니다. 또한 일관된 기준으로 분류하므로 누가 실행하든 같은 결과가 나옵니다.

무엇보다 대량의 데이터를 빠르게 처리할 수 있다는 큰 이점이 있습니다. 고차원 데이터도 문제없이 처리할 수 있습니다.

K-Means 알고리즘의 작동 원리는 다음과 같습니다. 첫 번째 단계는 초기화입니다.

K개의 중심점을 무작위로 정합니다. 예를 들어 고객을 3개 그룹으로 나누고 싶다면 데이터 공간에 3개의 점을 임의로 찍는 것입니다.

두 번째 단계는 할당입니다. 각 데이터 포인트를 가장 가까운 중심점에 배정합니다.

거리는 보통 유클리드 거리를 사용합니다. 마치 각 학생이 가장 가까운 주장에게 가는 것과 같습니다.

세 번째 단계는 업데이트입니다. 각 그룹에 속한 데이터들의 평균 위치를 계산하여 중심점을 새로 정합니다.

이제 중심점이 해당 그룹의 진정한 중심이 됩니다. 네 번째 단계는 반복입니다.

중심점이 더 이상 움직이지 않을 때까지 2~3단계를 반복합니다. 보통 몇 번의 반복만으로 안정적인 결과에 도달합니다.

위의 코드를 한 줄씩 살펴보겠습니다. 먼저 4번째 줄부터 9번째 줄을 보면 고객의 나이와 월 구매금액 데이터를 생성합니다.

실제로는 이런 데이터를 데이터베이스나 CSV 파일에서 가져올 것입니다. 12번째 줄에서는 KMeans 객체를 생성하는데, n_clusters=3은 3개 그룹으로 나누겠다는 의미입니다.

13번째 줄의 fit_predict()는 한 번에 학습과 예측을 수행합니다. 결과는 각 데이터가 어느 그룹에 속하는지를 나타내는 숫자입니다.

실제 현업에서는 어떻게 활용할까요? 예를 들어 이커머스 회사에서 고객 세분화를 한다고 가정해봅시다.

고객의 나이, 구매 빈도, 평균 구매 금액, 최근 접속일 등의 데이터를 K-Means로 분석하면 "VIP 고객", "일반 고객", "이탈 위험 고객" 같은 그룹을 자동으로 만들 수 있습니다. 각 그룹에 맞는 맞춤형 마케팅을 진행하면 훨씬 효과적입니다.

실제로 아마존, 넷플릭스 같은 기업들이 이런 패턴을 적극적으로 사용하고 있습니다. 하지만 주의할 점도 있습니다.

초보 개발자들이 흔히 하는 실수 중 하나는 K값(그룹의 개수)을 임의로 정하는 것입니다. "그냥 5개로 나누면 되겠지"라고 생각하면 의미 없는 결과가 나올 수 있습니다.

적절한 K값을 찾기 위해서는 엘보우 방법이나 실루엣 분석 같은 기법을 사용해야 합니다. 또한 데이터의 스케일이 다르면 왜곡된 결과가 나오므로 반드시 정규화표준화를 먼저 수행해야 합니다.

다시 김개발 씨의 이야기로 돌아가 봅시다. 박시니어 씨의 설명을 들은 김개발 씨는 고개를 끄덕였습니다.

"아, 그래서 그랬군요! 자동으로 패턴을 찾아주는 알고리즘이었네요." K-Means 알고리즘을 제대로 이해하면 복잡한 데이터 속에서 의미 있는 그룹을 찾아낼 수 있습니다.

여러분도 오늘 배운 내용을 실제 프로젝트에 적용해 보세요.

실전 팁

💡 - K값 선정이 중요합니다. 엘보우 방법으로 최적의 K를 찾으세요.

  • 데이터를 표준화(Standardization)하여 각 특성의 스케일을 맞추세요.
  • random_state를 설정하면 실행할 때마다 같은 결과를 얻을 수 있습니다.

2. 센트로이드 개념

K-Means 알고리즘을 이해한 김개발 씨가 코드를 작성하던 중 궁금한 점이 생겼습니다. "그런데 이 중심점은 어떻게 계산되는 걸까요?" 박시니어 씨가 다가와 말했습니다.

"그게 바로 센트로이드예요. K-Means의 핵심이죠."

**센트로이드(Centroid)**는 각 클러스터의 중심점을 의미합니다. 마치 교실에서 학생들의 평균 위치를 찾는 것처럼, 그룹에 속한 모든 데이터의 평균 좌표가 센트로이드입니다.

센트로이드를 이해하면 K-Means가 어떻게 데이터를 분류하는지 명확히 알 수 있습니다.

다음 코드를 살펴봅시다.

import numpy as np
from sklearn.cluster import KMeans

# 샘플 데이터
data = np.array([
    [1, 2], [2, 3], [3, 3],  # 첫 번째 그룹
    [8, 7], [9, 8], [10, 8]  # 두 번째 그룹
])

# K-Means 적용
kmeans = KMeans(n_clusters=2, random_state=42)
kmeans.fit(data)

# 센트로이드 확인
centroids = kmeans.cluster_centers_
print(f"센트로이드 좌표:\n{centroids}")
# 출력: [[2. 2.66666667], [9. 7.66666667]]

김개발 씨는 K-Means의 결과를 보면서 문득 궁금해졌습니다. "알고리즘이 찾은 이 중심점들은 대체 어떻게 계산된 걸까?" 그래프를 그려보니 각 그룹의 데이터들이 중심점 주변에 모여 있었습니다.

센트로이드란 정확히 무엇일까요? 쉽게 비유하자면, 센트로이드는 마치 시소의 무게중심과 같습니다.

여러 명의 아이들이 시소에 앉아 있을 때, 완벽하게 균형을 맞추는 지점이 바로 무게중심입니다. 센트로이드도 이와 같이 그룹 내 모든 데이터의 균형점을 나타냅니다.

수학적으로는 해당 그룹에 속한 모든 데이터 포인트의 좌표를 더한 후 개수로 나눈 평균값입니다. 센트로이드가 없다면 어떻게 될까요?

각 데이터가 어느 그룹에 속하는지 판단할 기준이 없어집니다. "이 데이터는 A 그룹에 가까울까, B 그룹에 가까울까?"를 결정하려면 각 그룹을 대표하는 점이 필요합니다.

센트로이드가 바로 그 대표점 역할을 합니다. 또한 센트로이드 없이는 K-Means의 반복 과정 자체가 불가능합니다.

바로 이런 이유로 센트로이드는 K-Means의 핵심 개념입니다. 센트로이드를 사용하면 각 그룹의 특성을 한눈에 파악할 수 있습니다.

예를 들어 고객 세분화에서 센트로이드를 보면 "이 그룹의 평균 나이는 45세, 평균 구매금액은 12만 원"처럼 그룹의 대표 특성을 알 수 있습니다. 또한 새로운 데이터가 들어왔을 때 어느 그룹에 속할지 빠르게 판단할 수 있습니다.

가장 가까운 센트로이드를 찾기만 하면 되기 때문입니다. 센트로이드는 어떻게 계산될까요?

처음에는 무작위로 K개의 점을 센트로이드로 지정합니다. 그 다음 각 데이터를 가장 가까운 센트로이드에 할당합니다.

할당이 끝나면 각 그룹별로 속한 데이터들의 평균을 계산하여 센트로이드를 업데이트합니다. 예를 들어 첫 번째 그룹에 (1,2), (2,3), (3,3) 세 개의 데이터가 있다고 가정해봅시다.

x좌표의 평균은 (1+2+3)/3 = 2이고, y좌표의 평균은 (2+3+3)/3 ≈ 2.67입니다. 따라서 새로운 센트로이드는 (2, 2.67)이 됩니다.

이 과정을 센트로이드가 더 이상 움직이지 않을 때까지 반복합니다. 위의 코드를 한 줄씩 살펴보겠습니다.

5번째 줄부터 8번째 줄에서 두 개의 그룹으로 명확히 구분되는 데이터를 만듭니다. 11번째 줄에서 KMeans 객체를 생성하고, 12번째 줄의 fit() 메서드로 학습을 수행합니다.

fit()이 실행되는 동안 센트로이드가 반복적으로 업데이트됩니다. 15번째 줄의 cluster_centers_ 속성으로 최종 센트로이드 좌표를 확인할 수 있습니다.

실제 현업에서 센트로이드는 어떻게 활용될까요? 예를 들어 배달 서비스를 운영하는 회사를 생각해봅시다.

수백 개의 배달 주문이 들어왔을 때, K-Means로 주문을 지역별로 그룹화할 수 있습니다. 이때 각 그룹의 센트로이드가 해당 지역의 중심 위치가 됩니다.

배달원을 센트로이드 근처에 대기시키면 평균 배달 시간을 최소화할 수 있습니다. 쿠팡이나 배민 같은 기업들이 이런 방식으로 배송 효율을 높입니다.

하지만 주의할 점도 있습니다. 초보 개발자들이 흔히 하는 실수는 센트로이드를 실제 데이터 포인트로 생각하는 것입니다.

센트로이드는 평균값이므로 실제로 존재하는 데이터가 아닐 수 있습니다. 예를 들어 가족 구성원의 평균 나이가 25.7세라고 해서 25.7세인 사람이 있는 것은 아닙니다.

또한 **이상치(outlier)**가 있으면 센트로이드가 왜곡될 수 있으므로 데이터 전처리가 중요합니다. 다시 김개발 씨의 이야기로 돌아가 봅시다.

박시니어 씨의 설명을 들은 김개발 씨는 무릎을 쳤습니다. "아하!

센트로이드가 각 그룹의 대표 선수 같은 거였네요!" 센트로이드 개념을 제대로 이해하면 K-Means의 동작 원리가 명확해집니다. 여러분도 cluster_centers_ 속성으로 센트로이드를 직접 확인해 보세요.

실전 팁

💡 - cluster_centers_ 속성으로 센트로이드 좌표를 확인하세요.

  • 센트로이드는 실제 데이터 포인트가 아닌 평균값입니다.
  • 시각화할 때 센트로이드를 다른 색으로 표시하면 이해하기 쉽습니다.

3. KMeans 임포트 및 사용

이제 본격적으로 K-Means를 사용해볼 차례입니다. 김개발 씨는 파이썬 코드를 작성하려다가 막혔습니다.

"어떤 라이브러리를 사용해야 하죠?" 박시니어 씨가 답했습니다. "Scikit-learn의 KMeans를 쓰면 돼요.

가장 많이 쓰는 라이브러리죠."

Scikit-learn은 파이썬의 대표적인 머신러닝 라이브러리로, KMeans 클래스를 제공합니다. 단 몇 줄의 코드로 강력한 클러스터링을 수행할 수 있습니다.

제대로 임포트하고 사용하는 방법을 익히면 실무에서 바로 적용할 수 있습니다.

다음 코드를 살펴봅시다.

# 필요한 라이브러리 임포트
from sklearn.cluster import KMeans
import numpy as np

# 데이터 준비 (실제로는 pandas DataFrame 등에서 가져옴)
customer_data = np.array([
    [25, 3, 50000],   # 나이, 방문횟수, 구매금액
    [30, 5, 80000],
    [45, 10, 150000],
    [50, 12, 200000]
])

# KMeans 객체 생성 및 학습
model = KMeans(n_clusters=2, random_state=42)
model.fit(customer_data)

# 결과 확인
print(f"레이블: {model.labels_}")
print(f"센트로이드:\n{model.cluster_centers_}")

김개발 씨는 이제 직접 코드를 작성해보고 싶었습니다. "K-Means를 파이썬에서 어떻게 사용하나요?" 박시니어 씨는 노트북을 펼치며 설명을 시작했습니다.

"파이썬에서 머신러닝을 할 때는 거의 항상 Scikit-learn을 사용해요." Scikit-learn이란 정확히 무엇일까요? 쉽게 비유하자면, Scikit-learn은 마치 요리할 때 사용하는 만능 조리 도구 세트와 같습니다.

칼, 도마, 냄비 등이 한 세트로 구성되어 있듯이, Scikit-learn에도 분류, 회귀, 클러스터링 등 머신러닝에 필요한 모든 도구가 들어 있습니다. 그중 KMeans는 클러스터링 전용 도구입니다.

이미 검증된 알고리즘이 구현되어 있어서 우리는 그냥 가져다 쓰기만 하면 됩니다. Scikit-learn 없이 K-Means를 구현한다면 어떨까요?

센트로이드 초기화, 거리 계산, 클러스터 할당, 센트로이드 업데이트 등 모든 과정을 직접 코딩해야 합니다. 수십 줄에서 수백 줄의 코드가 필요하고, 버그가 생기기도 쉽습니다.

또한 성능 최적화도 직접 해야 하므로 대용량 데이터 처리가 느려질 수 있습니다. 바퀴를 다시 발명하는 셈입니다.

바로 이런 이유로 Scikit-learn의 KMeans를 사용합니다. Scikit-learn을 사용하면 검증된 구현을 바로 활용할 수 있습니다.

수많은 개발자와 연구자가 사용하고 테스트한 코드이므로 안정성이 보장됩니다. 또한 일관된 API로 다른 머신러닝 알고리즘과 같은 방식으로 사용할 수 있습니다.

fit(), predict() 같은 메서드는 모든 Scikit-learn 모델에서 동일하게 작동합니다. 무엇보다 빠른 성능을 제공합니다.

내부적으로 C로 구현되어 있어 대용량 데이터도 빠르게 처리합니다. KMeans를 임포트하고 사용하는 과정을 단계별로 살펴보겠습니다.

첫 번째로 라이브러리를 임포트합니다. from sklearn.cluster import KMeans로 KMeans 클래스를 불러옵니다.

sklearn은 Scikit-learn의 파이썬 패키지 이름이고, cluster는 클러스터링 관련 모듈입니다. 두 번째로 데이터를 준비합니다.

NumPy 배열이나 Pandas DataFrame 형태로 준비하면 됩니다. 각 행은 하나의 데이터 포인트, 각 열은 하나의 특성(feature)을 나타냅니다.

세 번째로 KMeans 객체를 생성합니다. 이때 필요한 파라미터를 설정합니다.

가장 중요한 것은 n_clusters로, 몇 개의 그룹으로 나눌지 지정합니다. 네 번째로 fit() 메서드로 학습을 수행합니다.

이 과정에서 센트로이드가 계산되고 각 데이터가 클러스터에 할당됩니다. 다섯 번째로 결과를 확인합니다.

labels_ 속성으로 각 데이터의 클러스터 번호를, cluster_centers_ 속성으로 센트로이드 좌표를 확인할 수 있습니다. 위의 코드를 한 줄씩 살펴보겠습니다.

2번째 줄에서 KMeans 클래스를 임포트합니다. 3번째 줄의 numpy는 수치 계산용 라이브러리입니다.

6번째 줄부터 11번째 줄에서 고객 데이터를 준비하는데, 각 행은 한 명의 고객, 세 개의 열은 나이, 방문횟수, 구매금액을 나타냅니다. 14번째 줄에서 n_clusters=2로 두 개의 그룹으로 나누겠다고 지정하고, 15번째 줄의 fit()으로 실제 클러스터링을 수행합니다.

18~19번째 줄에서 결과를 출력합니다. 실제 현업에서는 어떻게 사용할까요?

예를 들어 쇼핑몰에서 상품 추천 시스템을 만든다고 가정해봅시다. 수만 명의 고객 구매 이력 데이터를 Pandas로 불러온 후, KMeans로 비슷한 구매 패턴을 가진 고객끼리 그룹화할 수 있습니다.

"이 고객은 3번 클러스터에 속하는군. 3번 클러스터 고객들이 자주 산 상품을 추천해주자"는 식으로 활용합니다.

실제로 많은 이커머스 기업들이 이런 패턴을 사용합니다. 하지만 주의할 점도 있습니다.

초보 개발자들이 흔히 하는 실수는 Scikit-learn을 설치하지 않고 KMeans를 임포트하려는 것입니다. pip install scikit-learn 명령으로 먼저 설치해야 합니다.

또한 데이터 형태가 잘못되면 에러가 발생합니다. KMeans는 2차원 배열을 입력으로 받으므로 1차원 리스트를 넣으면 안 됩니다.

반드시 reshape()하거나 처음부터 2차원으로 만들어야 합니다. 다시 김개발 씨의 이야기로 돌아가 봅시다.

박시니어 씨의 설명을 들은 김개발 씨는 직접 코드를 실행해보았습니다. "와, 정말 몇 줄 안 되는데 결과가 나오네요!" KMeans 임포트와 사용법을 익히면 실무에서 바로 클러스터링을 적용할 수 있습니다.

여러분도 직접 데이터를 준비해서 실행해 보세요.

실전 팁

💡 - pip install scikit-learn 명령으로 먼저 설치하세요.

  • 데이터는 2차원 배열(행: 샘플, 열: 특성) 형태여야 합니다.
  • fit() 메서드가 학습을 수행하고, labels_와 cluster_centers_로 결과를 확인합니다.

4. n clusters 파라미터

코드를 작성한 김개발 씨가 고민에 빠졌습니다. "고객을 몇 개 그룹으로 나눠야 할까요?

3개? 5개?" 박시니어 씨가 말했습니다.

"그게 바로 n_clusters 파라미터예요. K-Means에서 가장 중요한 설정이죠."

n_clusters는 데이터를 몇 개의 그룹으로 나눌지 지정하는 파라미터입니다. K-Means의 'K'가 바로 이 값입니다.

적절한 n_clusters 값을 찾는 것이 성공적인 클러스터링의 핵심입니다.

다음 코드를 살펴봅시다.

from sklearn.cluster import KMeans
import numpy as np
import matplotlib.pyplot as plt

# 샘플 데이터
data = np.array([[1,2], [2,3], [3,3], [8,7], [9,8], [10,8]])

# 다양한 n_clusters 값으로 실험
inertias = []  # 각 K값의 응집도 저장
K_range = range(1, 6)

for k in K_range:
    model = KMeans(n_clusters=k, random_state=42)
    model.fit(data)
    inertias.append(model.inertia_)  # 응집도(낮을수록 좋음)

print(f"K값별 inertia: {inertias}")
# 엘보우 그래프로 최적의 K를 찾습니다

김개발 씨는 KMeans 객체를 만들 때마다 고민했습니다. "n_clusters를 2로 할까, 3으로 할까?" 너무 적게 나누면 의미 없는 결과가 나올 것 같고, 너무 많이 나누면 각 그룹이 너무 작아질 것 같았습니다.

n_clusters란 정확히 무엇일까요? 쉽게 비유하자면, n_clusters는 마치 서랍장의 칸 개수를 정하는 것과 같습니다.

옷을 정리할 때 칸이 2개밖에 없으면 "상의/하의"로만 구분할 수 있습니다. 칸이 5개면 "티셔츠/셔츠/바지/치마/아우터"처럼 세밀하게 분류할 수 있습니다.

n_clusters도 이처럼 데이터를 얼마나 세밀하게 나눌지 결정합니다. 이 값이 K-Means의 'K'이며, 알고리즘 실행 전에 반드시 지정해야 합니다.

n_clusters를 잘못 설정하면 어떻게 될까요? n_clusters가 너무 작으면 성격이 다른 데이터들이 한 그룹에 섞입니다.

예를 들어 n_clusters=2로 하면 20대와 30대 고객이 한 그룹, 40대와 50대가 한 그룹이 될 수 있습니다. 너무 뭉뚱그려진 분류입니다.

반대로 n_clusters가 너무 크면 비슷한 데이터가 여러 그룹으로 쪼개집니다. 20대 고객을 5개 그룹으로 나누면 각 그룹의 특성 차이가 거의 없어서 의미가 없습니다.

그렇다면 적절한 n_clusters 값은 어떻게 찾을까요? 가장 널리 쓰이는 방법은 **엘보우 방법(Elbow Method)**입니다.

K값을 1부터 시작해서 하나씩 늘려가며 각각의 **inertia(응집도)**를 계산합니다. Inertia는 각 데이터와 해당 센트로이드 사이의 거리 제곱합으로, 낮을수록 클러스터가 잘 형성되었다는 의미입니다.

K값을 가로축, inertia를 세로축으로 그래프를 그리면 팔꿈치처럼 꺾이는 지점이 나타납니다. 바로 그 지점이 최적의 K값입니다.

또 다른 방법은 **실루엣 분석(Silhouette Analysis)**입니다. 각 데이터가 자신의 클러스터에 얼마나 잘 속해 있는지를 -1에서 1 사이의 점수로 나타냅니다.

점수가 높을수록 좋은 클러스터링입니다. 여러 K값에 대해 평균 실루엣 점수를 비교하여 가장 높은 K를 선택합니다.

위의 코드를 한 줄씩 살펴보겠습니다. 6번째 줄에서 두 개의 명확한 그룹을 가진 데이터를 준비합니다.

9번째 줄의 inertias 리스트는 각 K값에 대한 응집도를 저장할 공간입니다. 12번째 줄부터 반복문을 돌며 K=1부터 K=5까지 각각 KMeans를 적용합니다.

15번째 줄의 model.inertia_는 해당 K값에서의 응집도를 나타냅니다. 이 값들을 그래프로 그리면 어느 지점에서 감소폭이 줄어드는지 알 수 있습니다.

실제 현업에서는 어떻게 결정할까요? 예를 들어 모바일 게임 회사에서 사용자를 분류한다고 가정해봅시다.

처음에는 엘보우 방법으로 최적의 K를 찾습니다. 그래프를 보니 K=4에서 팔꿈치가 나타났습니다.

하지만 비즈니스적으로는 "무과금/소과금/중과금/고과금" 4개 그룹보다 "무과금/과금" 2개 그룹이 더 의미 있을 수 있습니다. 따라서 통계적 최적값비즈니스 요구사항을 함께 고려하여 최종 결정합니다.

하지만 주의할 점도 있습니다. 초보 개발자들이 흔히 하는 실수는 엘보우 그래프에서 명확한 꺾임이 없을 때 당황하는 것입니다.

실제 데이터는 이론처럼 깔끔하지 않아서 애매한 경우가 많습니다. 이럴 때는 여러 K값으로 시도해보고 실제 결과를 육안으로 확인하는 것이 좋습니다.

또한 n_clusters를 데이터 개수보다 크게 설정하면 의미 없는 결과가 나오므로 주의해야 합니다. 다시 김개발 씨의 이야기로 돌아가 봅시다.

박시니어 씨의 조언을 들은 김개발 씨는 엘보우 방법으로 최적의 K를 찾았습니다. "아, K=3일 때 꺾이는군요!" n_clusters 파라미터를 제대로 설정하는 것이 좋은 클러스터링의 시작입니다.

여러분도 엘보우 방법을 직접 시도해 보세요.

실전 팁

💡 - 엘보우 방법으로 최적의 K를 찾으세요 (inertia 그래프의 꺾이는 지점).

  • 실루엣 점수도 함께 확인하면 더 정확합니다.
  • 통계적 최적값뿐 아니라 비즈니스 의미도 고려하세요.

5. fit predict() 사용법

김개발 씨가 코드를 작성하다가 궁금한 점이 생겼습니다. "fit()과 predict()를 따로 호출하는데, 한 번에 할 수는 없나요?" 박시니어 씨가 웃으며 답했습니다.

"fit_predict()를 쓰면 돼요. 훨씬 간편하죠."

fit_predict() 메서드는 학습(fit)과 예측(predict)을 한 번에 수행합니다. 데이터를 학습하면서 동시에 각 데이터의 클러스터 레이블을 반환하므로 코드가 간결해집니다.

대부분의 경우 fit_predict()를 사용하는 것이 효율적입니다.

다음 코드를 살펴봅시다.

from sklearn.cluster import KMeans
import numpy as np

# 고객 데이터 (나이, 구매빈도, 구매금액)
customer_data = np.array([
    [25, 2, 30000],
    [28, 3, 35000],
    [45, 10, 150000],
    [50, 12, 180000],
    [30, 4, 50000]
])

# fit_predict()로 학습과 예측을 한 번에
kmeans = KMeans(n_clusters=2, random_state=42)
labels = kmeans.fit_predict(customer_data)

print(f"각 고객의 클러스터: {labels}")
# 출력: [0 0 1 1 0] (각 고객이 속한 그룹 번호)

김개발 씨는 코드를 작성하면서 패턴을 발견했습니다. 항상 fit()을 호출한 후 바로 predict()를 호출하고 있었습니다.

"이 두 줄을 한 줄로 줄일 수 있으면 좋을 텐데..." 그때 박시니어 씨가 fit_predict() 메서드를 알려주었습니다. fit_predict()란 정확히 무엇일까요?

쉽게 비유하자면, fit_predict()는 마치 세탁기의 "빨래+탈수" 통합 버튼과 같습니다. 빨래 버튼을 누르고 끝나기를 기다렸다가 탈수 버튼을 누르는 대신, 한 번에 두 과정을 실행하는 것입니다.

fit_predict()도 이처럼 학습(fit)과 예측(predict)을 한 번에 수행합니다. 결과적으로 코드가 짧아지고 가독성이 좋아집니다.

fit()과 predict()를 따로 호출하면 어떤 문제가 있을까요? 코드가 길어지고 중간 변수가 생깁니다.

kmeans.fit(data) 다음 줄에 labels = kmeans.predict(data) 를 써야 하는데, 같은 data를 두 번 전달해야 하므로 비효율적입니다. 또한 실수로 fit()과 predict()에 다른 데이터를 넘길 수도 있습니다.

예를 들어 훈련 데이터로 fit()하고 실수로 다른 데이터로 predict()하면 의도하지 않은 결과가 나옵니다. 바로 이런 이유로 fit_predict()를 사용합니다.

fit_predict()를 사용하면 코드가 간결해집니다. 두 줄이 한 줄로 줄어들어 가독성이 좋아집니다.

또한 실수를 방지할 수 있습니다. 같은 데이터를 한 번만 전달하므로 데이터 불일치 문제가 생기지 않습니다.

무엇보다 의도가 명확합니다. "이 데이터로 학습하고 바로 레이블을 얻겠다"는 의도가 한눈에 보입니다.

fit_predict()는 어떻게 작동할까요? 내부적으로 fit()과 predict()를 순서대로 실행합니다.

먼저 입력 데이터로 센트로이드를 계산하고 클러스터를 형성합니다(fit). 그 다음 각 데이터 포인트를 가장 가까운 센트로이드에 할당합니다(predict).

최종적으로 각 데이터의 클러스터 번호를 담은 배열을 반환합니다. 반환값은 1차원 NumPy 배열로, 각 원소는 해당 데이터가 속한 클러스터 번호입니다.

예를 들어 labels = [0, 0, 1, 1, 0]이면 첫 번째, 두 번째, 다섯 번째 데이터는 0번 클러스터에, 세 번째, 네 번째 데이터는 1번 클러스터에 속한다는 의미입니다. 위의 코드를 한 줄씩 살펴보겠습니다.

5번째 줄부터 11번째 줄에서 다섯 명의 고객 데이터를 준비합니다. 14번째 줄에서 KMeans 객체를 생성하는데, n_clusters=2로 두 그룹으로 나누겠다고 설정합니다.

15번째 줄의 fit_predict()가 핵심입니다. 이 한 줄로 학습과 예측이 모두 완료되고, 결과가 labels 변수에 저장됩니다.

18번째 줄에서 각 고객이 어느 그룹에 속하는지 확인할 수 있습니다. 실제 현업에서는 어떻게 활용할까요?

예를 들어 이미지 색상 압축을 한다고 가정해봅시다. 사진의 수백만 개 픽셀을 16개 대표 색상으로 줄이려고 합니다.

각 픽셀의 RGB 값으로 KMeans를 적용하면 16개의 클러스터(대표 색상)가 만들어집니다. 이때 fit_predict()를 사용하면 한 번에 각 픽셀이 어느 대표 색상에 속하는지 알 수 있습니다.

그 정보로 이미지를 다시 그리면 16색만 사용하는 압축 이미지가 완성됩니다. 하지만 주의할 점도 있습니다.

초보 개발자들이 흔히 하는 실수는 fit_predict()와 predict()의 차이를 모르는 것입니다. fit_predict(data)는 data로 학습하고 그 data의 레이블을 반환합니다.

반면 predict(new_data)는 이미 학습된 모델에 새로운 데이터를 넣어 레이블을 얻는 것입니다. 따라서 새로운 데이터의 클러스터를 알고 싶을 때는 predict()를 사용해야 합니다.

fit_predict()는 훈련 데이터에만 사용합니다. 다시 김개발 씨의 이야기로 돌아가 봅시다.

박시니어 씨의 조언을 들은 김개발 씨는 코드를 fit_predict()로 수정했습니다. "와, 정말 간결해졌네요!" fit_predict()를 제대로 사용하면 클린한 코드를 작성할 수 있습니다.

여러분도 학습과 예측을 동시에 할 때는 fit_predict()를 사용해 보세요.

실전 팁

💡 - 훈련 데이터의 레이블을 얻을 때는 fit_predict()를 사용하세요.

  • 새로운 데이터의 클러스터를 알고 싶으면 predict()를 사용하세요.
  • fit_predict()는 fit() + predict()를 한 번에 수행하는 편의 메서드입니다.

6. 클러스터 레이블 확인

클러스터링이 완료된 후 김개발 씨는 결과를 확인하고 싶었습니다. "각 데이터가 어느 그룹에 속하는지 어떻게 알 수 있죠?" 박시니어 씨가 답했습니다.

"labels_ 속성을 보면 돼요. 모든 정보가 거기 있어요."

클러스터 레이블은 각 데이터가 어느 클러스터에 속하는지를 나타내는 숫자입니다. labels_ 속성이나 fit_predict()의 반환값으로 확인할 수 있습니다.

레이블을 제대로 해석하면 클러스터링 결과를 실무에 활용할 수 있습니다.

다음 코드를 살펴봅시다.

from sklearn.cluster import KMeans
import numpy as np
import pandas as pd

# 고객 데이터
customers = np.array([
    [25, 30000], [28, 35000],  # 젊고 저소득
    [45, 150000], [50, 180000],  # 중년 고소득
    [30, 50000]  # 중간
])

# 클러스터링 수행
kmeans = KMeans(n_clusters=2, random_state=42)
labels = kmeans.fit_predict(customers)

# 레이블 확인 (방법 1: fit_predict 반환값)
print(f"레이블 배열: {labels}")

# 레이블 확인 (방법 2: labels_ 속성)
print(f"labels_ 속성: {kmeans.labels_}")

# 실전 활용: 원본 데이터에 레이블 추가
df = pd.DataFrame(customers, columns=['나이', '구매금액'])
df['클러스터'] = labels
print(df)

김개발 씨는 K-Means를 실행한 후 결과를 보고 싶었습니다. "이제 각 고객이 어느 그룹에 속하는지 알아야 마케팅 전략을 세울 수 있을 텐데..." 박시니어 씨가 화면을 가리키며 설명했습니다.

"labels를 출력해보세요. 모든 정보가 거기 있어요." 클러스터 레이블이란 정확히 무엇일까요?

쉽게 비유하자면, 클러스터 레이블은 마치 학생들의 반 번호와 같습니다. 학년 초에 학생들을 1반, 2반, 3반으로 나누면 각 학생은 자신의 반 번호를 배정받습니다.

"김철수: 1반, 이영희: 2반, 박민수: 1반" 이런 식입니다. 클러스터 레이블도 이처럼 각 데이터에 그룹 번호를 붙인 것입니다.

0번 클러스터, 1번 클러스터 같은 식으로 표현됩니다. 레이블 없이 클러스터링 결과를 사용한다면 어떨까요?

어느 데이터가 어느 그룹에 속하는지 알 수 없으므로 결과를 활용할 수 없습니다. "고객들을 3개 그룹으로 나눴어요"까지는 알겠는데, 정작 김철수 고객이 어느 그룹인지 모른다면 맞춤형 마케팅을 할 수 없습니다.

레이블이 있어야 "김철수는 1번 그룹이니 고가 상품을 추천하자"는 식의 액션이 가능합니다. 바로 이런 이유로 클러스터 레이블을 확인하는 것이 중요합니다.

레이블을 확인하면 각 데이터의 소속을 명확히 알 수 있습니다. 숫자 배열 형태로 제공되므로 프로그래밍으로 쉽게 처리할 수 있습니다.

또한 그룹별 분석이 가능해집니다. "0번 클러스터에는 몇 명이 있고 평균 나이는 얼마다" 같은 통계를 낼 수 있습니다.

무엇보다 실무 적용의 기반이 됩니다. 레이블을 데이터베이스에 저장하거나 추천 시스템에 활용할 수 있습니다.

레이블은 어떻게 확인할까요? 두 가지 방법이 있습니다.

첫 번째는 fit_predict()의 반환값을 사용하는 것입니다. labels = kmeans.fit_predict(data) 처럼 쓰면 labels 변수에 각 데이터의 클러스터 번호가 배열로 저장됩니다.

두 번째는 labels_ 속성을 사용하는 것입니다. fit() 메서드로 학습을 완료한 후 kmeans.labels_로 접근하면 레이블 배열을 얻을 수 있습니다.

두 방법 모두 같은 결과를 줍니다. 레이블 배열은 0부터 시작하는 정수입니다.

n_clusters=3이면 레이블은 0, 1, 2 중 하나입니다. 배열의 길이는 입력 데이터의 개수와 같습니다.

예를 들어 100개 데이터를 클러스터링하면 100개 원소를 가진 레이블 배열이 나옵니다. 위의 코드를 한 줄씩 살펴보겠습니다.

6번째 줄부터 10번째 줄에서 다섯 명의 고객 데이터를 준비합니다. 13번째 줄에서 KMeans 객체를 만들고, 14번째 줄의 fit_predict()로 클러스터링을 수행합니다.

17번째 줄에서 fit_predict()의 반환값인 labels를 출력하고, 20번째 줄에서 kmeans.labels_ 속성도 출력해봅니다. 두 값은 동일합니다.

23~25번째 줄에서는 Pandas DataFrame으로 만들어 원본 데이터와 레이블을 함께 보여줍니다. 이렇게 하면 각 고객이 어느 클러스터에 속하는지 한눈에 알 수 있습니다.

실제 현업에서는 레이블을 어떻게 활용할까요? 예를 들어 음악 스트리밍 서비스를 운영한다고 가정해봅시다.

수백만 곡을 장르별로 자동 분류하고 싶습니다. 각 곡의 템포, 음높이, 에너지 같은 특성으로 K-Means를 적용하면 비슷한 곡끼리 묶입니다.

레이블을 데이터베이스에 저장해두면 "이 곡은 3번 클러스터야. 3번 클러스터의 다른 곡들을 추천해주자"는 식으로 활용할 수 있습니다.

스포티파이 같은 서비스가 실제로 이런 방식을 사용합니다. 하지만 주의할 점도 있습니다.

초보 개발자들이 흔히 하는 실수는 레이블 번호에 의미를 부여하는 것입니다. "0번 클러스터가 1번보다 중요하다" 같은 순서는 없습니다.

레이블은 단순히 그룹을 구분하는 ID일 뿐입니다. 또한 같은 데이터로 K-Means를 여러 번 실행하면 레이블 번호가 바뀔 수 있습니다.

random_state를 고정하지 않으면 이전에 0번이던 그룹이 다음 실행에서는 1번이 될 수 있습니다. 다시 김개발 씨의 이야기로 돌아가 봅시다.

박시니어 씨의 설명을 들은 김개발 씨는 레이블을 출력해보았습니다. "아, 각 고객이 어느 그룹인지 명확히 보이네요!" 클러스터 레이블을 제대로 이해하고 활용하면 클러스터링 결과를 실전에 바로 적용할 수 있습니다.

여러분도 레이블을 원본 데이터와 함께 출력해 보세요.

실전 팁

💡 - labels_ 속성 또는 fit_predict() 반환값으로 레이블을 확인하세요.

  • Pandas DataFrame에 레이블을 추가하면 보기 편합니다.
  • 레이블 번호 자체에는 순서나 중요도의 의미가 없습니다.

7. 초기화 방법 (k-means++)

김개발 씨가 K-Means를 여러 번 실행하다가 이상한 점을 발견했습니다. "왜 때때로 결과가 좋지 않게 나올까요?" 박시니어 씨가 말했습니다.

"초기 센트로이드를 어떻게 정하느냐가 중요해요. k-means++를 쓰면 훨씬 안정적이에요."

**k-means++**는 초기 센트로이드를 똑똑하게 선택하는 방법입니다. 무작위로 선택하는 대신 서로 멀리 떨어진 점들을 센트로이드로 정하여 더 좋은 클러스터링 결과를 보장합니다.

Scikit-learn의 KMeans는 기본적으로 k-means++를 사용합니다.

다음 코드를 살펴봅시다.

from sklearn.cluster import KMeans
import numpy as np

# 샘플 데이터
data = np.array([
    [1, 2], [2, 3], [3, 3],
    [8, 7], [9, 8], [10, 8],
    [15, 2], [16, 3], [17, 3]
])

# k-means++ 초기화 (기본값)
kmeans_pp = KMeans(n_clusters=3, init='k-means++', random_state=42)
labels_pp = kmeans_pp.fit_predict(data)

# 무작위 초기화
kmeans_random = KMeans(n_clusters=3, init='random', random_state=42)
labels_random = kmeans_random.fit_predict(data)

print(f"k-means++ inertia: {kmeans_pp.inertia_:.2f}")
print(f"random inertia: {kmeans_random.inertia_:.2f}")
# k-means++가 더 낮은(더 좋은) inertia를 보입니다

김개발 씨는 같은 데이터로 K-Means를 여러 번 실행해보았습니다. 대부분 비슷한 결과가 나왔지만, 가끔 이상하게 나쁜 결과가 나왔습니다.

"왜 이렇게 불안정하지?" 박시니어 씨가 화이트보드를 펼치며 설명을 시작했습니다. "초기 센트로이드 위치가 운에 맡겨져 있었기 때문이에요." 초기화란 정확히 무엇일까요?

쉽게 비유하자면, 초기화는 마치 보물찾기 게임에서 첫 출발점을 정하는 것과 같습니다. 출발점을 잘 정하면 빠르게 보물을 찾지만, 잘못 정하면 엉뚱한 곳을 헤매게 됩니다.

K-Means도 처음에 K개의 센트로이드를 어디에 둘지 정해야 하는데, 이 초기 위치가 최종 결과에 큰 영향을 미칩니다. 운이 나쁘면 나쁜 결과에 갇힐 수 있습니다.

무작위 초기화는 어떤 문제가 있을까요? K-Means의 원래 방식은 K개의 점을 완전히 무작위로 선택하는 것입니다.

하지만 이렇게 하면 초기 센트로이드들이 모두 한쪽에 몰릴 수 있습니다. 예를 들어 3개 그룹으로 나눠야 하는데 초기 센트로이드 3개가 모두 왼쪽 구석에 배치되면, 알고리즘이 올바른 답을 찾기 어렵습니다.

또한 실행할 때마다 초기값이 달라서 결과가 불안정합니다. 바로 이런 문제를 해결하기 위해 k-means++가 등장했습니다.

k-means++를 사용하면 안정적인 결과를 얻을 수 있습니다. 초기 센트로이드를 똑똑하게 선택하므로 나쁜 결과에 빠질 확률이 크게 줄어듭니다.

또한 수렴 속도가 빨라집니다. 좋은 출발점에서 시작하므로 반복 횟수가 줄어듭니다.

무엇보다 더 나은 품질의 클러스터링을 얻을 수 있습니다. 통계적으로 더 낮은 inertia를 보장합니다.

k-means++는 어떻게 작동할까요? 첫 번째 센트로이드는 여전히 무작위로 선택합니다.

그 다음부터가 핵심입니다. 두 번째 센트로이드를 선택할 때는 첫 번째 센트로이드에서 멀리 떨어진 점을 선택할 확률을 높입니다.

정확히는 각 점과 가장 가까운 센트로이드 사이의 거리에 비례하는 확률로 선택합니다. 세 번째 센트로이드도 마찬가지입니다.

이미 선택된 두 센트로이드에서 모두 멀리 떨어진 점을 선택할 확률이 높습니다. 이런 식으로 K개의 센트로이드를 모두 선택하면, 자연스럽게 서로 멀리 떨어진 점들이 초기 센트로이드가 됩니다.

이후에는 일반 K-Means와 똑같이 진행됩니다. 위의 코드를 한 줄씩 살펴보겠습니다.

5번째 줄부터 9번째 줄에서 세 개의 명확한 그룹을 가진 데이터를 만듭니다. 12번째 줄에서 init='k-means++'로 k-means++ 초기화를 사용합니다.

사실 이것이 기본값이므로 생략해도 됩니다. 16번째 줄에서는 비교를 위해 init='random'으로 무작위 초기화를 사용합니다.

19~20번째 줄에서 각각의 inertia(응집도)를 출력하면, 대부분의 경우 k-means++가 더 낮은 값을 보입니다. 낮을수록 좋은 클러스터링입니다.

실제 현업에서는 어떻게 활용할까요? 예를 들어 대형 물류 센터에서 창고 위치를 최적화한다고 가정해봅시다.

수천 개의 배송지 좌표 데이터를 K-Means로 분석하여 5개 권역으로 나눕니다. k-means++를 사용하면 안정적으로 5개 권역의 중심을 찾을 수 있습니다.

그 중심 근처에 소규모 창고를 설치하면 배송 시간을 최소화할 수 있습니다. 쿠팡, 마켓컬리 같은 물류 기업들이 실제로 이런 방식을 활용합니다.

하지만 주의할 점도 있습니다. 초보 개발자들이 흔히 하는 실수는 k-means++가 항상 완벽한 답을 준다고 생각하는 것입니다.

k-means++는 초기화를 개선할 뿐, K-Means 자체의 한계는 극복하지 못합니다. 예를 들어 데이터가 원형이 아닌 복잡한 형태면 여전히 좋은 결과를 얻기 어렵습니다.

또한 k-means++도 확률적 방법이므로 완전히 동일한 결과를 원하면 random_state를 설정해야 합니다. 다시 김개발 씨의 이야기로 돌아가 봅시다.

박시니어 씨의 설명을 들은 김개발 씨는 고개를 끄덕였습니다. "아, 그래서 Scikit-learn이 기본적으로 k-means++를 쓰는 거였군요!" k-means++ 초기화를 이해하면 왜 현대 K-Means가 안정적인지 알 수 있습니다.

여러분도 특별한 이유가 없다면 k-means++ 기본값을 그대로 사용하세요.

실전 팁

💡 - Scikit-learn의 KMeans는 기본적으로 k-means++를 사용합니다 (init='k-means++').

  • k-means++는 초기 센트로이드를 똑똑하게 선택하여 결과를 개선합니다.
  • 재현 가능한 결과를 원하면 random_state를 설정하세요.

이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!

#Python#K-Means#Clustering#MachineLearning#Scikit-learn

댓글 (0)

댓글을 작성하려면 로그인이 필요합니다.