我正在尝试使用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)
答案 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