数据竞赛:联通套餐个性化匹配
机器学习是一个理论结合实践的学科,手头没有实际数据和案例的时候,看一看数据竞赛就不错。这是2018年的一个数据比赛,当年第一、第二的优秀选手都慷慨分享了他们的代码,可以根据代码回顾一下Top选手当时的思路,共同学习一下。
原文首发与我的公众号
背景
比赛链接:https://www.datafountain.cn/competitions/311
电信产业作为国家基础产业之一,覆盖广、用户多,在支撑国家建设和发展方面尤为重要。随着互联网技术的快速发展和普及,用户消耗的流量也成井喷态势,近年来,电信运营商推出大量的电信套餐用以满足用户的差异化需求,面对种类繁多的套餐,如何选择最合适的一款对于运营商和用户来说都至关重要,尤其是在电信市场增速放缓,存量用户争夺愈发激烈的大背景下。
套餐的个性化推荐,能够在信息过载的环境中帮助用户发现合适套餐,也能将合适套餐信息推送给用户。解决的问题有两个:信息过载问题和用户无目的搜索问题。各种套餐满足了用户有明确目的时的主动查找需求,而个性化推荐能够在用户没有明确目的的时候帮助他们发现感兴趣的新内容。
任务
此题利用已有的用户属性(如个人基本信息、用户画像信息等)、终端属性(如终端品牌等)、业务属性、消费习惯及偏好匹配用户最合适的套餐,对用户进行推送,完成后续个性化服务。可知,为一个分类任务,评价指标为Macro F1。
标签共有11个类别,目标就是根据给定信息预测用户的套餐。有选手找到了ID对应的套餐:
其中,各个类别的总数分别如下。有意思的是,早在18年的时候,这个数据集最常用的套餐就是腾讯大王卡。
很多时候,对任务的理解是能够出奇制胜的一招,然而本题任务比较明显,就是分类了。
数据
初赛数据A有70W数据,复赛数据B有35W数据。因此,整个模型跑起来比较耗时间。虽然大家拿到的原始数据都一样,但选择合适的数据作为输入是结果好坏的关键一步,也是经常被忽视的一步。对于任务理解和数据选择这种奠基基础的工作还是要足够的重视。方向选对了,才是拼努力和经验的时候。
第一名的方案中构建了一个白名单机制,因为训练集和测试集中存在重复样本。虽然用户ID在数据是不同的,但很多特征却是一样的,根据本月话费、上月话费、上上月话费、当月流量、上月流量、充值次数、年龄、通话时常,精确到小数点后四位仍然完全相同的用户则视为重复用户。注意的是很多队伍判别重复样本用了所有特征,而第一的方案做了减法,用以下8个特征找到了接近15%的重复样本。
cc = ['1_total_fee','2_total_fee','3_total_fee','month_traffic','pay_times','last_month_traffic','service2_caller_time','age']
当然,大家的第一想法是既然测试集中有重复样本,那直接把重复样本的标签拿过来当预测,保证了重复样本的完全准确。其实不然,GBDT类模型还是很强的,本来对重复样本的预测率就接近完全准确。所以第一的方案为测试集中非重复样本单独训练了一个模型,模型使用的训练集去掉了重复样本。而重复样本可以直接从训练集中查询,这也告诉我们数据样本的选取对于结果有多重要。
除了白名单的应用外,由于初赛和复赛分别有两套数据,还有技巧地应用了这两套数据。先用初赛+复赛数据训练得到分类概率,然后分类概率作为特征加到复赛数据中,再用复赛数据进行训练得到结果。
特征
特征工程是数据挖掘比赛耗时最多、最值得花精力的事了。特征工程包括特征转化、选择、和生成。白话一点的说法就是,换一换、减一减、加一加。尤其是如何增加新的特征,常规的尝试是根据经验运算一下,再就是从业务角度、机理角度来考虑。可以根据给定列,心里先想一下可以构建哪些特征,再画画图看看效果,扔进模型看看效果。
数值特征
首先当然是利用原有数据,原始数据中提供的本月话费、上月话费、上上月话费。
['1_total_fee', '2_total_fee', '3_total_fee', '4_total_fee']
整数特征(rounding feature)和小数特征 对过去四个月每个月的话费都计算话费的整数、整数的最后一位、小数、小数位是否为0、小数位是否为5、小数位的最后一位、小数位是5的几倍等
train[column+'_int'] = train[column].fillna(-1).astype('int')
train[column+'_int_last'] = train[column+'_int']%10 #last int
train[column+'_decimal'] = round(((train[column]-train[column+'_int'])*100).fillna(-1)).astype('int') #decimal
train[column+'_decimal_is_0'] = (train[column+'_decimal']==0).astype('int')
train[column+'_decimal_is_5'] = (train[column+'_decimal']%5==0).astype('int')
train[column+'_decimal_last'] = train[column+'_decimal']%10
train[column+'_decimal_last2'] = train[column+'_decimal']//5
train[column+'_extra_fee'] = ((train[column]*100)-600)%1000
train[column+'_27perMB'] = ((train[column+'_extra_fee']%27 == 0)&(train[column+'_extra_fee'] != 0)).astype('int')
train[column+'_15perMB'] = ((train[column+'_extra_fee']%15 == 0)&(train[column+'_extra_fee'] != 0)).astype('int')
分箱特征 对数值特征进行分箱,这里分箱的是下面的二阶特征进行分箱
for column in ['4-fea-dealta', '3-fea-dealta', '2-fea-dealta', '1-fea-dealta','1-3-fea-dealta','1-min-fea-dealta']:train[column+'_is_0'] = (train[column]==0).astype('int')train[column+'_is_6000'] = ((train[column]%6000 == 0)&(train[column] != 0)).astype('int') train[column+'_is_5'] = ((train[column]%5 == 0)&(train[column] != 0)).astype('int')train[column+'_is_10'] = ((train[column]%10 == 0)&(train[column] != 0)).astype('int')train[column+'_is_15'] = ((train[column]%15 == 0)&(train[column] != 0)).astype('int')train[column+'_is_27'] = ((train[column]%27 == 0)&(train[column] != 0)).astype('int')train[column+'_is_30'] = ((train[column]%30 == 0)&(train[column] != 0)).astype('int')train[column+'_is_50'] = ((train[column]%50 == 0)&(train[column] != 0)).astype('int')train[column+'_is_100'] = ((train[column]%100 == 0)&(train[column] != 0)).astype('int')train[column+'_is_500'] = ((train[column]%500 == 0)&(train[column] != 0)).astype('int')
构造的第一个特征就是这几个月话费的最低值,毕竟目标是预测套餐,想想我们生活中的场景,有几个月可能会超,但最低一般能够反应套餐的本身水平。
train['fea-min'] = train[[str(1+i) +'_total_fee' for i in range(4)]].min(axis = 1)
判断四个月的话费是否是一个整数。如果是小数,可能是由于超出了套餐费用叠加而来。
二阶特征
由于每个月的话费是套餐类型的重要特征,二阶或高阶特征则是通过对两个以上特征进行运算获得新特征。例如对原特征求差值
train['4-fea-dealta'] = round((train['4_total_fee'] - train['3_total_fee'])*100).fillna(999999.9).astype('int')
train['3-fea-dealta'] = round((train['3_total_fee'] - train['2_total_fee'])*100).fillna(999999.9).astype('int')
train['2-fea-dealta'] = round((train['2_total_fee'] - train['1_total_fee'])*100).fillna(999999.9).astype('int')
train['1-fea-dealta'] = round((train['4_total_fee'] - train['1_total_fee'])*100).fillna(999999.9).astype('int')
train['1-3-fea-dealta'] = round((train['3_total_fee'] - train['1_total_fee'])*100).fillna(999999.9).astype('int')
train['1-min-fea-dealta'] = round((train['1_total_fee'] - train['fea-min'])*100).fillna(999999.9).astype('int')
统计特征
对1-2-3-4_total_fee进行min ,max ,std ,mean等操作
前四个月话费最低值
前四个月话费不同值的个数
grid特征
grid特征其实是我自己起的名字,大意是每一个用户对应着一个特征,我们也可以看做是每一个特征对应着用户,因此可以找到特征相关的特点,得到一个特征为行名的表,然后融合到用户表的里。
比如,首先筛选了过去四个月使用话费比较多花样的用户集,至少出现三种不同的数
df['fea_unum'] = df[['1_total_fee','2_total_fee','3_total_fee', '4_total_fee']].nunique(axis=1)
df.drop_duplicates(subset =['1_total_fee','2_total_fee','3_total_fee', '4_total_fee'],inplace=True)
df = df[df.fea_unum>2]
大致是把count encoder进一步推向极致的玩法: 比较复杂和高阶也不一定起作用的特征。
类别特征
类别特征比较简单,原数据中的特征进行LabelEncoder即可
序列特征
第二名除了常规的特征外,还训练了word2vec的embedding特征。word2vec是2018年bert流行之前最常用的词嵌入模型,用于给单词编码,也是成绩上分的一个关键。当然了,想到word2vec的使用首先需要理解序列特征的含义,根据上下文(context)推测中心词,以及根据中心词推测上下文,从而来构建序列中蕴含的信息。对数据和业务的理解,能够解读出序列蕴含的信息从而正确、灵活的运用。
其实,除了word2vec词嵌入特征外,1_total_fee至4_total_fee上,4个月的词频统计也可以看作NLP中的CountVectorizer。
所以根据前四个月的话费用NLP的各项技术也对结果有所提高
模型
对于结构化的表格数据,lightgbm、xgboost、catbost为代表的GBDT类模型几乎占据统治地位,是首选模型。
params = {'metric': 'multi_logloss','num_class':11,'boosting_type': 'gbdt', 'objective': 'multiclass','feature_fraction': 0.7,'learning_rate': 0.02,'bagging_fraction': 0.7, 'num_leaves': 64,'max_depth': -1, 'num_threads': 16, 'seed': 2018, 'verbose': -1, }
验证
分层交叉验证,取样时,根据目标label的分布每次取样中的label也满足同样分布。
folds = StratifiedKFold(n_splits= num_folds, shuffle=True, random_state=1234)
技巧总结
数据:如何更好的选取训练样本?这里的关键是选取非重复样本进行学习。如何在决赛更加合理利用新增数据?
特征:除了在数据集上,精细的做一做各种尝试外。word2vec来自NLP的也值得尝试
后处理:针对比赛的评价函数Macro F1, 例如830的类别F1最低,只有不到0.3,严重拉低了整体macro-F1的分数,可以对其进行特殊后处理,增加子类别的F1,以达到分数最大化。
1st solution:https://github.com/PPshrimpGo/BDCI2018-ChinauUicom-1st-solution
2nd solution:https://github.com/PandasCute/2018-CCF-BDCI-China-Unicom-Research-Institute-top2
6th solution:https://github.com/ZengHaihong/2018_CCF_BDCI_ChinaUnicom_Package_Match_Rank6
联系方式
公众号搜索:YueTan
数据竞赛:联通套餐个性化匹配相关推荐
- CCF大数据竞赛-面向电信行业存量用户的智能套餐个性化匹配模型
题目:面向电信行业存量用户的智能套餐个性化匹配模型(2018 CCF-大数据竞赛(联通研究院举办) ) 网址:https://www.datafountain.cn/competitions/311/ ...
- 开源-BDCI2018面向电信行业存量用户的智能套餐个性化匹配模型Top1解决方案和代码...
本人经过作者同意,公布了:BDCI2018面向电信行业存量用户的智能套餐个性化匹配模型数据竞赛top1解决方案和代码.该方案利用已有的用户属性(如个人基本信息.用户画像信息等).终端属性(如终端品牌等 ...
- 开源-BDCI2018面向电信行业存量用户的智能套餐个性化匹配模型Top1解决方案和代码
来源 本人经过作者同意,公布了:BDCI2018面向电信行业存量用户的智能套餐个性化匹配模型数据竞赛top1解决方案和代码.该方案利用已有的用户属性(如个人基本信息.用户画像信息等).终端属性(如终端 ...
- 面向电信行业存量用户的智能套餐个性化匹配模型(top6)
1.赛题链接:面向电信行业存量用户的智能套餐个性化匹配模型 2.赛题任务: 利用已有的用户属性(如个人基本信息.用户画像信息等).终端属性(如终端品牌等).业务属性.消费习惯及偏好匹配用户最合适的套餐 ...
- CCF BDCI面向电信行业存量用户的智能套餐个性化匹配模型解题方案
参考源码:Top2方案 原始数据 问题主要是根据如下的数据所进行的11类分类问题. 字段 中文名 数据类型 说明 USERID 用户ID VARCHAR2(50) 用户编码,标识用户的唯一字段 cur ...
- 数据竞赛专题 | 从赛题理解到竞赛入门基础
为了帮助更多竞赛选手入门进阶比赛,通过数据竞赛提升理论实践能力和团队协作能力.DataFountain 和 Datawhale 联合邀请了数据挖掘,CV,NLP领域多位竞赛大咖,将从赛题理解.数据探索 ...
- 中国数据竞赛解决方案汇总
搜索了点进行汇总,方便大家找以及参考 名称 类型 截止日期 解决方案 平台 主办方 津南数字制造算法挑战赛--赛场一:原料企业工艺优化 预测.医药化学 20190221 第?名算法 第6名代码 第17 ...
- 数据竞赛专题 | 数据探索-从数据中发现隐藏价值
为了帮助更多竞赛选手入门进阶比赛,通过数据竞赛提升理论实践能力和团队协作能力.DataFountain 和 Datawhale 联合邀请了数据挖掘,CV,NLP领域多位竞赛大咖,将从赛题理解.数据探索 ...
- 达观数据于敬:个性化推荐系统实践
达观数据于敬:个性化推荐系统实践 在DT(data technology)时代,网上购物.观看视频.聆听音乐.阅读新闻等各个领域无不充斥着各种推荐,个性化推荐已经完全融入人们的日常生活当中.个性化推荐 ...
最新文章
- C++实现int与string之间的相互转换
- python构建一个简单的备份脚本
- Python之美[从菜鸟到高手]--一步一步动手给Python写扩展(异常处理和引用计数)
- MS SQLSERVER通用存储过程分页
- 导出Excle java
- ApiOperationSupport注解的使用
- Java finally
- java decompiler_Java Decompiler(Java反编译工具)
- Android Monkey测试入门(摘)
- 从SAP Leonardo到SAP Data Intelligence
- 大河抽奖盲盒运营版 1.9.12开源版
- 关于SQL的基础语法(二)
- 基本文件上传漏洞攻击实验
- 云课堂让职业院校软件开发教学更简单
- 比色皿洗涤不干净会造成很大实验误差
- SpringCloud第八章:Gateway新一代网关
- TypeScript基础快速上手
- 中国历史上十大经典遗言
- #if endif 的意思
- 超分辨率的国内外研究现状