特征选择(Feature Selection)
特征选择是选择对模型训练最重要的特征,去除冗余或不相关特征,从而提高模型的性能和训练速度,并减少过拟合。
原理
特征选择通过减少数据维度,去除冗余或不相关特征,可以提高模型的性能和训练速度,并减少过拟合。常用的方法包括基于统计量的方法(如方差选择、相关系数选择)、基于模型的方法(如基于树模型的特征重要性度量)、和基于嵌入的方法(如正则化模型中的L1正则化)。
核心公式
可以使用基于树模型的特征重要性度量,如在随机森林中计算特征重要性:
其中,Importancejm 是第 m 棵树中特征 Xj 的重要性度量。
假设使用基于Gini系数的特征重要性计算方法,单棵树的特征重要性可以表示为:
其中,nodes(Xj) 是所有包含特征 Xj的节点, Nt 是节点 t 的样本数量, N 是总样本数量, ΔGini(t) 是节点 t 上的Gini系数变化。
案例
数据集说明
假设我们有一个包含以下特征的数据集:
- 饮食习惯:
Diet
(类别特征,如“健康饮食”、“普通饮食”、“不健康饮食”) - 每周运动频率:
ExerciseFrequency
(数值特征,单位:次/周) - 平均睡眠时间:
SleepHours
(数值特征,单位:小时/天) - 血压:
BloodPressure
(数值特征,单位:mmHg) - 体重:
Weight
(数值特征,单位:kg) - 心率:
HeartRate
(数值特征,单位:次/分钟)
我们的目标是找出哪些特征对预测健康状况(HealthStatus
,如“健康”或“不健康”)最重要。
代码实现
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.feature_selection import SelectKBest, f_classif, mutual_info_classif
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
# 生成示例数据
data = {
'Diet': ['Healthy', 'Regular', 'Unhealthy', 'Healthy', 'Regular', 'Unhealthy', 'Healthy', 'Regular'],
'ExerciseFrequency': [5, 2, 0, 4, 3, 1, 5, 2],
'SleepHours': [8, 6, 5, 7, 6, 5, 8, 7],
'BloodPressure': [120, 130, 140, 115, 125, 135, 110, 128],
'Weight': [70, 80, 90, 65, 75, 85, 68, 78],
'HeartRate': [70, 75, 80, 65, 72, 78, 68, 74],
'HealthStatus': ['Healthy', 'Unhealthy', 'Unhealthy', 'Healthy', 'Healthy', 'Unhealthy', 'Healthy', 'Unhealthy']
}
df = pd.DataFrame(data)
# 将类别特征编码为数值形式
encoder = OneHotEncoder(sparse_output=False)
diet_encoded = encoder.fit_transform(df[['Diet']])
diet_encoded_df = pd.DataFrame(diet_encoded, columns=encoder.get_feature_names_out(['Diet']))
# 合并编码后的特征与原始数据
df_encoded = pd.concat([diet_encoded_df, df.drop(['Diet'], axis=1)], axis=1)
# 特征和标签分开
X = df_encoded.drop(['HealthStatus'], axis=1)
y = df_encoded['HealthStatus'].apply(lambda x: 1 if x == 'Healthy' else 0)
# 标准化数值特征
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
# 特征选择方法
selectors = [
('SelectKBest_f_classif', SelectKBest(score_func=f_classif, k=5)),
('SelectKBest_mutual_info_classif', SelectKBest(score_func=mutual_info_classif, k=5))
]
# 绘制图形
plt.figure(figsize=(14, 6))
for i, (name, selector) in enumerate(selectors):
plt.subplot(1, 2, i + 1)
X_new = selector.fit_transform(X_scaled, y)
mask = selector.get_support()
selected_features = X.columns[mask]
X_new_df = pd.DataFrame(X_new, columns=selected_features)
plt.scatter(X_new_df.iloc[:, 0], X_new_df.iloc[:, 1], c=y, edgecolor='k', s=50, cmap='viridis')
plt.title(f'{name} Feature Selection')
plt.xlabel('Feature 1')
plt.ylabel('Feature 2')
plt.tight_layout()
plt.show()
# 输出选出的特征
for name, selector in selectors:
selector.fit(X_scaled, y)
mask = selector.get_support()
selected_features = X.columns[mask]
print(f"{name} 选择的特征: {selected_features.tolist()}")
代码解析
- 生成示例数据:我们生成了一个包含生活方式和健康指标的示例数据集。
- 编码类别特征:使用
OneHotEncoder
将类别特征“饮食习惯”编码为数值形式。 - 合并数据:将编码后的类别特征与其他特征合并。
- 特征和标签分开:将特征和标签分开,标签“健康状况”用0和1表示。
- 标准化数值特征:使用
StandardScaler
对数值特征进行标准化。 - 特征选择方法:我们使用了两种特征选择方法:
SelectKBest
基于方差分析(ANOVA F-value)和互信息法(mutual information)。 - 绘制图形:通过绘制散点图,展示了特征选择后的数据分布情况。
- 输出选出的特征:输出每种方法选择的特征列表。
通过这个生活场景的案例,我们可以看到特征选择在简化模型、提高模型性能和减少过拟合方面的重要性。
特征缩放(Feature Scaling)
特征缩放是调整特征的尺度,使其在相似的范围内。这在某些算法中尤其重要,例如基于距离的算法和梯度下降优化算法。常见的方法包括标准化和归一化,以及其他变换方法。
原理
减少特征值范围的差异,帮助某些算法更快收敛。
核心公式
- 最大最小缩放(Min-Max Scaling):将特征缩放到一个固定的范围(通常是[0, 1])。
-
- 公式:
- 对数变换(Log Transformation):应用对数变换,可以压缩较大的特征值。
-
- 公式:
加1是为了避免对数零或负值的问题。
案例
为了展示特征缩放的效果,我们使用一个生活场景的数据集。假设我们有一个包含房价、面积、卧室数量、浴室数量和距离市中心的生活数据集。我们将应用标准化(Standardization)和最大最小缩放(Min-Max Scaling)来调整特征尺度,并展示缩放前后的数据分布。
import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
from sklearn.preprocessing import StandardScaler, MinMaxScaler
# 生成生活场景数据集
data = {
'Price': [300000, 450000, 350000, 500000, 600000, 700000, 650000, 800000],
'Area': [1500, 2000, 1800, 2200, 2500, 3000, 2700, 3200],
'Bedrooms': [3, 4, 3, 5, 4, 5, 4, 6],
'Bathrooms': [2, 3, 2, 4, 3, 4, 3, 5],
'Distance_to_CityCenter': [10, 20, 15, 30, 25, 35, 28, 40]
}
df = pd.DataFrame(data)
# 原始数据集的描述性统计
print("原始数据集描述性统计:")
print(df.describe())
# 原始数据集的散点图矩阵
sns.pairplot(df, markers=['o', 's', 'D', 'v'])
plt.suptitle('Original Data', y=1.02)
plt.show()
# 标准化
scaler = StandardScaler()
df_standardized = pd.DataFrame(scaler.fit_transform(df), columns=df.columns)
# 标准化数据集的描述性统计
print("标准化数据集描述性统计:")
print(df_standardized.describe())
# 标准化数据集的散点图矩阵
sns.pairplot(df_standardized, markers=['o', 's', 'D', 'v'])
plt.suptitle('Standardized Data', y=1.02)
plt.show()
# 最小最大缩放
scaler = MinMaxScaler()
df_minmax = pd.DataFrame(scaler.fit_transform(df), columns=df.columns)
# 最小最大缩放数据集的描述性统计
print("最小最大缩放数据集描述性统计:")
print(df_minmax.describe())
# 最小最大缩放数据集的散点图矩阵
sns.pairplot(df_minmax, markers=['o', 's', 'D', 'v'])
plt.suptitle('Min-Max Scaled Data', y=1.02)
plt.show()
代码解析
- 生成生活场景数据集:创建一个包含房价、面积、卧室数量、浴室数量和距离市中心的示例数据集。
- 描述性统计:输出原始数据集的描述性统计信息,以便了解数据的基本特征。
- 原始数据的可视化:使用Seaborn的
pairplot
函数绘制原始数据集的散点图矩阵,以便观察特征之间的关系。 - 标准化:
-
- 使用
StandardScaler
对数据进行标准化,使每个特征具有均值为0、标准差为1。 - 输出标准化后的数据描述性统计信息。
- 可视化标准化后的数据集,绘制散点图矩阵。
- 使用
- 最小最大缩放:
-
- 使用
MinMaxScaler
对数据进行最小最大缩放,使每个特征的值都在[0, 1]范围内。 - 输出最小最大缩放后的数据描述性统计信息。
- 可视化最小最大缩放后的数据集,绘制散点图矩阵。
- 使用
通过这个生活场景的案例,我们可以清楚地看到特征缩放在数据预处理中是如何帮助减少特征值范围差异,从而帮助某些机器学习算法更快收敛并提高模型性能。