目录
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)
如本文章有错误,或者有其他见解,请在评论区进行讨论,感谢各位指教!