1 什么是特征筛选(Feature Selection)
特征筛选:从给定的特征集合中选择出相关特征子集的过程,称为“特征筛选”
特征筛选的目的:1 降低维度灾难;2 去除不相关特征,降低学习任务难度,加速模型训练速度
例如,全基因组关联分析(genome-wide association studies,GWAS)中使用的典型病例对照基因型数据集可以包含多达一百万个 SNP,但样本却只有几千个或者更少。SNP的特征数量要远远大于样本数量。原始数据集可能包含过多的特征和大量不相关的 SNP,精简后的数据集包含相关 SNP(rSNP),可用于训练学习算法。虽然随着维数的增加,分类器的性能会不断提高,直到达到最佳特征数量。在不增加训练样本数量的情况下进一步增加维数会导致分类器性能下降
2 什么是维数灾难( Curse of Dimensionality )
维数灾难:随着数据维数呈指数增加,算法的效率和有效性会下降的现象。维数灾难会导致计算复杂度增加、过拟合和虚假相关性
其实维数灾难说人话就是特征数量远远多于样本数量。Quora上有一个维数灾难的形象举例,如果一在条100米的直线上掉了一枚硬币你可能很容易找到,但是如果你在一个足球场找一枚硬币呢,以及在一个30层的建筑里面找一枚硬币呢。维数增加后学习任务的难度也增加了
举一个根据特征区分猫狗的例子:
1、单一特征(如猫狗图片中平均红色占比)并不能完美地分离训练数据
2、添加第二个特征仍然不会产生线性可分离的分类问题:在此示例中,没有一条线可以将所有猫与所有狗分开
3、在上面示例中,添加第三个特征会导致线性可分离。存在一个超平面,可以完美区分狗和猫
4、高维分类结果投影回低维空间,这种方法的一个严重问题就会变得明显:分类器开始学习特定于训练数据的异常,并且在遇到新数据时不能很好地概括(过拟合)
3 维度灾难与数据的稀疏性
1、在高维空间中,数据点变得稀疏,由于需要大量数据才能对空间进行充分采样,因此很难辨别有意义的模式或关系
2、如果理论上有无限数量的训练样本,则维数灾难就不适用了,可以简单地使用无限数量的特征来实现完美的分类。训练数据的大小越小,应该使用的特征就越少
举一个栗子:
1、如果一个特征值(平均红色)对于猫和狗都是唯一的,并且平均分布,如果我们希望训练数据覆盖这个范围的20%,所需的训练数据就要占到猫狗总数的20%
2、如果是2个特征,需要覆盖20%的2D特征,每个维度上就要获得45%的猫狗总数,因为45%的平方是20%
3、如果是3个特征,需要覆盖30%的3D特征,每个维度需要获得58%的总数,因为58%的立方是20%
4、在上面的例子中,表明维数灾难引入了训练数据的稀疏性。我们使用的特征越多,数据就越稀疏,以至于准确估计分类器的参数(即其决策边界)变得更加困难
4 特征选择的方法
特征选择的方法包括过滤法,包裹法和嵌入法。
1、过滤法对每个特征进行独立评估,选择最相关的特征,与最终学习器无关。所使用的一般是相关性分析、统计检验、互信息等方法
2、包裹法使用机器学习算法评估特征子集的效果,选择能提高模型表现的特征子集,反复训练模型来评估子集的优劣,使用的方法一般是递归特征消除
3、嵌入法是在训练模型的过程中进行特征选择,最著名的方法是Lasso回归和决策树
4.1 过滤法(Filter)
过滤法:按离散程度或相关性对各个特征进行评分,设定阈值选择特征。过滤法先对数据集进行特征选择,然后再训练学习器,特征选择过程与后续学习器无关
现有的过滤方法大致可分为单变量方法和多变量方法。单变量方法单独测试每个特征,而多变量方法则同时考虑特征子集。
如上面的表格可采用单变量的方法进行过滤:
1、salary列: 包含多个0值(如第4、5、7、10行),可能存在异常数据,需要清理或进一步分析
2、如果是特征的方差值为0,可以过滤掉
3、如果特征中某一类的类型占比过高,可以过滤掉
4、如果特征中含有较多的缺失值,可以进行过滤
多变量方法需要考虑自变量和因变量之间的关系。
自变量与自变量之间的相关性: 相关性越高,会引发多重共线性问题,进而导致模型稳定性变差,样本微小扰动都会带来大的参数变化,建议在具有共线性的特征中选择一个即可,其余剔除
自变量和因变量之间的相关性: 相关性越高,说明特征对模型预测目标更重要,建议保留。
Pearson,Spearman, Kendall 三种相关性方法以及 Chi-Squared 检验较为常用,下面只以方差分析 ANOVA和互信息 Mutual Information 为例进行介绍。
方差分析(ANOVA):
这里的 F 统计量,后面会用到,可以作为相关性特征重要性的一个值。
互信息(Mutual Information):
最大相关,最小冗余(mRMR):
互信息的应用是mRMR。
1、公式1 互信息可以度量两个变量x,y之间的关系
2、公式2,根据最大相关筛选出平均互信息最大的集合D,特征的数量是S,c代表类别,X代表特征;这样筛选出来的特征大概率是冗余的
3、公式3,根据最小冗余筛选出平均互信息最小的集合R
4、公式4,最大相关和最小冗余就是同时优化这两个数据集
Relief 方法:
1、H是同类型的“猜中近邻” nearest hit ; M是不同类型的“猜错近邻”;diff值需要看属性的特征,如果属性为离散型变量,diff为0,或者为1;如果属性为连续型变量,diff值为绝对值,这个diff 的度量可以用欧几里得距离,也可用曼哈顿距离
2、如果属性A与猜中近邻的之间的差异,小于猜错近邻之间的差距,说明属性A对区分同类和异类是有益的,所以属性A对应的统计分量应该增大;反之,属性A的统计分量应该减少;分量值越大,对应属性的分类能力就越小
3、ReliefF 中的思想还是类似的,不过它有多个类别,比如先在第C类中找最近邻,在C类之外找最近邻,就算统计信息
过滤方法汇总:
过滤方法示例代码:
your_dataset.csv的格式为:
1、预处理数据(去除无变异特征、填补缺失值、标准化)
2、特征选择(SelectKBest 选择 20 个最具区分力的特征),这里的score_func=f_classif 就是用的方差分析的F统计量
3、降维(PCA 将 20 个特征降维至 10 维)
4、训练分类器(使用 随机森林 训练并评估分类器,在降维前后对比模型性能)
import numpy as np
import pandas as pd
from sklearn.feature_selection import SelectKBest, f_classif, VarianceThreshold
from sklearn.decomposition import PCA
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score
from sklearn.impute import SimpleImputer
df = pd.read_csv('your_dataset.csv')
# Assuming 'X' contains your features and 'y' contains your target variable
X = df.drop(columns=['Time', 'Pass/Fail'])
y = df['Pass/Fail']
# Remove constant features
selector = VarianceThreshold()
X_selected = selector.fit_transform(X)
# Impute missing values
imputer = SimpleImputer(strategy='mean')
X_imputed = imputer.fit_transform(X_selected)
# Split the data into training and test sets(测试集占 20%,训练集占 80%,设置随机种子,使结果一致)
X_train, X_test, y_train, y_test = train_test_split(X_imputed, y, test_size=0.2, random_state=42)
# Standardize the features(对列进行z-score标准化)
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)
# Perform feature selection
selector_kbest = SelectKBest(score_func=f_classif, k=20)
X_train_selected = selector_kbest.fit_transform(X_train_scaled, y_train)
X_test_selected = selector_kbest.transform(X_test_scaled)
# Perform dimensionality reduction
pca = PCA(n_components=10)
X_train_pca = pca.fit_transform(X_train_selected)
X_test_pca = pca.transform(X_test_selected)
# Train a classifier (e.g., Random Forest) without dimensionality reduction
clf_before = RandomForestClassifier(n_estimators=100, random_state=42)
clf_before.fit(X_train_scaled, y_train)
# Make predictions and evaluate the model before dimensionality reduction
y_pred_before = clf_before.predict(X_test_scaled)
accuracy_before = accuracy_score(y_test, y_pred_before)
print(f'Accuracy before dimensionality reduction: {accuracy_before}')
# Train a classifier (e.g., Random Forest) on the reduced feature set
clf_after = RandomForestClassifier(n_estimators=100, random_state=42)
clf_after.fit(X_train_pca, y_train)
# Make predictions and evaluate the model after dimensionality reduction
y_pred_after = clf_after.predict(X_test_pca)
accuracy_after = accuracy_score(y_test, y_pred_after)
print(f'Accuracy after dimensionality reduction: {accuracy_after}')
4.2 包裹法(Wrapper)
包裹法: 将要使用的学习器的性能作为特征子集的评价准则,目的是为给的学习器选择“量身定做”的特征子集
优点:最终学习器的性能比过滤方法好,更具有针对性
缺点:特征选择过程需要多次训练学习,计算开销大
包裹法方法介绍:
1、完全搜索:遍历所有可能组合的特征子集,然后输入模型,选择最佳模型分数的特征子集。不推荐使用,计算开销过大
2、启发式搜索:利用启发式信息不断缩小搜索空间的方法。在特征选择中,模型分数或特征权重可作为启发式信息
3、向前/向后搜索:向前搜索是先从空集开始,每轮只加入一个特征,然后训练模型,若模型评估分数提高,则保留该特征;逐步回归的例子
4、随机特征子集:随机选择多个特征子集,然后分别评估模型表现,选择评估分数高的特征子集
5、下面重点介绍RFE,RFECV,Null importance
递归特征消除(Recursive Feature Elimination , RFE ):
递归特征消除加交叉验证(RFE + CV):
RFE + RFECV示例代码:
RFE: 递归地移除特征,基于SVM的训练结果选择最重要的特征
RFECV:结合递归特征和交叉验证,用交叉验证评估不同特征集的性能,选择最优特征数
### 生成数据
from sklearn.datasets import make_classification
X, y = make_classification(n_samples=1000, # 样本个数
n_features=25, # 特征个数
n_informative=3, # 有效特征个数
n_redundant=2, # 冗余特征个数(有效特征的随机组合)
n_repeated=0, # 重复特征个数(有效特征和冗余特征的随机组合)
n_classes=8, # 样本类别
n_clusters_per_class=1, # 簇的个数
random_state=0)
### 特征选择
# RFE
from sklearn.svm import SVC
svc = SVC(kernel="linear")
from sklearn.feature_selection import RFE
rfe = RFE(estimator = svc, # 基分类器
n_features_to_select = 2, # 选择特征个数
step = 1, # 每次迭代移除的特征个数
verbose = 0 # 显示中间过程
).fit(X,y)
X_RFE = rfe.transform(X)
print("RFE特征选择结果——————————————————————————————————————————————————")
print("有效特征个数 : %d" % rfe.n_features_)
print("全部特征等级 : %s" % list(rfe.ranking_))
# RFECV
from sklearn.svm import SVC
svc = SVC(kernel="linear")
from sklearn.model_selection import StratifiedKFold
from sklearn.feature_selection import RFECV
rfecv = RFECV(estimator=svc, # 学习器
min_features_to_select=2, # 最小选择的特征数量
step=1, # 移除特征个数
cv=StratifiedKFold(2), # 交叉验证次数
scoring='accuracy', # 学习器的评价标准
verbose = 0,
n_jobs = 1
).fit(X, y)
X_RFECV = rfecv.transform(X)
print("RFECV特征选择结果——————————————————————————————————————————————————")
print("有效特征个数 : %d" % rfecv.n_features_)
print("全部特征等级 : %s" % list(rfecv.ranking_))
Null Importance方法:
Null Importance示例代码:
Permutation Importance 或者 Null Importance 是一种通过随机打乱标签来评估其对模型性能影响的方法。模型性能(通常是准确率或其他评价指标)下降的幅度反映了该特征的重要性。推荐RFECV和Null Importance,因为他们既考虑了特征权重也考虑了模型表现
# import the package
import target_permutation_importances as tpi
# Prepare a dataset
import pandas as pd
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
# Models
from sklearn.ensemble import RandomForestClassifier
data = load_breast_cancer()
# Convert to a pandas dataframe
X = pd.DataFrame(data.data, columns=data.feature_names)
y = data.target
# Train test split, only compute importances in the train set
X_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.2, shuffle=True, random_state=2023)
# Convert to a pandas dataframe
Xpd = pd.DataFrame(data.data, columns=data.feature_names)
# Compute permutation importances with default settings
result_df = tpi.compute(
model_cls=RandomForestClassifier, # The constructor/class of the model.
model_cls_params={ # The parameters to pass to the model constructor. Update this based on your needs.
"n_jobs": -1,
},
model_fit_params={}, # The parameters to pass to the model fit method. Update this based on your needs.
X=X_train, # pd.DataFrame
y=y_train, # pd.Series, np.ndarray
num_actual_runs=2,
num_random_runs=10,
# Options: {compute_permutation_importance_by_subtraction, compute_permutation_importance_by_division}
# Or use your own function to calculate.
permutation_importance_calculator=tpi.compute_permutation_importance_by_subtraction,
)
print(result_df[["feature", "importance"]].sort_values("importance", ascending=False).head())
4.3 嵌入法(Embedded)
嵌入法:特征选择过程被嵌入到学习器训练中。不像包裹法,特征选择和学习训练有明显的区分
优点:比包裹法更省时省力,把特征选择交给模型去学习
缺点:增加模型训练负担
嵌入法示例代码:
1、鸢尾花数据集(Iris dataset),这个数据集包含了 150 个样本和 4 个特征
2、LogisticRegression:逻辑回归模型,用于基于 L1 惩罚(L1 regularization)选择特征
3、GradientBoostingClassifier:梯度提升决策树分类器(GBDT),一种集成学习方法,通常用于分类任务。使用梯度提升决策树分类器(GBDT)作为基模型。GBDT 会根据每个特征的 信息增益(或其他指标)计算特征重要性,从而选择最重要的特征
from sklearn.datasets import load_iris
from sklearn.feature_selection import SelectFromModel
from sklearn.linear_model import LogisticRegression
from sklearn.ensemble import GradientBoostingClassifier
iris = load_iris()
# "Use logistic regression with L1 regularization as the base model for feature selection
selected_data_lr = SelectFromModel(LogisticRegression(penalty='l1', C = 0.1, solver = 'liblinear'), max_features = 3).fit_transform(iris.data, iris.target)
# Use GBDT (Gradient Boosting Decision Tree) as the base model for feature selection.
selected_data_gbdt = SelectFromModel(GradientBoostingClassifier(), max_features = 3).fit_transform(iris.data, iris.target)
print(iris.data.shape)
print(selected_data_lr.shape)
print(selected_data_gbdt.shape)
5 特征选择方法及应用场景
1、哪种特征选择方法最好是一个特定问题,取决于正在分析的数据集和研究人员想要实现的特定目标。例如,假设目标是确定哪些特征相对最重要(这有助于揭示疾病背后的生物机制)。在这种情况下,过滤方法更好,因为它们会生成一个排序的特征列表,并且计算效率最高
2、如果数据集包含的特征数量相对较少(例如,数十到数百个),则应用包裹法可能会产生最佳预测性能
3、混合方法将不同的特征选择方法组合成一个多步骤过程,以充分利用各组件方法的优势。在这种情况下,使用过滤方法是因为它简单且速度快。相比之下,使用包裹法是因为它可以对特征依赖性进行建模并允许与分类器交互,从而产生更好的性能
4、集成特征选择方法基于这样的假设:组合多种算法的输出比使用单一算法的输出更好
5 特征选择方法的一个多组学应用示例
1、现在微生物宏基因组价格都比较便宜了,但是代谢组的价格还是比较贵,这篇NC的文章是利用训练的模型,利用宏基因组和扩增子的数据来预测微生物代谢组数据的量的数据,是一篇经典的多组学分析和高维数据分析的例子
2、例如肠道中微生物代表性的代谢产物是短链脂肪酸,与宿主的免疫和健康相关。肠道微生物和代谢特征有紧密的关系
3、MelonnPan 通过以下方式推断复合代谢组:(1) 通过数据驱动识别一组最佳的预测性微生物特征,以及 (2) 对预测良好的代谢物的预测准确性进行稳健量化
4、最终验证结果,克罗恩病和肠炎和健康人的数据集中,模型预测的群落代谢趋势与实际测量的代谢趋势高度一致。准确预测了胆汁酸和脂肪酸的变化
5、ElasticNet 弹性网络(L1正则化能够实现变量选择,L2正则化通过惩罚回归系数的平方,避免过拟合,并且能有效处理多重共线性问题)
6、Y= βX1 + Βx2 + .. Y是代谢物,X是分类特征;利用正则化筛选特征,交叉验证进行训练。这里使用的是feature是宏基因UniRef90的丰度
7、重建进行宏基因组测序和代谢组测序(LC/MS),证明了可以预测的很好
6 参考资料
1 https://www.visiondummy.com/2014/04/curse-dimensionality-affect-classification/
2 https://zhuanlan.zhihu.com/p/184840799
3 https://cloud.tencent.com/developer/article/1827396
4 https://blog.csdn.net/Datawhale/article/details/120582526
5 https://www.nature.com/articles/s41467-019-10927-1#ethics