특이값 분해
더 많은 작업
- Singular value decomposition; SVD; 특이값 분해
- 임의의 실수(복소수) 행렬을 두 직교 행렬과 음이 아닌 대각 행렬의 곱 로 나누는 행렬 분해
특이값 분해(singular value decomposition, SVD)는 모양에 상관없이 모든 행렬에 존재하는 분해다. 행렬이 하는 일을 "회전(또는 반사) → 축 방향 늘이기 → 회전"으로 쪼개 보여 주며, 늘이는 배율이 특이값(singular value)이다. 특이값이 큰 몇 개의 성분만 남기면 원래 행렬에 가장 가까운 저랭크 행렬을 얻는다는 성질(에카르트-영 정리) 때문에 주성분 분석, 잠재 의미 분석, 추천 시스템, 신경망 압축, LoRA의 이론적 배경까지 기계 학습 곳곳에 쓰인다. 사이시옷을 넣어 '특잇값 분해'로 쓰기도 한다.
모든 실수 행렬 는 다음처럼 쓸 수 있다.
- : 직교 행렬. 열 를 왼쪽 특이벡터라 한다.
- : 직교 행렬. 열 를 오른쪽 특이벡터라 한다.
- : 대각 행렬. 대각 원소 이 특이값이다.
0이 아닌 특이값의 개수가 의 랭크 이다. 같은 내용을 랭크 1 행렬의 합으로도 쓸 수 있다.
복소 행렬이면 전치 대신 켤레 전치 를 쓴다. 특이값은 유일하고, 특이벡터는 부호(또는 중복 특이값이면 그 부분공간 안의 회전)만큼의 자유가 있다. 그래서 라이브러리마다 특이벡터의 부호가 다르게 나올 수 있다.
, 이다. 즉 오른쪽 특이벡터는 의 고유벡터, 왼쪽 특이벡터는 의 고유벡터이고, 특이값은 그 고윳값의 제곱근이다.
| 항목 | 고윳값 분해 | 특이값 분해 |
|---|---|---|
| 대상 | 정사각 행렬, 대각화 가능할 때만 | 모든 행렬 |
| 기저 | 하나(), 직교라는 보장 없음 | 둘(, ), 항상 직교 |
| 값 | 고윳값, 음수·복소수 가능 | 특이값, 항상 음이 아닌 실수 |
| 같아지는 경우 | 대칭 양의 준정부호 행렬에서는 두 분해가 같다 | |
수치 계산에서 를 만들어 고윳값 분해하면 조건수가 제곱이 되어 작은 특이값의 정확도를 잃는다. 그래서 SVD는 에 직접 계산한다.
| 형태 | 쓰임 | |||
|---|---|---|---|---|
| 완전 SVD | 이론, 영공간 기저까지 필요할 때 | |||
| 얇은(경제) SVD | . 실무 기본값(full_matrices=False)
| |||
| 축약 SVD | 0인 특이값을 버린 형태( = 랭크) | |||
| 절단 SVD | . 근사이며 원래 행렬과 같지 않다 |
- 노름: 스펙트럼 노름 , 프로베니우스 노름 , 핵 노름 .
- 조건수: . 크면 선형 시스템의 해가 입력 오차에 민감하다.
- 기본 부분공간: 은 열공간, 은 행공간, 나머지 은 영공간의 정규 직교 기저다.
- 의사역행렬(무어-펜로즈 역행렬): . 는 0이 아닌 특이값의 역수를 대각에 둔 것이다. 최소제곱 문제 의 최소 노름 해가 다. 작은 특이값을 0으로 잘라 버리면 잡음에 강한 해를 얻는다(절단 SVD 규제).
- 기하학적 의미: 단위 구를 로 옮기면 반지름이 , 축 방향이 인 타원체가 된다.
큰 특이값 개만 남긴 행렬을 라 하자. 에카르트-영 정리는 랭크가 이하인 모든 행렬 중에서 가 에 가장 가깝다는 정리다.
1936년 에카르트와 영이 심리측정 데이터의 근사 문제로 프로베니우스 노름에 대해 보였다.[1] 스펙트럼 노름에서도 같은 가 최적이고 오차는 이다. 그래서 특이값이 빠르게 줄어드는 행렬은 적은 수의 성분으로 거의 손실 없이 압축된다. 남길 는 보통 누적 에너지 가 정한 비율(예: 90%)을 넘는 지점이나, 특이값 그래프가 꺾이는 지점으로 고른다.
저장량은 에서 로 준다. 행렬을 로 근사하면 약 1,678만 개에서 약 52만 개로, 3% 남짓이 된다.
| 방법 | 계산량 | 쓰는 경우 | 구현 |
|---|---|---|---|
| 골럽-카한 이중대각화 + QR 반복 | 크기가 수천 이하인 조밀 행렬의 전체 SVD | LAPACK gesvd
| |
| 분할 정복 | 같은 차수, 대체로 더 빠름 | 조밀 행렬 기본값 | LAPACK gesdd(numpy가 사용)
|
| 란초시 계열 반복법 | 상위 개만, 행렬-벡터 곱 위주 | 큰 희소 행렬 | ARPACK, scipy.sparse.linalg.svds
|
| 무작위 SVD | 안팎 | 큰 행렬의 상위 개 근사 | sklearn.utils.extmath.randomized_svd, torch.svd_lowrank
|
현대적 계산법의 기초는 1965년 골럽과 카한의 이중대각화 알고리즘이다.[2] numpy의 numpy.linalg.svd는 LAPACK의 _gesdd로 계산한다.[3] 무작위 SVD는 무작위 행렬을 곱해 의 열공간 대부분을 담는 작은 부분공간을 찾은 뒤, 그 안에서만 정확한 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를 만드는 것보다 빠르다.
데이터 행렬 (개 표본 × 개 특징)를 열마다 평균을 빼 중심화한 뒤 SVD하면 에서 의 열이 주성분 방향이고, 각 주성분의 분산은 이다. 공분산 행렬 를 만들지 않으므로 수치적으로 더 안정적이다. 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해 로 두면, 층 하나를 폭 인 층 두 개로 바꿀 수 있다. 매개변수는 에서 으로 준다. 특이값이 천천히 줄어드는 층은 정확도 손실이 커서, 압축 뒤 미세 조정을 한다. 자세한 내용은 저랭크 분해 문서 참고.
- LoRA: 미세 조정 때 가중치 변화량 가 낮은 "내재 랭크"를 가진다는 가설에서 출발해, (, , )만 학습한다. 원 논문은 GPT-3 175B에서 학습 매개변수를 1만 분의 1로, GPU 메모리를 3분의 1로 줄였다고 보고했다. 같은 논문은 랭크 8과 64로 학습한 어댑터를 각각 SVD해 상위 특이벡터 방향이 겹친다는 것을 보여, 작은 로도 충분한 이유를 설명했다.[8] 자세한 내용은 LoRA (인공지능) 문서 참고.
- PiSSA: LoRA와 구조는 같되, 어댑터 , 를 원래 가중치 의 상위 특이값·특이벡터로 초기화하고 나머지 성분을 고정된 잔차 행렬로 둔다. LoRA의 "가우스 잡음과 0" 초기화보다 수렴이 빠르다고 주장한다.[9]
- 스펙트럼 정규화: GAN의 판별자 각 층을 최대 특이값 로 나눠 립시츠 상수를 제한한다. 은 전체 SVD 대신 거듭제곱법 한 번으로 근사한다.[10]
| 분해 | 형태 | 조건 | 주 용도 |
|---|---|---|---|
| SVD | 모든 행렬 | 저랭크 근사, PCA, 의사역행렬, 랭크·조건수 판단 | |
| 고윳값 분해 | 정사각, 대각화 가능 | 동역학, 마르코프 연쇄, 스펙트럼 군집 | |
| QR 분해 | 모든 행렬 | 최소제곱, 직교화. SVD보다 싸다 | |
| 촐레스키 분해 | 대칭 양의 정부호 | 선형 시스템, 가우시안 프로세스 | |
| 비음수 행렬 분해(NMF) | , 원소가 모두 0 이상 | 비음수 행렬 | 부분 기반 표현, 주제 모델. 해가 유일하지 않다 |
- ↑ 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.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
- ↑ NumPy 문서, numpy.linalg.svd
- ↑ 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
- ↑ 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
- ↑ scikit-learn 1.9 문서, TruncatedSVD
- ↑ Yehuda Koren, Robert Bell, Chris Volinsky, 〈Matrix Factorization Techniques for Recommender Systems〉, Computer 42(8), 30~37, 2009. doi:10.1109/MC.2009.263
- ↑ Edward J. Hu 외, 〈LoRA: Low-Rank Adaptation of Large Language Models〉, ICLR 2022. arXiv:2106.09685
- ↑ Fanxu Meng, Zhaohui Wang, Muhan Zhang, 〈PiSSA: Principal Singular Values and Singular Vectors Adaptation of Large Language Models〉, 2024. arXiv:2404.02948
- ↑ Takeru Miyato 외, 〈Spectral Normalization for Generative Adversarial Networks〉, ICLR 2018. arXiv:1802.05957
- ↑ 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