@amoeba были отличные ответы на PCA вопросы, в том числе это одно по отношению к СВД PCA. Отвечая на ваш точный вопрос, я сделаю три замечания:
- математически нет никакой разницы, вычисляете ли вы 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]
Обновить
Отвечая на комментарий Федерико Полони, вот код с тестированием стабильности 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
Здесь код код работает. Вместо того, чтобы генерировать случайную ковариационную матрицу для проверки подпрограмм, я генерирую случайную матрицу данных с двумя переменными:
где - независимые однородные случайные величины. Таким образом, ковариационная матрица имеет вид:
где - дисперсия униформ и коэффициент корреляции между их.
x1=ux2=u+εv
u,v(σ21σ21+ερσ1σ2σ21+ερσ1σ2σ21+2ερσ1σ2+ε2σ22σ2)
σ21,σ22,ρ
Наименьшее собственное значение:
Маленькое собственное значение не может быть вычислено простым подключением в формулу из-за ограниченной точности, поэтому вам нужно Тейлор развернуть его:
λ=12(σ22ε2−σ42ε4+4σ32ρσ1ε3+8σ22ρ2σ21ε2+8σ2ρσ31ε+4σ41−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−√+2σ2ρσ1ε+2σ21)
ελ≈σ22ε2(1−ρ2)/2
Я запускаю моделирования реализаций матрицы данных, вычисляю собственные значения моделируемой ковариационной матрицы и ошибки .j=1,…,mλ^jej=λ−λ^j