Storm技术内幕与大数据实践

978-7-115-38853-7
作者: 陈敏敏 黄奉线 王新春
译者:
编辑: 杨海玲

图书目录:

详情

本书先介绍实时大数据平台架构上的一些知识和难点,然后引入Storm来解决其中的问题。开始介绍Storm开发,再分享Storm集群中性能调优、资源隔离的一些知识和经验,然后加入Storm监控和日志的内容。后面介绍如何通过Storm构建公司的基础数据层;如何通过良好的架构设计实时更新基础数据层的用户画像、分布式索引等,最后依托实时更新的基础数据层,介绍如何构建各类个性化应用。

图书摘要

版权信息

书名:Storm技术内幕与大数据实践

ISBN:978-7-115-38853-7

本书由人民邮电出版社发行数字版。版权所有,侵权必究。

您购买的人民邮电出版社电子书仅供您个人使用,未经授权,不得以任何方式复制和传播本书内容。

我们愿意相信读者具有这样的良知和觉悟,与我们共同保护知识产权。

如果购买者有侵权行为,我们可能对该用户实施包括但不限于关闭该帐号等维权措施,并可能追究法律责任。


内容提要

本书内容主要围绕实时大数据系统的各个方面展开,从实时平台总体介绍到集群源码、运维监控、实时系统扩展、以用户画像为主的数据平台,最后到推荐、广告、搜索等具体的大数据应用。书中提到的不少问题是实际生产环境中因为数据量增长而遇到的一些真实问题,对即将或正在运用实时系统处理大数据问题的团队会有所帮助。

本书适合对大数据领域感兴趣的技术人员或者在校学生阅读,更适合大数据方向的架构师、运维工程师、算法/应用的开发者参考。


2011年5月,管理咨询公司麦肯锡的全球研究所发表的分析报告《大数据:下一个创新、竞争和生产率的前沿》提出了“大数据”的概念,并在业界掀起了广泛的热潮。而随着谷歌关于MapReduce和GFS设计的论文公布,以及开源世界对Hadoop的实现,对大量非结构化数据分布式处理的支持使得大数据已经成熟起来。

而在此之前,已经出现很多大数据使用的案例。

1998年,《哈佛商业评论》上面报道了一则啤酒与尿布的故事。沃尔玛超市分析人员对消费者的购物行为进行分析,发现年轻的父亲在购买尿布的同时会捎带点啤酒来犒劳自己,于是超市将这两个风牛马不相干的商品摆放在一起,结果两个商品的销量都大幅增加。

2009年,《自然》杂志上刊登了一则关于Google预测流感传播的文章。通过分析搜素引擎数据,得到之前所发生的流感的流行病学特征,发现分析出来的2004~2008年的流感数据和美国国家疾病控制中心实际收集的历年数据非常吻合,而更重要的是,分析这些历史数据的规律甚至还能够预测出未来流感传播的走势。

2012年,《时代》杂志刊登了一篇“Inside the Secret World of the Data Crunchers Who Helped Obama Win”的文章,奥巴马竞选美国总统其背后的数据分析团队逐渐为人所知。数据分析团队通过对不同地区、年龄段的选民进行多维度分析,在获取有效选民、投放广告、募集资金等方面提供了有效的参考,并在竞选结果上进行了精准的预测。

在大数据的批处理领域,Hadoop是不可撼动的王者,然而在实时性上的延迟,Hadoop却是其天生的不足,为完善大数据实时性处理的需求,业界进行了不少的尝试,如Facebook在2011年发表的论文“Apache Hadoop Goes Realtime at Facebook”中介绍了其基于Hadoop上进行实时性系统的相关改进,同时开发了Puma对网站用户进行实时分析以便对自己的产品或服务进行营销,为解决广告计费(cost-per-click)Yahoo启动了S4用于实时计算、预测用户对广告的可能的点击行为,LinkedIn则基于Kafka开发了Samza用于实时新闻推送、广告和复杂的监控等,而Storm是由Twitter开源的实时计算框架,适用于实时分析、在线机器学习、连续计算、分布式RPC和ETL等场景。

大数据技术的发展日新月异,不断涌现的技术代表着需求的旺盛。本书用深入浅出的方法系统介绍了Storm技术以及大数据的一些应用。在我们的环境中,Storm在网站信息、商家信息、用户画像与实时推荐等领域均取得了不错的效果。技术上的默默钻研使得1号店在实时计算方面具有自己的特色,并有力地支持了业务的发展。从2014年5月我们的第一个Storm应用——爬虫系统上线,到随后的短短四到五个月内,涌现出了 30 多个实时计算应用,大大提升了1号店的整个数据实时处理能力。

本书作者为Storm应用中的一线工程师,基于他们对大数据应用的理解以及在线上环境中遇到的一些问题分享了对应的优化措施,并就用户画像、实时推荐系统、广告和搜索等常见的大数据应用进行了介绍,将他们宝贵的大数据实践经验奉献给读者。他们走过的弯路希望读者不再走,他们的经验希望读者好好把握。

1号店会在实时计算领域继续投资,相信很多公司会认识到实时计算带来的价值。希望这本书给大家带来价值。

韩军,1号店CTO

2015年3月


本书意在介绍实时大数据的各个方面,分享我们在设计实时应用过程中遇到的一些问题,让一些从零开始构建实时计算平台的公司少走弯路。我们力图使不同背景的读者都能从其中获益。

如果你从事基础架构方面的工作,可以着重阅读以下几章:在第1章中,我们整理了国内主要互联网公司在Storm应用方面的一些情况;在第2章中,我们介绍了实时平台的总体架构,随后引入了大众点评和1号店目前实时平台的一些基本情况;在第4章中,我们给出了源码剖析,为了让不懂Clojure语言的读者也能容易地理解Storm的内部原理,我们配了很多顺序图来描述调用逻辑;在第5章中,我们分享了一些在实践中总结出来的监控Storm应用的常用方法;在第6章中,我们介绍了在Storm上如何做一些扩展,方便更好地维护和管理集群;在第10章中,我们主要分享了Storm的一些小技巧和性能优化的经验。

如果你是大数据产品的开发和架构人员,可以着重阅读后面的几章,其中分享了我们一年来遇到的一些瓶颈。

如果你是算法工程师,可以着重了解第8章和第9章,里面的用户生命周期模型、实时推荐系统的算法和架构、千人千面架构等不少内容来自于我们的生产实践。设计严谨的模型在实时系统上往往会遇到比较大的性能问题,数据量、实时和算法的精准性是相互制约的,提高某一方面,往往不得不牺牲另外两个指标。在实际推荐系统的生产环境中,关联规则和协同过滤的推荐效果往往比较好,被广泛采用,而利用用户画像,结合地域、天气等上下文信息,可以进行一些更加精准的推荐。目前基于用户画像和上下文内容做个性化推荐和搜索、精准化运营和广告营销等提高交易额等转换率,也是很多公司尝试的方向。

对于网上有的或者其他书中介绍过的内容,为适应不同读者的需求,我们会简单提及以做一点点过渡。

尽管我们投入了大量的精力来写这本书,但因为水平所限,书中的内容存在不足和疏漏也在所难免,恳请读者批评指正。如果读者对本书有什么建议,欢迎发送邮件至邮箱xiaochen_0260@qq.com,期待得到真挚的反馈。


Apache Storm(http://storm.apache.org/)是由Twitter开源的分布式实时计算系统。Storm可以非常容易并且可靠地处理无限的数据流。对比Hadoop的批处理,Storm是一个实时的、分布式的、具备高容错的计算系统。Storm应用可以使用何编程语言来进行开发,并且非常有趣。

Storm的使用场景非常广泛,比如实时分析、在线机器学习、分布式RPC、ETL等。Storm非常高效,在一个多节点集群上每秒钟可以轻松处理上百万条的消息。Storm还具有良好的可扩展性和容错性以及保证数据可以至少被处理一次等特性。

图1-1中水龙头和后面水管组成的拓扑图就是一个Storm应用(Topology),其中的水龙头是Spout,用来源源不断地读取消息并发送出去,水管的每一个转接口就是一个Bolt,通过Stream分组的策略转发消息流。

图1-1 Topology图(来源http://storm.apache.org/

Storm的集群表面上看和Hadoop的集群非常像。但是在Hadoop上运行的是MapReduce的作业(job),而在Storm上运行的是Topology。Storm和Hadoop一个非常关键的区别是Hadoop的MapReduce作业最终会结束,而Storm的Topology会一直运行(除非显式地杀掉它)。

如果说批处理的Hadoop需要一桶桶地搬走水,那么Storm就好比自来水水管,只要预先接好水管,然后打开水龙头,水就源源不断地流出来了,即消息就会被实时地处理。

在Storm的集群中有两种节点:主节点(Master Node)Nimbus和工作节点(Worker Node)Supervisor。Nimbus的作用类似于Hadoop中的JobTracker,Nimbus负责在集群中分发代码,分配工作给机器,并且监控状态。每个工作节点上运行一个Supervisor进程(类似于TaskTracker)。Supervisor会监听Nimbus分配给那台机器的工作,根据需要启动/关闭具体的Worker进程。每个Worker进程执行一个具体的Topology,Worker进程中的执行线程称为Executor,可以有一个或者多个。每个Executor中又可以包含一个或者多个Task。Task为Storm中最小的处理单元。一个运行的Topology由运行在一台或者多台工作节点上的Worker进程来完成具体的业务执行。Storm组件和Hadoop组件的对比参见表1-1。

表1-1 Storm组件和Hadoop组件对比

Storm Hadoop
角色 Nimbus JobTracker
Supervisor TaskTracker
Worker Child
应用名称 Topology Job
编程接口 Spout/Bolt Mapper/Reducer

Nimbus和Supervisor之间的通信依靠ZooKeeper完成,并且Nimbus进程和Supervisor都是快速失败(fail-fast)和无状态的,所有的状态要么在ZooKeeper里面,要么在本地磁盘上。这也就意味着你可以用kill -9来杀死Nimbus和Supervisor进程,然后再重启它们,它们可以继续工作,就好像什么都没有发生过似的。这个设计使得Storm具有非常高的稳定性。Storm的基本体系架构参见图1-2。

图1-2 Storm基本体系架构

在Storm中有一些核心基本概念,包括Topology、Nimbus、Supervisor、Worker、Executor、Task、Spout、Bolt、Tuple、Stream、Stream分组(grouping)等,如表1-2所示。

表1-2 Storm组件基本概念

组 件

概 念

Topology

一个实时计算应用程序逻辑上被封装在Topology对象中,类似Hadoop中的作业。与作业不同的是,Topology会一直运行直到显式地杀死它

Nimbus

负责资源分配和任务调度,类似Hadoop中的JobTracker

Supervisor

负责接受Nimbus分配的任务,启动和停止属于自己管理的Worker进程,类似Hadoop中的TaskTracker

Worker

运行具体处理组件逻辑的进程

Executor

Storm 0.8之后,Executor为Worker进程中的具体的物理线程,同一个Spout/Bolt的Task可能会共享一个物理线程,一个Executor中只能运行隶属于同一个Spout/Bolt的Task

Task

每一个Spout/Bolt具体要做的工作,也是各个节点之间进行分组的单位

Spout

在Topology中产生源数据流的组件。通常Spout获取数据源的数据(如Kafka、MQ等读取数据),然后调用nextTuple函数,发射数据供Bolt消费,参见图1-3

Bolt

在Topology中接受Spout的数据然后执行处理的组件。Bolt可以执行过滤、函数操作、合并、写数据库等任何操作。Bolt在接收到消息后会调用execute函数,用户可以在其中执行自己想要的操作,参见图1-4

Tuple

消息传递的基本单元

Stream

源源不断传递的Tuple组成了Stream

Stream分组

即消息的分区(partition)方法。Storm中提供若干种实用的分组方式,包括Shuffle、Fields、All、Global、None、Direct和Local or shuffle等

图1-3 Spout工作示意图

图1-4 Bolt工作示意图

在Storm中有7种内置的分组方式,也可以通过实现CustomStreamGrouping接口来定义自己的分组。

(1)Shuffle分组:Task中的数据随机分配,可以保证同一级Bolt上的每个Task处理的Tuple数量一致,如图1-5所示。

图1-5 Shuffle分组随机分配模式

(2)Fields分组:根据Tuple中的某一个Filed或者多个Filed的值来划分。比如Stream根据user-id的值来分组,具有相同user-id值的Tuple会被分发到相同的Task中,如图1-6所示。(具有不同user-id值的Tuple可能会被分发到其他Task中。比如user-id为1的Tuple都会分发给Task1,user-id为2的Tuple可能在Task1上也可能在Task2上,但是同时只能在一个Task上。)

(3)All分组:所有的Tuple都会到分发到所有的Task上,如图1-7所示。

图1-6 Fields分组哈希分布模式

图1-7 All分组全量发送模式

(4)Global分组:整个Stream会选择一个Task作为分发的目的地,通常是具有最新ID的Task,如图1-8所示。

图1-8 Global分组单选发送模式

(5)None分组:也就是你不关心如何在Task中做Stream的分发,目前等同于Shuffle分组。

(6)Direct分组:这是一种特殊的分组方式,也就是产生数据的Spout/Bolt自己明确决定这个Tuple被Bolt的哪些Task所消费。如果使用Direct分组,需要使用OutputCollector的emitDirect方法来实现。

(7)Local or shuffle分组:如果目标Bolt中的一个或者多个Task和当前产生数据的Task在同一个Worker进程中,那么就走内部的线程间通信,将Tuple直接发给在当前Worker进程中的目的Task。否则,同Shuffle分组。

Storm允许用户在Spout中发射一个新的Tuple时为其指定一个MessageId,这个MessageId可以是任意的Object对象。多个Stream Tuple可以共用同一个MessageId,表示这多个Stream Tuple对用户来说是同一个消息单元。Storm的可靠性是指Storm会告知用户每一个消息单元是否在一个指定的时间内被完全处理。完全处理的意思是该MessageId绑定的Stream Tuple以及由该Stream Tuple衍生的所有Tuple都经过了Topology中每一个应该到达的Bolt的处理。在Storm中,使用Acker来解决Tuple消息处理的可靠性问题。

总结起来,Storm具有如下优点。

由于Storm具有诸多优点,使用的业务领域和场景也越来越广泛。

Apache S4(http://incubator.apache.org/s4/)是由Yahoo开源的多用途、分布式的、可伸缩的、容错的、可插入式的实时数据流计算平台。

S4填补了复杂的专有系统和面向批处理的开源计算平台之间的差距。其目标是开发一个高性能计算平台,对应用程序开发者隐藏并行处理系统固有的复杂性。S4已经在Yahoo!系统中大规模使用,目前最新版本是0.6.0。

S4相对于Storm在可靠性和容错性上差一些,S4不保证完全不丢失数据。在用户活跃度上S4也要差一些。

Spark是UC Berkeley AMP Lab开源的类Hadoop MapReduce的通用的并行计算框架。Spark基于MapReduce算法实现的分布式计算拥有Hadoop MapReduce所具有的优点,但不同于MapReduce的是,作业中间输出结果可以保存在内存中,从而不再需要读写HDFS,因此Spark能更好地适用于数据挖掘与机器学习等需要迭代的MapReduce的算法。

Spark Streaming是建立在Spark上的实时计算框架,通过它提供的API和基于内存的高速执行引擎,用户可以结合流式、批处理和交互式进行查询和实时计算。Spark Streaming的基本的原理是将Stream数据分成小的时间片断(几秒钟到几分钟),以类似batch批量处理的方式来处理这些小部分数据。Spark Streaming构建在Spark上,一方面是因为Spark的低延迟执行引擎可以用于实时计算,另一方面相比基于Record的其他处理框架(如Storm),弹性分布式数据集(Resilient Distributed Datasets,RDD)更容易实现高效的容错处理。此外,小批量处理的方式使得它可以同时兼容批量和实时数据处理的逻辑和算法,方便了一些需要历史数据和实时数据联合分析的特定应用场合。

Spark Streaming和Storm两个框架都提供了可扩展性和容错性,它们根本的区别在于它们的处理模型。Storm处理的是每次传入的一个事件,而Spark Streaming是处理某个时间段窗口内的事件流。因此,Storm处理一个事件可以达到极低的延迟,而Spark Streaming的延迟相对较高。

大数据的价值在各行各业中得到了广泛使用。针对离线处理,Hadoop已经成为事实上的标准;针对数据实时处理的需求,目前涌现出了许多平台和解决方案。以下汇总了截至2014年流计算和Storm的使用情况。

1.新浪的实时分析平台

新浪实时分析平台的计算引擎是Storm,整个实时计算平台包括可视化的任务提交Portal界面、对实时计算任务的管理监控平台以及核心处理实时计算平台。

Storm作为核心处理,待处理数据来源为Kafka。对于实时性要求比较高的应用、数据会直接发送到Kafka,然后由Storm中的应用进行实时分析处理;而对实时性要求不太高的应用,则由Scribe收集数据,然后转发到Kafka中,再由Storm进行处理。

任务提交到Portal之前,作业的提交者需要确定数据源、数据的每个处理逻辑,同时确定处理完成后数据的存储、获取和展示方式。在任务提交后,可以完成对任务的管理:编辑、停止、暂停和恢复等。

整个核心处理平台提供了一些通用的模块,如数据的解析(不同的应用有不同的数据格式,可以是简单的分隔符分隔和正则表达式)、对特定字段的TopN计数以及数据的过滤和去重,数据处理过程中使用到了缓存Redis,支持多种存储方式(数据处理完成后可选择的持久化方式有HBase、HDFS、本地文件和MySQL等)。

在应用上,实时分析平台的应用包括HTTP日志分析、PV计算等。

在监控上,通过Storm的Nimbus节点,获取集群的运行数据,结合JMX收集到进程状态信息,将数据发送到统一的监控工具中(如Ganglia)。

2.腾讯的实时计算平台

腾讯的实时计算平台Tencent Real-time Computing主要由两部分组成:分布式K/V存储引擎TDEngine和支持数据流计算的TDProcess。TDProcess是基于Storm的计算引擎,提供了通用的计算模型,如Sum、Count、PV/UV计算和TopK统计等。整个平台修复了运行中发现的Storm的问题,同时引入YARN进行资源管理。

据称,整个计算平台每天承载了超过1000亿数据量的计算,支持广点通、微信、视频、易迅、秒级监控、电商和互娱等业务上百个实时统计的需求。

3.奇虎360实时平台

奇虎360从2012年开始引入Storm,Storm主要应用场景包括云盘缩略图、日志实时分析、搜索热词推荐、在线验证码识别、实时网络入侵检测等包括网页、图片、安全等应用。在部署中,使用了CGroup进行资源隔离,并向Storm提交了很多补丁,如log UI(https://github.com/nathanmarz/storm/pull/598)等。在部署上,Storm集群复用了其他机器的空闲资源(Storm部署在其他服务的服务器上,每台机器贡献1~2核处理器、1~2 GB内存),整个规模达到60多个集群,15 000多台物理机,服务于170多个业务。每天处理数据量约几百TB、几百亿条记录。

4.京东的实时平台

京东的实时平台基于LinkedIn开源的Samza,整个Samza包括流处理层Kafka,执行层YARN和处理层Samza API。一个流式处理由一个或多个作业组成,作业之间的信息交互借助Kafka实现,一个作业在运行状态表现为一个或者多个Task,整个处理过程实际上是在Task中完成的。在Samza中,Kafka主要的角色是消息的缓冲、作业交互信息的存储,同一个业务流程中使用YARN进行任务调度。在其整个架构中,引入了Redis作为数据处理结果的存储,并通过Comet技术将实时分析的数据推送到前台展示,整个业务主要应用于京东大家电的订单处理,实时分析统计出待定区域中各个状态的订单量(包括待定位、待派工、待拣货、待发货、待配送、待妥投等)。

5.百度的实时系统

相对而言,百度在实时系统上开展的比较早,在其流计算平台DStream开发时业界尚未有类似的开源系统。截至2014年,从公开的资料可以发现,DStream平台的集群规模已超千台,单集群最大处理数据量超过50 TB/天,集群峰值QPS 193W/S,系统稳定性、计算能力已完全满足海量数据时效性处理需求。另一个平台TM则保证数据不重不丢,主要用于报表生成系统、计费流计算等。

6.阿里巴巴团队的JStorm

JStorm(https://github.com/alibaba/jstorm)是阿里巴巴团队基于Storm二次开发的,Spout/Bolt等接口的使用方式和Storm保持完全一致,在Storm上开发和运行的代码可以一行不修改就运行在JStorm上。Storm的内核是Clojure编写,JStorm完全用Java重写。JStorm还提供了一些Storm没有的特性。

JStrom的开发和更新速度非常快,用户活跃度也很高。更多详细信息可以参考GitHub的介绍。


本章中主要介绍了具体的大数据的应用,首先通过例子讲解了如何开发一个计算实时UV(Unique Visitor,是指不同的、通过互联网访问、浏览的自然人)数的程序,随后引入推荐、广告、搜索等常用的大数据应用场景。

在实际推荐系统的生产环境中,关联规则和协同过滤的推荐效果往往比较好,但是利用用户画像,结合时间、天气等上下文信息,可以进行一些更加精准化的推荐,因此基于画像的内容和上下文推荐也是很多公司不可或缺的一部分。大数据发展,离不开互联网广告的蓬勃发展,广告系统中也存储着大量的用户信息,这些用户信息往往存储在DMP(数据管理平台)中,通过点击率预测等计算可以实现更加精准化的广告投放,用户画像也是很多DMP的组成部分。移动互联网时代,移动设备的屏幕相对比较小,一屏中展示的物品有限,如果知道用户的实时意图和实现个性化搜索,可以缩短用户寻找物品的时间,改善产品的用户体验,实现这些功能的数据支撑同样来自于每一个用户信息。

DAU是每天访问的UV数,00:00~24:00内相同的客户端只被计算一次。UV是非常核心的一个指标,通过对每个时间点的DAU数据的分析,可以查看运营活动的效率以及当前网站运行的整体情况等,可以对系统优化和运营效率提升等起到很好的促进作用。目前大众点评各个平台的累积DAU达到千万级,PV到亿级。本节以DAU为例,简要介绍一下大众点评是怎么使用实时平台的。实时DAU计算包括了大众点评的所有不同平台移动客户端(大众点评APP、大众点评团APP和周边快查APP)、PC端和M站。

移动端实时DAU的Topology计算逻辑的DAG参见图9-1。

图9-1 DAU计算逻辑有向图

在Storm UI上Topology的运行情况如图9-2所示。

图9-2 Topology运行状态

各个组件的功能描述如下。

(1)BlackholeBlockingQueueSpout:作为Blackhole的Consumer获取Mobile的日志数据。源码可以参考https://github.com/xinchun-wang/storm-util

(2)MobileLogParserBolt:解析Mobile的日志,输出后续计算所需要的数据,具体包含trainId(不同平台的trainId不一样。例如,Android为7,iOS为10)、deviceId是设备ID(deviceId可能是IMEI、UUID、MAC、UDID、IDFA、OPENUDID中的一个或多个,根据不同的操作系统和才做系统版本来确定)、addtime表示日志达到时间、source表示APP是从哪个AppStore安装的、userId为登录后的大众点评的用户ID。Spout的数据Shuffle到MobileLogParserBolt上,保证每个日志的Parser节点分到的数据基本上相同。

@Override
public void declareOutputFields(OutputFieldsDeclarer declarer) {
 declarer.declare(new Fields("trainId","deviceId", "addtime", "source", "userId"));
}

(3)DPIDBolt:根据deviceIdtrainId从HBase获取对应的DPID。DPID是大众点评对每个安装了点评APP的设备所标识的唯一ID,只要设备不变,DPID就是同一个。deviceId和DPID存在映射关系,目的是当deviceId切换(随着OS对安全性的策略改变,设备可以获取到的ID会发生变化,如从IMEI变成UUID)的时候,我们还可以正确标识这个设备。在HBase中,维护了DPID到deviceId的多对多的映射关系。MobileLogParserBolt输出的数据根据trainIddeviceIdfieldsGrouping输出到DPIDBolt中,相同的trainIddeviceId可以到同一个Bolt。这样DPIDBolt可以在内部缓存,减少HBase访问的次数。DPIDBolt的输出为:

@Override
public void declareOutputFields(OutputFieldsDeclarer outputfieldsdeclarer) {
  outputfieldsdeclarer.declare(new Fields("trainId", "dpid", "isNew", " addtime", "source"));
}

DPIDBolt中,同时会实时更新DPIDuserId的映射关系。如果这个DPID是今天新产生的,那么isNewtrue,表示是个新用户。

(4)PartialUVBolt:在PartialUVBolt中,主要是统计不同设备的DPID出现次数(UV),如果今天这个DPID之前没有访问过,次数加1,否则不计算。为了保证数据不出错,数据会存储到Redis中。当某个Bolt出现错误的时候,数据不会丢失。每隔10秒会将计数值输出。

@Override
public void declareOutputFields(OutputFieldsDeclareroutputFieldsDeclarer) {
  outputFieldsDeclarer.declare(new Fields("type", "date", "dau", "newUV", "source"));
}

type等同于前面的trainIddate表示当前是那个计算周期,dau就是当前计算周期的UV值,newUV是当前计算周期的新UV值,source是当前计算周期来源的渠道。DPIDBoltPartialUVBolt之间是fieldsGrouping,也就是相同的trainIdDPID发送到同一个PartialUVBolt中。PartialUVBolt的数据是每隔一定周期发射出去,具体的周期是依靠Tick Tuple消息来完成。重载在Bolt的getComponentConfiguration()方法:

@Override
public Map getComponentConfiguration(){
  Map<String, Object>conf = new HashMap<String, Object>();
  conf.put(Config.TOPOLOGY_TICK_TUPLE_FREQ_SECS, Constants.EMIT_FREQUENCY_IN_SECONDS);
  returnconf;
}

这样PartialUVBolt就可以以Constants.EMIT_FREQUENCY_IN_SECONDS的频率收到Tick Tuple消息,然后在Bolt的execute方法中,判断是Tick Tuple,就发射数据出去。判断的方法为:

public static booleanisTickTuple(Tuple tuple) {
  returntuple.getSourceComponent().equals(Constants.SYSTEM_COMPONENT_ID) && tuple.getSourceStreamId().equals(Constants.SYSTEM_TICK_STREAM_ID);
}

(5)AggregatorUVBoltAggregatorUVBolt完成的是将PartialUVBolt的数据聚合起来,根据不同的type计算当前周期的数据汇总,汇总完毕的数据发射到PersistenceUVBolt中。

(6)PersistenceUVBoltPersistenceUVBolt就是将数据写入MySQL中,然后由RPC服务提供给不同的使用者,包括Dashboard、微信公众号和大众点评的内部APP等,用来展示或者报警等。

整个Topology的构建参考下面的代码逻辑:

public class MobileUVTopology {
private static final int TOPOLOGY_NAME_INDEX = 0;
  private static final String BLOCKHOLE_TOPIC = "dpods_log_mobile-log-web_MAIN"; private static final String MOBILE_WEB_MAIN_SPOUT_ID = "MobileWebMainSpout"; private static final String LOG_PARSER_ID = "LogParser"; private static final String DPID_ID = "DPID";
public static void main(String[] args) throws Exception {
  TopologyBuilder builder = new TopologyBuilder();   String TopologyName = getTopologyName(args);   builder.setSpout(MOBILE_WEB_MAIN_SPOUT_ID ,     newBlackholeBlockingQueueSpout(BLOCKHOLE_TOPIC, getTopologyName(args)),       CommonUtil.getParallelism(args, 1));   builder.setBolt(LOG_PARSER_ID, new MobileLogParserBolt(),     20).shuffleGrouping(MOBILE_WEB_MAIN_SPOUT_ID, BLOCKHOLE_TOPIC);   builder.setBolt(DPID_ID, new DPIDBolt(),     24).fieldsGrouping(LOG_PARSER_ID, new Fields("trainId","deviceId"));   builder.setBolt(Constants.PARTIAL_UV_ID, new PartialUVBolt("APP"),     16).fieldsGrouping(DPID_ID, new Fields("trainId","dpid"));   builder.setBolt(Constants.AGGREGATOR_UV_ID, new AggregatorUVBolt(),     1).noneGrouping(Constants.PARTIAL_UV_ID);   builder.setBolt(Constants.PERSISTENCE_UV_ID, new PersistenceUVBolt("APP",     TopologyName), 2).shuffleGrouping(Constants.AGGREGATOR_UV_ID);
  Configconf = new Config();
  if (args != null &&args.length> 0) {     conf.setNumWorkers(8);     conf.registerMetricsConsumer(       backtype.storm.metric.LoggingMetricsConsumer.class, 1);     conf.registerMetricsConsumer(       com.dianping.cosmos.metric.CatMetricsConsumer.class, 1);     StormSubmitter.submitTopology(args[0], conf,builder.createTopology());     } else {       LocalCluster cluster = new LocalCluster();       cluster.submitTopology("MobileUV", conf, builder.createTopology());     }   }
  private static String getTopologyName(String[] args) {     try {      returnargs[TOPOLOGY_NAME_INDEX];     } catch (Exception e) {      return "MovileUV";     }   } }

网页实时DAU结果的部分Dashboard如图9-3所示。

从图上可以看出,10点半左右有个高峰,通常是某个运营活动(如抽奖、抢红包等)产生的,从该图可以直接看出运营的效果。

从日环比和周同比(如图9-4所示)可以看出今天的用户访问情况是增加还是减少了,如果发生明显增加或者减少就可以及时分析问题,采取应对的策略。

图9-3 每5分钟的新DAU

图9-4 Android的DAU的日环比和周同比

自从1992年施乐的科学家为了解决信息负载的问题,第一次提出协同过滤算法,个性化推荐已经经过了二十几年的发展。1998年,林登和他的同事申请了“item-to-item”协同过滤技术的专利,经过多年的实践,亚马逊宣称销售的推荐占比可以占到整个销售GMV(Gross Merchandise Volume,即年度成交总额)的30%以上。随后Netflix举办的推荐算法优化竞赛,吸引了数万个团队参与角逐,期间有上百种的算法进行融合尝试,加快了推荐系统的发展,其中SVD(Sigular Value Decomposition,即奇异值分解,一种正交矩阵分解法)和Gavin Potter跨界的引入心理学的方法进行建模,在诸多算法中脱颖而出。其中,矩阵分解的核心是将一个非常稀疏的用户评分矩阵R分解为两个矩阵:User特性的矩阵P和Item特性的矩阵Q,用P和Q相乘的结果R'来拟合原来的评分矩阵R,使得矩阵R'在R的非零元素那些位置上的值尽量接近R中的元素,通过定义R和R'之间的距离,把矩阵分解转化成梯度下降等求解的局部最优解问题。Netflix最新的实时推荐系统如图9-5所示。

图9-5 NetFlix的实时推荐系统系统架构图(来源:http://techblog.netflix.com/2013/03/system-architectures-for.html

与此同时,Pandora、LinkedIn、Hulu、Last.fm等一些网站在个性化推荐领域都展开了不同程度的尝试,使得推荐系统在垂直领域有了不少突破性进展,但是在全品类的电商、综合的广告营销上,进展还是缓慢,仍然有很多的工作需要探索。特别是在全品类的电商中,单个模型在母婴品类的效果还比较好,但在其他品类就可能很差,很多时候需要根据品类、推荐栏位、场景等不同,设计不同的模型。同时由于用户、SKU不停地增加,需要定期对数据进行重新分析,对模型进行更新,但是定期对模型进行更新,无法保证推荐的实时性,一段时间后,由于模型训练也要相当时间,可能传统的批处理的Hadoop的方法,无法再缩短更新频率,最终推荐效果会因为实时性问题达到一个瓶颈。

推荐算法主要有基于人口统计学的推荐、基于内容的推荐、基于协同过滤的推荐等,而协同过滤算法又有基于邻域的方法(又称基于记忆的方法)、隐语义模型、基于图的随机游走算法等。基于内容的推荐解决了商品的冷启动问题,但是解决不了用户的冷启动问题,并且存在过拟合问题(往往在训练集上有比较好的表现,但在实际预测中效果大打折扣),对领域知识要求也比较高,通用性和移植性比较差,换一个产品形态,往往需要重新构建一套,对于多媒体文件信息特征提取难度又比较大,往往只能通过人工标准信息。基于邻域的协同过滤算法,虽然也有冷启动问题和数据稀疏性等问题,但是没有领域知识要求,算法通用性好,增加推荐的新颖性,并且对行为丰富的商品,推荐准确度较高。基于模型的协同过滤算法在一定程度上解决了基于邻域的推荐算法面临的一些问题,在RMSE(Root Mean Squared Error,即均方根误差)等推荐评价指标上更优,但是通常算法复杂,计算开销大,所以目前基于邻域的协同过滤算法仍然是最为流行的推荐算法。

基于邻域的协同过滤主要分为User CF和Item CF,根据以下条件不同,各自又有不同的使用场景。

对于用户数量远远大于产品,并且产品相对稳定的电商系统,计算产品相似度计算量小,适用Item CF,否则用户量大,并且如果用户购买频繁,计算用户相似度计算量很大,极端情况下,100个用户对应2个产品,一个要计算C1002次相似度,一个只要计算C22,即一次相似度;反之,对于更新频繁,物品数量海量的新闻、博客、微博等系统,User CF效果更好。

当然,虽然SVD在分解矩阵上花费了一定时间,同时降维也会导致用户-项目矩阵中的信息丢失,但是用户-项目矩阵降维后, 运算复杂度大大降低,同时矩阵稀疏性问题得到了较好地解决,作为Netflix比赛中最终提升效果较好的两个方法之一,被众多网站采用。用户-项目矩阵中的信息丢失问题可以通过选取合适的保留维数k在一定程度上得到缓解。

在一个电商系统中,有商品、类目、品牌、团购、闪购、搜索、店铺、广告、促销活动、抵用券等诸多实体;有首页的大轮播、猜你喜欢栏位,详情页的看了还看、看了还买、推荐品牌等栏位,购物车页面的买了还买、凑单免邮等栏位。如何在不同的栏位融入不同的推荐算法给用户推荐相应的实体,构建出属于电商自己的场景引擎,实现全站精准化,让网站的GMV或者利润达到最高,是每一个电商需要思考的问题。在实际中,很多推荐算法不一定要求实时,实时推荐在哪些场景下能带给栏位更高的GMV转化率,也是需要一定时间摸索和试错的。

目前基于用户画像的推荐,主要用在基于内容的推荐,从最近的RecSys大会(ACM Recommender Systems)上来看,不少公司和研究者也在尝试基于用户画像做Context-Aware的推荐(情境感知,又称上下文感知)。利用用户的画像,结合时间、天气等上下文信息,给用户做一些更加精准化的推荐是一个不错的方向。

目前的商用推荐系统,当用户数和商品数达到一定数目时,推荐算法都面临严重的可扩展性问题,推荐的实效性变得非常差,如何在算法和架构上提高推荐速度是很多公司不得不思考的问题。目前,在算法上主要通过引入聚类技术和改进实时协同过滤算法提高推荐速度;在架构上,目前实时推荐主要有基于Spark、Kiji框架和Storm的流式计算3种方法。

1.聚类技术和实时协同过滤算法

在算法上,一般采用EM(Expectation-Maximization)、K-means、吉布斯(Gibbs Sampling)、模糊聚类等聚类技术提高推荐速度。因为使用聚类技术可以大大缩小用户或项目的最近邻居搜索范围,从而提高推荐的实时性,如表9-1所示。

表9-1 聚类技术比较

算 法

概  念

缺  点

EM

最大期望算法,估计用户或项目属于某一类的概率

每个用户或项目属于两个不同的用户分类或项目分类,EM算法就不再适用

K-means

主要思想是以空间中k个点为中心进行聚类,对最靠近它们的对象归类。通过迭代的方法,逐次更新各聚类中心的值,直至得到最好的聚类结果

聚类数目k需要事先给定而且不同的应用中k值是不同的,难于选取。 另外初始聚类中心是随机选取的,对于同一组数据,可能因为初始聚类中心的不同而产生不同的聚类结果。 有些类型的数据,比如说全是1和0组成的一个二进制数组,如果要对这种二进制数组进行聚类,K-means不适合,因为如果采用欧式距离,很难定义和计算它们的聚类中心点,这时可以采用Jaccard相似度和ROCK等层次聚类算法

吉布斯采样

与 EM 算法类似,不同的是吉布斯采样方法基于贝叶斯模型,计算可以离线进行

算法复杂度较大,聚类过程比较耗时

模糊聚类

利用模糊等价关系将给定的对象分为一些等价类,并由此得到与关系对应的模糊相似矩阵,该模糊相似矩阵满足传递性.根据相似矩阵求出其传递关系的闭包,然后在传递关系的闭包上实现分类,计算可以离线进行

可能性划分的收敛速度慢,当数据离散程度大,即数据灰度大,预测精度越差,需要对历史数据的平滑处理

除此之外,实时协同过滤算法本身一直是人们研究的热点,早在2003年,Edward F. Harrington就第一次提出了基于感知器的实时协同过滤算法,但是这种方法需要所有用户的偏好,实用性较差;2010年,杨强等提出了实时进化的协同过滤算法,给予新得分更高的权重来增量更新User和Item的相似度;2011年,UC Berkeley的Jacob Abernethy等人提出了OCF-SGD算法,我们知道传统的矩阵分解把用户评分矩阵R分解成多个矩阵,比如R≈P*Q,该方法提出当新来一个User到Item的得分,把更新R矩阵的问题转换成更新P和Q矩阵,从而达到实时协同过滤;近几年的RecSys大会上,实时协同过滤也是讨论的热点,OCF-SGD算法每次只考虑一个用户,忽略了用户之间的关系,Jialei Wang等人提出了基于多任务学习的实时协同过滤算法,把每一个用户当做一个任务,定义一个表示各个任务间相似性和交互程度的矩阵A,当新来一个User到Item的得分,通过矩阵A来更新其他用户的得分。

2.基于Spark的方式

在架构上,第一种是使用Spark把模型计算放在内存中,加快模型计算速度,Hadoop中作业的中间输出结果是放到硬盘的HDFS中,而Spark是直接保存在内存中,因此Spark能更好地适用于数据挖掘与机器学习等需要迭代的模型计算,如表9-2所示。

表9-2 MapReduce和Spark的Shuffle过程对比

 

MapReduce

Spark

collect

在内存中构造了一块数据结构用于Map输出的缓冲

没有在内存中构造一块数据结构用于Map输出的缓冲,而是直接把输出写到磁盘文件

sort

Map输出的数据有排序

Map输出的数据没有排序

merge

对磁盘上的多个spill文件最后进行合并成一个输出文件

在Map端没有merge过程,在输出时直接是对应一个Reduce的数据写到一个文件中,这些文件同时存在并发写,最后不需要合并成一个

copy框架

Jetty

Netty或者直接socket流

对于本节点上的文件

仍然是通过网络框架拖取数据

不通过网络框架,对于在本节点上的Map输出文件采用本地读取的方式

copy过来的数据存放位置

先放在内存,内存放不下时写到磁盘

一种方式全部放在内存;另一种方式先放在内存

merge sort

最后会对磁盘文件和内存中的数据进行合并排序

对于采用另一种方式时也会有合并排序的过程

(来源:http://www.csdn.net/article/2014-05-19/2819831-TDW-Shuffle/2

3.基于Kiji框架的方式

第二种是使用Kiji,它是一个用来构建大数据应用和实时推荐系统的开源框架,本质上是对HBase上层的一个封装,用Avro来承载对象化的数据,使得用户能更容易地用HBase管理结构化的数据,使得用户姓名、地址等基础信息和点击、购买等动态信息都能存储到一行,在传统数据库中,往往需要建立多张表,在计算的时候要关联多张表,影响实时性。Kiji与HBase的映射关系如表9-3所示。

表9-3 Kiji到HBase的映射关系

Kiji HBase
Entity相关 Entity 相同键的值都属于同一行
EntityID 行键(row key)
Column相关 locality:family:key Family:qualifier
locality Family
Family:key Qualifier
Schema相关 Table Layout HBase上的KijiMetaTable,如kiji.default.meta
Cell Schema Avro Schema
Cell Schema mapping HBase上的Schema Table,如kiji.default.schema_hash、keji.default.schema_id

Kiji提供了一个KijiScoring模块,它可以定义数据的过期策略,如综合产品点击次数和上次的点击时间,设置数据的过期策略把数据刷新到KijiScoring服务器中,并且根据自己定义的规则,决定是否需要重新计算得分。如用户有上千万浏览记录,一次的行为不会影响多少总体得分,不需要重新计算,但如果用户仅有几次浏览记录,一次的行为,可能就要重新训练模型。Kiji也提供了一个Kiji模型库,使得改进的模型部署到生产环境时不用停掉应用程序,让开发者可以轻松更新其底层的模型。

4.基于Storm的方式

最后一种基于 Storm 的实时推荐系统。在实时推荐上,算法本身不能设计的太复杂,并且很多网站的数据库是TB、PB级别,实时读写大表比较耗时。可以把算法分成离线部分和实时部分,利用Hadoop离线任务尽量把查询数据库比较多的、可以预先计算的模型先训练好,或者把计算的中间数据先计算好,比如,线性分类器的参数、聚类算法的群集位置或者协同过滤中条目的相似性矩阵,然后把少量更新的计算留给Storm实时计算,一般是具体的评分阶段。

基于本章前面的学习,我们可以设计图9-6所示的实时推荐系统。

图9-6 实时推荐系统(图片来源PRANAB GHOSH,Big Data Cloud meetup。版权归原书作者所有)

用HBase或HDFS存储历史的浏览、购买行为信息,用Hadoop基于User CF的协同过滤,先把用户的相似度离线生成好,用户到商品的矩阵往往比较大,运算比较耗时,把耗时的运行先离线计算好,实时调用离线的结果进行轻量级的计算有助于提高产品的实时性。

我们来简单回顾一下协同过滤算法(如图9-7所示):首先程序获取用户和产品的历史偏好,得到用户到产品的偏好矩阵,利用Jaccard相似系数(Jaccard coefficient)、向量空间余弦相似度(Cosine similarity)、皮尔逊相关系数(Pearson correlation coefficient)等相似度计算方法,得到相邻的用户(User CF)或相似商品(Item CF)。在User CF中,基于用户历史偏好的相似度得到邻居用户,将邻居用户偏好的产品推荐给该用户;在Item CF中,基于用户对物品的偏好向量得到相似产品,然后把这款产品推荐给喜欢相似产品的其他用户。

图9-7 协同过滤算法过程

然后通过Kafka或者Redis队列,保存前端的最新浏览等事件流,在Storm的Topology中实时读取里面的信息,同时获取缓存中用户topN个邻居用户,把邻居用户喜欢的商品存到缓存中,前端从缓存中取出商品,根据一定的策略,组装成推荐商品列表。

当然除了相似性矩阵,其他模型大体实现也相似,比如实际的全品类电商中不同的品类和栏位,往往要求不同的推荐算法,如母婴产品,如图9-8所示,如果结合商品之间的序列模式和母婴年龄段的序列模式,效果会比较好,可以把模型通过Hadoop预先生成好,然后通过Storm实时计算来预测用户会买哪些产品。

图9-8 序列模式在母婴类目推荐中的应用

广告投放的精准化其实和个性化推荐本质是一样,都是在合适的时间、地点以及场景下,把用户最需要的信息推荐给他。不算黑暗互联网,广告、秀场/游戏和电商被认为是互联网的三大变现模式(也有说,移动互联网多了增值服务第四大变现模式),其中广告对收入的驱动力最直接,商业驱动技术的发展,造就了精准化营销发展的相对比较好;而推荐对电商的收入贡献是间接的(要知道现在电商的盈利模式不是通过差价,目前主要的盈利模式还是基于阿里巴巴的租金和渠道的流量模式,以及唯品会的清理尾货的模式),对于比较标准化的产品,互联网的信息对称让价格变得透明,通过差价来盈利目前还是比较困难的,除非有通过政策或者合同壁垒,只能独家销售的产品。广告投放区别于推荐和搜索主要见表9-4。

表9-4 搜索、广告和推荐的比较

搜  索 搜索广告 显示广告 推  荐
个性化 较少的个性化需求 TB级别个性化需求
需求点 反作弊,索引规模等 质量,安全性,CTR预估等 多样性,覆盖度等
关注点 相关性 投资回报率(ROI) GMV、利润等提高
常用算法 PageRank算法,NLP等 逻辑回归等 协同过滤,关联规则,内容推荐等

互联网广告的蓬勃发展,让广告的精准化需求越来越大,计算广告学正在成为一个兴起的分支学科。2009年由Yahoo!的资深研究员Andrei Broder提出的计算广告学涉及大规模搜索和文本分析、信息获取、统计模型、机器学习、分类、优化以及微观经济学。计算广告学所面临的最主要挑战是在特定语境下特定用户和相应的广告之间找到“最佳匹配”。语境可以是用户在搜索引擎中输入的查询词,也可以是用户正在读的网页,还可以是用户正在看的电影,等等;用户相关的信息可能非常多也可能非常少;潜在广告的数量可能达到几十亿。但核心的思想都是为了市场参与者的利益平衡与最大化。

广告的收费模式主要有CPM(cost per thousand impressions,按每千次的展示进行付费)和CPC(cost per click,按点击付费),通常用eCPM(effective cost per mille, 每一千次展示可以获得的广告收入)来反映网站盈利能力,见表9-5。

表9-5 CPM和CPC比较

 

CPM

CPC

主要应用

显示广告,如图形多媒体广告、条幅广告

搜索广告、广告联盟的排序规则

特征

固定eCPM

动态eCPM和固定点击值

在CPC环境中,通过eCPM对广告进行排名。eCPM=bidPrice×CTR,其中bidPrice是指广告主给出的竞拍价格,CTR(Click Through Rate)是我们预估的该广告的点击率,总体结果越高越容易被展示。广告的bidPrice可事先确定,然后根据每个广告的最大价值和历史bidPrice,不断调整最新的bidPrice。广告的最大价值由CTR、客户愿意支付的CPC和毛利率目标来计算得到,其中最关键的是根据广告创意等特征估算广告的CTR。一般做法是通过逻辑回归等机器学习模型,根据查询和广告的特征来预估CTR。逻辑回归几乎是所有广告系统和推荐系统中点击率预估模型的基本算法,可以用来判断是否是垃圾邮件,是不是金融欺诈,等等,逻辑回归同时也是目前互联网广告的三大机器学习系统之一(另外两个是隐含主题模型和深度神经网络系统)。

逻辑回归的模型将多维特征根据其训练得到的权重和当前计算得到的值回归到(0, 1)区间上的一个概率值,对于预估点击率来说,即表示用户可能点击广告的概率。它是一个非线性的sigmoid模型,本质上还是一个线性回归模型,因为除去sigmoid映射函数关系,其他的步骤和算法都是线性回归的,一般采用如下公式:

Logistic曲线如图9-9所示,逻辑回归就是通过拟合Logistic曲线,最终得到不同特征的权重,从而预测某个事例出现的概率。

图9-9 Logistic曲线

其中X是输入变量,y是输出,是特征权向量,X可以是用户,广告,上下文组成的数据对<用户,广告,上下文>。预测CTR中,y=1代表用户会点击该广告,y=−1代表用户不会点击该广告。X中<用户,广告,上下文>数据对可以由数据对相应的特征向量构成,其特征可以达到10亿数量级,比如图9-10的组合。

图9-10 用户历史数据

其问题转换为通过历史中用户是否点击广告的情况,作为训练集来求解,从而得到模型。可以利用极大似然法估算模型的参数:

转换为使得以上公式的结果概率最大,因为前面线性回归中梯度下降法是求最小值,通过对公式取负log,转换为求最小值问题,给赋予随机值,通过反复迭代得到极小值,作为最终的

CTR除了用在搜索的竞价广告中,在目前的实时竞价中也有广泛应用,实时竞价过程一般通过cookie映射(cookie mapping)技术关联用户,我们把实时竞价中的各方简化成ADX和DSP。

当用户访问一个加入应用ADX服务的网站,网站上相关栏位出租广告,各个DSP通过cookie映射和ADX建立映射,识别为同一用户,然后DSP根据从ADX或者广告主等得到的用户信息,自动对广告进行出价,ADX也通过用户历史数据,预测CTR,对各个DSP出价的广告进行排名,显示收入最高的广告。实时竞价的过程如图9-11所示。

图9-11 实时竞价过程

当然有些用户经常关闭cookie功能或者清空cookie,现实当中除了通过cookie映射来关联用户,还有很多种方式,具体可以参考访客追踪的开源项目evercookie,它可以通过standard HTTP cookies、Flash cookies、HTTP Etags、window.name.caching、userDatastorage、你强制缓存的PNG的RGB值、Web history、HTML5一些特有的存储特性等来关联用户的行为,但是切勿用于非法用途。

实时的CTR预测主要通过加快更新用户历史点击率的数据,越及时,预估出来的点击率就越准确。实际生产环境中,更新频次依赖于训练数据的规模、日志数据的回流速度以及集群规模等因素。在数据稀疏的情况下,可以通过经典的贝叶斯方案对点击率做平滑,贝叶斯需要通过先验概率训练参数。为提高实时性,也可以用一些简单的平滑公式做点击率平滑来提高性能。

目前提高流量的方式有搜索引擎营销(Search Engine Marketing,SEM)、网盟、搜索引擎优化(Search Engine Optimization,SEO)等。各大互联网巨头抢占流量入口,目前搜索引擎仍然是主要的流量入口之一,每天十几亿的PV,应用得当,会让网站跨上千万级的PV。SEM的优化工作分为关键词选取、创意登录页面设计、网站结构优化等,在一个电商网站中,关键字又主要分为品牌词、竞品词、通用词、商品词等,其中商品词是流量的主要来源。一个网站拥有百万的SKU,通过人工管理代价很大,同时一些热门事情,如果处理不及时,会让几个热门词几分钟会花完网站一天的预算,所以需要一套智能的系统自动做SEM。SEM包含预警模块,用来实时监控投放数据、库存、热门关键字等,保证投放的时效性;还有爬虫模块,用来实时爬取投放关键字在搜索引擎中的排名,判断什么时候可以降低出价,让关键词出现在最好的位置,平衡高排名关键字的成本和转换率。当然,数百万的关键词,往往性能是很大的瓶颈。目前Storm在实时监控数据和实现爬虫上,都有不少的应用,利用Storm和内存数据库可以很好地解决SEM中实效性的问题。

腾讯等公司在投放广告的时候,可以根据用户标签信息、用户原始行为和商圈等定向地投放广告,如图9-12和图9-13所示。

而下层的支持很大一块是准实时的用户画像系统,结合不同的场景用不同的算法展现广告或者物品。通过Storm的Spout实时收集不同的数据源的行为,从而屏蔽不同数据源的差异性,通过Bolt提取各种行为的标签,对各类标签进行去噪、补充近义词/同义词等进行数据清洗,然后把统计次数存入存储引擎中,最后根据历史统计的次数,实时计算得到的用户的兴趣度。通过图9-14可以看到,为了在海量数据下达到实时性,腾讯设计的实时计算的算法不是很复杂,没有对用户当前类目的其他相关类目的兴趣度进行降权,甚至没对当前类目的兴趣度根据日期变化进行相关衰减等,算法精准性、数据量、实时性总是一个相互制约的过程,腾讯牺牲了部分算法的精准性提高了实时性。

图9-12 选择用户基本信息投放广告

(截取自腾讯广点通网站e.qq.com,图中相关内容的著作权归属原著作权人所有)

图9-13 选取区域投放广告(截取自腾讯广点通网站e.qq.com,图中相关内容的著作权归属原著作权人所有)

图9-14 腾讯实时画像系统(图片来源腾讯广点通介绍,图中相关内容的著作权归属原著作权人所有)

阿里通过阿里妈妈达摩盘来做消费者洞察和精准化营销,如图9-15和图9-16所示,商家或运营通过选取用户画像标签、商品信息以及天气、时间等上下文信息,对用户进行分群投放广告,或者对新客、半新客等不同生命周期的用户采取不同的引导,从而达到精准化营销。

图9-15 选取用户群

图9-16 人群分析(来源:http://www.split.alimama.com/college_detail.htm?spm=a2320.7393609.0.0.N10Q2c&contentId=633

其他大型手机制造商,可以根据收集到的用户的详细行为,为应用开发者提供相应的广告接口,应用开发者开发相应的应用时,只需要调用它们的广告API,填入基本的用户群标签信息和广告的位置、大小等信息,就可以完成精准化的广告投放。

亚马逊等网站的千人千面基本也是基于用户标签、用户浏览、购买、搜索、加车等行为来让运营或者商家选取用户群,在非广告的栏位通过后台算法筛选出最优的产品展示在栏位上,这样运营和商家根据自己的产品特点,可以自己来选取人群(比如:一个月前买过洗发水,最近一月没有买过洗发水的用户;最近一周浏览和收藏过手机,但是没有购买的用户)。对于广告栏位,广告主通过选取用户群和栏位进行竞价,展示在用户前面的商品也是另外一种形式的千人千面。电商网站的精准化营销和千人千面可以基于如图9-17所示的统一平台来完成。

图9-17 精准化营销和千人千面统一平台

(1)根据用户画像和用户历史数据生成用户维度的基础表,在索引平台建立用户维度的索引。

(2)抽取商品的各种属性建立选品中心,在索引平台建立商品的索引。

(3)根据天气、地域等上下文信息和用户基础表,通过Apriori、FP-Growth等关联规则挖掘算法,挖掘出和类目或品牌经常一起出现的属性集合(称为频繁项集),然后在索引平台建立类目和品牌的索引。

(4)设置索引平台用户和商品等维度的Schema,选人、选品应用根据这些Schema的属性组合生成唯一的键。

(5)新建栏位或者短信、邮件等精准化营销的应用名,绑定到相应的键(通过这些键找到具体的查询条件)。

(6)精准化营销的产品或者千人一面的栏位就可以根据上一步的应用名,找到选人或选品需要的具体的属性条件,然后通过搜索平台精准的找出商品或用户。

(7)对于千人千面的栏位可以通过用户基础表找出用户的属性,然后根据之前设置的键,筛选出需要的属性,最后通过搜索平台找出相应的商品个性化的展现在栏位上。

基于目前流行的ElasticSearch或Solr建分布式索引和搜索平台,前端每个千人千面栏位哪怕只访问一次索引往往也需要较多的机器,也可以通过给栏位设置相应的选人和选品的属性条件,每天离线生成每一个栏位的用户到产品的映射关系,关存到MySQL或HBase中,前端栏位根据访问的当前用户,直接读取用户-产品的映射表来实现千人千面的个性化推荐。

在PC互联网时代,谷歌2005年就推出了个性化搜索服务,因为引发公众对隐私的担忧,没有太商业化,如今移动互联网时代,去哪儿、京东等电商,慢慢都推出了个性化搜索,无论消费者登录与否,通过追踪客户的搜索行为来判断其消费喜好,即便用户退出登录,也会根据Cookie、设备号等信息,给用户返回个性化的搜索内容。当然搜索引擎本身的爬虫/反爬虫等异常检测、对搜索关键字分词后进行同义词/反义词/全半角/简繁体等自动扩展以及错别字的纠正都可以在实时计算中进行数据清洗。例如,eBay应用Jetstream流处理技术,对海量的用户行为进行了实时的数据清洗。

要让个性化的搜索内容更加精准,需要打通外部用户画像和不断更新着的商家的服务、商品等。在电商的搜索系统中,为了解决搜索的并发和性能,往往有内存中的实时分布式索引和硬盘中的全量索引,热门商品信息直接从内存中读取,当内存中不存在时,才从硬盘中读全量索引。在实时流计算出来之前,初始化分布式索引对索引的切分很多时候是根据类目。不同的类目的PV和产品数不一样,根据每一个类目的PV和产品数不同,对产品的索引进行切分。有的类目PV高,产品数少,放到内存中,冗余多份数据在不同的机器上;有的类目PV少,产品数多,这种就不适合初始化进实时索引中了。当卖家更新和增加产品信息时,索引等产品信息统一更新到一个地方,然后每隔一段时间把信息一并推送到分布式索引中。当实时流计算出来之后,实时的更新分布式索引就更加方便了。

电商的搜索系统中,一般都会自己维护一套词库,以对搜索内容进行更加准确地切词,然后通过切出来的词映射到对应的类目、品牌等。电商每一个类目的专有名词比较多,仅仅应用外面通用的中文分词词库在实践中很难提高分词的准确性。在这里,想强调下,维护一个自己的属性、品牌词库,对整个网站价值很大。首先,自己的准确的词库让用户画像系统也会更加精准。网站早期很多产品属性命名的不统一,相同的属性,业务方录入很多不同的属性名称,导致一个系统的产品属性到后面往往是混乱的,画像打出来的分也因此不能准确反应出用户的偏好。有准确和健全的词汇,一来可以对属性做清洗,提高用户打分的准确性,二来可以规范业务方录入的各种产品的命名等,让业务方更好地进行品类管理。其次,对电商的比价系统也是至关重要。电商往往要识别其他竞争对手的同类产品的价格,好进行智能调价或者统计,准确的词汇,能让系统更精确的对产品的标题进行分词和特征词标注,提高抓取竞争对手网站产品的覆盖率和匹配准确率。最后,对识别用户搜索的内容也是不可或缺的,有了完善的自己的词库,对搜索的内容才能进行更加准确地切分和映射,更加精准地明白用户想要的产品、类目等,从而提高用户搜索排序的准确率,以及减少用户筛选时间。

在电商系统中,在书籍、衣服等同一类目下用基于ItemCF的推荐计算出产品相似度,推荐同类目下其他产品,相对可解释。但是如果因为两本书所购买的人群比较相似,把人群中A用户喜欢的某茶叶推荐给B用户,显然不大好解释。所以我们应首先识别用户目前需要购物的类目,并识别出购物类目的意图,再在同一类目下通过协同过滤、内容推荐等方式给用户推荐合适的商品。目前各大电商网站都在识别用户搜索的实时意图上进行了不同程度的探索和研发,有些在用户长期画像的基础上抽出一张用户短期画像表来实现,我们认为用户对大部分的品牌和产品具有长期的偏好,偏好的更新周期相对比较长;而对于类目,用户往往根据外界环境、家庭短缺等不确定因素而购买,比如家里什么东西意外坏了需要补充,如果能实时的识别用户需要购买的类目,显然是比较有价值的。我们假设用户画像有用户基础属性(性别、年龄、职业等)和用户偏好(类目、品牌、产品等),在用户偏好的基础上抽出长期偏好和短期偏好两个表,用短期偏好和基础属性来实时预测用户购买类目,再记录各个类目的用户短期购买行为。因为基于邻域的协同过滤具有扩展性问题,计算复杂度随着用户数和产品数增加而增加,在实时推荐中,是一个亟需要解决的问题,我们这里用判别分析方法实时预测用户需要购买的类别(判别分析法实际上是一种基于模型的推荐算法)。判别分析是用户判断个体所属类别的一种统计方法,对用户的购买意图进行预测,根据已知观测对象的分类和若干表明观测对象特征的变量值,建立判别函数和判别准则,并使其错判率最小。

我们保存最近一段时间有过用户行为的用户 最近一段时间有行为的用户}。

的行向量看作n维随机向量,对其进行降维,采用PCA(主成分分析)技术进行降维,然后对采用信息增益技术进行特征选取,选取topN个最能判别用户购买意图的基础属性。常用的判别函数有Fisher判别法、马式距离判别法、广义平方距离判别法、最大后验概率判别法、贝叶斯判别法等,它们各自有不同的优缺点,当加入或者减少某一种条件,它们又可以互相转换。这里我们以马氏距离为例,与欧氏距离不同的是它考虑到各种特性之间的联系,马氏距离能够很好地处理多维向量各维度的量纲不一致的问题以及各维度具有相关性的问题,只需要知道总体的特征值,不需要知道总体的分布类型,方法简单,结论明确。首先根据样本矩阵统计购买Cj这个总体的协方差矩阵和总体均值向量

对任意用户,设其降维后的行为向量为,得到用户与Cj总体的广义马氏距离为。其中 为总体Cj发生的先验概率,根据用户与总体Cj的距离,即可以得到用户最近一段时间内topN的意图类目,然后利用构建的场景引擎应用到相应的栏位。我们以个性化搜索为例,当用户刚上来,没有太多初始化行为的时候,默认排序根据用户的长期偏好、广告主投放等排序,当用户点击浏览了一定的商品后,利用上面的判别分析方法,实时预测出用户实时的类目,当用户再次搜索或者到达某一个推荐栏位的时候,结合得到的实时topN的意图类目,并给予一定的权重,融进原来的排序结果,给出新的排序结果,如图9-18所示。

图9-18 融入实时意图的个性化排序

在推荐栏位中,得到用户topN的类目再在同类目下利用协同过滤、关联规则等推荐方法,对同一类目下的产品进行相关推荐,以得到更加精准的结果;但是在搜索系统中,往往搜索词已经映射到相应的一些类目上,这个时候,对用户需要购买的产品、品牌的意图识别往往更有用,比如品牌,因为数量相对产品小很多。可以把类目的意图识别方法应用过来,然后融入实时个性化搜索中。对于海量的产品,可以根据用户前面几年购买的产品属性、天气、地域等各类标签预先作一定的关联分析,然后把天气,地域等标签融入选品中心,通过这些标签做意图识别。

一淘、帮5买、网易惠惠等都是目前比较成熟的比价搜索,通过它们能搜出各类商品在各个电商上的价格;目前不少电商也实现了自己的比价/调价系统。如何定价,是传统零售业和电商必须需要思考的问题,只有定价策略应用得当,才能在成本、销量、利润、转化率上找到一种平衡。而且电商相比其他的互联网产品,用户粘性偏弱,用户对价格敏感性更强,往往一点点的差价,就会影响用户的留存率和产品的利润,因此合理设置和竞争对手商品的价格,在利润和用户转化率上取得平衡,才能提高电商的整体竞争力。面对百万、甚至千万的SKU,通过人工来定价成本非常高,因此需要系统进行智能定价。当然系统中也会设置自己的价格底线,如果发现对手的价格偏低,那么可以及早地分析是不是对手的进货渠道不同,或者是不是他们在运营、物流、供应链上成本控制得更好,还是对手贴钱促销。及时知道热门商品竞争对手的信息,可以快速优化自己的运营和决策,可以挖掘出竞争对手新品和好的缺失品,对自己的品类分析、品类管理、价格监控等都有非常大的价值。

一个比价系统通常包括爬虫系统、匹配系统、数据分析系统、智能决策系统(优化品类、自动调价、促销策略等)。爬虫系统用来抓取竞争对手的商品信息,Java的爬虫一般应用httpclient和Jsoup库来实现,通过httpclient发起HTTP请求,通过Jsoup解析请求回来的页面的元素。但是有些电商网站商品页的价格、库存、评论数等值,在页面的源代码里面是无法获取到的,它们往往是页面里面有另外的Ajax请求去获取价格、库存等值,然后通过JavaScript填充到页面的相关div或者span等位置,通过Java的方式抓取脚本通常都是要发起多个请求才能完成一次完整信息的抓取,失败率也特别高,抓取脚本相当复杂,去开发一个Java的JavaScript解析引擎时间成本又过高。而Python有pyqt4这个类库,它有内置的webkit浏览器引擎,通过它来请求URL,返回的HTML源码是渲染好的网页,价格、库存等信息已经填充在了相应的HTML元素中,不用多次请求。如果用Java的Runtime的方式去调用Python脚本,显然性能存在一定的损耗,但Storm支持Python等多种语言,这样可以不受语言限制,充分运用各类优秀的开源库,更低成本地实现爬虫系统。Python有解析HTML元素成熟的beautifulSoup库(类似Java的Jsoup库),结合Python的urllib2或者Requests库,可以更好地完成这个任务。

有了爬虫系统,要得到同一款商品竞争对手的信息,那么匹配系统运用而生,假设不考虑商品详情页中价格、商品介绍、商品图片等因素,仅对商品详情页的标题做匹配,一般把标题切成特征词(品牌词、规格、货号、成份、品类词),然后不同类目下给每种特征词不同的权重,计算出哪些商品是同一商品,从而结合调价系统,进行自动调价。

有诸多的因子决定电商搜索产品的排序,常见的包括:是否自营、销售额、销量、收藏数、点击数,曝光数、毛利率、用户评价数、好评率、是否促销、退款率、投诉率、发货速度等。如果是商城或者店铺,商品的排序因子主要如表9-6所示,分为推广量、服务、店铺优化、店铺等级等。同时商家为了提高商品排名,会出现不同程度的作弊,有的通过虚假交易进行刷单;有的在商品标题中加入竞争对手品牌;有的用低价引流,实际上是将一个低价产品和一个正常产品组成套餐;有的过一阵偷偷更换标题和商品,把一个销量比较好的商品变成另外一个新商品,这些不同的作弊方式也是一个排序因子,用来惩罚违规的商家。

表9-6 店铺的排序因子

分  类

具体因子

推广量

近7日销量、近1月销量、评价数、近1月销售额、收藏量等

服务

发货速度、投诉率、侵权率、好评率、发货速度、支付方式等

店铺优化

上下架时间、产品相关性、产品图、产品描述、库存量等

店铺等级

信用等级、店铺装修等级、保障等级(正品保障、假一赔三)等

影响排序因子又分为静态因子和动态因子,如表9-7所示。动态因子中对搜索关键字和标题相关性的计算需要对标题进行切词,前面比价系统中的匹配模块也需要对标题进行切词,两者的切词算法可以用一套,关键是对各个商家和品牌的一些词汇需要人工整理,这个一般会耗费一定的成本。

表9-7 静态和动态因子

分  类

具体因子

静态

近7日销量、近1月销量、评价数等

动态

标题的相关性、类目的相关性、个性化意图等

可以看到,影响搜索排序的因子数目繁多,并且不同的因子(比如销售额、送货速度、好评率等)量纲不同,首先需要对各个因子归一化,不同的因子需要设计不同的归一化公式。归一化后,根据各因子对结果的贡献不同,要设置不同的权重,可以通过SVM等机器学习算法来训练各因子的系数。检测训练出来的模型是否靠谱,通常使用已知目标值的样本作为输入,观察其准确率。对于电商而言,转化率是一个不错的选择,转化率的分子可以是商品的销售量、销售额、加入购物车的数目等,分母可以用商品的曝光率。训练得到各因子系数,并且模型经过验证后,搜索的基本排序公式应运而生,实际的系统中还会加入人工规则和广告竞价排序的因素。


相关图书

SPSS医学数据统计与分析
SPSS医学数据统计与分析
首席数据官知识体系指南
首席数据官知识体系指南
大数据实时流处理技术实战——基于Flink+Kafka技术
大数据实时流处理技术实战——基于Flink+Kafka技术
大数据安全治理与防范——流量反欺诈实战
大数据安全治理与防范——流量反欺诈实战
搜索引擎与程序化广告:原理、设计与实战
搜索引擎与程序化广告:原理、设计与实战
医疗大数据挖掘与可视化
医疗大数据挖掘与可视化

相关文章

相关课程