PCA与python numpy和sklearn之间的区别

时间:2017-11-17 19:45:59

标签: python numpy pca

我正在尝试使用numpy.linalg.eig和两个不同的方法(使用协方差和特征脸中使用的pca方法)来实现PCA,并将我的结果与sklearn中的PCA进行比较。但我观察到我的结果不同,所以我想知道我在做什么错。我有3个样本,每个样本有4个特征。我试图将样本的维度减少到3。 编辑:使用SVD方法添加。我使用协方差PCA得到的结果,来自sklearn的SVD和PCA非常接近。但是使用" eigenface"方法它完全不同为什么?     导入numpy为np     来自sklearn.decomposition导入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)

2 个答案:

答案 0 :(得分:2)

首先:这是什么意思?你在四维空间中有三个点。它们跨越二维平面。 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的行是该子集中三个点的坐标。

Sklearn返回

[[ -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相同,只是第3列中的随机噪声是不同的。顺便说一句,对于这种计算,您不需要使数据居中; cov自己做。 cov = np.cov(x.T)也可以正常运作。

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

“特征脸”方法

这里的主要想法是,不是计算np.dot(X.T, X)(基本上是协方差,直到常数因子),我们将使用更小的C = np.dot(X, X.T)。我们需要的基矢量将通过将C的特征向量乘以X.T来获得(如果您跟随Wikipedia's article,请注意它们的T与X的方向不同)。但是,与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 ]]

在前两列中是正确的。同样,第三列是噪音,但现在它是通过标准化的噪音,因此它根本不小。人们必须明白,第三列毫无意义。

答案 1 :(得分:1)

我的猜测是你的两种计算特征向量的方法给出的结果与scipy.linalg.svd不同,后者是scipy的PCA实现使用的(https://github.com/scikit-learn/scikit-learn/blob/f3320a6f/sklearn/decomposition/pca.py#L399)。

这可能是一个很好的起点:Eigenvectors computed with numpy's eigh and svd do not match