簡體   English   中英

python numpy和sklearn之間的PCA差異

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM