7.6 SVD·PCA·저랭크 근사

실습 · 연습문제 2개

최적 저랭크 근사 low-rank-approx

Eckart–Young 정리에 의해, 행렬 A\mathbf{A}의 최적 rank-kk 근사는 SVD의 앞 kk개 항만 남긴

Ak=∑i=1kσi uivi⊤\mathbf{A}_k = \sum_{i=1}^k \sigma_i\,\mathbf{u}_i\mathbf{v}_i^\top

이고, 그 Frobenius 오차는 ∥A−Ak∥F=∑i>kσi2\lVert\mathbf{A} - \mathbf{A}_k\rVert_F = \sqrt{\sum_{i > k}\sigma_i^2}이다.

함수 low_rank_approx(A, k)를 완성해 (A_k, err)를 반환한다.

반환 shape 내용
A_k A와 같음 최적 rank-kk 근사
err 스칼라 버린 특이값으로 계산한 Frobenius 오차 ∑i>kσi2\sqrt{\sum_{i>k}\sigma_i^2}

k가 0이면 영행렬과 ∥A∥F\lVert\mathbf{A}\rVert_F를, k가 min⁡(m,n)\min(m, n) 이상이면 A 자신과 0을 반환한다.

테스트는 다음을 확인한다.

  • A_k의 rank가 k이고, err가 실제 오차 np.linalg.norm(A - A_k)와 같다.
  • 같은 rank의 다른 근사(무작위 부분공간으로의 사영)보다 오차가 작다.
  • 경계 경우(k = 0, k ≥ min(m, n))

힌트: np.linalg.svd(A, full_matrices=False)가 돌려주는 Vt의 행이 vi⊤\mathbf{v}_i^\top이다. U[:, :k] * s[:k]처럼 브로드캐스팅을 쓰면 대각행렬을 만들지 않아도 된다.

SVD로 PCA 구현하기 pca-svd

데이터 행렬 X∈RN×D\mathbf{X} \in \mathbb{R}^{N \times D}(행 = 샘플)의 주성분을 SVD로 구한다. 열 평균 μ\boldsymbol{\mu}를 뺀 Xc=X−μ\mathbf{X}_c = \mathbf{X} - \boldsymbol{\mu}의 thin SVD가 Xc=UΣV⊤\mathbf{X}_c = \mathbf{U}\boldsymbol{\Sigma}\mathbf{V}^\top이면

  • 주성분 방향: V\mathbf{V}의 앞 kk개 열(Vt의 앞 kk개 행)
  • 각 방향의 분산: σi2/(N−1)\sigma_i^2 / (N - 1)
  • 점수(score): XcVk\mathbf{X}_c\mathbf{V}_k

이다. 함수 pca(X, k)를 완성해 딕셔너리를 반환한다.

키 shape 내용
"mean" (D,) 열 평균
"components" (k, D) 행 = 주성분 방향(단위 벡터), 분산이 큰 순서
"explained_variance" (k,) 각 주성분의 분산, 내림차순
"scores" (N, k) 중심화한 데이터의 주성분 좌표

np.cov나 np.linalg.eig·eigh는 쓰지 않는다(테스트가 비교 기준으로 쓴다).

테스트는 다음을 확인한다.

  • 분산과 방향이 공분산 행렬의 고유분해와 같다(방향은 부호를 무시하고 비교).
  • 평균이 원점에서 먼 데이터에서도 올바른 방향을 찾는다(중심화).
  • 점수의 표본 분산이 explained_variance와 같고, 점수끼리 상관이 없다.
  • scores @ components + mean이 rank-kk 재구성이며, k=Dk = D이면 원래 데이터로 돌아간다.