본문으로 이동
메뉴 여닫기
환경 설정 메뉴 여닫기
개인 메뉴 여닫기
로그인하지 않음
지금 편집한다면 당신의 IP 주소가 공개될 수 있습니다.
Singular value decomposition; SVD; 특이값 분해
임의의 실수(복소수) 행렬을 두 직교 행렬과 음이 아닌 대각 행렬의 곱 A=UΣV⊤로 나누는 행렬 분해

특이값 분해(singular value decomposition, SVD)는 모양에 상관없이 모든 행렬에 존재하는 분해다. 행렬이 하는 일을 "회전(또는 반사) → 축 방향 늘이기 → 회전"으로 쪼개 보여 주며, 늘이는 배율이 특이값(singular value)이다. 특이값이 큰 몇 개의 성분만 남기면 원래 행렬에 가장 가까운 저랭크 행렬을 얻는다는 성질(에카르트-영 정리) 때문에 주성분 분석, 잠재 의미 분석, 추천 시스템, 신경망 압축, LoRA의 이론적 배경까지 기계 학습 곳곳에 쓰인다. 사이시옷을 넣어 '특잇값 분해'로 쓰기도 한다.

저랭크 근사 일반은 저랭크 분해 문서, 선형대수 기초는 선형대수학과 고유값 문제 문서 참고.

모든 m×n 실수 행렬 A는 다음처럼 쓸 수 있다.

A=UΣV⊤

  • U: m×m 직교 행렬. 열 ui를 왼쪽 특이벡터라 한다.
  • V: n×n 직교 행렬. 열 vi를 오른쪽 특이벡터라 한다.
  • Σ: m×n 대각 행렬. 대각 원소 σ1≥σ2≥…≥σmin⁡(m,n)≥0이 특이값이다.

0이 아닌 특이값의 개수가 A의 랭크 r이다. 같은 내용을 랭크 1 행렬의 합으로도 쓸 수 있다.

A=∑i=1rσiuivi⊤,Avi=σiui,A⊤ui=σivi

복소 행렬이면 전치 대신 켤레 전치 VH를 쓴다. 특이값은 유일하고, 특이벡터는 부호(또는 중복 특이값이면 그 부분공간 안의 회전)만큼의 자유가 있다. 그래서 라이브러리마다 특이벡터의 부호가 다르게 나올 수 있다.

고윳값 분해와의 관계

편집 원본 편집

A⊤A=VΣ⊤ΣV⊤, AA⊤=UΣΣ⊤U⊤이다. 즉 오른쪽 특이벡터는 A⊤A의 고유벡터, 왼쪽 특이벡터는 AA⊤의 고유벡터이고, 특이값은 그 고윳값의 제곱근이다.

항목 고윳값 분해 A=QΛQ−1 특이값 분해 A=UΣV⊤
대상 정사각 행렬, 대각화 가능할 때만 모든 m×n 행렬
기저 하나(Q), 직교라는 보장 없음 둘(U, V), 항상 직교
값 고윳값, 음수·복소수 가능 특이값, 항상 음이 아닌 실수
같아지는 경우 대칭 양의 준정부호 행렬에서는 두 분해가 같다

수치 계산에서 A⊤A를 만들어 고윳값 분해하면 조건수가 제곱이 되어 작은 특이값의 정확도를 잃는다. 그래서 SVD는 A에 직접 계산한다.

크기에 따른 형태

편집 원본 편집
형태 U Σ V⊤ 쓰임
완전 SVD m×m m×n n×n 이론, 영공간 기저까지 필요할 때
얇은(경제) SVD m×k k×k k×n k=min⁡(m,n). 실무 기본값(full_matrices=False)
축약 SVD m×r r×r r×n 0인 특이값을 버린 형태(r = 랭크)
절단 SVD m×k k×k k×n k<r. 근사이며 원래 행렬과 같지 않다
  • 노름: 스펙트럼 노름 ‖A‖2=σ1, 프로베니우스 노름 ‖A‖F=∑iσi2, 핵 노름 ‖A‖*=∑iσi.
  • 조건수: κ(A)=σ1/σn. 크면 선형 시스템의 해가 입력 오차에 민감하다.
  • 기본 부분공간: u1,…,ur은 열공간, v1,…,vr은 행공간, 나머지 vr+1,…,vn은 영공간의 정규 직교 기저다.
  • 의사역행렬(무어-펜로즈 역행렬): A+=VΣ+U⊤. Σ+는 0이 아닌 특이값의 역수를 대각에 둔 것이다. 최소제곱 문제 minx‖Ax−b‖의 최소 노름 해가 x=A+b다. 작은 특이값을 0으로 잘라 버리면 잡음에 강한 해를 얻는다(절단 SVD 규제).
  • 기하학적 의미: 단위 구를 A로 옮기면 반지름이 σi, 축 방향이 ui인 타원체가 된다.

절단 SVD와 에카르트-영 정리

편집 원본 편집

큰 특이값 k개만 남긴 행렬을 Ak=∑i=1kσiuivi⊤=UkΣkVk⊤라 하자. 에카르트-영 정리는 랭크가 k 이하인 모든 행렬 B 중에서 Ak가 A에 가장 가깝다는 정리다.

minrank(B)≤k‖A−B‖F=‖A−Ak‖F=σk+12+…+σr2

1936년 에카르트와 영이 심리측정 데이터의 근사 문제로 프로베니우스 노름에 대해 보였다.[1] 스펙트럼 노름에서도 같은 Ak가 최적이고 오차는 σk+1이다. 그래서 특이값이 빠르게 줄어드는 행렬은 적은 수의 성분으로 거의 손실 없이 압축된다. 남길 k는 보통 누적 에너지 ∑i≤kσi2/∑iσi2가 정한 비율(예: 90%)을 넘는 지점이나, 특이값 그래프가 꺾이는 지점으로 고른다.

저장량은 mn에서 k(m+n+1)로 준다. 4096×4096 행렬을 k=64로 근사하면 약 1,678만 개에서 약 52만 개로, 3% 남짓이 된다.

방법 계산량 쓰는 경우 구현
골럽-카한 이중대각화 + QR 반복 O(mnmin⁡(m,n)) 크기가 수천 이하인 조밀 행렬의 전체 SVD LAPACK gesvd
분할 정복 같은 차수, 대체로 더 빠름 조밀 행렬 기본값 LAPACK gesdd(numpy가 사용)
란초시 계열 반복법 상위 k개만, 행렬-벡터 곱 위주 큰 희소 행렬 ARPACK, scipy.sparse.linalg.svds
무작위 SVD O(mnk) 안팎 큰 행렬의 상위 k개 근사 sklearn.utils.extmath.randomized_svd, torch.svd_lowrank

현대적 계산법의 기초는 1965년 골럽과 카한의 이중대각화 알고리즘이다.[2] numpy의 numpy.linalg.svd는 LAPACK의 _gesdd로 계산한다.[3] 무작위 SVD는 무작위 행렬을 곱해 A의 열공간 대부분을 담는 작은 부분공간을 찾은 뒤, 그 안에서만 정확한 SVD를 한다. 할코, 마르티네손, 트롭이 체계를 정리했다.[4]

랭크가 8인 행렬에 작은 잡음을 섞고 SVD로 구조를 찾는다(numpy 2.1, scikit-learn 1.7에서 실행 확인).

import numpy as np
rng = np.random.default_rng(0)
A = rng.normal(size=(100, 8)) @ rng.normal(size=(8, 60)) + 0.01 * rng.normal(size=(100, 60))

U, s, Vt = np.linalg.svd(A, full_matrices=False)    # 얇은 SVD: (100,60) (60,) (60,60)
print(np.allclose(A, U @ np.diag(s) @ Vt))            # True
print(s[:10].round(2))  # [111.09 92.61 86.71 69.11 63.46 53.28 47.74 42.23 0.16 0.15]

k = 8
A_k = (U[:, :k] * s[:k]) @ Vt[:k]                     # 절단 SVD
print(np.isclose(np.linalg.norm(A - A_k, "fro"), np.sqrt((s[k:] ** 2).sum())))  # True: 에카르트-영
print(np.isclose(np.linalg.norm(A - A_k, 2), s[k]))   # True: 스펙트럼 노름 오차 = sigma_{k+1}

# 의사역행렬
print(np.allclose(np.linalg.pinv(A), Vt.T @ np.diag(1 / s) @ U.T))   # True

9번째 특이값부터 0.16으로 급감해, 행렬의 실제 랭크가 8임이 드러난다. U * s는 브로드캐스팅으로 각 열에 특이값을 곱하는 것이라 np.diag를 만드는 것보다 빠르다.

기계 학습에서의 쓰임

편집 원본 편집

데이터 행렬 X(n개 표본 × d개 특징)를 열마다 평균을 빼 중심화한 뒤 SVD하면 Xc=UΣV⊤에서 V의 열이 주성분 방향이고, 각 주성분의 분산은 σi2/(n−1)이다. 공분산 행렬 Xc⊤Xc/(n−1)를 만들지 않으므로 수치적으로 더 안정적이다. scikit-learn의 PCA도 기본적으로 SVD 계열 솔버로 계산한다.

from sklearn.decomposition import PCA
Xc = A - A.mean(axis=0)
_, s2, Vt2 = np.linalg.svd(Xc, full_matrices=False)
pca = PCA(n_components=3).fit(A)
print(np.allclose(np.abs(pca.components_), np.abs(Vt2[:3])))          # True(부호만 다를 수 있음)
print(np.allclose(pca.explained_variance_, s2[:3] ** 2 / (len(A) - 1)))  # True

자세한 내용은 주성분 분석 문서 참고.

잠재 의미 분석

편집 원본 편집

잠재 의미 분석(LSA)은 단어-문서 행렬(문서 단어 행렬, 보통 TF-IDF 가중)을 절단 SVD해 문서와 단어를 같은 저차원 "의미 공간"에 놓는다. 함께 쓰이는 단어들이 같은 축으로 묶이므로, 표현이 달라도 주제가 같은 문서를 찾을 수 있다. 1990년 디어웨스터 등이 정보 검색용으로 발표했다.[5] scikit-learn의 TruncatedSVD는 PCA와 달리 데이터를 중심화하지 않아 희소 행렬을 그대로 받으며, 텍스트에 쓰면 LSA가 된다. 문서는 LSA용으로 성분 100개를 권한다.[6]

from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.decomposition import TruncatedSVD
docs = ["고양이가 소파에서 잔다", "강아지가 소파에서 잔다", "주가가 급락했다", "증시에서 주가가 반등했다"]
X = TfidfVectorizer().fit_transform(docs)           # 희소 행렬(문서 x 단어)
Z = TruncatedSVD(n_components=2, random_state=0).fit_transform(X)
print(Z.round(2))   # 앞의 두 문서는 첫째 축, 뒤의 두 문서는 둘째 축에 모인다

이후 주제 모델은 확률 모델인 잠재 디리클레 할당으로, 단어 표현은 워드 임베딩과 문장 임베딩으로 발전했다.

사용자 × 아이템 평점 행렬을 저랭크로 근사하면, 사용자와 아이템이 같은 잠재 요인 공간의 벡터가 되고 그 내적이 예측 평점이 된다. 다만 실제 평점 행렬은 대부분이 비어 있어 고전적 SVD를 그대로 계산할 수 없다. 넷플릭스 프라이즈 이후 널리 퍼진 방식은 관측된 칸에서만 오차를 재고 규제를 더해 minP,Q∑(u,i)관측(rui−pu⊤qi)2+λ(‖pu‖2+‖qi‖2)를 확률적 경사 하강법이나 교대 최소제곱(ALS)으로 푸는 행렬 인수분해다.[7] 추천 분야에서는 이것도 관행적으로 "SVD"라 부르지만, 엄밀한 의미의 특이값 분해와는 다르다. 자세한 내용은 추천 시스템, 협업 필터링 문서 참고.

신경망 압축과 LoRA

편집 원본 편집
  • 가중치 압축: 학습된 선형층 가중치 W(dout×din)를 절단 SVD해 W≈(UkΣk)(Vk⊤)로 두면, 층 하나를 폭 k인 층 두 개로 바꿀 수 있다. 매개변수는 doutdin에서 k(dout+din)으로 준다. 특이값이 천천히 줄어드는 층은 정확도 손실이 커서, 압축 뒤 미세 조정을 한다. 자세한 내용은 저랭크 분해 문서 참고.
  • LoRA: 미세 조정 때 가중치 변화량 ΔW가 낮은 "내재 랭크"를 가진다는 가설에서 출발해, ΔW=BA(B∈ℝd×r, A∈ℝr×k, r≪min⁡(d,k))만 학습한다. 원 논문은 GPT-3 175B에서 학습 매개변수를 1만 분의 1로, GPU 메모리를 3분의 1로 줄였다고 보고했다. 같은 논문은 랭크 8과 64로 학습한 어댑터를 각각 SVD해 상위 특이벡터 방향이 겹친다는 것을 보여, 작은 r로도 충분한 이유를 설명했다.[8] 자세한 내용은 LoRA (인공지능) 문서 참고.
  • PiSSA: LoRA와 구조는 같되, 어댑터 A, B를 원래 가중치 W의 상위 특이값·특이벡터로 초기화하고 나머지 성분을 고정된 잔차 행렬로 둔다. LoRA의 "가우스 잡음과 0" 초기화보다 수렴이 빠르다고 주장한다.[9]
  • 스펙트럼 정규화: GAN의 판별자 각 층을 최대 특이값 σ1로 나눠 립시츠 상수를 제한한다. σ1은 전체 SVD 대신 거듭제곱법 한 번으로 근사한다.[10]

그 밖의 쓰임

편집 원본 편집
  • 최소제곱과 선형 회귀: 정규 방정식 대신 SVD(의사역행렬)로 풀면 다중 공선성이 있어도 안정적이다.
  • 이미지 압축과 잡음 제거: 큰 특이값만 남기면 저주파 구조가 남고 잡음이 줄어든다.
  • 커널 방법의 니스트룀 근사, 희소 데이터를 t-SNE에 넣기 전의 차원 축소(TruncatedSVD).

19세기 후반 벨트라미(1873)와 조르당(1874)이 이차 형식 연구에서 독립적으로 발견했고, 이후 실베스터, 슈미트, 바일 등이 일반화했다.[11] 저랭크 근사 정리는 1936년 에카르트와 영,[1] 실용 알고리즘은 1965년 골럽과 카한[2]으로 이어졌고, 2000년대 이후 큰 데이터용 무작위 알고리즘이 정리되었다.[4]

다른 행렬 분해와 비교

편집 원본 편집
분해 형태 조건 주 용도
SVD UΣV⊤ 모든 행렬 저랭크 근사, PCA, 의사역행렬, 랭크·조건수 판단
고윳값 분해 QΛQ−1 정사각, 대각화 가능 동역학, 마르코프 연쇄, 스펙트럼 군집
QR 분해 QR 모든 행렬 최소제곱, 직교화. SVD보다 싸다
촐레스키 분해 LL⊤ 대칭 양의 정부호 선형 시스템, 가우시안 프로세스
비음수 행렬 분해(NMF) WH, 원소가 모두 0 이상 비음수 행렬 부분 기반 표현, 주제 모델. 해가 유일하지 않다
  1. ↑ 1.0 1.1 Carl Eckart, Gale Young, 〈The approximation of one matrix by another of lower rank〉, Psychometrika 1(3), 211~218, 1936. doi:10.1007/BF02288367
  2. ↑ 2.0 2.1 G. Golub, W. Kahan, 〈Calculating the Singular Values and Pseudo-Inverse of a Matrix〉, Journal of the Society for Industrial and Applied Mathematics Series B Numerical Analysis 2(2), 205~224, 1965. doi:10.1137/0702016
  3. ↑ NumPy 문서, numpy.linalg.svd
  4. ↑ 4.0 4.1 N. Halko, P. G. Martinsson, J. A. Tropp, 〈Finding Structure with Randomness: Probabilistic Algorithms for Constructing Approximate Matrix Decompositions〉, SIAM Review 53(2), 217~288, 2011. doi:10.1137/090771806 arXiv:0909.4061
  5. ↑ Scott Deerwester, Susan T. Dumais, George W. Furnas, Thomas K. Landauer, Richard Harshman, 〈Indexing by latent semantic analysis〉, Journal of the American Society for Information Science 41(6), 391~407, 1990. doi
  6. ↑ scikit-learn 1.9 문서, TruncatedSVD
  7. ↑ Yehuda Koren, Robert Bell, Chris Volinsky, 〈Matrix Factorization Techniques for Recommender Systems〉, Computer 42(8), 30~37, 2009. doi:10.1109/MC.2009.263
  8. ↑ Edward J. Hu 외, 〈LoRA: Low-Rank Adaptation of Large Language Models〉, ICLR 2022. arXiv:2106.09685
  9. ↑ Fanxu Meng, Zhaohui Wang, Muhan Zhang, 〈PiSSA: Principal Singular Values and Singular Vectors Adaptation of Large Language Models〉, 2024. arXiv:2404.02948
  10. ↑ Takeru Miyato 외, 〈Spectral Normalization for Generative Adversarial Networks〉, ICLR 2018. arXiv:1802.05957
  11. ↑ G. W. Stewart, 〈On the Early History of the Singular Value Decomposition〉, SIAM Review 35(4), 551~566, 1993. doi:10.1137/1035134
  • Gene H. Golub, Charles F. Van Loan, 《Matrix Computations》 4판, Johns Hopkins University Press, 2013.
  • Lloyd N. Trefethen, David Bau III, 《Numerical Linear Algebra》, SIAM, 1997.
  • NumPy 문서, numpy.linalg.svd