Инвертирующее преобразование PCA с помощью sklearn (с whiten=True)

обычно преобразование 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_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? Или это вообще возможно? Большое спасибо за любую помощь.

2 ответов


это поведение, по общему признанию, потенциально странно, но тем не менее документировано в документах соответствующих функций.

класс docstring 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.

код и docstring 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.

для того, чтобы наконец-то ответьте на свой вопрос: элемент PCA объект sklearn.декомпозиция не позволяет восстановить исходные данные из отбеленной матрицы,потому что сингулярные значения центрированных данных собственных значений ковариационной матрицы собирают мусор после функции 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

как вы можете видеть из строки creating inverse_transformed, если вы умножите сингулярные значения обратно на компоненты, вы можете вернуться в исходное пространство.

на самом деле, сингулярные значения S на самом деле скрыто в нормы компонентов, поэтому нет необходимости вычислять SVD вдоль стороны PCA. Используя определения выше, можно увидеть

S_recalculated = 1. / np.sqrt((pca.components_ ** 2).sum(axis=1) / n_samples_train)
assert_array_almost_equal(S, S_recalculated)

вывод: получив сингулярные значения центрированной матрицы данных, мы можем отменить отбеливание и преобразовать обратно в исходное пространство. Однако эта функциональность изначально не реализована в


self.components_ первоначально является Eignenvectors, которые подлежат

>>> np.allclose(self.components_.T, np.linalg.inv(self.components_))
True

в проект(transform на sklearn) к этим компонентам, PCA вычитание их self.mean_ и умножение self.components_ as

   Y = np.dot(X - self.mean_, self.components_.T) 
=> Y = (X - mean) * V.T # rewritten for simple notation

здесь X - это образцы, mean является средним из учебных образцов, и V является основными компонентами.

затем реконструкция(inverse_transform на sklearn) следующим образом (к get Y С X)

   Y = (X - mean) * V.T
=> Y*inv(V.T) = X - mean
=> Y*V = X - mean # inv(V.T) = V
=> X = Y*V + mean
=> Xrec = np.dot(X, self.components_) + self.mean_

проблема self.components_ of whiten PCA не подлежит

>>> np.allclose(self.components_.T, np.linalg.inv(self.components_))
False

вы можете вывести причину почему из кода @eickenberg.

Итак, вам нужно изменить sklearn.decomposition.pca

  1. код сохраняет the reconstruction matrix. self.components_ of whiten PCA и

    self.components_ = V / S[:, np.newaxis] * np.sqrt(n_samples)
    

    таким образом, мы можем назначить the reconstruction matrix as

    self.recons_ = V * S[:, np.newaxis] / np.sqrt(n_samples)
    
  2. когда inverse_transform вызывается, мы вернем результат, полученный этой матрицей, как

    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

извините за мой английский. Пожалуйста, улучшите этот пост, я не уверен, что выражения тезисов верны.