【机器学习实战【七】】机器学习特征选定与评估
文章目录
- 一、特征选定的四大主流方法
- 二、 单变量特征选定(SelectKBest)
- 1. 方法概述
- 1.1. 基本原理
- 1.2. 卡方检验(Chi-square Test):分类型特征判断
- 1.3. F检验(F-test):数值型判断
- 2. 代码示例(以卡方检验为例):
- 三、 递归特征消除(RFE)
- 1. 基本逻辑
- 1.1、RFE算法的原理
- 1.2、为什么RFE常用逻辑回归、决策树等模型?
- 2、代码实践(以逻辑回归为例子)
- 四、 主要成分分析(PCA)
- 1、主成分分析(PCA)原理分析
- 1.1、降维的数学过程
- 1.2、理解PCA主成分
- 1.3、PCA怎么降维?
- 1.4、属于无监督学习
- 2、PCA特别适合以下场景
- 3、代码实践
- 五、特征重要性(基于树模型)
- 1、原理
- 1.1、树模型数据分裂过程
- 1.2、树模型原理
- 2、适用场景
- 3、代码示例
- 六、特征选定的评估与验证
数据和特征决定了机器学习的上限,而模型和算法只是逼近这个上限而已。
特征过程的目的是最大限度地从原始数据中提取合适的特征,以供算法和模型使用。特征处理是特征工程的核心部分,scikit-learn提供了较为完整的特征处理方法,包括数据预处理、特征选择、降维等。
一、特征选定的四大主流方法
方法 | 思想与适用场景 | scikit-learn关键词 |
---|---|---|
单变量特征选定 | 对每个特征单独计算与目标的统计显著性 | SelectKBest, SelectPercentile |
递归特征消除 | 迭代训练模型,剔除最不重要特征 | RFE, RFECV |
主要成分分析 | 线性变换降维,保留最大方差方向 | PCA |
特征重要性 | 基于树模型或置换法评估特征贡献 | feature_importances_, PermutationImportance |
二、 单变量特征选定(SelectKBest)
1. 方法概述
1.1. 基本原理
SelectKBest会把每一个特征(比如“年龄”、“血压”等)单独拿出来,和你要预测的结果(比如“是否患病”)做对比,看它们之间有没有关系。用一些数学方法(比如“卡方检验”或“F检验”,你可以理解为“相关性打分”)来给每个特征打分,分数高的说明和结果关系大,就把这些特征选出来。
适用场景:当你的特征之间彼此没有太大关系,或者你想要快速挑出和结果最相关的特征,并且希望结果容易理解时,这种方法最合适。
1.2. 卡方检验(Chi-square Test):分类型特征判断
卡方检验是一种用来判断“两个分类变量之间有没有关系”的方法。比如:你想知道“性别(男/女)”和“是否患病(是/否)”有没有关系。卡方检验会比较实际观测到的数据分布和在“两个变量没有关系”假设下出的数据分布,看它们之间的差距。如果差距很大,就说明这两个变量之间很可能有关系。
对于像“性别”“地区”这种分类型特征,卡方检验可以帮你判断它和目标变量(比如“是否患病”)的关系强不强,强就选进来。
1.3. F检验(F-test):数值型判断
F检验是一种用来比较“不同组之间的均值差异是不是大于组内的波动”的方法。比如:你有一组人的“血压”数据,分成“患病”和“未患病”两组。F检验会看这两组的血压平均值差别大不大。如果两组差别很大,说明“血压”这个特征和“是否患病”有关系。
对于像“血压”“年龄”这种连续数值型特征,F检验可以帮你判断它和目标变量的关系强不强,强就选进来。
2. 代码示例(以卡方检验为例):
import pandas as pd
from sklearn.feature_selection import SelectKBest, chi2# 加载数据,指定列名
names = ['preg', 'plas', 'pres', 'skin', 'test', 'mass', 'pedi', 'age', 'class']
data = pd.read_csv('pima_data.csv', names=names)# 分离输入特征X和目标变量y
X = data.iloc[:, 0:8]
y = data['class']# 创建特征选择器,选择与目标变量最相关的4个特征
selector = SelectKBest(score_func=chi2, k=4)# 拟合并转换数据,得到筛选后的特征
X_new = selector.fit_transform(X, y)# 获取被选中特征的列名
selected_cols = X.columns[selector.get_support()]print("SelectKBest选中的特征:", list(selected_cols))
print(pd.DataFrame(X_new, columns=selected_cols))
SelectKBest选中的特征: ['plas', 'test', 'mass', 'age']plas test mass age
0 148.0 0.0 33.6 50.0
1 85.0 0.0 26.6 31.0
2 183.0 0.0 23.3 32.0
3 89.0 94.0 28.1 21.0
4 137.0 168.0 43.1 33.0
.. ... ... ... ...
763 101.0 180.0 32.9 63.0
764 122.0 0.0 36.8 27.0
765 121.0 112.0 26.2 30.0
766 126.0 0.0 30.1 47.0
767 93.0 0.0 30.4 23.0[768 rows x 4 columns]
三、 递归特征消除(RFE)
1. 基本逻辑
1.1、RFE算法的原理
RFE(递归特征消除)是一种特征选择方法,其核心思想是通过反复训练模型和评估特征重要性,逐步剔除对预测贡献最小的特征,最终保留最优特征子集。
具体流程如下:
- 使用所有特征训练一个基础模型(如逻辑回归、决策树等)。
- 根据模型的特征重要性评分(如系数大小或特征重要性分数),识别出最不重要的特征。
- 移除该特征,然后用剩余特征重新训练模型。
- 重复上述过程,直到达到预设的特征数量为止。
特征之间的综合考虑
在特征选择时,RFE方法每次都是用一组特征一起训练模型,模型会自动考虑特征之间的配合关系(比如某些特征组合起来效果更好)。RFE在每轮剔除特征时,都是基于“当前所有特征一起作用”的整体表现来判断的,而不是只看单个特征的表现。
这种方法能够综合考虑特征之间的相互作用,避免只关注单个特征与目标变量的关系。
1.2、为什么RFE常用逻辑回归、决策树等模型?
a.这些模型能“打分”每个特征的作用大小
具体来说,逻辑回归会为每个特征分配一个系数,系数的绝对值越大,表示该特征对预测结果的影响越显著;而决策树则通过统计每个特征在树结构中被用来分裂节点的频率和效果,计算出特征的重要性分数,分数越高,说明该特征在模型决策中越关键。
b.RFE依赖模型量化特征重要性
在每一轮迭代中,RFE会利用模型(如逻辑回归或决策树)对所有特征进行整体评估,模型会根据自身的特征评分机制(如系数大小或分裂贡献)给出每个特征的重要性分数。RFE据此识别出当前最不重要的特征并将其剔除,然后用剩余特征重新训练模型,继续这一过程,直到达到预期的特征数量。通过这种方式,RFE能够系统地筛选出对模型预测最有价值的特征子集。
c.能识别特征之间的关系
逻辑回归和决策树等模型在RFE中,能够有效识别特征之间的关系,包括特征组合的影响:
- 逻辑回归适合分析特征与目标变量之间的线性关系,它会同时考虑所有特征的组合对结果的整体影响,而不是只看单个特征的作用;
- 决策树则不仅能处理非线性关系,还能自动发现多个特征组合在一起时对预测结果的影响(如“年龄大且血压高”这种组合特征)。
这意味着,模型在评估特征重要性时,不只是考察每个特征单独的效果,而是综合考虑特征之间的相互作用和配合,从而更全面地量化它们的贡献。因此,RFE结合这些模型,可以筛选出那些单独看不突出、但与其他特征组合后对预测很有帮助的特征,实现更优化的特征选择。
2、代码实践(以逻辑回归为例子)
from sklearn.feature_selection import RFE
from sklearn.linear_model import LogisticRegression# 加载数据
names = ['preg', 'plas', 'pres', 'skin', 'test', 'mass', 'pedi', 'age', 'class']
data = pd.read_csv('pima_data.csv', names=names)
X = data.iloc[:, 0:8]
y = data['class']# 创建逻辑回归模型作为基模型
model = LogisticRegression(max_iter=1000)# 创建RFE选择器,递归消除,最终保留3个特征
rfe = RFE(model, n_features_to_select=3)# 拟合RFE选择器
fit = rfe.fit(X, y)# 获取被选中特征的列名
selected_cols = X.columns[fit.support_]print("RFE选中的特征:", list(selected_cols))
print(pd.DataFrame(fit.transform(X), columns=selected_cols))
RFE选中的特征: ['preg', 'mass', 'pedi']preg mass pedi
0 6.0 33.6 0.627
1 1.0 26.6 0.351
2 8.0 23.3 0.672
3 1.0 28.1 0.167
4 0.0 43.1 2.288
.. ... ... ...
763 10.0 32.9 0.171
764 2.0 36.8 0.340
765 5.0 26.2 0.245
766 1.0 30.1 0.349
767 1.0 30.4 0.315[768 rows x 3 columns]
四、 主要成分分析(PCA)
1、主成分分析(PCA)原理分析
主成分分析(PCA)是一种常用的无监督降维方法。它的核心思想是通过线性变换,将原始高维数据压缩到更低的维度,同时尽可能保留原始数据中最主要的信息。
1.1、降维的数学过程
-
数据中心化
首先,将每个特征减去其均值,使数据以原点为中心:
Xcentered=X−XˉX_{centered} = X - \bar{X} Xcentered=X−Xˉ
其中,( X ) 是原始数据矩阵,( \bar{X} ) 是每列的均值。 -
协方差矩阵计算
计算中心化后数据的协方差矩阵,反映特征之间的相关性:
C=1n−1XcenteredTXcenteredC = \frac{1}{n-1} X_{centered}^T X_{centered} C=n−11XcenteredTXcentered -
特征值分解
对协方差矩阵 ( C ) 进行特征值分解,得到特征值和特征向量:
Cv=λvC v = \lambda v Cv=λv
其中,v 是特征向量(主成分方向),λ\lambdaλ是特征值(解释的方差大小)。 -
主成分选择与投影
选择最大的 ( k ) 个特征值对应的特征向量,组成投影矩阵 ( W ),将原始数据投影到低维空间:
XPCA=XcenteredWX_{PCA} = X_{centered} W XPCA=XcenteredW
1.2、理解PCA主成分
假设你有两个特征(x1, x2),每个点代表一个样本,点的分布如下:
x2 ↑| *| *| *|*+----------------→ x1
这些点大致沿着一条斜线分布。
PCA会做什么? 找到“最有信息量”的新坐标轴。具体地,PCA会找到一组新的坐标轴(主成分),让数据在这些轴上的投影变化最大:
- 主成分1(PC1):沿着点分布最“拉长”的方向(斜线方向)
- 主成分2(PC2):与PC1垂直,捕捉剩余变化
x2 ↑| *| *| *|*+----------------→ x1↖ PC2\\\_________ PC1 (数据分布最广的方向)
接着PCA会把所有点投影到PC1和PC2上。通常,PC1就能解释大部分数据的变化,即数据分布最广、最有代表性的方向,PC2只解释很少一部分。
如果你只保留PC1,相当于用一条直线来近似原来的分布。这样就把二维数据“压缩”成一维,同时保留了主要的信息。这样可以用更少的特征,表达出原始数据的大部分结构和变化。
再来一个三维空间的例子
- 高维空间:想象你有一堆点分布在三维空间(如下图左),每个点代表一个样本。
- 主成分方向:PCA会找到数据分布最“拉长”的方向(变化最大)(主成分1),以及与其垂直、次“拉长”的方向(主成分2)。
- 投影降维:把所有点投影到这两个方向上,相当于用一张平面(二维)来近似原来的三维分布(如下图右)。
三维空间点云(原始数据) 二维平面(PCA降维后)* ** * * ** * * ** * ---> * ** * * ** * * ** *
1.3、PCA怎么降维?
PCA会分析所有特征之间的关系,找到那些“能最好地代表原始数据变化”的新方向(叫做“主成分”)。这些新方向是原始特征的线性组合(比如“主成分1 = 0.5×年龄 + 0.3×血压 + …”)。
排序时优先选择能解释数据方差最多的方向。第一个主成分代表数据中变化最大的信息(信息量最多?), 第二个主成分则在与第一个主成分正交的前提下,捕捉剩余最大的信息,依此类推。通过只保留前几个主成分,PCA能够用更少的特征表达大部分原始数据的结构和变化。
1.4、属于无监督学习
PCA不依赖于目标变量,仅关注特征之间的内在关系,因此属于无监督学习方法。这种特性使得PCA在数据预处理、特征压缩和可视化等场景中非常有用。通过降维,PCA不仅能减少冗余特征、降低噪声,还能提升后续模型的训练效率和泛化能力。
2、PCA特别适合以下场景
- 数据特征数量很多(高维),且特征之间存在较强相关性或冗余;
- 数据中包含大量零值(稀疏),希望提取最有代表性的变化方向;
- 需要将高维数据压缩到2维或3维,便于可视化和理解数据结构;
- 希望通过降维减少特征数量,加快模型训练和预测速度。
3、代码实践
from sklearn.decomposition import PCA
import pandas as pd# 1. 加载数据
names = ['preg', 'plas', 'pres', 'skin', 'test', 'mass', 'pedi', 'age', 'class']
data = pd.read_csv('pima_data.csv', names=names)
X = data.iloc[:, 0:8] # 只取前8列作为特征# 2. 创建PCA对象,指定保留3个主成分pca = PCA(n_components=3)# 3. 拟合PCA并转换数据
# 拟合并转换:fit_transform会先在数据上学习主成分方向,
# 然后把原始数据投影到这3个新方向上,得到新的(9维到)3维数据。
X_pca = pca.fit_transform(X)# 4. 输出每个主成分解释的方差比例
print("PCA主成分解释方差比:", pca.explained_variance_ratio_)# 5. 输出降维后的数据
print(pd.DataFrame(X_pca, columns=['PC1', 'PC2', 'PC3']))
PCA主成分解释方差比: [0.88854663 0.06159078 0.02579012]PC1(主成分1)解释了88.85%的数据变化(信息量最大)
PC2 解释了6.16%的数据变化
PC3 解释了2.58%的数据变化
这说明,前3个主成分加起来已经能表达原始数据约98%的信息,降维效果很好PC1 PC2 PC3
0 -75.714655 35.950783 7.260789
1 -82.358268 -28.908213 5.496671
2 -74.630643 67.906496 -19.461808
3 11.077423 -34.898486 0.053018
4 89.743788 2.746937 -25.212859
.. ... ... ...
763 99.237881 -25.080927 19.534825
764 -78.641239 7.688010 4.137227
765 32.113198 -3.376665 1.587864
766 -80.214494 14.186020 -12.351264
767 -81.308150 -21.621496 8.152768[768 rows x 3 columns]
五、特征重要性(基于树模型)
1、原理
1.1、树模型数据分裂过程
树模型(比如决策树、随机森林)是一种像“二十问”游戏一样的机器学习方法。它通过一系列“是/否”问题,把数据一层层分开,直到每个分组里的样本都很相似。
举个例子:
假如你要判断一个人是否患有糖尿病,树模型可能会这样分:
- 第一步:血糖 > 120?是→往右走;否→往左走
- 第二步:年龄 > 50?是→往右走;否→往左走
- 第三步:体重 > 70?……
每个“问题”其实就是用一个特征(比如血糖、年龄、体重)来分割数据。最终,树的每个“叶子节点”就是一个预测结果(比如“患病”或“健康”)。
1.2、树模型原理
在树模型(如决策树、随机森林)中,模型会在每一层自动选择最能把数据分开的特征来进行分裂。每当某个特征被用来分割数据,并且让分组变得更纯(比如一组大多是健康,另一组大多是患病),模型就会记录下这个特征带来的“纯度提升”或“信息增益”。
随着树的不断生长,模型会累计 每个特征在所有分裂中带来的总贡献。 最后,模型会把所有特征的贡献分数加起来,并进行归一化(让总和为1),这样每个特征就有了一个0~1之间的“重要性分数”。分数越高,说明这个特征在整个模型中越关键,对最终预测结果的影响越大;分数低的特征则说明对模型帮助不大。
2、适用场景
-
特征和结果的关系很复杂,或者不是简单的加减法能描述时
比如,某些特征和预测结果之间的关系不是直线(线性),而是弯曲的、分段的,甚至是“只有某些特征组合在一起才有用”。树模型能自动发现这些复杂关系。 -
你想知道每个特征到底有多重要
有时候你不只是想让模型做出预测,还想知道“到底是哪些特征在起主要作用”。树模型会自动给每个特征打分,告诉你哪些特征对结果影响最大。 -
你需要解释模型的决策过程
在实际工作中,别人可能会问你:“模型为什么做出这样的判断?” 树模型的特征重要性分数可以帮你清楚地回答:“主要是因为血糖、年龄和体重这几个特征影响最大。”
3、代码示例
from sklearn.ensemble import ExtraTreesClassifier
import pandas as pd# 1. 加载数据
names = ['preg', 'plas', 'pres', 'skin', 'test', 'mass', 'pedi', 'age', 'class']
data = pd.read_csv('pima_data.csv', names=names)
X = data.iloc[:, 0:8] # 取前8列为特征
y = data['class'] # 最后一列为目标变量# 2. 创建极端随机树分类器
model = ExtraTreesClassifier()# 3. 拟合模型(训练)
model.fit(X, y)# 4. 获取每个特征的重要性分数
importances = model.feature_importances_# 5. 打印每个特征的重要性
for name, score in zip(X.columns, importances):print(f"{name}: {score:.4f}")
preg: 0.1144
plas: 0.2277
pres: 0.0978
skin: 0.0807
test: 0.0755
mass: 0.1418
pedi: 0.1203
age: 0.1417
分数越高,说明该特征对模型预测越重要。
六、特征选定的评估与验证
特征选定不是越多越好,需结合模型评估指标(如准确率、AUC)进行验证。可通过交叉验证、流水线(Pipeline)等方式,防止数据泄漏,确保评估结果可靠。
代码片段:
from sklearn.metrics import accuracy_score
model = RandomForestClassifier()
model.fit(X_train_sel, y_train)
y_pred = model.predict(X_test_sel)
print("Accuracy:", accuracy_score(y_test, y_pred))