[英]PCA difference between python numpy and sklearn
我正在嘗試使用numpy.linalg.eig和兩種不同的方法(本征面中使用協方差和pca方法)來實現PCA,並將結果與sklearn的PCA進行比較。 但是我發現我的結果有所不同,所以我想知道自己在犯哪個錯誤。 我有3個示例,每個示例都有4個功能。 我正在嘗試將樣本的尺寸減小到3。編輯:使用SVD方法添加。 我使用sklearn的協方差PCA,SVD和PCA得到的結果非常接近。 但是為什么使用“特征臉”方法卻完全不同呢? 從sklearn.decomposition導入numpy作為np導入PCA
x = np.array([[0.5, 0.8, 1.5, -2.4], [-1.9, -8.7, 0.02, 4.9], [5.5,6.1, -8.1,3.0]])
print(x)
K = 3
# -- sklearn -- #
pca = PCA(n_components=K).fit(x)
res = pca.transform(x)
print('sklearn :', res)
# -- numpy covariance -- #
X = x - np.mean(x, axis = 0)
cov = np.cov(X.T)
print("covariance :", cov.shape)
evals , evecs = np.linalg.eig(cov)
idx = np.argsort(evals)[::-1]
evecs = evecs[:,idx]
evals = evals[idx]
res2 = X.dot(evecs[:,:K])
print("numpy with cov:", res2)
# -- numpy scatter matrix -- #
X = x - np.mean(x, axis = 0)
C = np.dot(X, X.T)
evals , evecs = np.linalg.eig(C)
idx = np.argsort(evals)[::-1]
evecs = evecs[:,idx]
evals = evals[idx]
v = np.dot(evecs, X)
print("v :", v.shape)
res3= X[:, :K].dot(v)
print('numpy with scatter matrix : ', res3)
# -- numpy svd -- #
X = x - np.mean(x, axis = 0)
U, S, V = np.linalg.svd(X, full_matrices=False)
U, V = svd_flip(U, V)
res2 = X.dot(V.T)
print("numpy with svd:", res2)
首先:這意味着什么? 您在4維空間中有3個點。 它們跨越二維平面。 PCA會為此平面找到一個基礎,並在此基礎上找到您的點的系數。 使用Matlab的[C, S] = pca(x)
進行比較:
C =
0.4028 0.1082
0.7895 -0.3198
-0.4560 -0.5881
-0.0806 0.7349
和
S =
-0.5865 -5.8249
-8.9674 3.1891
9.5539 2.6357
它們是具有S*C'
恢復中心數據(在您的符號中為X
)的屬性的矩陣。 C的列是2D子空間的基礎向量,S的行是該子集中三個點的坐標。
斯克萊恩回歸
[[ -5.86525831e-01 5.82485371e+00 -2.65147201e-16]
[ -8.96738194e+00 -3.18911605e+00 1.41061647e-16]
[ 9.55390777e+00 -2.63573766e+00 -5.28988843e-16]]
第三列是噪聲(基本上為零),表明這些點位於2D平面中; 找不到第三主要成分。 前兩列與Matlab中的S匹配,除了符號選擇。
您對“帶cov的NumPy”的計算與sklearn相同,只是第三列中的隨機噪聲不同。 順便說一下,對於此計算,您不需要將數據居中。 cov
自行完成。 cov = np.cov(xT)
也可以工作。
[[ -5.86525831e-01 -5.82485371e+00 5.26721273e-16]
[ -8.96738194e+00 3.18911605e+00 3.83725073e-15]
[ 9.55390777e+00 2.63573766e+00 -3.35763132e-15]]
這里的主要思想是,我們將使用較小的C = np.dot(X, XT)
代替計算np.dot(XT, X)
(本質上是協方差,直到一個恆定因子)。 我們需要的基向量將通過將C的特征向量乘以XT
來獲得(如果您遵循Wikipedia的文章 ,請注意它們的T與X具有不同的方向)。 但是,與np.linalg.eig
返回的向量不同,這些向量沒有被np.linalg.eig
。 在使用之前,我們必須將它們標准化:
X = x - np.mean(x, axis = 0)
C = np.dot(X, X.T)
evals , evecs = np.linalg.eig(C)
idx = np.argsort(evals)[::-1]
evecs = evecs[:,idx]
evals = evals[idx]
v = np.dot(X.T, evecs)
v /= np.linalg.norm(v, axis=0)
res3 = X.dot(v)
這返回
[[-0.58652583 -5.82485371 5.05711518]
[-8.96738194 3.18911605 1.72266002]
[ 9.55390777 2.63573766 -6.7797752 ]]
在前兩列中是正確的。 同樣,第三列是噪聲,但是現在經過規范化的是噪聲,因此它一點也不小。 必須理解第三欄是沒有意義的。
我的猜測是,兩種計算特征向量的方法所得出的結果與scipy.linalg.svd不同,這是scipy的PCA實現所使用的( https://github.com/scikit-learn/scikit-learn/blob/f3320a6f/ sklearn / decomposition / pca.py#L399 )。
這可能是一個不錯的起點: 用numpy的eigh和svd計算的特征向量不匹配
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.