50万奖金+官方证书,深圳国际金融科技大赛正式启动,点击报名 了解详情
写点什么

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:002505
用户头像

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

关注

评论

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

视觉大模型训练和推理加速

Baidu AICLOUD

异构计算 AI加速 云原生AI 百度百舸

消息服务 + Serverless 函数计算如何助力企业降本提效?

阿里巴巴云原生

阿里云 云原生 消息队列

从开源安全看汽车安全新挑战

安势信息

开源安全 汽车安全 SBOM 开源漏洞 清源CleanSource SCA

如何看待PyTorch 2.0?

OneFlow

人工智能 深度学习 PyTorch

构建api gateway - 序与目录

八苦-瞿昙

Gateway API Gateway build api gateway

邮箱中的Qt线程设计

统信软件

邮箱 邮箱系统

天翼账号网关系统架构演进历程

Sean Chen

高并发 API网关 Kong 网关 天翼账号 网关演进

Intel 联合测试:YMatrix 并发查询 TPS 最高达 163 万

YMatrix 超融合数据库

intel 超融合数据库 HTAP 场景实践 YMatrix tpcb

14条日常维护LED显示屏的建议

Dylan

LED显示屏 全彩LED显示屏 led显示屏厂家

音视频开发进阶|第七讲:分辨率与帧率·上篇

ZEGO即构

Polaris 和 dubbogo 全面对接,让微服务更简单

阿里巴巴云原生

阿里云 云原生 dubbogo

什么是A/B实验,为什么要开A/B实验?

字节跳动数据平台

大数据 AB testing实战

大咖说·阿里云瑶池|电商业务与数据库技术发展的双向奔赴

大咖说

数据库 电商

使用QuTrunk+AWS ParallelCluster3进行并行计算

启科量子开发者官方号

量子计算

专注软件供应链安全,「安势信息」完成数千万元级别Pre-A轮融资

安势信息

开源安全 开源软件供应链 安势信息 Pre-A轮融资 开源软件供应链安全

鲸品堂:2023你好

鲸品堂

技术人

解密秒杀系统架构:不是所有的秒杀都是秒杀

华为云开发者联盟

后端 开发 华为云 企业号 1 月 PK 榜

开源共创,StarRocks 祝您 2023 年前 “兔” 无量!

StarRocks

数据库 开源

细数华为云云原生产品及五大开源实践

华为云开发者联盟

云计算 云原生 后端 华为云 企业号 1 月 PK 榜

2022 年,这 20+22 位共建者闪耀 StarRocks 社区

StarRocks

数据库 开源

阿里云专有云重磅亮相中国信通院2022混合云技术发展论坛

云布道师

阿里云 混合云

Spring Security怎么添加图片验证功能?

小小怪下士

Java spring spring security

袋鼠云产品功能更新报告03期丨产品体验全面优化,请查收!

袋鼠云数栈

大数据

专精特新小巨人VC/PE覆盖率24.2%;上市649家;北京以589家位居城市榜首丨创业邦发布《2022专精特新小巨人发展报告》

创业邦

如何使用 NFTScan 追踪 NFT 巨鲸

NFT Research

NFT 数据基础设施

Java常见异常:NPE空指针异常怎么解决?

千锋IT教育

《迷你世界》亿级玩家都在用的游戏场景推荐系统长啥样?

华为云开发者联盟

数据库 后端 华为云 企业号 1 月 PK 榜

性能测试技术笔记(一):如何快速上手压测工作

老张

性能测试

《SBOM: 提高软件供应链透明度的关键》重磅白皮书来袭

安势信息

SCA SBOM 清源CleanSource SCA 安势信息 开源软件供应链安全

“新范式”——2022创业邦100未来独角兽峰会暨创业邦年会圆满落幕

创业邦

技术贴 | SQL编译与执行-parser

KaiwuDB

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