10 月 23 - 25 日,QCon 上海站即将召开,现在购票,享9折优惠 了解详情
写点什么

Collective 的 Spark ML 经验分享:读者模型

  • 2015-11-19
  • 本文字数:3803 字

    阅读完需:约 12 分钟

【编者的话】 Collective 成立于 2005 年,其总部位于纽约,是一家从事数字广告业务的公司。 该公司的数字广告业务非常依赖于机器学习和预测模型,对于特定的用户在特定的时间应该投放什么样的广告完全是由实时或者离线的机器学习模型决定的。本文来自 Databricks 的技术博客,Eugene Zhulenev 分享了自己在 Collective 公司从事机器学习和读者模型工作的经验

Collective 公司有很多使用机器学习的项目,这些项目可以统称为读者模型,因为这些项目都是基于用户的浏览历史、行为数据等因素预测读者转化、点击率等信息的。在机器学习库的选择上,Collective 公司内部新开发的大部分项目都是基于 Spark 和 Spark MLLib 的,对于一些被大家广泛使用而 Spark 并不具备的工具和类库 Collective 还专门创建了一个扩展库 Spark Ext 。在本文中,Eugene Zhulenev 介绍了如何使用 Spark Ext 和 Spark ML 两个类库基于地理位置信息和浏览历史数据来预测用户转化。

预测数据

预测数据包含两种数据集,虽然这些数据都是使用虚拟的数据生成器生成的,但是它们与数字广告所使用的真实数据非常相似。这两类数据分别是:

重要通知:接下来 InfoQ 将会选择性地将部分优秀内容首发在微信公众号中,欢迎关注 InfoQ 微信公众号第一时间阅读精品内容。<>

用户的浏览历史日志

复制代码
Cookie | Site | Impressions
--------------- |-------------- | -------------
wKgQaV0lHZanDrp | live.com | 24
wKgQaV0lHZanDrp | pinterest.com | 21
rfTZLbQDwbu5mXV | wikipedia.org | 14
rfTZLbQDwbu5mXV | live.com | 1
rfTZLbQDwbu5mXV | amazon.com | 1
r1CSY234HTYdvE3 | youtube.com | 10

经纬度地理位置日志

复制代码
Cookie | Lat | Lng | Impressions
--------------- |---------| --------- | ------------
wKgQaV0lHZanDrp | 34.8454 | 77.009742 | 13
wKgQaV0lHZanDrp | 31.8657 | 114.66142 | 1
rfTZLbQDwbu5mXV | 41.1428 | 74.039600 | 20
rfTZLbQDwbu5mXV | 36.6151 | 119.22396 | 4
r1CSY234HTYdvE3 | 42.6732 | 73.454185 | 4
r1CSY234HTYdvE3 | 35.6317 | 120.55839 | 5
20ep6ddsVckCmFy | 42.3448 | 70.730607 | 21
20ep6ddsVckCmFy | 29.8979 | 117.51683 | 1

转换预测数据

正如上面所展示的,预测数据是长格式,对于每一个 cookie 与之相关的记录有多条,通常情况下,这种格式并不适合于机器学习算法,需要将其转换成“主键——特征向量”的形式。

Gather 转换程序
受到了 R 语音 tidyrreshape2包的启发,Collective 将每一个键对应的值的长数据框(long DataFrame)转换成一个宽数据框(wide DataFrame),如果某个键对应多个值就应用聚合函数。

复制代码
val gather = new Gather()
.setPrimaryKeyCols("cookie")
.setKeyCol("site")
.setValueCol("impressions")
.setValueAgg("sum") // 通过 key 对 impression 的值求和
.setOutputCol("sites")
val gatheredSites = gather.transform(siteLog)

转换后的结果

复制代码
Cookie | Sites
-----------------|----------------------------------------------
wKgQaV0lHZanDrp | [
| { site: live.com, impressions: 24.0 },
| { site: pinterest.com, impressions: 21.0 }
| ]
rfTZLbQDwbu5mXV | [
| { site: wikipedia.org, impressions: 14.0 },
| { site: live.com, impressions: 1.0 },
| { site: amazon.com, impressions: 1.0 }
| ]

Google S2 几何单元 Id 转换程序

Google S2 几何类库是一个球面几何类库,该库非常适合于操作球面(通常是地球)上的区域和索引地理数据,它会为地球上的每一个区域分配一个唯一的单元 Id。

为了将经纬度信息转换成键值对的形式,Eugene Zhulenev 结合使用了 S2 类库和 Gather,转换后数据的键值是 S2 的单元 Id。

复制代码
// Transform lat/lon into S2 Cell Id
val s2Transformer = new S2CellTransformer()
.setLevel(5)
.setCellCol("s2_cell")
// Gather S2 CellId log
val gatherS2Cells = new Gather()
.setPrimaryKeyCols("cookie")
.setKeyCol("s2_cell")
.setValueCol("impressions")
.setOutputCol("s2_cells")
val gatheredCells = gatherS2Cells.transform(s2Transformer.transform(geoDf))

转换后的结果

复制代码
Cookie | S2 Cells
-----------------|----------------------------------------------
wKgQaV0lHZanDrp | [
| { s2_cell: d5dgds, impressions: 5.0 },
| { s2_cell: b8dsgd, impressions: 1.0 }
| ]
rfTZLbQDwbu5mXV | [
| { s2_cell: d5dgds, impressions: 12.0 },
| { s2_cell: b8dsgd, impressions: 3.0 },
| { s2_cell: g7aeg3, impressions: 5.0 }
| ]

生成特征向量

虽然 Gather 程序将与某个 cookie 相关的所有信息都组织到了一行中,变成了键值对的形式,但是这种形式依然不能作为机器学习算法的输入。为了能够训练一个模型,预测数据需要表示成 double 类型的向量。

Gather 编码程序
使用虚拟变量对明确的键值对进行编码。

复制代码
// Encode S2 Cell data
val encodeS2Cells = new GatherEncoder()
.setInputCol("s2_cells")
.setOutputCol("s2_cells_f")
.setKeyCol("s2_cell")
.setValueCol("impressions")
.setCover(0.95) // dimensionality reduction

原始数据

复制代码
Cookie | S2 Cells
-----------------|----------------------------------------------
wKgQaV0lHZanDrp | [
| { s2_cell: d5dgds, impressions: 5.0 },
| { s2_cell: b8dsgd, impressions: 1.0 }
| ]
rfTZLbQDwbu5mXV | [
| { s2_cell: d5dgds, impressions: 12.0 },
| { s2_cell: g7aeg3, impressions: 5.0 }
| ]

转换后的结果

复制代码
Cookie | S2 Cells Features
-----------------|------------------------
wKgQaV0lHZanDrp | [ 5.0 , 1.0 , 0 ]
rfTZLbQDwbu5mXV | [ 12.0 , 0 , 5.0 ]

对于转换后的结果,用户还可以根据场景选择性地使用顶部转换进行降维。首先计算不同用户每个特征的值,然后根据特征值进行降序排序,最后从结果列表中选择最上面那些数值总和占所有用户总和的百分比超过某个阈值(例如,选择最上面覆盖 99% 用户的那些网站)的数据作为最终的分类值。

Spark ML 管道

Spark ML 管道是 Spark MLLib 的一个新的高层 API。一个真正的 ML 管道通常会包含数据预处理、特征提取、模型拟合和验证几个阶段。例如,文本文档的分类可能会涉及到文本分割与清理、特征提取、使用交叉验证训练分类模型这几步。在使用 Spark ML 时,用户能够将一个 ML 管道拆分成多个独立的阶段,然后可以在一个单独的管道中将他们组合到一起,最后使用交叉验证和参数网格运行该管道从而找到最佳参数集合。

使用 Spark ML 管道将它们组合到一起

复制代码
// Encode site data
val encodeSites = new GatherEncoder()
.setInputCol("sites")
.setOutputCol("sites_f")
.setKeyCol("site")
.setValueCol("impressions")
// Encode S2 Cell data
val encodeS2Cells = new GatherEncoder()
.setInputCol("s2_cells")
.setOutputCol("s2_cells_f")
.setKeyCol("s2_cell")
.setValueCol("impressions")
.setCover(0.95)
// Assemble feature vectors together
val assemble = new VectorAssembler()
.setInputCols(Array("sites_f", "s2_cells_f"))
.setOutputCol("features")
// Build logistic regression
val lr = new LogisticRegression()
.setFeaturesCol("features")
.setLabelCol("response")
.setProbabilityCol("probability")
// Define pipeline with 4 stages
val pipeline = new Pipeline()
.setStages(Array(encodeSites, encodeS2Cells, assemble, lr))
val evaluator = new BinaryClassificationEvaluator()
.setLabelCol(Response.response)
val crossValidator = new CrossValidator()
.setEstimator(pipeline)
.setEvaluator(evaluator)
val paramGrid = new ParamGridBuilder()
.addGrid(lr.elasticNetParam, Array(0.1, 0.5))
.build()
crossValidator.setEstimatorParamMaps(paramGrid)
crossValidator.setNumFolds(2)
println(s"Train model on train set")
val cvModel = crossValidator.fit(trainSet)

结论

Spark ML API 让机器学习变得更加容易。同时,用户还可以通过 Spark Ext 创建自定义的转换 / 估计,并对这些自定义的内容进行组装使其成为更大管道中的一部分,此外这些程序还能够很容易地在多个项目中共享和重用。如果想要查看本示例的代码,可以点击这里

编后语

《他山之石》是InfoQ 中文站新推出的一个专栏,精选来自国内外技术社区和个人博客上的技术文章,让更多的读者朋友受益,本栏目转载的内容都经过原作者授权。文章推荐可以发送邮件到editors@cn.infoq.com。


感谢郭蕾对本文的审校。

给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家通过新浪微博( @InfoQ @丁晓昀),微信(微信号: InfoQChina )关注我们,并与我们的编辑和其他读者朋友交流(欢迎加入 InfoQ 读者交流群(已满),InfoQ 读者交流群(#2))。

2015-11-19 18:002439
用户头像

发布了 321 篇内容, 共 130.3 次阅读, 收获喜欢 19 次。

关注

评论

发布
暂无评论
发现更多内容

昆仑分布式数据库独特的变量读写功能介绍

KunlunBase昆仑数据库

数据库 读写分离

在线TOML转JSON工具

入门小站

工具

网易X工行:云原生日志系统 Loggie 正式开源!

网易数帆

开源 云原生 日志 Filebeat Loggie

见证中国云势力崛起!博睿数据实力入围2021~2022 Cloud 100 榜单

博睿数据

如何使用一个有安全性问题的隐私计算技术?

易观分析

隐私计算

网络安全之文件包含漏洞总结

网络安全学海

黑客 网络安全 信息安全 渗透测试 WEB安全

DSTC10 赛道最佳论文揭晓!文心 PLATO 再获殊荣

百度开发者中心

多任务学习模型之DBMTL介绍与实现

阿里云大数据AI技术

机器学习 深度学习 数据模型 多任务学习

恒源云(GpuShare)_表序编码器的联合实体和关系提取(论文浅谈)

恒源云

自然语言处理 机器学习 深度学习

J2PaaS低代码平台,如何赋能开发者,助力企业数字化?

J2PaaS低代码平台

开发者 低代码 企业数字化 地代码平台 J2PaaS

阿里巴巴如何提升构建的效率 | 阿里巴巴DevOps实践指南

阿里云云效

阿里巴巴 阿里云 CI/CD 持续交付 构建工具

建设领先的AI原生云,百度智能云落地新一代高性能AI计算集群

百度开发者中心

如何为你的企业创建自助服务知识库

小炮

知识管理——企业数字化转型的新挑战

小炮

【技术学习】一次Node.js站点渗透

H

node.js 网络安全 渗透测试

以领先NLP技术构建AI数字疗法体系,聆心智能致力于解开更多“心结”

硬科技星球

Meetup预告|面向云原生的架构及演进

云智慧AIOps社区

开源 云原生 AIOPS 智能运维

吉利控股集团与百度深化战略合作

百度开发者中心

干货|性能提升密钥,由代码细节带来的极致体验

SphereEx

Apache 数据库 开源 ShardingSphere SphereEx

Hadoop HDFS 3.3.1分布式存储搭建

KunlunBase昆仑数据库

国产数据库

开发者说丨如何从零开始构建一个轻量级应用

华为云开发者联盟

Vue 低代码 页面 应用 AppCube

国内领先的云软件厂商安超云加入,为龙蜥社区注入新动能

OpenAnolis小助手

开源 操作系统 云厂商 国产

亿级月活沙盒平台《迷你世界》背后的黑科技

华为云数据库小助手

GaussDB DDM 华为云数据库

华为SmartCare和AUTIN品牌升级:助力运营商走出5G发展迷宫

脑极体

【OpenMLDB Monthly Meeting】2022.2 月会议纪要

第四范式开发者社区

人工智能 数据库 开源 Meetup 特征平台

培训第二弹!全国大学生智能汽车竞赛百度竞速组预告

百度开发者中心

RTC 音频质量评价和保障

网易云信

WebRTC

【Kali】中密码暴力破解工具hydra的使用

学神来啦

网络安全 字典 kali kali Linux

ARP欺骗攻击

喀拉峻

网络安全

星汉未来成为 FinOps 产业标准生态联盟首批会员

星汉未来

云原生 开源社区 成本优化 IT运维

2022年2月云主机性能评测报告

博睿数据

Collective的Spark ML经验分享:读者模型_语言 & 开发_孙镜涛_InfoQ精选文章