지금까지는 텍스트(고차원 희소 벡터)를 그대로 클러스터링했다.
[STUDY/ML(Machine Learning)] - [ML] K-means clustering
[ML] K-means clustering
1. K-means clustering?데이터를 $K$개의 그룹(군집)으로 자동으로 나눠주는 비지도학습 알고리즘각 군집을 대표하는 중심점(centroid)을 그 군집에 속한 데이터들의 평균(mean) 위치로 잡아가면서 군집을
blog.chaenii.me
[STUDY/ML(Machine Learning)] - [ML] 실루엣 스코어(Silhouette Score) - 군집화 평가 지표
[ML] 실루엣 스코어(Silhouette Score) - 군집화 평가 지표
1. Silhouette Score?[STUDY/ML(Machine Learning)] - [ML] K-means clustering [ML] K-means clustering1. K-means clustering?데이터를 $K$개의 그룹(군집)으로 자동으로 나눠주는 비지도학습 알고리즘각 군집을 대표하는 중심점(c
blog.chaenii.me
신경망 기반 클러스터링(SOM)으로 넘어가기 전에,
고차원 데이터를 다루기 쉬운 저차원으로 줄이는 SVD/차원축소를 먼저 알아야 한다.
SVD(특이값분해)?
KMeans, 임베딩 같은 글에서 데이터를 계속 "벡터"와 "행렬"로 다루는데,
행렬을 다루다 보면 "이 복잡한 행렬을 더 단순한 형태로 쪼개고 싶다"는 상황이 자주 온다.
그게 행렬분해(matrix decomposition)이다.
그리고 SVD(Singular Value Decomposition, 특이값분해)는 그중 가장 널리 쓰이는 방법이다.
SVD/차원축소에서 이걸 어떻게 응용하는지 다루기 전에, SVD 자체가 뭘 하는 건지부터 짚고 간다.
행렬 - 공간을 바꾸는 변환
벡터에 행렬을 곱하면 그 벡터는 다른 위치로 움직인다.
쉽게 예를 들어보자.
평면 위의 점 하나에 어떤 2×2 행렬을 곱하면 그 점은 회전하거나 늘어나거나 찌그러진 위치로 이동한다.
이런 식으로 평면 전체(원 모양으로 늘어선 점들이라고 해보자)에 같은 행렬을 곱하면
원은 보통 한쪽으로 길쭉하게 늘어난 타원이 되고, 방향도 돌아간다.

즉 행렬 = 공간을 회전시키거나 늘리거나 줄이는 변환이라고 보면 된다.
SVD는 "이 변환이 정확히 어떤 회전과 어떤 늘이기로 이루어져 있는지"를 분해해서 보여주는 도구다.
SVD의 핵심 아이디어: 회전 → 확대 → 회전
아무리 복잡해 보이는 행렬 변환이라도, 항상 다음 세 단계로 쪼갤 수 있다:
$$
X = U \Sigma V^T
$$
- $V^T$ (회전): 먼저 입력 공간을 한 번 회전시켜 "가장 많이 늘어나는 방향"이 좌표축에 딱 맞게 정렬되도록 만든다.
- $\Sigma$ (확대/축소)
- 그렇게 정렬된 축들을 따라 각각 다른 비율로 늘리거나 줄인다.
- 이 비율들이 대각선에 놓이는데, 이게 바로 특이값(singular value)이다. (큰 것부터 순서대로 나열됨)
- $U$ (다시 회전): 마지막으로 결과를 다시 한 번 회전시켜서 최종 위치로 옮긴다.
비유하면: 종이를 한 번 돌리고, 가로세로 비율을 다르게 늘렸다가, 다시 한 번 돌리는 것.
이 세 동작만으로 어떤 모양의 변형이든 표현할 수 있다는 게 SVD의 핵심 주장이다.
계산
대칭행렬(transpose해도 자기 자신과 같은 행렬)을 예로 들어보자.
$$
A = \begin{pmatrix} 3 & 1 \\ 1 & 3 \end{pmatrix}
$$
이 행렬의 고유값(eigenvalue)을 구하면 $\lambda = 4$ 또는 $\lambda = 2$가 나오고(특성방정식 $(3-\lambda)^2 - 1 = 0$을 풀면 됨),
각 고유값에 대응하는 고유벡터를 정규화하면:
$$
Q = \begin{pmatrix} \frac{1}{\sqrt2} & \frac{1}{\sqrt2} \\ \frac{1}{\sqrt2} & -\frac{1}{\sqrt2} \end{pmatrix}, \qquad \Sigma = \begin{pmatrix} 4 & 0 \\ 0 & 2 \end{pmatrix}
$$
대칭행렬인 경우 $U = V = Q$가 되어서, $A = Q \Sigma Q^T$로 분해된다.
실제로 $Q \Sigma Q^T$를 계산해보면 정확히 원래 $A$로 돌아온다.
즉 "45도 방향으로는 4배 늘리고, 그 수직 방향으로는 2배 늘리는 변환"이 바로 행렬 $A$의 정체였다는 뜻.
주의: 이 예시는 대칭행렬이라서 $U=V$로 단순해졌을 뿐, 일반적인 행렬(특히 정사각형이 아닌 행렬)에서는 $U$와 $V$가 서로 다른 행렬이다.
특이값이 크다?
특이값은 "그 방향으로 얼마나 많이 늘어나는가"를 나타낸다.
- 특이값이 큰 방향: 원본 데이터에서 변동이 가장 큰(=가장 중요한 정보를 담은) 방향
- 특이값이 작은 방향: 거의 안 늘어나는, 미세한 노이즈에 가까운 방향
이 성질 때문에 "특이값이 작은 방향은 버려도 정보 손실이 적다"는 아이디어가 나오고,
이게 바로 SVD를 이용한 차원축소의 출발점이다.
고유값분해와의 차이
- 대칭행렬의 고유값분해(eigendecomposition) - 정사각 행렬에서만 가능
- 반면 SVD - 행과 열의 개수가 달라도(m \times n, m \ne n이어도) 항상 존재
→ TF-IDF처럼 보통 정사각형이 아닌(문서 수 × 단어 종류 수) 행렬에도 SVD는 바로 적용할 수 있다.
→ 머신러닝 파이프라인에서 고유값분해 대신 SVD를 쓰는 실용적인 이유.
전체 SVD와 얇은 SVD
수식으로는 $X = U\Sigma V^T$라고 쓰지만,
실전에서는 전체 $U$, 전체 $V$를 모두 만들지 않는 경우가 많다.
특히 문서-단어 행렬처럼 행과 열이 큰 직사각형 행렬에서는 필요한 상위 성분만 계산하는 thin SVD 또는 truncated SVD 를 쓴다.
개념은 같지만, 메모리와 속도 때문에 "필요한 방향만 뽑아 쓴다"고 보면 된다.
SVD 정리
- 행렬은 "공간을 회전·확대·축소하는 변환"으로 볼 수 있다
- SVD는 어떤 행렬이든 "회전($V^T$) → 축별로 다르게 늘이기($\Sigma$) → 다시 회전($U$)"의 세 단계로 쪼개는 분해
- 특이값(대각선 값)은 각 방향으로 얼마나 늘어나는지를 나타내고, 클수록 더 중요한 변동 방향
- 정사각 행렬에만 되는 고유값분해와 달리, SVD는 어떤 모양의 행렬에도 항상 존재한다
SVD가 뭘 하는지 알았다면, 이제 이걸 실제로 어떻게 써먹는지,
즉 고차원 데이터를 저차원으로 압축하는 데 어떻게 쓰이는지 알아보자.
문제 상황
TF-IDF로 텍스트를 벡터화하면 차원이 수천~수만이 된다 (단어 종류만큼). 이런 고차원 희소(sparse) 벡터는:
- 계산이 무겁다: 차원이 클수록 거리 계산, 행렬 연산이 느려짐
- 일부 알고리즘은 희소행렬을 직접 못 다룬다: 예를 들어 SOM은 내부적으로 dense(밀집) 벡터 연산을 전제로 짜여 있어서, 수천 차원 희소행렬을 그대로 넣으면 메모리·속도 문제가 생긴다
- "차원의 저주(curse of dimensionality)": 차원이 높아질수록 거리 개념 자체가 무의미해지는 경향이 있다 (모든 점이 서로 비슷하게 멀어짐)
해결책: 정보를 최대한 보존하면서 차원을 확 줄인다.
SVD의 핵심 아이디어
임의의 행렬 $X$ (크기 $m \times n$)는 다음과 같이 분해된다:
$$
X = U \Sigma V^T
$$
- $U$: $m \times m$ 직교행렬 (좌측 특이벡터)
- $\Sigma$: $m \times n$ 대각행렬, 대각선에 특이값(singular value)들이 큰 것부터 순서대로 나열
- $V^T$: $n \times n$ 직교행렬 (우측 특이벡터)
→ 핵심: 특이값이 큰 순서대로 정렬되어 있다.
특이값이 클수록 그 방향(성분)이 원본 데이터의 "주요한 변동"을 더 많이 설명한다.
작은 특이값에 대응하는 방향은 노이즈에 가까운 미세한 변동일 가능성이 높다.
TruncatedSVD: 상위 k개만 남기기
전체 $\Sigma$를 다 쓰지 않고, 가장 큰 특이값 $k$개만 남기고 나머지를 버린다:
$$
X \approx U_k \Sigma_k V_k^T
$$
이러면 원본을 $n$차원에서 $k$차원으로 압축한 근사치를 얻는다.
$k$를 원본보다 훨씬 작게 잡으면(예: 8000차원 → 100차원) 정보 손실은 있지만 대부분의 "중요한 변동"은 보존된 채로 차원이 확 줄어든다.
이게 "최선의" 근사라는 보장이 있는가?
에카르트-영 정리(Eckart–Young theorem)에 의하면, 상위 $k$개의 특이값만으로 만든 $U_k\Sigma_k V_k^T$는 원본 행렬 $X$와의 차이(프로베니우스 노름 기준 재구성 오차)를 최소화하는, 랭크(rank)가 $k$인 행렬 중에서 가장 가까운 행렬이다:
$$
U_k\Sigma_k V_k^T = \arg\min_{\text{rank}(B)=k} \|X - B\|_F
$$
즉 "차원을 $k$개로 줄인다"는 제약 안에서 임의로 잘 줄인 게 아니라, 수학적으로 증명 가능한 최적해라는 뜻이다.
KMeans처럼 반복해서 수렴시키는 방식이 아니라, 한 번의 행렬분해로 바로 최적해가 나오는 "닫힌 형태(closed-form) 해"라는 점이 KMeans와의 중요한 차이다.
scikit-learn의 TruncatedSVD(n_components=k)가 이 작업을 한다. PCA(주성분분석)와 매우 비슷한 개념인데, PCA는 데이터를 먼저 평균중심화(centering)하는 반면 TruncatedSVD는 희소행렬에 그대로 적용 가능하다는 실전적 차이가 있다 (평균중심화를 하면 희소행렬이 dense가 되어버려서 메모리 이점이 사라짐).
정보 손실의 트레이드오프
$k$를 어떻게 잡느냐에 따라 트레이드오프가 갈린다:
- $k$를 작게 잡으면: 계산은 빨라지지만 정보 손실이 커짐
- $k$를 크게 잡으면: 정보는 더 보존되지만 차원축소의 이점이 줄어듦
"얼마나 정보를 보존하는가"는 특이값의 제곱이 전체 분산에서 차지하는 비율로 정량화할 수 있다
$i$번째 특이값을 $\sigma_i$라 하면, 상위 $k$개를 썼을 때의 누적 비율은:
$$\frac{\sum_{i=1}^{k} \sigma_i^2}{\sum_{i=1}^{r} \sigma_i^2}$$
PCA에서는 이 값을 보통 누적 분산 설명비율이라고 부르고, "원본 분산의 몇 %를 보존했는가"로 해석한다.
단, `TruncatedSVD`는 PCA와 달리 데이터를 평균중심화(centering)하지 않는다.
그래서 TF-IDF 같은 희소 행렬에 바로 적용할 수 있지만,
이 비율을 PCA에서처럼 완전히 같은 의미의 "분산 설명비율"로 해석하면 안 된다.
실전에서는 이 값을 원본 행렬의 주요 구조/에너지를 얼마나 담았는지 보는 참고 지표로 쓴다.
예를 들어 0.9 근처에서 곡선이 완만해진다면 "상위 성분들이 대부분의 큰 구조를 담고 있다"고 보고,
이후 단계(SOM/KMeans)의 계산 비용과 성능을 함께 보면서 $k$를 정한다
e.g: "8000차원 TF-IDF 벡터를 100차원으로 줄인다"는 결정도 이런 과정을 거쳐 나온 값이다.


SVD의 한계
SVD는 데이터의 선형 구조만 포착한다. 행렬 분해 자체가 선형 연산이기 때문이다.
데이터가 나선형·원형·S자 같은 비선형 구조로 분포해 있다면, SVD로 차원을 줄여도 그 구조가 보존되지 않는다.
2차원으로 줄였더니 원래는 분리돼 있던 군집들이 섞여버리는 경우가 생길 수 있다.
이런 경우를 위한 비선형 차원축소 방법들이 있다:
- t-SNE(t-distributed Stochastic Neighbor Embedding)
- 가까운 점들끼리의 거리 관계를 보존하면서 저차원으로 압축.
- 비선형 구조를 잘 잡아서 시각화(2~3차원) 목적으로 주로 쓰인다.
- 단, 축소된 공간의 전역적 거리 관계는 보존되지 않고, 랜덤성이 있어서 실행마다 결과가 달라질 수 있다.
- UMAP(Uniform Manifold Approximation and Projection)
- t-SNE보다 빠르고 전역 구조도 어느 정도 보존.
- 마찬가지로 시각화와 탐색 목적으로 쓰인다.
중요한 차이: t-SNE/UMAP은 학습(훈련)에 쓰기보다 탐색·시각화 도구에 가깝다. SVD와 달리 새로운 데이터 포인트를 기존 학습 공간에 투영하는 게 어렵고(out-of-sample 문제), 결과가 확정적이지 않아서 모델 입력으로 쓰기엔 불안정하다. 반면 SVD/PCA는 학습 후 새 데이터를 동일하게 변환할 수 있어서 ML 파이프라인에 안정적으로 붙일 수 있다.
| SVD / PCA | t-SNE / UMAP | |
| 구조 | 선형 | 비선형 |
| 새 데이터 투영 | ✓ 안정적 | △ 어렵거나 불안정 |
| 주요 용도 | ML 파이프라인 전처리 | 데이터 탐색·시각화 |
| 속도 | 빠름 | 느림 (t-SNE), 중간 (UMAP) |
왜 TF-IDF 다음에, SOM 전에 위치하는가
파이프라인 순서:
텍스트 → TF-IDF (수천차원 희소행렬) → TruncatedSVD (100차원 dense) → SOM 입력
KMeans는 희소행렬을 그대로 받아도 잘 작동하지만(scikit-learn의 KMeans는 sparse 입력을 지원),
SOM 라이브러리(MiniSom 등)는 dense 입력을 전제로 짜여 있는 경우가 많아서 SVD로 차원을 줄이고 dense로 변환하는 전처리가 필요하다.
정리
- SVD는 고차원 데이터를 정보 손실을 최소화하면서 저차원으로 압축하는 행렬분해 기법
- TruncatedSVD로 상위 $k$개 특이값만 남기면, 에카르트-영 정리에 의해 그게 랭크 $k$ 근사 중 수학적으로 최적
- $k$는 누적 분산 설명비율이나 스크리 플롯을 보고 경험적으로 정함
- TF-IDF(희소·고차원) → SVD(압축) → SOM(밀집 입력 필요) 파이프라인의 중간 단계 역할
GitHub 댓글