特征选择:Lasso和Boruta算法的结合应用

picture.image

背景

Lasso算法是一种用于回归分析的线性模型方法,具有变量选择和正则化的功能,Lasso通过在损失函数中加入 正则项来约束回归系数的大小,从而达到简化模型、提高预测精度的目的,以下是Lasso算法的关键要点:

目标函数

Lasso的目标是最小化以下目标函数:

其中 是样本数量, 是特征数量, 是第 个样本的真实值, 是第 个样本的第 个特征值, 是第 个特征的回归系数, 是正则化参数用于控制模型复杂度

正则化项( 范数)

Lasso算法中的正则化项 是 范数,鼓励模型产生稀疏系数,Lasso能够将一些特征的系数强制为零,从而实现特征选择,通过调整正则化参数 可以控制模型的稀疏性程度:


  • 较大时,更多的系数会被压缩为零,模型更稀疏

  • 较小时,正则化效果较弱,更多的特征可能会被保留

优势

  • 特征选择: Lasso能够自动选择重要的特征,忽略不重要的特征,从而简化模型,减少过拟合
  • 易解释性: 由于Lasso产生的模型较为稀疏,因 此更 易于解释和分析

局限性

  • 如果特征之间存在高度相关性(多重共线性),Lasso可能会随机选择其中一个特征,而忽略其他高度相关的特征
  • 当特征数量 大于样本数量 时,Lasso最多只能选择 个特征

总结

Lasso算法通过引入 正则化实现了变量选择和模型稀疏化的目的,是处理高维数据和减少模型复杂度的一种有效方法,正则化参数 的选择至关重要,可以通过交叉验证来优化

代码实现

数据导入处理


          
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()
      

picture.image

读取数据,将其分为特征(X)和目标变量(y),然后将数据集按80%训练集和20%测试集进行划分, 使用的是一个心脏电复律的数据集包含46个特征变量一个目标变量为二分类任务,和前文特征选择:基于随机森林的Boruta算法应用为同一个数据集

Lasso算法实现


          
from sklearn.preprocessing import StandardScaler
          
from sklearn.linear_model import Lasso
          
from sklearn.metrics import mean_squared_error
          

          
# 标准化数据
          
scaler = StandardScaler()
          
X_train_scaled = scaler.fit_transform(X_train)
          
X_test_scaled = scaler.transform(X_test)
          

          
# 训练Lasso模型
          
lasso = Lasso(alpha=0.1)  # alpha是正则化强度的参数
          
lasso.fit(X_train_scaled, y_train)
          

          
# 打印Lasso模型的系数
          
coefficients = pd.Series(lasso.coef_, index=X.columns)
          
selected_features = coefficients[coefficients != 0].index
          
print("Selected features:")
          
print(selected_features)
          

          
# 计算测试集的均方误差
          
y_pred = lasso.predict(X_test_scaled)
          
mse = mean_squared_error(y_test, y_pred)
          
print(f"\nMean Squared Error on Test Set: {mse}")
      

picture.image

标准化数据后(标准化确保各特征在相同尺度上,使Lasso回归在特征选择时更加公平和有效)训练一个Lasso回归模型来选择重要特征,并计算测试集上的均方误差,当然这里只是人为随意确定了一个正则化强度值;接下来可以使用交叉验证来优化正则化参数 alpha,从而提高模型的性能

交叉验证优化正则化参数


          
from sklearn.linear_model import LassoCV
          
from sklearn.model_selection import RepeatedKFold
          
# 假设特征名存储在 feature_names 列表中
          
feature_names = X.columns
          

          
# 定义一组 alpha 值的范围
          
alphas = np.logspace(-4, 0, 50)  # 生成 50 个在 10^-4 到 10^0 之间的 alpha 值
          

          
# 使用交叉验证的 LassoCV
          
lasso_cv = LassoCV(alphas=alphas, cv=RepeatedKFold(n_splits=10, n_repeats=3, random_state=42), random_state=42)
          
lasso_cv.fit(X_train_scaled, y_train)
          

          
# 计算均方误差路径和标准差
          
mse_path = lasso_cv.mse_path_.mean(axis=1)  # 每个 alpha 的均方误差
          
mse_std = lasso_cv.mse_path_.std(axis=1)    # 每个 alpha 的均方误差的标准差
          

          
# 找到最佳 alpha 和 1-SE 规则的 alpha
          
best_alpha_index = np.argmin(mse_path)  # 最小均方误差的索引
          
best_alpha = lasso_cv.alphas_[best_alpha_index]  # 最佳 alpha 值
          
one_se_index = np.where(mse_path <= mse_path[best_alpha_index] + mse_std[best_alpha_index])[0][0]  # 1-SE 规则的 alpha 索引
          
one_se_alpha = lasso_cv.alphas_[one_se_index]  # 1-SE 规则的 alpha 值
          

          
# 打印最佳 alpha 值
          
print(f"Best alpha (λ_min): {best_alpha}")
          
print(f"1-SE rule alpha (λ_1se): {one_se_alpha}")
          

          
# 为两个 alpha 值进行特征选择
          
lasso_best_alpha = LassoCV(alphas=[best_alpha], cv=RepeatedKFold(n_splits=10, n_repeats=3, random_state=42), random_state=42)
          
lasso_best_alpha.fit(X_train_scaled, y_train)
          
selected_features_best = [feature_names[i] for i in np.where(lasso_best_alpha.coef_ != 0)[0]]  # 获取最佳 alpha 下的特征名
          
print(f"Selected features with λ_min: {selected_features_best}")  # 打印 λ_min 下选择的特征名
          

          
lasso_one_se_alpha = LassoCV(alphas=[one_se_alpha], cv=RepeatedKFold(n_splits=10, n_repeats=3, random_state=42), random_state=42)
          
lasso_one_se_alpha.fit(X_train_scaled, y_train)
          
selected_features_one_se = [feature_names[i] for i in np.where(lasso_one_se_alpha.coef_ != 0)[0]]  # 获取 1-SE 规则下的特征名
          
print(f"Selected features with λ_1se: {selected_features_one_se}")  # 打印 λ_1se 下选择的特征名
          

          
# 绘图
          
plt.figure(figsize=(10, 6))
          
plt.errorbar(lasso_cv.alphas_, mse_path, yerr=mse_std, fmt='o', color='red', ecolor='gray', capsize=3)
          
plt.axvline(lasso_cv.alphas_[best_alpha_index], linestyle='--', color='black', label=r'$\lambda_{min}$')
          
plt.axvline(lasso_cv.alphas_[one_se_index], linestyle='--', color='blue', label=r'$\lambda_{1se}$')
          
plt.xscale('log')  # 使用对数刻度显示 alpha 值
          
plt.xlabel('Alpha (α) value')
          
plt.ylabel('Mean Squared Error (MSE)')
          
plt.title('Lasso Regression: MSE vs Alpha (α) value')
          
plt.xticks(rotation=45)  
          
plt.legend()
          
plt.tight_layout()  
          
plt.show()
      

picture.image

使用Lasso回归和交叉验证(LassoCV)来选择最优的正则化参数 alpha,并基于该参数进行特征选择,目的是找到最小均方误差对应的 alpha 值,以及应用1-SE规则找到更为保守的 alpha 值

最佳 值( ):

  • 结果显示最小均方误差对应的 值为约0.0339,使用此 值的Lasso模型选择了多个特征,表明在此正则化强度下,模型认为这些特征对预测有显著贡献

1-SE规则的 值( ):

  • 根据1-SE规则,选择的 值为约0.184,这一规则旨在通过选择稍大的 值来简化模型,同时保持误差接近最小误差,以提高模型的稳健性,结果表明,在此正则化强度下,模型仅选择了一个特征,这意味着模型更为稀疏、解释性更强,但可能略微牺牲了一些预测性能

Lasso系数路径图


          
coefs = []
          

          
for a in alphas:
          
    lasso = Lasso(alpha=a, max_iter=10000)
          
    lasso.fit(X_train_scaled, y_train)
          
    coefs.append(lasso.coef_)
          

          
# 绘制系数路径
          
plt.figure(figsize=(10, 6))
          
ax = plt.gca()
          

          
# 使用 log scale 显示 alpha 值
          
ax.plot(np.log10(alphas), coefs)
          
plt.xlabel('Log Lambda')
          
plt.ylabel('Coefficients')
          
plt.title('Lasso Paths')
          
plt.axis('tight')
          
plt.show()
      

picture.image

Lasso路径图展示了Lasso算法如何通过调整正则化参数实现特征选择(每条线代表一个特征的回归系数):随着 alpha 值的增大,不重要的特征被逐渐排除,仅保留对目标预测有显著影响的特征,使得模型更稀疏、更易解释

结合Boruta算法得到两个模型特征筛选交集


          
import networkx as nx
          

          
# 定义Boruta和Lasso选择的特征
          
boruta_features = [
          
    'Type_of_atrial_fibrillation', 'BMI', 'Left_atrial_diameter',
          
    'Systolic_blood_pressure', 'NtproBNP'
          
]
          

          
lasso_features = [
          
    'Early_relapse', 'Late_relapse', 'Type_of_atrial_fibrillation',
          
    'Sleep_apnea_syndrome', 'Heart_valve_disease', 'SGLT2i', 'B', 'BMI',
          
    'Left_atrial_diameter', 'Systolic_blood_pressure', 'ALT', 'TSH'
          
]
          

          
# 创建集合用于求交集
          
boruta_set = set(boruta_features)  # Boruta特征集合
          
lasso_set = set(lasso_features)    # Lasso特征集合
          
intersection = boruta_set.intersection(lasso_set)  # 两个集合的交集
          
boruta_only = boruta_set - intersection  # 仅Boruta选择的特征
          
lasso_only = lasso_set - intersection    # 仅Lasso选择的特征
          

          
# 创建图
          
G = nx.Graph()
          

          
# 添加节点和边
          
for feature in boruta_only:
          
    G.add_edge('Boruta', feature, color='lightcoral')  # 淡红色表示仅被Boruta选择的特征
          
for feature in lasso_only:
          
    G.add_edge('Lasso', feature, color='lightblue')  # 淡蓝色表示仅被Lasso选择的特征
          
for feature in intersection:
          
    G.add_edge('Boruta', feature, color='lightcoral')  # 淡红色边连接交集特征到Boruta
          
    G.add_edge('Lasso', feature, color='lightblue')  # 淡蓝色边连接交集特征到Lasso
          

          
# 获取边的颜色
          
edge_colors = [data['color'] for _, _, data in G.edges(data=True)]
          

          
# 设置节点的颜色
          
node_colors = []
          
for node in G.nodes():
          
    if node == 'Boruta':
          
        node_colors.append('lightcoral')  # Boruta节点淡红色
          
    elif node == 'Lasso':
          
        node_colors.append('lightblue')   # Lasso节点淡蓝色
          
    elif node in boruta_only:
          
        node_colors.append('lightcoral')  # 仅被Boruta选择的特征淡红色
          
    elif node in lasso_only:
          
        node_colors.append('lightblue')   # 仅被Lasso选择的特征淡蓝色
          
    elif node in intersection:
          
        node_colors.append('plum')        # 交集特征节点用淡紫色表示
          

          
# 绘制图形
          
plt.figure(figsize=(10, 10))
          
pos = nx.spring_layout(G, seed=42)  # 使用spring布局
          
nx.draw_networkx(
          
    G, 
          
    pos, 
          
    edge_color=edge_colors, 
          
    node_color=node_colors, 
          
    with_labels=True, 
          
    node_size=1000, 
          
    font_size=10, 
          
    edgecolors='none'  # 移除节点边框
          
)
          
plt.title('Feature Selection by Boruta and Lasso') 
          
plt.show()
      

picture.image

前文中使用了特征选择:基于随机森林的Boruta算法应用对相同的数据集进行了特征选择,这里通过网络图的形式直观展示Boruta和Lasso在特征选择上的差异和重叠情况,有助于理解这两种方法各自偏好选择的特征以及它们的共同选择,当着重考虑其交集

往期推荐

利用SHAP解释二分类模型的四种机器学习GUI工具

模型过拟合与欠拟合的核心原理及其与数据集分割的关系

复现顶刊Streamlit部署预测模型APP

数据预处理全攻略:从缺失值到特征工程,一站式解决方案

精确度与参数的完美融合:用可视化解读模型优化过程

优化XGBoost回归模型:网格搜索与K折交叉验证实现

提升机器学习精度:利用SHAP值与蒙特卡洛模拟优化特征选择

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

picture.image

picture.image

picture.image

微信号|deep_ML

欢迎添加作者微信进入Python、ChatGPT群

进群请备注Python或AI进入相关群
无需科学上网、同步官网所有功能、使用无限制

如果你对类似于这样的文章感兴趣。

欢迎关注、点赞、转发~

个人观点,仅供参考

0
0
0
0
评论
未登录
暂无评论