2017 CCF 大数据竞赛Top4%

2017 CCF 大数据比赛记录(首赛)

此次是在CCF平台与天池平台联合举办的大数据比赛,题目是:商场中精确定位用户所在店铺,主办方是蚂蚁金服。我和朋友两个人都是第一次参加这种大数据比赛,也算一步一个坑走了过来…线下赛最终经过反作弊筛选后是前100,共有2845支队伍。最后因为我俩临近期末考试,比赛也就就此结束了。

赛题

给出用户在商场使用手机支付时所采集到的信息,包括用户信息,店铺信息,商场信息等,要求预测给出上述信息后精准预测用户所在店铺。具体给出的数据表可以点击这里来看。

赛题分析

  1. 问题初看是多分类问题,但是如果直接处理的话$label$上千个。仔细观察题目可以发现, 不同商场里面的商店相互独立,所以这里考虑为每一个商场建立一个独立的预测模型。
  2. WiFi信息信息量最大,为主要信息。但是所给WiFi信息繁杂不规范,给WiFi信息找到合理的处理模式至关重要(WiFi指纹)。
  3. 如何挖掘WiFi隐含信息,并建立其与$label$之间的关联(具体WiFi id、信号强度、连接与否)至关重要。
  4. WiFi信息中有比较稳定的WiFi(商店自己的WiFi),有用户的个人热点,还有各种公共WiFi(CMCC之类的)。这里讲后两者当做是不稳定WiFi,或者说是噪音WiFi,预测时过滤掉
  5. 许多记录WiFi信息缺失,将此作为异常值直接删除(因为总数据量大,单条数据影响不大,WiFi信息又为最重要的信息)
  6. 不同商铺的经纬、WiFi信息有大量重合,说明商场存在多楼层问题,并且单单依靠原始WiFi信息无法全面对楼层进行区分。
  7. 按照商场划分模型后,每个商场还是有几十上百个$label$,可以多分类,或者二分类构造多分类。(因为$label$多,二分类构造多分类会遇到$label$不均衡的问题,需依靠采样来解决)
  8. 百万数据,训练时间成本太高,需要在特征工程、反复验证时减小时间成本。(前期用$LR$模型试错)
  9. 也算带有时间序列,如何构造可靠的线下验证集
  10. 顾客数据训练集与预测集交集仅为三分之一,顾客特征是否为噪音(我们尝试过顾客特征空值填充、保留,取顾客有交集的单独训练,效果均不好)

我们的具体操作

数据预处理

  • 删除公共WiFi:如果一个WiFi在一个以上商场出现或者在某个商场覆盖率超过一定范围,那么可以断定此WiFi为噪音信息
  • 删除移动热点或者出现次数较少的WiFi:如果一个wifi出现次数少于某个阈值或者出现时间仅有一天,那么可以断定次WiFi为噪音信息
  • 训练集和测试集WiFi取交集,因为对于以后要构建的WiFi指纹
  • 删除WiFi信息为空的记录
  • $GPS$离群点的删除
  • 定义不同等级稳定WiFi列表(不同等级原因某些特征防止维度爆炸),WiFi出现总次数超过一定阈值或者频率前200
  • 字典:shop历史WiFi

经纬度信息

该题目给了两种经纬度信息店铺经纬度和购买发生时候的经纬度(预测集只有后者)。两种经纬度理论上差距应该很小,实际差距很大。单独利用经纬度信息训练的结果并不理想,印象中只有70%多一点。

  • 对经纬度数值做了泛化(精确小数点位数),印象中最后的粒度可能在10米左右
  • $L1、L2$距离,在商场中随意选择一个点做基准,经纬度到这个点的距离
  • 经纬度聚类、以商铺经纬为质心聚类,然后哑编码(没有提升,后来没用)

时间特征的处理

  • 饭点指示器(万分位的提升)
  • 早晨深夜指示器,因为这两种店铺可能比较固定

用户特征

构造的所有的用户特征在我们这都是坑,一是因为训练集、预测集用户交集只有$1/3$左右,我们尝试过空值填充、空值保留、只保留交集部分用户特征、对交集部分用户单独训练预测…均是强力的反向上分特征。另一个原因可能是,每个用户在数据统计量均不足,没有统计意义,所以这就类似于$ID$类特征,妥妥没有现实意义的过拟合特征。

  • 用户购买力
  • 用户常去商店
  • 用户与WiFi相关特征

WiFi特征

WiFi特征是最为重要的特征,我们构建的如下的WiFi特征

当前记录连接到的最强WiFi

仅用这一个特征采取规则预测准确率也可达到80%,是个强特。哑编码。

记录中稳定WiFi的数量

商场WiFi原点

根据商场稳定WiFi(等级频率前50)建立一个基准点,计算每条数据到基准点的距离(离散化之后),没有的稳定WiFi按照强度为-99处理。

WiFi指纹

根据商场稳定WiFi(等级频数大于20),训练集、预测集上下$concat$将所有WiFi id展开为特征,值为当前记录对应WiFi的强度,将强度离散化(粒度为10).

WiFi评分(最后仅用于规则)

遍历数据建立嵌套字典$WiFi_score$,一层$key$为WiFi id,二层$key$为一层WiFi出现过的店铺,二层$val$为历史上该WiFi在该店铺强度的中位数。打分,对于每条数据的稳定WiFi信息,遍历嵌套字典$WiFi_score$,用打分函数对每条WiFi出现过的店铺打分。分数结果最高的直接作为结果。打分函数如下:

其中:

$tanh={e^x-e^{-x}\over e^x+e^{-x}}$

$power_{now}$表示当前记录中当前WiFi强度

$power_{middle}$表示嵌套字典中当前WiFi对各商店的强度中位数

$k$为参数,最后确定值为2

仅仅用该种规则,不用机器学习模型,精度就可以达到87%+(还是89%+来着)。但是当时将所得结果转化为特征加入模型训练,分数强力掉了一波。不用说,这里用了训练集构造的规则结果当特征继续加入训练集训练当然会造成$label leak$,也就是强力过拟合。

遗憾的是,当时没考虑采用滑动窗口,更没有使用候选集,这个特征就放弃了…如果采用滑动窗口,在特征采取区间构造嵌套字典$WiFi_score$,打分结果加在训练区间的特征,结果应该不会差。

如果在滑动窗口的基础上,采用候选集(甚至可以用这个打分函数构造候选集),并把分数当做该条记录对候选商店的特征,绝对会是个强力特征(这里其实可以理解为类条件概率)

WiFi id组合的最长公共子串

典型看起来巧妙没有卵用系列

xgb构造特征组合

用$model.apply()$返回叶子节点$index$构造特征。注意此部分一定要划分数据集,即一部分数据用于生成特征,另一部分数据集用于加入该部分生成特征并训练。

总结

以上就是比赛用到的、或者有代表性值得拿出来说的特征。这里发现其实我们的特征并不多,其实我们比赛时候尝试构造过远远比这多得多的特征,只是因为效果或者训练时间的原因,最后筛选剩这些。挖空心思构造各种想法复杂、实现困难、强力掉分的反向特征实在是能锤炼人强大的灵魂 :)。

滑窗的欠缺

  • 无法构造统计类特征,构造了会造成过拟合
  • 训练集过大时间成本过高
  • 既然也是时间序列,信息或多或少有时效性(比如WiFi)

候选集的欠缺

  • 无法构造联合特征,商店-WiFi,商店-经纬度等等
  • 完全丢弃了任何商店特征,包括特别重要的统计特征

总之,这两种的欠缺也早早为我们的特征工程确定了天花板,也大大限制了我们特征的提取,少了这两项能够造的有效特征数量至少少了70%

数据集的划分

这里说一下我们实际的数据集划分,在最后给出可能的改进。

这是个时间相关的问题,而且数据量比较大,就放弃了分层$k$折交叉,我们选取一个周作为本地验证集

模型训练

模型选择

最开始我们用的是$xgboost$的多分类模型,分商场进行预测,效果一般。然后转向使用二分类实现多分类,提升显著。具体为每次将一个$label$作为正例,其他作为反例,每次输出每条记录是该正例的概率,最后以最大概率的$label$作为预测结果

二分类&样本不均衡

每次有将近100个$label$每次只将一个当做正例,正负样本比会非常小,会导致时间成本过长,精度下降,需要靠采样解决。

我们直接放弃了上采样跟随机下采样,前者会加大时间成本后者因为样本比相差悬殊,随机采样可能破坏边界样本的分布。我们直接选用了用规则下采样以保存边界有效信息,具体通过购买经纬度与店铺经纬度的距离与当前样本$top2$强度任一WiFi存在历史列表的shop的并集。这样加快了训练速度,但是精度略有损失,可能因为评价指标是$AUC$所以对样本不均衡不敏感。我们最后还是放弃了采样。

二分类还会造成一个问题:每次对于一个模型训练的时候,正样本平均只有小几百,负样本可能大几千甚至上万。这样正样本太少可能造成过拟合,因此我们模型部分着重调整了泛化部分的参数。

模型参数

xgb的模型参数就常用的那几个:

1、eta[默认0.3]

和GBM中的 learning rate 参数类似。 通过减少每一步的权重,可以提高模型的鲁棒性。 典型值为0.01-0.2。

2、min_child_weight[默认1]

建立每个模型所需要的最小样本数。决定最小叶子节点样本权重和。 和GBM的 min_child_leaf 参数类似,但不完全一样。XGBoost的这个参数是最小样本权重的和,而GBM参数是最小样本总数。 这个参数用于避免过拟合。当它的值较大时,可以避免模型学习到局部的特殊样本。 但是如果这个值过高,会导致欠拟合。这个参数需要使用CV来调整。

3、max_depth[默认6]

和GBM中的参数相同,这个值为树的最大深度。 这个值也是用来避免过拟合的。max_depth越大,模型会学到更具体更局部的样本。 需要使用CV函数来进行调优。 典型值:3-10

4、max_leaf_nodes

树上最大的节点或叶子的数量。 可以替代max_depth的作用。因为如果生成的是二叉树,一个深度为n的树最多生成n2个叶子。 如果定义了这个参数,GBM会忽略max_depth参数。

5、gamma[默认0]

在节点分裂时,只有分裂后损失函数的值下降了,才会分裂这个节点。Gamma指定了节点分裂所需的最小损失函数下降值。 这个参数的值越大,算法越保守。这个参数的值和损失函数息息相关,所以是需要调整的。

6、max_delta_step[默认0]

这参数限制每棵树权重改变的最大步长。如果这个参数的值为0,那就意味着没有约束。如果它被赋予了某个正值,那么它会让这个算法更加保守。 通常,这个参数不需要设置。但是当各类别的样本十分不平衡时,它对逻辑回归是很有帮助的。 这个参数一般用不到,但是你可以挖掘出来它更多的用处。

7、subsample[默认1]

和GBM中的subsample参数一模一样。这个参数控制对于每棵树,随机采样的比例。 减小这个参数的值,算法会更加保守,避免过拟合。但是,如果这个值设置得过小,它可能会导致欠拟合。 典型值:0.5-1

8、colsample_bytree[默认1]

和GBM里面的max_features参数类似。用来控制每棵随机采样的列数的占比(每一列是一个特征)。 典型值:0.5-1

9、colsample_bylevel[默认1]

用来控制树的每一级的每一次分裂,对列数的采样的占比。 我个人一般不太用这个参数,因为subsample参数和colsample_bytree参数可以起到相同的作用。但是如果感兴趣,可以挖掘这个参数更多的用处。

10、lambda[默认1]

权重的L2正则化项。(和Ridge regression类似)。 这个参数是用来控制XGBoost的正则化部分的。虽然大部分数据科学家很少用到这个参数,但是这个参数在减少过拟合上还是可以挖掘出更多用处的。

11、alpha[默认1]

权重的L1正则化项。(和Lasso regression类似)。 可以应用在很高维度的情况下,使得算法的速度更快。

12、scale_pos_weight[默认1]

在各类别样本十分不平衡时,把这个参数设定为一个正值,可以使算法更快收敛。

真正调参方法

  • 选择较高的学习速率(learning rate)。一般情况下,学习速率的值为0.1。但是,对于不同的问题,理想的学习速率有时候会在0.05到0.3之间波动。选择对应于此学习速率的理想决策树数量
  • 对于给定的学习速率和决策树数量,进行决策树特定参数调优(max_depth, min_child_weight, gamma, subsample, colsample_bytree)。
  • xgboost的正则化参数的调优。(lambda, alpha)。这些参数可以降低模型的复杂度,从而提高模型的表现。
  • 降低学习速率,确定理想参数。

实际上比赛的调参

因为数据量巨大,样本充足,所以实际上参数不是太差的话对结果影响不是很大。况且大的数据量调参时间成本太高,而且这个比赛自始至终不是一个分类器,90+的商场每个商场有多少个$label$就有多少个分类器,总共上千个不同分类器,也不可能找出每个分类器最适合的参数。因此本次比赛只是在刚开始时调整了一下树的深度,最后的时候调整了一下过拟合的参数。

模型融合

最后我们取了$0.65xgb+0.35lgb$简单的加权融合,融合方法是直接把二分类生成的概率加权相加,选出概率最大的当做$label$。

其实可以再融合上多分类,提高模型融合的多样性,但是因为我们没分滑窗,训练时间巨长,就没再添加。

本次比赛不可能$stacking$,就像不可能$CV$一样,训练一次时间都受不了,更别说反复多次。

后话

这次比赛成绩不算好,但第一次正式参赛,通过比赛收获了很多,一整套流程熟悉了一遍,对模型、特征的理解也深了好多。我跟朋友两个一步一个坑,摸着石头过河,心情大起大落也是锻炼了心态…最后初赛要截止时候,我们还在复赛线边缘徘徊,眼看着自己慢慢掉出复赛线,反复讨论着各种可能,尝试构造各种复杂但看起来有意义的特征,每次连夜实现,怀着希望入睡,第二天都会被反向上分现实打脸。记得第一次掉出前100时候,我俩彻夜讨论到凌晨五点出了一套方案,从绝望到满怀希望,实际上上第二天还是究极反向上分,又坐了一次过山车:)(其实现在想想那套方案是明显的$label leak$)。

现在想想,这次比赛主要差在了套路经验上,没能构造滑窗和候选集严重限制了特征的构造,几乎所有统计特征、交叉特征、先验后延特征都无法构造,有效特征数量至少少了70%。特别是候选集,之前闻所未闻,各种地方也了解不到,后来才知道,这种方法可能从之前摩拜比赛大佬们一路传承了下来…

对样本特征的理解差也导致我们踩了好多坑。在一开始对数据分布、统计特征的构造不清楚导致我们训练时间成本巨大。现在想想当时构造的好多看起来有效的特征其实都是明显的$label leak$(先从当前数据提取特征,再用当前数据训练模型),这次比赛过后对标签泄露、过拟合是印象深刻了。

0%