基于hadoop的商品推荐系统_新闻个性化推荐系统源代码之基于内容特征的离线召回...

相比于协同过滤来说,基于内容的召回会简单很多,主要思路就是召回用户点击过的文章的相似文章,通常也被叫做 u2i2i。

离线召回

首先,读取用户历史行为数据,得到用户历史点击过的文章

spark.sql('use profile')user_article_basic = spark.sql("select * from user_article_basic")user_article_basic = user_article_basic.filter('clicked=True')

user_article_basic 结果如下所示

504f78894d744a8443969945acb3ea57.png

user_article_basic

接下来,遍历用户历史点击过的文章,获取与之相似度最高的 K 篇文章即可。可以根据之前计算好的文章相似度表 article_similar 进行相似文章查询,接着根据历史召回结果进行过滤,防止重复推荐。最后将召回结果按照频道分别存入召回结果表及历史召回结果表

user_article_basic.foreachPartition(get_clicked_similar_article)def get_clicked_similar_article(partition):    """召回用户点击文章的相似文章    """    import happybase    pool = happybase.ConnectionPool(size=10, host='hadoop-master')        with pool.connection() as conn:        similar_table = conn.table('article_similar')        for row in partition:            # 读取文章相似度表,根据文章ID获取相似文章            similar_article = similar_table.row(str(row.article_id).encode(),                                                columns=[b'similar'])            # 按照相似度进行排序            similar_article_sorted = sorted(similar_article.items(), key=lambda item: item[1], reverse=True)            if similar_article_sorted:                # 每次行为推荐10篇文章                similar_article_topk = [int(i[0].split(b':')[1]) for i in similar_article_sorted][:10]                # 根据历史召回结果进行过滤                history_table = conn.table('history_recall')                history_article_data = history_table.cells('reco:his:{}'.format(row.user_id).encode(), 'channel:{}'.format(row.channel_id).encode())                # 将多个版本都加入历史文章ID列表                history_article = []                if len(history_article_data) >= 2:                    for article in history_article_data[:-1]:                        history_article.extend(eval(article))                else:                    history_article = []                # 过滤history_article                recall_article = list(set(similar_article_topk) - set(history_article))                # 存储到召回结果表及历史召回结果表                if recall_article:                    content_table = conn.table('cb_recall')                    content_table.put("recall:user:{}".format(row.user_id).encode(), {'content:{}'.format(row.channel_id).encode(): str(recall_article).encode()})                    # 放入历史召回结果表                    history_table.put("reco:his:{}".format(row.user_id).encode(), {'channel:{}'.format(row.channel_id).encode(): str(recall_article).encode()})

可以根据用户 ID 和频道 ID 来查询召回结果

hbase(main):028:0> get 'cb_recall', 'recall:user:2'COLUMN                     CELLcontent:13                    timestamp=1558041569201, value=[141431,14381, 17966, 17454, 14125, 16174]

最后,使用 Apscheduler 定时更新。在用户召回方法 update_user_recall() 中,增加基于内容的离线召回方法 update_content_recall(),首先读取用户行为日志,并筛选用户点击的文章,接着读取文章相似表,获取相似度最高的 K 篇文章,然后根据历史召回结果进行过滤,防止重复推荐,最后,按频道分别存入召回结果表及历史召回结果表

def update_user_recall():    """    用户的频道推荐召回结果更新逻辑    :return:    """    ur = UpdateRecall(500)    ur.update_als_recall()    ur.update_content_recall()

之前已经添加好了定时更新用户召回结果的任务,每隔 3 小时运行一次,这样就完成了基于内容的离线召回。

from apscheduler.schedulers.blocking import BlockingSchedulerfrom apscheduler.executors.pool import ProcessPoolExecutor# 创建scheduler,多进程执行executors = {    'default': ProcessPoolExecutor(3)}scheduler = BlockingScheduler(executors=executors)# 添加一个定时运行文章画像更新的任务, 每隔1个小时运行一次scheduler.add_job(update_article_profile, trigger='interval', hours=1)# 添加一个定时运行用户画像更新的任务, 每隔2个小时运行一次scheduler.add_job(update_user_profile, trigger='interval', hours=2)# 添加一个定时运行用户召回更新的任务,每隔3小时运行一次scheduler.add_job(update_user_recall, trigger='interval', hours=3)# 添加一个定时运行特征中心平台的任务,每隔4小时更新一次scheduler.add_job(update_ctr_feature, trigger='interval', hours=4)scheduler.start()

在线召回

前面我们实现了基于内容的离线召回,接下来我们将实现基于内容的在线召回。在线召回的实时性更好,能够根据用户的线上行为实时反馈,快速跟踪用户的偏好,也能够解决用户冷启动问题。离线召回和在线召回唯一的不同就是,离线召回读取的是用户历史行为数据,而在线召回读取的是用户实时的行为数据,从而召回用户当前正在阅读的文章的相似文章。

首先,我们通过 Spark Streaming 读取 Kafka 中的用户实时行为数据,Spark Streaming 配置如下

from pyspark import SparkConffrom pyspark.sql import SparkSessionfrom pyspark import SparkContextfrom pyspark.streaming import StreamingContextfrom pyspark.streaming.kafka import KafkaUtilsfrom setting.default import DefaultConfigimport happybaseSPARK_ONLINE_CONFIG = (        ("spark.app.name", "onlineUpdate"),        ("spark.master", "yarn"),        ("spark.executor.instances", 4)    )KAFKA_SERVER = "192.168.19.137:9092"# 用于读取hbase缓存结果配置pool = happybase.ConnectionPool(size=10, host='hadoop-master', port=9090)conf = SparkConf()conf.setAll(SPARK_ONLINE_CONFIG)sc = SparkContext(conf=conf)stream_c = StreamingContext(sc, 60)# 基于内容召回配置,用于收集用户行为similar_kafkaParams = {"metadata.broker.list": DefaultConfig.KAFKA_SERVER, "group.id": 'similar'}SIMILAR_DS = KafkaUtils.createDirectStream(stream_c, ['click-trace'], similar_kafkaParams)

Kafka 中的用户行为数据,如下所示

{"actionTime":"2019-12-10 21:04:39","readTime":"","channelId":18,"param":{"action": "click", "userId": "2", "articleId": "116644", "algorithmCombine": "C2"}}

接下来,利用 Spark Streaming 将用户行为数据传入到 get_similar_online_recall() 方法中,这里利用 json.loads() 方法先将其转换为了 json 格式,注意用户行为数据在每条 Kafka 消息的第二个位置

SIMILAR_DS.map(lambda x: json.loads(x[1])).foreachRDD(get_similar_online_recall)

接着,遍历用户行为数据,这里可能每次读取到多条用户行为数据。筛选出被点击、收藏或分享过的文章,并获取与其相似度最高的 K 篇文章,再根据历史召回结果表进行过滤,防止重复推荐,最后,按频道分别存入召回结果表及历史召回结果表

def get_online_similar_recall(rdd):    """    获取在线相似文章    :param rdd:    :return:    """    import happybase    topk = 10    # 初始化happybase连接    pool = happybase.ConnectionPool(size=10, host='hadoop-master', port=9090)    for data in rdd.collect():        # 根据用户行为筛选文章        if data['param']['action'] in ["click", "collect", "share"]:            with pool.connection() as conn:                similar_table = conn.table("article_similar")                # 根据用户行为数据涉及文章找出与之最相似文章(基于内容的相似)                similar_article = similar_table.row(str(data["param"]["articleId"]).encode(), columns=[b"similar"])                similar_article = sorted(similar_article.items(), key=lambda x: x[1], reverse=True)  # 按相似度排序                if similar_article:                    similar_article_topk = [int(i[0].split(b":")[1]) for i in similar_article[:topk]] # 选取K篇作为召回推荐结果                    # 根据历史召回结果进行过滤                    history_table = conn.table('history_recall')                    history_article_data = history_table.cells(b"reco:his:%s" % data["param"]["userId"].encode(), b"channel:%d" % data["channelId"])                    # 将多个版本都加入历史文章ID列表                    history_article = []                    if len(history_article_data) >1:                        for article in history_article_data[:-1]:                            history_article.extend(eval(article))                    else:                        history_article = []                    # 过滤history_article                    recall_article = list(set(similar_article_topk) - set(history_article))                    # 如果有召回结果,按频道分别存入召回结果表及历史召回结果表                    if recall_article:                        recall_table = conn.table("cb_recall")                        recall_table.put(b"recall:user:%s" % data["param"]["userId"].encode(), {b"online:%d" % data["channelId"]: str(recall_article).encode()})                        history_table.put(b"reco:his:%s" % data["param"]["userId"].encode(), {b"channel:%d" % data["channelId"]: str(recall_article).encode()})                conn.close()

可以根据用户 ID 和频道 ID 来查询召回结果

hbase(main):028:0> get 'cb_recall', 'recall:user:2'COLUMN                     CELLonline:13                    timestamp=1558041569201, value=[141431,14381, 17966, 17454, 14125, 16174]

创建 online_update.py,加入基于内容的在线召回逻辑

if __name__ == '__main__':    ore = OnlineRecall()    ore.update_content_recall()    stream_sc.start()    _ONE_DAY_IN_SECONDS = 60 * 60 * 24    try:        while True:            time.sleep(_ONE_DAY_IN_SECONDS)    except KeyboardInterrupt:        pass

利用 Supervisor 进行进程管理,并开启实时运行,配置如下,其中 environment 需要指定运行所需环境

[program:online]environment=JAVA_HOME=/root/bigdata/jdk,SPARK_HOME=/root/bigdata/spark,HADOOP_HOME=/root/bigdata/hadoop,PYSPARK_PYTHON=/miniconda2/envs/reco_sys/bin/python ,PYSPARK_DRIVER_PYTHON=/miniconda2/envs/reco_sys/bin/python,PYSPARK_SUBMIT_ARGS='--packages org.apache.spark:spark-streaming-kafka-0-8_2.11:2.2.2 pyspark-shell'command=/miniconda2/envs/reco_sys/bin/python /root/toutiao_project/reco_sys/online/online_update.pydirectory=/root/toutiao_project/reco_sys/onlineuser=rootautorestart=trueredirect_stderr=truestdout_logfile=/root/logs/onlinesuper.logloglevel=infostopsignal=KILLstopasgroup=truekillasgroup=true
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值