문서 클러스터링
클러스터링
클러스터링은 데이터를 유사한 특성을 가진 그룹으로 묶는 방법이다. 이는 개별 데이터 간의 유사도를 일일이 비교하는 대신, 미리 데이터를 그룹화하는 기법이다.
'클러스터'라는 용어는 '모여 있는 것'을 의미하며, 한국어로는 '군집'이라고 번역된다. 이 개념은 다양한 분야에서 사용되는데, 예를 들어 '산업 클러스터'는 유사한 산업들이 모여 있는 산업 단지를 의미한다.
클러스터링은 비지도 학습의 한 종류다. 이는 데이터를 어떻게 그룹화할지에 대한 명확한 정답이 없기 때문이다. 같은 데이터 셋이라도 다양한 방식으로 클러스터링될 수 있으며, 사용하는 방법과 기준에 따라 결과가 달라질 수 있다.
이 방법은 특히 대량의 데이터를 처리할 때 유용하다. 개별 데이터 간의 유사도를 일일이 계산하는 대신, 비슷한 특성을 가진 데이터들을 미리 그룹화함으로써 분석 과정을 효 율적으로 만들 수 있다.
클러스터링의 종류
클러스터링 알고리즘은 크게 두 종류로 나눌 수 있다. 파티션 알고리즘과 위계적 알고리즘이다.
파티션 알고리즘은 데이터를 수평적으로 나누는 방식이다. 이는 데이터 공간에 경계선을 그어 각 영역을 구분하는 방식으로 작동한다. 예를 들어, 전체 데이터를 빨간색, 초록색, 파란색 그룹으로 나누는 식이다.
반면 위계적 알고리즘은 데이터를 다단계로 나누는 방식이다. 이는 비슷한 데이터끼리 점진적으로 모으거나 나누는 과정을 반복하며, 트리 구조와 같은 계층적 구조를 형성한다.
일반적으로 파티션 알고리즘이 위계적 알고리즘보다 더 많이 사용된다. 그러나 위계적 알고리즘도 특정 단계에서 멈추면 파티션 알고리즘과 유사한 결과를 얻을 수 있다.
K-Means
K-Means는 클러스터링에서 가장 대표적인 알고리즘이다. 특별한 요구사항이 없다면 보통 K-Means를 먼저 시도해본다.
이 알고리즘의 이름에서 'K'는 단순히 '몇 개'를 의미한다. 알 수 없는 값을 X로 표현하는 것처럼, 개수를 나타낼 때는 주로 K를 사용한다. 따라서 K-Means는 '몇 개의 평균들'이라는 뜻으로 볼 수 있다.
K-Means 알고리즘의 핵심은 군집의 평균점, 즉 중심점을 찾는 것이다. 군집을 만드는 방법에는 여러 가지가 있는데, K-Means는 경계선을 그리는 대신 중심점을 기준으로 군집을 형성한다. 즉, 각 데이터 포인트를 가장 가까운 중심점에 할당하여 클러스터를 만든다.
이 방법은 각 클러스터의 중심을 평균으로 잡고, 그 중심에 가까운 데이터 포인트들을 하나의 클러스터로 묶는 방식으로 작동한다. 이러한 접근 방식은 직관적이며, 많은 경우에 효과적인 클러스터링 결과를 제공한다.
K-Means 알고리즘은 클러스터의 중심을 기준으로 데이터를 그룹화하기 때문에 특정한 특성을 가진 클러스터 형태가 만들어진다.
다른 클러스터링 방법들은 복잡한 형태의 경계선을 만들 수 있다. 예를 들어, 구불구불한 형태나 불규칙한 모양의 클러스터를 만들 수 있다. 하지만 K-Means는 이런 복잡한 경계를 만들지 않는다.
K-Means에서는 항상 중심점을 기준으로 가까운 데이터들을 모으기 때문에, 클러스터의 경계가 기본적으로 볼록한 형태를 띄게 된다.
예를 들어, 클러스터의 평균점이 어떤 위치에 있다면, 그 주변으로 움푹 들어간 형태의 경계는 만들어지기 어렵다. 왜냐하면 움푹 들어간 부분의 데이터들도 중심점에 가까우므로 같은 클러스터에 속하게 되기 때문이다.
만약 두 개의 클러스터가 서로 인접해 있다면, 그 경계는 직선 형태가 될 수 있다. 이는 두 중심점으로부터 같은 거리에 있는 지점들이 직선을 이루기 때문이다.
Normalization
K-Means(K-means) 알고리즘은 기본적으로 유클리드 거리를 사용한다. 그러나 문서나 단어의 유사도를 계산할 때는 주로 코사인 유사도를 사용하기 때문에, 일반적인 K-Means 알고리즘은 텍스트 데이터에 적합하지 않다.
이러한 문제를 해결하기 위해 스페리컬 K-Means(Spherical K-means)라는 방법이 있다. 이 방법은 K-Means 알고리즘에서 유클리드 거리 대신 코사인 유사도를 사용하도록 변형한 것이다. 하지만 불행히도 사이킷런(scikit-learn) 라이브러리에는 이 방법이 구현되어 있지 않다.
대신, 비슷한 효과를 얻을 수 있는 방법으로 L2 정규화(normalization)를 사용할 수 있다. L2 정규화는 데이터를 일정한 거리로 변환해주는 방법이다. 이 과정을 거치면 모든 데이터 포인트가 구면 위에 위치하게 된다. 멀리 있는 데이터는 가까워지고, 가까이 있는 데이터는 멀어지는 식으로 조정된다.
L2 정규화를 적용한 후 유클리드 거리로 K-Means를 수행하면, 코사인 유사도로 K-Means를 수행한 것과 유사한 결과를 얻을 수 있다. 완전히 동일하지는 않지만, 상당히 근사한 결과를 제공한다.
이 방법을 구현하기 위해 사이킷런에서 제공하는 normalize 함수를 사용할 수 있다. 이를 통해 텍스트 데이터에 대해서도 K-Means 알고리즘을 효과적으로 적용할 수 있다.
from sklearn.preprocessing import normalize
norm_doc_emb = normalize(doc_emb, 'l2')
문서들을 9개의 클러스터로 나눔
from sklearn.cluster import KMeans
km = KMeans(n_clusters=9, random_state=1234)
km.fit(norm_doc_emb)
클러스터 번호 얻기
cluster = km.predict(norm_doc_emb)
i번째 문서의 클러스터 번호
cluster_idx = cluster[i]
클러스터 번호가 같은 문서
df[cluster == cluster_idx]
클러스터 개수 정하기
클러스터의 개수를 정하는 것은 쉽지 않은 문제다. 실제 데이터는 깔끔하게 나누어지지 않고 서로 연결되어 있는 경우가 많아 정확한 클러스터 수를 결정하기 어렵다.
그러나 이를 결정하는 데 도움을 주는 몇 가지 지표가 있다. 그 중 두 가지는 관성(inertia)과 실루엣 점수다.
관성
관성은 각 데이터 포인트와 그 포인트가 속한 클러스터의 중심점 사이의 거리를 나타낸다. 클러스터링이 잘 되었다면 데이터 포인트들이 중심점 주변에 밀집해 있어 관성이 작아진다. 반대로 데이터 포인트들이 중심점에서 멀리 흩어져 있다면 관성이 커진다.
클러스터의 수를 늘리면 관성은 계속 감소하는 경향이 있다. 예를 들 어, 3개의 클러스터를 2개로 줄이면 관성이 커지고, 3개에서 4개로 늘리면 관성이 줄어든다. 그러나 클러스터 수를 계속 늘려도 어느 순간부터는 관성이 크게 감소하지 않는다.
이를 시각화하면 LSA에서 본 것과 유사한 스크리 플롯이 만들어진다. 클러스터 수를 늘릴 때 관성이 급격히 감소하다가 어느 지점부터 완만해지는데, 이 '꺾이는 지점'을 최적의 클러스터 수로 선택하는 경우가 많다.
inertia = []
ks = np.arange(1, 20)
for k in ks:
km = KMeans(n_clusters=k, random_state=1234)
km.fit(norm_doc_emb)
inertia.append(km.inertia_)
import matplotlib.pyplot as plt
plt.plot(ks, inertia)
plt.xticks(ks);
실루엣 계수
실루엣 계수는 클러스터링의 품질을 평가하는 또 다른 방법이다. 이는 관성과는 달리 클러스터의 중심이 아닌, 각 데이터 포인트와 다른 데이터 포인트들 간의 거리를 기반으로 한다.
실루엣 계수를 계산할 때는 두 가지 거리를 고려한다. 첫째는 데이터 포인트가 속한 클러스터 내에서 가장 가까운 다른 포인트와의 거리이고, 둘째는 그 데이터 포인트와 다른 클러스터에 있는 가장 가까운 포인트와의 거리이다.
예를 들어, 두 개의 클러스터가 있다고 가정해보자. 한 데이터 포인트에 대해, 자신이 속한 클러스터 내에서 가장 가까운 점과의 거리, 그리고 다른 클러스터에서 가장 가까운 점과의 거리를 비교한다. 자신의 클러스터 내 점과의 거리가 다른 클러스터의 점과의 거리보다 훨씬 가까우면, 이 데이터 포인트는 잘 클러스터링된 것으로 볼 수 있다.
실루엣 계수는 특히 클러스터 간 경계에 있는 데이터 포인트들에 대해 중요하다. 클러스터 내부에 깊숙이 위치한 포인트들은 대체로 높은 실루엣 점수를 가지게 되지만, 경계 부근의 포인트들은 클러스터링의 품질을 더 잘 반영한다. 예를 들어, 두 클러스터 사이의 경계 근처에 있는 데이터 포인트는 어느 클러스터에 속해야 할지 불분명할 수 있으며, 이는 낮은 실루엣 점수로 나타날 것이다.
from sklearn.metrics import silhouette_samples
clusterer = KMeans(n_clusters=9, random_state=10)
cluster_labels = clusterer.fit_predict(norm_doc_emb)
silhouette = silhouette_samples(norm_doc_emb, cluster_labels)
sil = pd.DataFrame({
'cluster': cluster_labels,
'silhouette': silhouette,
})
sil = sil.sort_values(by=['cluster', 'silhouette'], ascending=False)
시각화
n = len(sil)
plt.plot(range(n), sil.silhouette)
avg = silhouette.mean()
plt.plot([0, n], [avg, avg], '--') # 평균선
각각의 클러스터가 봉우리로 표현된다. 여기서 각 봉우리의 폭과 높이가 비슷한 것이 바람직하다.