# 数据挖掘汇报

大家好,今天由我向大家汇报一下我们组的项目。我们组做的项目题目是:基于XGBoost模型的O2O用户优惠券使用行为分析与预测方法研究。相信一些同学应该对这个题目很熟悉,因为这个题目来源于CSA数据工程实践的考核任务。之所以选择这个题目,一方面是因为它涵盖了许多我们在数据挖掘课程中学习过的机器学习相关知识点,另一方面也是因为我在这个项目上投入了大量时间和精力,并从中真正获得了一些实质性的收获。

因此,我希望借此机会向大家分享我的学习成果和一些心得体会。

下面我会从这几个方面来具体展开整个项目流程,最后还会讲讲K-Means的代码实现。

项目介绍

首先是项目介绍。

可能很多同学不知道O2O是做什么的,这里我对O2O做一个解释。O2O是“Online to Offline”的缩写,它是一种商业模式,指的是通过线上平台将用户引导到线下实体店进行消费的方式。比如美团外卖就采用的这种模式,也就是用户线上先下单,商家线下制作并且送餐这种。

那我们为什么要对优惠券进行预测呢?因为优惠券是O2O的一种重要营销手段,反映到我们的生活上也是,哪个商家发优惠券我们就比较喜欢去这个商家去消费。但是发优惠券也不能随便发,要根据用户的喜好发,个性化地发,这样才能吸引更多的用户来促进消费。

接下来是样本来源。

那整个项目是在做什么?就是围绕用户过去怎么领取、消费优惠券,提取一些关键特征,最后预测用户在未来会怎么消费优惠券。

最后的评测目标是预测投放的优惠券是否核销,我们使用优惠券核销的平均AUC来作为评分标准。

这里简单介绍一下AUC,介绍AUC得先知道ROC是什么,ROC纵轴是“真正例率TPR”,横轴是“假正例率FPR”。AUC就是ROC曲线下的面积。

image

image

整个O2O优惠券预测分为了如上的六个步骤

对于特征工程,我仅考虑了线下数据集,主要从历史区间和标签区间两个部分进行考虑,提取用户(User)、商家(Merchant)、优惠券(Coupon),同时结合一些交互特征,最后还将少量贡献度高的特征进行排列组合构造新的特征。

特征提取完毕后,使用XGBoost模型预测优惠券使用概率,调参得到最优解。

最后我将特征的重要性进行输出,并且通过一个自定义的auc函数来模拟比赛的auc结果,辅助评判模型。

数据处理

首先我先查看了属性列有哪些。左侧是比赛官方提供的预测样本的属性列说明,对于领取行为,多了一个Date列用于记录用户什么时候核销了优惠券。

接下来我对数据进行一个整体预览。具体就是进行一些计数,看看有多少条记录,统计下最早最晚的领取时间。

接着结合isnull()和any()对数据进行缺失值的检查。可以看到distance存在缺失值。

然后对数据进行预处理。主要做了如上这些事情。空距离缺失我用-1来进行简单的填充。在提取特征前也要对数据进行一个预处理。

预处理完毕后就可以对数据进行可视化了。这里我使用pyecharts生成了如上几个统计图。每个统计图其实可以分析出一些用户偏好行为。就拿用户消费距离统计来说,大部分用户的消费距离为0,即领券后直接在领取地点消费的用户最多,数量为43867。说明很多用户更倾向于在领取优惠券的门店内直接使用,而不会去其他更远的地方消费。随着距离的增加,领取用户数量逐渐减少。说明大多数用户更倾向于在较近的范围内消费。

在 10km 时出现异常高峰,推测有以下原因:

  1. 特定商圈或门店 在 10km 位置,有大量用户前往。
  2. 用户误差:数据采集时,部分 10km 以上的用户被归为 10km

由于最终的目标是要确定优惠券是否在15天内要被核销,所以我们需要对数据进行打标。具体打标方式就是新加label列,算一下date与date_received的差,如果在15天内就说明核销率,记录为1,否则为0。

由于数据有明显的时间性,所以可以采用时间窗口划分法来划分数据集。这种划分方法把每个数据集划分为历史区间、中间区间和标签区间。历史区间上存在用户过往领券行为,中间区间作为一个缓冲,增加时间隔离,避免数据泄露,标签区间则用来模拟真实的目标,用来预测用户未来是否核销优惠券。

特征工程

整个特征工程一共提取了111个特征,分为三个部分。每个部分提取的特征对象都类似,也就是用户、商家、以及一些交互特征。

对于交叉特征,主要逻辑是通过两层循环,遍历传入的需要进行交叉组合的高贡献度特征列,两两组合,构造交叉特征。

这里简单说下特征是怎么提取的:使用的是pivot_table()透视表或者groupby(),按照一个分组键,在cnt列或者其他列应用一个方法,也就是aggfunc,最后把结果重命名成特征名,最后合并进主特征表。如果有缺失值,就补充为0.

交叉特征不再赘述。

特征提取好以后,我们把所有提取出的特征合并到历史区间上,然后去除公共列,最后使用concat()合并进数据集。

接着对数据集进行修正:

对于训练集和验证集,将不需要训练的数据列使用drop()方法删除,并将label放在最后。同时加入交叉特征。

对于测试集,除了删除不需要的列,我还将数据某些列转为int型,便于 xgboost 识别。同样地也加入交叉特征。

模型构建与评估

模型构建的话主要是这几个步骤。

首先得修正下数据集,因为xgboost需要数值型特征数据,而User_id、Coupon_id不是训练数据的一部分,它们不能提供有用的信息,因此需要删除。另一方面,如果将label传入,模型可能会通过目标变量直接猜测结果,这样会导致模型训练时的“数据泄漏”问题,从而影响模型的泛化能力。

最后我们得到训练集、验证集和测试集。监控训练集和验证集,开始训练,最后按照比赛要求输出csv。

下面讲下xgboost的参数。左图的这些参数可以分为如上6类。这些参数就不一一展开说了,我认为值得关注的是eta学习率(越小训练越慢但稳定)、max_depth树的最大深度、最小损失函数下降值(控制节点是否分裂)gamma和L2 正则化权重(对叶子权重的惩罚)lambda:大了可以防止过拟合。

下面讲讲我的提分曲线

整个提分过程出现了4次瓶颈。

从0.58到0.61:在baseline的基础上,添加更多特征,包括历史区间和标签区间。

从0.61到0.71:得益于用户标签区间的leakage,让AUC整整提高了0.1

leakage是一种数据泄露,比如标签区间上可以出现这种情况,就是用户在7月10日领取了优惠券,而在14号,16号又领取了同一种优惠券,说明7月10号领取的优惠券被核销的可能性很大。但是实际生产生活中是获取不到这种特征的。

从0.71到0.74:挖掘了用户标签区间上的更多特征,其中7天内用户是否多次领取同一优惠券贡献最大。

从0.74到0.78:在用户标签区间上继续挖掘与时间强相关的特征。其中15天、30天内的领券次数的贡献最大。

为了方便知道特征的重要性,我利用get_score()方法对所有特征进行gain重要性评分,最后按贡献度排序,方便剪除贡献度低的特征。

我还利用了验证集,并且搭配early_stop_rounds让模型提前收敛,确定最佳训练轮数。可以看到在1000次训练轮数的前提下,模型在846轮性能不再提升,实现早停,避免了过拟合。

对于AUC的提升,我还做过一些尝试。

1、尝试使用LightGBM和XGBoost模型融合,加权输出结果。但是AUC反而没有XGBoost单独训练的AUC高,遂仅使用XGBoost。

2、XGBoost调整参数陷入瓶颈,几乎所有参数的微调都会导致 AUC 指标下降,模型效果反而变差。最终我放弃了通过调参提升AUC 的方向,转而从特征工程角度优化模型表现。

3、我还尝试过合并过线上数据集,但是会出现大量缺失值,甚至造成“数据泄露”。导致AUC严重下降。

K-Means

参数x代表数据集、k代表簇的数量、iter代表最大迭代次数、tol代表簇中心的容忍误差范围。

整个函数主要分为4步。

首先使用random.sample()方法,从数据集x中,随机选取k个点作为初始簇中心。

接着初始化一个标签列表,全部即为None。然后利用get_distance分别计算每个点到初始簇中心的距离,将所有距离中最小的一个保存在新标签列表中,然后不断迭代,直到新旧标签完全相同。然后更新簇中心,更新方法是计算每个簇中所有点的均值作为新的簇中心。具体算法是初始化一个中心列表用来保存新的簇中心。然后获取当前簇中所有的点,接着计算这些点横纵坐标的均值,把计算结果保存在中心列表中。利用allclose()方法判断新的簇中心与原来簇中心之间的误差是否不大于tol,满足条件就终止迭代。

最后返回簇中心与距离数组。

最后说下心得体会

在上这门课之前,我一直专注于云原生容器化领域,对机器学习、人工智能的认识也仅仅停留在“问问AI问题阶段”,从来没有深入研究过背后的实现机制。这次的O2O优惠券预测项目让我深刻体会到了数据挖掘和机器学习的魅力,最让我振奋的是我的提分历程,其中,受到 wepe 提示的 leakage 特征启发,我成功将 AUC 提升了整整 0.1。

在从 0.71 到 0.78 的关键阶段,我记得从周五到周日我看了整整3天,尝试了多种方法,包括合并线上特征、构造交叉特征、网格搜索参数调优和模型融合等,但效果都不显著。这一度让我怀疑自己的方向,甚至开始觉得机器学习就是一场“黑盒测试”。为此,我主动寻求学长的指导,并查阅大量博客和大模型的建议,最终意识到问题出在特征工程上还不够深入。

通过不断尝试与调整,我逐步解决了一部分问题,但也意识到自身技术能力仍有不足,对某些问题的根源仍未完全理解,尚未找到有效的解决方案。目前模型的AUC达到了 0.78,仍有进一步提升的空间,比如加入用户线上行为特征、针对性调整模型参数、挖掘与星期相关的时间特征等。

说到最后,其实数据挖掘挺有趣的,特别是你可以从一堆静态的数据里,发现一些有意思的东西,这也许就是大数据的魅力。