🤖

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

⚠️

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

A

AI Generated

2025. 12. 4. · 53 Views

비지도 학습과 클러스터링 완벽 가이드

머신러닝의 핵심 개념인 비지도 학습과 다양한 클러스터링 알고리즘을 초급 개발자도 쉽게 이해할 수 있도록 설명합니다. k-평균부터 DBSCAN까지, 실무에서 바로 적용할 수 있는 코드와 함께 배워봅니다.


목차

  1. 비지도_학습_개요
  2. 데이터_마이닝_생명_주기
  3. 유사도_계산_방법
  4. k평균_클러스터링
  5. 계층적_클러스터링
  6. DBSCAN_알고리즘
  7. 클러스터_평가_방법

1. 비지도 학습 개요

김개발 씨는 이커머스 회사에서 일하는 주니어 데이터 분석가입니다. 어느 날 팀장님이 다가와 말했습니다.

"김 씨, 우리 고객들을 비슷한 그룹으로 나눠볼 수 있을까요? 근데 어떤 그룹이 있는지는 나도 몰라요." 정답이 없는 데이터에서 패턴을 찾아야 하는 상황, 바로 비지도 학습이 필요한 순간입니다.

비지도 학습은 정답 라벨이 없는 데이터에서 숨겨진 패턴이나 구조를 스스로 발견하는 머신러닝 기법입니다. 마치 처음 가본 도서관에서 사서 없이 책들의 배치 규칙을 스스로 파악하는 것과 같습니다.

이 방법을 이해하면 고객 세분화, 이상 탐지, 추천 시스템 등 다양한 분야에 적용할 수 있습니다.

다음 코드를 살펴봅시다.

from sklearn.cluster import KMeans
import numpy as np

# 정답 라벨이 없는 고객 데이터 (구매금액, 방문횟수)
customer_data = np.array([
    [100, 5], [150, 8], [200, 12],  # 그룹 A로 추정
    [500, 25], [600, 30], [550, 28],  # 그룹 B로 추정
    [1000, 50], [1200, 55], [1100, 52]  # 그룹 C로 추정
])

# 비지도 학습: 라벨 없이 3개 그룹으로 클러스터링
model = KMeans(n_clusters=3, random_state=42)
labels = model.fit_predict(customer_data)

print("발견된 그룹:", labels)

김개발 씨는 입사 6개월 차 주니어 데이터 분석가입니다. 오늘 팀장님에게 특별한 과제를 받았습니다.

"우리 고객 데이터를 분석해서 비슷한 고객끼리 묶어줄 수 있어요? 마케팅팀에서 타겟 캠페인을 하고 싶대요." 김개발 씨는 고개를 갸웃거렸습니다.

"그런데 팀장님, 어떤 기준으로 묶어야 하는 건가요? VIP 고객이라든가, 이탈 위험 고객이라든가 라벨이 있어야 하지 않나요?" 팀장님이 웃으며 대답했습니다.

"그게 문제야. 우리는 아직 어떤 그룹이 있는지도 몰라.

데이터가 스스로 말해주길 바라는 거지." 바로 이런 상황에서 비지도 학습이 등장합니다. 머신러닝은 크게 두 가지로 나뉩니다.

하나는 지도 학습으로, 선생님이 정답을 알려주며 가르치는 방식입니다. 스팸 메일 분류기를 만들 때 "이건 스팸, 이건 정상"이라고 라벨을 붙여 학습시키는 것이 대표적입니다.

반면 비지도 학습은 정답 없이 학습합니다. 마치 처음 전학 온 학생이 교실에서 누가 누구와 친한지 스스로 파악하는 것과 같습니다.

아무도 알려주지 않았지만, 관찰을 통해 자연스럽게 그룹이 보이기 시작합니다. 비지도 학습이 왜 필요할까요?

현실 세계의 데이터 대부분은 라벨이 없기 때문입니다. 모든 고객에게 "당신은 VIP입니다"라고 미리 표시해둘 수 없습니다.

모든 거래에 "이건 사기입니다"라고 붙여둘 수도 없습니다. 위의 코드를 살펴보겠습니다.

customer_data에는 9명의 고객 정보가 있습니다. 각각 구매금액과 방문횟수를 담고 있죠.

우리는 이 고객들이 어떤 그룹에 속하는지 전혀 모릅니다. KMeans라는 알고리즘에게 "3개 그룹으로 나눠줘"라고만 요청합니다.

그러면 알고리즘이 스스로 데이터를 분석해서 비슷한 고객끼리 묶어줍니다. 결과적으로 소액 구매자, 중간 구매자, 대량 구매자로 자연스럽게 분류됩니다.

실제 현업에서 비지도 학습은 다양하게 활용됩니다. 넷플릭스는 시청 패턴이 비슷한 사용자를 묶어 추천에 활용합니다.

신용카드 회사는 평소와 다른 거래 패턴을 감지해 사기를 탐지합니다. 뉴스 사이트는 비슷한 기사를 자동으로 그룹화합니다.

주의할 점도 있습니다. 비지도 학습은 정답이 없기 때문에 결과가 좋은지 나쁜지 판단하기 어렵습니다.

3개 그룹이 맞는지, 5개가 맞는지 알고리즘이 알려주지 않습니다. 이 부분은 도메인 전문가의 해석이 필요합니다.

김개발 씨는 고개를 끄덕였습니다. "아하, 일단 데이터에게 패턴을 물어보고, 그 결과를 우리가 해석하면 되는 거군요!" 맞습니다.

비지도 학습은 데이터 속 숨겨진 이야기를 찾아내는 탐정 역할을 합니다.

실전 팁

💡 - 비지도 학습은 탐색적 데이터 분석의 첫 단계로 활용하기 좋습니다

  • 결과 해석을 위해 시각화 도구를 함께 사용하세요
  • 클러스터 개수는 여러 번 실험해보며 최적값을 찾아야 합니다

2. 데이터 마이닝 생명 주기

박시니어 씨가 김개발 씨에게 조언했습니다. "클러스터링을 하기 전에 전체 과정을 먼저 이해해야 해요.

데이터 마이닝에는 정해진 단계가 있거든요." 김개발 씨는 궁금해졌습니다. 도대체 어떤 단계들을 거쳐야 의미 있는 결과를 얻을 수 있을까요?

데이터 마이닝 생명 주기는 원시 데이터에서 유용한 지식을 추출하기까지의 체계적인 과정입니다. 마치 금광에서 원석을 캐내 순금으로 정제하는 과정과 같습니다.

이 단계를 따르면 체계적이고 재현 가능한 분석을 수행할 수 있습니다.

다음 코드를 살펴봅시다.

import pandas as pd
from sklearn.preprocessing import StandardScaler
from sklearn.cluster import KMeans

# 1단계: 데이터 수집
raw_data = pd.read_csv('customers.csv')

# 2단계: 데이터 전처리 (결측치 제거, 이상치 처리)
cleaned_data = raw_data.dropna()
cleaned_data = cleaned_data[cleaned_data['age'] > 0]

# 3단계: 데이터 변환 (정규화)
scaler = StandardScaler()
scaled_data = scaler.fit_transform(cleaned_data[['age', 'income']])

# 4단계: 모델링 (클러스터링)
model = KMeans(n_clusters=3, random_state=42)
clusters = model.fit_predict(scaled_data)

# 5단계: 평가 및 해석
cleaned_data['cluster'] = clusters
print(cleaned_data.groupby('cluster').mean())

김개발 씨는 고객 데이터를 받자마자 바로 클러스터링을 돌려보았습니다. 그런데 결과가 이상했습니다.

20대 청년과 80대 노인이 같은 그룹에 묶여 있었고, 월 소득 300만 원인 사람과 3000만 원인 사람이 같은 클러스터에 들어가 있었습니다. 박시니어 씨가 화면을 보더니 한숨을 쉬었습니다.

"전처리를 안 했구나. 데이터 마이닝에는 반드시 거쳐야 할 단계들이 있어." 첫 번째 단계는 데이터 수집입니다.

분석에 필요한 데이터를 모으는 과정입니다. 데이터베이스에서 추출할 수도 있고, API를 통해 외부에서 가져올 수도 있습니다.

이 단계에서 가장 중요한 것은 분석 목적에 맞는 데이터를 확보하는 것입니다. 두 번째 단계는 데이터 전처리입니다.

현실의 데이터는 완벽하지 않습니다. 나이가 -5인 레코드, 소득이 비어 있는 고객, 중복된 행들이 존재합니다.

마치 요리 전에 재료를 손질하듯, 분석 전에 데이터를 깨끗하게 정리해야 합니다. 세 번째 단계는 데이터 변환입니다.

여기서 김개발 씨가 실수한 부분이 드러납니다. 나이는 20에서 80 사이의 값이고, 소득은 300만에서 3000만 사이의 값입니다.

단위가 다릅니다. 클러스터링 알고리즘은 숫자 크기에 민감하기 때문에, 소득 차이가 나이 차이를 압도해버립니다.

이를 해결하기 위해 정규화를 수행합니다. StandardScaler는 모든 변수의 평균을 0, 표준편차를 1로 맞춰줍니다.

이제 나이와 소득이 동등한 영향력을 갖게 됩니다. 네 번째 단계는 모델링입니다.

드디어 알고리즘을 적용하는 단계입니다. 앞선 단계들을 제대로 수행했다면, 이 단계는 비교적 간단합니다.

적절한 알고리즘을 선택하고 파라미터를 설정한 뒤 실행하면 됩니다. 다섯 번째 단계는 평가 및 해석입니다.

모델이 뱉어낸 결과가 비즈니스적으로 의미가 있는지 확인합니다. 각 클러스터의 평균값을 확인하고, 실제로 구분이 잘 되었는지 시각화해봅니다.

마지막은 배포 및 모니터링입니다. 분석 결과를 실제 서비스에 적용하고, 시간이 지나도 유효한지 지속적으로 확인합니다.

실제 현업에서 이 과정은 CRISP-DM이라는 표준 방법론으로 정리되어 있습니다. 전 세계 데이터 과학자들이 이 프레임워크를 따릅니다.

체계적인 과정을 따르면 다른 사람이 분석을 재현할 수 있고, 어디서 문제가 생겼는지 추적하기도 쉽습니다. 김개발 씨는 처음부터 다시 시작했습니다.

이번에는 전처리와 정규화를 꼼꼼히 수행한 뒤 클러스터링을 실행했습니다. 결과는 훨씬 논리적이었습니다.

"역시 순서가 중요하군요!"

실전 팁

💡 - 전처리에 전체 시간의 60-70%가 소요되는 것은 정상입니다

  • 각 단계를 Jupyter Notebook에 기록하면 나중에 재현이 쉽습니다
  • 정규화는 거리 기반 알고리즘에서 특히 중요합니다

3. 유사도 계산 방법

"두 고객이 얼마나 비슷한지 어떻게 알 수 있어요?" 김개발 씨의 질문에 박시니어 씨가 대답했습니다. "그걸 측정하는 방법이 여러 가지 있어.

어떤 방법을 쓰느냐에 따라 결과가 완전히 달라지기도 하지." 클러스터링의 핵심은 바로 유사도를 어떻게 정의하느냐에 달려 있습니다.

유사도는 두 데이터 포인트가 얼마나 비슷한지를 수치로 나타낸 것입니다. 마치 두 사람의 취향이 얼마나 맞는지 점수로 매기는 것과 같습니다.

유클리드 거리, 맨해튼 거리, 코사인 유사도 등 다양한 방법이 있으며, 데이터의 특성에 따라 적절한 방법을 선택해야 합니다.

다음 코드를 살펴봅시다.

import numpy as np
from scipy.spatial.distance import euclidean, cityblock, cosine

# 두 고객의 특성 벡터
customer_a = np.array([100, 5, 3])  # 구매금액, 방문횟수, 리뷰수
customer_b = np.array([150, 8, 4])

# 유클리드 거리: 직선 거리
euclidean_dist = euclidean(customer_a, customer_b)
print(f"유클리드 거리: {euclidean_dist:.2f}")

# 맨해튼 거리: 격자 따라 이동하는 거리
manhattan_dist = cityblock(customer_a, customer_b)
print(f"맨해튼 거리: {manhattan_dist:.2f}")

# 코사인 유사도: 방향의 유사성 (1에 가까울수록 유사)
cosine_sim = 1 - cosine(customer_a, customer_b)
print(f"코사인 유사도: {cosine_sim:.4f}")

김개발 씨는 클러스터링 결과를 보며 의문이 들었습니다. "알고리즘이 어떻게 비슷한 고객을 찾아내는 거죠?

무슨 기준으로 비슷하다고 판단하는 건가요?" 박시니어 씨가 종이를 꺼내 그림을 그리기 시작했습니다. "자, 서울에서 부산까지 가는 거리를 어떻게 잴 수 있을까?" "직선 거리로 재면 약 325km쯤 될 것 같은데요." 김개발 씨가 대답했습니다.

"맞아. 그런데 실제로 자동차를 타고 가면?" "고속도로를 따라 가니까 400km 넘게 나오겠죠." 바로 이 차이가 유클리드 거리맨해튼 거리의 차이입니다.

유클리드 거리는 두 점 사이의 직선 거리입니다. 피타고라스 정리를 떠올리면 됩니다.

학교에서 배운 그 공식, 루트 안에 각 좌표 차이의 제곱을 더하는 방식입니다. 가장 직관적이고 널리 사용되는 방법입니다.

맨해튼 거리는 격자를 따라 이동하는 거리입니다. 뉴욕 맨해튼의 바둑판식 도로망에서 유래한 이름입니다.

직선으로 갈 수 없고 도로를 따라 가로, 세로로만 이동해야 할 때 적합합니다. 각 좌표 차이의 절댓값을 단순히 더합니다.

그런데 여기서 흥미로운 상황이 있습니다. 고객 A는 월 10만 원을 쓰고, 고객 B는 월 100만 원을 씁니다.

고객 C는 월 1000만 원을 쓰고, 고객 D는 월 1100만 원을 씁니다. 유클리드 거리로 보면 A-B 거리는 90만, C-D 거리도 100만으로 비슷합니다.

하지만 비율로 보면 A-B는 10배 차이, C-D는 1.1배 차이입니다. 이런 경우 코사인 유사도가 유용합니다.

코사인 유사도는 두 벡터가 이루는 각도로 유사성을 측정합니다. 크기보다 방향을 본다고 생각하면 됩니다.

두 벡터가 같은 방향을 가리키면 유사도 1, 직각이면 0, 반대 방향이면 -1입니다. 실무에서 언제 어떤 방법을 쓸까요?

물리적 거리가 중요한 배달 최적화에는 유클리드 거리가 적합합니다. 도시 내 택시 요금 계산에는 맨해튼 거리가 맞습니다.

문서 유사도나 추천 시스템에는 코사인 유사도를 주로 사용합니다. 위 코드에서 scipy 라이브러리의 함수들을 사용했습니다.

euclidean, cityblock, cosine 함수가 각각 세 가지 거리를 계산합니다. 주의할 점은 cosine 함수가 반환하는 값은 거리이므로, 유사도로 바꾸려면 1에서 빼야 합니다.

김개발 씨가 고개를 끄덕였습니다. "그러니까 데이터의 특성에 따라 적절한 거리 척도를 선택해야 하는 거군요.

만능 정답은 없고요." 정확합니다. 클러스터링 결과가 이상하다면, 거리 척도를 바꿔보는 것도 좋은 시도입니다.

실전 팁

💡 - 스케일이 다른 변수가 있다면 정규화 후 유클리드 거리를 사용하세요

  • 텍스트나 희소 벡터에는 코사인 유사도가 효과적입니다
  • 거리와 유사도는 반비례 관계임을 기억하세요

4. k평균 클러스터링

"가장 유명한 클러스터링 알고리즘이 뭐예요?" 김개발 씨가 물었습니다. "단연 k-평균이지.

이해하기 쉽고, 빠르고, 대부분의 상황에서 잘 작동해." 박시니어 씨가 화면을 켜며 설명을 시작했습니다.

k-평균 클러스터링은 데이터를 k개의 그룹으로 나누는 가장 대표적인 알고리즘입니다. 마치 k명의 반장이 학생들을 자기 조로 모으는 것과 같습니다.

각 반장의 위치를 조정하며 가장 효율적인 조 구성을 찾아갑니다. 구현이 단순하고 속도가 빨라 실무에서 가장 많이 사용됩니다.

다음 코드를 살펴봅시다.

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

# 샘플 데이터 생성
np.random.seed(42)
data = np.vstack([
    np.random.randn(50, 2) + [0, 0],
    np.random.randn(50, 2) + [5, 5],
    np.random.randn(50, 2) + [10, 0]
])

# k-평균 클러스터링 수행
kmeans = KMeans(n_clusters=3, random_state=42, n_init=10)
kmeans.fit(data)

# 결과 확인
print("클러스터 중심점:", kmeans.cluster_centers_)
print("각 데이터의 라벨:", kmeans.labels_[:10])
print("관성(inertia):", kmeans.inertia_)

박시니어 씨가 칠판에 점 20개를 찍었습니다. "자, 여기 있는 점들을 3개 그룹으로 나눠볼 거야.

내가 k-평균 알고리즘을 직접 해볼게." 그는 빨간색, 파란색, 녹색 마커를 꺼내 세 곳에 무작위로 점을 찍었습니다. "이게 초기 중심점이야.

아무 데나 찍어도 돼." 다음으로 각 데이터 포인트에서 세 중심점까지의 거리를 계산했습니다. 그리고 가장 가까운 중심점의 색으로 각 점을 칠했습니다.

"이게 첫 번째 할당 단계야." "근데 중심점 위치가 이상하지 않아요? 그룹의 가운데가 아니라 가장자리에 있는 것 같은데요." 김개발 씨가 지적했습니다.

"좋은 관찰이야. 그래서 두 번째 단계가 필요해." 박시니어 씨는 빨간 점들의 평균 위치를 계산해서 빨간 중심점을 옮겼습니다.

파란색, 녹색도 마찬가지로 옮겼습니다. "이게 업데이트 단계야." 그리고 다시 할당 단계를 수행했습니다.

중심점이 옮겨졌으니 일부 점들의 소속이 바뀌었습니다. 다시 업데이트 단계.

다시 할당 단계. 이 과정을 반복하자 어느 순간 더 이상 변화가 없어졌습니다.

"이렇게 수렴할 때까지 반복하는 거야. 보통 몇 번이면 끝나." 박시니어 씨가 마무리했습니다.

이것이 k-평균 알고리즘의 전부입니다. 할당과 업데이트를 반복하며 최적의 그룹을 찾아갑니다.

코드를 살펴보겠습니다. KMeans 클래스의 n_clusters가 바로 k값입니다.

fit 메서드를 호출하면 위의 과정이 자동으로 수행됩니다. cluster_centers_에 최종 중심점 좌표가, labels_에 각 데이터의 소속 그룹이 저장됩니다.

inertia는 각 데이터 포인트와 소속 중심점 사이 거리의 제곱 합입니다. 이 값이 작을수록 클러스터가 조밀하게 모여 있다는 의미입니다.

하지만 k-평균에는 약점이 있습니다. 첫째, k를 미리 정해야 합니다.

3개로 나눌지, 5개로 나눌지 알고리즘이 알려주지 않습니다. 둘째, 초기 중심점 위치에 따라 결과가 달라질 수 있습니다.

이를 해결하기 위해 n_init 파라미터로 여러 번 시도해서 가장 좋은 결과를 선택합니다. 셋째, 원형이 아닌 클러스터를 잘 찾지 못합니다.

초승달 모양이나 도넛 모양의 데이터 분포에는 k-평균이 적합하지 않습니다. 그럼 k는 어떻게 정할까요?

엘보우 방법이 있습니다. k를 1부터 늘려가며 inertia를 그래프로 그립니다.

꺾이는 지점, 마치 팔꿈치처럼 굽어지는 곳이 적절한 k입니다. 김개발 씨가 실습해보았습니다.

고객 데이터에 k-평균을 적용하니 3개 그룹으로 깔끔하게 나뉘었습니다. "생각보다 간단하고 효과적이네요!"

실전 팁

💡 - 엘보우 방법으로 적절한 k를 찾아보세요

  • 결과가 불안정하면 n_init 값을 높여 여러 번 시도하세요
  • 대용량 데이터에는 MiniBatchKMeans가 더 빠릅니다

5. 계층적 클러스터링

"k-평균은 k를 미리 정해야 하잖아요. 몇 개가 적당한지 모를 때는 어떻게 해요?" 김개발 씨의 질문에 박시니어 씨가 새로운 알고리즘을 소개했습니다.

"그럴 때 계층적 클러스터링을 써봐. 나무처럼 생긴 그래프를 보고 직접 자르는 위치를 정할 수 있거든."

계층적 클러스터링은 데이터를 계층 구조로 묶어가는 알고리즘입니다. 마치 회사의 조직도처럼, 개인에서 팀으로, 팀에서 부서로, 부서에서 본부로 점점 큰 그룹을 형성합니다.

덴드로그램이라는 나무 그래프를 통해 적절한 클러스터 개수를 시각적으로 결정할 수 있다는 장점이 있습니다.

다음 코드를 살펴봅시다.

from scipy.cluster.hierarchy import dendrogram, linkage, fcluster
from scipy.spatial.distance import pdist
import numpy as np
import matplotlib.pyplot as plt

# 샘플 데이터
data = np.array([
    [1, 2], [2, 2], [2, 3],
    [8, 7], [8, 8], [9, 8],
    [25, 80], [26, 82], [27, 81]
])

# 계층적 클러스터링 수행 (병합 방식)
linkage_matrix = linkage(data, method='ward')

# 덴드로그램 시각화
plt.figure(figsize=(10, 5))
dendrogram(linkage_matrix)
plt.title('Dendrogram')
plt.show()

# 3개 클러스터로 자르기
clusters = fcluster(linkage_matrix, t=3, criterion='maxclust')
print("클러스터 라벨:", clusters)

박시니어 씨가 작은 레고 블록들을 테이블 위에 늘어놓았습니다. "계층적 클러스터링을 레고로 설명해볼게." 처음에는 각 블록이 하나의 클러스터입니다.

가장 가까운 두 블록을 찾아 연결합니다. 이제 8개 블록 중 2개가 합쳐져서 7개 클러스터가 됩니다.

다시 가장 가까운 것들을 연결합니다. 6개, 5개, 4개...

결국 모든 블록이 하나로 합쳐질 때까지 반복합니다. 이 과정을 거꾸로 그래프로 그린 것이 덴드로그램입니다.

아래에서 위로, 개별 데이터가 점점 큰 그룹으로 병합되는 과정을 보여줍니다. "그런데 이게 k-평균보다 뭐가 좋은 거예요?" 김개발 씨가 물었습니다.

"덴드로그램을 보고 어디서 자를지 직접 결정할 수 있어." 박시니어 씨가 그래프를 가리켰습니다. "여기 높이 50에서 가로로 선을 그으면 3개 클러스터가 생기고, 높이 30에서 자르면 5개가 되지.

데이터의 자연스러운 구조를 보면서 판단할 수 있어." 위 코드에서 linkage 함수가 병합 과정을 계산합니다. method='ward'는 병합 시 클러스터 내 분산 증가가 최소화되도록 합니다.

다른 방법으로 single, complete, average 등이 있습니다. single linkage는 두 클러스터에서 가장 가까운 점 사이 거리를 사용합니다.

연쇄 효과가 발생할 수 있어 길쭉한 클러스터가 만들어지기 쉽습니다. complete linkage는 가장 먼 점 사이 거리를 사용합니다.

조밀한 구형 클러스터를 만드는 경향이 있습니다. average linkage는 모든 점 쌍의 평균 거리를 사용합니다.

single과 complete의 중간 성격을 가집니다. ward는 병합으로 인한 분산 증가를 최소화합니다.

일반적으로 가장 좋은 결과를 주어 기본값으로 많이 사용됩니다. 계층적 클러스터링의 단점도 있습니다.

모든 데이터 쌍의 거리를 계산해야 하므로 데이터가 많으면 느립니다. O(n^2) 이상의 시간 복잡도를 가집니다.

수천 개 정도까지는 괜찮지만, 수만 개가 넘어가면 다른 방법을 고려해야 합니다. 실무에서는 샘플링을 활용합니다.

전체 데이터 중 일부만 뽑아 계층적 클러스터링으로 구조를 파악하고, 적절한 k를 정한 뒤 전체 데이터에 k-평균을 적용하는 방식입니다. 김개발 씨가 덴드로그램을 유심히 살펴보았습니다.

"오, 진짜 나무처럼 생겼네요. 그리고 어디서 자르느냐에 따라 그룹 개수가 달라지는 게 직관적이에요."

실전 팁

💡 - 덴드로그램에서 긴 세로 막대 아래를 자르면 자연스러운 클러스터를 얻습니다

  • 대용량 데이터에는 샘플링 후 적용하세요
  • ward 방식이 일반적으로 가장 좋은 결과를 줍니다

6. DBSCAN 알고리즘

김개발 씨가 클러스터링 결과를 보며 고민에 빠졌습니다. 대부분의 고객은 잘 분류되었는데, 몇몇 이상한 고객들이 있었습니다.

새벽 3시에 접속해서 이상한 패턴을 보이는 고객들. "이 사람들은 어떤 그룹에 넣어야 하죠?" 박시니어 씨가 답했습니다.

"그 사람들은 어디에도 안 넣으면 돼. DBSCAN이라는 알고리즘이 그걸 할 수 있어."

DBSCAN은 밀도 기반 클러스터링 알고리즘입니다. 마치 사람들이 모여 있는 곳을 그룹으로 보고, 혼자 떨어져 있는 사람은 이상치로 분류하는 것과 같습니다.

k를 미리 정하지 않아도 되고, 다양한 형태의 클러스터를 찾을 수 있으며, 자동으로 이상치를 탐지한다는 장점이 있습니다.

다음 코드를 살펴봅시다.

from sklearn.cluster import DBSCAN
import numpy as np

# 샘플 데이터 (정상 데이터 + 이상치)
data = np.array([
    [1, 2], [1.5, 1.8], [1.2, 2.1], [0.8, 1.9],  # 그룹 1
    [5, 8], [5.2, 7.8], [4.8, 8.2], [5.1, 8.1],  # 그룹 2
    [10, 2], [10.5, 2.3], [9.8, 1.8], [10.2, 2.1],  # 그룹 3
    [20, 20], [0, 10]  # 이상치
])

# DBSCAN 클러스터링
dbscan = DBSCAN(eps=1.0, min_samples=3)
labels = dbscan.fit_predict(data)

print("클러스터 라벨:", labels)
print("이상치 개수:", list(labels).count(-1))
# -1은 이상치(noise)를 의미

박시니어 씨가 설명을 시작했습니다. "DBSCAN은 Density-Based Spatial Clustering of Applications with Noise의 약자야.

이름에서 알 수 있듯이 밀도 기반이고, 노이즈를 다룰 수 있어." 상상해봅시다. 넓은 광장에 사람들이 흩어져 있습니다.

어떤 곳에는 사람들이 옹기종기 모여 있고, 어떤 곳에는 아무도 없습니다. 그리고 저 멀리 혼자 서 있는 사람도 있습니다.

DBSCAN은 이렇게 동작합니다. 먼저 임의의 한 사람을 선택합니다.

그 사람 주변 일정 거리(eps) 안에 몇 명이 있는지 셉니다. 만약 최소 인원(min_samples) 이상이면, 그 사람은 핵심 포인트가 됩니다.

핵심 포인트끼리 연결되면 같은 클러스터입니다. 핵심 포인트 근처에 있지만 자신은 핵심 포인트가 아닌 사람은 경계 포인트입니다.

클러스터에 속하긴 하지만 가장자리에 있는 셈입니다. 어느 핵심 포인트와도 가깝지 않은 사람은 노이즈, 즉 이상치입니다.

어떤 그룹에도 속하지 않습니다. 위 코드에서 eps=1.0은 반경 1.0 안을 이웃으로 본다는 의미입니다.

min_samples=3은 최소 3개 이상의 이웃이 있어야 핵심 포인트가 된다는 뜻입니다. 결과를 보면 0, 1, 2의 라벨은 세 개의 클러스터를 나타냅니다.

-1은 이상치입니다. 마지막 두 데이터 포인트인 [20, 20]과 [0, 10]은 다른 점들과 너무 멀리 떨어져 있어서 이상치로 분류되었습니다.

DBSCAN의 가장 큰 장점은 클러스터 개수를 미리 정하지 않아도 된다는 것입니다. 알고리즘이 데이터의 밀도를 보고 자연스럽게 결정합니다.

또한 k-평균과 달리 원형이 아닌 클러스터도 찾을 수 있습니다. 초승달 모양, 도넛 모양 등 어떤 형태든 밀도가 연결되어 있으면 같은 클러스터입니다.

하지만 단점도 있습니다. eps와 min_samples 파라미터를 적절히 설정해야 합니다.

eps가 너무 작으면 모든 점이 이상치가 되고, 너무 크면 전체가 하나의 클러스터가 됩니다. 밀도가 균일하지 않은 데이터에서는 결과가 좋지 않을 수 있습니다.

실무에서 DBSCAN은 이상 탐지에 많이 사용됩니다. 사기 거래 탐지, 네트워크 침입 탐지, 제조업의 불량품 검출 등에 활용됩니다.

정상 패턴에서 벗어난 데이터를 자동으로 찾아주기 때문입니다. 김개발 씨가 눈을 빛냈습니다.

"새벽 3시에 이상한 행동을 하는 고객들, DBSCAN으로 자동으로 찾아낼 수 있겠네요!"

실전 팁

💡 - eps 값은 k-거리 그래프를 통해 결정할 수 있습니다

  • 이상치 비율이 너무 높으면 eps를 늘려보세요
  • 밀도가 다른 클러스터에는 OPTICS 알고리즘을 고려하세요

7. 클러스터 평가 방법

김개발 씨가 클러스터링 결과를 팀장님께 보고했습니다. "고객을 5개 그룹으로 나눴습니다!" 팀장님이 물었습니다.

"그 분류가 얼마나 좋은 거예요? 4개나 6개보다 5개가 낫다는 근거가 뭐죠?" 김개발 씨는 말문이 막혔습니다.

클러스터링의 품질을 어떻게 측정할 수 있을까요?

클러스터 평가는 클러스터링 결과의 품질을 수치로 측정하는 방법입니다. 마치 시험 점수처럼 클러스터링이 얼마나 잘 되었는지 객관적으로 판단할 수 있습니다.

실루엣 점수, Davies-Bouldin 지수, Calinski-Harabasz 지수 등 다양한 지표가 있으며, 이를 통해 최적의 클러스터 개수를 결정할 수 있습니다.

다음 코드를 살펴봅시다.

from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_score, davies_bouldin_score
from sklearn.metrics import calinski_harabasz_score
import numpy as np

# 샘플 데이터
np.random.seed(42)
data = np.vstack([
    np.random.randn(50, 2) + [0, 0],
    np.random.randn(50, 2) + [5, 5],
    np.random.randn(50, 2) + [10, 0]
])

# 다양한 k 값으로 평가
for k in range(2, 6):
    kmeans = KMeans(n_clusters=k, random_state=42, n_init=10)
    labels = kmeans.fit_predict(data)

    silhouette = silhouette_score(data, labels)
    db_score = davies_bouldin_score(data, labels)
    ch_score = calinski_harabasz_score(data, labels)

    print(f"k={k}: 실루엣={silhouette:.3f}, DB={db_score:.3f}, CH={ch_score:.1f}")

박시니어 씨가 화이트보드에 그림을 그리기 시작했습니다. "클러스터링의 품질을 측정하는 핵심 원리는 간단해.

같은 그룹 안의 데이터는 가깝고, 다른 그룹의 데이터는 멀어야 해." 이것을 숫자로 표현한 것이 실루엣 점수입니다. 각 데이터 포인트에 대해 두 가지를 계산합니다.

첫째, 같은 클러스터 내 다른 점들과의 평균 거리(a). 둘째, 가장 가까운 다른 클러스터의 점들과의 평균 거리(b).

실루엣 값은 (b - a) / max(a, b)로 계산합니다. 이 값은 -1에서 1 사이입니다.

1에 가까우면 해당 데이터가 자기 클러스터에 잘 속해 있고 다른 클러스터와 멀리 떨어져 있다는 의미입니다. 0에 가까우면 클러스터 경계에 있다는 뜻이고, 음수면 잘못된 클러스터에 배정되었을 가능성이 높습니다.

전체 데이터의 실루엣 값 평균이 실루엣 점수입니다. 일반적으로 0.5 이상이면 합리적인 클러스터링, 0.7 이상이면 강한 구조가 있다고 봅니다.

Davies-Bouldin 지수는 다른 관점에서 접근합니다. 각 클러스터 쌍에 대해 클러스터 내 분산과 클러스터 간 거리의 비율을 계산합니다.

이 값이 낮을수록 클러스터가 조밀하고 서로 잘 분리되어 있다는 의미입니다. 0에 가까울수록 좋습니다.

Calinski-Harabasz 지수는 클러스터 간 분산과 클러스터 내 분산의 비율입니다. 높을수록 좋습니다.

클러스터 간에는 멀리 떨어져 있고, 클러스터 내에는 조밀하게 모여 있을수록 높은 값이 나옵니다. 위 코드를 실행해보면 k=3일 때 실루엣 점수가 가장 높고, DB 지수가 가장 낮으며, CH 지수가 가장 높게 나올 것입니다.

데이터가 실제로 3개 그룹으로 생성되었기 때문입니다. 하지만 현실에서는 지표들이 항상 같은 k를 가리키지 않습니다.

어떤 지표는 3이 좋다 하고, 어떤 지표는 4가 좋다 합니다. 이럴 때는 도메인 지식을 활용해야 합니다.

예를 들어 고객 세분화에서 마케팅팀이 5개 이상의 세그먼트를 관리하기 어렵다면, 지표가 6을 가리켜도 5개로 제한하는 것이 현실적입니다. 주의할 점이 있습니다.

이 지표들은 라벨 없이 계산할 수 있는 내부 평가 지표입니다. 만약 정답 라벨이 있다면 외부 평가 지표(Adjusted Rand Index, Normalized Mutual Information 등)를 사용할 수 있습니다.

김개발 씨가 여러 k 값으로 실험해보았습니다. 지표들을 표로 정리하니 팀장님도 납득할 수 있는 근거가 생겼습니다.

"k=4일 때 실루엣 점수가 0.62로 가장 높고, DB 지수도 가장 낮습니다. 4개 그룹으로 나누는 것을 제안합니다." 팀장님이 고개를 끄덕였습니다.

"좋아, 객관적인 근거가 있으니 진행해봐."

실전 팁

💡 - 실루엣 점수 0.5 이상을 목표로 하세요

  • 여러 지표를 종합적으로 고려하되, 비즈니스 제약도 감안하세요
  • 시각화를 병행하면 결과 해석이 더 쉬워집니다

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

#Python#MachineLearning#Clustering#KMeans#DBSCAN#Python,ML,Clustering

댓글 (0)

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

함께 보면 좋은 카드 뉴스