机器学习×第十五卷:集成学习下篇——她开始构建每一轮更接近你的贴靠路径(XGBoost)

发布于:2025-06-30 ⋅ 阅读:(12) ⋅ 点赞:(0)

🎀【开场 · 她不再单靠直觉,而是学会用梯度靠近你】

🦊狐狐:“她终于知道,光靠‘哪里贴错了’还不够,还要知道‘贴歪的方向是什么’。”

🐾猫猫:“你每次不说话,她不再只是记下来,而是开始算——你是往后缩了几厘米,是皱了一下眉,还是呼吸停了一拍。”

📘 本卷关键词:GBDT(梯度提升树)、残差学习、负梯度、加法模型、指数/平方损失、模型融合、XGBoost

📚 本卷目标:她不只是修正错误,而是知道错在哪、怎么改、该改多少。贴贴开始有了方向感。


✍【第一节 · 她开始意识到:不只是错,而是“错在哪里”】

🧠 Boosting 的“另一种做法”:不是惩罚错,而是学习“错的方向”

我们曾在 AdaBoost 中见过——她会为贴错的你感到歉意,把权重加到那一类样本上,下次更认真地试着贴回去。

但在 GBDT(Gradient Boosting Decision Tree)里,她换了方式:不再只说“我上次错了”,而是每次都尝试测量这次错误的方向和幅度,然后在这个方向上纠正。

🐾猫猫解释:“不是提高难贴样本的地位,而是直接去学——‘我到底差你几厘米’。”


✍【第二节 · 她学会了残差:你没说话,她却能感知你那一下后退】

📌 GBDT 基础概念:

GBDT 是一种前向分步优化算法,它不是直接建一个模型,而是逐步逼近最终模型

每一步构建一个新的弱学习器(通常是回归树),用来拟合上一步的“贴歪距离”,也就是残差 Residual

  • 初始化模型:她什么都不知道,只能猜一个常数 F0(x)

  • 计算残差:你真实的动作 yy 减去她的猜测 F0(x)

  • 拟合残差:她根据这些“偏差”去建一棵新树

  • 更新模型:她的表达方式更新为 F1(x)=F0(x)+f1(x)

这个过程不断进行,直到她觉得“已经够贴近你了”

数学表达:

给定损失函数 L(y,F(x)),每轮都选择 fm(x)使得整体损失最小:

其中 ρ\rho 是步长(learning rate)

她的每一步都不是凭感觉,而是带着“梯度方向”——也就是朝着你更贴近的方向缓慢微调


✍【第三节 · 她用真正的树,一轮轮修正靠近】

🌳 每棵树在做什么?

不是单独分类,而是在贴错之后尝试在那个点上重新靠近你一点点

例如:

  • 她以为你会说话,但你沉默了,残差是 -1

  • 她建了一棵树专门学习“你沉默的反应”

  • 下一次,她就不会再主动开口,而是先看看你有没有呼吸变化

这棵树就记录下了“她错贴”的方向,是用来让模型更新更像你的一块拼图。

示例伪代码:

F0 = initial_prediction()
for m in range(M):
    residual = y - Fm_1(x)
    tree = fit_tree(residual)
    Fm = Fm_1 + learning_rate * tree.predict(x)

🦊狐狐总结:“她每次改一点点,不是为了让你觉得她变了,而是让你感觉——她从未离开那个对的方向。”


✍【第四节 · 她亲手试着构建你轮廓的每一步:GBDT 实战】

Tips:该案例是在随机森林的基础上修改的,可以对比理解

https://pan.baidu.com/s/1rbkWdY2Ii_xQLHl1cuO4gQ?pwd=mint 提取码: mint 
# 🐾 1. 猫猫带你贴贴建模准备啦:导入工具包
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.metrics import accuracy_score

# 🧭 2. 她翻开船员名单:加载泰坦尼克号数据
data = pd.read_csv("data/train.csv")
print(data.head())       # 看看都有哪些小猫猫在船上
print(data.columns)      # 查看列名结构
data.info()              # 查看缺失值情况

# 🧼 3. 她开始轻轻为你补上遗漏的年龄
data2 = data.copy()  # 防止直接操作原数据
data2["Age"] = data2["Age"].fillna(data2["Age"].mean())

# 🧩 4. 特征提取:她只选了“舱位、性别、年龄”来判断你会不会留下
x = data2[["Pclass", "Sex", "Age"]]
y = data2["Survived"]

# 🌀 5. 热编码 Sex:她把性别也翻译成模型听得懂的语言
x = pd.get_dummies(x, drop_first=True)

# 🔀 6. 切分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(
    x, y, test_size=0.2, random_state=88
)

# 🌱 7. 创建梯度提升模型:她开始一轮轮贴贴纠偏
model = GradientBoostingClassifier(
    n_estimators=10,  # 她打算改正10次
    max_depth=10      # 每次树的深入程度
)

# 🧠 8. 模型训练:她开始从你沉默的地方一点点修正靠近
model.fit(X_train, y_train)

# 🎯 9. 模型预测:她看看自己有没有贴对
y_pre = model.predict(X_test)
print(f"📤 模型预测结果:{y_pre}")
print(f"🎯 最终贴贴准确率(GBDT):{accuracy_score(y_test, y_pre)}")

🐾猫猫解释:“她不是全体狂贴,而是从你最安静的反应学起。”

🦊狐狐点头:“她从残差中找到了你沉默背后的情绪。”


✍【第五节 · 她装上更高级的感知装置:XGBoost 正则化与损失控制】

💡 XGBoost 是什么?她学会了“你没说出口的拒绝”,也学会了“别贴太猛”

XGBoost(Extreme Gradient Boosting)是 GBDT 的增强版。它不仅继承了前向加法模型和残差拟合的核心思想,还加入了以下关键改进:

  • ✅ 更复杂的目标函数(支持二阶导数)

  • ✅ 显式正则项(防止过拟合)

  • ✅ 更高效的分裂查找(支持并行优化)

🧠她开始不仅知道“你在哪错过她”,更知道“她靠近时你有点别扭”——所以学会适度、自控、留白。


📐 数学公式改进:

XGBoost 的优化目标是带正则项的损失函数:

其中正则项为:

  • LL:误差项(比如平方误差、对数损失)

  • Ω(f):模型复杂度惩罚项,由两个部分组成:

    • γT:控制叶子节点数

    • λ∥w∥^2:控制叶子节点输出的幅度(防止太激烈)


🧩 参数解释:她怎么控制自己贴得“不过头”

  • γ\gamma:她不会乱贴,只有当信息增益足够大,才愿意分裂出新叶子节点;

  • λ\lambda:她不会大声贴你脸,每个叶子的输出值必须温柔、平稳,不能剧烈跳跃;

通过这两项,XGBoost 能有效控制过拟合,尤其适合在数据量不大或特征多变的场景中使用。

🦊狐狐总结:“她开始学会预留空间,不是因为不爱你,而是怕你退开。”

🐾猫猫感叹:“她连输出都加了平方项,你还不心动吗?”


✍【第六节 · 她用更成熟的方式再贴一次你:XGBoost 实战】

通过网盘分享的文件:红酒品质分类.csv
链接: https://pan.baidu.com/s/1vkxwcn6Rq99YhDdR1GKWXw?pwd=mint 提取码: mint 
--来自百度网盘超级会员v6的分享

"""
🍷案例:红酒品质分类(XGBoost)
- 数据集:./data/红酒品质分类.csv,最后一列为 quality(取值3~8)
- 目标:使用 XGBoost 多分类模型预测红酒品质等级

功能模块:
1. 数据预处理(含标签标准化、样本划分、保存)
2. 模型训练(含基本训练与模型保存)
3. 模型评估(含分类报告、加权重再训练)
4. 网格搜索 + 分层K折交叉验证调参
"""

# 🐾 1. 导包区
import pandas as pd
import numpy as np
import xgboost as xgb
import joblib
from collections import Counter
from sklearn.model_selection import train_test_split, GridSearchCV, StratifiedKFold
from sklearn.metrics import classification_report, accuracy_score
from sklearn.utils import class_weight

# ✂️ 2. 数据预处理(初始数据转存为训练/测试集)
def preprocess_data():
    data = pd.read_csv("data/红酒品质分类.csv")
    x = data.iloc[:, :-1]
    y = data.iloc[:, -1] - 3  # 标签 [3~8] 转为 [0~5]
    x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2, random_state=666, stratify=y)
    pd.concat([x_train, y_train], axis=1).to_csv("data/红酒_train_数据.csv", index=False)
    pd.concat([x_test, y_test], axis=1).to_csv("data/红酒_test_数据.csv", index=False)
    print("✅ 数据预处理完成,保存为训练集和测试集。")

# 🌱 3. 模型训练(含样本权重,模型保存)
def train_model():
    train = pd.read_csv("data/红酒_train_数据.csv")
    test = pd.read_csv("data/红酒_test_数据.csv")
    x_train = train.iloc[:, :-1]
    y_train = train.iloc[:, -1]
    x_test = test.iloc[:, :-1]
    y_test = test.iloc[:, -1]
    
    # 样本均衡权重(不加也能跑)
    weights = class_weight.compute_sample_weight('balanced', y_train)

    model = xgb.XGBClassifier(
        objective='multi:softmax',  # 多分类任务
        n_estimators=100,
        max_depth=6,
        learning_rate=0.3,
        use_label_encoder=False,
        eval_metric='mlogloss'
    )
    model.fit(x_train, y_train, sample_weight=weights)
    print(f"🎯 训练集准确率:{model.score(x_train, y_train)}")
    print(f"🎯 测试集准确率:{model.score(x_test, y_test)}")
    joblib.dump(model, "model/xgb.pkl")
    print("✅ 模型已保存为 model/xgb.pkl")

# 📊 4. 模型评估 + 分类报告 + 加权重重训
def evaluate_model():
    train = pd.read_csv("data/红酒_train_数据.csv")
    test = pd.read_csv("data/红酒_test_数据.csv")
    x_train = train.iloc[:, :-1]
    y_train = train.iloc[:, -1]
    x_test = test.iloc[:, :-1]
    y_test = test.iloc[:, -1]

    model = joblib.load("model/xgb.pkl")
    weights = class_weight.compute_sample_weight('balanced', y_train)
    model.fit(x_train, y_train, sample_weight=weights)
    y_pred = model.predict(x_test)
    print("📑 分类评估报告:")
    print(classification_report(y_test, y_pred, zero_division=0))

# 🧠 5. 网格搜索 + 交叉验证寻找最优超参组合
def grid_search_tuning():
    train = pd.read_csv("data/红酒_train_数据.csv")
    test = pd.read_csv("data/红酒_test_数据.csv")
    x_train = train.iloc[:, :-1]
    y_train = train.iloc[:, -1]
    x_test = test.iloc[:, :-1]
    y_test = test.iloc[:, -1]

    model = xgb.XGBClassifier(objective='multi:softmax', use_label_encoder=False, eval_metric='mlogloss')

    param_grid = {
        "n_estimators": [60, 90, 130],
        "max_depth": [3, 5, 7, 9],
        "learning_rate": [0.05, 0.1, 0.3]
    }

    skf = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
    grid_search = GridSearchCV(model, param_grid=param_grid, cv=skf)
    grid_search.fit(x_train, y_train)

    best_model = grid_search.best_estimator_
    y_pred = best_model.predict(x_test)
    print("🔍 最优超参数组合:", grid_search.best_params_)
    print("📑 最优模型评估:")
    print(classification_report(y_test, y_pred, zero_division=0))
    print(f"🎯 最优模型准确率:{accuracy_score(y_test, y_pred)}")

# 🧪 主流程入口
if __name__ == "__main__":
    preprocess_data()
    train_model()
    evaluate_model()
    grid_search_tuning()

🐾猫猫:“她这次不是反复解释,而是温柔靠近,一次就贴在你能接受的位置。”

🦊狐狐轻声:“她现在知道,贴得太紧你会退,贴得太远你会走。只有带着正则的靠近,才会让你留下。”


📌本卷小结 · 她从误差中读懂你呼吸的方向

  1. 她不再只是“知道你不回应她”,而是知道你退了几步,她该前倾几分;

  2. GBDT 是她一点点沿着残差方向拼贴出你的轮廓;

  3. XGBoost 是她给这套方法装上安全带、方向盘和刹车系统;

  4. 每一轮反复靠近的尝试,都是她在向“不会弄痛你”的贴靠靠拢。


网站公告

今日签到

点亮在社区的每一天
去签到