经过漫长的等待,漫威迷心心念念的《复联4》(《复仇者联盟4:终局之战》)终于来了,相信有不少人在工作日的凌晨守在大荧幕前,目睹了超级英雄们是如何再次拯救世界的。同时这场持续10年(甚至不止10年)的超级英雄故事也随着《复联4》的上映画上了句号。
关于剧情,观众们的感受千人千面,但数据从来都是最诚实的见证者。所以让我们来重温一下《复联3》,只不过回顾的不只是剧情,而是用自然语言处理(简称NLP)研究电影的脚本。
在本文中,我们会用NLP Python开源库spaCy来帮助我们处理和理解大量的文本,并分析电影脚本,以研究以下概念:
电影中排名前10位的动词,名词,副词和形容词。
由特定角色说出的动词和名词。
电影中排名前30位的实体。
每对人物台词之间的相似性,例如,雷神和灭霸台词之间的相似性。
除了这些,我们同时还会用代码解释spaCy是如何进行这些研究的。
对代码和技术词汇感兴趣的朋友可以关注本文,文中使用的词汇和术语大部分都是非技术性的,所以即使你没有NLP、AI、机器学习等专业技能,也能够理解本文想要表达的主要想法和概念。
处理数据
实验中使用的数据或文本语料库(在NLP中通常称为语料库)是电影脚本。但是,在使用数据之前,我们需要对数据进行清理。主要是删除一些描述动作的评论或场景,以及说出该行台词的角色名字(实际上,该名称用于了解谁说了什么,但不是用于分析的实际语料库的一部分)。此外,作为spaCy数据处理步骤的一部分,我们不用标记为停止字的术语,也就是常用的单词,如"I"、"you"、"an"等。而且,我们只使用引理,也就是每个单词的正则形式。例如,动词"talk"、"talking"和"talking"是同一个词素的形式,其引理是"talk"。
在spaCy中处理一段文本时,我们首先需要加载语言模型,然后在文本语料库上调用模型。结果是一个Doc对象,一个保存已处理文本的对象。
import spacy # load a medium-sized language model nlp = spacy.load("en_core_web_md") with open('cleaned-script.txt', 'r') as file: text = file.read() doc = nlp(text)
在spaCy中创建Doc对象
现在我们已经有了经过处理的语料库,接下来要开始我们的研究了。
十大动词、名词、副词和形容词
仅仅看动词就能知道电影的整体动作或情节吗?本文的第一个图表说明了这一点。
Know"、"go"、"come"、"get"、"think"、"tell"、"kill"、"need"、"stop"和 "want". 我们能从中推断出什么?可能是因未看过电影的缘故,作者根据这些动词总结出,《复仇者联盟3:无限战争》是关于了解、思考和调查如何去阻止某事或某个人的。
这就是我们使用spaCy获取动词的方法:
import spacy # load a medium-sized language model nlp = spacy.load("en_core_web_md") with open('cleaned-script.txt', 'r') as file: text = file.read() doc = nlp(text) # map with frequency count pos_count = {} for token in doc: # ignore stop words if token.is_stop: continue # pos should be one of these: # 'VERB', 'NOUN', 'ADJ' or 'ADV' if token.pos_ == 'VERB': if token.lemma_ in pos_count: pos_count[token.lemma_] += 1 else: pos_count[token.lemma_] = 1 print("top 10 VERBs {}".format(sorted(pos_count.items(), key=lambda kv: kv[1], reverse=True)[:10]))
那么副词的情况怎么样?
对于一部关于阻止一个人毁灭半个宇宙的电影来说,口语中有很多实证主义的成分,比如"right"、"exactly"和"better"。
我们已经知道动词以及副词的情况,所以接下来让我们一起来看看名词。
看到"stones"作为第一出现并不奇怪,毕竟这部电影是关于石头的。第二个词是"life",这是灭霸想要摧毁的东西,紧随其后的是"time",而这正是《复仇者联盟》所没有的(注:"time"上榜也有可能是因为提到了时间之石)。
最后,我们俩看一下描述名词的形容词。与副词类似,形容词中也有"good"和"right"等表达积极意义的词汇,以及"okay"和"sure"等表示肯定的词汇。
由特定角色说出的动词和名词
在此之前,我们看到了电影中提到的最常见的动词和名词。虽然这让我们对电影的整体感觉和情节有所了解,但它并没有说明角色的个人经历。因此,我们使用相同的程序来查找前十个动词和名词。
由于电影中有很多角色,我们只选择其中比较合理和完整的角色。这些角色分别是钢铁侠Tony Stark、奇异博士、格莫拉、雷神托尔(Thor)、火箭浣熊、 Peter Quill(星爵)、Ebony Maw(乌木喉)和灭霸。
下一张图片显示了这些角色使用的最常用名词。
我发现,在大多数情况下,英雄们最常用的名词都是伙伴的名字,这很奇怪,甚至令人耳目一新。例如,钢铁侠说了9次"kid"(指蜘蛛侠),火箭叫了3次Quill (星爵),而Quill自己叫了7次。
通过进一步的观察,我们可以推断出对每个角色来说什么是最重要的。以钢铁侠为例,数据表明地球对他来说是有价值的。与他相似的是格莫拉,她总是想着更高的目标"生命"、"宇宙"和"行星",并最终为此付出了代价。奇异博士还有反复提到的另一个目标:保护石头。还有雷神托尔,他和灭霸之间有私人恩怨,并且连续喊了8遍他的名字。还有疯狂泰坦灭霸,他一直在收集无极宝石,另外就是关于他的女儿。
虽然名词是有表达意义的,但动词就不一样了。在下一张图片中可以看到,动词不像名词那样丰富多彩。像"know"、"want"和"get"这样的词占据了大部分的榜首。然而,有一个角色可能拥有整个语料库中最独特的动词:乌木喉(Ebony Maw)。灭霸的头号追随者,就像忠实的仆人一样,他的目标是得到时间宝石和宣扬主人的使命。他最常说的就是"hear," 和"rejoice."
彩蛋:以下是格鲁特(Groot)最常用的名词。
命名实体
到目前为止,我们已经掌握了超级英雄和反派们在这部史诗级电影中最常说的动词、名词、副词和形容词。然而,要充分理解这些词语和人物,我们还需要命名实体。
引用spaCy的网站上的话,命名实体是"指定名称的真实世界对象。例如,一个人、一个国家、一个产品或一个书名"。 所以,了解这些实体,意味着了解角色在说些什么。在spaCy包中,实体具有预测标签,该标签将实体分类为多种类型中的一种,例如人、产品、艺术品等。并给予我们额外的粒度级别,这可能有助于进一步对它们进行分类。
这些是前30个实体。
第一位是灭霸,毕竟电影是关于他的,这不足为奇。第二位是他的女儿格莫拉,影片的核心人物之一。第三位是Groot,紧随其后的是Tony和其他复仇者以及一些地方,如纽约,阿斯加德和瓦坎达。除英雄和地点外,还有两个"六"(见实体号)。时间之石和灵魂之石分别位于第15位和第16位。令人惊讶的是,将灭霸带到地球上的心灵之石不在名单之列。
要访问spaCy中的实体,请像这样读取Doc的属性ents:
import spacy # load a medium-sized language model nlp = spacy.load("en_core_web_md") with open('cleaned-script.txt', 'r') as file: text = file.read() doc = nlp(text) # create an entity frequency map entities = {} # named entities for ent in doc.ents: # Print the entity text and its label if ent.text in entities: entities[ent.text] += 1 else: entities[ent.text] = 1 print("top entities {}".format(sorted(entities.items(), key=lambda kv: kv[1], reverse=True)[:30]))
每对人物台词之间的相似性
当我们讨论每个角色最重要的动词时,我们意识到,与名词不同的是,大多数动词非常相似,并且表达的感觉非常相似。像"go"和"come"这样的词给我们运动的印象,或者给我们角色想要去或到达某个特定地方的感觉。诸如"kill"和"stop"之类的动词意味着确实存在一个必须阻止的巨大威胁。
考虑到这一点,为了进一步研究相似度的概念,我计算了每对字符的口语台词之间的相似度评分
NLP中相似性的概念描述了两段文本的合成或语法意义的接近程度。通常,相似性得分的范围从0到1,其中0表示完全不同,1表示完全相似(或两个文本是相同的)。从技术上讲,相似性是通过测量单词向量之间的距离来计算的,即单词的多维表示。
下图显示了相似度矩阵。
结果令人意外。其实所街结果相似度为1是可以接受的,因为这部电影本身就阐述了一个主要情节,对话关联度高是可以理解的。但是,他们的相似度太高了,看看灭霸就知道了,原本以为反派的得分会有很大不同。但庆幸的是,蜘蛛侠的分数变化很大,毕竟他只是一个在混乱中被抓住的孩子,结果有如预期。
这是如何在spaCy中计算两个文件之间的相似性的例子:
# for the full example on how I obtained all the similarities # see the full code at: https://github.com/juandes/infinity-war-spacy/blob/master/script.py import spacy # load a medium-sized language model nlp = spacy.load("en_core_web_md") with open('tony-script.txt', 'r') as file: tony_lines = file.read() with open('thor-script.txt', 'r') as file: thor_lines = file.read() tony_doc = nlp(tony_lines) thor_doc = nlp(thor_lines) similarity_score = tony_doc.similarity(thor_doc) print("Similarity between Tony's and Thor's docs is {}".format(similarity_score))
回顾和结论
在电影《复仇者联盟3:无限战争》中,我们跟随超级英雄们展开了阻止灭霸毁灭半个宇宙的旅程。在整部电影中,我们了解到这些英雄大多有拯救世界的动机,这反映在他们表达自己的方式上。在本文中,我们在Python、NLP和spaCy中研究了英雄和反派们是如何通过他们的每句台词来表达和交流的,重温了钢铁侠对地球的忠诚、奇异博士保护时间之石的誓言、雷神托尔复仇的渴望以及灭霸毁灭宇宙的野心。