目录
1. 引言
在数据科学领域,Titanic 数据集是一个非常经典的入门练习题。它是基于 1912 年著名的泰坦尼克号沉船事件的实际数据,目标是通过已知的乘客信息预测某位乘客是否能够在灾难中幸存下来。这不仅是一个有趣的历史案例,也为初学者提供了一个很好的机会来学习如何处理分类问题,应用机器学习算法并进行模型评估。
在这篇博客中,将带你一步一步地完成 Titanic 生还预测任务,主要涵盖以下几个方面:
- 理解数据的基本结构。
- 如何处理数据中的缺失值和类别型特征。
- 通过特征工程来提高模型的表现。
- 使用多个常见的机器学习模型来进行预测,包括逻辑回归、随机森林和梯度提升树(XGBoost、LightGBM)。
- 通过准确率、F1 分数和 AUC-ROC 等指标对模型进行评估,并解释这些指标的意义。
无论是刚接触机器学习,还是正在寻找提升模型表现的方法,希望这篇博客能帮助你顺利完成 Titanic 生还预测的初学者任务。
2. 理解 Titanic 数据集
在开始构建模型之前,我们首先需要深入理解 Titanic 数据集的结构。这个数据集包含了泰坦尼克号乘客的相关信息,包括他们的基本背景、船舱信息以及是否幸存(Survived
)。以下是一些主要的特征:
- PassengerId:乘客编号(无关特征,可以删除)。
- Pclass:客舱等级(1, 2, 3),代表社会经济地位。
- Name:乘客姓名,可以从中提取称谓(如 Mr., Mrs. 等),为后续特征工程提供信息。
- Sex:乘客的性别,幸存率与性别高度相关。
- Age:乘客的年龄,有些年龄值缺失,需要进行填充。
- SibSp:兄弟姐妹/配偶人数,表示乘客随行家人数量。
- Parch:父母/子女人数,表示乘客随行的父母或子女数量。
- Ticket:票号,可能不是直接有用的特征,但可以探索。
- Fare:票价,可以进行分段处理以便更好地进行建模。
- Cabin:船舱号码,大部分缺失,可以进行分组处理。
- Embarked:登船港口(C = Cherbourg, Q = Queenstown, S = Southampton),部分缺失,需要填充。
目标变量
- Survived:生还情况(0 = 未生还, 1 = 生还),这是我们的目标变量。
数据的初步探索
在机器学习项目的早期阶段,初步的数据探索(EDA, Exploratory Data Analysis)非常重要。通过 EDA,我们可以发现数据中的模式、不平衡、缺失值等问题。可以用以下方式简单检查数据:
import pandas as pd
# 读取数据
data = pd.read_csv('train.csv')
# 查看数据前几行
print(data.head())
# 检查数据的总体信息,缺失值和数据类型
print(data.info())
# 查看每个特征的基本统计信息
print(data.describe())
data.head()的打印结果
data.info()的打印结果
data.describe()的打印结果
通过上述代码,你可以清楚地了解数据中的每个特征,特别是它们的类型、缺失值情况和数值分布。这是我们接下来要进行数据预处理和特征工程的基础。
在接下来的部分,我们将介绍如何处理这些数据问题,为建模做好准备。
3. 数据预处理
在我们正式开始构建模型之前,需要对 Titanic 数据集进行数据预处理。这一步是非常关键的,因为原始数据通常包含缺失值、不平衡特征或非数值型特征,这些都会影响模型的表现。
3.1 处理缺失值
在 Titanic 数据集中,部分特征(如 Age
、Cabin
、Embarked
)存在缺失值。如果不处理这些缺失值,模型训练时会报错或表现不佳。
- Age:这是一个重要的特征,但存在较多缺失值。一个常见的处理方法是用乘客所在
Pclass
(客舱等级)的中位数年龄来填充缺失值。 - Embarked:表示乘客登船港口,缺失值较少,可以用出现频率最高的港口(众数)来填充。
- Cabin:此特征的缺失值很多,通常选择删除该列,或者只提取出有用的信息(如提取首字母代表船舱的分布)。
-
# 填充 Age 缺失值,按 Pclass 的中位数年龄填充 data['Age'] = data.groupby('Pclass')['Age'].transform(lambda x: x.fillna(x.median())) # 填充 Embarked 缺失值,使用出现频率最高的值 data['Embarked'].fillna(data['Embarked'].mode()[0], inplace=True) # 删除 Cabin 列,因其缺失值过多 data.drop(columns=['Cabin'], inplace=True)
3.2 处理类别型特征
机器学习算法无法直接处理类别型特征,因此我们需要将其转换为数值型特征。常见的做法是 独热编码(One-Hot Encoding),它将类别型变量转换为多个二进制特征。
- Sex:
Male
和Female
转换为 0 和 1。 - Embarked:将登船港口转换为独热编码。
- Pclass:客舱等级也是一个类别型特征,可以保留原数值形式,或者转换为独热编码。
# 独热编码处理 Sex 和 Embarked 特征 data = pd.get_dummies(data, columns=['Sex', 'Embarked'], drop_first=True) # Pclass 可以保留原数值,不需要处理
3.3 创建新特征
为了提高模型的预测能力,特征工程可以帮助我们从现有数据中创建更多有用的特征。
- FamilySize:通过
SibSp
和Parch
计算乘客的家庭规模。 - IsAlone:当
FamilySize
为 1 时,表示乘客独自一人。 - Title:从乘客的姓名中提取称谓(如 Mr., Mrs., Miss. 等)。
# 创建 FamilySize 特征 data['FamilySize'] = data['SibSp'] + data['Parch'] + 1 # 创建 IsAlone 特征 data['IsAlone'] = (data['FamilySize'] == 1).astype(int) # 从姓名中提取称谓(Title),并简化为常见的类别 data['Title'] = data['Name'].str.extract(r' ([A-Za-z]+)\.', expand=False) data['Title'] = data['Title'].replace(['Lady', 'Countess', 'Capt', 'Col', 'Don', 'Dr', 'Major', 'Rev', 'Sir', 'Jonkheer', 'Dona'], 'Rare') data['Title'] = data['Title'].replace('Mlle', 'Miss') data['Title'] = data['Title'].replace('Ms', 'Miss') data['Title'] = data['Title'].replace('Mme', 'Mrs') # 独热编码处理 Title 特征 data = pd.get_dummies(data, columns=['Title'], drop_first=True)
3.4 删除无关特征
一些特征与生还预测关系不大,或者无法直接用作模型输入,因此需要删除这些特征。例如:
- PassengerId:仅为标识符,不提供有用信息。
- Name 和 Ticket:文本信息不适合作为模型特征
# 删除无关特征 data.drop(columns=['PassengerId', 'Name', 'Ticket'], inplace=True)
3.5 确保所有特征都是数值型
经过上述处理后,我们已经将所有类别型特征转换为数值型特征,并删除了无关的列。我们可以检查数据的类型,确保所有特征都适合输入到机器学习模型中。
# 检查数据类型 print(data.dtypes)
数据类型的打印如下
4. 模型选择与训练
在完成数据预处理后,接下来我们进入模型选择与训练的阶段。初学者可以尝试使用几种经典的机器学习模型来预测乘客的生还情况。常用的模型包括:
通过这些指标,可以清晰地评估模型的优劣,并根据具体应用场景选择合适的模型。
各模型的评估如下:
逻辑回归模型 - 准确率: 0.7989, F1分数: 0.7534, AUC-ROC: 0.8865
随机森林模型 - 准确率: 0.8380, F1分数: 0.8079, AUC-ROC: 0.8993
XGBoost模型 - 准确率: 0.8324, F1分数: 0.7945, AUC-ROC: 0.9041
LightGBM模型 - 准确率: 0.8492, F1分数: 0.8163, AUC-ROC: 0.8997
- 逻辑回归(Logistic Regression)
- 随机森林(Random Forest)
- 梯度提升决策树(Gradient Boosting Decision Trees),如 XGBoost 和 LightGBM
4.1 数据集拆分
在训练模型之前,我们需要将数据集分为训练集和测试集,确保模型的泛化能力。通常会采用 80% 的数据用于训练,20% 的数据用于测试。
from sklearn.model_selection import train_test_split # 特征和目标变量 X = data.drop('Survived', axis=1) # 特征 y = data['Survived'] # 目标变量 # 数据集拆分 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
4.2 选择和训练模型
对于初学者来说,选择简单且解释性强的模型开始是一个不错的选择。以下是几个常用的模型及其简单的训练方法:
4.2.1 逻辑回归模型
逻辑回归是一种用于二分类问题的简单模型,易于理解并且通常有不错的表现。
from sklearn.linear_model import LogisticRegression from sklearn.metrics import accuracy_score, f1_score, roc_auc_score # 初始化并训练模型 logreg = LogisticRegression(max_iter=200) logreg.fit(X_train, y_train) # 预测 y_pred = logreg.predict(X_test) y_prob = logreg.predict_proba(X_test)[:, 1] # 模型评估 accuracy = accuracy_score(y_test, y_pred) f1 = f1_score(y_test, y_pred) roc_auc = roc_auc_score(y_test, y_prob) print(f"逻辑回归模型 - 准确率: {accuracy:.4f}, F1分数: {f1:.4f}, AUC-ROC: {roc_auc:.4f}")
4.2.2 随机森林模型
随机森林是一个集成学习方法,通过多个决策树的预测结果进行投票,具有较好的稳定性和性能。
from sklearn.ensemble import RandomForestClassifier # 初始化并训练模型 rf = RandomForestClassifier(n_estimators=100, random_state=42) rf.fit(X_train, y_train) # 预测 y_pred = rf.predict(X_test) y_prob = rf.predict_proba(X_test)[:, 1] # 模型评估 accuracy = accuracy_score(y_test, y_pred) f1 = f1_score(y_test, y_pred) roc_auc = roc_auc_score(y_test, y_prob) print(f"随机森林模型 - 准确率: {accuracy:.4f}, F1分数: {f1:.4f}, AUC-ROC: {roc_auc:.4f}")
4.2.3 XGBoost
XGBoost 是一种高效的梯度提升树模型,常用于结构化数据的分类任务,效果显著。
from xgboost import XGBClassifier # 初始化并训练模型 xgb = XGBClassifier(eval_metric='logloss', use_label_encoder=False, random_state=42) xgb.fit(X_train, y_train) # 预测 y_pred = xgb.predict(X_test) y_prob = xgb.predict_proba(X_test)[:, 1] # 模型评估 accuracy = accuracy_score(y_test, y_pred) f1 = f1_score(y_test, y_pred) roc_auc = roc_auc_score(y_test, y_prob) print(f"XGBoost模型 - 准确率: {accuracy:.4f}, F1分数: {f1:.4f}, AUC-ROC: {roc_auc:.4f}")
4.2.4 LightGBM
LightGBM 是另一种梯度提升树模型,特别擅长处理大规模数据集。
-
from lightgbm import LGBMClassifier # 初始化并训练模型 lgbm = LGBMClassifier(random_state=42) lgbm.fit(X_train, y_train) # 预测 y_pred = lgbm.predict(X_test) y_prob = lgbm.predict_proba(X_test)[:, 1] # 模型评估 accuracy = accuracy_score(y_test, y_pred) f1 = f1_score(y_test, y_pred) roc_auc = roc_auc_score(y_test, y_prob) print(f"LightGBM模型 - 准确率: {accuracy:.4f}, F1分数: {f1:.4f}, AUC-ROC: {roc_auc:.4f}")
4.3 模型评估与比较
为了判断模型的性能,我们使用以下常见的评估指标:
- 准确率(Accuracy):正确预测的比例。
- F1 分数:综合了精确率和召回率,适合处理类别不平衡问题。
- AUC-ROC:用于评估模型在不同阈值下的表现,越接近 1 越好。
5. 模型优化与调参
在初步训练和评估模型之后,通常需要对模型进行进一步优化,以提升其性能。模型优化主要包括超参数调优和特征选择两个方面。
5.1 超参数调优
每种模型都有许多可调节的超参数,这些参数对模型的表现有重要影响。超参数调优的目标是找到最优的超参数组合,以提高模型的性能。
超参数调优的方法
- 网格搜索(Grid Search):通过穷举所有可能的超参数组合,找到最佳参数。
- 随机搜索(Random Search):从超参数空间中随机选择组合进行尝试,比网格搜索更高效。
- 贝叶斯优化(Bayesian Optimization):利用概率模型来选择超参数组合,以更智能的方式进行优化。
下面是使用网格搜索调优超参数的示例:
# 随机森林超参数调优
param_grid_rf = {
'n_estimators': [100, 200, 300],
'max_depth': [10, 20, 30],
'min_samples_split': [2, 5, 10]
}
grid_search_rf = GridSearchCV(estimator=RandomForestClassifier(random_state=42), param_grid=param_grid_rf, cv=5, verbose=3, scoring='roc_auc', n_jobs=-1)
grid_search_rf.fit(X_train, y_train)
best_rf = grid_search_rf.best_estimator_
# 预测
y_pred = best_rf.predict(X_test)
y_prob = best_rf.predict_proba(X_test)[:, 1]
# 模型评估
accuracy = accuracy_score(y_test, y_pred)
f1 = f1_score(y_test, y_pred)
roc_auc = roc_auc_score(y_test, y_prob)
print(f"随机森林模型(超参数调优) - 准确率: {accuracy:.4f}, F1分数: {f1:.4f}, AUC-ROC: {roc_auc:.4f}")
from sklearn.model_selection import GridSearchCV
# 随机森林超参数调优
param_grid_rf = {
'n_estimators': [100, 200, 300],
'max_depth': [10, 20, 30],
'min_samples_split': [2, 5, 10]
}
grid_search_rf = GridSearchCV(estimator=RandomForestClassifier(random_state=42), param_grid=param_grid_rf, cv=5, verbose=3, scoring='roc_auc', n_jobs=-1)
grid_search_rf.fit(X_train, y_train)
best_rf = grid_search_rf.best_estimator_
# XGBoost超参数调优
param_grid_xgb = {
'n_estimators': [100, 200, 300],
'max_depth': [3, 5, 7],
'learning_rate': [0.01, 0.1, 0.2],
'subsample': [0.8, 0.9, 1.0]
}
grid_search_xgb = GridSearchCV(estimator=XGBClassifier(eval_metric='logloss', use_label_encoder=False, random_state=42), param_grid=param_grid_xgb, cv=5, verbose=3, scoring='roc_auc', n_jobs=-1)
grid_search_xgb.fit(X_train, y_train)
best_xgb = grid_search_xgb.best_estimator_
# LightGBM超参数调优
param_grid_lgbm = {
'n_estimators': [100, 200, 300],
'max_depth': [3, 5, 7],
'learning_rate': [0.01, 0.1, 0.2],
'num_leaves': [31, 63, 127]
}
grid_search_lgbm = GridSearchCV(estimator=LGBMClassifier(random_state=42), param_grid=param_grid_lgbm, cv=5, verbose=3, scoring='roc_auc', n_jobs=-1)
grid_search_lgbm.fit(X_train, y_train)
best_lgbm = grid_search_lgbm.best_estimator_
随机森林模型(超参数调优) - 准确率: 0.8492, F1分数: 0.8138, AUC-ROC: 0.9063 【未调优前:随机森林模型 - 准确率: 0.8380, F1分数: 0.8079, AUC-ROC: 0.8993】
5.2 特征选择
特征选择旨在选择对模型有用的特征,去除冗余或无关的特征,以提高模型的性能和训练效率。
常用的特征选择方法
- 基于模型的特征选择:使用模型的重要性评分来选择特征。例如,随机森林和梯度提升树模型提供特征重要性评分。
- 递归特征消除(RFE):递归地训练模型,逐步去除不重要的特征,直到找到最佳特征子集。
- 特征选择技术:如卡方检验、信息增益等。
-
from sklearn.feature_selection import RFE # 使用随机森林进行特征选择 selector = RFE(estimator=RandomForestClassifier(n_estimators=100, random_state=42), n_features_to_select=10, step=1) selector = selector.fit(X_train, y_train) # 查看选择的特征 selected_features = X_train.columns[selector.support_] print("选择的特征:", selected_features)
5.3 模型集成
集成学习方法将多个模型的预测结果结合起来,以提高最终预测的准确性。常见的集成方法包括
- 投票法(Voting):将不同模型的预测结果进行投票,得到最终预测。
- 堆叠(Stacking):将多个模型的预测结果作为特征,输入到另一个模型中进行训练。
from sklearn.ensemble import VotingClassifier # 创建集成模型 voting_clf = VotingClassifier(estimators=[ ('rf', best_rf), ('xgb', best_xgb), ('lgbm', best_lgbm) ], voting='soft') # 训练集成模型 voting_clf.fit(X_train, y_train) # 预测 y_pred = voting_clf.predict(X_test) y_prob = voting_clf.predict_proba(X_test)[:, 1] # 模型评估 accuracy = accuracy_score(y_test, y_pred) f1 = f1_score(y_test, y_pred) roc_auc = roc_auc_score(y_test, y_prob) print(f"集成模型 - 准确率: {accuracy:.4f}, F1分数: {f1:.4f}, AUC-ROC: {roc_auc:.4f}")
6. 结论与展望
在本篇博客中,我们详细介绍了从数据预处理到模型训练与优化的整个流程。我们使用了多种机器学习模型(逻辑回归、随机森林、XGBoost 和 LightGBM),并对这些模型进行了评估。通过对超参数的调优和特征选择,我们进一步提升了模型的性能。
本文运行源码
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score, f1_score, roc_auc_score
from sklearn.ensemble import RandomForestClassifier
from xgboost import XGBClassifier
from lightgbm import LGBMClassifier
from sklearn.model_selection import GridSearchCV
# 读取数据
data = pd.read_csv('train.csv')
# # 查看数据前几行
# print(data.head())
#
# # 检查数据的总体信息,缺失值和数据类型
# print(data.info())
# # 查看每个特征的基本统计信息
# print(data.describe())
# 填充 Age 缺失值,按 Pclass 的中位数年龄填充
data['Age'] = data.groupby('Pclass')['Age'].transform(lambda x: x.fillna(x.median()))
# 填充 Embarked 缺失值,使用出现频率最高的值
data['Embarked'].fillna(data['Embarked'].mode()[0], inplace=True)
# 删除 Cabin 列,因其缺失值过多
data.drop(columns=['Cabin'], inplace=True)
# 独热编码处理 Sex 和 Embarked 特征
data = pd.get_dummies(data, columns=['Sex', 'Embarked'], drop_first=True)
# 创建 FamilySize 特征
data['FamilySize'] = data['SibSp'] + data['Parch'] + 1
# 创建 IsAlone 特征
data['IsAlone'] = (data['FamilySize'] == 1).astype(int)
# 从姓名中提取称谓(Title),并简化为常见的类别
data['Title'] = data['Name'].str.extract(r' ([A-Za-z]+)\.', expand=False)
data['Title'] = data['Title'].replace(['Lady', 'Countess', 'Capt', 'Col', 'Don', 'Dr', 'Major', 'Rev', 'Sir', 'Jonkheer', 'Dona'], 'Rare')
data['Title'] = data['Title'].replace('Mlle', 'Miss')
data['Title'] = data['Title'].replace('Ms', 'Miss')
data['Title'] = data['Title'].replace('Mme', 'Mrs')
# 独热编码处理 Title 特征
data = pd.get_dummies(data, columns=['Title'], drop_first=True)
# 删除无关特征
data.drop(columns=['PassengerId', 'Name', 'Ticket'], inplace=True)
# 检查数据类型
print(data.dtypes)
# 特征和目标变量
X = data.drop('Survived', axis=1) # 特征
y = data['Survived'] # 目标变量
# 数据集拆分
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# 初始化并训练模型
logreg = LogisticRegression(max_iter=500)
logreg.fit(X_train, y_train)
# 预测
y_pred = logreg.predict(X_test)
y_prob = logreg.predict_proba(X_test)[:, 1]
# 模型评估
accuracy = accuracy_score(y_test, y_pred)
f1 = f1_score(y_test, y_pred)
roc_auc = roc_auc_score(y_test, y_prob)
print(f"逻辑回归模型 - 准确率: {accuracy:.4f}, F1分数: {f1:.4f}, AUC-ROC: {roc_auc:.4f}")
# from sklearn.ensemble import RandomForestClassifier
#
# # 初始化并训练模型
# rf = RandomForestClassifier(n_estimators=100, random_state=42)
# rf.fit(X_train, y_train)
#
# # 预测
# y_pred = rf.predict(X_test)
# y_prob = rf.predict_proba(X_test)[:, 1]
#
# # 模型评估
# accuracy = accuracy_score(y_test, y_pred)
# f1 = f1_score(y_test, y_pred)
# roc_auc = roc_auc_score(y_test, y_prob)
#
# print(f"随机森林模型 - 准确率: {accuracy:.4f}, F1分数: {f1:.4f}, AUC-ROC: {roc_auc:.4f}")
#
#
# from xgboost import XGBClassifier
#
# # 初始化并训练模型
# xgb = XGBClassifier(eval_metric='logloss', use_label_encoder=False, random_state=42)
# xgb.fit(X_train, y_train)
#
# # 预测
# y_pred = xgb.predict(X_test)
# y_prob = xgb.predict_proba(X_test)[:, 1]
#
# # 模型评估
# accuracy = accuracy_score(y_test, y_pred)
# f1 = f1_score(y_test, y_pred)
# roc_auc = roc_auc_score(y_test, y_prob)
#
# print(f"XGBoost模型 - 准确率: {accuracy:.4f}, F1分数: {f1:.4f}, AUC-ROC: {roc_auc:.4f}")
#
#
# from lightgbm import LGBMClassifier
#
# # 初始化并训练模型
# lgbm = LGBMClassifier(random_state=42)
# lgbm.fit(X_train, y_train)
#
# # 预测
# y_pred = lgbm.predict(X_test)
# y_prob = lgbm.predict_proba(X_test)[:, 1]
#
# # 模型评估
# accuracy = accuracy_score(y_test, y_pred)
# f1 = f1_score(y_test, y_pred)
# roc_auc = roc_auc_score(y_test, y_prob)
#
# print(f"LightGBM模型 - 准确率: {accuracy:.4f}, F1分数: {f1:.4f}, AUC-ROC: {roc_auc:.4f}")
# 随机森林超参数调优
param_grid_rf = {
'n_estimators': [100, 200, 300],
'max_depth': [10, 20, 30],
'min_samples_split': [2, 5, 10]
}
grid_search_rf = GridSearchCV(estimator=RandomForestClassifier(random_state=42), param_grid=param_grid_rf, cv=5 ,scoring='roc_auc', n_jobs=-1)
grid_search_rf.fit(X_train, y_train)
best_rf = grid_search_rf.best_estimator_
# 预测
y_pred = best_rf.predict(X_test)
y_prob = best_rf.predict_proba(X_test)[:, 1]
# 模型评估
accuracy = accuracy_score(y_test, y_pred)
f1 = f1_score(y_test, y_pred)
roc_auc = roc_auc_score(y_test, y_prob)
print(f"随机森林模型(超参数调优) - 准确率: {accuracy:.4f}, F1分数: {f1:.4f}, AUC-ROC: {roc_auc:.4f}")
# XGBoost超参数调优
param_grid_xgb = {
'n_estimators': [100, 200, 300],
'max_depth': [3, 5, 7],
'learning_rate': [0.01, 0.1, 0.2],
'subsample': [0.8, 0.9, 1.0]
}
grid_search_xgb = GridSearchCV(estimator=XGBClassifier(eval_metric='logloss', use_label_encoder=False, random_state=42), param_grid=param_grid_xgb, cv=5, scoring='roc_auc', n_jobs=-1)
grid_search_xgb.fit(X_train, y_train)
best_xgb = grid_search_xgb.best_estimator_
# LightGBM超参数调优
param_grid_lgbm = {
'n_estimators': [100, 200, 300],
'max_depth': [3, 5, 7],
'learning_rate': [0.01, 0.1, 0.2],
'num_leaves': [31, 63, 127]
}
grid_search_lgbm = GridSearchCV(estimator=LGBMClassifier(random_state=42), param_grid=param_grid_lgbm, cv=5, scoring='roc_auc', n_jobs=-1)
grid_search_lgbm.fit(X_train, y_train)
best_lgbm = grid_search_lgbm.best_estimator_
from sklearn.feature_selection import RFE
# 使用随机森林进行特征选择
selector = RFE(estimator=RandomForestClassifier(n_estimators=100, random_state=42), n_features_to_select=10, step=1)
selector = selector.fit(X_train, y_train)
# 查看选择的特征
selected_features = X_train.columns[selector.support_]
print("选择的特征:", selected_features)
from sklearn.ensemble import VotingClassifier
# 创建集成模型
voting_clf = VotingClassifier(estimators=[
('rf', best_rf),
('xgb', best_xgb),
('lgbm', best_lgbm)
], voting='soft')
# 训练集成模型
voting_clf.fit(X_train, y_train)
# 预测
y_pred = voting_clf.predict(X_test)
y_prob = voting_clf.predict_proba(X_test)[:, 1]
# 模型评估
accuracy = accuracy_score(y_test, y_pred)
f1 = f1_score(y_test, y_pred)
roc_auc = roc_auc_score(y_test, y_prob)
print(f"集成模型 - 准确率: {accuracy:.4f}, F1分数: {f1:.4f}, AUC-ROC: {roc_auc:.4f}")