作者介绍:王贺,武汉大学,计算机专业,研二

写在前面

首先很幸运能够拿到这次冠军,有两位大佬队友是这次获胜的关键,再次感谢鹏哥和阿水。

同时希望我的分享与总结能给大家带来些许帮助,并且一起交流学习。

接下来将会呈现ppt内容和部分代码

  1. 赛题分析

  2. 探索性分析

  3. 数据预处理

  4. 特征工程

  5. 算法模型

  6. 思考总结

1. 赛题分析

本次大赛提供了讯飞AI营销云的海量广告投放数据,参赛选手通过人工智能技术构建预测模型预估用户的广告点击概率,即给定广告点击相关的广告、媒体、用户、上下文内容等信息的条件下预测广告点击概率。

这又是一道关于CTR的问题,对于CTR问题而言,广告是否被点击的主导因素是用户,其次是广告信息。所以我们要做的是充分挖掘用户及用户行为信息,然后才是广告主、广告等信息。

赛题特征:广告信息、媒体信息、用户信息、上下文信息

提供数据:共1001650初赛数据 和 1998350条复赛数据(复赛训练数据为初赛数据和复赛数据)

评估指标:通过logloss评估模型效果(越小越好),公式如下:


其中N表示测试集样本数量,yi表示测试集中第i个样本的真实标签,pi表示第i个样本的预估转化率。这类评估函数常用logloss和AUC,简单的说logloss更关注和观察数据的吻合程度,AUC更关注rank order。如果是按照概率期望来进行收费投放的话就用logloss,如果定投一定量就用AUC,主要还是和业务相关。

2. 探索性分析

这一部分将会对部分数据进行分析,另外获取每个类别特征的转化率分布情况判断特征效果,看分布可以有一个很好的初步验证作用。

不同时刻的曝光量和点击率变化,将一天分成4个时段的曝光量和点击率情况


训练集正负样本比例,大约为1:4,应该经过了降采样。


广告的长宽是很重要的特征,正负样本中关于这两个特征的分布存在较为明显的区别。



在训练集中的部分热门广告,adid为1537089的广告在训练集中曝光次数超过了12万次。


在训练集中的部分热门广告,adid为1537080的广告在训练集中点击率最高,接近0.5。

OPPO和 vivo的用户最多,而这两种机型的用户点击率也高于其他手机的用户


3. 数据预处理

由于数据噪音比较多,所以细致的预处理能够是模型更具泛化性,同时挖掘更多特征。

1.初复赛训练数据合并后去重(7361条)

2.提取广告投放时间信息,日期、小时以及早中晚时间段

0-6>--1 | 7-12>--2 | 13-18>--3 | 19-24>--4

3. 细分广告主行业与媒体广告位,去除只有一个取值的字段

102400_102401>--102400 102401

4. 清洗手机品牌和机型字段,对同类进行合并

iphone>--apple | redmi>--xiaomi | honor>--huawei

5. 对操作系统及其版本、名称进行更细粒度的刻画

5.1.1>--5 1 1 | 6.0.1>--6 0 1

6. 构造虚拟用户组别,对用户标签和其他类别特征进行编码

7. 对city特征进行切分,如


框内为身份证前六位,51代表广东省,04代表广州市,10代表白云区

8. 缺失值填充,对不同类型的数据填充不同类型的值

4. 特征工程

4.1 特征构造

1)基础特征:原始特征(广告信息 媒体信息 用户信息 上下文信息)

2)One-hot:将类别特征离散化

由于最后融合三套代码的结果,所以有的代码进行了one-hot,有的没有这样做,而是直接labelencoder。

3)user_tags多值特征:因为包含用户的属性信息,所以完美的表达user_tags至关重要,提取有效属性,减少冗余。


从下图可以看出user_tags中标签属性的重要性分布情况


4)统计特征

统计特征我们用的都是常规操作,如count、ratio、nunique和ctr相关特征。

count:一维+二维count计数特征(如广告主id共计投放次数)

# 对交叉特征的求count

# add cross feature

first_feature = ['app_cate_id', 'f_channel', 'app_id']

second_feature = ["make", "model", "osv1", "osv2", "osv3", "adid", "advert_name", "campaign_id", "creative_id","carrier", "nnt", "devtype", "os"]

cross_feature = []

for feat_1 in first_feature:

for feat_2 in second_feature:

col_name = "cross_" + feat_1 + "_and_" + feat_2

cross_feature.append(col_name)

data[col_name] = data[feat_1].astype(str).values + '_' + data[feat_2].astype(str).values

# 求count计数特征

ratio:类别偏好的ratio比例特征(如广告主id的某个广告id投放比例)

# 这里会考虑所有的组合,当然也可以考虑进行一波特征选择

label_feature = ['advert_id','advert_industry_inner','advert_name','campaign_id',

'creative_height', 'creative_tp_dnf', 'creative_width', 'province', 'f_channel', 'car

rier', 'creative_type', 'devtype', 'nnt', 'adid', 'app_id', 'app_cate_id', 'city', 'os', 'orderid', 'inner_slot_id', 'make', 'osv','os_name', 'creative_has_deeplink', 'creative_is_download', 'hour', 'creative_id', 'model']

mean:用户标签与其他字段的组合mean特征(如广告id对用户性别的投放比例)


nunique: 类别变量的nunique特征(如广告主id有多少个不同的广告id)

# 此处参考@有风的冬

## 广告

adid_nuq = ['model', 'make', 'os', 'city', 'province', 'user_tags', 'f_channel',

'app_id', 'carrier', 'nnt', 'devtype', 'app_cate_id', 'inner_slot_id']

for feat in adid_nuq:

gp1 = data.groupby('adid')[feat].nunique().reset_index().rename(columns=

{feat:"adid_%s_nuq_num" % feat})

gp2 = data.groupby(feat)['adid'].nunique().reset_index().rename(columns=

{'adid': "%s_adid_nuq_num" % feat})

data = pd.merge(data, gp1, how='left', on=['adid'])

data = pd.merge(data, gp2, how='left', on=[feat])

## 广告主

advert_id_nuq = ['model', 'make', 'os', 'city', 'province', 'user_tags', 'f_channel',

'app_id', 'carrier', 'nnt', 'devtype','app_cate_id', 'inner_slot_id']

for fea in advert_id_nuq:

gp1 = data.groupby('advert_id')[fea].nunique().reset_index().rename(columns=

{fea: "advert_id_%s_nuq_num" % fea})

gp2 = data.groupby(fea)['advert_id'].nunique().reset_index().rename(

columns={'advert_id': "%s_advert_id_nuq_num" % fea})

data = pd.merge(data, gp1, how='left', on=['advert_id'])

data = pd.merge(data, gp2, how='left', on=[fea])

## app_id

app_id_nuq = ['model', 'make', 'os', 'city', 'province', 'user_tags', 'f_channel',

'carrier', 'nnt', 'devtype','app_cate_id', 'inner_slot_id']

for fea in app_id_nuq:

gp1 = data.groupby('app_id')[fea].nunique().reset_index().rename(columns=

{fea: "app_id_%s_nuq_num" % fea})

gp2 = data.groupby(fea)['app_id'].nunique().reset_index().rename(columns=

{'app_id': "%s_app_id_nuq_num" % fea})

data = pd.merge(data, gp1, how='left', on=['app_id'])

data = pd.merge(data, gp2, how='left', on=[fea])

点击率:这里使用的历史点击率,来挖掘历史点击信息,同时防止过拟合

# 和当初baseline所用一样

# add ctr feature

data['period'] = data['day']

data['period'][data['period'] < 27] = data['period'][data['period'] < 27] + 31

for feat_1 in ['advert_id', 'advert_industry_inner', 'advert_name', 'campaign_id',

'creative_height','creative_tp_dnf', 'creative_width', 'province', 'f_channel']:

res = pd.DataFrame()

temp = data[[feat_1, 'period', 'click']]

for period in range(27, 35):

if period == 27:

count = temp.groupby([feat_1]).apply(

lambda x: x['click'][(x['period'] <= period).values].count()).reset_index(name=feat_1 + '_all')

count1 = temp.groupby([feat_1]).apply(

lambda x: x['click'][(x['period'] <= period).values].sum()).reset_index(name=feat_1 + '_1')

else:

count = temp.groupby([feat_1]).apply(

lambda x: x['click'][(x['period'] < period).values].count()).reset_index(name=feat_1 + '_all')

count1 = temp.groupby([feat_1]).apply(

lambda x: x['click'][(x['period'] < period).values].sum()).reset_index(name=feat_1 + '_1')

count[feat_1 + '_1'] = count1[feat_1 + '_1']

count.fillna(value=0, inplace=True)

count[feat_1 + '_rate'] = round(count[feat_1 + '_1'] / count[feat_1 + '_all'], 5)

count['period'] = period

count.drop([feat_1 + '_all', feat_1 + '_1'], axis=1, inplace=True)

count.fillna(value=0, inplace=True)

res = res.append(count, ignore_index=True)

print(feat_1, ' over')

data = pd.merge(data, res, how='left', on=[feat_1, 'period'])

可以看出这些都是常规操作,如果能够顺利的完成这些就能得到不错的分数

4.2 特征选择

这里我们主要用了卡方检验和特征重要性,由于三套代码,所有使用的方法并不相同。

user_tags特征我们分别用了卡方检验和特征重要性。

train_new = pd.DataFrame()

test_new = pd.DataFrame()

cntv = CountVectorizer()

cntv.fit(data['user_tags'])

train_a = cntv.transform(train['user_tags'])

test_a = cntv.transform(test['user_tags'])

train_new = sparse.hstack((train_new, train_a), 'csr', 'bool')

test_new = sparse.hstack((test_new, test_a), 'csr', 'bool')

# 卡方检验

SKB = SelectPercentile(chi2, percentile=95).fit(train_new, train_y)

train_new = SKB.transform(train_new)

test_new = SKB.transform(test_new)

5. 算法模型

GBDT模型记忆性更强,记忆特征和标签相关特征组合能力强,因此在小数据集上有很好的结果 。

FFM和DeepFFM初期尝试并未得到很好的效果

最终我们选择了XGBoost和LightGBM,得到的结果并做了最终的加权融合。


# 模型参数及五折构造结果

lgb_clf = lgb.LGBMClassifier(boosting_type='gbdt', num_leaves=48, max_depth=-1, learning_rate=0.02, n_estimators=6000, max_bin=425, subsam

ple_for_bin=50000, objective='binary', min_split_gain=0,min_child_weight=5, min_child_samples=10, subsample=0.8, subsample_freq=1,colsample_bytree=0.8,

reg_alpha=3, reg_lambda=0.1, seed=1000, n_jobs=-1, silent=True)

skf = list(StratifiedKFold(y_loc_train, n_folds=5, shuffle=True, random_state=1024))

baseloss = []

loss = 0

for i, (train_index, test_index) in enumerate(skf):

print("Fold", i)

lgb_model = lgb_clf.fit(X_loc_train[train_index], y_loc_train[train_index],

eval_names=['train', 'valid'],

eval_metric='logloss',

eval_set=[(X_loc_train[train_index], y_loc_train[train_index]),

(X_loc_train[test_index], y_loc_train[test_index])], early_stopping_rounds=100)

baseloss.append(lgb_model.best_score_['valid']['binary_logloss'])

loss += lgb_model.best_score_['valid']['binary_logloss']

test_pred = lgb_model.predict_proba(X_loc_test, num_iteration=lgb_mod

el.best_iteration_)[:, 1]

print('test mean:', test_pred.mean())

res['prob_%s' % str(i)] = test_pred

print('logloss:', baseloss, loss / 5)


6. 思考总结

  • 由于本次比赛数据中缺乏用户id这一关键信息,用户画像难以得到清晰地建立,因此如何充分挖掘用户标签中所包含的信息至关重要。

  • 即使是同样的业务场景,在不同的数据收集背景下,同样的特征完全可能会起到完全相反的效果,这也是一种数据陷阱。

  • 匿名化数据需要对数据进行充分理解分析,甚至可以尝试根据业务理解进行反编码,这样能够为特征工程指明方向。

  • 建模过程中充分考虑了用户标签与其他信息的交互作用,并采用Stacking抽取特征信息的方式减少维度与内存的使用,对广告与用户交互信息的充分挖掘,也使得模型在AB榜测试相对稳定。

  • 模型缺乏差异性和创新性,最开始尝试过deepffm,由于效果一般而没有坚持改进,大部分精力放在了数据理解与特征挖掘上。

写在最后

以上就是此次比赛主要内容,总的来看操作还是很常规的,如有疑问欢迎提出。

路漫漫其修远兮,吾将上下而求索。

温馨提示:点击阅读原文,即可关注作者知乎噢!

2018科大讯飞AI营销算法大赛总结(冠军)相关推荐

  1. 赛后复盘 - DataCastle 科大讯飞AI营销算法大赛

    最近这个月参加了DataCastle上的科大讯飞AI营销算法大赛,最后的名次是97 / 1086,没能进入复赛(要求前50名).其实也没什么好失落的,已经尽力了,这就是我现阶段的真实水平.最大的遗憾, ...

  2. 2018科大讯飞AI营销算法比赛笔记

    题目背景: 本次大赛提供了讯飞AI营销云的海量广告投放数据,参赛选手通过人工智能技术构建预测模型预估用户的广告点击概率,即给定广告点击相关的广告.媒体.用户.上下文内容等信息的条件下预测广告点击概率. ...

  3. 竞赛|数据竞赛Top解决方案开源整理-科大讯飞AI营销算法、阿里妈妈搜索广告、腾讯广告算法、搜狗的用户画像

    https://mp.weixin.qq.com/s/_4QG0dWhh784lF0n1wymcw

  4. 科大讯飞AI营销云广告投放数据分析报告【实战总结】

    4月份项目实战的总结,前后进行了约20多天. 花了大量时间在清洗数据,虽然耗时但是觉得过程中很享受,喜欢"静静地泡在里面"的感觉. 另外,了解了涉及到的广告投放.业务指标体系.手机 ...

  5. 科大讯飞AI营销大赛 CTR预估总结

    背景介绍: 科大讯飞AI营销云在高速发展的同时,积累了海量的广告数据和用户数据,如何有效利用这些数据去预测用户的广告点击概率,是大数据应用在精准营销中的关键问题,也是所有智能营销平台必须具备的核心技术 ...

  6. 决胜北交大深度学习算法大赛,冠军揭示通关攻略

    7月初,北京交通大学为了让上过<深度学习>课程的同学更好地实践深度学习技术应用,面向该校学生开展"深度学习暑期争霸赛".OneFlow作为合作方承办了这次比赛,提供了1 ...

  7. 天池-安泰杯跨境电商智能算法大赛(冠军)方案分享

    竞赛分享 天池-安泰杯跨境电商智能算法大赛 --冠军团队:法国南部 团队成员:Rain/Fish/楠枰 在19年9月下旬结束的"安泰杯"跨境电商智能算法大赛中,来自京东零售的法国南 ...

  8. 合肥科大讯飞-AI研究算法工程师(视觉感知) 招聘贴

    AI研究算法工程师(视觉感知)方向:机器人 岗位职责: 负责扫地机器人业务中的感知算法研究: 任职资格: 1.硕士及以上学历,计算机.信号处理.自动化.应用数学等相关专业.具备丰富数理统计,模式识别. ...

  9. 竞赛冠军方案:2020珠港澳人工智能算法大赛双料冠军解读

    本文首发于极市平台 团队介绍 团队来自深圳市威富视界有限公司.中国科学院半导体研究所,队长为宁欣副研究员,成员分别为石园.刘江宽.支金林.王镇.荣倩倩,排名不分先后. 珠港澳人工智能算法赛题介绍 以检 ...

  10. 2018年京东JDATA算法 大赛:如期而至-用户购买时间预测,方案分享

    前言 去年6月份和实习生一起参加京东jdata2018算法大赛,取得了17名的成绩,本来以为这个成绩对于第一次参加算法大赛的我们来说已经算是不错了,但在听了决赛答辩之后感觉今后的路还很长,需要学习的东 ...

最新文章

  1. 刻意练习:LeetCode实战 -- Task19. 相同的树
  2. POJ 3687 拓扑排序
  3. 1、Tensorflow 之 saver与checkpoint
  4. TensorFlow 2.0深度强化学习指南
  5. python调用接口获取文件_python接口文件使用说明
  6. 数据结构之插入排序:希尔排序(缩小增量排序)
  7. JavaScript学习笔记(五)--深拷贝浅拷贝
  8. 【ABAP系列】SAP ABAP 取两个内表的交集 比较两个内表的不同
  9. ai训练 样本大小_防止过拟合(三):数据增强(增加训练样本)
  10. [Java] 蓝桥杯ALGO-11 算法训练 瓷砖铺放
  11. python定义16进制数组,十六进制字符串到python中的字节数组
  12. 新华三杯考前突击---Day4---综合篇
  13. AD原理图库和PCB封装库下载
  14. 创建txt文本文档快捷键设置
  15. 邮递员算法问题之c++实现
  16. re库中group(), groups(), groupdict() 用法
  17. Python基础-名片管理
  18. CSS如何在宽高不确定的父元素内画一个正方形
  19. 手机如何与运营商服务器,通过这样的操作,让你的手机网速瞬间提升。
  20. TINA导入Ti官网器件

热门文章

  1. 转太强了!一文讲透了标准Web系统的架构分层~
  2. 谷歌真是厉害,这次成了公敌!
  3. 如何设计一个本地缓存,涨姿势了!
  4. 从今天起,我不再使用 Java Date了!
  5. 日请求从百万到八亿的技术历程
  6. Linux tcp同时多个连接,我的linux tcp server最多只能同时进行10个TCP请求,其余的都在等待了,如何让所有的连接都同时进行?...
  7. ActiveMQ下载与安装(Linux环境下进行)
  8. 51Nod 1274 - 最长递增路径(DP)
  9. ADB命令行工具使用
  10. HTML5微数据学习笔记