基于Neo4j的个性化Pagerank算法文章推荐系统实践
新版的Neo4j图形算法库(algo)中增加了个性化Pagerank的支持,我一直想找个有意思的应用来验证一下此算法效果。最近我看Peter Lofgren的一篇论文《高效个性化Pagerank算法》(Efficient Algorithms for Personalized PageRank)(https://arxiv.org/pdf/1512.04633.pdf),在论文中,有一个比较有趣的示例:
我们想在论文引用网络中进行个性化搜索的尝试,但是要怎样设置个性化PageRank的参数,才能得到不同的排序结果?论文引用数据采用Citeseer检索中开放的。我们计划创建一个论文查询应用,用户输入一个关键词和一个作者名称,得到所有包含此关键词的论文,排序是从输入作者的角度去考虑。对于每一位作者,其所有论文都给以相同的权重,然后再使用个性化PageRank对关键词搜索出来的论文进行排序。例如,关键词“entropy”对于不同的作者有不同的含义,这样,我们就可以从不同的角度去比较关键词“entropy”搜索出来的结果。
接下来,我们就使用Neo4j来重建这个场景
前提
- Neo4j
- Neo4j图像库(algo)
- Neo4jAPOC库
- Graphaware的NLP插件
我们需要下载所有插件并做如下配置:
dbms.unmanaged_extension_classes=com.graphaware.server=/graphaware
com.graphaware.runtime.enabled=true
com.graphaware.module.NLP.1=com.graphaware.nlp.module.NLPBootstrapper
dbms.security.procedures.whitelist=ga.nlp.*,algo.*,apoc.*
dbms.security.procedures.unrestricted=apoc.*,algo.*
apoc.import.file.enabled=true
图模型
从上图可见,我们的模型很简单,模型里的结点分为两类,标签分别为Author和Article,每个Author节点有一个或多个到Article节点的AUTHOR关系,同时,Article节点与其他Article节点还有REFERENCE关系。
为了优化请求,此图模型还需要定义一些索引。分别在Article节点的index属性和Author节点的name属性上建立唯一约束。
CALL apoc.schema.assert(
{},
{Article:['index'],Author:['name']})
数据导入
我们使用aminer.org网站上提供的论文引用数据(https://static.aminer.cn/lab-datasets/citation/dblp.v10.zip),这是此数据的最新版本,最重要的是他以json方式存储的。
关于此数据库的更多信息可以看这篇论文《ArnetMiner:学术社交网络的提取与挖掘》(http://keg.cs.tsinghua.edu.cn/jietang/publications/KDD08-Tang-et-al-ArnetMiner.pdf)
译者言:《ArnetMiner:学术社交网络的提取与挖掘》一文的第一作者是清华大学唐杰教授
将数据导入到Neo4j中分为两步,第一步导入所有论文及他们的作者,第二步建立这些论文的引用关系。
导入数据我们使用 apoc.periodic.iterate 进行批量导入。
导入论文及作者
CALL apoc.periodic.iterate(
'UNWIND ["dblp-ref-0.json","dblp-ref-1.json","dblp-ref-2.json","dblp-ref-3.json"] as file
CALL apoc.load.json("file:///neo4j/import/" + file)
yield value return value',
'MERGE (a:Article{index:value.id})
ON CREATE SET a += apoc.map.clean(value,["id","authors","references"],[0])
WITH a,value.authors as authors
UNWIND authors as author
MERGE (b:Author{name:author})
MERGE (b)-[:AUTHOR]->(a)'
,{batchSize: 10000, iterateList: true})
建立引用关系
CALL apoc.periodic.iterate(
'UNWIND ["dblp-ref-0.json","dblp-ref-1.json","dblp-ref-2.json","dblp-ref-3.json"] as file
CALL apoc.load.json("file:///neo4j/import/" + file)
yield value return value',
'MERGE (a:Article{index:value.id})
WITH a,value.references as references
UNWIND references as reference
MERGE (b:Article{index:reference})
MERGE (a)-[:REFERENCES]->(b)'
,{batchSize: 10000, iterateList: true})
PageRank算法
PageRank设计之初是用来分析网页重要性的。它主要考虑的是网站拥有的连接个数和质量,如一网站,从reddit首页有一个链接到它,和从我的blog有一个链接到它,那么这两个链接的结果就完全不同了。
而这样一过程很容易应用到论文的引用网络上,论文的引用可以视为一篇文章对另一篇文章投了一个“赞成”票,而哪篇文章的“赞成”票最多?这正是PageRank最擅长解决的问题。
使用PageRank算法在全球论文引用网络上可以找到在图中最重要的文章和最有影响力的文章。
运行PageRank并把结果存储到结点的属性中
CALL algo.pageRank('Article', 'REFERENCES')
通过pagerank得到最重要的文章
MATCH (a:Article)
RETURN a.title as article,
a.pagerank as score
ORDER BY score DESC
LIMIT 10
运行结果如下:
自然语言处理(NLP)
如果我们要通过关键词来推荐文件,那么就需要从图中提取关键词。这里要感谢Graphaware的NLP插件,让这一过程非常简单,即使你完全不了解NLP算法也可以做NLP相关工作。
NLP过程将会在我们的图模型上增加一些节点和关系,具体如下图所示:
定义NLP模型
为了优化NLP处理,这里需要定义一些特殊的约束和索引。
CALL ga.nlp.createSchema()
增加处理管道
定义一些处理管道的配置,关于处理管道的更多信息见这里(https://github.com/graphaware/neo4j-nlp#pipelines-and-components)
CALL ga.nlp.processor.addPipeline({
textProcessor: 'com.graphaware.nlp.processor.stanford.StanfordTextProcessor',
name: 'defaultPipeline',
threadNumber: 4
processingSteps: {tokenize: true,
ner: true,
dependency: false}})
设置默认管道
CALL ga.nlp.processor.pipeline.default('defaultPipeline')
文本标注
原始的文本被拆成了单词、段落和函数。这里对文本的分析还仅仅只是一个开始。
如果想了解更多关于文本标注,推荐你看Christophe Willemsen写的这篇文章《用Neo4j和NLP插件逆向工程书籍存储》(https://graphaware.com/neo4j/2017/07/24/reverse-engineering-book-stories-nlp.html)
CALL apoc.periodic.iterate(
"MATCH (n:Article) WHERE exists (n.title) RETURN n",
"CALL ga.nlp.annotate({text: n.title, id: id(n)})
YIELD result MERGE (n)-[:HAS_ANNOTATED_TEXT]->(result)",
{batchSize:1, iterateList:true})
关键词提取
TextRank算法是一种相对简单、无监督的文本摘要方法,其可以直接进行主题提取。它的目标就是运用检索关键词及构建词共现关系图,得到对文档具有描述性的关键短语,而PageRank算法则对词的重要性进行排序。
---取之《使用图进行高效无监督关键词提取》(https://graphaware.com/neo4j/2017/10/03/efficient-unsupervised-topic-extraction-nlp-neo4j.html)
CALL apoc.periodic.iterate(
"MATCH (a:AnnotatedText) RETURN a",
"CALL ga.nlp.ml.textRank({annotatedText: a}) YIELD result
RETURN distinct 'done' ",
{batchSize:1,iterateList:true}
获取文章标题中出现次数最多的10个关键词
MATCH (k:Keyword)-[:DESCRIBES]->()
WHERE k.numTerms > 1
RETURN k.value as Keyphrase,
count(*) AS n_articles
ORDER BY n_articles DESC
LIMIT 10
结果如下:
最基本的文章推荐
如果你跟着本文一步一步执行下来,那么你现在已经有了一个最基本的基于PageRank分数和NLP关键词提取的文章推荐系统。
关键词“social networks”的前十推荐文章
MATCH (k:Keyword)-[:DESCRIBES]->()<-[:HAS_ANNOTATED_TEXT]-(a:Article)
WHERE k.value = "social networks"
RETURN a.title as title, a.pagerank as p
ORDER BY p DESC
LIMIT 10
结果如下:
个性化PageRank算法
个性化PageRank是从一个或多个源节点的视角给出其他节点的pagerank分。
我们再计算一次pagerank分数,但这次我们把描述中带有关键词“social networks”的文章作为源节点。
MATCH (k:Keyword)-[:DESCRIBES]->()<-[:HAS_ANNOTATED_TEXT]-(a:Article)
WHERE k.value = "social networks"
WITH collect(a) as articles
CALL algo.pageRank.stream('Article', 'REFERENCES', {sourceNodes: articles})
YIELD nodeId, score
WITH nodeId,score order by score desc limit 10
MATCH (n) where id(n) = nodeId
RETURN n.title as article, score
可以看到Sergey Brin和Larry Page所著的《大型超文本搜索引擎解析》(http://infolab.stanford.edu/pub/papers/google.pdf) 排在第一位。因此,可以看出,谷歌早期在图和PageRank方面的研究对社交网络方面有着巨大的影响。
个性化的推荐系统
需要再次重申,本文的目标是要重现这个场景
关键词“entropy”对于不同的人意味着不同的东西,我们希望从不同的角度还比较关键词“entropy"的结果。
首先我们找到某一作者的所有文章,这些文章将会作为个性化Pagerank的源节点。接着,我们运行pagerank算法并投影关键词”entropy“描述的文章节点,同时也投影这些文章节点之间的REFERENCES关系。
我们可以通过cypher投影语句过滤掉不需要的关系
只有在源节点和目标节点都被节点查询语句中所描述时,其在关系查询语句的关系才会被投影。源节点和目标节点任一个不在节点查询语句中描述时,则此关系会被忽略。
推荐示例
下面给出的是Jose C. Principe视角下搜索关键词“entropy”所得到的推荐文章。
MATCH (a:Article)<-[:AUTHOR]-(author:Author)
WHERE author.name="Jose C. Principe"
WITH collect(a) as articles
CALL algo.pageRank.stream(
'MATCH (a:Article)-[:HAS_ANNOTATED_TEXT]->()<-[:DESCRIBES]-(k:Keyword)
WHERE k.value contains "entropy" RETURN distinct id(a) as id',
'MATCH (a1:Article)-[:REFERENCES]->(a2:Article)
RETURN id(a1) as source,id(a2) as target',
{sourceNodes: articles,graph:'cypher'})
YIELD nodeId, score
WITH nodeId,score order by score desc limit 10
MATCH (n) where id(n) = nodeId
RETURN n.title as article, score
HongWang视角下搜索关键词“entropy”所得到的推荐文章
MATCH (a:Article)<-[:AUTHOR]-(author:Author)
WHERE author.name="Hong Wang"
WITH collect(a) as articles
CALL algo.pageRank.stream(
'MATCH (a:Article)-[:HAS_ANNOTATED_TEXT]->()<-[:DESCRIBES]-(k:Keyword)
WHERE k.value contains "entropy" RETURN distinct id(a) as id',
'MATCH (a1:Article)-[:REFERENCES]->(a2:Article)
RETURN id(a1) as source,id(a2) as target',
{sourceNodes: articles,graph:'cypher'})
YIELD nodeId, score
WITH nodeId,score order by score desc limit 10
MATCH (n) where id(n) = nodeId
RETURN n.title as article, score
结论
正如我们所料,从两位作者的不同视角进行搜索,得到的推荐结果也是不一样的。
Neo4j本身很强大,在特定的领域内使用相应的插件时,他会变的更强大。
基于Neo4j的个性化Pagerank算法文章推荐系统实践的更多相关文章
- elasticsearch算法之推荐系统的相似度算法(一)
一.推荐系统简介 推荐系统主要基于对用户历史的行为数据分析处理,寻找得到用户可能感兴趣的内容,从而实现主动向用户推荐其可能感兴趣的内容: 从物品的长尾理论来看,推荐系统通过发掘用户的行为,找到用户的个 ...
- 推荐系统实践 0x06 基于邻域的算法(1)
基于邻域的算法(1) 基于邻域的算法主要分为两类,一类是基于用户的协同过滤算法,另一类是基于物品的协同过滤算法.我们首先介绍基于用户的协同过滤算法. 基于用户的协同过滤算法(UserCF) 基于用户的 ...
- 推荐系统实践 0x07 基于邻域的算法(2)
基于邻域的算法(2) 上一篇我们讲了基于用户的协同过滤算法,基本流程就是寻找与目标用户兴趣相似的用户,按照他们对物品喜好的对目标用户进行推荐,其中哪些相似用户的评分要带上目标用户与相似用户的相似度作为 ...
- 14、RALM: 实时 look-alike 算法在推荐系统中的应用
转载:https://zhuanlan.zhihu.com/p/71951411 RALM: 实时 look-alike 算法在推荐系统中的应用 0. 导语 本论文题为<Real-time At ...
- 张洋:浅析PageRank算法
本文引自http://blog.jobbole.com/23286/ 很早就对Google的PageRank算法很感兴趣,但一直没有深究,只有个轮廓性的概念.前几天趁团队outing的机会,在动车上看 ...
- 浅析PageRank算法
很早就对Google的PageRank算法很感兴趣,但一直没有深究,只有个轮廓性的概念.前几天趁团队outing的机会,在动车上看了一些相关的资料(PS:在动车上看看书真是一种享受),趁热打铁,将所看 ...
- PageRank算法第一篇
摘要by crazyhacking: 一 搜索引擎的核心问题就是3个:1.建立资料库,通过爬虫系统实现:2.建立一种数据结构,可以根据关键词找到含有这个词的页面.通过索引系统(倒排索引)实现.3排序系 ...
- (转载)Google的PageRank算法
本文由张洋(敲代码的张洋)投稿于伯乐在线. 本文转载于:http://blog.jobbole.com/23286/ 很早就对Google的PageRank算法很感兴趣,但一直没有深究,只有个轮廓性的 ...
- PageRank算法和谷歌搜索讲解
PageRank算法和谷歌搜索讲解 吴裕雄 PageRank算法实际上就是Google使用它来计算每个网页价值的算法. Google每次的搜索结果都有成百上千万甚至上亿个相关的查询网页链接.如果将所有 ...
随机推荐
- TensorFlow v2.0的基本张量操作
使用TensorFlow v2.0的基本张量操作 from __future__ import print_function import tensorflow as tf # 定义张量常量 a = ...
- 工作日志,Excel导入树结构数据
目录 1. 前言 2. 需求分析 2.1 需求难点 2.2 解决难点 2.3 表格设计 3. 功能实现 3.1 一个分枝 3.2 一个分枝多个树叶 3.3 多个分枝多个树叶 4. 代码事例 4.1 目 ...
- coding++:事务管理 隔离级别
在声明事务时,只需要通过value属性指定配置的事务管理器名即可,例如:@Transactional(value="transactionManagerPrimary"). 除了指 ...
- 最适合新手入门的SpringCloud教程 6—Ribbon负载均衡「F版本」
SpringCloud版本:Finchley.SR2 SpringBoot版本:2.0.3.RELEASE 源码地址:https://gitee.com/bingqilinpeishenme/Java ...
- while与until
一.格式: while 条件测试 :do 循环体 done 二.条件测试 条件测试是指满足条件则会一直执行下去. 比如: let i =0 while i < 100;do echo $i i ...
- Unity 游戏框架搭建 2019 (二十三) 备份与版本号&危险的操作
先列出上一篇的总结: 要做的事情: 备份:导出文件,并取一个合理的名字. 遗留问题: 第八个示例与之前的示例代码重复,功能重复. 约定和规则: 每个示例在 QFramework 目录下创建一个文件夹, ...
- F - F HDU - 1173(二维化一维-思维)
F - F HDU - 1173 一个邮递员每次只能从邮局拿走一封信送信.在一个二维的直角坐标系中,邮递员只能朝四个方向移动,正北.正东.正南.正西. 有n个需要收信的地址,现在需要你帮助找到一个地方 ...
- Python魔法缓存,以数字开始
Python魔法缓存,以数字开始 众所周知,Python是弱类型的脚本语言,变量的定义是不用声明类型的. a = 1 Python所有数字的本质都是对象, 他们是不可改变的数据类型,这意味着改变数字数 ...
- Linux bash篇(二 操作环境)
1.命令执行的顺序 (1).相对/绝对路径 (2).由alias找到的命令 (3).由bash内置的命令 (4).通过$PATH变量找到的第一个命令 2.第一篇讲到的bash在注销后就会无效,如果想保 ...
- 推荐一款超实用的GitHub可视化代码树插件:Octotree
前言 大家在GitHub查看代码的时候,是不是会经常跳转搜索代码!过一段时间就不知道自己跑到哪里了!有了这款工具,妈妈再也不用担心我找不到代码位置了! 直接上效果图 插件名称 : octotree 作 ...