Andrew Ng์ Coursera ์ฝ์ค ๋ฐ ๊ธฐํ ์๋ฃ์์ PCA๋ฅผ ๊ณต๋ถํ๊ณ ์์ต๋๋ค. ์คํ ํฌ๋ NLP ๊ณผ์ ์์ cs224n์ ์ฒซ ๋ฒ์งธ ๊ณผ์ ์ Andrew Ng ์ ๊ฐ์ ๋น๋์ค์์ ๊ณต๋ถ์ฐ ํ๋ ฌ์ ๊ณ ์ ๋ฒกํฐ ๋ถํด ๋์ ํน์ด ๊ฐ ๋ถํด๋ฅผ ์ํํ๋ฉฐ Ng๋ SVD๊ฐ ๊ณ ์ ๋ถํด๋ณด๋ค ์์น ์ ์ผ๋ก ๋ ์์ ์ ์ด๋ผ๊ณ ๋งํฉ๋๋ค.
PCA์ ๊ฒฝ์ฐ (m,n)
ํฌ๊ธฐ์ ๊ณต๋ถ์ฐ ํ๋ ฌ์ด ์๋ ํฌ๊ธฐ ์ ๋ฐ์ดํฐ ํ๋ ฌ์ SVD๋ฅผ ์ํํด์ผ (n,n)
ํฉ๋๋ค. ๊ณต๋ถ์ฐ ํ๋ ฌ์ ๊ณ ์ ๋ฒกํฐ ๋ถํด.
์ ๋ฐ์ดํฐ ํ๋ ฌ์ด ์๋ ๊ณต๋ถ์ฐ ํ๋ ฌ์ SVD๋ฅผ ์ํํฉ๋๊น?
๋ต๋ณ
amoeba๋ ์ด๋ฏธ ์๊ฒฌ์ ์ข์ ๋๋ต์ํ์ง๋ง ๊ณต์์ ์ธ ์ฃผ์ฅ์ ์ํ๋ค๋ฉด ์ฌ๊ธฐ์ ๊ฐ๋ค.
ํ๋ ฌ์ ํน์ด ๊ฐ ๋ถํด ์ธ = U ฮฃ V T ์ ์ด, V๋ ์ ๊ณ ์ ๋ฒกํฐ์ด๋ค T ๊ทธ๋ฆฌ๊ณ ๋๊ฐ์ ์ํธ๋ฆฌ ฮฃ ์๋ค ์ ๊ณฑ๊ทผ ๊ทธ ๊ณ ์ ์, ์ฆ ฯ I I = โ
์์ดA=UฮฃVT
V
ATA
ฮฃ
.
ฯii=ฮปi(ATA)์์๋ค์ํผ ์ฃผ์ฑ๋ถ์ ๊ฒฝํ์ ๊ณต๋ถ์ฐ ํ๋ ฌ์ ๊ณ ์ ๋ฒกํฐ ๊ณต๊ฐ์ ๋ํ ๋ณ์์ ์ง๊ต ํฌ์์ ๋๋ค . ์ฑ๋ถ์ ๋ถ์ฐ์ ๊ณ ์ ๊ฐฮปi(1
1nโ1ATA.
ฮปi(1nโ1ATA)B v = ฮป v๊ฐ ๋๋๋ก ์ ์ฌ๊ฐ ํ๋ ฌ , ฮฑ โ R ๋ฐ ๋ฒกํฐ v๋ฅผ ๊ณ ๋ คํ์ญ์์ค . ๊ทธ๋
BฮฑโR
v
Bv=ฮปV
Bkv=ฮป์ผ์ดV
ฮป(ฮฑ๋น)=ฮฑฮป(๋น)
S = 1์ ์ ์ํ์. SVD์S๋์ ๊ณ์ฐํ๋ค eigendecompositionSTS=1
์์ค=1์โ1์์ดํฐ์์ด์์ค
๋ฅผ ์ฐ์ถ
STS=1(nโ1)2ATAATA- ์ ๊ณ ์ ๋ฒกํฐ ์์ฑ 1์ ๊ฒ์ด๋ฉฐ, T
(ATA)TATA=ATAATA ATA - ์ ๊ณฑ๊ทผ ์ ๊ณ ์ ์ , ํน์ฑ 2, 1, 2๋ ๋ค์โ
1(nโ1)2ATAATA . 1(nโ1)2ฮปi(ATAATA)=1(nโ1)2ฮปi2(ATA)=1nโ1ฮปi(ATA)=ฮปi(1nโ1ATA)
oil!
์์น ์์ ์ฑ๊ณผ ๊ด๋ จํ์ฌ, ์ฌ์ฉ ๋ ์๊ณ ๋ฆฌ์ฆ์ด ๋ฌด์์ธ์ง ์์ ๋ด์ผํฉ๋๋ค. ๋น์ ์ด ๊ทธ๊ฒ์ ๋ฌ๋ ค ์๋ค๋ฉด, ๋๋ ์ด๊ฒ์ด numpy์ ์ํด ์ฌ์ฉ๋๋ LAPACK ๋ฃจํด์ด๋ผ๊ณ ์๊ฐํฉ๋๋ค :
์ ๋ฐ์ดํธ : ์์ ์ฑ์์ SVD ๊ตฌํ์ ๋ถํ ๋ฐ ์ ๋ณต ์ ๊ทผ๋ฒ์ ์ฌ์ฉํ๋ ๊ฒ์ฒ๋ผ ๋ณด์ด์ง๋ง ๊ณ ์ ๋ถํด๋ ์ผ๋ฐ QR ์๊ณ ๋ฆฌ์ฆ์ ์ฌ์ฉํฉ๋๋ค. ๊ธฐ๊ด์์ ์ ๊ณตํ๋ ๊ด๋ จ SIAM ๋ ผ๋ฌธ (๋น๋ ์ฐ๊ตฌ ์ญ๊ฐ)์ ์ก์ธ์ค ํ ์ ์์ง๋ง SVD ๋ฃจํด์ด ๋ ์์ ์ ์ด๋ผ๋ ํ๊ฐ๋ฅผ ๋ท๋ฐ์นจ ํ ์์๋ ๊ฒ์ ๋ฐ๊ฒฌํ์ต๋๋ค.
์์
Nakatsukasa, Yuji ๋ฐ Nicholas J. Higham. โ๋์นญ ๊ณ ์ ๊ฐ ๋ถํด ๋ฐ SVD๋ฅผ์ํ ์์ ์ ์ด๊ณ ํจ์จ์ ์ธ ์คํํธ๋ผ ๋ถํ ๋ฐ ์ ๋ณต ์๊ณ ๋ฆฌ์ฆ.โ ๊ณผํ ์ปดํจํ ์ ๊ดํ SIAM Journal 35.3 (2013) : A1325-A1349.
๊ทธ๋ค์ ๋ค์ํ ๊ณ ์ ๊ฐ ์๊ณ ๋ฆฌ์ฆ์ ์์ ์ฑ์ ๋น๊ตํ๋ฉฐ, ๋ถํ ๋ฐ ์ ๋ณต ์ ๊ทผ๋ฒ (์คํ ์ค ํ๋์์ numpy์ ๋์ผํ ๋ฐฉ์์ ์ฌ์ฉํฉ๋๋ค!)์ด QR ์๊ณ ๋ฆฌ์ฆ๋ณด๋ค ๋ ์์ ์ ์ธ ๊ฒ์ผ๋ก ๋ณด์ ๋๋ค. ์ด๊ฒ์ D & C ๋ฐฉ๋ฒ์ด ๋ ์์ ์ ์ด๋ผ๋ ๋ค๋ฅธ ์ฃผ์ฅ๊ณผ ํจ๊ป Ng์ ์ ํ์ ๋ท๋ฐ์นจํฉ๋๋ค.
๋ต๋ณ
@amoeba ํฌํจ PCA ์ง๋ฌธ์ ํ๋ฅญํ ๋๋ตํ๋ค ์ด ํ PCA์ SVD์ ๊ด๊ณ์์๋ค. ์ ํํ ์ง๋ฌธ์ ๋๋ตํ๋ฉด ์ธ ๊ฐ์ง ์ ์ ์๋ ค ๋๋ฆฌ๊ฒ ์ต๋๋ค.
- ์ํ์ ์ผ๋ก ๋ฐ์ดํฐ ํ๋ ฌ์์ ์ง์ ๋๋ ๊ณต๋ถ์ฐ ํ๋ ฌ์์ PCA๋ฅผ ๊ณ์ฐํ๋์ง ์ฌ๋ถ์๋ ์ฐจ์ด๊ฐ ์์ต๋๋ค.
- ๊ทธ ์ฐจ์ด๋ ์์ ํ ์์น ์ ๋ฐ๋์ ๋ณต์ก์ฑ ๋๋ฌธ์ ๋๋ค. SVD๋ฅผ ๋ฐ์ดํฐ ํ๋ ฌ์ ์ง์ ์ ์ฉํ๋ ๊ฒ์ ๊ณต๋ถ์ฐ ํ๋ ฌ๋ณด๋ค ์์น ์ ์ผ๋ก ๋ ์์ ์ ์ ๋๋ค.
- SVD๋ฅผ ๊ณต๋ถ์ฐ ํ๋ ฌ์ ์ ์ฉํ์ฌ PCA๋ฅผ ์ํํ๊ฑฐ๋ ๊ณ ์ ๊ฐ์ ์ป์ ์ ์์ต๋๋ค. ์ค์ ๋ก ๊ณ ์ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ ๊ฒ์ด ๊ฐ์ฅ ์ข์ํ๋ ๋ฐฉ๋ฒ์ ๋๋ค.
SVD๋ ํนํ ๋จธ์ ๋ฌ๋์ ์ผ๋ฐ์ ์ธ ๊ณ ์ ๊ฐ ๊ฐ์ ์ ์ฐจ๋ณด๋ค ์์ ์ ์ ๋๋ค. ๋จธ์ ๋ฌ๋์์๋ ๊ณต ์ ํ ํ๊ท ๋ถ์์ ์ฝ๊ฒ ์ํ ํ ์ ์์ต๋๋ค. ์ด ๊ฒฝ์ฐ SVD๊ฐ ๋ ์ ์๋ํฉ๋๋ค.
์์ ์ ์์ฐํ๋ Python ์ฝ๋๊ฐ ์์ต๋๋ค. ๋๋ ๋งค์ฐ ๊ณต์ ์ ์ธ ๋ฐ์ดํฐ ํ๋ ฌ์ ๋ง๋ค๊ณ ๊ณต๋ถ์ฐ ํ๋ ฌ์ ๊ฐ์ ธ ์์ ํ์์ ๊ณ ์ ๊ฐ์ ์ป์ผ๋ ค๊ณ ํ์ต๋๋ค. SVD๋ ์ฌ์ ํ ์๋ํ์ง๋ง์ด ๊ฒฝ์ฐ ์ผ๋ฐ์ ์ธ ๊ณ ์ ๋ถํด๋ ์คํจํฉ๋๋ค.
import numpy as np
import math
from numpy import linalg as LA
np.random.seed(1)
# create the highly collinear series
T = 1000
X = np.random.rand(T,2)
eps = 1e-11
X[:,1] = X[:,0] + eps*X[:,1]
C = np.cov(np.transpose(X))
print('Cov: ',C)
U, s, V = LA.svd(C)
print('SVDs: ',s)
w, v = LA.eig(C)
print('eigen vals: ',w)
์ฐ์ถ:
Cov: [[ 0.08311516 0.08311516]
[ 0.08311516 0.08311516]]
SVDs: [ 1.66230312e-01 5.66687522e-18]
eigen vals: [ 0. 0.16623031]
์ต์ ์ ๋ณด
Federico Poloni์ ์๊ฒฌ์ ๋ฐ๋ผ SVD์ Eig์ ์์ ์ฑ ํ ์คํธ๊ฐ ์์ ๋์ผํ ํ๋ ฌ์ ๋ฌด์์ ์ํ 1000 ๊ฐ์ ๋ํ ์ฝ๋๊ฐ ์์ต๋๋ค. ๋ง์ ๊ฒฝ์ฐ์, Eig๋ 0์ ์์ ๊ณ ์ ๊ฐ์ ๋ณด์ฌ์ฃผ๋๋ฐ, ์ด๋ ํ๋ ฌ์ ํน์ด์ฑ์ ์ด๋ํ ๊ฒ์ ๋๋ค. ๊ทธ๋ฆฌ๊ณ SVD๋ ์ฌ๊ธฐ์ํ์ง ์์ต๋๋ค. SVD๋ ์์ ๊ณ ์ ๊ฐ ๊ฒฐ์ ์์ ๋ ๋ฐฐ ์ ๋ ๋ ์ ํํ๋ฉฐ, ๋ฌธ์ ์ ๋ฐ๋ผ ์ค์ํ๊ฑฐ๋ ์ค์ํ์ง ์์ ์ ์์ต๋๋ค.
import numpy as np
import math
from scipy.linalg import toeplitz
from numpy import linalg as LA
np.random.seed(1)
# create the highly collinear series
T = 100
p = 2
eps = 1e-8
m = 1000 # simulations
err = np.ones((m,2)) # accuracy of small eig value
for j in range(m):
u = np.random.rand(T,p)
X = np.ones(u.shape)
X[:,0] = u[:,0]
for i in range(1,p):
X[:,i] = eps*u[:,i]+u[:,0]
C = np.cov(np.transpose(X))
U, s, V = LA.svd(C)
w, v = LA.eig(C)
# true eigen values
te = eps**2/2 * np.var(u[:,1])*(1-np.corrcoef(u,rowvar=False)[0,1]**2)
err[j,0] = s[p-1] - te
err[j,1] = np.amin(w) - te
print('Cov: ',C)
print('SVDs: ',s)
print('eigen vals: ',w)
print('true small eigenvals: ',te)
acc = np.mean(np.abs(err),axis=0)
print("small eigenval, accuracy SVD, Eig: ",acc[0]/te,acc[1]/te)
์ฐ์ถ:
Cov: [[ 0.09189421 0.09189421]
[ 0.09189421 0.09189421]]
SVDs: [ 0.18378843 0. ]
eigen vals: [ 1.38777878e-17 1.83788428e-01]
true small eigenvals: 4.02633695086e-18
small eigenval, accuracy SVD, Eig: 2.43114702041 3.31970128319
u,v
ฯ12,ฯ22,ฯ
โ ์ ๋ํผ๊ณผ ๊ทธ๋ค ์ฌ์ด์ ์๊ด ๊ด๊ณ coeffient์ ํธ์ฐจ๋ฅผ.
The small eigenvalue canโt be calculated by simply plugging the
into formula due to limited precision, so you need to Taylor expand it:
I run
j=1,โฆ,msimulations of the realizations of the data matrix, calculate the eigenvalues of the simulated covariance matrix
ฮป^j, and obtain the errors
ej=ฮปโฮป^j.
๋ต๋ณ
For Python users, Iโd like to point out that for symmetric matrices (like the covariance matrix), it is better to use numpy.linalg.eigh
function instead of a general numpy.linalg.eig
function.
eigh
is 9-10 times faster than eig
on my computer (regardless of matrix size) and has better accuracy (based on @Aksakalโs accuracy test).
I am not convinced with the demonstration of the accuracy benefit of SVD with small eigenvalues. @Aksakalโs test is 1-2 orders of magnitude more sensitive to random state than to the algorithm (try plotting all errors instead of reducing them to one absolute maximum). It means that small errors in the covariance matrix will have a greater effect on accuracy than the choice of an eigendecomposition algorithm. Also, this is not related to the main question, which is about PCA. The smallest components are ignored in PCA.
A similar argument can be made about numerical stability. If I have to use the covariance matrix method for PCA, I would decompose it with eigh
instead of svd
. If it fails (which has not been demonstrated here yet), then it is probably worth rethinking the problem that you are trying to solve before starting to look for a better algorithm.
๋ต๋ณ
์ง๋ฌธ์ ๋ง์ง๋ง ๋ถ๋ถ์ ๋ตํ๊ธฐ ์ํด โ์ ๊ทธ๋ค์ ๋ฐ์ดํฐ ํ๋ ฌ์ด ์๋ ๊ณต๋ถ์ฐ ํ๋ ฌ์ SVD๋ฅผ ์ํํฉ๋๊น?โ ๋๋ ๊ทธ๊ฒ์ด ์ฑ๋ฅ ๋ฐ ์ ์ฅ์์ ์ด์ ๋ผ๊ณ ์๊ฐํฉ๋๋ค. ์ผ๋ฐ์ ์ผ๋ก
์๋งค์ฐ ํฐ ์ซ์๊ฐ ๋ ์ง๋ผ๋
์ํฌ๋ค, ์ฐ๋ฆฌ๋ ๊ธฐ๋ํ ๊ฒ์ด๋ค
์ โซ์.
๊ณต๋ถ์ฐ ํ๋ ฌ์ ๊ณ์ฐ ํ ๋ค์ SVD๋ฅผ ์ํํ๋ ๊ฒ์ ๋์ผํ ๊ฒฐ๊ณผ๋ฅผ ์ํด ์ด๋ฌํ ์กฐ๊ฑด์์ ์ ์ฒด ๋ฐ์ดํฐ ํ๋ ฌ์ ๋ํ SVD๋ฅผ ๊ณ์ฐํ๋ ๊ฒ๋ณด๋ค ํจ์ฌ ๋น ๋ฆ ๋๋ค.
์๋นํ ์์ ๊ฐ์ด๋ผ๋ ์ฑ๋ฅ ํฅ์์ ์์ฒ ๋ฐฐ (๋ฐ๋ฆฌ ์ด ๋ ์ด)์
๋๋ค. Matlab์ ์ฌ์ฉํ์ฌ ๋น๊ตํ๊ธฐ ์ํด ์ปดํจํฐ์์ ๋ช ๊ฐ์ง ํ
์คํธ๋ฅผ ์คํํ์ต๋๋ค.
๊ทธ๊ฒ์ CPU ์๊ฐ ์ผ ๋ฟ์ด์ง ๋ง ์คํ ๋ฆฌ์ง ์๊ตฌ๋ ๊ทธ๋ค์ง ์ค์ํ์ง ์์ต๋๋ค. Matlab์์ ๋ฐฑ๋ง x ์ฒ ๊ฐ์ ๋งคํธ๋ฆญ์ค์์ SVD๋ฅผ ์๋ํ๋ฉด 7.4TB์ ์์ ๋ฐฐ์ด ํฌ๊ธฐ๊ฐ ํ์ํ๊ธฐ ๋๋ฌธ์ ๊ธฐ๋ณธ์ ์ผ๋ก ์ค๋ฅ๊ฐ ๋ฐ์ํฉ๋๋ค.