编者按:本文来自微信公众号"Social Listening与文本挖掘",CDA人工智能学院经授权发布。

楔子

许多年之后,面对书桌上的两句残词,贬居黄州的东坡居士将会回想起,他在故乡眉山见到朱姓老尼的那个遥远的下午。彼时的东坡还不是东坡,还只是一个七岁孩童。有一天,他在家附近偶遇一位年约九十的朱姓老尼。老尼看到苏轼天资聪颖,就跟他聊起自己年轻时的经历,曾跟随师父进入后蜀主孟昶的宫中。一日,天酷热,孟昶和他的妃子花蕊夫人深夜纳凉于摩诃池上。面对此情此景,蜀主即兴赋词一首...老尼将她印象中仅存的打头两句告诉了苏轼。

四十年后,苏轼贬居于黄州,想起这段往事,遗憾于孟昶的词只余两句,突发奇想要将这两句词续写完整。他先猜测出这首词的词牌名---“洞仙歌令”,但要还原整首词作,必须深刻结合写词人当时之心情以及伴随而来的意境。苏轼因而循着仅存的两句词,根据老尼给他的描述,竭力在脑海中还原蜀主当时的创作场景和心境,最终将词续完,成就名作《洞仙歌》:

冰肌玉骨,自清凉无汗。水殿风来暗香满。绣帘开,一点明月窥人,人未寝,倚枕钗横鬓乱。起来携素手,庭户无声,时见疏星渡河汉。试问夜如何,夜已三更,金波淡,玉绳低转。但屈指西风几时来,又不道流年暗中偷换。

以上就是文学史上有名的“东坡续词”,虽说是文学史上的一段佳话,但笔者从中隐约看到了数理思维的影子:

诗词的创作过程有如在求解一个“最优化问题”:

在一定的约束条件下,如诗词要遵守的平仄、押韵、对仗/对偶、五七变式、词谱、情境等,诗词创作者用文字将自己内心的真实感动用语言文字表达出来,在“戴着镣铐在跳舞”的情况下,竭力达到音韵美、精炼美、言辞美、朦胧美、情感美、绘画美和形式美的至臻境界...

此时,诗词之精美和数理之严密是可以完美结合的。

既然诗歌的创作是有规律的,那么,通过一定的数据挖掘手段,我们是能够从中发现一些insight的。

在本文中,笔者循着这个思路,将运用若干文本挖掘方法对手头的诗歌语料库(该诗歌原始语料库地址为https://github.com/Werneror/Poetry)进行深入挖掘和分析,该诗歌语料库的基本统计数据如下:

从上表可以看到,该诗歌语料库中共计近85万余首诗歌,诗歌作者数量达29377位之多;其中,字段包括“题目”、“朝代”、“作者”和“内容(诗歌)”。

为了方便后续的分析,笔者仅取其中的律诗和绝句,且仅取其中的五言和七言,排律(如《春江花月夜》、《长恨歌》等)、杂言(如李白的将进酒)等就不在本文的分析范围之内。

经过数据清洗后,最终得诗504,443,占到原数据库的59.1%。以下分别是清洗后的诗歌数据统计结果和部分样例:

针对上述数据,笔者在本文中主要有两个大目标:

  • 构建一个包含热门题材标签的诗歌语料库,用于后续的诗歌题材分类和诗歌生成任务;

  • 基于上述诗歌语料库的各类文本挖掘和语义分析,以期得到有趣味的发现。

针对上述目标,本文的实现路线图,同时也是本文的行文脉络,如下所示(点击图片可放大查看):

值得注意的是,上述实现路径中,涉及到自然语言处理的两大组成部分,即自然语言理解(分词、语义建模、语义相似度、聚类和分类等)和自然语言生成(诗歌生成和诗歌翻译), 看完也会对自然语言处理有一定的了解。信息量大,请耐心享用~

1 诗歌分词和热词发现

给定一首诗歌文本,在其中随机取一个片段,如何判断这个片段是否是一个有意义的词汇呢?

如果这个片段左右的搭配变化较多、很丰富,同时片段内部的成分搭配很固定,那么,我们可以认为这个片段是一个词汇,比如下图中所示的“摩诘”就是符合这个定义,那么它就是一个词汇。

在具体实施的算法中,衡量片段外部左右搭配的丰富程度的指标叫“自由度”,可以用(左右)信息熵来度量;而片段内部搭配的固定程度叫“凝固度”,可以用子序列的互信息来度量。

在这里,笔者利用Jiayan(甲言)对这54余万首诗歌进行自动分词,在结果中按照词汇出现频率从高到低进行排序,最终从语料库中抽取若干有意义的高频词。其中,词汇的长度从1到4。

抽取结果如下(点击图片可放大查看):

笔者观察其中部分结果,发现一字词、二字词才能算得上一般意义上的词汇,如“不”、“烁”、“岁寒”、“留滞”等 ;三字词和四字词一般是多类词性词汇的组合,严格上讲,应该算作短语或者固定表达,如“随流水”、”云深处”、“人间万事”、“江湖万里”等。但本文为了表述方便,笔者将它们统一称之为词。

下面,笔者分别展示词长从1到4的TOP100的高频词词云(点击图片可放大查看)。

一字高频词中,除去“不”、“无”、“有”这类“虚词”,单看“人山风日天云春花年月水”这11个高频字,暗合了中国天人合一哲学传统,作诗如作画,作诗者是把人放到自然环境、天地岁月这个时空大画卷中,七情六欲、天人感应,诗情画意就由感而生,诗意盎然了

诗画本一律”,古人诚不我欺!

二字高频词中,较为显眼的是“万里”、“千里”,它们描绘出巨大的空间感,在诗歌中经常跟“宏景”“贬谪”、“思乡”、“闺怨”等主题捆绑在一起。

此外,“明月”、“故人”、“白云”、“功名”、“人间”、“平生”和“相逢”等词汇也是横亘古今的热门用语。

三字高频词中,数字的使用很是常见,如“二三子”、“二十四”、“一樽酒”、“二千石”等。其中,最值得一提的是诗人们用数词对时空的描绘:表达时间跨度的,如“二十年”、“四十年”、“五百年”、“十年前”、“千载后”等;表达空间距离的,如“千里外”、“三百里”、“百尺楼”...古人总是喜欢把自己置身于浩瀚渺茫的时空之中,去思考自己匆匆的人生。正如东坡在《赤壁赋》的感慨:“寄蜉蝣于天地,渺沧海之一粟。哀吾生之须臾,羡长江之无穷!

在四字高频词中,空间方位的词汇较多,如“南北东西”、“江南江北”、“东西南北”等词。因四字词词长较长,像“人间万事”、“千岩万壑”、“明月清风”、“白云深处”、“相逢一笑”等词就拥有较高的信息量,能够还原大部分的诗歌意境了。

2 训练含纳诗歌词汇语义关联性的词嵌入模型

词嵌入模型可以从海量的诗歌文本中自动学习到字词之间的关联关系,据此可实现字词关联度分析、字词相似度分析、聚类分析等任务。

然而,计算机程序不能直接处理字符串形式的文本数据,所以笔者首当其冲的一个步骤就是将诗歌文本数据分词,之后再“翻译”为计算机可以处理的数据形式,这由一个名为“文本向量化”的操作来实现。

先谈分词,它跟前面的高频词挖掘有联系,是后续所有分析任务的起始点。结合前面积累的词库,再基于有向无环词图、句子最大概率路径和动态规划算法对这54万首诗歌进行分词操作。现试举一例:

分词前:

“万物生芸芸,与我本同气。氤氲随所感,形体偶然异。丘岳孰为高,尘粒孰为细。忘物亦忘我,优游何所觊。”

分词后:

['万物', '生', '芸芸', ',', '与', '我', '本', '同', '气', '。','氤氲', '随', '所', '感', ',','形体', '偶然', '异', '。', '丘岳', '孰', '为', '高', ',', '尘', '粒', '孰', '为', '细', '。',                '忘', '物', '亦', '忘我', ',', '优游', '何', '所', '觊', '。']

分词之后再做适当处理就可以“喂给”词嵌入模型(这里是Word2vec)进行训练了。

基于Word2vec词嵌入模型能从大量未标注的文本数据中“学习”到字/词向量,而且这些字/词向量包含了字词之间的语义关联关系(可以是语义相关或句法相关),正如现实世界中的“物以类聚,类以群分”一样,字词可以由它们身边的字(上下文语境)来定义,而Word2vec词嵌入模型恰恰能学习到这种词汇和语境之间的关联性。

其基本原理如下图所示(点击图片可放大查看)

                        

训练完该模型后,将其训练结果投射到三维空间,则是如下景象(点击图片可放大查看)

在训练Word2vec的过程中,模型会从大量的诗歌文本数据中学习到词汇之间的2类关联关系,即聚合关系组合关系

聚合关系:如果词汇A和词汇B可以互相替换,则它们具有聚合关系。换言之,如果词汇A和词汇B含有聚合关系,在相同的语义或者句法类别中可以利用其中一个来替换另一个,但不影响对整个句子的理解。例如,“萧萧”、“潇潇”都是象声词,多用于描述雨声,具有聚合关系,那么“山下兰芽短浸溪,松间沙路净无泥,萧萧暮雨子规啼”中的“萧萧”可以换做“潇潇”。

组合关系:如果词汇A和词汇B可以在句法关系上相互结合,那么它们具有组合关系。例如,“雨打梨花深闭门,忘了青春,误了青春。赏心乐事共谁论?”中的“忘了”和“误了”都和“青春”存在组合关系,都是“动词+名词”的动宾结构。

现在来寻找与“兵燓”存在语义关联性的若干词汇:

结果大都是跟“战争”&“创伤”相关的词汇,语义关联关系捕获能力较强,后续的热门诗歌体裁挖掘任务也会用到词嵌入模型的这个特性。

3 度量诗歌词汇之间的语义关联关系

3.1 利用余弦相似性度量诗歌词汇关联度

度量词汇之间的相似度或者关联度,我们一般会使用两个词汇的词向量之间的余弦值,词向量之间的夹角越小,则余弦值越大,越接近1,则语义相关度越高;反之,相关度越低。如下图所示,展示了“甲兵”、“兵戈”和“烽火”之间的余弦相似度的可视化示意图(点击图片可放大查看)

通过上述词嵌入模型,similarity(“甲兵”,“兵戈”) = 0.75,similarity(“甲兵”,“烽火”) = 0.37,similarity(“兵戈”,“烽火”) = 0.48。则在这三个词汇中,“甲兵”和“兵戈”之间的语义相关度最高,其次是“兵戈”和“烽火”,最次的“甲兵”和“烽火”。

这种给一个数值来识别词汇相关不相关的方法优点在于表达简洁、计算高效,比如接下来将要进行的热门诗歌题材发现/聚类。但是,这种词汇相关度的计算没有把词汇之间的相关度的“因果路径”直观的反映出来。

那么,有没有一种直观的方法来展示词汇之间的语义相关性,并且能看到为什么它们是存在这样的关联关系(也就是找到词汇关联路径或者语义演变路径)?

答案是---当然有。

我们需要把这个找寻词汇语义演变路径的任务转换成一个TSP问题(旅行商问题)

3.2 利用A*算法找寻词汇之间的语义演变路径

TSP问题(Traveling Salesman Problem)又译为旅行推销员问题,是数学领域中著名问题之一。假设有一个旅行商人要拜访n个城市,他必须选择所要走的路径,路径的限制是每个城市只能拜访一次,而且最后要回到原来出发的城市。路径的选择目标是要求得的路径路程为所有路径之中的最小值。

回到词汇相关度度量的问题上来,如果我们能在上述训练得到的词嵌入空间中找到两个词汇之间的最短“语义演变”线路,我们就能直观的呈现出这2个词汇之间产生语义关联的“前因后果”。

要实现这个目的,有一个很棒的算法可以实现 --- A*算法(A* search algorithm)

A*算法,也叫A*(A-Star)算法,是一种静态路网中求解最短路径最有效的直接搜索方法,也是解决许多搜索问题的有效算法。算法中的距离估算值与实际值越接近,最终搜索速度越快。下图中(点击图片可放大查看),网状结果即是之前构建的word2vec词嵌入空间,节点是其中分布的词汇,边由字词之间的余弦相关度构成。

笔者基于上面的词嵌入模型,结合A*算法来计算两个词汇之间的最短语义路径,部分结果如下所示(点击图片可放大查看):

在上图的5个词汇对中,“渔樵”和“躬耕”之间的语义距离最短,也就是语义相关度最高,它们之间的语义演变路径也就越短,中间只隔了2个词汇;“燕市”和“宝婺”的语义距离最大,语义相关度最小,二者的语义演变路径隔了12个词汇。

可以看到,语义关联性越弱(distance值越大)的两个词汇之间的最短语义演变路径就越长,反之越短,所以语义距离与语义演变路径长度呈正相关关系,语义关联度与语义演变路径呈负相关关系。

有了前面的词嵌入模型和语义相关度做“铺垫”,后续的热门诗歌题材发现就水到渠成了~

4 用文本聚类进行热门诗歌题材发现

先开宗明义,在本文中,关于“诗歌题材”中的“题材”二字的定义,笔者认为是:

作为诗歌创作材料的社会生活的某些方面,亦特指诗人用以表现作品主题思想的素材,通常是指那些经过集中、取舍、提炼而进入作品的生活事件或生活现象。一言以蔽之,写景、摹物、抒情、记事、明理皆是“题材”。

因为事先不知道这54万余首诗歌中到底会存在多少个题材,所以笔者选取的聚类算法没有预设聚类数这个参数,且兼顾运行效率和节省计算资源,能利用前面训练好的word2vec词嵌入模型和语义关联度计算。此时,有个很好的选择 --- 社区发现算法中的Infomap

4.1 基于社区发现的热门诗歌题材发现

字词是承载诗歌题材的最小语义单元,如“五云山上五云飞,远接群峰近拂堤。若问杭州何处好,此中听得野莺啼”,看到其中的“五云山”和“群峰”,则可以给该诗打上一个“山川巍峨”的题材标签。由此,笔者接下来会基于社区发现算法,结合“词汇簇群--->词汇簇群语义特征--->题材标签”的思路来发现热门诗歌题材。

先说说基于社区发现的大致原理。

我们知道,在社交网络中,每个用户相当于每一个点,用户之间通过互相的关注关系构成了整个线上人际网络。

在这样的网络中,有的用户之间的连接较为紧密,有的用户之间的连接关系较为稀疏。其中连接较为紧密的部分可以被看成一个社区,其内部的节点之间有较为紧密的连接,而在两个社区间则相对连接较为稀疏。

如何去划分上述的社区便称为社区发现的问题。

基于社区发现算法的话题聚类/发现,在于挖掘词汇语义网络中居于头部的大型“圈子”。

将词汇拟人化,词汇之间存在的相似度/关联度可以视为词汇之间的亲密程度,那么,诗歌题材发现任务可以看做是找到不同成员组成的“圈子”,圈子的特性可以根据其中的成员特征来确定,换言之,题材的名称可以根据其中聚合的词汇的内涵来拟定,比如某个词汇簇群中包含“卫霍”、“甲兵”、“征战”等词汇,那么这个题材可以命名为“战争”。示意图如下所示(点击图片可放大查看)

运行社区发现算法后,居于头部的热门题材词汇簇群的可视化呈现如下(点击图片可放大查看)

其中,不同颜色表征不同的题材,字体大小代表其出现频次,词汇之间的距离远近表征其相关程度大小。

经聚类得到634个题材,根据热度(题材下辖词汇数量)的降序排列呈现最终结果,如下所示(点击图片可放大查看)

4.2 甄别热门诗歌题材在这一环节中,笔者的在于根据一些诗歌领域知识,找到上述运行结果中热门题材及其下辖的题材专属性词汇。其中,“题材专属性词汇”的内涵主要有以下两点:

  • 词汇不能再做进一步切割,否则词义会发生变化,比如,“丈夫”在古汉语中的意义是“男子汉”,在一个独立的词汇,若将其切割为“丈”和 “夫”,则原意丧失殆尽;

  • 词汇仅在一个题材中出现,具有排他性,如“杖藜”只出现在“云游四方”这个题材中,不会出现在“金戈铁马”、“对酒当歌”、“悼亡故人”等其他诗歌题材中。

根据笔者在前文中的定义,写景、摹物、抒情、记事、明理皆是“题材”,这里的热门题材甄别采取“抓大放小”的原则。

此外,虽然聚类出的结果较为理想,但还是存在些许噪音,比如,出现少许跟题材相关性不强的词汇、题材区分度较低的词汇、词汇簇群中的词汇过少(如低于10个)等,这些都是需要被刨除掉的情况。

经过笔者的仔细甄别,共甄别出23个热门诗歌题材,分别是山川巍峨、田园躬耕、羁旅思乡、金戈铁马、咏史怀古、咏物抒怀、赠友送别、爱情闺怨、悼亡故人、楼船画舫、花开荼蘼、对酒当歌、骐骥骏马、得道修仙、世事变迁、静悟禅机、壮怀激烈、云游四方、黯然神伤、星宿璀璨、报效君恩、嘤嘤鸟语、蓑笠纶竿等,当然这些并不是全部的题材,限于笔者学识,仍然有大量题材没有发掘出来。枚举部分结果如下(点击图片可放大查看)

在这一环节,笔者根据对诗歌背景知识的了解,筛选出部分热门诗歌题材,并形成题材对应的关键词规则体系,后续可用于对这54万余首诗歌进行基于关键词的诗歌题材分类。

值得注意的是,由于这一环节挑选关键词过于苛刻,导致数量较少,规则体系不甚健全。所以,在对诗歌语料库进行正式的诗歌题材分类前,笔者需要使用一些“小手段”,对上述热门题材的关键词规则进行扩充

5 根据线性分类器特征延伸关键词

在这里,笔者先利用已得到的热门题材分类体系及其关键词规则给这54万余首诗歌打上题材标签,允许出现同一首诗歌命中多个标签的情形。除去其中未命中题材标签的数据,共计443,589行,其中多数诗歌打上了2个及以上的题材标签。

部分结果如下所示(点击图片可放大查看):

有了带标签的数据以后,笔者将多标签问题转换为单标签问题,再将上述诗歌文本及其对应的标签“喂进”线性分类器,根据线性分类器的权重来找到每个类别下最具代表性的词汇,也就是题材专有性词汇。这里选择线性分类器而不是时下流行的深度学习分类器的原因就在于它的可解释性,能让我们清楚的知道是哪些显著的特征(此处是词汇)让诗歌分到这个题材类别下的。其大致原理如下图所示(点击图片可放大查看):

在笔者测试的众多线性分类器中,即RandomForestClassifier、Perceptron、PassiveAggressiveClassifier、MultinomialNB、RidgeClassifier、SGDClassifier,RidgeClassifier的区分效果最好,其F1_score为0.519,鉴于是词袋模型,语义表示较为简单,且原本是多标签分类任务,这个结果尚可接受。基于RidgeClassifier的特征词汇权重的降序排列结果,可得到上述23个热门诗歌题材分类中的若干题材专有性词汇,部分结果展示如下(点击图片可放大查看):

这样,各个类别各取TOP500词汇,经过笔者的甄别和梳理后,各个题材关键词规则得到了不同程度的扩充,使得该分类标签体系可以较好的辅助完成诗歌题材多标签分类任务,且后续可以结合分类结果做不断的扩充。

基于这个更加完善的诗歌题材分类体系,笔者运行完之后得到58W+行数据,在之前的基础上增加了14W+行数据,数据规模提升明显!

至此,笔者第一个目标,即热门诗歌题材标签语料库构建完毕,后续的文本挖掘任务可以在此基础上进行。

由分类标签及其分类模型反向推导其中最具代表性的特征词汇,这是一个“数据--->规律”的归纳过程,很好的体现了数据驱动思维;而模型将学习归纳得到的“经验”推广到新样本的标签预测任务中,则体现了“规则--->数据”的演绎过程。

【文本挖掘实操】用文本挖掘剖析54万首诗歌(下)