Skip to content

Latest commit

 

History

History
397 lines (245 loc) · 11.9 KB

chapter2.md

File metadata and controls

397 lines (245 loc) · 11.9 KB
title description prev next type id
第二章:使用spaCy进行大规模数据分析
在本章中,我们会用一些新技术来从大量语料中抽取特定信息。 我们会学习如何利用spaCy的数据结构来结合统计与规则模型进行文本分析。
/chapter1
/chapter3
chapter
2

Part 1

  • nlp.vocab.strings中查找字符串"猫"来得到哈希值。
  • 查找这个哈希值来返回原先的字符串。
  • 你可以像使用一个普通的Python字典一样使用nlp.vocab.strings中存储的字符串。 举个例子,nlp.vocab.strings["独角兽"]会返回哈希值,再查找这个哈希值 就会返回字符串"独角兽"

Part 2

  • nlp.vocab.strings中查找字符串标签"PERSON"来得到哈希值。
  • 查找这个哈希值来返回原先的字符串。
  • 你可以像使用一个普通的Python字典一样使用nlp.vocab.strings中存储的字符串。 举个例子,nlp.vocab.strings["独角兽"]会返回哈希值,再查找这个哈希值 就会返回字符串"独角兽"

为什么这段代码抛出了错误信息?

from spacy.lang.en import English
from spacy.lang.de import German

# 创建一个英文和德文的nlp实例
nlp = English()
nlp_de = German()

# 获取字符串'Bowie'的ID
bowie_id = nlp.vocab.strings["Bowie"]
print(bowie_id)

# 在vocab中查找"Bowie"的ID
print(nlp_de.vocab.strings[bowie_id])

哈希值是不能逆求原始值的。为了解决这个问题, 我们要通过处理文本或者查找字符串把词组加入到新的vocab中, 或者使用同样的vocab把哈希值变回一个字符串。

任何字符串都可以转变为一个哈希值。

变量名nlp只是一个约定俗成的名字。如果你的代码中用了nlp而不是nlp_de, 程序会把已经存在的nlp实例包括其vocab覆盖掉。

让我们来从头开始创建Doc这种实例。

第一部分

  • spacy.tokens中导入Doc
  • wordsspaces创建一个Doc。别忘了把vocab传进去!

Doc类需要3个参数:共享的词汇表,通常是nlp.vocab; 一个词组words的列表; 一个空格spaces的列表,里面是一系列的布尔值表示对应词汇后面是否跟着空格。

第二部分

  • spacy.tokens中导入Doc
  • wordsspaces创建一个Doc。别忘了把vocab传进去!

检查目标文本输出中的每一个词后面是否跟着一个空格。 如果是spaces的值就是True,否则就是False

第三部分

  • spacy.tokens中导入Doc
  • 完成wordsspaces来匹配目标文本并创建一个doc

注意那些单独的词符。 如果想看下spaCy通常是如何对字符串进行分词,你可以试下打印nlp("Oh, really?!")的词符。

在这个练习中,我们要手动创建DocSpan实例,然后更新命名实体。 实际中spaCy在后台也就是这么做的。 一个共享的nlp实例已经创建好了。

  • spacy.tokens中导入DocSpan
  • Doc类使用词组和空格直接创建一个doc实例。
  • doc实例创建一个"David Bowie"的Span,赋予它"PERSON"的标签。
  • 用一个实体的列表,也就是"David Bowie" span,来覆盖doc.ents
  • Doc类用三个参数来初始化:共享的词汇表比如nlp.vocab,一个词组的列表 以及一个布尔值的列表代表了每个词后面是否跟着一个空格。
  • Span类用四个参数来初始化:一个doc的引用,起始词符索引,终止词符索引, 以及一个可选的标签。
  • doc.ents属性是可写的,所以我们可以赋予它任何含有Span实例的可遍历的数据结构。
import spacy

nlp = spacy.load("zh_core_web_sm")
doc = nlp("北京是一座美丽的城市")

# 获取所有的词符和词性标注
token_texts = [token.text for token in doc]
pos_tags = [token.pos_ for token in doc]

for index, pos in enumerate(pos_tags):
    # 检查当前词符是否是专有名词
    if pos == "PROPN":
        # 检查下一个词符是否是动词
        if pos_tags[index + 1] == "VERB":
            result = token_texts[index]
            print("Found proper noun before a verb:", result)

第一部分

这段代码哪里不对?

我们并不需要把字符串变回Token实例。 实际上我们应该要避免把词符变成字符串,因为这样的话 我们就不能够读取其属性和关系了。

一般来说我们到了最后才会考虑将结果转成字符串。 使用原生的词符属性也能保证前后一致。

.pos_属性返回的是粗粒度的词性标注结果, "PROPN"才是正确的专有名词标签。

第二部分

  • 用原生的词符属性而不是token_textspos_tags的列表来重写代码。
  • doc中遍历每一个token并检查其token.pos_属性。
  • doc[token.i + 1]来检查下一个词符及其.pos_属性
  • 如果找到一个处于动词前的专有名词,我们就打印其token.text
  • 删除token_textspos_tags因为我们并不需要提前编译一个字符串的列表。
  • 我们不应该遍历pos_tags,而应该是在doc中遍历每一个token并且获取其token.pos_属性。
  • 读取doc[token.i + 1].pos_来检查下一个词符是否是动词。

在这个练习中我们要用到一个更大的中文模型, 该模型有大概两万个词向量。这里模型已经提前安装好了。

  • 读取中等大小的"zh_core_web_md"模型,该模型含有词向量
  • token.vector属性来打印"老虎"的向量。
  • spacy.load调用模型名字来读取统计模型。
  • 可以直接用索引来读取doc中的一个词符token,比如doc[4]

在这个练习中我们要用spaCy的similarity方法来比较 DocTokenSpan实例,得到相似度分数。

第一部分

  • 使用doc.similarity方法来比较doc1doc2的相似度并打印结果。
  • doc.similarity方法需要一个参数:需要和当前实例计算相似度的另一个实例。

第二部分

  • 使用token.similarity方法来比较token1token2的相似度并打印结果。
  • token.similarity 方法需要一个参数:需要和当前实例计算相似度的另一个实例。

第三部分

  • 为"不错的餐厅"/"很好的酒吧"创建跨度(span)。
  • 使用span.similarity来比较它们并打印结果。

为什么这个模板不能匹配到doc中的词符"Silicon Valley"?

pattern = [{"LOWER": "silicon"}, {"TEXT": " "}, {"LOWER": "valley"}]
doc = nlp("Can Silicon Valley workers rein in big tech from within?")

模板中的"LOWER"属性描述了那些_小写字母形式_能匹配到指定值的词符。 所以{"LOWER": "valley"}是可以匹配到诸如"Valley","VALLEY", "valley" 这样的词符的。

分词器已经自动用空格来分割文本了,模板中的每一个字典描述了一个词符。

所有词符默认被模板匹配刚好1次。 运算符是用来改变这种情况的,比如要匹配0次或者多于1次。

这个练习中的两个模板都出错了,匹配不到我们想要的结果。 你能改正它们吗?要是你卡住了,可以尝试把doc中的词符打印出来, 看看这些文本应该怎样被分割,然后调整你的模板保证每个字典表示一个词符。

  • 编辑pattern1使其可以正确匹配到所有的形容词后面跟着"笔记本"
  • 编辑pattern2使其可以正确匹配到"锐龙"加上后面的数字 (LIKE_NUM) 和符号 (IS_ASCII) 。
  • 试着处理那些能匹配到nlp实例的字符串,比如[token.text for token in nlp("锐龙4000U")]
  • 检查这些词符,确保模板中的每个字典正确描述了一个词符。

有时候相比起写一些描述单个词符的模板,直接精确匹配字符串可能更高效。 在面对有限个种类的东西时尤其如此,比如世界上的所有国家。 我们已经有一个国家的列表,所以我们用它作为我们信息提取代码的基础。 变量COUNTRIES中存取了这些字符串名字的列表。

  • 导入PhraseMatcher并用含有共享vocab的变量matcher来初始化。
  • 加入短语模板并在'doc'上面调用matcher

共享vocabnlp.vocab中。

在上一个练习中,我们写了一段代码用spaCy的PhraseMatcher来寻找文本中的国家名。 我们现在用这个国家匹配器来匹配一段更长的文本,分析句法, 并用匹配到的国家名更新文档中的实体。

  • 对匹配结果进行遍历, 创建一个标签为"GPE"(geopolitical entity,地理政治实体)的Span
  • 覆盖doc.ents中的实体,加入匹配到的跨度span。
  • 获取匹配到的跨度span中的根词符的头。
  • 打印出词符头和跨度span的文本。
  • 记住文本信息是在变量text中。
  • 跨度span的根词符是在span.root中。一个词符的头是在token.head属性中。