numpy 和 sklearn 中的 PCA 产生不同的结果

机器算法验证 主成分分析 Python scikit-学习
2022-01-21 00:20:09

我是不是误会了什么。这是我的代码

使用 sklearn

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from sklearn import decomposition
from sklearn import datasets
from sklearn.preprocessing import StandardScaler

pca = decomposition.PCA(n_components=3)

x = np.array([
        [0.387,4878, 5.42],
        [0.723,12104,5.25],
        [1,12756,5.52],
        [1.524,6787,3.94],
    ])
pca.fit_transform(x)

输出:

array([[ -4.25324997e+03,  -8.41288672e-01,  -8.37858943e-03],
   [  2.97275001e+03,  -1.25977271e-01,   1.82476780e-01],
   [  3.62475003e+03,  -1.56843494e-01,  -1.65224286e-01],
   [ -2.34425007e+03,   1.12410944e+00,  -8.87390454e-03]])

使用 numpy 方法

x_std = StandardScaler().fit_transform(x)
cov = np.cov(x_std.T)
ev , eig = np.linalg.eig(cov)
a = eig.dot(x_std.T)

输出

array([[ 0.06406894,  0.94063993, -1.62373172],
   [-0.35357757,  0.7509653 ,  0.63365168],
   [ 0.29312477,  0.6710958 ,  1.11766206],
   [-0.00361615, -2.36270102, -0.12758202]])

我保留了所有 3 个组件,但它似乎不允许我保留我的原始数据。

我可以知道为什么会这样吗?

如果我想取回我的原始矩阵,我该怎么办?

2个回答

不同之处在于decomposition.PCA,在进行 PCA 之前没有标准化您的变量,而在您的手动计算中,您调用StandardScaler进行标准化。因此,您正在观察这种差异:PCA 是相关性还是协方差?

如果你更换

pca.fit_transform(x)

x_std = StandardScaler().fit_transform(x)
pca.fit_transform(x_std)

您将获得与手动计算相同的结果...

...但仅取决于 PC 的顺序。那是因为当你跑

ev , eig = np.linalg.eig(cov)

你得到的特征值不一定按降序排列。我明白了

array([ 0.07168571,  2.49382602,  1.43448827])

因此,您将需要手动订购它们。Sklearn 会为您做到这一点。


关于重构原始变量,请参阅如何反转 PCA 并从几个主成分重构原始变量?

是一个很好的实现,其中讨论和解释了 Python 中的 PCA。此实现导致与 scikit PCA 相同的结果。这是您的 PCA 错误的另一个指标。

import numpy as np
from scipy import linalg as LA

x = np.array([
        [0.387,4878, 5.42],
        [0.723,12104,5.25],
        [1,12756,5.52],
        [1.524,6787,3.94],
    ])

#centering the data
x -= np.mean(x, axis = 0)  

cov = np.cov(x, rowvar = False)

evals , evecs = LA.eigh(cov)

您需要对特征值(和相应的特征向量)进行降序排序

idx = np.argsort(evals)[::-1]
evecs = evecs[:,idx]
evals = evals[idx]

a = np.dot(x, evecs) 

通常,我建议您通过实现一个简单的示例(尽可能简单)并手动计算正确的结果(和中间结果)来检查您的代码。这有助于您识别问题。