XGBoost 实战:从房价预测到信用卡欺诈识别

举报
yd_276081655 发表于 2026/10/19 22:30:51 2026/10/19
【摘要】 如果你正在学习机器学习,XGBoost 是一个值得优先掌握的算法。它尤其擅长处理结构化表格数据,能够学习变量之间复杂的非线性关系,在房价预测、信用风险评估、欺诈检测、客户流失预测等任务中都有广泛应用。本文从两个典型案例入手:房价预测(回归问题)和信用卡欺诈识别(二分类问题)。通过实际 Python 代码,介绍 XGBoost 的基本原理、建模流程、核心参数,以及如何选择真正符合业务目标的评价指标。

本文从两个典型案例入手:房价预测(回归问题)和信用卡欺诈识别(二分类问题)。通过实际 Python 代码,介绍 XGBoost 的基本原理、建模流程、核心参数,以及如何选择真正符合业务目标的评价指标。

一、XGBoost 到底是什么?

XGBoost,全称 eXtreme Gradient Boosting,是一种基于梯度提升树的机器学习算法。

它的核心思想并不复杂:训练一系列决策树,让后面的树不断弥补前面模型的不足,最终将多棵树的预测结果组合起来。

例如,要预测一套房子的价格。

  • 第一棵树根据面积、地段等信息,给出一个初步估计。
  • 第二棵树重点学习第一棵树预测不准确的部分。
  • 第三棵树继续修正当前模型的误差。
  • 经过多轮迭代,模型逐渐形成更准确的预测结果。

严格来说,后续的树并非简单地拟合原始残差,而是利用损失函数的一阶梯度和二阶梯度信息,寻找能够降低整体目标函数的分裂与叶节点权重。

XGBoost 的优化目标可以概括为:

[
\text{目标函数}=\text{预测损失}+\text{模型复杂度惩罚}
]

预测损失衡量模型预测得有多差;复杂度惩罚则限制树的规模和叶节点权重,降低过拟合风险。

这也是 XGBoost 与简单地不断增加决策树有所不同的地方:它不仅追求拟合效果,还对模型复杂度进行显式约束。

XGBoost 适合解决什么问题?

问题类型 典型任务 常用模型
回归 房价、销售额、违约损失金额预测 XGBRegressor
二分类 违约识别、欺诈检测、客户流失预测 XGBClassifier
多分类 客户分群、风险等级分类 XGBClassifier
排序 搜索结果排序、推荐候选排序 XGBoost 排序模型
生存分析 违约时间、设备失效时间预测 XGBoost AFT 等目标函数

它的优势主要在于:

  1. 擅长表格数据。 数值特征、经过编码的类别特征以及非线性变量关系都可以纳入模型。
  2. 能够捕捉特征交互。 例如,贷款期限本身未必能决定违约风险,但它可能与客户收入、负债率共同影响风险。
  3. 支持正则化。 可以通过限制树的复杂度、惩罚叶节点权重等方式控制过拟合。
  4. 计算效率较高。 支持直方图算法、多线程,以及多种分布式训练方案。
  5. 支持缺失值处理。 树模型可以学习缺失值在分裂时的默认方向,但这不意味着所有数据质量问题都可以忽略。

需要注意,XGBoost 并不是所有任务的最佳选择。对于原始图像、音频或复杂文本等非结构化数据,深度学习模型通常更合适。

二、案例一:使用 XGBoost 预测房价

1. 业务背景

第一个案例参考 Kaggle 的经典竞赛 House Prices: Advanced Regression Techniques。

数据集包含美国艾姆斯市的住宅销售记录,每套房屋有多种属性,例如:

  • OverallQual:房屋整体质量评分。
  • GrLivArea:地上居住面积。
  • GarageCars:车库可容纳的车辆数量。
  • YearBuilt:建造年份。
  • Neighborhood:所在社区。
  • SalePrice:实际销售价格,即需要预测的目标变量。

这属于典型的监督学习回归问题。

输入是房屋属性 (X),输出是预测价格 (\hat y)。模型通过历史样本学习房屋属性与销售价格之间的关系,再对新房屋进行预测。

2. 安装依赖与准备数据

安装所需的 Python 库:

pip install xgboost pandas numpy scikit-learn

去 Kaggle 的 House Prices 竞赛页面下载 train.csv,放在代码运行目录下。

下面的代码会自动处理数值特征、类别特征和缺失值,并划分训练集与验证集。

3. 完整实践代码

import numpy as np
import pandas as pd

from sklearn.model_selection import train_test_split
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import OneHotEncoder
from sklearn.impute import SimpleImputer
from sklearn.metrics import mean_absolute_error
from sklearn.metrics import mean_squared_error, r2_score

from xgboost import XGBRegressor

# 1. 读取数据
df = pd.read_csv("train.csv")

X = df.drop(columns=["SalePrice"])
y = df["SalePrice"]

# 2. 划分训练集和验证集
X_train, X_valid, y_train, y_valid = train_test_split(
    X, y, test_size=0.2, random_state=42
)

# 3. 区分数值特征和类别特征
num_cols = X.select_dtypes(include=np.number).columns
cat_cols = X.select_dtypes(exclude=np.number).columns

# 4. 定义预处理流程
numeric_pipe = SimpleImputer(strategy="median")

categorical_pipe = Pipeline([
    ("imputer", SimpleImputer(strategy="most_frequent")),
    ("onehot", OneHotEncoder(handle_unknown="ignore"))
])

preprocessor = ColumnTransformer([
    ("num", numeric_pipe, num_cols),
    ("cat", categorical_pipe, cat_cols)
])

# 5. 构建 XGBoost 回归模型
model = XGBRegressor(
    n_estimators=700,
    max_depth=4,
    learning_rate=0.03,
    subsample=0.8,
    colsample_bytree=0.8,
    reg_lambda=5,
    objective="reg:squarederror",
    tree_method="hist",
    n_jobs=-1,
    random_state=42
)

pipeline = Pipeline([
    ("prep", preprocessor),
    ("model", model)
])

# 6. 对房价取 log1p,降低目标变量偏态的影响
pipeline.fit(X_train, np.log1p(y_train))

# 7. 预测并还原到原始价格尺度
pred_log = pipeline.predict(X_valid)
pred = np.expm1(pred_log)

# 8. 评价模型
mae = mean_absolute_error(y_valid, pred)
rmse = np.sqrt(mean_squared_error(y_valid, pred))
r2 = r2_score(y_valid, pred)

print(f"MAE:  {mae:.2f}")
print(f"RMSE: {rmse:.2f}")
print(f"R²:   {r2:.4f}")

# 9. 查看重要特征
feature_names = pipeline.named_steps[
    "prep"
].get_feature_names_out()

importance = pd.Series(
    pipeline.named_steps["model"].feature_importances_,
    index=feature_names
).sort_values(ascending=False)

print("\n重要特征 Top 15:")
print(importance.head(15))

4. 这段代码的关键是什么?

第一,为什么要进行 One-Hot 编码?

Neighborhood 等类别变量并不是具有天然数值大小的变量。如果直接将社区编码成 1、2、3,模型可能错误地利用这些数字之间的大小关系。

One-Hot 编码会将不同类别转换为独立的指示变量。例如:

Neighborhood
    ↓
Neighborhood_A = 1 或 0
Neighborhood_B = 1 或 0
Neighborhood_C = 1 或 0

这使模型能够学习不同类别对应的预测差异。

第二,为什么要对房价取对数?

房价往往呈现右偏分布,少数高价房屋可能对平方误差产生很大影响。

使用 log1p 对目标变量进行变换,可以压缩极端价格之间的数值差距。预测后使用 expm1 还原,便于直接用美元等原始价格单位计算误差。

这是一种可选的建模策略,不是 XGBoost 的硬性要求;最终仍应通过验证集判断它是否有效。

第三,如何评价模型?

指标 含义 解读
MAE 平均绝对误差 预测价格平均偏离实际价格多少
RMSE 均方根误差 对较大的预测误差给予更高惩罚
R² 决定系数 衡量模型相对均值基准解释目标变量变化的能力

MAE 和 RMSE 越低通常越好,R² 越高通常越好。

例如,若 MAE 为 15,000,意味着验证集上的平均绝对预测误差约为 15,000 个货币单位。这里仅作指标解释,并非上述代码的实际运行结果。

5. 如何理解特征重要性?

代码最后会输出模型认为较重要的特征。

如果 OverallQual、GrLivArea 或 Neighborhood 排名靠前,说明这些变量对当前模型的预测贡献较大。

但必须区分两件事:

  • 预测重要性: 该变量对当前模型的预测有多大贡献。
  • 因果效应: 改变该变量是否会导致房价发生变化。

特征重要性只能直接支持前者,不能证明后者。对于高度相关的变量,重要性还可能被分散。

本案例的 GitHub 实践代码可参考:

https://github.com/AloneMask001/Ames-Housing-Price-Prediction

三、案例二:使用 XGBoost 识别信用卡欺诈

如果说房价预测展示了 XGBoost 如何解决回归问题,那么信用卡欺诈检测则展示了它在不平衡二分类问题中的应用。

1. 业务背景

参考经典的 Credit Card Fraud Detection 数据集。该数据集包含约 28.5 万笔交易,其中欺诈交易只有 492 笔,占比约 0.17%。

每条交易记录包含交易金额、时间,以及经过匿名化处理的特征 V1 至 V28。目标变量为:

  • Class = 0:正常交易。
  • Class = 1:欺诈交易。

模型需要根据交易特征,估计一笔交易属于欺诈交易的概率。

这个问题的难点不在于普通分类,而在于正负样本极度不平衡,以及漏报和误报的业务成本不同。

假如模型把所有交易都判断为正常交易,准确率仍可能达到约 99.83%,但欺诈识别能力实际上为零。

因此,不能仅仅追求准确率。

2. 完整实践代码

将下载的数据集保存为 creditcard.csv,然后运行以下代码。

import numpy as np
import pandas as pd

from sklearn.model_selection import train_test_split
from sklearn.metrics import (
    average_precision_score,
    precision_recall_curve,
    classification_report,
    confusion_matrix
)

from xgboost import XGBClassifier

# 1. 读取数据
df = pd.read_csv("creditcard.csv")

X = df.drop(columns=["Class"])
y = df["Class"]

# 2. 划分训练集、验证集和测试集
X_train_valid, X_test, y_train_valid, y_test = (
    train_test_split(
        X, y, test_size=0.2,
        stratify=y, random_state=42
    )
)

X_train, X_valid, y_train, y_valid = (
    train_test_split(
        X_train_valid, y_train_valid,
        test_size=0.25,
        stratify=y_train_valid,
        random_state=42
    )
)

# 3. 计算类别不平衡权重
neg = (y_train == 0).sum()
pos = (y_train == 1).sum()
scale_pos_weight = neg / pos

# 4. 建立分类模型
model = XGBClassifier(
    n_estimators=500,
    max_depth=4,
    learning_rate=0.05,
    subsample=0.8,
    colsample_bytree=0.8,
    reg_lambda=5,
    scale_pos_weight=scale_pos_weight,
    eval_metric="aucpr",
    tree_method="hist",
    n_jobs=-1,
    random_state=42
)

model.fit(X_train, y_train)

# 5. 在验证集上选择分类阈值
valid_prob = model.predict_proba(X_valid)[:, 1]

precision, recall, thresholds = precision_recall_curve(
    y_valid, valid_prob
)

# 选择验证集 F1 最大的阈值
f1 = (
    2 * precision[:-1] * recall[:-1]
    / (precision[:-1] + recall[:-1] + 1e-12)
)

best_threshold = thresholds[np.argmax(f1)]

# 6. 在独立测试集上评估
test_prob = model.predict_proba(X_test)[:, 1]
test_pred = (test_prob >= best_threshold).astype(int)

print("验证集选定阈值:", best_threshold)

print(
    "测试集 Average Precision:",
    average_precision_score(y_test, test_prob)
)

print("\n分类报告:")
print(classification_report(
    y_test, test_pred,
    digits=4, zero_division=0
))

print("混淆矩阵:")
print(confusion_matrix(y_test, test_pred))

3. 重点理解三个问题

第一,为什么要使用 scale_pos_weight?

正常交易远多于欺诈交易。如果不进行任何调整,模型可能更关注数量占绝大多数的正常交易。

scale_pos_weight 用于提高正类样本在目标函数中的相对权重。常见初始设置是:

[
w=\frac{N_{\text{负类}}}{N_{\text{正类}}}
]

这样可以增强模型对欺诈样本的关注。

但这个比值只是起点。权重过高也可能增加误报,因此实际项目中应结合验证集、业务成本和模型表现进行调整。

第二,为什么要单独划分验证集和测试集?

训练集用于拟合模型;验证集用于选择分类阈值和调整模型;测试集则用于评估最终方案的泛化能力。

如果反复根据测试集结果修改阈值,测试集就不再是独立评估依据,最终表现容易产生乐观偏差。

对于真实交易场景,最好进一步采用按时间划分的数据集:使用较早的交易训练模型,再用后续交易验证和测试,避免随机切分掩盖时间漂移。

第三,为什么不直接使用 0.5 作为分类阈值?

predict_proba 输出的是模型估计的正类概率,0.5 只是常见的默认分类阈值,并不是业务上的最佳阈值。

例如:

  • 阈值较低:更多交易会被标记为可疑,通常有利于减少漏报,但可能增加误报。
  • 阈值较高:触发告警的交易减少,但可能漏掉更多欺诈交易。

代码使用验证集上的 F1 分数选择阈值,是一个便于教学的起点。

但在实际风控中,F1 最大不一定意味着业务收益最大。如果漏掉一笔大额欺诈的损失远高于人工审核一笔正常交易的成本,就应当根据损失函数、审核能力和风险容忍度确定阈值,而不是机械地最大化 F1。

4. 如何看懂评价结果?

本案例重点关注以下指标:

指标 业务含义
Precision(精确率) 被模型判定为欺诈的交易中,真正欺诈的比例
Recall(召回率) 全部真实欺诈交易中,被模型识别出来的比例
F1 Precision 与 Recall 的调和平均
Average Precision(AP) 综合评价不同分类阈值下精确率与召回率表现的指标
混淆矩阵 直观统计正常交易、欺诈交易的正确识别和误判数量

对于严重不平衡的数据,AP 通常比单纯的准确率更有参考价值。

不过,模型指标最终必须转化为业务指标。例如,每识别 100 笔可疑交易,需要人工审核多少笔?漏掉的欺诈交易造成多少损失?这些问题决定了模型在生产环境中是否真正有效。

本案例的 GitHub 实践代码可参考:

https://github.com/too-baa/credit-card-fraud-detection

四、掌握 XGBoost 时,哪些参数最重要?

初学阶段不必一次性研究所有参数。以下参数足以帮助你理解大部分实际建模过程。

参数 作用 调整方向
n_estimators 树的数量 树太少可能欠拟合,太多可能过拟合且增加训练成本
learning_rate 每棵树的学习步长 越小通常需要更多树,训练也可能更慢
max_depth 单棵树的最大深度 深度越大,模型越复杂,也越容易过拟合
min_child_weight 限制子节点所需的最小二阶梯度权重 增大通常会让模型更加保守
subsample 每棵树使用的训练样本比例 适度采样有助于降低过拟合
colsample_bytree 每棵树使用的特征比例 减少特征采样比例有时能提升泛化能力
reg_alpha L1 正则化系数 增大通常会加强对叶节点权重的约束
reg_lambda L2 正则化系数 增大通常会让模型更保守
scale_pos_weight 正类相对权重 用于处理不平衡分类,需结合验证集调整

一个实用的调参顺序是:

  1. 先建立简单基线,确认数据切分和评价指标没有问题。
  2. 调整 max_depth、min_child_weight,控制单棵树的复杂度。
  3. 调整 learning_rate 和 n_estimators,平衡训练轮数与学习步长。
  4. 尝试 subsample、colsample_bytree 和正则化参数,改善泛化能力。
  5. 使用交叉验证或早停机制,判断增加树的数量是否仍然带来有效收益。

不建议一开始就对十几个参数进行大规模网格搜索。先搞清楚模型在哪些数据上表现不好,再决定需要调整什么,通常更有效。

五、从两个案例中总结一套建模流程

无论预测房价,还是识别欺诈交易,XGBoost 的基本工作流程都可以概括为:

明确业务目标 → 整理数据 → 划分训练、验证和测试集 → 建立基线 → 训练 XGBoost → 选择指标与阈值 → 评估泛化能力 → 部署与监控。

其中最容易被忽略的,恰恰不是算法本身,而是数据与评价设计。

房价预测需要防止高价异常样本影响模型,合理处理类别变量,并选择符合实际业务含义的误差指标。

欺诈识别需要处理严重的类别不平衡,关注漏报与误报的权衡,并确保阈值选择没有使用测试集信息。

在金融风险、信用评分或违约损失预测等领域,还需要进一步考虑样本选择偏差、时间漂移、模型校准、解释性和模型验证要求。XGBoost 可以作为强有力的预测工具,但不能代替完整的风险管理流程。

最后记住一点:XGBoost 的价值不在于把树的数量堆得足够多,而在于通过合理的数据处理、目标函数、正则化和验证方法,让模型学到可以泛化到新样本的规律。 先把这两个案例跑通,再深入学习特征工程、早停、交叉验证和模型解释,会更容易建立完整的技术体系。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。