在scikit-learn中仅对特征子集使用PCA的管道
我有一组特征想要建模,其中有一个特征是一个在100个不同点上采样的直方图。也就是说,这个直方图特征实际上包含了100个不同的特征。我想通过对直方图特征进行主成分分析(PCA)来减少建模问题的维度,但我不想把其他特征也包括在PCA中,以便保持模型的可解释性。
理想情况下,我想建立一个管道,使用PCA来转换直方图特征,然后用支持向量分类器(SVC)来进行拟合,最后将这个管道输入到GridSearchCV中,以确定SVC的超参数。在这种设置中,有没有办法让PCA只转换我的特征子集(即直方图的各个区间)呢?最简单的方法是修改PCA对象,让它接受一个特征掩码,但我更希望能使用现有的功能。
编辑
在实现了@eickenberg的答案后,我意识到我还想要一个新的PCA类的逆转换方法。这个方法可以用来重新创建最初的特征集,并保持列的原始顺序。下面提供了这个方法,供其他感兴趣的人参考:
def inverse_transform(self, X):
if self.mask is not None:
# Inverse transform appropriate data
inv_mask = np.arange(len(X[0])) >= sum(~self.mask)
inv_transformed = self.pca.inverse_transform(X[:, inv_mask])
# Place inverse transformed columns back in their original order
inv_transformed_reorder = np.zeros([len(X), len(self.mask)])
inv_transformed_reorder[:, self.mask] = inv_transformed
inv_transformed_reorder[:, ~self.mask] = X[:, ~inv_mask]
return inv_transformed_reorder
else:
return self.pca.inverse_transform(X)
2 个回答
你可以使用 ColumnTransformer:
https://scikit-learn.org/stable/modules/generated/sklearn.compose.ColumnTransformer.html
pca_transformer = ColumnTransformer([('pca', PCA(), pca_columns)], remainder="passthrough")
pipe = Pipeline(steps=[('pca_transformer', pca_transformer), ('logistic', logistic)])
这件事在使用scikit-learn时,不能直接做到。为了充分利用Pipeline
和GridSearchCV
的所有功能,你可以考虑创建一个叫MaskedPCA
的对象,这个对象要从sklearn.base.BaseEstimator
继承,并且需要实现fit
和transform
这两个方法。在这个对象里,你应该对被遮罩的特征使用一个PCA
对象。遮罩应该在创建这个对象的时候传入。
from sklearn.base import BaseEstimator, TransformerMixin
from sklearn.decomposition import PCA
class MaskedPCA(BaseEstimator, TransformerMixin):
def __init__(self, n_components=2, mask=None):
# mask should contain selected cols. Suppose it is boolean to avoid code overhead
self.n_components = n_components
self.mask = mask
def fit(self, X):
self.pca = PCA(n_components=self.n_components)
mask = self.mask
mask = self.mask if self.mask is not None else slice(None)
self.pca.fit(X[:, mask])
return self
def transform(self, X):
mask = self.mask if self.mask is not None else slice(None)
pca_transformed = self.pca.transform(X[:, mask])
if self.mask is not None:
remaining_cols = X[:, ~mask]
return np.hstack([remaining_cols, pca_transformed])
else:
return pca_transformed
你可以在一些生成的数据上进行测试
import numpy as np
X = np.random.randn(100, 20)
mask = np.arange(20) > 4
mpca = MaskedPCA(n_components=2, mask=mask)
transformed = mpca.fit(X).transform(X)
# check whether first five columns are equal
from numpy.testing import assert_array_equal
assert_array_equal(X[:, :5], transformed[:, :5])
注意现在transformed
的列数是(~mask).sum + mpca.n_components == 7