3. 模型训练与导出:基于 XGBoost 的异常交易分类模型
本章将使用 Python 训练一个基于 XGBoost 的异常交易二分类模型,并将其导出为 ONNX 格式。ONNX(Open Neural Network Exchange)是一种开放的模型表示标准,能够使模型在不同框架和推理引擎之间无缝迁移,是实时风控系统部署的关键步骤。
前置条件:确保已安装
xgboost、onnx、sklearn、pandas、numpy 以及 skl2onnx 库。建议使用 Python 3.8+ 环境。
3.1 数据准备与特征工程
我们模拟一个包含正常交易与异常交易的金融数据集。特征包括交易金额、交易时间、用户历史行为统计等。以下为数据加载与预处理的核心步骤:
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
# 加载示例数据(实际场景中替换为真实数据源)
df = pd.read_csv('transaction_data.csv')
# 特征列与标签列
feature_cols = ['amount', 'hour_of_day', 'day_of_week', 'user_avg_amount',
'user_txn_count_1h', 'merchant_risk_score', 'distance_from_home']
target_col = 'is_fraud'
X = df[feature_cols]
y = df[target_col]
# 处理缺失值
X.fillna(X.median(), inplace=True)
# 划分训练集与测试集
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42, stratify=y
)
# 标准化(XGBoost 对尺度不敏感,但 ONNX 推理时建议保持一致性)
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)
注意:标准化器(
StandardScaler)需要在训练集上拟合,并保存其参数(均值、方差)。在 ONNX 导出时,需要将标准化步骤与模型合并,或作为预处理节点单独部署。
3.2 XGBoost 模型训练
XGBoost 在异常检测任务中表现优异,尤其适合处理不平衡数据集。我们使用 scale_pos_weight 参数来缓解正负样本不平衡问题。
import xgboost as xgb
# 计算正负样本比例
neg_count = (y_train == 0).sum()
pos_count = (y_train == 1).sum()
scale_pos_weight = neg_count / pos_count
# 定义 XGBoost 分类器
model = xgb.XGBClassifier(
n_estimators=200,
max_depth=6,
learning_rate=0.1,
subsample=0.8,
colsample_bytree=0.8,
scale_pos_weight=scale_pos_weight,
eval_metric='auc',
use_label_encoder=False,
random_state=42
)
# 训练模型
model.fit(
X_train_scaled, y_train,
eval_set=[(X_test_scaled, y_test)],
verbose=False
)
# 评估模型
from sklearn.metrics import classification_report, roc_auc_score
y_pred = model.predict(X_test_scaled)
y_proba = model.predict_proba(X_test_scaled)[:, 1]
print("AUC-ROC:", roc_auc_score(y_test, y_proba))
print(classification_report(y_test, y_pred))
| 参数 | 值 | 说明 |
|---|---|---|
n_estimators |
200 | 树的数量,控制模型复杂度 |
max_depth |
6 | 树的最大深度,防止过拟合 |
scale_pos_weight |
neg/pos | 正负样本权重平衡 |
eval_metric |
auc | 使用 AUC 作为早停评估指标 |
3.3 模型导出为 ONNX 格式
使用 skl2onnx 库将训练好的 XGBoost 模型转换为 ONNX 格式。关键步骤包括:定义输入特征的类型与形状,以及将标准化器与模型组合成一个完整的推理管道。
from skl2onnx import convert_sklearn
from skl2onnx.common.data_types import FloatTensorType
import onnx
# 定义 ONNX 输入类型(特征数量为 7)
initial_type = [('float_input', FloatTensorType([None, X_train_scaled.shape[1]]))]
# 将标准化器与 XGBoost 模型组合成管道
from sklearn.pipeline import Pipeline
pipeline = Pipeline([
('scaler', scaler),
('xgb', model)
])
# 转换管道为 ONNX
onnx_model = convert_sklearn(
pipeline,
initial_types=initial_type,
target_opset=12 # 推荐使用 opset 12 或更高
)
# 保存 ONNX 模型
onnx_path = 'fraud_detection_xgb.onnx'
onnx.save_model(onnx_model, onnx_path)
print(f"ONNX 模型已保存至: {onnx_path}")
关键点:将
StandardScaler 与 XGBClassifier 封装在 Pipeline 中一起导出,可以确保推理时输入数据自动完成标准化,无需在部署端额外编写预处理代码。
3.4 ONNX 模型验证
导出后,必须使用 ONNX Runtime 对模型进行推理验证,确保输出结果与原始 XGBoost 模型一致。
import onnxruntime as ort
import numpy as np
# 创建 ONNX Runtime 会话
session = ort.InferenceSession(onnx_path)
input_name = session.get_inputs()[0].name
# 取测试集前 5 条样本进行验证
sample = X_test_scaled[:5].astype(np.float32)
# ONNX 推理
onnx_output = session.run(None, {input_name: sample})[0]
onnx_proba = 1 / (1 + np.exp(-onnx_output[:, 1])) # 转换为概率
# 原始模型推理
original_proba = model.predict_proba(sample)[:, 1]
# 对比结果
print("ONNX 概率:", onnx_proba)
print("原始概率:", original_proba)
print("最大差异:", np.max(np.abs(onnx_proba - original_proba)))
3.5 模型导出参数对比
不同的 ONNX opset 版本对算子支持程度不同。下表列出了常用 opset 版本与 XGBoost 的兼容性:
| ONNX opset | XGBoost 兼容性 | 推荐场景 |
|---|---|---|
| 11 | 基本支持 | 旧版推理引擎兼容 |
| 12 | 完全支持 | 通用部署(推荐) |
| 13+ | 完全支持 | 需要新算子时使用 |
最佳实践:建议使用
target_opset=12,它在兼容性与新特性之间取得了良好平衡。如果部署环境较老,可降级至 opset 11。
3.6 导出后的文件结构
ONNX 模型文件是一个独立的二进制文件,通常包含完整的计算图与权重。以下为导出后的典型文件清单:
- fraud_detection_xgb.onnx — 主模型文件,可直接被 ONNX Runtime 加载。
- scaler_params.pkl (可选)— 如果未将标准化器封装进 Pipeline,则需要单独保存标准化参数。
- feature_columns.json (可选)— 记录特征顺序与名称,用于部署端输入校验。
重要:ONNX 模型文件不包含特征名称信息。部署时,必须确保输入张量的顺序与训练时完全一致。建议在导出时记录特征列表,并在推理服务启动时进行校验。
3.7 常见问题与排查
- ONNX 导出报错 "Unsupported operator":通常是因为 XGBoost 版本与 skl2onnx 版本不匹配。建议升级
skl2onnx至最新版,或降级 XGBoost 至 1.6.x 系列。 - 推理结果与原始模型不一致:检查 Pipeline 中是否遗漏了预处理步骤(如缺失值填充、独热编码)。ONNX 模型仅包含导出时 Pipeline 内的操作。
- ONNX Runtime 加载失败:确认 ONNX 文件路径正确,且 opset 版本被当前 Runtime 支持。可使用
onnx.checker.check_model()进行完整性校验。