Auto Byte

专注未来出行及智能汽车科技

微信扫一扫获取更多资讯

Science AI

关注人工智能与其他前沿技术、基础学科的交叉研究与融合发展

微信扫一扫获取更多资讯

图计算的浪漫:图解《长安三万里》

缘起

《长安三万里》在最近上映后,掀起了一股“追唐人风骨,颂唐诗风雅”的热潮。唐人诗歌璀璨如星汉,唐朝诗人涌现如波涛,大家在观影之余想必也想了解一下唐朝诗人之间的一些关联关系。我们也因此准备了这一期的“图解《长安三万里》”,尝试用“图”来帮大家梳理一下这其中错综复杂的关联。

图是什么

图这个结构是用来表示世间万物关联的数据结构,包含节点和边两种基本组成单元,其中节点用来表示万物的实体,而边则用来表示他们之间的关联。我们用“微信”和“淘宝”这两个大家经常使用的软件来举例。在“微信”中,我们每个人都是这种社交网络图上的节点,而当我们扫码加了他人好友之后,我们和他人之间就形成了名为“好友”的边。在“淘宝”购物的时候,我们作为“用户”的节点,而“淘宝“上维护了成千上万的”商品“节点,当我们完成了一单购物,我们和所买的商品之间就形成了名为”购买“的边。

我们用图来分析唐朝诗人的关系,诗人自然就是节点,而诗人之间互为”友人“或者互相”倾慕“等就可以作为诗人之间的边。我们因此创建了这张“唐朝诗人图”:

# gpt_index: 是用chatgpt评估的诗人的知名度,从1分最高到6分最低,以0.5分为最小跨度
# official_rank: 是根据史料得到的诗人在历史上所任最大的官职,从1品最高到10品(无官职),以0.5品为最小跨度
# representative: 为诗人最广为流传的名句,篇幅关系,在此仅展示一句
poet (id, name, gpt_index, official_rank, representative)

# 诗人A与诗人B有密切交往,quotation是交往过程中A赠予B的诗句
# 这个关系是相互的,实际上,(A)-[friendship]->(B),一定有 (B)-[friendship]->(A)
# 这里我们显示地表示了方向,是因为赠诗是有方向的。
(poet: A)-[friendship (quotation)]->(poet: B)

# 诗人A仰慕诗人B
(poet: A)-[worship (quotation)]->(poet: B)

# 诗人A举荐过诗人B
 (poet: A)-[promote]->(poet: B)

这些关联关系的数据(点此下载),很大一部分是参考该文章生成。

我们将他们导入到GraphScope Portal中,可以看到如下的展示:

 


一日看尽长安花(看看诗人自己)

中国文化上下传承千年,但是要论起诗文化,却好像独独凝固在唐朝近三百年的时光里了。如果上天有掌管诗的神明,那么他肯定是在长安街头被迷花了眼,驻足不肯离去了。他先是化身为初唐的天才,过路滕王阁时便“却之不恭”地写道:

g.V().has("name", "王勃").values("representative")
 
 => 落霞与孤与齐飞,秋水共长天一色

随后,他摇身一变从西域胡地款款而来,失意于庙堂之高却笑傲于江湖之远,奋笔疾书:

g.V().has("name", "李白").values("representative")
 
 => 天生我材必有用,千金散尽还复来

他虽留下了诗仙的美名,但是在阅尽人间疾苦和世事沧桑之后,哪怕是神仙也难以不动容,他拂去满面的老泪高呼:

g.V().has("name", "杜甫").values("representative")
 
 => 无边落木萧萧下,不尽长江滚滚来

罢了罢了,苍生福祸,斗转星移,人与这漫原的荒草又有什么分别呢:

g.V().has("name", "白居易").values("representative")
 
 => 离离原上草,一岁一枯荣。野火烧不尽,春风吹又生

他终于累了乏了,转身离去之际,他不忘给中国历史上这段璀璨的时光划下华丽的句点:

g.V().has("name", "李商隐").values("representative")
 
 => 锦瑟无端五十弦,一弦一柱思华年

纵观这唐朝诗人的历程,不恰恰是一篇起承转合的长文,王勃起的头不可一世,气象万千,李白承于盛唐,仙风道骨,如梦似幻,由杜甫转入乱世,乍然国破,民生凋敝,最后这合笔由李商隐来作,妙笔生花,绝妙无双。

盛极而衰,否极泰来,是天道;

乐不思蜀,苦中作乐,是人道。

两个黄鹂鸣翠柳 (看看诗人和好友)

在《长安三万里》华丽的回忆篇幅之中,高适回首了年轻时与众多赫赫有名的诗人交往的历程,其中最重的笔墨,当然是李白和杜甫。高适曾与李白杜甫一起同游大梁古都,此时高适年过四十,还是一名布衣,而李白刚被唐玄宗赐金放还,杜甫还是小辈,寄人篱下。李白和高适,二人神传已久,相互称道对方诗情文韬,可惜当时二人描绘彼此的诗歌并未传世。倒是杜甫这位晚生小辈对于两位前辈的仰慕之意跃然纸上:

g.V().has("name", "杜甫").as("a")
.outE("worship").as("e")
.inV().has("name", "李白").as("b")
.select("a", "b", "e").by("name").by("name").by("quotation")

 => {"a": "杜甫", "b": "李白", "e": "白也诗无敌,飘然思不群"}

g.V().has("name", "杜甫").as("a")
.outE("worship").as("e")
.inV().has("name", "高适").as("b")
.select("a", "b", "e").by("name").by("name").by("quotation")

 => {"a": "杜甫", "b": "高适", "e": "忆与高李辈,论交入酒垆"}

安史之乱爆发之后,高适作为讨伐永王叛乱的将领一时青云平步。倒是李白投错永王幕府,以叛国之罪身陷囹圄,此时李白写诗向高适求助:

g.V().has("name", "李白").as("a")
       .outE("friendship").as("e")
     .inV().has("name", "高适").as("b")
     .select("a", "b", "e").by("name").by("name").by("quotation")
 
 => {"a": "李白", "b": "高适", "e": "高公镇淮海,谈笑却妖氛"}

《长安三万里》中,高适承情救出了自己这位老友。然而,据历史所载,正是高适”大义灭亲“将李白投入监狱。另外,高适得了李白这封纯”拍马屁“的书信也并未理会,倒是郭子仪出面向唐肃宗求情使李白大罪得赦。

高、李、杜在大梁古都的这次聚会不失为中国文坛历史上最伟大的聚会,翻遍中国历史,恐怕也只有”孔子适周,论礼与老子“能与之并论。然而,李白乃谪仙人,大概是不通人情的,杜甫尚属小辈,赏识之意多过友情。堪与高适称兄论弟的好友知己,当属边塞诗人王之涣(其实还有另一位,王昌龄,但我们这里先不说了)。

公元732年,高适前来探望王之涣,不过王之涣并不在蓟门,高适因而留诗,王之涣得到后作诗回敬好友,王之涣对高适的友情可见一斑。我们从“唐朝诗人图”中,也找到了这样的链接。

g.V().has("name", "王之涣").as("a")
       .outE("friendship").as("e")
     .inV().has("name", "高适").as("b")
     .select("a", "b", "e").by("name").by("name").by("quotation")
 
 => {"a": "王之涣", "b": "高适", "e": "今日暂同芳菊酒,明朝应作断蓬飞"}

王之涣此时辞官,自由洒脱,放浪形骸,在凉州远眺黄河的时候,诗人千般思绪,百般愁肠,千古名篇响彻云霄:

g.V().has("name", "王之涣").values("representative")
 
 => 羌笛何须怨杨柳,春风不度玉门关

凉州曲一时风靡大唐,在当时已成为广为传唱的名篇。高适听了之后大为赞赏,写诗附和:

g.V().has("name", "高适").as("a")
       .outE("friendship").as("e")
     .inV().has("name", "王之涣").as("b")
     .select("a", "b", "e").by("name").by("name").by("quotation")
 
 => {"a": "高适", "b": "王之涣", "e": "古人吹笛戍楼间,楼上萧条海月闲"}

诗人之间惺惺相惜,遥遥相寄,在这一首又一首的名诗绝句里,历经千年仍鲜活如斯。

九曲黄河万里沙 (看看诗人们更广泛的关系)

从这个章节开始,我们要利用“唐朝诗人图”对唐朝诗人和他们的关系做更深入的探索。为此,我们需要少一点感性的抒发,多一点理性的分析;少一点历史的考据,多一点现世的研判。

找三角形是图中常见的任务,一个节点所关联的三角形越多,意味着这个点在图里越是处于网络的中心。在“唐朝诗人图”中,我们预计有两类诗人会聚集起更多的三角形,一类是交友广泛的,另一类就是官位显赫的。前者与诗人的知名度关联,后者则直接是其为官的品级。根据这种思路,我们在“唐朝诗人图”计算每个诗人所包含的三角形的数量,并和gpt知名度以及他的官职进行比对,如下:

g.V().match(
      as("a").both().as("b"),
      as("a").both().as("c"),
      as("b").both().as("c")
 )
 .where(expr("@a.id > @b.id && @b.id > @c.id" ))
 .groupCount().by(select("a"))
  .select(keys).valueMap("name", "gpt_index", "official_rank").as("poem")
  .select(values).order().by(desc).as("tc")
  .select("poem", "tc")
 
 => 0: {
     "poem": { "杜甫", 1.5, 6.5 }
     "tc":41
 }
 1: {
     "poem": { "刘禹锡", 3.5, 4.0 }
     "tc":24
 }
 2: {
     "poem": { "李白", 1.0, 10.0 }
     "tc":20
 }
 3: {
     "poem": { "岑参", 3.0, 4.0 }
     "tc":16
 }
 4: {
     "poem": { "王维", 2.0, 4.0 }
     "tc":10
 }
 5: {
     "poem": { "高适", 3.5, 3.0 }
     "tc":8
 }

从得到的结果来看,确实如我们预料,三角形计数排名靠前的诗人,要么声名在外,要么位居高位。当然,我们这个结论未必是严肃的,我们只在“大唐诗人图”中包含了和《长安三万里》关联较大的信息,难免有所偏颇。

为了衡量诗人在图中的重要性,另外一种选择是在“大唐诗人图”中跑pagerank算法,这里,pagerank算法是google用来衡量网站上网页的重要程度从而影响搜索排名的重要算法,其核心思想是,“越重要的网页会越容易被其他网页引用”。通过GraphScope Portal或者GraphScope Python SDK,我们可以很方便地在“大唐诗人图”执行pagerank程序,得到的结果如下所示:

由于pagerank算法本来就是用于衡量节点的重要性,所以通过pagerank算法计算得到的每个诗人的重要性确实比上面的三角形的计数在直观上更为准确一些。例如,由于韩愈周边的诗人在“唐朝诗人图”中没有连边(数据缺失),导致韩愈不能出现在任何的三角形中,而韩愈不管是知名度还是官职,在唐朝诗人中都是佼佼者。

欲穷千里目,更上一层楼

图在我们身边无处不在,今天我们使用图带大家观影《长安三万里》,明天大家可以使用图来解决实际的业务问题。商品推荐、安全风控、流量检测、路由计算,乃至前沿的生物制药、蛋白质分析,这些场景都在图相关技术的加持下持续发展。想了解更多前沿图计算的内容,请大家关注开源免费的GraphScope


工程
1
暂无评论
暂无评论~