查看原文
其他

基于Kylin的推荐系统效果评价系统

唐汉英/詹坤林 58AILab 2020-08-20

        OLAP(联机分析处理)是数据仓库的主要应用之一,通过设计维度、度量,我们可以构建星型模型或雪花模型,生成数据多维立方体Cube,基于Cube可以做钻取、切片、旋转等多维分析操作。早在十年前,SQL Server、Oracle 等数据库软件就有OLAP产品,为用户提供关系数据库、多维数据集、可视化报表的整套商业智能方案。(本科毕业设计就是做OLAP分析,对相关理论和实践有兴趣的可以参阅我的论文,链接:https://share.weiyun.com/d6b7a9b521927d93c004efb9290ce8f1)

        随着大数据的发展,Kylin、Druid、Presto 等基于大数据的OLAP开源工具开始涌现,我们可以对亿级的数据进行OLAP分析。Kylin 就是一款基于Hive的开源OLAP工具,我们可以设计Hive表的字段为维度和度量,通过Kylin来构建Cube,Kylin会将Cube结构存储在 HBase 之上,基于Cube我们可以做各种多维分析。在推荐系统开发过程中,我们往往需要按场景、策略、平台等多个不同维度来比较效果数据,推荐系统的线上效果评价是一个很强的多维分析应用场景。因此,我们基于Kylin搭建了推荐系统效果评价系统。

        本文主要包含三部分,首先介绍了我们的需求背景,其次介绍了Kylin的基本概念和原理,最后介绍了我们如何利用Kylin来完成推荐系统的效果评价。了解Kylin基础的朋友可以直接阅读第三部分,对Kylin原理较生疏的朋友建议先阅读第二部分,尤其是Cube维度优化相关内容。


1 背景介绍

        五八同城智能推荐系统是一个通用的推荐平台,旨在为五八同城不同业务线提供推荐服务。目前,我们接入了不同业务线在APP、PC、M三端上共计约六十个推荐位,包括大类页、列表页、详情页、公共页(如APP首页、消息中心页等)等推荐场景。在一些核心推荐位上,我们会持续迭代召回策略、排序算法以及展示样式,以不断优化推荐效果。比较和分析不同业务线、不同推荐场景、不同推荐算法的效果是我们的基础工作,早期我们是通过 Hive + MySQL的方式来实现效果数据的统计分析,但耗费了大量人力。在数据平台提供了Kylin服务之后,我们基于Kylin重构了我们的推荐系统效果评价系统,大大节约了人力成本,提高了开发效率。




2 Apache Kylin基本原理和架构

       Apache Kylin是一个开源的分布式分析引擎,提供Hadoop之上的SQL查询接口及多维分析(OLAP)能力以支持超大规模数据,能够支持TB到PB级别的数据量,最初由eBay Inc开发并于2014年10月贡献至开源社区,于2014年11月加入Apache孵化器项目,于2015年11月正式毕业成为Apache 顶级项目。

2.1 名词、概念解释

       事实表:事实表是用来记录具体事件的,包含了每个事件的具体要素,以及具体发生的事情。

例如超市A的购物事实表:

时间         客户id    商品id  数量   支付id

2016-10-11 10:52:30  2      3    1    1003

2016-10-11 10:52:30  2      4    2    1003

2016-10-11 12:31:10  2      2    2    1005

2016-10-11 12:31:10  2      4    1    1005

       这张事实表记录的其中一条事实为:客户id为2的客户在2016-10-11 10:52:30购买了1个商品id为3的商品。

      维表:维表包含对事实表的某些列进行扩充说明的字段。例如超市A的商品维表:

商品id    商品名称    商品单价

2       泡泡糖     1

3       泡面      5

4       电池      2

       事实表结合维表,可以得到更为详细的事实。当我们需要知道事实表中记录的某个事实的更加详细信息时,就需要使用对应的维表。

       星型模式:包含一个或多个事实表、一组维表,以及事实表与维表的join方式。例如:


       度量:度量是具体考察的聚合数量值,例如:销售数量、销售金额、人均购买量。计算机一点描述就是在SQL中就是聚合函数。

  例如:select cate,count(1),sum(num) from fact_table where date>’20161112’ group by cate;

count(1)、sum(num)是度量

       维度:维度是观察数据的角度。例如:销售日期、销售地点。计算机一点的描述就是在SQL中就是where、group by里的字段

例如:select cate,count(1),sum(num) from fact_table where date>’20161112’ group by cate;

date、cate是维度

       预计算结果:预计算结果是对事实表进行预计算的结果,预计算结果以键值对形式存在,键是维度的特定值、值是对应的度量值,一条预计算结果有一个对应的维度集合。例如商品销售的预计算结果:

键值对<商品id=’2’,日期=’20161223’>:<数量=12,金额=21>是一条预计算结果,它对应的维度集合是{商品id, 日期},这条预计算结果表示商品id为2的商品在20161223这一天总共卖出了12个,总价格为21元。

键值对<商品id=’2’,日期=’20161223’,城市=’北京’>:<数量=8,金额=14>也是一条预计算结果。它对应的维度集合是{商品id, 日期, 城市},这条预计算结果表示商品id为2的商品在20161223这一天的北京市总共卖出了8个,总价格为14元。

键值对<>:<数量=100,金额=300>也是一条预计算结果。它对应的维度集合是空集,这条预计算结果表示历史所有卖出的商品数量为100个,总价格为300元。

      预计算结果全集:全部预计算结果组成的集合。

      cuboid:预计算结果全集中对应的维度集合相同的预计算结果的集合.

例:<商品id, 日期>cuboid是预计算结果全集中对应的维度子集等于{商品id, 日期}的预计算结果的集合。

数学一点的表示:<商品id, 日期>cuboid =

{预计算结果 | 预计算结果对应的维度子集={商品id, 日期},预计算结果∈预计算结果全集}

      cuboid树:若干个不同的cuboid组成的有向树,满足孩子对应的维度集合是父亲对应维度集合的子集

例如下面这两棵cuboid树:


      Cube:Cube是一个集合,包含若干个cuboid。


2.2 Apache Kylin核心思想

       简单来说,Kylin的核心思想是预计算,用空间换时间,即对多维分析可能用到的度量进行预计算,将计算好的结果保存成Cube,供查询时直接访问。把高复杂度的聚合运算、多表连接等操作转换成对预计算结果的查询,这决定了Kylin能够拥有很好的快速查询和高并发能力。


2.3 Apache Kylin架构


kylin由以下几部分组成:

· REST Server:提供一些restful接口,例如创建cube、构建cube、刷新cube、合并cube等cube的操作,project、table、cube等元数据管理、用户访问权限、系统配置动态修改等。除此之外还可以通过该接口实现SQL的查询,这些接口一方面可以通过第三方程序的调用,另一方也被kylin的web界面使用。

· jdbc/odbc接口:kylin提供了jdbc的驱动,驱动的classname为org.apache.kylin.jdbc.Driver,使用的url的前缀jdbc:kylin:,使用jdbc接口的查询走的流程和使用RESTFul接口查询走的内部流程是相同的。这类接口也使得kylin很好的兼容tebleau甚至mondrian。

· Query引擎:kylin使用一个开源的Calcite框架实现SQL的解析,相当于SQL引擎层。

· Routing:该模块负责将解析SQL生成的执行计划转换成cube缓存的查询,cube是通过预计算缓存在hbase中,这部分查询是可以再秒级甚至毫秒级完成,而还有一些操作使用过查询原始数据(存储在hadoop上通过hive上查询),这部分查询的延迟比较高。

· Metadata:kylin中有大量的元数据信息,包括cube的定义,星型模型的定义、job的信息、job的输出信息、维度的directory信息等等,元数据和cube都存储在hbase中,存储的格式是json字符串,除此之外,还可以选择将元数据存储在本地文件系统。

· Cube构建引擎:这个模块是所有模块的基础,它负责预计算创建cube,创建的过程是通过hive读取原始数据然后通过一些mapreduce计算生成Htable然后load到hbase中。

 

2.4 Kylin的Cube优化

       计算Cube的存储代价以及计算代价都是比较大的, 传统OLAP的维度爆炸的问题Kylin也一样会遇到。 Kylin提供给用户一些优化措施,在一定程度上能降低维度爆炸的问题。

       在业务分析中有许多cuboid是我们不会用到的。例如我们的推荐中我们不会分析没有date维度的cuboid,就是说我们不会不指定日期来分析数据;我们在分析cuboid中带recname的时候,就一定有scene,就是说我们在分析数据recname的时候,一定会同时分析scene。

       我们可以不存储也不计算这些我们不需要的cuboid。这样就节省了很大的硬盘空间和Cube构建时间。这些都是Cube可以优化的空间。

       Kylin的4种维度类型:

       1.普通维度(General dimension):普通维度是不做任何优化的维度。有n个普通维度的Cube的cuboid的数量为2^n

       2.强制维度(Mandatory Dimensions):强制维度是Cube中所有cuboid中必有的维度。强制维度可以使Cube的cuboid减少一半。

       3.层级维度(Hierarchy Dimensions),层级维度是有层次关系的维度,使得cuboid中低层次的维度总是伴随着高层次维度的出现。一个有n个层次的层次维度可以使cuboid的数量从2^n降到n+1。例如 年、月、日 可以作为3个层级的层级维度

       4.组合维度(Joint Dimensions),组合维度是将几个维度组合成一个维度,使得这几个维度在cuboid中总是同时出现或总是同时不出现。一个有n个维度的组合维度可以使cuboid数量从2^n降到2。 例如 年、月、日 可以作为有3个维度的组合维度(日期)。

      Cube的cuboid数量计算例子:

      普通维度数有n个,强制维度有m个,i个维度和j个维度的层级维度各一个,x个维度和y个维度的组合维度各一个的Cube。

      普通维度有出现和不出现于cuboid中两种情况,所以n个普通维度有2^n个不同的组合。

       强制维度一定会出现在cuboid中,所以它不会使cuboid数目增加。

       层级维度,在cuboid中低级的维度总是伴随着高级的维度出现,所以i个维度和j个维度的层级维度各有(i+1)、(j+1)种不同的组合。

       组合维度,组合维度中的维度总是同时出现/不出现在cuboid中,所以有y个维度的组合维度和有y个维度的组合维度各有2种不同的组合。

      所以此Cube的Cuboid数量 =  (2^n)*(i+1)*(j+1)*2*2。

 

      做Cube优化需要我们对自己的分析业务非常了解。不然可能会将我们需要cuboid去掉,导致查不出结果,或在查询的时候引起不必要的较大的聚合使查询过慢。

     例如:

     有2个维度date、cate,1个度量count(1) as pv。不做任何优化的Cube X有所有的cuboid共2^2=4个,做了优化的的Cube Y 有 只有{date, cate }这个cuboid。在X上执行select count(1) from fact_table where ‘20161112’ = date

会直接在{date}这个cuboid上得到结果。在Y上执行select count(1) from fact_table where ‘20161112’ = date。因为Y没有{date}这个cuboid,所以会发生聚合计算,将{date,cate}中的date=’20161112’的预查询结果做聚合计算后返回结果


2.5 Kylin的distinct count聚合计算

       Kylin中的度量,例如count、max、min、sum大部分我们都能理解是如何计算的,但count(distinct xxx)(UV)是如何计算的呢?

       在Kylin有两中方式计算UV:

       第一种是近似Count Distinct。使用HyperLogLog算法实现了近似Count Distinct,提供了错误率从9.75%到1.22%几种精度供选择。算法计算后的Count Distinct指标,理论上,结果最大只有64KB,最低的错误率是1.22%。这种实现方式用在需要快速计算、节省存储空间,并且能接受错误率的Count Distinct指标计算。

      第二种是精准Count Distinct。使用bitmap数据类型来做标记,虽然是bitmap类型,但不是真正的位图,而是被当成了类似C++的bitset的数据结构。当数据类型为int、short int、tiny等32位以内的数值型时,会直接映射到bitmap上,当数据类型为long、string等其他类型时,会将数据值以字符串形式编成字典,在将字典上对应的字符串id映射到bitmap。这种实现方式提供了精确的无错误的Count Distinct结果,但是需要更多的存储资源。 


2.6 Kylin的Cube构建

       逐层算法是构建Cube的一种算法,它将cuboid按对应维度子集中纬度的个数分层。逐层计算cuboid,对维度个数较多的cuboid做聚合操作得到维度个数较少的cuboid。

       假设我们有date、platform、algo三个维度,count(1) as pv、sum(num) as sumofnum两个度量。不做任何的优化。事实集:

dateplatform    algo      num

20161213      android   13

20161214      ios     22

20161215      android   23

 那么cuboid树为:


      因为有4层,所以会做4次mapreducer计算。除了第一次mapreducer计算的输入是源数据集外,其他每一次mapreducer计算的输入输出的每一行都是一条预计算结果。

     第一次mapreducer将源数据集作为输入,计算并输出第1层的cuboid {{date,platform,algo}},第二次的mapreducer以第一次的mapreducer的输出做为输入,计算得出第二层的第2层的cuboid {{date,platform}, {date,algo}, {platform,algo}}。之后的mapreducer类似第二次的mapreducer。

map阶段(除了第一次mapreducer的map),对于一行输入,根据cuboid树输出对应的若干行,例如:

对于输入:

<date=’20161213’,platform=’android’,algo=’1’>:<pv=1,sumofnum=3>

输出:

<date=’20161213’,platform=’android’>:<pv=1,sumofnum=3>

<date=’20161213’,algo=’1’>:<pv=1,sumofnum=3>

<platform=’android’,algo=’1’>:<pv=1,sumofnum=3>


对于输入:<date=’20161213’,algo=’1’>:<pv=1,sumofnum=3>

输出:<date=’20161213’>:<pv=1,sumofnum=3>


reducer阶段,将相同键的值做聚合操作,并输出预计算结果,例如:

对于输入:

<algo=’2’>:<pv=1,sumofnum=2>

<algo=’2’>:<pv=1,sumofnum=3>

输出:<algo=’2’>:<pv=2,sumofnum=5>

 

       除了逐层算法,还有快速Cube算法。该算法的主要思想是对Mapper所分配的数据块,将它计算成一个完整的小Cube 段(包含所有Cuboid);每个Mapper将计算完的Cube段输出给Reducer做合并,生成大Cube,也就是最终结果,这里不在详细介绍。


2.7 参考资料

http://blog.csdn.net/xiao_jun_0820/article/details/50731117

http://webdataanalysis.net/web-data-warehouse/data-cube-and-olap/

http://webdataanalysis.net/web-data-warehouse/multidimensional-data-model/

http://blog.csdn.net/rogerxi/article/details/3966782

http://www.cnblogs.com/en-heng/p/5239311.html

http://www.cnblogs.com/hark0623/p/5521006.html

http://lxw1234.com/archives/2016/08/712.htm

http://www.infoq.com/cn/articles/apache-kylin-algorithm/




3 如何利用 Kylin 完成推荐系统的效果评价

       我们的推荐系统是一个通用的推荐平台,为五八同城不同业务线提供推荐服务。目前,接入了不同业务线在APP、PC、M三端上共计约六十个推荐位,包括大类页、列表页、详情页、公共页(如APP首页、消息中心页等)等推荐场景。在一些核心推荐位上,我们会做AB Test多层实验,持续迭代不同的召回、排序、展示方式,以优化推荐效果,如下图所示:

       日常工作中,我们经常会比较不同业务线、不同客户端、不同推荐位、不同推荐策略的数据效果,例如我们会比较房产和车在相同推荐位上的数据对比,猜你喜欢场景上不同排序算法的数据对比,二手房详情页在Android和IPhone上数据对比。各种数据对比能帮助我们优化推荐策略,甚至发现某些业务线功能逻辑上的隐藏BUG,例如在我们推荐项目攻坚阶段,我们通过分析比较二手房详情页在Android和IPhone两端的推荐效果,发现了IPhone上详情页浏览回退的BUG,最终反馈给业务方并解决了该问题,该BUG的解决使得我们在二手房的推荐点击占比绝对值提高了百分之一。总之,我们的推荐效果数据分析是一个很好的多维分析场景。

        早期,我们的推荐效果数据统计是通过 MR + Hive + MySQL 来实现的,首先会从编写MapReduce程序对原始埋点日志进行抽取生成Hive表,然后会编写大量的Hive SQL来统计各类指标数据,并将结果数据写入MySQL数据表,最终做可视化展示和邮件报表。由于我们的比较维度多,比较指标多,Hive SQL语句的编写消耗了我们不少人力。在数据平台部门提供了Kylin服务支持后,我们将我们的效果数据统计工作迁移到了Kylin之上,我们只需要设计好Hive源数据表,并设置好维度和度量,Kylin便能根据维度和度量来自动预计算结果数据,这省去了我们编写Hive SQL的工作,大大提高了效率。接入Kylin之后,我们的效果数据统计流程如下图所示:

Hive源数据表的设计

     根据需求,维度和度量设计如下:

     (1) 我们会从以下共计15个维度来做数据分析:

     日期:比较不同日期的效果趋势。

     平台:PC、M、APP(Android、IPhone)。

     业务分类一级分类:如房产、车。

     业务分类二级分类:如整租房、二手房;二手车、工程车。

     推荐场景:如大类页、列表页、详情页、公共页等。

     推荐位:如二手车详情页同价位、同品牌推荐位。

     排序算法号:不同的机器学习排序算法。

     召回策略号:不同的召回方式。

     前端展现号:在某个推荐场景下可能会同时ABTest两种页面样式。

     推荐规则号:例如在推荐逻辑的最后一步使用不同的去重规则或打散规则。

     自定义维度:d1 ~d5,共五个扩展维度,以便于后续扩展。

     (2) 我们的指标主要是点击,需要统计的度量有:

     点击PV:详情页点击量,称作VPPV

     点击UV:详情页点击用户量

     曝光PV:推荐位(一般是一个列表)曝光次数

     曝光UV:推荐位曝光用户量

     曝光帖子数:推荐位上曝光的帖子数量。

     基于这5个度量,会衍生出一些指标,例如我们关心的点击率、人均VPPV,以及一些中间指标如用户点击百分比、人均曝光帖子数、每次曝光帖子数等,这些中间指标很多人不关注,其实同样重要,往往能帮助我们发现系统的一些问题。相关指标数据实例如下图所示:

      根据原始埋点所包含的信息以及我们设计的维度和度量,我们设计了推荐点击信息表和推荐曝光信息表两张Hive宽表,这两张Hive表中除了维度相关字段,还包括很多属性字段。

      推荐曝光信息表主要字段如下:


CREATE EXTERNAL TABLE `gul_ext_58app_recommend_show`(

 `imei` string,   // 用户id

 `numofshowinfoid` int,  // 曝光帖子数量

 `seqno` string, // 曝光序列号

 `timestamp` string, // 时间戳

  .......,   // 各种属性字段

 `platform` string,       // 平台

 `cate_path1` string,  // 一级分类

 `cate_path2` string,  // 二级分类

 `recname` string,  // 推荐位

 `algo` string,       //算法号

 `recall` string,     // 召回号

 `viewno` string,  // 展示号

 `ruleno` string,   // 规则号

 `d1` string,

 `d2` string,

 `d3` string,

 `d4` string,

 `d5` string)

PARTITIONED BY (

 `date` string,    // 日期

 `scene` string) //  推荐场景

ROW FORMAT DELIMITED

FIELDS TERMINATED BY '\u0001'

COLLECTION ITEMS TERMINATED BY '\u0002'

MAP KEYS TERMINATED BY '\u0003'

LINES TERMINATED BY '\n'

LOCATION

 '/home/xxx/58app_recommend_show_for_hive';



      推荐点击信息表主要字段如下:


CREATE EXTERNAL TABLE `gul_ext_58app_recommend_click`(

 `imei` string,     // 用户id

 `seqno` string,  // 点击序列号

 `timestamp` string, // 时间戳

 `position` string,  // 点击位置

 `infoid` string,     // 点击帖子id

  ......,   // 各种属性字段

 `platform` string,       // 平台

 `cate_path1` string,  // 一级分类

 `cate_path2` string,  // 二级分类

 `recname` string,  // 推荐位

 `algo` string,       //算法号

 `recall` string,     // 召回号

 `viewno` string,  // 展示号

 `ruleno` string,   // 规则号

 `d1` string,

 `d2` string,

 `d3` string,

 `d4` string,

 `d5` string)

PARTITIONED BY (

 `date` string,    // 日期

 `scene` string) //  推荐场景

ROW FORMAT DELIMITED

FIELDS TERMINATED BY '\u0001'

COLLECTION ITEMS TERMINATED BY '\u0002'

MAP KEYS TERMINATED BY '\u0003'

LINES TERMINATED BY '\n'

LOCATION

 '/home/xxx/58app_recommend_click_for_hive';


  

       我们按日期、推荐场景对Hive表做分区。我们的数据一般以天为单位,基本没有查看一周或一月内的汇总数据(如周/月活跃用户数)这种需求,所以按日期分区,kylin也只需构建不同日期的cube segment。由于APP Native埋点历史遗留原因,部分推荐场景的埋点数据格式不统一,不同推荐场景的埋点格式可能不尽相同(题外话:埋点设计是一项非常重要的工作,产品初期设计通用合理的埋点能为后续工作节省大量的人力成本,后续我们将分享如何设计埋点)。在我们的开发工作中,不同推荐场景是由不同的工程同事负责,工程同事需要负责埋点抽取工作,因此为了方便不同工程同事独立抽取各自场景下的埋点,我们将推荐场景也设计成分区。(为什么不安排一个人来独立完成抽取工作?这是因为写 ETL 程序是一个又苦又累的事情,而且技术含量不高,没有这样的人力来独立完成该项工作,所以我们将工作分散至多人。)



如何将原始数据抽取至Hive数据表

       值得一提的是,五八APP每天所有埋点数据都集中在HDFS上一个目录下,数据非常庞大,而我们不同推荐场景下的埋点抽取都是一个独立的MR作业,若每次都从原始数据抽取,将会消耗大量的集群资源。因此,我们设计了一个中间Hive表,采用两个步骤得到最终的Hive数据表:第一步,基于曝光和点击埋点的通用字段,运行一个MR作业解析原始埋点数据,得到中间表,其实是原始埋点的一个子集;第二步,基于中间表,多个MR抽取作业来解析不同场景的埋点,将结果数据追加至最终Hive数据表。这样,便大大提高了抽取效率。抽取流程如下图所示:


Kylin Cube 设计以及维度优化

      基于Hive源数据表,我们创建了曝光和点击两个Cube。

      (1) 点击Cube:

      事实表:上述设计的推荐点击信息表 gul_ext_58app_recommend_click。

      维表:没有维表,直接利利用事实表中的字段做维度。

      维度:date(日期)、scene(推荐场景)、platform(平台)、cate_path1(一级分类)、cate_path2(二级分类)、recname(推荐位)、algo(算法号)、recall(触发号)、viewno(展现号)、ruleno(规则号)、d1、d2、d3、d4、d5(扩展维度)。

      度量:

    • count(1),即点击PV;

    • count(distinct imei),即点击UV。

      Cube维度优化:

    • 强制维度:date      

    • 层级维度:

      • cate_path1→cate_path2  分类层级

      • scene→recname→algo    场景层级

    • 组合维度:(d1,d2,d3,d4,d5)

      (2) 曝光Cube:

      事实表:上述设计的推荐曝光信息表 gul_ext_58app_recommend_show。

      维度:和点击Cube的维度相同。

      度量:

    • count(1),即曝光PV;

    • count(distinct imei),即曝光UV。

    • sum(numofinfoid),即曝光帖子数。

      Cube维度优化和点击Cube维度优化相同。


维度优化很重要

      下面我们来解释一下曝光和点击Cube的几处优化:

       第1处优化:我们在查询数据时,总是会带日期维度,所以我们将日期date作为强制维度,这会使得Cube中的所有cuboid对应的维度集合都包含date,即所有的预查询结果都包含date的特定值。

       第2处优化:我们的业务分类是有层次关系的,二级分类 cate_path2 是一级分类 cate_path1 的下级,查询时若cate_path2出现,cate_path1也会一并出现,所以我们将 cate_path1→cate_path2 设计为一个二层的层级维度,这会使得Cube中所有的cuboid对应的维度集合在包含cate_path2的时候,必定也包含cate_path1。

       第3处优化:在我们的业务中,推荐场景其实也有层次关系,算法号 algo 是推荐位 recname 的下级,推荐位 recname 是 推荐场景 scene的下级,因此,我们将 scene->recname->algo 设计为一个三层的层级维度。

       第4处优化:d1、d2、d3、d4、d5是自定义维度,是作为扩展字段,实事表里大部分数据这五个字段是空值,小部分数据这五个字段的其中某几项有值。如果将他们作为普通维度,那么我们大约有 (2^5-1)/(2^5) = 31/32 的预查询结果是无意义的,而将 (d1,d2,d3,d4,d5) 作为组合维度的话,无意义的预查询结果会降至约 (2^1-1)/(2^1) = 1/2,因此将(d1,d2,d3,d4,d5)作为组合维度。这会使得Cube中所有cuboid对应的维度集合同时包含这5个维度或同时不包含这5个维度。在查询那小部分自定义维度有意义的数据时,SQL中出现这五个维度中的某几个,这会引起聚合计算,使得查询变的稍慢,但我们认为这是值得的,这算是在时间和空间中做的一个权衡。

       经过这四处优化,我们的维度如下图所示:


       最终共包含4个普通维度、1个强制维度、1个层次为2的层级维度、1个层次为3的层级维度、1个维数为5的组合维度。最终cuboid的数量为 (2^4)*1*(2+1)*(3+1)*2 = 384,而不做Cube优化的cuboid数量是 2^15 = 32768,可想而知,维度优化有多重要。


Kylin Cube 数据可视化

       为了直观的查看推荐效果数据,我们还需要对效果数据进行可视化,以图和表的形式展示数据。我们调研了一些开源可视化工具,部分不满足我们的需求,部分部署麻烦,最后我们自主开发了数据展示模块。我们自主设计了前端页面和后台查询接口,在后台查询接口中调用了Kylin 的 REST API来获取数据。下图是一个可视化页面实例。




4 总结和展望

       本文主要介绍了Kylin的基本概念和原理,以及我们如何利用Kylin来完成推荐系统的效果评价。然而,当前是基于批量历史数据的多维分析,我们的实时数据统计仍旧采用的是传统的计算模式,下一步我们将接入实时多维分析。

       后续我们将分享通用推荐平台设计、个性化Push、机器学习通用特征框架、埋点设计、大规模机器学习应用实践方面的文章,敬请期待。




作者简介

      唐汉英,五八集团 TEG 智能推荐部 2017届校招实习生,湖南科技大学大四在读,校ACM战队主力成员,曾三次荣获ACM亚洲区域赛铜奖,现从事推荐系统数据和算法开发工作。

      詹坤林,五八集团 TEG 智能推荐部负责人、算法架构师,前腾讯高级工程师,从事推荐算法和架构设计工作。


------ 请长按二维码关注我们 -----


    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存