TIL (Today I Learn)

TIL [내배캠] 41일차

dataguard 2025. 4. 15. 20:54
군집 분석 핸들링
이전 기초 프로젝트에서 K-means 군집화를 써보려다가
무지개떡 처럼 분포가 형성되어서, 당황한 적이 있었다.
이번에 배우는 김에 어떻게 사용하는지 정도는 익혀두기 위해서 연습을 좀 했다.

 

군집화의 경우에는 다른 sklearn 패키지들과는 다르게, 훈련용과 테스트용을 나누지 않아도 된다.

또 이상치에 덜 민감하여, 데이터 정제에 시간을 덜 쓸수 있다는 장점도 있다.

하지만 지도학습과 달리, 정해진 답이 없기 때문에 분석가가 만들어내는 것이 곧 답이 된다.

때문에 설득하는 과정또한 탄탄해야하며, 모든 실행과정에 이유와 방향성이 존재해야 한다.

방망이 깎는 노인처럼 데이터를 집중해서 깎아서, 설명하기 좋은 형태로 나눠야한다.

지금은 테스트용 데이터만 다루고 있기 때문에, 목적과 방향성이 없는 군집분석이다.

하지만, 도구를 다룰 줄 알아야 데이터를 깎아낼 수 있기 때문에 연습해보려한다.

 

 

  1. 데이터는 sklearn 패키지의 와인 데이터를 사용했다.
    from sklearn.datasets import load_wine
    wine = load_wine()
    X = wine.data
    y = wine.target  # 실제 레이블(군집 평가엔 직접 사용 안 함)

     
  2. K-means와 DBSCAN 그리고 계층적 클러스터링을 사용하여 군집화를 시행한다.
    kmeans = KMeans(n_clusters=3, random_state=42)
    kmeans_labels = kmeans.fit_predict(X)
    
    dbscan = DBSCAN(eps=50, min_samples=5)
    dbscan_labels = dbscan.fit_predict(X)
    
    agg = AgglomerativeClustering(n_clusters=3)
    agg_labels = agg.fit_predict(X)
  3. 실루엣 계수와 Davies bounldin index를 사용하여, 군집화 성능을 평가한다.
    kmeans_sil = silhouette_score(X, kmeans_labels)
    dbscan_sil = silhouette_score(X, dbscan_labels)
    agg_sil = silhouette_score(X, agg_labels)
    
    kmeans_davies = davies_bouldin_score(X, kmeans_labels)
    dbscan_davies = davies_bouldin_score(X, dbscan_labels)
    agg_davies = davies_bouldin_score(X, agg_labels)
    
    print("=== 군집 결과 비교 ===")
    print("K-Means: 실루엣 점수 =", kmeans_sil, "| Davies Bouldin =", kmeans_davies)
    print("DBSCAN: 실루엣 점수 =", dbscan_sil,  "| Davies Bouldin =", dbscan_davies)
    print("Agglomerative: 실루엣 점수 =", agg_sil, "| Davies Bouldin =", agg_davies)
    === 군집 결과 비교 ===
    K-Means: 실루엣 점수 = 0.5595823478987213 | Davies Bouldin = 0.5495575974642909
    DBSCAN: 실루엣 점수 = 0.5545832841751992 | Davies Bouldin = 0.7478038408536398
    Agglomerative: 실루엣 점수 = 0.5644796401732068 | Davies Bouldin = 0.5357343073560253​
  4. 시각화를 위해 PCA를 통해 차원을 축소 시킨다.
    pca = PCA(n_components=2)
    X_pca = pca.fit_transform(X)
  5. 다중 그래프로 시각화를 진행한다.
    fig, axes = plt.subplots(1, 3, figsize=(15, 4))
    
    # K-Means 시각화
    axes[0].scatter(X_pca[:, 0], X_pca[:, 1], c=kmeans_labels)
    axes[0].set_title("K-Means")
    
    # DBSCAN 시각화
    axes[1].scatter(X_pca[:, 0], X_pca[:, 1], c=dbscan_labels)
    axes[1].set_title("DBSCAN")
    
    # Agglomerative 시각화
    axes[2].scatter(X_pca[:, 0], X_pca[:, 1], c=agg_labels)
    axes[2].set_title("Agglomerative")
    
    plt.tight_layout()
    plt.show()

 

 

 

 

 

사실, 다른 군집화 같은 경우에는 내가 나눌 수 있는 파라미터가 많지 않아서 활용이 제한적이었다.
하지만, DBSCAN 같은 경우에는 그래도 내가 이해하는 선에서 조절을 할 수 있었다.

 

 

  • 초기의 DBSCAN 그래프는 eps와 최소표본 갯수가 적절하지 않아서 군집화가 전혀 되지 않았다.
    dbscan = DBSCAN(eps=0.5, min_samples=5)
    dbscan_labels = dbscan.fit_predict(X)
    
    pca = PCA(n_components=2)
    X_pca = pca.fit_transform(X)
    
    plt.scatter(X_pca[:, 0], X_pca[:, 1], c=dbscan_labels)
    plt.title("DBSCAN")
    plt.tight_layout()
    plt.show()
  • 어떻게는 군집을 나눠보기 위해 최소표본 갯수를 1로 해보았다.
    dbscan = DBSCAN(eps=0.5, min_samples=1)
    dbscan_labels = dbscan.fit_predict(X)
    
    pca = PCA(n_components=2)
    X_pca = pca.fit_transform(X)
    
    plt.scatter(X_pca[:, 0], X_pca[:, 1], c=dbscan_labels)
    plt.title("DBSCAN")
    plt.tight_layout()
    plt.show()
  • 데이터 간의 간격이 꽤나 넓다는 것을 생각하고, eps를 크게 높여보기로 했다.
    dbscan = DBSCAN(eps=30, min_samples=2)
    dbscan_labels = dbscan.fit_predict(X)
    
    dbscan_sil = silhouette_score(X, dbscan_labels)
    print("실루엣 계수:" , dbscan_sil)
    
    pca = PCA(n_components=2)
    X_pca = pca.fit_transform(X)
    
    plt.scatter(X_pca[:, 0], X_pca[:, 1], c=dbscan_labels)
    plt.title("DBSCAN")
    plt.tight_layout()
    plt.show()
    실루엣 계수: 0.42084043435603075


  • 샘플 값을 조금 더 조절해보았다.
    dbscan = DBSCAN(eps=50, min_samples=5)
    dbscan_labels = dbscan.fit_predict(X)
    
    dbscan_sil = silhouette_score(X, dbscan_labels)
    print("실루엣 계수:" , dbscan_sil)
    
    pca = PCA(n_components=2)
    X_pca = pca.fit_transform(X)
    
    plt.scatter(X_pca[:, 0], X_pca[:, 1], c=dbscan_labels)
    plt.title("DBSCAN")
    plt.tight_layout()
    plt.show()
    실루엣 계수: 0.5545832841751992
    

아직은 핸들링 실력이 부족해서 이정도 이긴 하지만, 파라미터를 통해 나눠지는 것을 보니 꽤나 흥미롭다.

내일은 다른 것을 연습해야겠지만, 군집분석도 나름 재미있을 것 같다.

 

 

 

'TIL (Today I Learn)' 카테고리의 다른 글

TIL [내배캠] 44일차  (0) 2025.04.18
TIL [내배캠] 43일차  (0) 2025.04.17
TIL [내배캠] 40일차  (0) 2025.04.14
WIL [내배캠] 9주차  (0) 2025.04.11
TIL [내배캠] 39일차  (0) 2025.04.11