Kaggle-练习赛-Titanic -机器学习 -集成学习
·
集成学习
集成学习(Ensemble Learning)是一种机器学习方法,通过结合多个基学习器的预测结果来提高整体模型的性能。其核心思想是“群策群力”,即多个弱学习器可以组合成一个强学习器。集成学习通常能显著降低过拟合风险,提高泛化能力。
集成学习的核心原理
偏差-方差分解:集成学习通过减少模型的偏差或方差来提升性能。单个模型可能因高偏差(欠拟合)或高方差(过拟合)表现不佳,而集成方法通过结合多个模型的预测来平衡两者。
多样性:基学习器之间需要具备一定的差异性(如不同算法、数据子集或参数设置),否则集成效果有限。常见的多样性来源包括数据扰动(如Bagging)、特征扰动或算法扰动。
主要集成方法
Bagging(Bootstrap Aggregating)
- 通过自助采样(Bootstrap Sampling)生成多个训练子集,每个子集训练一个基学习器。
- 最终结果通过投票(分类)或平均(回归)产生。
- 典型算法:随机森林(Random Forest),进一步引入特征随机性。
Boosting
- 基学习器按顺序训练,后续模型关注前序模型的错误样本。
- 通过加权投票或加权求和组合预测结果。
- 典型算法:AdaBoost(Adaptive Boosting)、梯度提升树(GBDT)、XGBoost、LightGBM。
Stacking
- 使用初级学习器(基模型)生成预测结果,再将结果作为输入训练次级学习器(元模型)。
- 需注意避免数据泄露,通常通过交叉验证生成元特征。
实际应用中的优势
- 鲁棒性:对噪声数据和异常值更具容忍度。
- 灵活性:支持异构模型(如Stacking中混合决策树和SVM)。
- 可解释性:部分方法(如随机森林)能提供特征重要性分析。
挑战与注意事项
- 计算开销:训练多个模型可能增加时间和资源消耗。
- 模型选择:基学习器的性能需均衡,过强的基学习器可能导致集成无效。
- 过拟合风险:Boosting方法需控制迭代次数,Stacking需谨慎设计次级模型。
XGBoost
XGBoost(eXtreme Gradient Boosting)是一种基于梯度提升框架的机器学习算法,由陈天奇于2016年提出。它通过集成多个弱学习器(通常是决策树)来构建强学习器,广泛应用于分类、回归和排序任务。其核心优势在于高效性、可扩展性和对缺失值的鲁棒性。
核心特点
并行计算:通过特征预排序和块结构优化,支持并行化处理,大幅提升训练速度。
正则化:在目标函数中加入L1/L2正则项,防止过拟合。
缺失值处理:自动学习缺失值的分裂方向,无需人工填充。
灵活性:支持自定义损失函数和评估指标。
关键优化技术
加权分位数草图:近似寻找最优分裂点,减少计算量。
稀疏感知算法:高效处理稀疏数据(如One-Hot编码后的特征)。
缓存访问优化:通过缓存命中率提升数据读取效率。
应用场景
- 金融风控(信用评分、欺诈检测)
- 推荐系统(点击率预测)
- 结构化数据竞赛(如Kaggle比赛中常见)
与同类算法对比
- LightGBM:更快的训练速度,但可能牺牲部分精度。
- CatBoost:擅长处理类别特征,无需显式编码。
- 随机森林:基于Bagging,抗噪声能力更强,但解释性较差。
代码:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.model_selection import train_test_split, GridSearchCV
from sklearn.preprocessing import OneHotEncoder
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
from xgboost import XGBClassifier
from sklearn.metrics import accuracy_score, confusion_matrix, classification_report, roc_auc_score
# ---------------------- 1. 数据加载 ----------------------
def load_data(train_path, test_path):
train_df = pd.read_csv(train_path)
test_df = pd.read_csv(test_path)
# 保存测试集ID用于最终提交
test_passenger_ids = test_df["PassengerId"]
print(f"✅ 数据加载完成:训练集{train_df.shape} | 测试集{test_df.shape}")
return train_df, test_df, test_passenger_ids
# ---------------------- 2. 特征工程(针对XGBoost优化) ----------------------
def feature_engineering(train_df, test_df):
# 合并数据便于统一处理
combined_df = pd.concat([train_df, test_df], ignore_index=True)
original_test_size = test_df.shape[0]
# 1. 提取头衔特征(对XGBoost重要)
combined_df["Title"] = combined_df["Name"].str.extract(r"([A-Za-z]+)\.")
rare_titles = ["Dona", "Lady", "Countess", "Capt", "Col", "Don", "Dr", "Major", "Rev", "Sir", "Jonkheer"]
combined_df["Title"] = combined_df["Title"].replace(rare_titles, "Rare")
combined_df["Title"] = combined_df["Title"].replace({"Mlle": "Miss", "Ms": "Miss", "Mme": "Mrs"})
# 2. 填充缺失值(XGBoost对缺失值有一定容忍度,但填充能提升性能)
## 先处理Embarked(分类特征)
combined_df["Embarked"] = combined_df["Embarked"].fillna(combined_df["Embarked"].mode()[0])
## 年龄:按Title+Pclass分组填充(更精准)
age_median = combined_df.groupby(["Title", "Pclass"])["Age"].median()
for (title, pclass) in age_median.index:
mask = (combined_df["Age"].isnull()) & (combined_df["Title"] == title) & (combined_df["Pclass"] == pclass)
combined_df.loc[mask, "Age"] = age_median[(title, pclass)]
if combined_df["Age"].isnull().any():
combined_df["Age"] = combined_df["Age"].fillna(combined_df["Age"].median())
## 票价:按Pclass+Embarked分组填充
fare_median = combined_df.groupby(["Pclass", "Embarked"])["Fare"].median()
def fill_fare(x):
try:
return fare_median[x["Pclass"], x["Embarked"]]
except KeyError:
return combined_df.groupby("Pclass")["Fare"].median()[x["Pclass"]]
combined_df["Fare"] = combined_df["Fare"].fillna(combined_df.apply(fill_fare, axis=1))
# 3. 衍生高价值特征(XGBoost能有效利用交互特征)
combined_df["FamilySize"] = combined_df["SibSp"] + combined_df["Parch"] + 1
combined_df["IsAlone"] = 1 # 1表示独自旅行
combined_df.loc[combined_df["FamilySize"] > 1, "IsAlone"] = 0
combined_df["Age_Pclass"] = combined_df["Age"] * combined_df["Pclass"] # 年龄与舱位交互
combined_df["Fare_Per_Person"] = combined_df["Fare"] / combined_df["FamilySize"] # 人均票价
# 4. 分箱处理(减少噪声,增强稳定性)
combined_df["AgeBin"] = pd.cut(combined_df["Age"], bins=[0, 12, 18, 30, 50, 100], labels=["Child", "Teen", "Young", "Mid", "Elder"])
combined_df["FareBin"] = pd.qcut(combined_df["Fare"], 5, labels=["Fare1", "Fare2", "Fare3", "Fare4", "Fare5"])
# 5. 移除无用特征
drop_cols = ["Name", "Ticket", "Cabin", "PassengerId", "Age", "Fare"] # 移除原始连续特征,保留分箱和交互特征
combined_df = combined_df.drop(columns=drop_cols)
# 拆分回训练集和测试集
train_processed = combined_df.iloc[:-original_test_size, :].copy()
test_processed = combined_df.iloc[-original_test_size:, :].copy()
print(f"✅ 特征工程完成:训练集特征{train_processed.columns.tolist()}")
return train_processed, test_processed
# ---------------------- 3. 数据拆分 ----------------------
def split_data(train_processed):
# 分离特征和标签
X = train_processed.drop(columns=["Survived"])
y = train_processed["Survived"].astype(int)
# 拆分训练集和验证集(分层抽样)
X_train, X_val, y_train, y_val = train_test_split(
X, y, test_size=0.2, random_state=42, stratify=y
)
print(f"✅ 数据拆分完成:训练集{X_train.shape} | 验证集{X_val.shape}")
return X, y, X_train, X_val, y_train, y_val
# ---------------------- 4. XGBoost模型超参数调优 ----------------------
def tune_xgboost(X_train, y_train):
# 定义特征类型
numeric_features = ["SibSp", "Parch", "FamilySize", "Age_Pclass", "Fare_Per_Person"]
categorical_features = ["Sex", "Pclass", "Embarked", "Title", "IsAlone", "AgeBin", "FareBin"]
# 预处理管道(XGBoost需要将分类特征转为数值)
preprocessor = ColumnTransformer(
transformers=[
("num", "passthrough", numeric_features), # 数值特征直接使用
("cat", OneHotEncoder(drop="first", sparse_output=False, handle_unknown="ignore"), categorical_features) # 分类特征独热编码
]
)
# XGBoost管道
xgb_pipeline = Pipeline(steps=[
("preprocessor", preprocessor),
("classifier", XGBClassifier(
random_state=42,
use_label_encoder=False,
eval_metric='logloss', # 二分类评估指标
objective='binary:logistic' # 二分类任务
))
])
# 超参数搜索空间(针对泰坦尼克号数据优化)
param_grid = {
"classifier__n_estimators": [100, 200, 300], # 树的数量
"classifier__max_depth": [3, 4, 5], # 树深度(控制复杂度)
"classifier__learning_rate": [0.01, 0.05, 0.1], # 学习率
"classifier__subsample": [0.8, 0.9, 1.0], # 样本采样比例
"classifier__colsample_bytree": [0.8, 0.9, 1.0], # 特征采样比例
"classifier__gamma": [0, 0.1, 0.2], # 分裂所需的最小损失减少量
"classifier__reg_alpha": [0, 0.1, 1], # L1正则化
"classifier__reg_lambda": [1, 10, 100] # L2正则化
}
# 网格搜索
grid_search = GridSearchCV(
estimator=xgb_pipeline,
param_grid=param_grid,
cv=5, # 5折交叉验证
scoring="accuracy",
n_jobs=-1, # 并行计算
verbose=1
)
grid_search.fit(X_train, y_train)
print(f"✅ XGBoost调优完成:最优准确率{grid_search.best_score_:.4f},最优参数:{grid_search.best_params_}")
return grid_search.best_estimator_
# ---------------------- 5. 模型评估 ----------------------
def evaluate_model(model, X_val, y_val):
# 预测
y_pred = model.predict(X_val)
y_proba = model.predict_proba(X_val)[:, 1]
# 评估指标
acc = accuracy_score(y_val, y_pred)
roc = roc_auc_score(y_val, y_proba)
print("\n=== 模型评估结果 ===")
print(f"验证集准确率:{acc:.4f}")
print(f"ROC-AUC:{roc:.4f}")
print("\n混淆矩阵:")
print(confusion_matrix(y_val, y_pred))
print("\n分类报告:")
print(classification_report(y_val, y_pred, digits=4))
return acc
# ---------------------- 6. 生成提交文件 ----------------------
def generate_submission(model, X, y, test_processed, test_passenger_ids, output_path):
# 用全量训练数据重新训练模型
model.fit(X, y)
# 预测测试集
test_pred = model.predict(test_processed)
# 生成提交文件
submission = pd.DataFrame({
"PassengerId": test_passenger_ids.astype(int),
"Survived": test_pred.astype(int)
})
submission.to_csv(output_path, index=False)
print(f"\n✅ 提交文件生成完成:{output_path}")
print("提交文件前5行:\n", submission.head())
return submission
# ---------------------- 7. 主函数 ----------------------
def main():
# 文件路径
TRAIN_PATH = "/kaggle/input/titanic/train.csv"
TEST_PATH = "/kaggle/input/titanic/test.csv"
OUTPUT_PATH = "titanic_xgboost_submission.csv"
# 流程串联
train_df, test_df, test_passenger_ids = load_data(TRAIN_PATH, TEST_PATH)
train_processed, test_processed = feature_engineering(train_df, test_df)
X, y, X_train, X_val, y_train, y_val = split_data(train_processed)
best_xgb = tune_xgboost(X_train, y_train)
evaluate_model(best_xgb, X_val, y_val)
generate_submission(best_xgb, X, y, test_processed, test_passenger_ids, OUTPUT_PATH)
if __name__ == "__main__":
main()
训练结果:
✅ 数据加载完成:训练集(891, 12) | 测试集(418, 11)
✅ 特征工程完成:训练集特征['Survived', 'Pclass', 'Sex', 'SibSp', 'Parch', 'Embarked', 'Title', 'FamilySize', 'IsAlone', 'Age_Pclass', 'Fare_Per_Person', 'AgeBin', 'FareBin']
✅ 数据拆分完成:训练集(712, 12) | 验证集(179, 12)
Fitting 5 folds for each of 6561 candidates, totalling 32805 fits
✅ XGBoost调优完成:最优准确率0.8371,最优参数:{'classifier__colsample_bytree': 0.9, 'classifier__gamma': 0.1, 'classifier__learning_rate': 0.1, 'classifier__max_depth': 4, 'classifier__n_estimators': 100, 'classifier__reg_alpha': 0.1, 'classifier__reg_lambda': 10, 'classifier__subsample': 0.9}
=== 模型评估结果 ===
验证集准确率:0.8101
ROC-AUC:0.8577
更多推荐



所有评论(0)