我用Python挖掘了一哈《权力的游戏》中的人物关系

最近在学习自然语言处理,看完了《Python 自然语言处理》这本书,想做一点实践性的练习。之前我在知乎上看见有人用机器学习来做过红楼梦人物关系梳理,于是我便想尝试着做了下《权力的游戏》中人物关系的梳理

背景知识

  • 词向量

    在自然语言处理(natural language processing,NLP)领域,首要任务便是将单词转换为由数字组成的向量,这样计算机才可以进行更进一步处理。下面我们简要介绍一下词向量的表示方法

  1. One-Hot representation

    这是最简单的一种词向量的表示方式。其基本思想便是用一个很长很长的向量来表示一个单词,向量的长度便是字典的长度,该向量由0和1组成,有且仅有一个1,其他全为0,1的位置便是该词在词典中的顺序。

    例如,在某个词典中,'like'的词向量可表示为[0,0,0,1,0,0,...],'hate'的词向量可表示为[0,1,0,0,0,0,...]。可以根据以上向量可知,like在词典中处于第四个位置,hate在词典中处于第二个位置。

    这种向量表示法的优点是直观,易于理解。缺点也是很明显的。首先,会出现维数灾难,词典的长度通常是上万的,一篇简单的文档,例如新闻,也都是上千上万的单词,如果用这种方式来表达一篇文档很容易都达到极高的维度,同时也浪费了很多存储空间。再者就是这种表示方法假设了任意两个词之间都是孤立的存在,无法表达语义相近的两个词之间的关系,比如hate与dislike。

  2. Distributed Representation

    这种表示方法最早是由Hinton提出来的,它克服了one-hot表示方法的缺点。其基本思想是,通过对某种语言中的词语进行训练,获得一个普通向量(例如[0.0268,-0.234,0.5263,...]这种形式,分布较为均匀),其维数通常为50-100(相比于one-hot可是大大减少了)。

    利用这种表示方法,就可以计算不同词之间的相似度,可利用常用的距离计算公式或者相似度计算公式来进行计算。

    训练生成词向量通常采用的是神经网络算法,例如RNN(循环神经网络)。

    在本项目中我们将采用gensim库中的word2vec来训练并获得所需要的词向量。

  • 相似度计算

    在得到每个词的向量过后,计算两个词之间的相似度就很方便了。常见的计算相似度的方法有Euclidean距离,向量余弦相似度,Jaccard方法。这个比较简单,不在此赘述了

  • 名字识别

    对《权力的游戏》小说中人物的识别我采用的策略是:这个词不是处在一句话的首位;正则表达式“[A-Z][a-z]+”,即首字母大写,后面的字母小写;利用nltk中的词性标注,如果是人名应该是属于名词。

    利用这种方法可以正确识别大部分的名字,也会将一些地名认为是人物名字,当然由于我找到的这个权力的游戏的txt文本中存在一些排版错误,也可能会影响到实体识别。

实验过程

  • 完整代码

    项目的github地址:可查看完整代码

  • 实验材料

    《Game of Thrones》txt文本资源的下载。

  • 利用gensim获取词向量

    前面提到,这里我利用Python的gensim库来训练得到相关的词向量。

  1. 安装gensim库

    Python库的安装都比较简单,在终端中输入下面的命令行语句即可

    $ pip3 install gensim
    
  2. 获取词向量

    导入gensim中的word2vec

    from gensim.models import word2vec
    

    读取game_of_thrones.txt,对它进行分词,并且识别出相关的人物名称。在我的代码中创建了一个类BuildDict来完成这项工作,以下是该类的的部分代码

    class BuildDict(object):
    def __init__(self,filename):
        self.filename = filename
    
    
    def get_tokens_and_names(self):
        '''
    
        :return:tokens and names from the file
        '''
        file = open(self.filename)
        tokens = [];names = []
        for line in file:
            token,name = self.get_tokens_and_names_per_line(line)
            tokens.append(token)
            names.extend(name)
        return tokens,names
    

    get_tokens_and_names()这个方法就是处理文本,将文本切割成一个一个的单词,返回tokens和names。

    如果文档很多,gensim库提供了一次读一个文档的方法,由于这里只有一个文档,并且文档并不大,所以未使用该方法。如果有兴趣可以看一看gensim的官方文档。

    在得到tokens和names过后,下面便进行训练了,利用之前导入的word2vec里的Word2Vec类。将训练的结果保存在一个文件中,方便后面使用。

    sentences,names= BuildDict("./game_of_thrones.txt").get_tokens_and_names()
    
    #train and get word vector
    model = word2vec.Word2Vec(sentences,min_count=1)
    model.save("./model.txt")
    
    #compute names' frequency and save the list
    freq = nltk.FreqDist(names)
    name_list = [w for w in set(names) if freq[w]>10]
    f = open('names.txt','w')
    for i in name_list:
        f.write(i+'\n')
    

    model文件有乱码,在这就不展示了。得到的names.txt部分内容如下图所示:

name_list
  • 人物关系分析

    经过前面一系列的操作,我们现在获得到了《权力的游戏》里大部分人的名字(频数大于10的人名)以及这些名字的词向量,接下来我们就可以来分析这些人物之间关系的紧密程度了。

  1. 导入词典向量文件和人名

    将之前训练好的模型文件以及保存人名的txt文件导入到代码中。

    model = word2vec.Word2Vec.load("./model.txt")
    names = open('names.txt')
    
  2. 相似度矩阵

    计算人物两两之间的相似度,可以选择前面所提到的方法来进行计算,在这里我嫌麻烦直接就用了model对象的similarity方法得到相似度。

    sim = model.similarity(name_row,name_column)
    

    最终得到了一个相似度的对称矩阵,并且对角线上的数字都是1,由于所有人物来自同一个文档,毫无疑问他们之间的相似度都比较大,粗略观察了一下,我所得到的相似度最小的也有0.65左右,这样不利于我们分析他们的亲疏关系。于是我对这个进行了归一化处理

    from sklearn import preprocessing
    name_mat = preprocessing.MinMaxScaler().fit_transform(np.mat(name_similarity_array))
    

    好,这就是我们所需要的相似度矩阵了。

  3. 数据处理以及可视化

    光得到相似度矩阵可不行哇,无法给人直观的感受,下面我们来对这个矩阵进行一些分析。这个矩阵大概是196*196的规模,还是比较大的,下面我们对它进行降维处理。目前我采用的降维方法有主成分分析(PCA)以及矩阵的奇异值分解(SVD)这两种,两种方法得到的结果差不太多.

    • PCA
    from sklearn import decomposition
    
    pca = decomposition.PCA(n_components=3)
    pca.fit(name_mat)
    U = pca.transform(name_mat)
    

    得到的三维图像如下图:


    PCA
    • SVD
    import numpy as np
    
    U,S,V = np.linalg.svd(name_mat)
    

    得到三维图像如下图:


    SVD

    得到的三维图像效果并不是很好,全部都聚集在一堆去了,也难怪,这都是一个文档的人物,无论怎样关系还是很大的。人物关系图嘛,用网络拓扑图来表示肯定比这个三维图像更加直观。正好,Python就有这么一个库,可以绘制网络拓扑图,这个库便是networkx。
    安装networkx

    $pip3 install networkx
    

    用network绘制拓扑图的代码如下

    def visulize2(name_mat,name_list,name,layer = 2,accurate = 0.9):
    '''
    
    :param name_mat: 
    :param name_list: 
    :param name: the center of the graph
    :param layer: decide the layer number of the graph
    :param accurate: the similarity between the center with next layer
    :return: 
    '''
    graph = nx.Graph()
    add_element(graph,name_mat,name_list,name,layer,accurate)
    nx.draw(graph, node_size=100, node_color='g', with_labels=True,
            font_size =8,alpha = 0.5,font_color='b',edge_color = 'gray')
    plt.show()
    
    def add_element(graph,name_mat,name_list,name,layer,accurate):
        '''
        add element to the graph,including node and edge
        
        :param graph: 
        :param name_mat: 
        :param name_list: 
        :param name: 
        :param layer: 
        :param accurate: 
        :return: 
        '''
        graph.add_node(name)
        if layer == 1:return
        pos = name_list.index(name)
        for i in range(len(name_list)):
            if name_mat[pos, i] >= accurate:
                graph.add_node(name_list[i])
                graph.add_edge(name, name_list[i])
                add_element(graph,name_mat,name_list,name_list[i],layer-1,accurate)
    

    你只需要向visualize2方法传入相似度矩阵,名字列表,以及作为中心点的人物的名字,即可绘制出该人物与和他相似度大于0.9的人物之间的关系图。你也可以自定义layer和accurate的值,绘制更复杂的拓扑图。

    下面是layer = 2,accurate = 0.9,name = 'Snow'的关系图:


    Snow's Relationship

    下面是layer = 3,accurate = 0.95,name = 'Daenerys'的关系图


    Daenerys's Relationship

    虽然关系还是很复杂,但是不是很直观了呢!

总结

本项目中还是存在很多的不足,比如不能完全正确识别人物名字,人物间区分度不高(相似度都比较大),不过作为才学两周nlp的我来说已经很满意了,刚刚画出拓扑图的时候还是踌躇满志呢。由于笔者水平较低,是一枚刚入门的菜鸟,如有错误之处望各位读者不吝赐教。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容