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

Ответ 1

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

Класс 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.decomposition не позволяет восстановить исходные данные из отбеленной матрицы, , поскольку сингулярные значения центрированных данных/собственные значения ковариационной матрицы представляют собой мусор, собранный после функции 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 фактически скрыты в нормах компонентов, поэтому нет необходимости вычислять SVD вдоль стороны PCA. Используя приведенные выше определения, можно увидеть

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_)

(IMHO функция inverse_transform любой оценки должна вернуться как можно ближе к исходным данным. В этом случае не стоило бы слишком сильно хранить сингулярные значения, поэтому, возможно, эта функциональность действительно должна быть добавлена ​​к sklearn.)

EDIT Особые значения центрированной матрицы не являются сборкой мусора, как первоначально предполагалось. На самом деле, они хранятся в pca.explained_variance_ и могут использоваться для небезопасности. См. Комментарии.

Ответ 2

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

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

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

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

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

Тогда реконструкция (inverse_transform in sklearn) выглядит следующим образом (чтобы получить 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_ whiten PCA не подвержена

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

Вы можете определить причину почему из кода @eickenberg.

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

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

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

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

    self.recons_ = V * S[:, np.newaxis] / np.sqrt(n_samples)
    
  • Когда вызывается 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

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