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 得到的结果非常接近。但是使用 "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)
我的猜测是,您计算特征向量的两种方法给出的结果与 scipy.linalg.svd 不同,这正是 scipy 的 PCA 实现所使用的 (https://github.com/scikit-learn/scikit-learn/blob/f3320a6f/sklearn/decomposition/pca.py#L399)。
这可能是一个很好的起点:
首先:这是什么意思?您在 4 维 space 中有三个点。它们跨越一个二维平面。 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 subspace 的基向量,S 的行是该子集中三个点的坐标。
Sklearn returns
[[ -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]]
其中第三列是噪声(基本上为零),反映出这些点位于二维平面内;没有找到第 3 个主成分。前两列与 Matlab 中的 S 匹配,但符号选择除外。
您对 "NumPy with cov" 的计算与 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]]
"Eigenface"接近
这里的主要思想是,我们将使用更小的 C = np.dot(X, X.T)
,而不是计算 np.dot(X.T, X)
(本质上是协方差,直到常数因子)。我们需要的基向量将通过将 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)
这个returns
[[-0.58652583 -5.82485371 5.05711518]
[-8.96738194 3.18911605 1.72266002]
[ 9.55390777 2.63573766 -6.7797752 ]]
前两列是正确的。同样,第三列是噪声,但现在是经过归一化处理的噪声,所以一点也不小。人们必须明白第三列是没有意义的。
我正在尝试使用 numpy.linalg.eig 和两种不同的方法(使用特征脸中使用的协方差和 pca 方法)来实现 PCA,并将我的结果与 sklearn 的 PCA 进行比较。但是我发现我的结果不同,所以我想知道我犯了哪个错误。我有 3 个样本,每个样本有 4 个特征。我正在尝试将样本的维度减少到 3。 编辑:使用 SVD 方法添加。我使用来自 sklearn 的协方差 PCA、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)
我的猜测是,您计算特征向量的两种方法给出的结果与 scipy.linalg.svd 不同,这正是 scipy 的 PCA 实现所使用的 (https://github.com/scikit-learn/scikit-learn/blob/f3320a6f/sklearn/decomposition/pca.py#L399)。
这可能是一个很好的起点:
首先:这是什么意思?您在 4 维 space 中有三个点。它们跨越一个二维平面。 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 subspace 的基向量,S 的行是该子集中三个点的坐标。
Sklearn returns
[[ -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]]
其中第三列是噪声(基本上为零),反映出这些点位于二维平面内;没有找到第 3 个主成分。前两列与 Matlab 中的 S 匹配,但符号选择除外。
您对 "NumPy with cov" 的计算与 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]]
"Eigenface"接近
这里的主要思想是,我们将使用更小的 C = np.dot(X, X.T)
,而不是计算 np.dot(X.T, X)
(本质上是协方差,直到常数因子)。我们需要的基向量将通过将 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)
这个returns
[[-0.58652583 -5.82485371 5.05711518]
[-8.96738194 3.18911605 1.72266002]
[ 9.55390777 2.63573766 -6.7797752 ]]
前两列是正确的。同样,第三列是噪声,但现在是经过归一化处理的噪声,所以一点也不小。人们必须明白第三列是没有意义的。