Python 2.7 使用sklearn反演PCA变换(whiten=True)
通常,PCA变换很容易反转:Python 2.7 使用sklearn反演PCA变换(whiten=True),python-2.7,scikit-learn,pca,Python 2.7,Scikit Learn,Pca,通常,PCA变换很容易反转: import numpy as np from sklearn import decomposition x = np.zeros((500, 10)) x[:, :5] = random.rand(500, 5) x[:, 5:] = x[:, :5] # so that using PCA would make sense p = decomposition.PCA() p.fit(x) a = x[5, :] print p.inverse_trans
import numpy as np
from sklearn import decomposition
x = np.zeros((500, 10))
x[:, :5] = random.rand(500, 5)
x[:, 5:] = x[:, :5] # so that using PCA would make sense
p = decomposition.PCA()
p.fit(x)
a = x[5, :]
print p.inverse_transform(p.transform(a)) - a # this yields small numbers (about 10**-16)
现在,如果我们尝试添加whiten=True参数,结果将完全不同:
p = decomposition.PCA(whiten=True)
p.fit(x)
a = x[5, :]
print p.inverse_transform(p.transform(a)) - a # now yields numbers about 10**15
所以,由于我没有找到任何其他的方法可以做到这一点,我想知道如何才能得到a的原始值?或者甚至有可能吗?非常感谢您的帮助。这种行为无疑有可能很奇怪,但相关函数的文档字符串中记录了它
PCA
的类docstring说明了以下关于whiten
:
whiten : bool, optional
When True (False by default) the `components_` vectors are divided
by n_samples times singular values to ensure uncorrelated outputs
with unit component-wise variances.
Whitening will remove some information from the transformed signal
(the relative variance scales of the components) but can sometime
improve the predictive accuracy of the downstream estimators by
making there data respect some hard-wired assumptions.
PCA.inverse_transform
的代码和文档字符串表示:
def inverse_transform(self, X):
"""Transform data back to its original space, i.e.,
return an input X_original whose transform would be X
Parameters
----------
X : array-like, shape (n_samples, n_components)
New data, where n_samples is the number of samples
and n_components is the number of components.
Returns
-------
X_original array-like, shape (n_samples, n_features)
Notes
-----
If whitening is enabled, inverse_transform does not compute the
exact inverse operation as transform.
"""
return np.dot(X, self.components_) + self.mean_
现在看看在函数PCA中whiten=True
时会发生什么情况
if self.whiten:
self.components_ = V / S[:, np.newaxis] * np.sqrt(n_samples)
else:
self.components_ = V
其中,S
为奇异值,V
为奇异向量。根据定义,白化对光谱进行分级,基本上将协方差矩阵的所有特征值设置为1
为了最后回答您的问题,sklearn.decomposition的PCA
对象不允许从白化矩阵重建原始数据,因为中心数据的奇异值/协方差矩阵的特征值在函数PCA.\u fit
后被垃圾收集
但是,如果手动获取奇异值S
,则可以将其相乘并返回原始数据
试试这个
import numpy as np
rng = np.random.RandomState(42)
n_samples_train, n_features = 40, 10
n_samples_test = 20
X_train = rng.randn(n_samples_train, n_features)
X_test = rng.randn(n_samples_test, n_features)
from sklearn.decomposition import PCA
pca = PCA(whiten=True)
pca.fit(X_train)
X_train_mean = X_train.mean(0)
X_train_centered = X_train - X_train_mean
U, S, VT = np.linalg.svd(X_train_centered, full_matrices=False)
components = VT / S[:, np.newaxis] * np.sqrt(n_samples_train)
from numpy.testing import assert_array_almost_equal
# These assertions will raise an error if the arrays aren't equal
assert_array_almost_equal(components, pca.components_) # we have successfully
# calculated whitened components
transformed = pca.transform(X_test)
inverse_transformed = transformed.dot(S[:, np.newaxis] ** 2 * pca.components_ /
n_samples_train) + X_train_mean
assert_array_almost_equal(inverse_transformed, X_test) # We have equality
正如您从创建逆变换
的行中所看到的,如果将奇异值乘以组件,则可以返回原始空间
事实上,奇异值S
实际上隐藏在组件的范数中,因此不需要沿着PCA
计算SVD。使用上面的定义可以看到
S_recalculated = 1. / np.sqrt((pca.components_ ** 2).sum(axis=1) / n_samples_train)
assert_array_almost_equal(S, S_recalculated)
结论:通过获得中心数据矩阵的奇异值,我们能够撤销白化并转换回原始空间。但是,此功能不是在PCA
对象中本机实现的
补救措施:在不修改scikit learn的代码的情况下(如果社区认为有用,可以正式进行),您正在寻找的解决方案是(现在我将使用您的代码和变量名,请检查这是否适用于您):
(任何估计器的逆变换
函数应尽可能回到原始数据。在这种情况下,显式存储奇异值不会花费太多,因此,可能该功能实际上应该添加到sklearn中。)
编辑居中矩阵的奇异值不会像最初认为的那样被垃圾收集。事实上,它们存储在pca.explained\u variance\uuu
中,可用于解除伤害。请参阅注释。self.components\u
最初是受
>>> np.allclose(self.components_.T, np.linalg.inv(self.components_))
True
>>> np.allclose(self.components_.T, np.linalg.inv(self.components_))
False
要将(transform
insklearn
)投影到这些组件,PCA减去它们的self.mean,然后乘以它们的self.components
Y = np.dot(X - self.mean_, self.components_.T)
=> Y = (X - mean) * V.T # rewritten for simple notation
self.recons_ = V * S[:, np.newaxis] / np.sqrt(n_samples)
其中,X
是样本,mean
是训练样本的平均值,V
是主成分
然后,重构(sklearn
中的逆变换
)如下所示(从X
获取Y
)
问题是self.whiten
PCA的组件不受
>>> np.allclose(self.components_.T, np.linalg.inv(self.components_))
True
>>> np.allclose(self.components_.T, np.linalg.inv(self.components_))
False
您可以从@Eikenberg的代码中导出原因为什么
因此,您需要修改sklearn.decomposition.pca
代码保留重建矩阵
<代码>自身。美白PCA的组件
为
self.components_ = V / S[:, np.newaxis] * np.sqrt(n_samples)
因此,我们可以将重构矩阵分配为
Y = np.dot(X - self.mean_, self.components_.T)
=> Y = (X - mean) * V.T # rewritten for simple notation
self.recons_ = V * S[:, np.newaxis] / np.sqrt(n_samples)
调用逆_变换
时,我们将返回由该矩阵导出的结果,如下所示
if self.whiten:
return np.dot(X, self.recons_) + self.mean_
就这样。让我们测试一下
>>> p = decomposition.PCA(whiten=True)
>>> p.fit(x)
>>> np.allclose(p.inverse_transform(p.transform(a)), a)
True
对不起我的英语。请改进此帖子,我不确定这些表达式是否正确。PCA
对象有一个关键字参数n\u components
。如果设置了此选项,则您的第一个条件将不会呈现True
(同样在SVD中,关键字full\u matrix=False
可能导致非平方矩阵)。您的解决方案似乎向scikit学习包添加了代码。如果您感兴趣,您应该通过向社区提出请求的方式向社区提出。但是,可能需要进行一些更改,因为您的代码现在的方式是复制组件,这是低效的。一旦删除,这可能会很有趣。再次查看您的帖子,您可能应该使用inv
编辑掉调用以反转组件
向量,因为它通常没有很好的定义。伪逆pinv
将在非平方情况下工作,但我不确定结果是否理想。非常感谢,这非常有效。我以前在R中使用PCA,无论组件是否被重新调整为具有单位方差,它都能够反转转换,因此我非常惊讶地发现sklearn无法这样做。我也很惊讶。它被明确记录的事实表明,它背后有一个原因。如果没有,我认为应该更改。重新阅读您的评论,准确地说,增白不仅仅是将组件转换为单位方差(这可以通过sklearn.preprocessing.StandardScaler轻松完成,也可以轻松地进行逆变换)。这是高斯意义上的去相关:白色特征的协方差矩阵将是对角的。顺便说一句,实际上不需要知道len(x)
参数(它被取消了)