A competition on jdata which is about repurchase date prediction ! Rank : 8 / 5182 !
如期而至-用户购买时间预测由jdata智汇平台举办,本次大赛京东携手中国信息通信研究院,中关村加一战略新兴产业人才发展中心,以“创响中国”在大数据领域落地为契机,旨在激发算法创新,释放数据价值,培养数据人才。希望各界算法精英能够利用脱敏后的京东真实用户历史行为数据,自建算法模型,预测热销品类的用户购买时间,搏击浪潮之巅,勇夺桂冠,赢得高额奖金。
最终排名:A榜:14 / 5182,B榜:8 / 5182
附:github地址
-
1 比赛简介
如期而至-用户购买时间预测由jdata智汇平台举办,本次比赛从订单表随机选取时间节点前三个月内购买过目标品类(101,30)的部分用户作为目标用户集合,并且提供了目标用户在时间节点前一年内的行为、订单、评论等用户数据以及目标用户的基本信息和商品的信息,要求预测时间节点后一个月内目标用户中最有可能购买目标品类的用户,并预测他们在考察时间段内的首次购买时间。 -
2 问题分析
根据题述,赛题可以看作是两个子问题,可以细分为:
(1)在考察时间段内,目标用户是否会发生下单行为?(二分类问题)
(2)在(1)的前提下,下单的用户对目标品类商品的首次下单时间?(回归问题)
因此,我们可以分为两步解题:
(1)根据已有数据建立模型,寻找在考察时间段内最有可能下单的用户(用户模型)。
(2)根据已有数据建立模型,在(1)的基础上,寻找目标用户在考察时间段内首次下单的时间(日期模型)。
通过分析问题,我们将赛题分解成一个二分类问题和一个回归问题。根据已有数据,我们先建立一个用户模型,通过二分类的方法寻找考察时间段内最有可能下单的用户;基于用户模型得到的用户,我们接着建立一个日期模型,通过回归的方法预测目标用户在考察时间段内首次下单的时间。
评价指标思考:本次比赛的评价指标分为用户评价S1和用户下单日期评价S2两部分,其中,S1所占权重为40%,S2所占权重是60%。此外,通过分析S1的评分公式,可以看到,我们提交的5w条用户,排序越靠前的用户重要性越高,也就是说,第一个用户预测对了和第5w条用户预测对了,他们在S1中贡献的分数是不一样的;通过分析S2的评价指标,可以看到,S2的评价依赖于S1(对于一个用户,如果不在S1答案用户集合中,S2不得分),S2评价指标跟均方误差MSE有点类似,但并不相同。 -
3 数据分析与数据清洗
(1)关于用户的回购时间,通过对数据的分析可以看出,AB榜在用户回购时间间隔上的分布基本一致。此外,我们发现用户回购时间会在14天左右出现一个峰值,回购时间超过90天的用户已经寥寥无几,因此在进行滑窗的时候可以参考回购时间统计出来的规律进行滑窗(我们根据回购时间可以猜测这个商品类别是日用品);
(2)通过分析用户活跃天数与标签的关系,我们发现,越活跃的用户,他的回购概率会越高,这个对我们做特征工程和后处理都有帮助;
(3)根据用户回购时间统计得出的规律,我们可以知道,在时间节点前两个月内购买过的用户比三个月前购买过的用户的购买概率会增大,比如,对于B榜来说,七八月份购买过的用户相对于六月份购买过的用户,在九月份购买的概率会增大;
(4)关于用户的集合,由于6月份有京东的618大型折扣活动,会导致6月份购买用户激增。我们发现在99446个目标用户中,有12315用户是只在六月份购买过目标类目商品的。因此,可以考虑把这部分用户视为没有日常购买需求、仅仅是在618凑折扣的用户;
(5)从各月份下单日的统计分析来看,普通月份的用户第一次下单日期大致是从月初到月末呈逐渐递减的趋势,特殊月份(如6月、11月)会在特殊日期呈现高峰值,并对前后月份的购买行为趋势产生一定影响;
(6)抛开月份的划分,经统计,对于任意一个时间点T,前90天有购买的用户,在未来30天内的第一次购买日期同样呈现逐渐递减的统计趋势;
(7)针对A榜跟B榜的数据分布差异分析,我们各取了AB榜17年2月和3月的数据曲线做了对比,发现曲线规律基本一致,因此初步认为A榜B榜数据分布一致,A榜的数据分析结论可以沿用至B榜;
(8)实际操作中,S2模型由于受损失函数均方误差的影响,预测结果呈单峰曲线,并不能很好拟合训练集逐渐递减的趋势。因此后期采取一些技巧加大了前半个月的预测比例;
(9)我们发现在B榜中sku基本信息表中存在商品id为空的情况,我们对这部分数据进行了剔除操作;
(10)通过分析我们看到了sku基本信息表中商品参数二和商品参数三字段存在较为严重的空缺情况,我们对这两个字段进行了剔除操作,因为空缺情况比较严重时,这个字段已经不能给模型提供什么信息了; -
4 训练样本构造
本次比赛,我们构造了三个用户模型,一个日期模型,每个模型的训练样本构造方法都不同:
(1)用户模型
用户模型一(我自己的模型)
对于每个月来说,以该月前三个月内有购买过的用户为样本,根据他是否在这个月购买过打标签,如果有购买过则标为1,否则标为0。此外,考虑到样本扩充的问题,以月为时间间隔进行滑窗,最终采用了六个月的训练样本量。
用户模型二(队友的模型)
与用户模型一类似,不同的是,对于每个月来说,以该月用户订单数量为标签。
用户模型三(队友的模型)
线下数据集的用户样本来自于2017-08-01(T)前60区间内在目标cate(30、101)产生订单行为的用户进行去重;线上数据集的用户样本来自于2017-09-01(T)前75天区间内在目标cate(30、101)产生订单行为的用户进行去重。
穿越问题
由于本次比赛是从订单表随机选取时间节点前三个月内购买过目标品类(101,30)的部分用户作为目标用户集合,以B榜为例,时间节点是2017-09-01,目标用户集合在2017年6、7、8三个月内肯定是有购买过目标品类的商品,如果我们在构造训练集时的候选用户集时间区间和打标签区间有一部分跟6、7、8三个月重合的话,就会存在穿越问题。举个例子,假如,我们用567三个月的用户去预测8月份的购买情况。其实暗含了一个先验条件,就是这些用户在678月一定会购买,换句话说,对于67月份(也就是后两个月)没有购买的用户,他们在8月份一定会购买,但是线上预测集中,78月份没购买的用户9月份并非一定会购买。从根本来看,其实就是我们构造的训练集跟实际的线上预测集的数据分布都不一样,数据分布不一样的话,我们在做特征工程和模型训练都会有很大的问题(比如,如果我们在线下构造了一个上次购买时间间隔特征,因为穿越数据的存在,5月份的时间间隔较长,导致标签与这个特征的关系是时间间隔越长,越可能会买,但是线上并没有这个规律,就会成为脏特征)。如果用234三个月购买过的用户作为候选用户集,根据他们在5月份是否购买打标签的话就不存在穿越问题。
对于上述三个用户模型,只有用户模型三不存在数据穿越问题,用户模型一二都存在数据穿越的问题。因为我们是在A榜结束前一天才找到了队友(队友的模型是用户模型三,队友也是一个新手,他是基于朋友的baseline基础上来做的,构造训练集的方法也是从baseline里面来的,没有考虑穿越问题),当时我们还没有做模型融合,因此我们在A榜最后一天做了三个用户模型的融合。在A榜结束后,我们就在思考,为什么队友的用户模型训练样本量只有8w,但是出来的效果却比我们两个用户模型几十万训练样本的效果要好,于是我们就想到了训练样本构造方案上的穿越问题。发现了这个问题,已经到了B榜关键时刻,我们在B榜不敢做大变动,而且用户模型一和用户模型二的效果并不是比用户模型三差了特别多,我们在A榜将三个模型融合后的效果是比用户模型三单模型的效果要好的(应该是跟模型差异性有关系),因此保留了用户模型一和用户模型二。
(2)日期模型
日期模型最终采用的15天的滑窗间隔倍增训练集。每一个预测窗口均是预测窗口前90天有购买行为用户,在未来30天内的第一次购买日期。样本的特征统计区间均为2016-09-01至预测窗口前的时间段。考虑到训练集最初几个月的用户没有足够的历史统计数据,因此训练集倍增窗口从第4个月(即B榜12月)开始。
在这里,我们通过增加15天数据集(训练集17)的方法尝试拟合线上测试集的分布,在增加15天数据集前后预测出来的结果都是呈单峰正态分布,但是增加15天数据集后峰值从13天左右提前7天左右,由于在数据分析时我们发现其实实际分布是从月初逐渐降低的,峰值提前到7天左右,因为S2评价函数的误差项在分母,跟MSE不同,MSE误差较大的样本变动对分数影响很大,而对于S2来说,误差较小的样本的变动对分数影响更大(比如,预测误差是1和5的样本之间的分数差,是比预测误差是11和15的样本之间的分数差要大,因为平方项在分母),我们将峰值提前,就是导致误差较大的样本往误差小的方向靠拢,而原本误差很大的样本继续变大,但是产生的影响没那么明显,提高了模型的精度。
-
5 特征工程
提供的数据:sku基本信息表,用户基本信息表,用户行为表,用户订单表,评论分数数据表,由于本次比赛是预测用户是否回购以及回购时间,因此我们在做特征工程的时候更多地是从用户的角度来做。
(在做特征的时候,我们更多地是从业务理解的角度入手,先设想一些能够给模型提供信息的特征,然后通过可视化的方法(看特征分布图,比如我们构造了一个特征是用户前一个月下单的次数,我们可以画出回购用户和没有回购用户的分布来验证这个特征是否有效)或者计算相关系数(皮尔逊系数和最大互信息系数,皮尔逊系数只能衡量线性相关性而互信息系数能够很好地度量各种相关性,但是计算相对复杂一些,好在很多toolkit里边都包含了这个工具)来验证特征是否有效。此外,我们也需要将特征丢到模型里面验证,因为有的特征会存在多重共线性问题,可以通过添加特征群到模型观察效果的方法来验证)
本次比赛我们共构造了四个模型,包括三个用户模型和一个日期模型,每个模型的特征并不一样,但是大同小异,所以下面就一起说明了:
(1)用户基础特征
用户年龄,性别以及等级码
思考:通过不同的用户基础特征可以在一定程度上反映用户群的购买习惯和购买倾向;
(2)滑窗内用户的统计特征
用户半个月/一个月/三个月/六个月内对目标类目/相关类目/101类目/30类目商品的下单次数/购买数量/购买天数/购买种类数/购买月份数/操作次数/操作数量/操作商品种类数/操作天数/浏览数量/浏览天数/浏览商品种类数/关注次数/好评数量/中评数量/差评数量/评论数量
用户半个月/一个月/三个月/六个月内对目标类目/相关类目/101类目/30类目商品购买在当月第几天的最大值/最小值/均值
用户半个月/一个月/三个月/六个月内购买过目标类目/相关类目/101类目/30类目商品价格的最大值/最小值/均值/总值
思考:此类特征可以在一定程度上反映出用户的一些购买习惯(用户喜欢以什么节奏购买东西),活跃程度,进而推算下个月购买的概率。在这里我们滑窗的窗口大小是根据我们在分析用户回购时间分布来确定的,在数据分析时我们发现,用户回购时间会在14天左右出现一个峰值,回购时间超过90天的用户已经寥寥无几,所以我们划分了半个月/一个月/三个月/六个月四种区间;
(3)历史用户统计特征
用户历史平均每个月对目标类目/相关类目/101类目/30类目的购买数量/下单次数/购买种类数/购买天数/操作数量/操作种类数/操作天数/浏览数量/关注数量/
用户历史购买过目标类目/相关类目/101类目/30类目商品价格的最大值/最小值/均值
思考:此类特征统计的是用户在历史每个月的行为习惯,购买习惯和活跃程度,可以将最活跃的一批用户跟非活跃用户区分开,提高模型预测准确度;
(4)商品特征
用户半个月/一个月/三个月/六个月内购买过目标类目/相关类目/101类目/30类目参数一的最大值/最小值/均值
用户历史购买过目标类目/相关类目/101类目/30类目商品参数一的最大值/最小值/均值
用户上次下单的商品参数一均值,总和
思考:我们通过分析可以知道,商品价格和参数一都为非空,而参数二和参数三空值较多,因此在做统计特征的时候没有考虑参数二和参数三的维度。我们猜测,本次比赛的商品是日用品类型的,然后商品参数一是类似于商品容量之类的一个属性,比如,洗发水,有1升的,有250毫升的,由于我们每天消耗日用品的量是一定的,所以如果上一次购买的商品容量较大的话,用的时间就会越久,回购的时间距离上次购买时间就会越久,因此我们有针对性地做了一些特征;
(5)最近一次行为特征
用户各类别、所有类别的最近一次浏览时间
用户各类别、所有类别的最近一次下单时间
用户各类别、所有类别的最近一次下单时间与浏览时间之差
用户101、30类别、所有类别的最近一次订单价格
用户101、30类别最近一次订单的商品参数一均值
思考:最近一次行为特征,其实是跟用户回购相关性比较大的一个特征群,从直观上来看,对于有日常需求的用户来说,他上一次购买时间距离时间节点T越远,用户在这个月回购概率会越大,初次回购时间就会越靠前; -
6 模型选择
我们在前期时测试了LR、XGBoost、LightGBM等多种模型,从精确度和效率上对比后,我们最终采用了三个用户模型(一个xgb,两个lgb),一个日期模型(xgb),训练得到最终的结果。
用户模型
关于用户模型,我们训练了一个xgboost和两个lightgbm模型,每个模型都是采用k折交叉验证方法训练。在得到三个用户模型后,我们需要进行模型融合,主要考虑两方面问题,一个是用户的集合,一个是用户的排序:
1)关于用户的集合,由于6月份有京东的618大型折扣活动,导致6月份购买用户激增。为了剔除掉并没有日常购买需求、仅仅是在618凑折扣的用户,我们最终决定S1的用户从5/7/8月有购买的用户中选取。
2)关于用户的排序:排序主要依据三个模型的用户评分。我们希望根据模型的线上成绩来确定模型的评分比重,而模型的比重反应到结果上则是用户的占比。比如比重为3:2,就相当于当A模型提供3个用户时,B模型提供2个用户。然而各个模型之间的评分规则和分布区间并不一致,不能直接将模型比重用于分数的加权系数。
结合这两方面因素考虑,我们最终制定的模型融合方案如下:
1)三个模型各自取前5w条用户,然后剔除掉5/7/8月没有购买记录的用户;
2)将各模型的用户分数进行0-1归一化,缩小模型间评分标准的差异;
3)根据各模型A榜成绩,确认三个模型的比重(分别为72:105:148),即模型1的第7200个用户的重要性等同于模型2的第10500个用户等同于模型3的第14800个用户;
4)根据模型比重查找各模型对应用户的分数,根据分数确定三个模型的加权系数。即加权后三个模型的分数区间如下:
5)将三个模型的用户分数进行加权求和,没有预测到的用户分数为0,然后按用户总分排序取前5w作为最终的S1结果。
日期模型
对于日期模型,考虑到模型在数据上有时序性,所以线下并没有采用交叉验证,而是采用了时间顺序划分线下验证集。
我们选择了8月2号作为验证集,一方面是考虑到8月2号同9月1号都是月初,另一方面也是为了方便与用户模型效果做对比,日期模型依赖于用户模型,因为用户模型在线下的时候是以8月份为验证集,容易对比线下线上效果。
最终模型
关于整个模型,整体来说,我们采用不同的训练集以及打标签的方法训练了一个XGBoost模型和两个LightGBM模型,然后进行融合,得出最终的五万条用户。日期模型采用15天滑窗倍增训练集的方式,训练出一个XGBoost单模型,预测用户在九月份第一次下单的时间。通过结合用户模型和预测模型,我们得到了最终的提交结果。
-
7 赛后总结
本次比赛是我们参加的第二个比较大型的数据挖掘比赛(第一个是天池上阿里妈妈的比赛),在参加这次比赛前,其实我们对数据挖掘的一个流程已经有一定的了解,因此,本次比赛上手其实并不难,但是在比赛前期一直到A榜结束前一周,我们一直在100名左右徘徊,虽然最后达到了top8的成绩,但整个过程,现在回想起来,还是比较坎坷的。困扰我们最大的一个问题是用户模型的穿越问题,一直到A榜结束。在一开始,我们就确定了先做S1再做S2的策略,并且因为S2是依赖于S1的,所以一开始我们都是先去研究用户模型。但是因为穿越问题的存在,我们几个人的S1成绩都在200名左右,并且没有找到直接的原因。因此,我们就在思考会不会是因为有脏特征的存在(对训练集拟合得比较好,对测试集没效果甚至有反效果的特征),我们就开始在做特征筛选。通过特征筛选的方法,我们发现距离上一次购买的时间间隔等跟时间间隔相关的特征一加进去模型就会崩,因此先我们剔除了这一部分特征,但是没想到为什么会这样。队友觉得时间不够了,就去做S2了,我继续研究S1,因为我觉得S1依然是根本。后来我通过研究用户回购时间,剔除了2个月内没购买过的用户,使我们S1到了前100名。在A榜结束前一天我们才找到了队友(队友的模型是用户模型三,队友也是一个新手,他是基于朋友的baseline基础上来做的,构造训练集的方法也是从baseline里面来的,没有考虑穿越问题),当时我们还没有做模型融合,因此我们在A榜最后一天做了三个用户模型的融合。在A榜结束后,我们就在思考,为什么队友的用户模型训练样本量只有8w,但是出来的效果却比我们两个用户模型几十万训练样本的效果要好,于是我们就想到了训练样本构造方案上的穿越问题,并且想通了为什么时间间隔这种强特在用户模型一和用户模型二反而是脏特征(线上线下特征与标签的关系不一样)。现在回想起来,感觉能坚持下来特别不容易,想过很多次,我们都在考虑要不要放弃,但是又觉得心有不甘,觉得开始了就应该坚持下去,最后撑到了决赛,感觉坚持和毅力真的很重要。在决赛上认识了很多大佬,才真的知道自己做的很简单,水平还差很多,激励了自己,总得来说,是一个很不错的经历。
下面总结一下决赛现场听到的比较惊艳的一些做法:
(1)第九和第十名都没有发现用户模型中的穿越问题,他们主要是依据用户回购时间分析,通过剔除最近一段时间没有购买的用户或者根据上次购买时间设置权重的方法,提高S1模型的精度(剔除用户的方法其实就是将模型关注点更多地倾向于有周期性购买且更为活跃的用户);
(2)第七名队伍发现了数据穿越问题,他们尝试了三种不同的S1数据集构造方案,第一种是每个月都用全部的用户,第二种是每个月用前三个月的用户,第三种是每个月用前两个月的用户,在他们发现数据泄露问题后,采取了第三种方案,并且针对S1做了一个迁移学习(植物大佬阿里妈妈冠军方案)的模型。针对B榜,他们先构造了一个7月份的数据集,用于学习前面的数据规律,预测8月份和9月份的用户,然后将得到的预测结果作为一个特征,利用8月份线下用户预测9月份线上用户,得到最终的S1结果;针对S2,他们训练了一个残差模型,先通过stacking思路训练得到一个初步结果,然后将实际标签减去初步结果得到对应的残差,再训练一个残差模型,最后将初步结果加上残差模型结果就得到了最终的线上S2预测结果;
(3)第六名队伍针对S1做了两个后处理规则,第一个是他们通过分析发现用户在进行浏览后很大概率会产生购买行为,因此他们将预测节点前几天有浏览行为但是没有下单的用户,将他们处理为会回购的用户;第二个规则是,他们将订单数大于30且活跃月数在六个月以上的用户排序在前面,优于其他用户,S1评分函数跟用户排序有关;针对S2,他们队做了一个跟普通队伍不一样的做法,我们都是预测用户在这个月首次下单的日期,他们是预测用户在这个月对某件商品首次下单的日期,这样就将样本粒度细化到用户和商品(具体打标签是根据用户在前面买过的商品userid-skuid这个主键来打的),此外还对预测结果做了后处理,将预测出来的正太分布结果进行压平前移处理,拟合正常月份的分布;
(4)第五名队伍采用了Hawkes建模的方法,而不是采用简单的滑窗提取特征,Hawkes的思路是说历史上发生的事情对未来的概率密度函数有影响,只是随着时间流逝这种影响会逐渐减弱,因此在这里可以提炼出用户先前行为对未来购买的概率影响;
(5)第四名队伍针对S2评分指标,采用了分位数回归的方法(分位数回归不要求样本服从正太分布,且对异常值点不敏感),取q < 0.5,对预测值偏大的结果惩罚更大,于是会出现模型预测值普遍偏小的情况,更符合S2的分布;此外,他们也利用A榜数据以及B榜数据做了stacking,作为特征放到B榜主模型里面;
(6)第三名选手对sku做了一个聚类,得到商品对应的subcate(先将用户看过买过的所有skuid作为词组成document,利用LDA主题模型得到10为embedding向量,然后再进行聚类操作);S1用不同的打标签方式构造3个模型,S2用不同的打标签方式以及目标函数构造5个模型,并进行stacking操作;针对S2的评价函数,直接进行优化(MSE的误差项在分子,S2评价函数的误差项在分母,对于MSE来说,误差越大的样本对分数产生的影响大,而对于S2来说,误差较小的样本对分数影响更大),因为S2函数二阶可导,可以直接求出一阶和二阶函数,修改lightgbm的损失函数,达到优化效果;
(7)冠军队伍方案:关于数据预处理方面,针对一个用户在一天内因为促销凑单而产生的多个订单的情况,进行订单的合并操作,以免影响后面特征的构造;构造了S1和S2的交叉特征,将S2的stacking结果作为S1的特征;提取了一些复杂推算特征,第一个是将用户上一次购买的时间间隔减去用户平均购买的时间间隔来推算用户下次购买的时间,第二个是推断para1是商品容量,先将用户购买目标品类商品para1求和然后除以购买时间间隔之和,得到用户对目标品类商品的平均每天的消耗量,然后将上一次购买目标品类商品para1总量除以商品日消耗量得到上次购买的商品大概可以用几天,然后将最后一笔下单距今天数减去可以使用的天数来推断用户下次下单的时间;针对S2评分函数,做了两个操作,第一个是将标签y取log,使其服从正态分布,并采用S2评分函数作为loss,第二个是采用分位数回归的方法拟合多条直线。