线性判别分析逆变换

5
我尝试使用scikit-learn库中的线性判别分析来对具有超过200个特征的数据进行降维。但是我在LDA类中找不到inverse_transform函数。
我想问一下,如何从LDA域中的一个点重构原始数据?
根据@bogatron和@kazemakase的答案进行编辑:
我认为术语“原始数据”是错误的,而应该使用“原始坐标”或“原始空间”。我知道如果没有所有PCA,我们无法重构原始数据,但是当我们构建形状空间时,我们通过PCA的帮助将数据投影到较低的维度。 PCA尝试用仅2或3个组件解释数据,这些组件可以捕获大部分数据的方差,如果我们基于它们重构数据,它应该显示导致此分离的形状的部分。
我再次检查了scikit-learn LDA的源代码,发现特征向量存储在scalings_变量中。当使用svd求解器时,无法反转特征向量(scalings_)矩阵,但是当我尝试使用矩阵的伪逆时,我可以重构形状。
这里有两个图像,分别从[4.28, 0.52]和[0, 0]点重构而来:

from [ 4.28, 0.52] from [0, 0]

我认为,如果有人深入解释LDA逆变换的数学限制,那将是非常好的。

2个回答

3

一般而言,不存在逆转换,因为您无法从较低维度的特征空间返回到原始坐标空间。

可以将其视为在墙上投影出的二维阴影。 从单个阴影中无法返回三维几何信息,因为在投影过程中会丢失信息。

关于PCA的评论,考虑一个由10个随机三维向量组成的数据集:

In [1]: import numpy as np

In [2]: from sklearn.decomposition import PCA

In [3]: X = np.random.rand(30).reshape(10, 3)

如果我们应用主成分转换(PCT)并通过仅保留前两个(三个中的两个)PC来进行降维,然后应用逆变换,会发生什么呢?

In [4]: pca = PCA(n_components=2)

In [5]: pca.fit(X)
Out[5]: 
PCA(copy=True, iterated_power='auto', n_components=2, random_state=None,
  svd_solver='auto', tol=0.0, whiten=False)

In [6]: Y = pca.transform(X)

In [7]: X.shape
Out[7]: (10, 3)

In [8]: Y.shape
Out[8]: (10, 2)

In [9]: XX = pca.inverse_transform(Y)

In [10]: X[0]
Out[10]: array([ 0.95780971,  0.23739785,  0.06678655])

In [11]: XX[0]
Out[11]: array([ 0.87931369,  0.34958407, -0.01145125])

显然,逆变换未能重建原始数据。原因是通过删除最低的主成分,我们丢失了信息。接下来,让我们看看如果保留全部主成分(即不应用任何降维),会发生什么:

In [12]: pca2 = PCA(n_components=3)

In [13]: pca2.fit(X)
Out[13]: 
PCA(copy=True, iterated_power='auto', n_components=3, random_state=None,
  svd_solver='auto', tol=0.0, whiten=False)

In [14]: Y = pca2.transform(X)

In [15]: XX = pca2.inverse_transform(Y)

In [16]: X[0]
Out[16]: array([ 0.95780971,  0.23739785,  0.06678655])

In [17]: XX[0]
Out[17]: array([ 0.95780971,  0.23739785,  0.06678655])

在这种情况下,我们能够重建原始数据,因为我们没有丢弃任何信息(因为我们保留了所有的主成分)。
LDA的情况更糟,因为可以保留的最大组件数量不是200(输入数据的特征数量),而是n_classes - 1。因此,例如,如果您正在进行二元分类问题(2个类),则LDA变换将从200个输入维度减少到仅一个维度。

不,这不是真的,一般情况下是可以做到的!例如,您可以在执行PCA [PCA inverse_transform]之后重建数据(http://scikit-learn.org/stable/modules/generated/sklearn.decomposition.PCA.html#sklearn.decomposition.PCA.inverse_transform)。 - Babak Hashemi
只有在不进行降维的情况下才是普遍真实的。在你的问题中,你提到了“进行降维”。主成分转换只是坐标的平移(如果需要进行均值减法)和旋转,因此你可以应用反向旋转和平移来获取回原始数据。但是,如果你减少了组件的数量,你将会失去信息(除非被删除的组件具有零方差的特殊情况),因此你将无法获得回原始数据。 - bogatron
1
我不知道你是否熟悉形状空间。基本上,你选择一个点在2D或3D中,并在形状的原始空间中重构形状,这意味着只使用2或3个主成分(具有数据最大变量)就可以猜出形状,无需知道其余的成分。也许我应该使用“原始空间”而不是“原始数据”。 - Babak Hashemi
1
不,我不熟悉形状空间,但我认为你在“原始空间”和“原始数据”之间的区别上是核心问题。对于具有降维功能的PCA,您可以返回到原始空间,因为这只是在反向变换中将与已删除的特征值/向量相关联的系数归零的问题。对于LDA,您没有一个200维的转换空间,可以在其中将系数归零以转换回原始空间。 - bogatron

3
LDA的逆矩阵通常没有意义,因为它会损失很多信息。
相比之下,考虑PCA。在这里,我们得到一个系数矩阵用于转换数据。我们可以通过从矩阵中去除行来进行降维。要获得逆变换,我们首先要反转整个矩阵,然后删除对应已移除行的列。
LDA并不给我们一个完整的矩阵。我们只得到一个无法直接反转的简化矩阵。虽然可以使用伪逆,但这远不如如果我们拥有完整矩阵那么高效。
考虑一个简单的例子:
C = np.ones((3, 3)) + np.eye(3)  # full transform matrix
U = C[:2, :]  # dimensionality reduction matrix
V1 = np.linalg.inv(C)[:, :2]  # PCA-style reconstruction matrix
print(V1)
#array([[ 0.75, -0.25],
#       [-0.25,  0.75],
#       [-0.25, -0.25]])

V2 = np.linalg.pinv(U)  # LDA-style reconstruction matrix
print(V2)
#array([[ 0.63636364, -0.36363636],
#       [-0.36363636,  0.63636364],
#       [ 0.09090909,  0.09090909]])

如果我们拥有完整的矩阵,我们会得到一个不同的逆变换(V1),而不是简单地反转变换(V2)。这是因为在第二种情况下,我们失去了关于被丢弃的组件的所有信息。
你已经被警告了。如果你仍然想要进行逆LDA变换,这里有一个函数:
import matplotlib.pyplot as plt

from sklearn import datasets
from sklearn.decomposition import PCA
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis

from sklearn.utils.validation import check_is_fitted
from sklearn.utils import check_array, check_X_y

import numpy as np


def inverse_transform(lda, x):
    if lda.solver == 'lsqr':
        raise NotImplementedError("(inverse) transform not implemented for 'lsqr' "
                                  "solver (use 'svd' or 'eigen').")
    check_is_fitted(lda, ['xbar_', 'scalings_'], all_or_any=any)

    inv = np.linalg.pinv(lda.scalings_)

    x = check_array(x)
    if lda.solver == 'svd':
        x_back = np.dot(x, inv) + lda.xbar_
    elif lda.solver == 'eigen':
        x_back = np.dot(x, inv)

    return x_back


iris = datasets.load_iris()

X = iris.data
y = iris.target
target_names = iris.target_names

lda = LinearDiscriminantAnalysis()
Z = lda.fit(X, y).transform(X)

Xr = inverse_transform(lda, Z)

# plot first two dimensions of original and reconstructed data
plt.plot(X[:, 0], X[:, 1], '.', label='original')
plt.plot(Xr[:, 0], Xr[:, 1], '.', label='reconstructed')
plt.legend()

enter image description here

你看,逆变换的结果与原始数据没有太多关系(嗯,可能可以猜测投影的方向)。相当一部分的变异已经消失了。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接