5. K-Means¶
군집화, 유사한 특징을 가지는 데이터들을 그룹화
데이터를 K개의 클러스터(그룹)로 군집화하는 알고리즘, 각 데이터로부터 이들의 속한 클러스터의 중심점(Centroid)까지의 평균 거리를 계산
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
dataset = pd.read_csv('../data/KMeansData.csv')
dataset.head()
hour | score | |
---|---|---|
0 | 7.33 | 73 |
1 | 3.71 | 55 |
2 | 3.43 | 55 |
3 | 3.06 | 89 |
4 | 3.33 | 79 |
비지도 학습이기 때문에 y 값 없이 X만 가져옴¶
X = dataset.iloc[:, :].values
# X = dataset.values
# X = dataset.to_numpy() # 공식 홈페이지 권장
X[:5]
array([[ 7.33, 73. ],
[ 3.71, 55. ],
[ 3.43, 55. ],
[ 3.06, 89. ],
[ 3.33, 79. ]])
데이터 시각화 (전체 데이터 분포 확인)¶
plt.scatter(X[:, 0], X[:, 1]) # x축 : hour, y축 : score
plt.title('Score by hours')
plt.xlabel('hours')
plt.ylabel('score')
plt.show()
데이터 시각화 (축 범위 통일)¶
X축 Y축 범위가 다르기 때문에 확인
plt.scatter(X[:, 0], X[:, 1]) # x축 : hour, y축 : score
plt.title('Score by hours')
plt.xlabel('hours')
plt.xlim(0, 100)
plt.ylabel('score')
plt.ylim(0, 100)
plt.show()
피처 스케일링 (Feature Scaling)¶
X축 Y축 데이터 범위를 맞춤
import os # 경고 대응
os.environ['OMP_NUM_THREADS'] = '1'
# 아래의 KMeans() 구문에서 Warning 대응 하기 위한 코드 (StandardScaler 모듈 import 전에 넣어주면 해결)
# UserWarning: KMeans is known to have a memory leak on Windows with MKL,
# when there are less chunks than available threads.
# You can avoid it by setting the environment variable OMP_NUM_THREADS=1.
X[:5]
array([[ 7.33, 73. ],
[ 3.71, 55. ],
[ 3.43, 55. ],
[ 3.06, 89. ],
[ 3.33, 79. ]])
from sklearn.preprocessing import StandardScaler
sc = StandardScaler()
X = sc.fit_transform(X) # X 데이터 스케일링
X[:5]
array([[ 0.68729921, 0.73538376],
[-0.66687438, 0.04198891],
[-0.77161709, 0.04198891],
[-0.9100271 , 1.35173473],
[-0.8090252 , 0.96651537]])
데이터 시각화 (스케일링된 데이터)¶
plt.figure(figsize=(5, 5))
plt.scatter(X[:, 0], X[:, 1])
plt.title('Score by hours')
plt.xlabel('hours')
plt.ylabel('score')
plt.show()
최적의 K 값 찾기¶
1. 엘보우 방식 Elbow Method¶
- K 변화에 따른 중심점까지의 평균 거리 비교
- 경사가 완만해지는 지점의 K 선정 (최적의 K)
from sklearn.cluster import KMeans
from kneed import KneeLocator
inertia_list = []
for i in range(1, 11):
kmeans = KMeans(n_clusters=i, init='k-means++', random_state=0)
kmeans.fit(X)
inertia_list.append(kmeans.inertia_) # 각 지점으로부터 클러스터의 중심(centroid) 까지의 거리의 제곱의 합
# Kneedle 알고리즘을 사용해 엘보우 포인트 찾기
kneedle = KneeLocator(range(1, 11), inertia_list, curve="convex", direction="decreasing")
elbow_point = kneedle.elbow
print(f"Optimal number of clusters (Elbow Point): {elbow_point}")
# 결과 시각화
plt.plot(range(1, 11), inertia_list, marker='o')
plt.axvline(x=elbow_point, color='r', linestyle='--')
plt.xlabel('Number of Clusters (K)')
plt.ylabel('inertia_list')
plt.title('Elbow Method with Kneedle Algorithm')
plt.show()
Optimal number of clusters (Elbow Point): 4
2. 실루엣 분석 (Silhouette Analysis)¶
실루엣 분석은 클러스터링 품질을 평가하는 또 다른 방법으로
각 데이터 포인트가 자신의 클러스터에 얼마나 잘 맞는지를 나타내는 실루엣 계수(Silhouette Score) 를 사용합니다.
실루엣 점수는 -1에서 1 사이의 값을 가지며, 1에 가까울수록 군집이 잘 형성된 것으로 간주됩니다.
여러 개의 𝐾 값에 대해 클러스터링을 수행한 후, 각각의 클러스터링 결과에 대한 실루엣 점수를 계산합니다.
실루엣 점수가 가장 높은 𝐾 값을 최적의 𝐾 값으로 선택합니다.
from sklearn.metrics import silhouette_score
range_n_clusters = list(range(2, 11)) # 최소 2부터 시작 (K=1은 실루엣 분석 불가)
silhouette_scores = []
for k in range(2, 11): # 실루엣 분석은 최소 K=2부터 시작
kmeans = KMeans(n_clusters=k, random_state=0)
cluster_labels = kmeans.fit_predict(X)
score = silhouette_score(X, cluster_labels)
silhouette_scores.append(score)
# 최적의 K 값은 실루엣 점수가 가장 높은 K
optimal_k = range_n_clusters[silhouette_scores.index(max(silhouette_scores))]
print(f"Optimal number of clusters (K) based on silhouette score: {optimal_k}")
plt.plot(range_n_clusters, silhouette_scores, marker='o')
plt.xlabel('Number of Clusters (K)')
plt.ylabel('Silhouette Score')
plt.title('Silhouette Analysis for Optimal K')
plt.axvline(x=optimal_k, color='r', linestyle='--')
plt.show()
Optimal number of clusters (K) based on silhouette score: 4
3. DB Index (Davies-Bouldin Index)¶
DB Index는 클러스터 내 분산과 클러스터 간 거리의 비율을 사용하여 최적의 𝐾, K 값을 계산하는 방법입니다.
DB Index 값이 작을수록 군집이 잘 형성된 것으로 간주됩니다.
클러스터 내 데이터가 얼마나 뭉쳐져 있는지와, 클러스터 간 거리가 얼마나 먼지를 고려하여 DB Index를 계산합니다.
최소의 DB Index 값을 가지는 𝐾, K 값을 선택합니다.
from sklearn.metrics import davies_bouldin_score
from sklearn.cluster import KMeans
db_scores = []
for k in range(2, 11):
kmeans = KMeans(n_clusters=k, random_state=0)
labels = kmeans.fit_predict(X)
db_score = davies_bouldin_score(X, labels)
db_scores.append(db_score)
# 최적의 K 값은 가장 최소의 DB Index 값 K
optimal_k = db_scores.index(min(db_scores)) + 2
print(f"Optimal number of clusters: {optimal_k}")
plt.plot(range(2, len(db_scores) + 2), db_scores, marker='o')
plt.xlabel('Number of Clusters (K)')
plt.ylabel('db_scores Score')
plt.title('db_scores Analysis')
plt.axvline(x=optimal_k, color='r', linestyle='--')
plt.show()
Optimal number of clusters: 4
4. Calinski-Harabasz Index¶
Calinski-Harabasz Index는 클러스터 간 분산과 클러스터 내 분산의 비율을 통해 군집의 품질을 평가합니다.
높은 Calinski-Harabasz 점수를 얻는 𝐾 값이 최적의 𝐾 값으로 간주됩니다.
클러스터 간 거리(분산)가 크고, 클러스터 내 거리가 작을수록 점수가 높아집니다.
점수가 최대가 되는 𝐾 값을 최적의 𝐾로 선택합니다.
from sklearn.metrics import calinski_harabasz_score
ch_scores = []
for k in range(2, 11):
kmeans = KMeans(n_clusters=k, random_state=0)
labels = kmeans.fit_predict(X)
ch_score = calinski_harabasz_score(X, labels)
ch_scores.append(ch_score)
optimal_k = ch_scores.index(max(ch_scores)) + 2
print(f"Optimal number of clusters: {optimal_k}")
# 결과 시각화
plt.plot(range(2, len(ch_scores) + 2), ch_scores, marker='o')
plt.xlabel('Number of Clusters (K)')
plt.ylabel('ch_scores Score')
plt.title('ch_scores Analysis')
plt.axvline(x=optimal_k, color='r', linestyle='--')
plt.show()
Optimal number of clusters: 4
DB Index와 Calinski-Harabasz Index는 클러스터 품질을 평가하는 데 적합하여, 데이터 내에서 클러스터가 명확하게 나뉠 때 유리합니다.
최적의 K, optimal_k 값(4) 으로 KMeans 학습¶
K = optimal_k # 최적의 K 값
kmeans = KMeans(n_clusters=K, random_state=0) # KMeans 모델 생성
# kmeans.fit(X)
y_kmeans = kmeans.fit_predict(X) # 스케일랑 한 X 데이터 학슴
y_kmeans # X 데이터의 클러스터(군집) 확인
array([1, 0, 3, 0, 0, 2, 2, 0, 1, 0, 0, 3, 2, 3, 3, 0, 2, 1, 3, 0, 2, 0,
3, 2, 1, 1, 3, 3, 3, 3, 2, 2, 3, 0, 1, 1, 3, 0, 0, 0, 3, 2, 1, 3,
3, 1, 2, 0, 2, 2, 1, 0, 2, 2, 0, 0, 0, 0, 3, 2, 2, 1, 1, 1, 1, 2,
2, 0, 2, 1, 3, 1, 1, 1, 3, 3, 3, 3, 0, 1, 2, 1, 2, 2, 1, 0, 3, 2,
1, 3, 0, 2, 0, 1, 3, 0, 1, 0, 2, 3])
데이터 시각화¶
centers = kmeans.cluster_centers_ # 클러스터의 중심점 (centroid) 좌표
centers
array([[-0.54299598, 0.79316666],
[ 0.96910697, 0.97133061],
[ 0.8837666 , -1.26929779],
[-1.24939347, -0.48807293]])
for cluster in range(K): # K은 최적의 K 값 4
plt.scatter(X[y_kmeans == cluster, 0], X[y_kmeans == cluster, 1], s=100, edgecolor='black') # 각 클러스터에 속하는 X 데이터
plt.scatter(centers[cluster, 0], centers[cluster, 1], s=300, edgecolor='black', color='yellow', marker='s') # 클러스터 중심점 좌표
plt.text(centers[cluster, 0], centers[cluster, 1], cluster, va='center', ha='center') # 클러스터 텍스트 출력
plt.title('Score by hours')
plt.xlabel('hours')
plt.ylabel('score')
plt.show()
데이터 시각화 (스케일링 원복)¶
X_org = sc.inverse_transform(X) # Feature Scaling 된 X 데이터를 다시 원복
X_org[:5]
array([[ 7.33, 73. ],
[ 3.71, 55. ],
[ 3.43, 55. ],
[ 3.06, 89. ],
[ 3.33, 79. ]])
centers_org = sc.inverse_transform(centers) # Feature Scaling 된 중심점 좌표 데이터를 다시 원복
centers_org
array([[ 4.04115385, 74.5 ],
[ 8.08333333, 79.125 ],
[ 7.8552 , 20.96 ],
[ 2.1528 , 41.24 ]])
for cluster in range(K):
plt.scatter(X_org[y_kmeans == cluster, 0], X_org[y_kmeans == cluster, 1], s=100, edgecolor='black') # # 각 클러스터에 속하는 X 데이터
plt.scatter(centers_org[cluster, 0], centers_org[cluster, 1], s=300, edgecolor='black', color='yellow', marker='s') # 클러스터 중심점 좌표
plt.text(centers_org[cluster, 0], centers_org[cluster, 1], cluster, va='center', ha='center') # 클러스터 텍스트 출력
plt.title('Score by hours')
plt.xlabel('hours')
plt.ylabel('score')
plt.show()
K-means vs K-means++¶
K-means와 K-means++는 둘 다 데이터 클러스터링에 사용되는 알고리즘이지만
초기 중심점 (centroid) 설정 방식에서 차이가 있어 성능 차이가 발생합니다.
- K-means¶
- K-means 작동 방식:
- K-means 알고리즘은 다음과 같은 단계를 거칩니다.
- 임의로 K개의 중심점을 초기화합니다.
- 각 데이터 포인트를 가장 가까운 중심점에 할당하여 클러스터를 형성합니다.
- 각 클러스터의 평균값을 계산해 새로운 중심점으로 설정합니다.
- 새로운 중심점으로 다시 데이터 포인트를 할당하고, 클러스터가 더 이상 변하지 않을 때까지 2-3단계를 반복합니다.
- 단점: 초기 중심점이 무작위로 선택되기 때문에, 초기화에 따라 성능이 크게 달라질 수 있습니다.
잘못된 초기화는 최적이 아닌 로컬 최적해에 수렴하거나 더 많은 반복을 요구하여 속도가 느려질 수 있습니다.
- K-means 작동 방식:
- K-means++¶
- 개선된 초기화 방법:
- K-means++는 초기 중심점을 선택할 때 더 스마트한 방식을 사용하여 성능을 개선합니다.
- 첫 번째 중심점을 무작위로 선택합니다.
- 나머지 중심점들은 기존 중심점과의 거리에 비례하여 선택됩니다. 즉, 거리가 먼 데이터 포인트일수록 중심점으로 선택될 확률이 높아집니다.
- 위 과정을 통해 K개의 중심점을 모두 선택하고, 이후 K-means와 동일한 방식으로 클러스터링을 진행합니다.
- 장점: 초기화 단계에서 데이터 포인트들이 고르게 분포된 중심점을 선택하기 때문에, K-means보다 더 빠르게 수렴하고 안정적인 결과를 얻을 수 있습니다. 이는 클러스터링의 정확도와 효율성을 향상시킵니다.
- 개선된 초기화 방법:
요약¶
- K-means: 무작위로 초기 중심점을 설정하여 클러스터링 진행. 초기값에 따라 성능 차이가 발생할 수 있음.
- K-means++: 중심점 간의 거리를 고려해 초기 중심점을 설정함으로써, 더 빠른 수렴과 더 나은 성능을 제공.
따라서, K-means++는 K-means의 초기화 문제를 해결하기 위한 방법으로, 일반적으로 더 나은 결과를 얻기 위해 K-means++가 널리 사용됩니다.
'Python > SikitLearn' 카테고리의 다른 글
04. (지도학습)_Logistic Regression(로지스틱 회귀) (0) | 2024.11.23 |
---|---|
03. (지도학습)_Polynomial Regression(다항 회귀) (0) | 2024.11.23 |
02. (지도학습)_Multiple Linear Regression(다중 선형 회귀) (0) | 2024.11.23 |
01. (지도학습)_Linear Regression(선형 회귀) (0) | 2024.11.23 |