文章推荐系统(三) | 构建离线文章画像

前面我们已经计算得到tfidf的结果‘tfidf_keywords_values’和textrank的结果‘textrank_keywords_values’。
下面要计算基于tfidftextrank计算离线文章画像

所谓文章画像,就是给文章定义一些主题词和关键词,用来代表这篇文章。
关键词和主题词最大的区别在于

  • 主题词是经过规范化处理的。
  • 关键词是文章中权重较高的词。

这个项目中,我们采用tfidf和textrank两种方式得到了每篇文章两组权重前20的词组,对于如何确定主题词和关键词,我们处理的方式是:
1) 关键词:textrank处理的topN个词,其权重乘以对应词的逆文档频率(较少个别意义不大的词在多个文档中出现导致权重过高)
2) 主题词:textrank和tfidf计算结果的共同出现的词

一、文章关键词

因为我们是将每篇文章的textrank处理得到的topN个词乘以idf,消除在多篇文章出现的词带来的权重影响,所以要分别加载textrank_keywords_values和idf_keywords_values

# 加载idf逆文档频率
idf = ktt.spark.sql("select * from idf_keywords_values") # 
# 加载textrank_keywords_values
textrank_keywords_df = ktt.spark.sql("select * from textrank_keywords_values")
# 重命名idf的keyword列为keyword1
idf = idf.withColumnRenamed("keyword", "keyword1")   

# 按照keyword关键词作连接,相当于在textrank结果右边补充对应词的逆文档频率
result = textrank_keywords_df.join(idf,textrank_keywords_df.keyword==idf.keyword1)

# 利用withcolumn方法新增‘weights’列,数据=textrank词权重X该词的idf,最后只选择需要的列
keywords_res = result.withColumn("weights", result.textrank * result.idf).select(["article_id", "channel_id", "keyword", "weights"])

文章推荐系统(三) | 构建离线文章画像_第1张图片
以上计算已经得到最终的结果,但是每篇文章的每个关键词和权重都是单独一列,因此利用文章id进行聚合,将关键词结果放在一起,让结果以字典形式展现

# spark sql的dataframe注册临时表temptable,方便用sql语句操作
keywords_res.registerTempTable("temptable")  

# 根据文章id进行分组,一篇文章的所有textrank关键词用collect_list聚合成list,weights同理
merge_keywords = ktt.spark.sql("select article_id, min(channel_id) channel_id, collect_list(keyword) keywords, collect_list(weights) weights from temptable group by article_id")

# 定义函数,实现:单独聚合的keyword和weight如何成为keyword:weight的字典形式
def _func(row):
    return row.article_id, row.channel_id, dict(zip(row.keywords, row.weights))

# 调用函数,合并关键词权重合并成字典
keywords_info = merge_keywords.rdd.map(_func).toDF(["article_id", "channel_id", "keywords"])

文章推荐系统(三) | 构建离线文章画像_第2张图片

二、文章主题词

前面我们计算完了关键词,接下来我们将 TF-IDF 和 TextRank 的共现词作为主题词,将 TF-IDF 权重表 tfidf_keywords_values 和 TextRank 权重表 textrank_keywords_values 进行关联,并利用 collect_set() 对结果进行去重,即可得到 TF-IDF 和 TextRank 的共现词,即主题词

# 利用inner join取两个结果的keyword交集
topic_sql =  select t.article_id article_id2, collect_set(t.keyword) topics 
		from tfidf_keywords_values t                 
		inner join textrank_keywords_values r
              where t.keyword=r.keyword
   		group by article_id2
              
article_topics = ktt.spark.sql(topic_sql)

article_topics 结果如下所示,topics 即为每篇文章的主题词列表

文章推荐系统(三) | 构建离线文章画像_第3张图片

三、文章画像

最后,将主题词结果和关键词结果合并,即为文章画像,保存到表 article_profile

# 通过文章id,将关键词和主题词的结果必定
article_profile = keywords_info.join(article_topics,keywords_info.article_id==article_topics.article_id2).select(["article_id", "channel_id", "keywords", "topics"])

# 结果写入hive数仓的表
articleProfile.write.insertInto("article_profile")

到hive中检查一下表

hive> select * from article_profile limit 1;
OK
26      17      {
     "策略":0.3973770571351729,"jpg":0.9806348975390871,"用户":1.2794959063944176,"strong":1.6488457985625076,"文件":0.28144603583387057,"逻辑":0.45256526469610714,"形式":0.4123994242601279,"全自":0.9594604850547191,"h2":0.6244481634710125,"版本":0.44280276959510817,"Adobe":0.8553618185108718,"安装":0.8305037437573172,"检查更新":1.8088946300014435,"产品":0.774842382276899,"下载页":1.4256311032544344,"过程":0.19827163395829256,"json":0.6423301791599972,"方式":0.582762869780791,"退出应用":1.2338671268242603,"Setup":1.004399549339134}   ["Electron","全自动","产品","版本号","安装包","检查更新","方案","版本","退出应用","逻辑","安装过程","方式","定性","新版本","Setup","静默","用户"]
Time taken: 0.322 seconds, Fetched: 1 row(s)

四、Apscheduler 定时更新

定义离线更新文章画像的方法,首先合并最近一个小时的文章信息,接着计算每个词的 TF-IDF 和 TextRank 权重,并根据 TF-IDF 和 TextRank 权重计算得出文章关键词和主题词,最后将文章画像信息保存到 Hive

def update_article_profile():
    """
    定时更新文章画像
    :return:
    """
    ua = UpdateArticle()
    # 合并文章信息
    sentence_df = ua.merge_article_data()
    if sentence_df.rdd.collect():
        textrank_keywords_df, keywordsIndex = ua.generate_article_label()
        ua.get_article_profile(textrank_keywords_df, keywordsIndex)

利用 Apscheduler 添加定时更新文章画像任务,设定每隔 1 个小时更新一次

from apscheduler.schedulers.blocking import BlockingScheduler
from 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)

scheduler.start()

利用 Supervisor 进行进程管理,配置文件如下

[program:offline]
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
command=/miniconda2/envs/reco_sys/bin/python /root/toutiao_project/scheduler/main.py
directory=/root/toutiao_project/scheduler
user=root
autorestart=true
redirect_stderr=true
stdout_logfile=/root/logs/offlinesuper.log
loglevel=info
stopsignal=KILL
stopasgroup=true
killasgroup=true

你可能感兴趣的:(文章推荐系统(三) | 构建离线文章画像)