所有文章 > AI驱动 > 特征选择:基于随机森林的Boruta算法应用

特征选择:基于随机森林的Boruta算法应用

前言

Boruta算法是一种用于特征选择的包裹式算法,专门设计用于确定数据集中哪些特征对预测模型是重要的。

Boruta算法原理

  • 构建随机森林模型:首先,Boruta算法使用随机森林模型来计算每个特征的重要性,随机森林是一种集成学习方法,它通过构建多个决策树并取平均值来提高模型的准确性和稳定性。
  • 创建影子特征:为了评估原始特征的重要性,Boruta算法会对数据进行打乱,生成一组与原始特征对应的影子特征,这些影子特征是通过随机排列原始特征的数据生成的,它们不应该对目标变量有预测能力。
  • 比较特征重要性:算法将原始特征的重要性与影子特征的重要性进行比较,如果一个原始特征的重要性显著高于所有影子特征的最大重要性,那么它被认为是“重要的”;如果它的重要性低于影子特征的最大重要性,则被认为是“无关的”,对于那些无法明显判断的重要性,算法会将其标记为“待定”。
  • 逐步消除无关特征:算法通过迭代的方式,逐步剔除那些被标记为“无关”的特征,然后重新构建模型,直到所有特征都被分类为“重要”或“无关”,或者达到设定的迭代次数。
  • 输出结果:最终,Boruta算法输出三类特征——重要的、无关的和待定的,重要特征可以保留用于模型构建,而无关特征则可以被舍弃,对于待定的特征,可能需要进一步的分析或选择性保留。

代码实现

数据读取处理

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
df = pd.read_csv('Chabuhou.csv')

# 划分特征和目标变量
X = df.drop(['Electrical_cardioversion'], axis=1)
y = df['Electrical_cardioversion']
# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2,
random_state=42, stratify=df['Electrical_cardioversion'])
df.head()

读取数据,将其分为特征(X)和目标变量(y),然后将数据集按80%训练集和20%测试集进行划分,使用的是一个心脏电复律的数据集包含46个特征变量一个目标变量为二分类任务。

Boruta算法特征选择

from sklearn.ensemble import RandomForestClassifier
from boruta import BorutaPy

# 初始化随机森林模型
rf = RandomForestClassifier(n_jobs=-1, class_weight='balanced', max_depth=5)

# 初始化Boruta特征选择器
boruta_selector = BorutaPy(rf, n_estimators='auto', verbose=2, random_state=42)

# 对训练数据进行特征选择
boruta_selector.fit(X_train.values, y_train.values)

# 检查选中的特征
selected_features = X_train.columns[boruta_selector.support_].to_list()

# 打印被选择的特征
print("Selected Features: ", selected_features)

# 打印被剔除的特征
rejected_features = X_train.columns[~boruta_selector.support_].to_list()
print("Rejected Features: ", rejected_features)

# 打印有待定性的特征
tentative_features = X_train.columns[boruta_selector.support_weak_].to_list()
print("Tentative Features: ", tentative_features)

初始化随机森林分类器模型用于评估特征重要性,并通过Boruta特征选择器与影子特征进行对比,确定哪些特征是重要的,随后对训练数据应用Boruta算法,以筛选出对模型预测最有用的特征,Boruta算法默认在内部迭代100次,如需更改迭代次数,可通过添加参数max_iter进行调整。

# 获取特征排名
feature_ranks = boruta_selector.ranking_
# 将特征名称和排名结合成一个DataFrame
feature_importance_df = pd.DataFrame({
'Feature': X_train.columns,
'Rank': feature_ranks
})
feature_importance_df

使用boruta_selector.ranking_获取每个特征的排名,这个排名表示了特征的重要性,数值越小代表特征越重要,这里只展示部分特征,最后确定重要的特征为[‘Type_of_atrial_fibrillation’, ‘BMI’, ‘Left_atrial_diameter’, ‘Systolic_blood_pressure’, ‘NtproBNP’]。

多次运行Boruta算法以评估特征排名稳定性

# 初始化随机森林模型
rf = RandomForestClassifier(n_jobs=-1, class_weight='balanced', max_depth=5)

# 初始化存储特征排名的 DataFrame
ranking_df = pd.DataFrame(index=range(1, 21), columns=X_train.columns)

# 运行 Boruta 20 次
for i in range(20):
print(f"Iteration {i+1}")

# 初始化Boruta特征选择器
boruta_selector = BorutaPy(rf, n_estimators='auto', verbose=2, random_state=i, max_iter=50)

# 对训练数据进行特征选择
boruta_selector.fit(X_train.values, y_train.values)

# 获取特征排名
feature_ranks = boruta_selector.ranking_

# 将特征排名保存到 DataFrame 中
ranking_df.loc[i+1] = feature_ranks
ranking_df

多次运行Boruta算法,以不同的随机种子生成特征排名,并将每次的排名结果保存到一个DataFrame中,用于分析特征排名的一致性,并且指定max_iter=50不在是默认的100,提高运行速度。

可视化排名稳定性

# 确保数据集中只有数值列
numeric_ranking_df = ranking_df.apply(pd.to_numeric, errors='coerce')

# 计算每个特征的中位数
median_values = numeric_ranking_df.median()

# 根据中位数对列进行排序
sorted_columns = median_values.sort_values().index

# 设置绘图风格
plt.figure(figsize=(15, 8))
sns.set(style="whitegrid")

# 绘制箱线图
sns.boxplot(data=numeric_ranking_df[sorted_columns], palette="Greens")

plt.xticks(rotation=90)
plt.title("Sorted Feature Ranking Distribution by Boruta", fontsize=16)
plt.xlabel("Attributes", fontsize=14)
plt.ylabel("Importance", fontsize=14)
plt.tight_layout()
plt.show()

从图表中可以看出,最左侧的特征(Type_of_atrial_fibrillation、BMI等)在多次迭代中排名较高,表明它们是模型中较为重要的特征,而最右侧的特征(如COPD、Amiodarone等)则在多次迭代中排名较低,说明它们在模型中被认为不太重要,与前面单次运行Boruta算法的结果一致,但是相比于单次运行,通过这个可视化可以发现某些特征在个别迭代中的重要性排名与大多数迭代结果不同,从而判断这些特征的重要性是否稳定,图中位于须线外的点正是代表了这些异常的排名结果,如果过多这种情况出现作者认为需要着重去考虑这种特征。

文章转自微信公众号@Python机器学习AI