用户画像、推荐系统、Flink实时数仓、准实时数仓中,遇到的棘手的问题都有什么?
用户画像
1. 我们在选择如何存储用户标签时,遇到了问题(标签查询速度慢,并且构建不够灵活,标签更新和删除比较麻烦),比如之前用HDFS或者ES存储,后来切换为ClikcHouse,并用BitMap存储,原因如下
针对标签的表示形式,存储方式有很多,结构为`宽表,BitMap` 都可以,存储选择`HDFS,ES,ClickHouse 等` 也都可以,需要衡量的有两点`1.标签构建的灵活性和构建速度 2.标签的查询效率 ` `HDFS [Presot,Impala]:` 标签的增加,删除,更新不友好, 一个小变动,要重写整个`Parquet`, 写放大问题。 查询效率还可以,但是不够优秀。 支持查询并发较小。 `ES:`标签的构建的写入速度一般, 新增和修改标签需要对ES文档结构更新,ES的DSL语法不友好,有一定学习成本。查询效率还算优秀,同时支持高并发。 ES资源占用高,需要较好的硬件配置。 `ClickHouse[BitMap]` 标签可以并行构建,查询效率优秀,标签的增加非常方便,标签的更新和删除可以实现,但是并不高效,并发查询支持比Presto,Impala要好,但同样不支持高并发,能够满足大部分场景需求。注意两点`1. BitMap存储的是用户ID 2. BitMap使用了RoaringBitMap, 解决BitMap空间占用问题,不然1亿这一个数也要占用11.9M空间`
2. 如何构建用户的稠密向量的问题
如果我们直接将用户的标签转换为稀疏向量来存储,对于类别标签使用`one-hot`编码,但这样会出现维度爆炸的问题,向量过于稀疏,向量之间的余弦相似度计算结果基本没有意义,根本无法实现用户相似度的计算。所以就开始思考如何将用户表示为转换为稠密向量,经过调研发现,Word2Vec可以将词转换为稠密向量,同时借助Word2Vec思想,也可以将物品转换为向量Item2Vec,比如将一个Session内,用户购买的物品或者点击的物品列表,看成是一句话,每个物品看成是一个单词,就可以借助Word2Vec的思想将物品转换为稠密向量表示。(这里注意如果是文章,可以使用分词,然后抽取关键词,将词通过Word2Vec转换为向量的方式) ,我们再将用户点击或者购买的物品列表中物品向量加和求平均,就可以得到用户的稠密向量。后来发现通过ALS模型`矩阵分解`的方式也可以得到用户的稠密向量,两者`表达的用户向量含义`是不同的,一个是有浓重的物品属性特征的,一个是有协同特征的向量。但是都可以作为用户的向量表示方式。
推荐系统
1. SparkML Pipline 训练模型通过PMML跨平台部署时字符串转向量的问题
由于我们通过Pipline训练出来的排序模型,模型的输入是之前存入HBase中向量(用户和物品)字符串,当我们使用`jpmml-sparkml` 这个类库去生成PMML模型,进行扩平台部署时,发现无法正常生成PMML。 原因是因为对于字符串转向量这种`transformer操作` jpmml没有支持,我们参照jpmml源码的实现方式,做了自定义transformer的实现。原理是先自定义一个Spark ML的transform,然后再扩展一个jpmml对应的converter即可。
2. 特征向量Load到HBase慢的问题
我们构建出来的用户特征向量和物品特征向量,最终是存储到HBase中的,最初是使用HBase API写入数据,但是太慢了,整个数据的写入要耗费5~6个小时,之后我们`使用了bulkLoad的方式`,直接通过使用Spark生成将数据`生成HFile文件`写入到HDFS,然后使用blukLoad直接生成好的HFile文件mv过去即可,15分钟完成。 更具体点,首先我们把我们将要写入hbase的rdd,按照设定的行键排序,之后将行键和值构造一个HFile的KeyValue结构,设定outputformat 为HFileOutputFormat2,将生成的hfile数据写入到hdfs,之后通过doBulkLoad方法将写到HDFS上hfile数据移动到hbase目录中。(这些项目的代码中都有)
3. 多路召回结果如何如何统一排序的问题
因为我们采用了多种召回算法,比如ItemCF,ALS, 基于热门,基于地域 等召回算法。 没有召回算发的结果集我们是无法直接排序的,因为各个召回算法表达的含义是不同的,最开始不知道该怎么做,因此就是各个召回算法设定一个人为比例去取。 之后学习了解到可以加`一个排序模型`做这个事情,原理就是用户向量和物品向量作为基础特征,用户是否点击物品作为标签,训练一个排序模型(LR),只有将各路召回策略输入排序模型重新排序即可。 # 注意如果你同时说1,3问题,注意顺序
数仓问题
1. Flink Watermark激增的问题
参考:http://coder.yihongyeyan.com/question/7, `里面有watermark 激增的场景说明`。从这上面的我举的例子,你应该知道这种情况发生的原因,是因为我们抽取事件事件直接减去延迟时间造成,解决方式就是我们再抽取watermark时,判断一下事件中的时间和上次watermark的时间,如果两者时间相差很大,我们就不更新watermark或者将watermark加上一个小值就可以了,一般选择不更新。
2. 实时作业和离线作业的资源竞争问题
因为我们统一用Yarn做资源调度,实时作业Flink(Spark Streaming)和离线作业会调度到同一个机器上,集群相对空闲时没什么问题,但是当集群负载较高时,尤其是晚上大批离线任务启动,就会造成我们实时作业的某些Container所在机器负载过高,同时我们实时作业中如果有重计算逻辑,Flink计算不过来,背压产生,Kafka消费延迟,数据积压。解决这个问题的方法是,YARN Label,给YARN管理的机器打上标签,离线和实时分开,提交作业时指定Lable。
3. 实时作业调度集中的问题
问题产生的背景是,当提交一个作业时(Flink,Spark),作业不大,YARN上申请10个Container,发现10个Container都调度到一个节点上,或者大部分调度到一个节点上,几个调度到另一个节点,资源分配倾斜。 这样造成如果我的作业是一个重计算的作业,10Container都在一个节点上,CPU load过高,计算延迟。 当时出现这个问题,比较苦恼,不知道什么原因,也没有search到解决方案,最后只能去看源码了(我们用的是Fair调度器),发现Container的分配策略是在一个NodeManger心跳中尽可能多的分配Container,这是为了提升调度的吞吐,但是源码中有参数可以控制,是否一个心跳允许分配多个Container,以及一次心跳最大分配多少个Container给当前的NodeManager.这个参数Yarn已经暴露给用户了`yarn.scheduler.fair.assignmultiple` 默认是true。`yarn.scheduler.fair.max.assign` 默认是-1,就是无限制。 解决的方式是`yarn.scheduler.fair.max.assign` 设置为一个较小的值,比如2.
更多关于大数据培训的问题,欢迎咨询千锋教育在线名师。千锋教育拥有多年IT培训服务经验,采用全程面授高品质、高体验培养模式,拥有国内一体化教学管理及学员服务,助力更多学员实现高薪梦想。