这种行为可能很奇怪,但它仍然记录在相关函数的文档字符串中。
PCA 的类文档字符串对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_
现在看看whiten=True在函数PCA._fit中会发生什么:
if self.whiten:
self.components_ = V / S[:, np.newaxis] * np.sqrt(n_samples)
else:
self.components_ = V
其中S 是奇异值,V 是奇异向量。根据定义,白化对频谱进行水平处理,本质上是将协方差矩阵的所有特征值设置为1。
为了最终回答您的问题:sklearn.decomposition 的PCA 对象不允许从白化矩阵重建原始数据,因为居中数据的奇异值 /协方差矩阵的特征值在函数PCA._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
从创建inverse_transformed的行可以看出,如果将奇异值乘回分量,则可以返回原始空间。
事实上,奇异值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 的代码(如果社区认为有用,可以正式完成),您正在寻找的解决方案是这样的(并且我现在将使用您的代码和变量名,请检查这是否适合您):
transformed_a = p.transform(a)
singular_values = 1. / np.sqrt((p.components_ ** 2).sum(axis=1) / len(x))
inverse_transformed = np.dot(transformed_a, singular_values[:, np.newaxis] ** 2 *
p.components_ / len(x)) + p.mean_)
(恕我直言,任何估计器的inverse_transform 函数都应该尽可能接近原始数据。在这种情况下,显式存储奇异值也不会花费太多,所以也许这个功能实际上应该添加到sklearn。)
编辑中心矩阵的奇异值不像最初想象的那样被垃圾收集。事实上,它们存储在pca.explained_variance_ 中,可以用于反白。见 cmets。