在sklearn中使用LDA降维,并解决输出维度与要求维度不相等的问题

目录

1.sklearn中LDA的简单使用方法

2.维度不一致问题


1.sklearn中LDA的简单使用方法

最近在对数据进行预处理的过程中,使用了有监督的降维方式——线性判别分析(LDA)。直接能通过调用sklearn提供的接口就能实现。具体导入方式如下:

from sklearn.discriminant_analysis import LinearDiscriminantAnalysis

使用方法也很简单,如下所示:

lda_model = LinearDiscriminantAnalysis(n_components=dim_out)
lda_model.fit(data, label)
data_after_lda = lda_model.transform(data)

其中,

  • dim_out指的是指定降维后的维度(这里需要注意的是,该维度必须小于等于类别数减一,举个例子,如果你的数据集包含100个类别,那降维后的数据的维度最多只能到99维);
  • data指的是原始需要降维的数据;
  • label指的是原始数据的标签值。

或者另外一种使用方式:

lda_model = LinearDiscriminantAnalysis(n_components=dim_out)
data_after_lda = lda_model.fit_transform(data, label)

这两种方式得到的结果都一样,fit就是用数据将模型需要的参数解算出来,有点类似于训练。transform就是对数据进行降维。fit_transform就是两者一起做。

2.维度不一致问题

问题的具体描述:参照上面的代码块,将dim_out设置为32,但是输出的降维后的数据维度是28。排除了是由于类别数或者样本数过小的问题。

分析:既然排除了设置参数的问题,那么大概率问题是出自数据本身以及sklearn源码。

于是我查看了transform的源码,如下:

def transform(self, X):
    """Project data to maximize class separation.

    Parameters
    ----------
    X : array-like of shape (n_samples, n_features)
        Input data.

    Returns
    -------
    X_new : ndarray of shape (n_samples, n_components) or \
        (n_samples, min(rank, n_components))
        Transformed data. In the case of the 'svd' solver, the shape
        is (n_samples, min(rank, n_components)).
    """
    if self.solver == "lsqr":
        raise NotImplementedError(
            "transform not implemented for 'lsqr' solver (use 'svd' or 'eigen')."
        )
    check_is_fitted(self)
    xp, _ = get_namespace(X)
    X = self._validate_data(X, reset=False)

    if self.solver == "svd":
        X_new = (X - self.xbar_) @ self.scalings_
    elif self.solver == "eigen":
        X_new = X @ self.scalings_

    return X_new[:, : self._max_components]

可以看到,sklearn默认的解算方法采用的是SVD即奇异值分解。同时也注意到有一个self.scalings的参数存在,大概率就是这个参数的原因了。于是,找到该参数定义的代码块。这里只截取部分:

# (n_classes) centers
_, S, Vt = svd(X, full_matrices=False)
rank = xp.sum(xp.astype(S > self.tol * S[0], xp.int32)

scalings = (Vt[:rank, :] / std).T / S[:rank]
self.scalings_ = scalings @ Vt.T[:, :rank]

可以看到,scalings的的选取和rank有关,svd使用的是linalg.svd方法,返回的S矩阵是奇异值矩阵,即对角矩阵。

而rank其实就是经过奇异值分解之后,大于某一个数值的奇异值的个数。奇异值越大,代表该奇异值所占原始数据信息比重更大,所以剔除掉过小的奇异值,有助于运算,并且基本不会损失多少信息。

由此,上述问题产生的原因得到了解释。解决方式也很简单,在创建lda_model的时候,手动传入tol参数,将其改小就行,如下。(改小后的影响需要结合具体数据与实验自己进行分析,或不使用SVD进行解算

lda_model = LinearDiscriminantAnalysis(n_components=dim_out, tol=1e-15)

如本文章有错误,或者有其他见解,请在评论区进行讨论,感谢各位指教!

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
LDA(线性判别分析)是一种经典的有监督降维方法,它可以将高维数据映射到一个低维空间,以便于分类和可视化。下面是一个简单的 Python 实现: 1.首先,我们需要计算类内散度矩阵 Sw 和类间散度矩阵 Sb。 ```python import numpy as np def compute_scatter_matrices(X, y): # 计算均值向量 class_labels = np.unique(y) n_classes = len(class_labels) n_features = X.shape[1] mean_vectors = [] for cl in class_labels: mean_vectors.append(np.mean(X[y==cl], axis=0)) # 计算类内散度矩阵 Sw = np.zeros((n_features, n_features)) for cl,mv in zip(class_labels, mean_vectors): class_sc_mat = np.zeros((n_features, n_features)) # scatter matrix for every class for row in X[y == cl]: row, mv = row.reshape(n_features,1), mv.reshape(n_features,1) # make column vectors class_sc_mat += (row-mv).dot((row-mv).T) Sw += class_sc_mat # sum class scatter matrices # 计算类间散度矩阵 overall_mean = np.mean(X, axis=0) Sb = np.zeros((n_features, n_features)) for i,mean_vec in enumerate(mean_vectors): n = X[y==class_labels[i]].shape[0] mean_vec = mean_vec.reshape(n_features,1) # make column vector overall_mean = overall_mean.reshape(n_features,1) # make column vector Sb += n * (mean_vec - overall_mean).dot((mean_vec - overall_mean).T) return Sw, Sb ``` 2.然后,我们需要计算 Sw 的逆矩阵和 Sw 和 Sb 的乘积。 ```python def lda(X, y, n_components): Sw, Sb = compute_scatter_matrices(X, y) eig_vals, eig_vecs = np.linalg.eig(np.linalg.inv(Sw).dot(Sb)) eig_pairs = [(np.abs(eig_vals[i]), eig_vecs[:,i]) for i in range(len(eig_vals))] eig_pairs = sorted(eig_pairs, key=lambda k: k[0], reverse=True) W = np.hstack([eig_pairs[i][1].reshape(len(X[0]),1) for i in range(n_components)]) return X.dot(W) ``` 这个函数将返回一个降维后的特征矩阵,其 n_components 是我们想要的输出维度数。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值