一、前言
在人工智能技术体系中,XGBoost作为经典的梯度提升树模型,凭借高效的特征学习能力和优秀的结构化数据处理性能,长期占据机器学习应用的核心地位;而大模型则以其强大的语义理解、上下文建模和通用推理能力,成为自然语言处理、多模态分析等领域的颠覆性技术。将XGBoost与大模型结合,既能发挥XGBoost在结构化数据建模上的精准性和可解释性,又能借助大模型处理非结构化数据(文本、语音、图像)的优势,构建更全面、更强大的 AI 系统,今天我们由浅入深拆解 XGBoost 与大模型融合的全链路知识,深度了解其核心原理和应用实践。
二、基础知识
1. XGBoost 核心概念
1.1 梯度提升树(GBT)基础
梯度提升树是集成学习的核心分支,其核心思想是“逐步修正错误”:通过串行训练多个弱分类器,通常是决策树,每个新的弱分类器都专注于修正前一轮模型的预测误差,最终将所有弱分类器的预测结果加权求和,得到强分类器。
举个通俗的例子,假设我们要预测一个商品的销量:
- 第一个决策树根据“价格”特征预测销量为1000件,但实际销量是1200件,误差为200 件;
- 第二个决策树则专门学习“价格 + 促销活动”特征来修正这200件的误差,预测补充值为180件;
- 第三个决策树再学习“价格 + 促销 + 季节”特征修正剩余20件误差,最终三个树的结果相加得到精准预测。
1.2 XGBoost 的核心优势
XGBoost是梯度提升树的优化版本,相比传统 GBT,其核心优势体现在:
- 1. 正则化优化:在损失函数中加入 L1(Lasso)和 L2(Ridge)正则项,有效防止过拟合;
- 2. 并行化训练:在构建决策树时,对特征的分裂点选择进行并行计算,大幅提升训练速度;
- 3. 缺失值处理:自动学习缺失值的最优分裂方向,无需手动填充;
- 4. 预排序与直方图优化:支持预排序和直方图两种分裂策略,平衡精度与效率;
- 5. 剪枝策略:采用 “贪心 + 正则” 的剪枝方式,从叶子节点向上剪枝,保留最优树结构。
1.3 XGBoost 的关键参数
掌握以下核心参数是使用 XGBoost 的基础:
- n_estimators:弱学习器数量,常用值100-1000
- max_depth:决策树最大深度,常用值3-10
- learning_rate:学习率,即步长,常用值0.01-0.3
- subsample:样本采样比例,常用值0.7-1.0
- colsample_bytree:特征采样比例,常用值0.7-1.0
- reg_alpha:L1 正则化系数,常用值0-10
- reg_lambda:L2 正则化系数,常用值1-10
- objective:目标函数,常用值的分类函数:binary:logistic/multi:softmax;回归函数:reg:squarederror
2. XGBoost 与大模型结合
2.1 大模型的核心痛点
尽管大模型能力强大,但在实际应用中存在明显短板:
- 1. 结构化数据处理能力弱:大模型擅长处理文本类非结构化数据,但对表格、数值型结构化数据的建模能力远不如XGBoost;
- 2. 计算成本高:训练和推理大模型需要昂贵的GPU/TPU资源,单条推理成本远高于传统机器学习模型;
- 3. 可解释性差:大模型的黑箱特性导致其预测结果难以解释,无法满足金融、医疗等领域的合规要求;
- 4. 过拟合特定模式:在小样本、强规律的结构化数据任务中,大模型的泛化能力不如XGBoost;
- 5. 数值计算精度低:大模型对数值的精准计算能力弱,无法替代传统模型完成高精度的回归、分类任务。
2.2 结合的核心逻辑
将 XGBoost 与大模型结合,本质是扬长避短、优势互补:
- 大模型负责处理非结构化数据,如用户评论、商品描述、文档内容,将其转化为高质量的语义特征(Embedding);
- XGBoost负责处理结构化数据(如用户年龄、商品价格、交易金额)+ 大模型输出的语义特征,完成精准的预测或分类任务;
- 同时,XGBoost 的可解释性可以弥补大模型黑箱的缺陷,大模型的语义理解能力可以拓展 XGBoost 的特征维度。
举个实际场景:电商平台的用户购买预测;
- 大模型将用户的历史评论、商品标题、客服对话转化为语义 Embedding;
- XGBoost 结合用户的消费金额、购买频次、商品类别等结构化数据,以及大模型输出的 Embedding 特征;最终预测用户是否会购买某商品。
- 这种融合方案的准确率远高于单独使用 XGBoost,缺少语义特征,也高于单独使用大模型,因为大模型的数值建模能力弱。
三、融合的核心原理
1. 特征融合原理
XGBoost 与大模型的融合本质是特征级融合,核心步骤为:
- 1. 特征提取:
- 结构化数据:直接提取数值型 / 类别型特征(如年龄、性别、价格),类别型特征需进行编码(One-Hot/Label Encoding);
- 非结构化数据:通过大模型提取 Sentence Embedding,转化为固定维度的数值向量(如 768 维)。
- 2. 特征拼接:将结构化特征向量与大模型输出的 Embedding 向量拼接,形成融合特征矩阵;
- 3. 特征训练:将融合特征矩阵输入 XGBoost,利用 XGBoost 的梯度提升机制进行训练,得到最终模型。
这种融合方式的优势在于:
- 保留了结构化数据的精准数值信息;
- 融入了非结构化数据的语义信息;
- XGBoost 能够自动学习不同特征的重要性,权重高的特征会被优先分裂。
2. 模型互补原理
XGBoost 与大模型的互补性体现在多个维度:
| 维度 | XGBoost | 大模型 | 融合优势 |
| 数据类型 | 擅长结构化数据 | 擅长非结构化数据 | 同时处理两类数据 |
| 可解释性 | 高(特征重要性、决策路径) | 低(黑箱) | 用 XGBoost 提升整体可解释性 |
| 计算成本 | 低(CPU 即可训练) | 高(需 GPU) | 仅用大模型做 Embedding 提取,降低整体成本 |
| 数值精度 | 高(精准拟合数值规律) | 低(数值计算误差大) | 用 XGBoost 保证预测精度 |
| 语义理解 | 无(无法处理文本语义) | 高(强语义建模) | 用大模型拓展特征维度 |
3. 数据一致性处理
在实际融合过程中,会遇到一些数据匹配度的问题,以下提供了一些常规的解决方案:
3.1特征维度匹配
- 大模型输出的 Embedding 维度通常较高,如768维,而结构化特征维度较低,如几十维,可能导致特征分布不均衡。
- 解决方案:对 Embedding 进行降维(PCA/TSNE)或特征选择(如基于方差的筛选);
3.2 特征尺度差异
- 结构化特征(如年龄:0-100)与 Embedding 特征(如 - 1~1 之间的浮点数)尺度差异大,可能影响 XGBoost 的分裂决策。
- 解决方案:对所有特征进行标准化、归一化;
3.3 过拟合风险
- 高维度的 Embedding 特征可能导致XGBoost过拟合。
- 解决方案:增加 XGBoost 的正则化参数(reg_alpha/reg_lambda)、降低树深度(max_depth)、使用早停(early stopping);
3.4. Embedding 一致性
- 不同批次的文本输入大模型,可能输出分布略有差异的 Embedding,导致 XGBoost 模型泛化能力下降。
- 解决方案:对大模型的 Embedding 进行归一化(L2 归一化),保证向量长度一致。
四、融合的执行流程
1. 执行流程
XGBoost 与大模型融合的完整执行流程可分为 6 个核心步骤:
2. 分步详解
步骤 1:数据收集
数据收集是融合模型的基础,需要同时收集两类数据:
2.1 结构化数据:
- 数值型:如用户年龄、消费金额、商品价格、交易次数、评分等;
- 类别型:如用户性别、商品类别、地区、支付方式等;
- 时间型:如购买时间、注册时间、活动时间等(需转化为数值特征,如时间差、小时 / 天 / 月等)。
示例结构化数据集(电商用户购买预测):
| 用户 ID | 年龄 | 性别 | 消费金额 | 购买频次 | 商品类别 | 评分 | 是否购买(标签) |
| 1001 | 25 | 男 | 500 | 10 | 电子产品 | 4.5 | 1 |
| 1002 | 32 | 女 | 1200 | 25 | 美妆 | 4.8 | 1 |
| 1003 | 45 | 男 | 800 | 5 | 服装 | 3.2 | 0 |
2. 非结构化数据:
- 文本型:如用户评论(“这款手机续航太差了”)、商品标题(“2024 新款华为 Mate60 Pro 12GB+512GB”)、客服对话、用户画像描述等;
- 其他类型:如商品图片,可通过视觉大模型转化为 Embedding、用户语音,可通过语音大模型转化为文本再提取 Embedding。
示例非结构化数据(对应上述结构化数据):
| 用户 ID | 用户评论 | 商品标题 |
| 1001 | 这款手机性能很好,值得购买 | 2024 新款华为 Mate60 Pro 12GB+512GB |
| 1002 | 这款粉底液遮瑕效果超棒,推荐! | 兰蔻持妆粉底液 PO-01 30ml |
| 1003 | 这件衣服尺码偏小,材质一般 | 优衣库男士纯棉 T 恤 458762 |
步骤 2:数据预处理
预处理的目标是将原始数据转化为模型可处理的格式,分为两部分:
1. 结构化数据预处理:
- 缺失值处理:数值型特征用均值、中位数、众数填充,类别型特征用“未知”填充或基于业务逻辑填充;
- 类别特征编码:二分类特征(如性别)用Label Encoding(0/1),多分类特征(如商品类别)用 One-Hot Encoding或Target Encoding;
- 数值特征归一化:对数值型特征进行标准化(Z-Score)或归一化(Min-Max),使特征尺度统一;
- 时间特征转化:将时间戳转化为小时、天、周、月、季度等特征,或计算时间差。
2. 非结构化数据预处理:
- 文本清洗:去除特殊字符、标点、多余空格,统一大小写,纠正错别字;
- 分词:将文本拆分为单词、中文分词,如使用Jieba分词;
- 去停用词:去除无意义的词汇,如“的”、“了”、“a”、“the”;
- 文本长度统一:对过长、过短的文本进行截断、补全,保证输入大模型的文本长度一致。
步骤 3:大模型 Embedding 提取
这是融合的核心步骤,目标是将文本转化为语义Embedding:
1. 选择合适的大模型:
- 开源模型:LLaMA2、ChatGLM、Qwen等,适合本地部署;
- 闭源 API:OpenAI Embedding、通义Embedding 等,适合快速开发。
- 推荐使用开源的 BERT-base 模型,中文的为bert-base-chinese,参数量小(110M),部署成本低,Embedding效果好。
2. Embedding 提取流程:
- 加载预训练大模型和 Tokenizer;
- 将预处理后的文本输入 Tokenizer,转化为模型可识别的 token;
- 将 token 输入大模型,获取输出的 Hidden States;
- 对 Hidden States 进行池化,如均值池化、最大值池化、[CLS] token等,得到 Sentence Embedding;
- 对 Embedding 进行 L2 归一化,保证向量长度为 1,提升稳定性。
3. Embedding 降维:
- 如果 Embedding 维度过高,如768维,可使用 PCA 进行降维,保留 95% 以上的方差,减少特征维度,提升 XGBoost 训练速度。
步骤 4:特征融合
特征融合的目标是将结构化特征与 Embedding 特征结合:
- 1. 特征拼接:将预处理后的结构化特征矩阵(n_samples × n_structured_features)与 Embedding 特征矩阵(n_samples × n_embedding_features)按列拼接,得到融合特征矩阵(n_samples × (n_structured_features + n_embedding_features));
- 2. 特征选择:
- 方差筛选:去除方差过小的特征,如全部为0的特征;
- 相关性筛选:计算特征与标签的相关性,去除相关性极低的特征;
- 递归特征消除(RFE):使用 XGBoost 的特征重要性,递归去除不重要的特征;
- 3. 特征验证:检查融合后的特征是否存在缺失值、异常值,确保数据质量。
步骤 5:XGBoost 模型训练
这一步是传统 XGBoost 的训练流程,但需要针对融合特征进行参数调优:
- 1. 数据集划分:将融合特征矩阵按 7:2:1 的比例划分为训练集、验证集、测试集;
- 2. 参数初始化:设置 XGBoost 的基础参数,如n_estimators=100、max_depth=5、learning_rate=0.1;
- 3. 模型训练:使用训练集训练模型,验证集监控过拟合,通过早停策略:当验证集损失连续 n 轮不下降时停止训练;
- 4. 参数调优:使用网格搜索、随机搜索、贝叶斯优化调优核心参数,如max_depth、learning_rate、reg_alpha、reg_lambda,提升模型性能;
- 5. 模型保存:将训练好的 XGBoost 模型保存为文件,如 JSON/PMML 格式,方便后续部署。
步骤 6:模型评估与部署
1. 模型评估:
- 分类任务:计算准确率(ACC)、精确率(Precision)、召回率(Recall)、F1 值、AUC-ROC 等指标;
- 回归任务:计算均方误差(MSE)、平均绝对误差(MAE)、R² 等指标;
- 可解释性分析:输出 XGBoost 的特征重要性,分析哪些特征对预测结果影响最大,包括结构化特征和 Embedding 特征;
- 对比实验:与单独使用 XGBoost(无 Embedding)、单独使用大模型直接预测的结果对比,验证融合方案的优势。
2. 模型部署:
- 离线部署:将模型集成到批处理系统中,处理离线数据;
- 在线部署:搭建 API 服务,如 FastAPI/Flask,接收结构化特征和文本数据,实时提取 Embedding 并调用 XGBoost 模型预测;
- 性能优化:对 Embedding 提取过程进行缓存,对 XGBoost 模型进行量化,提升在线推理速度。
流程简化总结:
五、完整实例实现
步骤 1:导入库
import os import re import numpy as np import pandas as pd import jieba import matplotlib.pyplot as plt import seaborn as sns from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler, OneHotEncoder from sklearn.impute import SimpleImputer from sklearn.compose import ColumnTransformer from sklearn.pipeline import Pipeline from sklearn.decomposition import PCA from sklearn.metrics import accuracy_score, classification_report, roc_auc_score, confusion_matrix from sklearn.feature_selection import VarianceThreshold import xgboost as xgb from xgboost import callback as xgb_callback from transformers import BertTokenizer, BertModel import torch import warnings warnings.filterwarnings('ignore') # 设置中文字体(解决matplotlib中文显示问题) plt.rcParams['font.sans-serif'] = ['SimHei'] plt.rcParams['axes.unicode_minus'] = False # 设置设备(优先使用GPU) device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') print(f"使用设备:{device}")
输出结果:
使用设备:cpu
步骤 2:数据准备与预处理
# ====================== 1. 生成示例数据(电商用户购买预测) ====================== def generate_sample_data(): """生成示例结构化数据和文本数据""" # 结构化数据 structured_data = pd.DataFrame({ 'user_id': range(1000, 1500), 'age': np.random.randint(18, 60, size=500), 'gender': np.random.choice(['男', '女'], size=500, p=[0.5, 0.5]), 'consumption_amount': np.random.normal(1000, 300, size=500).round(2), 'purchase_frequency': np.random.randint(1, 30, size=500), 'product_category': np.random.choice(['电子产品', '美妆', '服装', '食品', '家居'], size=500), 'rating': np.random.uniform(1, 5, size=500).round(1), 'is_purchase': np.random.choice([0, 1], size=500, p=[0.4, 0.6]) }) # 文本数据(用户评论 + 商品标题) comments = [ "这款产品性能很好,值得购买", "质量一般,价格偏高", "使用体验很棒,推荐!", "物流很慢,包装破损", "性价比超高,会回购", "尺寸不合适,材质差", "比预期的好,满意", "售后服务差,不推荐" ] product_titles = [ "2024新款华为Mate60 Pro 12GB+512GB", "兰蔻持妆粉底液PO-01 30ml", "优衣库男士纯棉T恤458762", "三只松鼠坚果大礼包1500g", "小米空气净化器4 Pro", "美的电饭煲5L大容量", "苹果AirPods Pro 2代", "Nike Air Max 270运动鞋" ] text_data = pd.DataFrame({ 'user_id': range(1000, 1500), 'user_comment': np.random.choice(comments, size=500), 'product_title': np.random.choice(product_titles, size=500) }) # 合并文本数据(将评论和标题拼接) text_data['combined_text'] = text_data['user_comment'] + " " + text_data['product_title'] # 合并结构化数据和文本数据 data = pd.merge(structured_data, text_data, on='user_id') return data # 生成数据 data = generate_sample_data() print("数据基本信息:") print(data.info()) print("\n数据前5行:") print(data.head())
输出结果:
数据基本信息:
RangeIndex: 500 entries, 0 to 499
Data columns (total 11 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 user_id 500 non-null int64
1 age 500 non-null int32
2 gender 500 non-null str
3 consumption_amount 500 non-null float64
4 purchase_frequency 500 non-null int32
5 product_category 500 non-null str
6 rating 500 non-null float64
7 is_purchase 500 non-null int64
8 user_comment 500 non-null str
9 product_title 500 non-null str
10 combined_text 500 non-null str
dtypes: float64(2), int32(2), int64(2), str(5)
memory usage: 100.2 KB
None
数据前5行:
user_id age gender consumption_amount ... is_purchase user_comment product_title combined_text
0 1000 36 男 1508.83 ... 1 物流很慢,包装破损 Nike Air Max 270运动鞋 物流很慢,包装破损 Nike Air Max 270运动鞋
1 1001 50 女 370.92 ... 1 售后服务差,不推荐 Nike Air Max 270运动鞋 售后服务差,不推荐 Nike Air Max 270运动鞋
2 1002 22 男 451.52 ... 0 尺寸不合适,材质差 苹果AirPods Pro 2代 尺寸不合适,材质差 苹果AirPods Pro 2代
3 1003 55 女 716.35 ... 0 质量一般,价格偏高 小米空气净化器4 Pro 质量一般,价格偏高 小米空气净化器4 Pro
4 1004 53 男 1442.72 ... 0 尺寸不合适,材质差 三只松鼠坚果大礼包1500g 尺寸不合适,材质差 三只松鼠坚果大礼包1500g
[5 rows x 11 columns]
步骤 3:大模型 Embedding 提取
# ====================== 2. 结构化数据预处理 ====================== print("====================== 2. 结构化数据预处理 ======================") def preprocess_structured_data(data): """预处理结构化数据""" # 分离特征和标签 X_structured = data[['age', 'gender', 'consumption_amount', 'purchase_frequency', 'product_category', 'rating']] y = data['is_purchase'] # 定义数值特征和类别特征 numeric_features = ['age', 'consumption_amount', 'purchase_frequency', 'rating'] categorical_features = ['gender', 'product_category'] # 数值特征处理管道:缺失值填充 + 标准化 numeric_transformer = Pipeline(steps=[ ('imputer', SimpleImputer(strategy='median')), # 中位数填充缺失值 ('scaler', StandardScaler()) # 标准化 ]) # 类别特征处理管道:缺失值填充 + One-Hot编码 categorical_transformer = Pipeline(steps=[ ('imputer', SimpleImputer(strategy='most_frequent')), # 众数填充缺失值 ('onehot', OneHotEncoder(handle_unknown='ignore')) # One-Hot编码,忽略未知类别 ]) # 组合预处理管道 preprocessor = ColumnTransformer( transformers=[ ('num', numeric_transformer, numeric_features), ('cat', categorical_transformer, categorical_features) ]) # 执行预处理 X_structured_processed = preprocessor.fit_transform(X_structured) # 转换为DataFrame(方便后续拼接) # 获取特征名称 num_feature_names = numeric_features cat_feature_names = preprocessor.named_transformers_['cat'].named_steps['onehot'].get_feature_names_out(categorical_features) feature_names = list(num_feature_names) + list(cat_feature_names) # 根据输出类型选择合适的转换方式 if hasattr(X_structured_processed, 'toarray'): X_structured_df = pd.DataFrame(X_structured_processed.toarray(), columns=feature_names) else: X_structured_df = pd.DataFrame(X_structured_processed, columns=feature_names) return X_structured_df, y, preprocessor # 预处理结构化数据 X_structured, y, structured_preprocessor = preprocess_structured_data(data) print("\n预处理后的结构化特征:") print(X_structured.head()) print(f"\n结构化特征形状:{X_structured.shape}") # ====================== 3. 文本数据预处理 ====================== def preprocess_text(text): """预处理单条文本""" # 去除特殊字符 text = re.sub(r'[^\u4e00-\u9fa5a-zA-Z0-9\s]', '', text) # 分词 words = jieba.lcut(text) # 去停用词(简单停用词表) stopwords = ['的', '了', '是', '我', '你', '他', '她', '它', '在', '有', '就', '都', '和', '与', '及', 'a', 'an', 'the'] words = [word for word in words if word not in stopwords and len(word) > 0] # 拼接为字符串 processed_text = ' '.join(words) return processed_text # 预处理所有文本 data['processed_text'] = data['combined_text'].apply(preprocess_text) print("\n预处理后的文本示例:") print(data[['combined_text', 'processed_text']].head())
输出结果:
预处理后的结构化特征:
age consumption_amount purchase_frequency ... product_category_电子产品 product_category_美妆 product_category_食品
0 -0.198386 1.565593 0.142051 ... 1.0 0.0 0.0
1 0.968590 -2.000561 0.866802 ... 0.0 1.0 0.0
2 -1.365362 -1.747964 -0.945075 ... 0.0 0.0 0.0
3 1.385368 -0.918000 1.591552 ... 0.0 0.0 1.0
4 1.218657 1.358407 -0.945075 ... 0.0 0.0 0.0
[5 rows x 11 columns]
结构化特征形状:(500, 11)
Building prefix dict from the default dictionary ...
Loading model from cache C:\Users\Admin\AppData\Local\Temp\jieba.cache
Loading model cost 0.387 seconds.
Prefix dict has been built successfully.
预处理后的文本示例:
combined_text processed_text
0 物流很慢,包装破损 Nike Air Max 270运动鞋 物流 很 慢 包装 破损 Nike Air Max 270 运动鞋
1 售后服务差,不推荐 Nike Air Max 270运动鞋 售后服务 差 不 推荐 Nike Air Max 270 运动鞋
2 尺寸不合适,材质差 苹果AirPods Pro 2代 尺寸 不 合适 材质 差 苹果 AirPods Pro 2 代
3 质量一般,价格偏高 小米空气净化器4 Pro 质量 一般 价格 偏高 小米 空气 净化器 4 Pro
4 尺寸不合适,材质差 三只松鼠坚果大礼包1500g 尺寸 不 合适 材质 差 三只 松鼠 坚果 大礼包 1500g
步骤 4:XGBoost 模型训练与评估
class BertEmbeddingExtractor: """BERT Embedding提取器""" def __init__(self, model_name='bert-base-chinese'): self.tokenizer = BertTokenizer.from_pretrained(model_name) self.model = BertModel.from_pretrained(model_name).to(device) self.model.eval() # 设置为评估模式 def get_embedding(self, text, max_length=32): """提取单条文本的Embedding""" # 编码文本 inputs = self.tokenizer( text, max_length=max_length, padding='max_length', truncation=True, return_tensors='pt' ).to(device) # 前向传播(不计算梯度) with torch.no_grad(): outputs = self.model(**inputs) # 获取最后一层的Hidden States last_hidden_state = outputs.last_hidden_state # [1, max_length, 768] # 均值池化得到句子Embedding embedding = torch.mean(last_hidden_state, dim=1).squeeze(0) # [768] # L2归一化 embedding = torch.nn.functional.normalize(embedding, p=2, dim=0) return embedding.cpu().numpy() def batch_get_embedding(self, texts, batch_size=32): """批量提取Embedding""" embeddings = [] for i in range(0, len(texts), batch_size): batch_texts = texts[i:i+batch_size] # 批量编码 inputs = self.tokenizer( batch_texts, max_length=32, padding='max_length', truncation=True, return_tensors='pt' ).to(device) # 前向传播 with torch.no_grad(): outputs = self.model(**inputs) # 均值池化 batch_embeddings = torch.mean(outputs.last_hidden_state, dim=1) # [batch_size, 768] # L2归一化 batch_embeddings = torch.nn.functional.normalize(batch_embeddings, p=2, dim=1) embeddings.extend(batch_embeddings.cpu().numpy()) return np.array(embeddings) # 初始化Embedding提取器 embedding_extractor = BertEmbeddingExtractor() # 提取文本Embedding print("\n开始提取文本Embedding...") texts = data['processed_text'].tolist() X_embedding = embedding_extractor.batch_get_embedding(texts) # Embedding降维(PCA) print(f"\n原始Embedding形状:{X_embedding.shape}") pca = PCA(n_components=0.95) # 保留95%的方差 X_embedding_pca = pca.fit_transform(X_embedding) print(f"降维后的Embedding形状:{X_embedding_pca.shape}") # 转换为DataFrame embedding_feature_names = [f'embedding_{i}' for i in range(X_embedding_pca.shape[1])] X_embedding_df = pd.DataFrame(X_embedding_pca, columns=embedding_feature_names) # ====================== 4. 特征融合 ====================== # 拼接结构化特征和Embedding特征 X_combined = pd.concat([X_structured.reset_index(drop=True), X_embedding_df.reset_index(drop=True)], axis=1) # 特征选择:去除方差过小的特征 variance_selector = VarianceThreshold(threshold=0.01) X_combined_selected = variance_selector.fit_transform(X_combined) # 获取选择后的特征名称 selected_feature_indices = variance_selector.get_support(indices=True) selected_feature_names = [X_combined.columns[i] for i in selected_feature_indices] print(f"\n融合后的特征形状(原始):{X_combined.shape}") print(f"融合后的特征形状(特征选择后):{X_combined_selected.shape}") print(f"选择后的特征数量:{len(selected_feature_names)}") # 转换为DataFrame X_combined_df = pd.DataFrame(X_combined_selected, columns=selected_feature_names)
输出结果:
Loading weights: 100%|████████████████████████████████| 199/199 [00:00<00:00, 4484.75it/s, Materializing param=pooler.dense.weight]
BertModel LOAD REPORT from: bert-base-chinese
Key | Status | |
-------------------------------------------+------------+--+-
cls.predictions.transform.LayerNorm.bias | UNEXPECTED | |
cls.seq_relationship.bias | UNEXPECTED | |
cls.seq_relationship.weight | UNEXPECTED | |
cls.predictions.transform.dense.weight | UNEXPECTED | |
cls.predictions.transform.dense.bias | UNEXPECTED | |
cls.predictions.transform.LayerNorm.weight | UNEXPECTED | |
cls.predictions.bias | UNEXPECTED | |
Notes:
- UNEXPECTED :can be ignored when loading from different task/architecture; not ok if you expect identical arch.
开始提取文本Embedding...
原始Embedding形状:(500, 768)
降维后的Embedding形状:(500, 16)
融合后的特征形状(原始):(500, 27)
融合后的特征形状(特征选择后):(500, 16)
选择后的特征数量:16
步骤 5:在线推理示例
# ====================== 1. 划分数据集 ====================== X_train, X_test, y_train, y_test = train_test_split( X_combined_df, y, test_size=0.2, random_state=42, stratify=y ) X_train, X_val, y_train, y_val = train_test_split( X_train, y_train, test_size=0.25, random_state=42, stratify=y_train ) print(f"\n训练集形状:{X_train.shape}") print(f"验证集形状:{X_val.shape}") print(f"测试集形状:{X_test.shape}") # ====================== 2. 定义XGBoost模型 ====================== # 设置参数 xgb_params = { 'objective': 'binary:logistic', # 二分类任务 'max_depth': 5, # 树最大深度 'learning_rate': 0.1, # 学习率 'n_estimators': 200, # 树的数量 'subsample': 0.8, # 样本采样比例 'colsample_bytree': 0.8, # 特征采样比例 'reg_alpha': 0.1, # L1正则化 'reg_lambda': 1, # L2正则化 'eval_metric': 'auc', # 评估指标 'seed': 42, # 随机种子 'verbosity': 1 # 输出日志级别 } # 初始化模型 model = xgb.XGBClassifier(**xgb_params) # ====================== 3. 训练模型 ====================== print("\n开始训练XGBoost模型...") model.fit(X_train, y_train, verbose=True) # ====================== 4. 模型评估 ====================== # 预测 y_pred = model.predict(X_test) y_pred_proba = model.predict_proba(X_test)[:, 1] # 计算评估指标 accuracy = accuracy_score(y_test, y_pred) auc = roc_auc_score(y_test, y_pred_proba) classification_rep = classification_report(y_test, y_pred) print("\n====================== 模型评估结果 ======================") print(f"准确率(ACC):{accuracy:.4f}") print(f"AUC-ROC:{auc:.4f}") print("\n分类报告:") print(classification_rep) # ====================== 5. 特征重要性分析 ====================== # 获取特征重要性 feature_importance = model.get_booster().get_score(importance_type='weight') # 转换为DataFrame feature_importance_df = pd.DataFrame({ 'feature': list(feature_importance.keys()), 'importance': list(feature_importance.values()) }) # 按重要性排序 feature_importance_df = feature_importance_df.sort_values('importance', ascending=False).head(20) # 绘制特征重要性图 plt.figure(figsize=(12, 8)) sns.barplot(x='importance', y='feature', data=feature_importance_df) plt.title('XGBoost特征重要性(Top 20)', fontsize=14) plt.xlabel('重要性得分', fontsize=12) plt.ylabel('特征名称', fontsize=12) plt.tight_layout() plt.savefig('feature_importance.png', dpi=300) plt.show() # ====================== 6. 混淆矩阵 ====================== cm = confusion_matrix(y_test, y_pred) plt.figure(figsize=(8, 6)) sns.heatmap(cm, annot=True, fmt='d', cmap='Blues', xticklabels=['未购买', '购买'], yticklabels=['未购买', '购买']) plt.title('混淆矩阵', fontsize=14) plt.xlabel('预测标签', fontsize=12) plt.ylabel('真实标签', fontsize=12) plt.tight_layout() plt.savefig('confusion_matrix.png', dpi=300) plt.show() # ====================== 7. 对比实验(仅使用结构化特征) ====================== print("\n====================== 对比实验:仅使用结构化特征 ======================") # 仅使用结构化特征训练XGBoost X_train_struct = X_train[X_structured.columns] if set(X_structured.columns).issubset(X_train.columns) else X_train.iloc[:, :len(X_structured.columns)] X_test_struct = X_test[X_structured.columns] if set(X_structured.columns).issubset(X_test.columns) else X_test.iloc[:, :len(X_structured.columns)] # 训练模型 model_struct = xgb.XGBClassifier(**xgb_params) model_struct.fit(X_train_struct, y_train, verbose=False) # 评估 y_pred_struct = model_struct.predict(X_test_struct) y_pred_proba_struct = model_struct.predict_proba(X_test_struct)[:, 1] accuracy_struct = accuracy_score(y_test, y_pred_struct) auc_struct = roc_auc_score(y_test, y_pred_proba_struct) print(f"仅结构化特征 - 准确率(ACC):{accuracy_struct:.4f}") print(f"仅结构化特征 - AUC-ROC:{auc_struct:.4f}") print(f"\n融合模型 vs 仅结构化特征:") print(f"准确率提升:{(accuracy - accuracy_struct)*100:.2f}%") print(f"AUC提升:{(auc - auc_struct)*100:.2f}%") # ====================== 8. 保存模型 ====================== # 保存XGBoost模型 model.save_model('xgboost_llm_fusion.model') print("\n模型已保存为:xgboost_llm_fusion.model") # 保存特征名称 with open('feature_names.txt', 'w', encoding='utf-8') as f: for name in selected_feature_names: f.write(f"{name}\n") class XGBoostLLMFusionInference: """XGBoost+大模型融合推理类""" def __init__(self, model_path, feature_names_path, embedding_extractor, structured_preprocessor): # 加载模型 self.model = xgb.XGBClassifier() self.model.load_model(model_path) # 加载特征名称 with open(feature_names_path, 'r', encoding='utf-8') as f: self.feature_names = [line.strip() for line in f.readlines()] # Embedding提取器 self.embedding_extractor = embedding_extractor # 结构化数据预处理器 self.structured_preprocessor = structured_preprocessor # PCA模型(这里简化,实际应保存训练好的PCA模型) self.pca = pca def preprocess_input(self, structured_data, text): """预处理输入数据""" # 1. 预处理结构化数据 structured_df = pd.DataFrame([structured_data]) structured_processed = self.structured_preprocessor.transform(structured_df) # 根据输出类型选择合适的转换方式 if hasattr(structured_processed, 'toarray'): structured_df_processed = pd.DataFrame(structured_processed.toarray(), columns=self.structured_preprocessor.get_feature_names_out()) else: structured_df_processed = pd.DataFrame(structured_processed, columns=self.structured_preprocessor.get_feature_names_out()) # 2. 预处理文本并提取Embedding processed_text = preprocess_text(text) embedding = self.embedding_extractor.get_embedding(processed_text) embedding_pca = self.pca.transform([embedding]) # 3. 特征融合 embedding_df = pd.DataFrame(embedding_pca, columns=[f'embedding_{i}' for i in range(embedding_pca.shape[1])]) combined_df = pd.concat([structured_df_processed, embedding_df], axis=1) # 4. 确保所有需要的特征都存在,缺失的填充为0 for col in self.feature_names: if col not in combined_df.columns: combined_df[col] = 0 # 5. 选择模型训练时的特征 combined_df = combined_df[self.feature_names] return combined_df def predict(self, structured_data, text): """预测""" # 预处理输入 input_data = self.preprocess_input(structured_data, text) # 预测 pred_proba = self.model.predict_proba(input_data)[0] pred_label = self.model.predict(input_data)[0] return { 'pred_label': int(pred_label), 'pred_prob': float(pred_proba[1]), 'label_name': '购买' if pred_label == 1 else '未购买' } # 初始化推理类 inference = XGBoostLLMFusionInference( model_path='xgboost_llm_fusion.model', feature_names_path='feature_names.txt', embedding_extractor=embedding_extractor, structured_preprocessor=structured_preprocessor ) # 示例推理 sample_structured_data = { 'age': 28, 'gender': '女', 'consumption_amount': 1500.0, 'purchase_frequency': 20, 'product_category': '美妆', 'rating': 4.8 } sample_text = "这款粉底液遮瑕效果超棒,持妆久,推荐! 兰蔻持妆粉底液PO-01 30ml" # 推理 result = inference.predict(sample_structured_data, sample_text) print("\n====================== 在线推理示例 ======================") print(f"输入结构化数据:{sample_structured_data}") print(f"输入文本:{sample_text}") print(f"预测结果:{result['label_name']}") print(f" - 购买概率:{result['pred_prob']:.4f} ({result['pred_prob']*100:.2f}%)") print(f" - 未购买概率:{1-result['pred_prob']:.4f} ({(1-result['pred_prob'])*100:.2f}%)") # 测试多个样本 print("\n====================== 多样本对比测试 ======================") test_cases = [ { 'structured': {'age': 35, 'gender': '女', 'consumption_amount': 2000.0, 'purchase_frequency': 25, 'product_category': '美妆', 'rating': 4.9}, 'text': "产品非常棒,会继续购买! 兰蔻持妆粉底液PO-01 30ml" }, { 'structured': {'age': 22, 'gender': '男', 'consumption_amount': 500.0, 'purchase_frequency': 2, 'product_category': '服装', 'rating': 2.5}, 'text': "质量太差,不建议购买 优衣库男士纯棉T恤458762" }, { 'structured': {'age': 45, 'gender': '女', 'consumption_amount': 3000.0, 'purchase_frequency': 15, 'product_category': '电子产品', 'rating': 4.5}, 'text': "性能出色,推荐购买 2024新款华为Mate60 Pro 12GB+512GB" } ] for i, test_case in enumerate(test_cases, 1): pred = inference.predict(test_case['structured'], test_case['text']) print(f"\n样本{i}:") print(f" 预测: {pred['label_name']} (购买概率: {pred['pred_prob']:.4f})") print(f" 评论: {test_case['text']}")
输出结果:
训练集形状:(300, 16)
验证集形状:(100, 16)
测试集形状:(100, 16)
开始训练XGBoost模型...
====================== 模型评估结果 ======================
准确率(ACC):0.6500
AUC-ROC:0.6419
分类报告:
precision recall f1-score support
0 0.56 0.46 0.51 39
1 0.69 0.77 0.73 61
accuracy 0.65 100
macro avg 0.63 0.62 0.62 100
weighted avg 0.64 0.65 0.64 100
====================== 对比实验:仅使用结构化特征 ======================
仅结构化特征 - 准确率(ACC):0.5400
仅结构化特征 - AUC-ROC:0.5254
融合模型 vs 仅结构化特征:
准确率提升:11.00%
AUC提升:11.64%
模型已保存为:xgboost_llm_fusion.model
====================== 在线推理示例 ======================
输入结构化数据:{'age': 28, 'gender': '女', 'consumption_amount': 1500.0, 'purchase_frequency': 20, 'product_category': '美妆', 'rating': 4.8}
输入文本:这款粉底液遮瑕效果超棒,持妆久,推荐! 兰蔻持妆粉底液PO-01 30ml
预测结果:购买
- 购买概率:0.9326 (93.26%)
- 未购买概率:0.0674 (6.74%)
样本1:
预测: 购买 (购买概率: 0.8819)
评论: 产品非常棒,会继续购买! 兰蔻持妆粉底液PO-01 30ml
样本2:
预测: 未购买 (购买概率: 0.3257)
评论: 质量太差,不建议购买 优衣库男士纯棉T恤458762
样本3:
预测: 购买 (购买概率: 0.9219)
评论: 性能出色,推荐购买 2024新款华为Mate60 Pro 12GB+512GB
六、总结
XGBoost 结合大模型核心就是两者优势互补,大模型搞定文本这类非结构化数据,提取语义特征,XGBoost 擅长结构化数据建模,把两者的特征结合起来,既解决了大模型处理数值差、成本高的问题,又弥补了 XGBoost 不懂语义的短板。传统模型和大模型不是替代关系,而是协同关系。以前总觉得大模型很高大上,传统模型过时了,现在才明白,能落地、能解决实际问题的组合才是最好的。整个学习过程不用死磕复杂公式,从原理到代码一步步来,就能慢慢理解。不管是学 XGBoost 还是大模型,打好基础、注重实战,比盲目追求高大上的技术更重要。