深度 | 你知道《圣经》中的主要角色有哪些吗?三种NLP工具将告诉你答案!

来源:机器之心时间:2018-06-25 16:00:44

原标题:深度 | 你知道《圣经》中的主要角色有哪些吗?三种NLP工具将告诉你答案!


选自pmbaumgartner

作者:Peter Baumgartner

机器之心编译

参与:Nurhachu Null、王淑婷


提到数据科学,我们想到的都是数字的统计分析,但如今需要对很多非结构文本进行量化分析。本文将以《圣经》为例,用 spaCy Python 库把三个最常见的 NLP 工具(理解词性标注、依存分析、实体命名识别)结合起来分析文本,以找出《圣经》中的主要人物及其动作。




引言

在思考数据科学的时候,我们常常想起数字的统计分析。但是,各种组织机构越来越频繁地生成大量可以被量化分析的非结构文本。一些例子如社交网络评论、产品评价、电子邮件以及面试记录。

就文本分析而言,数据科学家们通常使用自然语言处理(NLP)。我们将在这篇博客中涵盖 3 个常见的 NLP 任务,并且研究如何将它结合起来分析文本。这 3 个任务分别是:

1. 词性标注——这个词是什么类型?

2. 依存分析——该词和句子中的其他词是什么关系?

3. 命名实体识别——这是一个专有名词吗?

我们将使用 spaCy Python 库把这三个工具结合起来,以发现谁是《圣经》中的主要角色以及他们都干了什么。我们可以从那里发现是否可以对这种结构化数据进行有趣的可视化。

这种方法可以应用于任何问题,在这些问题中你拥有大量文档集合,你想了解哪些是主要实体,它们出现在文档中的什么位置,以及它们在做什么。例如,DocumentCloud 在其「View Entities」分析选项中使用了类似的方法。

分词 & 词性标注

从文本中提取意思的一种方法是分析单个单词。将文本拆分为单词的过程叫做分词(tokenization)——得到的单词称为分词(token)。标点符号也是分词。句子中的每个分词都有几个可以用来分析的属性。词性标注就是一个例子:名词可以是一个人,地方或者事物;动词是动作或者发生;形容词是修饰名词的词。利用这些属性,通过统计最常见的名词、动词和形容词,能够直接地创建一段文本的摘要。

使用 spaCy,我们可以为一段文本进行分词,并访问每个分词的词性。作为一个应用示例,我们将使用以下代码对上一段文本进行分词,并统计最常见名词出现的次数。我们还会对分词进行词形还原,这将为词根形式赋予一个单词,以帮助我们跨单词形式进行标准化。


from collections import Counter
import spacy
from tabulate import tabulate
nlp = spacy.load("en_core_web_lg")

text = """
One way to extract meaning from text is to analyze individual words.
The processes of breaking up a text into words is called tokenization --
the resulting words are referred to as tokens.
Punctuation marks are also tokens.
Each token in a sentence has several attributes we can use for analysis.
The part of speech of a word is one example: nouns are a person, place, or thing;
verbs are actions or occurences; adjectives are words that describe nouns. 
Using these attributes, it"s straightforward to create a summary of a piece of text
by counting the most common nouns, verbs, and adjectives. 
"""

doc = nlp(text)
noun_counter = Counter(token.lemma_ for token in doc if token.pos_ == "NOUN")

print(tabulate(noun_counter.most_common(5), headers=["Noun", "Count"]))
Noun         Count
---------  -------
word             5
text             3
token            3
noun             3
attribute        2


依存分析

单词之间也是有关系的,这些关系有好几种。例如,名词可以做句子的主语,它在句子中执行一个动作(动词),例如「Jill 笑了」这句话。名词也可以作为句子的宾语,它们接受句子主语施加的动作,例如「Jill laughed at John」中的 John。

依存分析是理解句子中单词之间关系的一种方法。尽管在句子「Jill laughed at John」中,Jill 和 John 都是名词,但是 Jill 是发出 laughing 这个动作的主语,而 John 是承受这个动作的宾语。依存关系是一种更加精细的属性,可以通过句子中单词之间的关系来理解单词。

单词之间的这些关系可能变得特别复杂,这取决于句子结构。对句子做依存分析的结果是一个树形数据结构,其中动词是树根。

让我们来看一下「The quick brown fox jumps over the lazy do」这句话中的依存关系。


nlp("The quick brown fox jumps over the lazy dog.")
spacy.displacy.render(doc, style="dep", options={"distance" : 140}, jupyter=True)


依存关系也是一种分词属性,spaCy 有专门访问不同分词属性的强大 API(https://spacy.io/api/token)。下面我们会打印出每个分词的文本、它的依存关系及其父(头)分词文本。


token_dependencies = ((token.text, token.dep_, token.head.text) for token in doc)

print(tabulate(token_dependencies, headers=["Token", "Dependency Relation", "Parent Token"]))

Token    Dependency Relation    Parent Token
-------  ---------------------  --------------
The      det                    fox
quick    amod                   fox
brown    amod                   fox
fox      nsubj                  jumps
jumps    ROOT                   jumps
over     prep                   jumps
the      det                    dog
lazy     amod                   dog
dog      pobj                   over
.        punct                  jumps

作为分析的先导,我们会关心任何一个具有 nobj 关系的分词,该关系表明它们是句子中的宾语。这意味着,在上面的示例句子中,我们希望捕获到的是单词「fox」。

命名实体识别

最后是命名实体识别。命名实体是句子中的专有名词。计算机已经相当擅长分析句子中是否存在命名实体,也能够区分它们属于哪一类别。

spaCy 在文档水平处理命名实体,因为实体的名字可以跨越多个分词。使用 IOB(https://spacy.io/usage/linguistic-features#section-named-entities)把单个分词标记为实体的一部分,如实体的开始、内部或者外部。

在下面的代码中,我们在文档水平使用 doc.ents 打印出了所有的命名实体。然后,我们会输出每个分词,它们的 IOB 标注,以及它的实体类型(如果它是实体的一部分的话)。

我们要使用的句子示例是「Jill laughed at John Johnson」。


doc = nlp("Jill laughed at John Johnson.")

entity_types = ((ent.text, ent.label_) for ent in doc.ents)
print(tabulate(entity_types, headers=["Entity", "Entity Type"]))
print()
token_entity_info = ((token.text, token.ent_iob_, token.ent_type_,) for token in doc)
print(tabulate(token_entity_info, headers=["Token", "IOB Annotation", "Entity Type"]))

Entity        Entity Type
------------  -------------
Jill          PERSON
John Johnson  PERSON

Token    IOB Annotation    Entity Type
-------  ----------------  -------------
Jill     B                 PERSON
laughed  O
at       O
John     B                 PERSON
Johnson  I                 PERSON
.        O

实例:对《圣经》进行自然语言处理

上面提到的每个方法本身就很强大了,但如果将它们结合起来,遵循语言学的模式提取信息,就能发挥自然语言处理的真正力量。我们可以使用词性标注、依存分析、实体命名识别的一部分来了解大量文本中的所有角色及其动作。因其文本长度和角色范围之广,《圣经》是一个很好的例子。

我们正在导入的数据每个《圣经》经文包含一个对象。经文被用作圣经部分的参考方案,通常包括一个或多个经文句子。我们会遍历所有的经文,并提取其主题,确定它是不是一个人物,并提取这个人物所做的所有动作。

首先,让我们从 GitHub 存储库中以 JSON 的形式加载圣经。然后,我们会从每段经文中抽取文本,通过 spaCy 发送文本进行依存分析和词性标注,并存储生成的文档。


import requests

r = requests.get("https://github.com/tushortz/Bible/raw/master/json/kjv.json")
bible_json = [line["fields"] for line in r.json()]
print("Number of Verses:", len(bible_json))

text_generator = (line["text"] for line in bible_json)

%time verse_docs = [doc for doc in nlp.pipe(text_generator, n_threads=-1)]

我们已经用 3 分钟多一点的时间将文本从 json 解析到了 verse_docs,大约每秒 160 个经文章节。作为参考,下面是 bible_json 前 3 行的内容。

[{"book_id": 1,
  "chapter": 1,
  "comment": "",
  "text": "In the beginning God created the heaven and the earth.",
  "verse": 1},
 {"book_id": 1,
  "chapter": 1,
  "comment": "",
  "text": "And the earth was without form, and void; and darkness was upon the face of the deep. And the Spirit of God moved upon the face of the waters.",
  "verse": 2},
 {"book_id": 1,
  "chapter": 1,
  "comment": "",
  "text": "And God said, Let there be light: and there was light.",
  "verse": 3}]

使用分词属性

为了提取角色和动作,我们将遍历一段经文中的所有分词,并考虑 3 个因素:


1. 这个分词是句子的主语吗?(它的依存关系是不是 nsubj?)

2. 它的父分词是不是动词?(通常是这样的,但是有时候 POS 标注和依存分析之间会存在冲突,我们会安全地使用它。此外,我并不是语言学家,所以这里还会有一些奇怪的案例。)

3. 一个分词的命名实体是否为一个人物?我们不想提取任何不是人物的名词。(为了简便,我们仅仅会提取名字)


如果我们的分词满足以上 3 种条件,我们将会收集以下的属性:1. 名词/实体分词的文本。2. 包含名词和动词的范围。3. 动词。4. 动词出现在标准英语文本中的对数概率(使用对数的原因是这里的概率都很小)。5. 经文数量。


actors_and_actions = []

def token_is_subject_with_action(token):
    nsubj = token.dep_ == "nsubj"
    head_verb = token.head.pos_ == "VERB"
    person = token.ent_type_ == "PERSON"
    return nsubj and head_verb and person

for verse, doc in enumerate(verse_docs):
    for token in doc:
        if token_is_subject_with_action(token):
            span = doc[token.head.left_edge.i:token.head.right_edge.i+1]
            data = dict(name=token.orth_,
                        span=span.text,
                        verb=token.head.lower_,
                        log_prob=token.head.prob,
                        verse=verse)
            actors_and_actions.append(data)

print(len(actors_and_actions))

分析

我们已经获得了提取到的所有角色及其动作的列表,现在我们做以下两件事来快速分析:

1. 找出每个角色最常做出的动作(动词)

2. 找出每个人最独特的动作。我们将其确定为英文文本中出现概率最低的动词。

import pandas as pd

action_df = pd.DataFrame(actors_and_actions)

print("Unique Names:", action_df["name"].nunique())

most_common = (action_df
    .groupby(["name", "verb"])
    .size()
    .groupby(level=0, group_keys=False)
    .nlargest(1)
    .rename("Count")
    .reset_index(level=1)
    .rename(columns={
        "verb": "Most Common"
    })
)

# exclude log prob < -20, those indicate absence in the model vocabulary
most_unique = (action_df[action_df["log_prob"] > -20]
    .groupby(["name", "verb"])["log_prob"]
    .min()
    .groupby(level=0, group_keys=False)
    .nsmallest(1)
    .rename("Log Prob.")
    .reset_index(level = 1)
    .rename(columns={
        "verb": "Most Unique"
    })
)

# SO groupby credit
# https: //stackoverflow.com/questions/27842613/pandas-groupby-sort-within-groups

让我们看一下前 15 个角色的动词数及其最常用的动词。

most_common.sort_values("Count", ascending=False).head(15)

貌似《圣经》里面很多人都说了很多话,而所罗门简直是个例外,他做了很多事情。

那么从出现概率来看,最独特的动词是什么呢?(我们将在此处删去重复项,以便每个单词都是唯一的)


(most_unique
 .drop_duplicates("Most Unique")
 .sort_values("Log Prob.", ascending=True)
 .head(15)
)

看来我们要学习一些有趣的新词汇了!我最喜欢的是 discomfited 和 ravin。

可视化

接下来可视化我们的结果。我们将选取行动最多、情节最多的前 50 个名字,这些行动发生在整篇文章中。我们还会在《圣经》每本书的开头画垂直线。姓名将按首次出现的顺序排序。

这可以让我们知道圣经中每个角色最活跃的时候。

我们将添加一些分隔符来分隔《圣经》的不同部分。我自己并非研究《圣经》学者,所以我参考了如下分隔法(https://www.thoughtco.com/how-the-books-of-the-bible-are-organized-363393):

《旧约》:

摩西五经或律法书:《创世纪》、《出埃及记》、《利未记》、《民数记》和《申命记》。

旧约历史书:《约书亚记》、《士师记》、《路得记》、《撒慕耳记》上下、《列王记》上下、《历代志》上下、《尼希米记》、《以斯拉记》、《以斯帖记》

诗歌智慧书:《约伯记》、《诗篇》、《箴言》、《传道书》和《雅歌》;

大先知书:《以赛亚书》、《耶利米书》、《耶利米哀歌》、《以西结书》、《但以理书》、《何西阿书》、《约珥书》、《阿摩司书》、《俄巴底亚书》、《约拿书》、《弥迦书》、《那鸿书》、《哈巴谷书》、《西番雅书》、《哈该书》、《撒迦利亚书》、《玛拉基书》。

《新约》:

福音书:《马太福音》、《马可福音》、《路加福音》、《约翰福音》

新约历史书:《使徒行传》

保罗书信:《罗马书》、《哥林多前书》、《哥林多后书》、《加拉太书》、《以弗所书》、《腓立比书》、《歌罗西书》《帖撒罗尼迦前书》、《帖撒罗尼迦后书》、《提摩太前书》、《提摩太后书》、《提多书》、《腓利门书》、《希伯来书》、《雅各书》、《彼得前书》、《彼得后书》、《约翰壹书》、《约翰贰书》、《约翰叁书》和《犹大书》

语言/启示录:《启示录》

此外,我们还会用一条红色的标志线分割《旧约》和《新约》。

import seaborn as sns
import matplotlib.pyplot as plt
%matplotlib inline
sns.set(context="notebook", style="dark")

most_frequent_actors = list(action_df["name"].value_counts().index[:50])
top_actors_df = action_df[action_df["name"].isin(most_frequent_actors)].copy()
book_locations = (pd.DataFrame(bible_json)
                  .reset_index()
                  .groupby("book_id")["index"]
                  .min()
                  .to_dict()
                 )

fig, ax = plt.subplots(figsize=(8,12), dpi=144*2)
sns.stripplot(x="verse", y="name", 
              data=top_actors_df, ax=ax,
              color="xkcd:cerulean",
              size=3, alpha=0.25, jitter=0.25)

sns.despine(bottom=True, left=True)

for book, verse_num in book_locations.items():
    ax.axvline(verse_num, alpha=1, lw=0.5, color="w")

divisions = [1, 6, 18, 23, 40, 44, 45, 65]
for div in divisions:
    ax.axvline(book_locations[div], alpha=0.5, lw=1.5, color="grey")

ax.axvline(book_locations[40], alpha=0.5, lw=1.75, color="xkcd:coral")

ax.set_xlim(left=-150)

ax.set_title("Where Actions Occur in the Bible\nCharacters Sorted by First Appearance");

可视化分析

在《圣经》开头的《创世纪》中,上帝(God)被密集地提到。

在《新约》中,主(Lord)不再作为一个实体使用。

我们第一次看到保罗是在《使徒行传》中被提及。(福音书后的第一本书)

在《诗歌智慧书》里没有提到很多实体。

耶稣的生活在《福音书》中被密集地记录了下来。

彼拉多出现在《福音书》的末尾。

这种方法的问题

实体识别无法区分两个名字相同的人

       扫罗王(《旧约》)

       直到《使徒行传》的中途,保罗(使徒)一直被称作扫罗

有些名词不是实际的实体(如 Ye)

有些名词可以使用更多的语境和全名(如 Pilate)

下一步

一如既往,有办法扩展和改进这一分析。我在写这篇文章的时候想到了以下几点:


1. 使用依存关系来寻找实体之间的关系,通过网络分析的方法来理解角色。

2. 改进实体提取,以捕获单个名称之外的实体。

3. 对非人物实体及其语言关系进行分析——《圣经》中提到了哪些位置?

写在结尾

仅仅通过使用文本中分词级别的属性我们就可以做一些很有趣的分析!在本文中,我们介绍了 3 种主要的 NLP 工具:

1. 词性标注——这个词是什么类型?

2. 依存分析——该词和句子中的其他词是什么关系?

3. 命名实体识别——这是一个专有名词吗?

我们结合这三个工具来发现谁是《圣经》中的主要角色,以及他们采取的动作。并且我们还绘制了这些角色和动作的图表,以了解每个角色的主要动作发生在何处。

原文链接:https://pmbaumgartner.github.io/blog/holy-nlp/

本文为机器之心编译,转载请联系本公众号获得授权

✄------------------------------------------------

加入机器之心(全职记者 / 实习生):hr@jiqizhixin.com

投稿或寻求报道:content@jiqizhixin.com

广告 & 商务合作:bd@jiqizhixin.com

相关阅读

推荐阅读

两连阳为啥还没回本?买入绩优、白马、中大盘股才能轻松获利

两连阳为啥还没回本?买入绩优、白马、中大盘股才

一、大盘点评展望周二沪深两市小幅低开后,沪深300权重带动指数震荡上行。最终沪指上涨0 53%报收3410点,K线上收出一根中阳线;深成指上涨1 更多

2017-11-22 16:17:00
2017百度世界大会李彦宏透露无人车2018年量产 无人驾驶概念股备受期待

2017百度世界大会李彦宏透露无人车2018年量产 无

一年一度的百度世界大会11月16日在北京举行,每年的百度世界大会,百度创始人李彦宏都会带来他对过去,现在和未来关于互联网和整个IT领域的 更多

2017-11-16 11:17:37
贵州茅台股价突破700元 贵州茅台股价为什么那么高?

贵州茅台股价突破700元 贵州茅台股价为什么那么

今日贵州茅台延续昨日强势走势,继续大幅攀升,盘中最高价突破700元整数关口,刷新上市新高纪录,截至发稿,最高价报704 97元,总市值超越8 更多

2017-11-16 10:32:47
百度世界大会今日召开聚焦智能硬件 百度世界大会受益概念股一览

百度世界大会今日召开聚焦智能硬件 百度世界大会

据怀新资讯报道,2017百度世界大会将于16日在北京举行。从邀请函上出现的神秘的盒子推测,本次百度将会有AI硬件以及诸多AI新技术发布。从今 更多

2017-11-16 10:17:03
中国财富总值全球第二但是超4亿人家庭没有卫生厕所 你拖后腿了吗?

中国财富总值全球第二但是超4亿人家庭没有卫生厕

瑞士信贷研究所(CSRI)最新出炉的《全球财富报告》显示,全球财富总额现已达到280万亿美元,比十年前金融危机爆发时高出27%。美国占全球财 更多

2017-11-16 10:07:07
比特币今日价格大幅反弹逾9% 比特币价格再次突破7000美元

比特币今日价格大幅反弹逾9% 比特币价格再次突破

在短短两周时间内,比特币价格呈现了非常惊险的过山车。由于对于这款加密货币未来趋势存在争议,上周比特币价格出现暴跌,曾一度低于6000美 更多

2017-11-16 10:04:14
油价调整最新消息:国内油价今日24时或迎年内最大涨幅 附92号/93号汽油最新价格

油价调整最新消息:国内油价今日24时或迎年内最大

新一轮成品油调价窗口将于16日24时开启。国际原油价格一度涨至近两年高位,受此影响,国内油价或迎年内最大涨幅。隆众资讯统计数据显示,以 更多

2017-11-16 09:22:17
国际油价调整最新消息:EIA原油及汽油库存双双增长 延长减产协议预期支撑油市反弹

国际油价调整最新消息:EIA原油及汽油库存双双增

美国能源信息署(EIA)周三(11月15日)公布的数据显示,上周美国原油库存意外录得增加,同时汽油库存也意外增长。EIA公布,截至11月10日当 更多

2017-11-16 09:21:49
+ 点击查看更多精彩
29日零时将上调汽柴油限价  每吨汽油上调170柴油上调165元
    人民网北京3月28日电 (朱江)今日,记者从隆众、卓创社会监测机构...
今年政策方向没有变,“三去一降一补”具体该怎么干?
    2018年,我国开启高质量发展新征途。中央经济工作会议把深化供给...
蓝筹股带动大盘继续上攻 沪指重返3400点
    【盘面简述】今日早盘,随着油气股的拉升上涨,中国石油和中国石...
白马股崛起补涨强烈 短期恐慌性抛盘并不大
    今日市场点评:沪深两市早盘各股指纷纷小幅低开,开盘之后一度呈...
市场再度面临重要的时间窗口 一板块有望迎来年末行情
    【今日小结】今日,两市小幅高开,开盘回撤后快速上行翻红,金融...
不离谱的回落 三理由力挺节后机会
    今日市场点评:大盘在节后第一天走出了高开低走的行情。在国庆期...