天池O2O竞赛复盘:除了XGBoost,冠军方案中那些被低估的‘特征工程’细节与业务思考
天池O2O竞赛复盘冠军方案中特征工程的业务逻辑与技术实现在数据科学竞赛中模型算法往往受到最多关注但真正决定胜负的通常是那些隐藏在代码背后的特征工程细节。2016年天池O2O优惠券使用预测竞赛的冠军方案就是一个典型案例。本文将深入剖析该方案中那些被低估却至关重要的特征工程技巧以及它们背后的业务思考。1. 竞赛背景与数据特性天池O2O优惠券使用预测竞赛要求参赛者根据用户2016年1月至6月的线上线下消费行为预测用户在7月领取优惠券后15天内是否会核销。评价指标采用AUC值先对每个优惠券单独计算核销预测的AUC再对所有优惠券的AUC求平均。数据集包含两个主要部分线下数据用户线下消费和优惠券领取核销记录线上数据用户线上点击/消费和优惠券领取核销记录数据的时间跨度为2016年1月1日至6月30日需要预测的是7月份用户领取优惠券后的核销行为。这种时间划分带来了几个关键挑战如何合理划分训练集和测试集如何处理时间序列数据中的概念漂移如何构建具有时间鲁棒性的特征冠军方案采用了一种创新的滑窗法来处理这些挑战我们将在后续章节详细讨论。2. 数据划分的时序逻辑滑窗法的业务考量在时间序列预测问题中数据划分方式直接影响模型效果。冠军方案采用了独特的滑窗法来构建多份训练数据集这种方法背后有着深刻的业务逻辑。2.1 滑窗法的具体实现方案中将数据划分为三个主要部分# 训练集一 dataset1 off_train[(off_train.date_received20160414)(off_train.date_received20160514)] feature1 off_train[((off_train.date20160101)(off_train.date20160413))| ((off_train.datenull)(off_train.date_received20160101)(off_train.date_received20160413))] # 训练集二 dataset2 off_train[(off_train.date_received20160515)(off_train.date_received20160615)] feature2 off_train[((off_train.date20160201)(off_train.date20160514))| ((off_train.datenull)(off_train.date_received20160201)(off_train.date_received20160514))] # 测试集 dataset3 off_test feature3 off_train[((off_train.date20160315)(off_train.date20160630))| ((off_train.datenull)(off_train.date_received20160315)(off_train.date_received20160630))]这种划分方式有两个主要优点增加训练样本通过滑动窗口可以获得多份训练数据交叉验证便于进行模型调参和验证2.2 业务逻辑支撑滑窗法的设计并非随意而是基于对业务特性的深刻理解用户和商户属性的稳定性用户消费习惯和商户特性相对稳定可以用历史数据反映当前特性标签提取的时间窗口6月15日后的数据无法准确反映15天内是否会核销因此需要从训练集中剔除特征提取的时间范围虽然某些时间段的数据不能用于标签提取但仍可用于特征工程这种划分方式确保了模型既能学习到稳定的用户/商户特性又能避免因时间窗口不完整导致的标签噪声。3. 特征工程的多视角构建冠军方案的特征工程是其成功的关键它从多个视角构建了丰富而有业务解释性的特征。这些特征可以归纳为以下几类3.1 用户相关特征用户特征是预测其行为的基础。方案中构建的用户特征包括特征类别具体特征示例优惠券使用统计领取次数、核销次数、核销率消费习惯不同折扣类型的核销率、平均消费折率地理行为核销优惠券的平均/最小/最大用户-商户距离线上行为点击率、购买率、领取率这些特征通过代码实现如下# 用户核销优惠券的总次数 t7 user3[(user3.date!null)(user3.coupon_id!null)][[user_id]] t7[buy_use_coupon] 1 t7 t7.groupby(user_id).agg(sum).reset_index() # 用户购买的总次数 t8 user3[user3.date!null][[user_id]] t8[buy_total] 1 t8 t8.groupby(user_id).agg(sum).reset_index() # 用户领取优惠券的总次数 t9 user3[user3.coupon_id!null][[user_id]] t9[coupon_received] 1 t9 t9.groupby(user_id).agg(sum).reset_index()3.2 商户相关特征商户特性直接影响优惠券的吸引力。方案中构建的商户特征包括优惠券被领取次数、核销次数、核销率核销的平均/最小/最大消费折率核销用户的地理分布特征不同优惠券类型的表现差异这些特征帮助模型理解不同商户的优惠券吸引力以及商户自身的运营特点。3.3 用户-商户交互特征用户与特定商户的交互历史是预测核销行为的重要信号。方案中构建的交互特征包括用户在特定商户的优惠券领取和核销历史用户在该商户的核销行为占其总核销行为的比例用户在该商户的未核销行为比例用户与商户的地理距离特征这些特征通过代码实现如下# 用户在特定商户下的消费次数 t feature3[[user_id,merchant_id,date]] t t[t.date!null][[user_id,merchant_id]] t[user_merchant_buy_total] 1 t t.groupby([user_id,merchant_id]).agg(sum).reset_index() # 用户在特定商户处领取优惠券次数 t1 feature3[[user_id,merchant_id,coupon_id]] t1 t1[t1.coupon_id!null][[user_id,merchant_id]] t1[user_merchant_received] 1 t1 t1.groupby([user_id,merchant_id]).agg(sum).reset_index() # 用户在特定商户处核销优惠券的次数 t2 feature3[[user_id,merchant_id,date,date_received]] t2 t2[(t2.date!null)(t2.date_received!null)][[user_id,merchant_id]] t2[user_merchant_buy_use_coupon] 1 t2 t2.groupby([user_id,merchant_id]).agg(sum).reset_index()3.4 优惠券自身特征优惠券本身的属性也是影响核销的关键因素。方案中构建的优惠券特征包括优惠券类型直接优惠 vs 满减折扣率计算满减优惠券的最低消费门槛领取时间特征星期几、月中日期历史表现出现次数、核销次数、核销率这些特征通过以下代码实现# 计算折扣率函数 def calc_discount_rate(s): s str(s) s s.split(:) if len(s) 1: return float(s[0]) else: return 1.0 - float(s[1])/float(s[0]) # 是不是满减卷 def is_man_jian(s): s str(s) s s.split(:) if len(s) 1: return 0 else: return 1.0 # 周几领取的优惠券 dataset3[day_of_week] dataset3.date_received.astype(str).apply( lambda x:date(int(x[0:4]),int(x[4:6]),int(x[6:8])).weekday()1)4. 数据泄漏的识别与利用在实际数据竞赛中识别和合理利用数据泄漏Data Leakage往往是取得好成绩的关键。冠军方案中敏锐地发现并利用了赛题中的一个重要泄漏点。4.1 泄漏点的发现赛题提供的预测集中包含了同一个用户在整个7月份里的优惠券领取情况。这导致了一种特殊场景一个用户在7月10日领取了某优惠券该用户在7月12日和7月15日又领取了相同的优惠券这种情况下7月10日领取的优惠券被核销的可能性就很大这种未来信息在实际业务场景中是不可能获取的属于典型的数据泄漏。4.2 泄漏特征的构建冠军方案基于这个泄漏点构建了一系列特征包括用户当月领取的所有优惠券数量用户当月领取的特定优惠券数量用户当天领取的优惠券数量用户领取特定优惠券的时间间隔用户领取特定商家优惠券的数量这些特征的构建代码如下# 用户领取的所有优惠券数目 t dataset3[[user_id]] t[this_month_user_receive_all_coupon_count] 1 t t.groupby(user_id).agg(sum).reset_index() # 用户领取的特定优惠券数目 t1 dataset3[[user_id,coupon_id]] t1[this_month_user_receive_same_coupon_count] 1 t1 t1.groupby([user_id,coupon_id]).agg(sum).reset_index() # 用户在领取优惠券的当天共领取了多少张优惠券 t4 dataset3[[user_id,date_received]] t4[this_day_user_receive_all_coupon_count] 1 t4 t4.groupby([user_id,date_received]).agg(sum).reset_index()4.3 业务可行性的反思虽然这些泄漏特征带来了显著的AUC提升约10个百分点但方案作者也明确指出这些特征在实际业务中是无法获取的。这一点体现了优秀数据科学家应有的业务思维——不仅要追求竞赛成绩还要考虑解决方案的实际可行性。在实际业务场景中我们需要寻找与这些泄漏特征相关但不依赖未来信息的替代特征例如用户历史领取频率用户对特定优惠券的偏好程度用户在特定时间的活跃程度这种业务思维使得该方案不仅适用于竞赛也为实际业务应用提供了有价值的参考。5. 模型构建与特征重要性虽然本文重点讨论特征工程但简单了解模型构建过程有助于理解特征的实际应用。冠军方案采用了XGBoost模型其参数设置如下params { booster:gbtree, objective:rank:pairwise, eval_metric:auc, gamma:0.1, min_child_weight:1.1, max_depth:5, lambda:10, subsample:0.7, colsample_bytree:0.7, colsample_bylevel:0.7, eta:0.01, tree_method:exact, seed:0, nthread:12 }模型训练后可以通过特征重要性分析来验证特征工程的有效性。通常好的特征工程会产生以下特征重要性分布用户-商户交互特征权重较高用户历史行为特征次之基础统计特征作为补充这种分布反映了预测问题的本质用户对特定商户的偏好和历史行为最能预测其未来的优惠券使用行为。6. 可迁移的特征工程方法论从天池O2O竞赛冠军方案中我们可以总结出一套可迁移的特征工程方法论适用于各种用户行为预测问题多视角特征构建从用户、商户、交互三个维度系统性地构建特征时间序列处理采用符合业务逻辑的滑窗法处理时间序列数据泄漏识别与利用在竞赛中合理利用泄漏同时考虑业务可行性特征重要性验证通过模型反馈验证特征的有效性业务解释性确保每个特征都有合理的业务解释避免黑箱特征这套方法论不仅适用于优惠券预测场景也可以应用于推荐系统、广告点击率预测、用户流失预警等各种用户行为预测场景。