[机器学习+pyspark库包]xgboost模型训练(实战)

217 阅读6分钟

下面是一个使用 PySpark库包 训练 XGBoost 模型的详细步骤指南,包含模拟数据生成和完整代码。

由于 PySpark 本身不直接支持 XGBoost, 我们将使用 sparkxgb 包(XGBoost4J-Spark 的 Python 包装器)。

这里需要重点说明一下:

(Spark原生MLlib并不直接支持XGBoost算法),

(Spark原生MLlib是支持随机森林模型算法的, 如 from pyspark.ml.classification import RandomForestClassifier)

先决条件

安装必要的包:

pip install xgboost==1.7.6 pyspark==3.4.1 sparkxgb==0.4.1

完整代码示例

from pyspark.sql import SparkSession
from pyspark.ml.feature import VectorAssembler
from sparkxgb import XGBoostClassifier  # 注意:需要安装sparkxgb
from pyspark.ml.evaluation import BinaryClassificationEvaluator, MulticlassClassificationEvaluator
import numpy as np
import pandas as pd

# 步骤 1:初始化 Spark 会话
spark = SparkSession.builder \
    .appName("XGBoost-Example") \
    .config("spark.jars.packages", "ml.dmlc:xgboost4j-spark_2.12:1.7.6") \
    .config("spark.driver.memory", "4g") \
    .getOrCreate()

# 步骤 2:生成模拟数据
def generate_data(n_samples=10000, seed=42):
    np.random.seed(seed)
    data = {
        'age': np.random.randint(18, 70, n_samples),
        'income': np.random.normal(50000, 15000, n_samples),
        'credit_score': np.random.randint(300, 850, n_samples),
        'debt_ratio': np.random.uniform(0.1, 0.8, n_samples),
        'existing_loans': np.random.poisson(2, n_samples),
        'default': np.random.binomial(1, 0.3, n_samples)  # 30% 的违约概率
    }
    
    # 添加一些特征交互
    data['risk_factor'] = data['age'] * 0.1 + data['debt_ratio'] * 5
    data['affordability'] = data['income'] / (data['existing_loans'] + 1) * 0.01
    
    return pd.DataFrame(data)

# 创建 10,000 个样本
pdf = generate_data(n_samples=10000)
df = spark.createDataFrame(pdf)

# 查看数据结构
print("数据样例:")
df.show(5)
print(f"数据集大小: {df.count()} 行")

# 步骤 3:数据预处理
# 选择特征列
feature_cols = ['age', 'income', 'credit_score', 'debt_ratio', 'existing_loans', 'risk_factor', 'affordability']

# 创建特征向量
assembler = VectorAssembler(
    inputCols=feature_cols,
    outputCol="features"
)

df = assembler.transform(df).select("features", "default")

# 重命名标签列(XGBoost要求标签列名为"label")
df = df.withColumnRenamed("default", "label")

# 步骤 4:划分训练集/测试集
train_ratio, test_ratio = 0.8, 0.2
train_data, test_data = df.randomSplit([train_ratio, test_ratio], seed=42)

print(f"训练集数量: {train_data.count()}")
print(f"测试集数量: {test_data.count()}")

# 步骤 5:配置并训练 XGBoost 模型
xgb = XGBoostClassifier(
    featuresCol="features",
    labelCol="label",
    numWorkers=2,           # 并行工作节点数
    nthread=4,              # 每个worker的线程数
    numRound=100,           # 提升轮数(树的数量)
    maxDepth=6,             # 最大深度
    eta=0.3,                # 学习率
    subsample=0.8,          # 样本采样率
    colsampleBytree=0.8,    # 特征采样率
    objective="binary:logistic",  # 二分类目标函数
    eval_metric="auc",      # 评估指标
    earlyStoppingRounds=10, # 早停轮数
    seed=42
)

# 训练模型
model = xgb.fit(train_data)

# 步骤 6:模型评估
# 在测试集上进行预测
predictions = model.transform(test_data)

# 显示预测结果
print("\n预测结果样例:")
predictions.select("label", "prediction", "probability").show(10, truncate=False)

# 计算评估指标
# 1. 准确率
evaluator_acc = MulticlassClassificationEvaluator(
    labelCol="label",
    predictionCol="prediction",
    metricName="accuracy"
)
accuracy = evaluator_acc.evaluate(predictions)

# 2. AUC
evaluator_auc = BinaryClassificationEvaluator(
    labelCol="label",
    rawPredictionCol="rawPrediction",
    metricName="areaUnderROC"
)
auc = evaluator_auc.evaluate(predictions)

# 3. F1 分数
evaluator_f1 = MulticlassClassificationEvaluator(
    labelCol="label",
    predictionCol="prediction",
    metricName="f1"
)
f1 = evaluator_f1.evaluate(predictions)

print("\n模型评估结果:")
print(f"测试集准确率: {accuracy:.4f}")
print(f"AUC 值: {auc:.4f}")
print(f"F1 分数: {f1:.4f}")

# 步骤 7:特征重要性分析
feature_importances = model.nativeBooster().get_score(importance_type='weight')
print("\n特征重要性:")
for feature, importance in sorted(feature_importances.items(), key=lambda x: x[1], reverse=True):
    print(f"{feature}: {importance}")

# 步骤 8:模型保存与加载(可选)
model_path = "xgboost_model"
model.write().overwrite().save(model_path)
print(f"\n模型已保存到: {model_path}")

# 加载模型示例
# from sparkxgb import XGBoostClassificationModel
# loaded_model = XGBoostClassificationModel.load(model_path)

# 步骤 9:停止 Spark 会话
spark.stop()

详细步骤说明

  1. 环境准备

    • 初始化 SparkSession 并配置 XGBoost 依赖
    • 指定 spark.jars.packages 加载 XGBoost4J-Spark 库
    • 分配足够内存(根据数据集大小调整)
  2. 数据生成

    • 创建包含 7 个特征的模拟金融数据集:
      • 年龄、收入、信用评分、债务比率
      • 现有贷款数量、风险因子、可负担性
    • 目标变量:default(是否违约)
    • 添加了特征交互增强数据真实性
  3. 数据预处理

    • 使用 VectorAssembler 将特征列合并为特征向量
    • 重命名目标列为 label(XGBoost 要求)
    • 数据格式转换:DataFrame → 特征向量
  4. 数据划分

    • 按 80:20 比例划分训练集/测试集
    • 设置随机种子确保可复现性
  5. 模型配置

    • 关键参数说明:
      • numWorkers:并行执行器数量(根据集群规模设置)
      • numRound:树的数量(提升迭代次数)
      • maxDepth:单棵树的最大深度
      • eta:学习率(控制过拟合)
      • subsample:样本采样率(随机森林特性)
      • colsampleBytree:特征采样率
      • earlyStoppingRounds:验证集性能不再提升时提前停止
  6. 模型训练

    • 使用 fit() 方法训练模型
    • 训练过程自动处理特征向量格式
  7. 模型评估

    • 计算三大关键指标:
      • 准确率(Accuracy)
      • AUC(衡量排序能力)
      • F1 分数(平衡精确率与召回率)
    • 展示预测概率分布
  8. 特征重要性

    • 使用 get_score() 方法获取特征重要性
    • 按重要性降序排列特征
  9. 模型部署

    • 使用 Spark ML 的保存/加载机制
    • 支持分布式存储(HDFS/S3)

参数调优建议

  1. 学习率 (eta)

    • 典型范围:[0.01, 0.3]
    • 较低值:更稳健但需要更多树
    • 较高值:更快收敛但可能过拟合
  2. 树的数量 (numRound)

    • 与学习率配合:eta 越小,需要越多树
    • 使用早停自动确定最优树数量
  3. 正则化参数

    • lambda:L2 正则化(默认 1)
    • alpha:L1 正则化(默认 0)
    • 增加这些值可减少过拟合
  4. 采样参数

    • subsample:控制每棵树使用的样本比例
    • colsampleBytree:控制每棵树使用的特征比例
    • 典型值:[0.7, 0.9]

性能优化技巧

  1. 数据缓存

    train_data.cache()  # 缓存训练数据加速迭代
    
  2. 并行度调整

    • 增加 numWorkers 到集群执行器数量
    • 设置 nthread 为每个 worker 的 CPU 核心数
  3. 特征工程

    • 添加交互特征(如代码中的 risk_factor)
    • 对连续特征进行分桶
    • 标准化数值特征
  4. 分布式训练

    # 在集群配置中添加
    .config("spark.task.cpus", "4")  # 每个任务CPU核心
    .config("spark.executor.instances", "8")  # 执行器数量
    

典型输出示例

数据样例:
+---+------+------------+----------+-------------+----------+------------+-------+
|age|income|credit_score|debt_ratio|existing_loans|risk_factor|affordability|default|
+---+------+------------+----------+-------------+----------+------------+-------+
| 51| 62305|         623|    0.4135|            2|     4.535|     311.525|      0|
| 34| 32980|         582|    0.6512|            1|     6.256|     329.800|      1|
| 68| 45992|         712|    0.2467|            0|     4.467|     459.920|      0|
+---+------+------------+----------+-------------+----------+------------+-------+

训练集数量: 7982
测试集数量: 2018

预测结果样例:
+-----+----------+---------------------------------------+
|label|prediction|probability                            |
+-----+----------+---------------------------------------+
|0    |0.0       |[0.8132284283638,0.18677155673503876]  |
|1    |1.0       |[0.2102809101343155,0.7897191047668457]|
|0    |0.0       |[0.7243872880935669,0.2756127119064331]|
+-----+----------+---------------------------------------+

模型评估结果:
测试集准确率: 0.7823
AUC 值: 0.8617
F1 分数: 0.7124

特征重要性:
f5: 142.0  # affordability
f2: 120.0  # income
f4: 98.0   # risk_factor
f3: 85.0   # credit_score
f1: 76.0   # age
f0: 65.0   # debt_ratio
f6: 52.0   # existing_loans

模型已保存到: xgboost_model

常见问题解决

  1. ClassNotFoundException

    • 确保 spark.jars.packages 配置正确
    • 检查 Scala 版本兼容性(通常为 2.12)
  2. 内存不足

    • 增加 driver/executor 内存:
      .config("spark.driver.memory", "8g")
      .config("spark.executor.memory", "8g")
      
  3. 性能优化

    • 对连续特征进行分桶
    • 减少分类特征的基数
    • 使用 persist(StorageLevel.MEMORY_AND_DISK) 缓存数据
  4. 处理类别特征

    from pyspark.ml.feature import StringIndexer
    
    indexer = StringIndexer(inputCol="category", outputCol="categoryIndex")
    df = indexer.fit(df).transform(df)
    # 然后将 categoryIndex 加入特征向量
    

这个流程提供了完整的 XGBoost 模型训练实现,从数据生成到模型评估,适用于二分类问题。对于回归或多分类问题,只需调整 objective 参数(如 reg:squarederrormulti:softmax)。