python自然语言开发(在Python中使用)(1)

如果您想在 Python 中进行自然语言处理 (NLP),那么 spaCy 是您的不二之选,它是一个免费的开源库,有许多内置功能。 它在 NLP 领域的数据处理和分析中越来越受欢迎。

随着互联网和移动网络的普及,非结构化文本以惊人的规模由公司、政府和普通民众产生。 自动处理和分析人类无法处理的文本变得很重要。 要自动处理和分析文本,您需要以计算机可以理解的格式表示文本。 spaCy 可以帮助你做到这一点。

在本教程中,您将学习:

如果您是 NLP 新手,请不要担心! 在开始使用 spaCy 之前,您将首先了解 NLP 中的基本术语和概念。 不过,您应该熟悉 Python 的基础知识。 本教程中的代码包含字典、列表、元组、for 循环、理解面向对象编程和 lambda 函数,以及其他基本的 Python 概念。

NLP 和 spaCy 简介

NLP 是人工智能的一个子领域,通过它可以让计算机理解人类语言。 NLP 涉及分析、量化、理解和从自然语言中获取意义。

注意:目前,最强大的 NLP 模型是基于 转换器(transformer) 的。 Google 的 BERT 和 OpenAI 的 GPT 系列就是此类模型的例子。

自 3.0 版本发布以来,spaCy 支持基于 transformer 的模型。 本教程中的示例是使用较小的 CPU 优化模型完成的。 但是,您可以改为使用transformer模型来运行示例。 所有 Hugging Face transformer模型都可以与 spaCy 一起使用。

NLP 可帮助您从非结构化文本中提取信息,并且有许多用例,例如:

spaCy 是用 Cython 编写的 Python NLP 的免费开源库。 spaCy 旨在简化信息提取或通用自然语言处理系统的构建。

安装spaCy

在本节中,您将把 spaCy 安装到虚拟环境中,然后下载英语的数据和模型。

您可以使用 Python 包管理器 pip 安装 spaCy。 最好使用虚拟环境来避免依赖系统范围的包。 要了解有关虚拟环境和 pip 的更多信息,请查看使用 Python 的 pip 管理项目的依赖项和 Python 虚拟环境:入门。

首先,您将创建一个新的虚拟环境,将其激活并安装 spaCy。 在下方是在Windows中的操作方法:

PS> python -m venv venv PS> ./venv/Scripts/activate (venv) PS> python -m pip install spacy

在您的虚拟环境中安装 spaCy 后,就可以开始使用 NLP 了。 但是你还需要安装其他东西:

(venv) $ python -m spacy download en_core_web_sm

不同语言有不同的 spaCy 模型。 英语语言的默认模型指定为 en_core_web_sm。 由于模型非常大,最好单独安装它们——将所有语言包含在一个包中会增加下载量。

en_core_web_sm 模型下载完成后,打开 Python REPL 并验证安装是否成功:

>>> import spacy >>> nlp = spacy.load("en_core_web_sm")

如果以上代码运行没有错误,则意味着 spaCy 已安装并且模型和数据已成功下载。 您现在已准备好使用 spaCy 深入研究 NLP!

DocObject处理文本

在本节中,您将使用 spaCy 解构给定的输入字符串,您还将从文件中读取文本。

首先,需要在 spaCy 中加载语言模型实例:

>>> import spacy >>> nlp = spacy.load("en_core_web_sm") >>> nlp <spacy.lang.en.English at 0x291003a6bf0>

load() 函数返回一个可调用对象,该对象通常分配给一个名为 nlp 的变量。

要开始处理您的输入,您需要构造一个 Doc 对象。 Doc 对象是表示词法标记的一系列 Token 对象。 每个 Token 对象都有关于一段特定文本(通常是一个词)的信息。 您可以通过使用输入字符串作为参数调用对象来实例化 Doc 对象:

>>> introduction_doc = nlp( ... "This tutorial is about Natural Language Processing in spaCy." ... ) >>> type(introduction_doc) spacy.tokens.doc.Doc >>> [token.text for token in introduction_doc] ['This', 'tutorial', 'is', 'about', 'Natural', 'Language', 'Processing', 'in', 'spaCy', '.']

在上面的示例中,文本用于实例化 Doc 对象。 在那里,您可以访问有关已处理文本的大量信息。

例如,您使用生成一系列 Token 对象的列表遍历 Doc 对象。 在每个Token对象上,调用 .text 属性以获取该Token中包含的文本。

不过,您通常不会将文本直接复制和粘贴到构造函数中。 相反,您可能会从文件中读取它:

>>> import pathlib >>> file_name = "introduction.txt" >>> introduction_doc = nlp(pathlib.Path(file_name).read_text(encoding="utf-8")) >>> print ([token.text for token in introduction_doc]) ['This', 'tutorial', 'is', 'about', 'Natural', 'Language', 'Processing', 'in', 'spaCy', '.', '\n']

在此示例中,您使用 pathlib.Path 对象的 .read_text() 方法读取 introduction.txt 文件的内容。 由于该文件包含与前面示例相同的文本内容息,因此您将获得相同的结果。

句子检测

句子检测是定位给定文本中句子开始和结束位置的过程。 这使您可以将文本划分为具有语言意义的单元。 在处理文本以执行词性 (POS) 标记和命名实体识别等任务时,将使用这些单元。将在本教程的后面部分介绍这些任务。

在 spaCy 中,.sents 属性用于从 Doc 对象中提取句子。 以下是针对给定输入提取句子总数和句子本身的方法:

>>> about_text = ( ... "Gus Proto is a Python developer currently" ... " working for a London-based Fintech" ... " company. He is interested in learning" ... " Natural Language Processing." ... ) >>> about_doc = nlp(about_text) >>> sentences = list(about_doc.sents) >>> len(sentences) 2 >>> for sentence in sentences: ... print(f"{sentence[:5]}...") ... Gus Proto is a Python... He is interested in learning...

在上面的示例中,spaCy 能够正确识别输入的句子。 使用 .sents,您可以获得代表单个句子的 Span 对象列表。 您还可以对 Span 对象进行切片以生成句子的各个部分。

您还可以使用自定义分隔符来自定义句子检测行为。 下面是一个示例,其中除了句点 (.) 之外,还使用省略号 (...) 作为定界符:

>>> ellipsis_text = ( ... "Gus, can you, ... never mind, I forgot" ... " what I was saying. So, do you think" ... " we should ..." ... ) >>> from spacy.language import Language >>> @Language.component("set_custom_boundaries") ... def set_custom_boundaries(doc): ... """以 `...` 作为句子检测定界符""" ... for token in doc[:-1]: ... if token.text == "...": ... doc[token.i 1].is_sent_start = True ... return doc ... >>> custom_nlp = spacy.load("en_core_web_sm") >>> custom_nlp.add_pipe("set_custom_boundaries", before="parser") >>> custom_ellipsis_doc = custom_nlp(ellipsis_text) >>> custom_ellipsis_sentences = list(custom_ellipsis_doc.sents) >>> for sentence in custom_ellipsis_sentences: ... print(sentence) ... Gus, can you, ... never mind, I forgot what I was saying. So, do you think we should ...

对于此示例,您使用 @Language.component("set_custom_boundaries") 装饰器定义了一个以 Doc 对象作为参数的新函数。 此函数的作用是识别 Doc 中作为句子开头的标记,并将其 .is_sent_start 属性标记为 True。 完成后,该函数必须再次返回 Doc 对象。

然后,您可以使用 .add_pipe() 方法将自定义边界函数添加到 Language 对象。 使用此修改后的 Language 对象解析文本现在会将省略号后的单词视为新句子的开头。

spaCy 中的Token

构建 Doc 容器涉及对文本进行标记化(tokenizing)。 标记化过程将文本分解为其基本单元(或标记),这些单元在 spaCy 中表示为标记(token)对象。

正如您已经看到的,使用 spaCy,您可以通过迭代 Doc 对象来打印token。 但是 Token 对象还有其他可供探索的属性。 例如,token在字符串中的原始索引位置仍然可用作token上的属性:

>>> import spacy >>> nlp = spacy.load("en_core_web_sm") >>> about_text = ( ... "Gus Proto is a Python developer currently" ... " working for a London-based Fintech" ... " company. He is interested in learning" ... " Natural Language Processing." ... ) >>> about_doc = nlp(about_text) >>> for token in about_doc: ... print (token, token.idx) ... Gus 0 Proto 4 is 10 a 13 Python 15 developer 22 currently 32 working 42 for 50 a 54 London 56 - 62 based 63 Fintech 69 company 77 . 84 He 86 is 89 interested 92 in 103 learning 106 Natural 115 Language 123 Processing 132 . 142

在此示例中,您迭代 Doc,同时打印 Token 和 .idx 属性,该属性表示原始文本中标记的起始位置。 例如,保留此信息可能有助于在线进行就地单词替换。

spaCy 为 Token 类提供了各种其他属性:

>>> print( ... f"{"Text with Whitespace":22}" ... f"{"Is Alphanumeric?":15}" ... f"{"Is Punctuation?":18}" ... f"{"Is Stop Word?"}" ... ) >>> for token in about_doc: ... print( ... f"{str(token.text_with_ws):22}" ... f"{str(token.is_alpha):15}" ... f"{str(token.is_punct):18}" ... f"{str(token.is_stop)}" ... ) ... Text with Whitespace Is Alphanum? Is Punctuation? Is Stop Word? Gus True False False Proto True False False is True False True a True False True Python True False False developer True False False currently True False False working True False False for True False True a True False True London True False False - False True False based True False False Fintech True False False company True False False . False True False He True False True is True False True interested True False False in True False True learning True False False Natural True False False Language True False False Processing True False False . False True False

在此示例中,您使用 f-string 格式输出一个表,该表访问 Doc 中每个 Token 的公共属性:

与 spaCy 的许多方面一样,您还可以自定义标记化过程以检测自定义字符上的标记(token)。 这通常用于带连字符的单词,例如 London-based。

要自定义标记化,您需要使用新的 Tokenizer 对象更新可调用 Language 对象上的 tokenizer 属性。

假设您有一些文本使用 @ 符号而不是通常的连字符 (-) 作为将单词链接在一起的中缀。 所以是 London@based,而不是London-based:

>>> custom_about_text = ( ... "Gus Proto is a Python developer currently" ... " working for a London@based Fintech" ... " company. He is interested in learning" ... " Natural Language Processing." ... ) >>> print([token.text for token in nlp(custom_about_text)[8:15]]) ['for', 'a', 'London@based', 'Fintech', 'company', '.', 'He']

在此示例中,默认解析将 London@based 文本读取为单个标记,但如果您使用连字符“-”而不是 @ 符号,那么您将获得三个标记。

要将 @ 符号包含为自定义中缀,您需要构建自己的 Tokenizer 对象:

1>>> import re 2>>> from spacy.tokenizer import Tokenizer 3 4>>> custom_nlp = spacy.load("en_core_web_sm") 5>>> prefix_re = spacy.util.compile_prefix_regex( 6... custom_nlp.Defaults.prefixes 7... ) 8>>> suffix_re = spacy.util.compile_suffix_regex( 9... custom_nlp.Defaults.suffixes 10... ) 11 12>>> custom_infixes = [r"@"] 13 14>>> infix_re = spacy.util.compile_infix_regex( 15... list(custom_nlp.Defaults.infixes) custom_infixes 16... ) 17 18>>> custom_nlp.tokenizer = Tokenizer( 19... nlp.vocab, 20... prefix_search=prefix_re.search, 21... suffix_search=suffix_re.search, 22... infix_finditer=infix_re.finditer, 23... token_match=None, 24... ) 25 26>>> custom_tokenizer_about_doc = custom_nlp(custom_about_text) 27 28>>> print([token.text for token in custom_tokenizer_about_doc[8:15]]) 29['for', 'a', 'London', '@', 'based', 'Fintech', 'company']

在此示例中,您首先实例化一个新的 Language 对象。 要构建新的 Tokenizer,您通常会为其提供:

涉及的函数通常是您可以从已编译的正则表达式对象访问的正则表达式函数。 要为您不想自定义的前缀和后缀构建正则表达式对象,您可以使用默认值生成它们,如第 5 到 10 行所示。

要创建自定义中缀函数,首先您要在第 12 行定义一个新列表,其中包含您想要包含的任何正则表达式模式。 然后,您使用 Language 对象的 .Defaults.infixes 属性加入您的自定义列表,该属性需要在加入之前转换为列表。 这样做以包括所有现有的中缀。 然后将扩展元组作为参数传递给 spacy.util.compile_infix_regex() 以获得中缀的新正则表达式对象。

当您调用 Tokenizer 构造函数时,您将在前缀和后缀正则表达式对象上传递 .search() 方法,并在中缀正则表达式对象上传递 .finditer() 函数。 现在您可以替换 custom_nlp 对象上的分词器。

完成后,您会看到 @ 符号现在已单独标记。

停用词(stop words)

停用词通常被定义为一种语言中最常见的词。 在英语中,停用词的一些示例是 the、are、but 和 they。 大多数句子需要包含停用词才能成为具有语法意义的完整句子。

在 NLP 中,停用词通常会被删除,因为它们并不重要,而且它们会严重扭曲任何词频分析。 spaCy 存储了英语停用词列表:

>>> import spacy >>> spacy_stopwords = spacy.lang.en.stop_words.STOP_WORDS >>> len(spacy_stopwords) 326 >>> for stop_word in list(spacy_stopwords)[:10]: ... print(stop_word) ... using becomes had itself once often is herein who too

在此示例中,您检查了 spacy.lang.en.stop_words 中的 STOP_WORDS 列表。 不过,您不需要直接访问此列表。 您可以使用每个token的 .is_stop 属性从输入文本中删除停用词:

>>> custom_about_text = ( ... "Gus Proto is a Python developer currently" ... " working for a London-based Fintech" ... " company. He is interested in learning" ... " Natural Language Processing." ... ) >>> nlp = spacy.load("en_core_web_sm") >>> about_doc = nlp(custom_about_text) >>> print([token for token in about_doc if not token.is_stop]) [Gus, Proto, Python, developer, currently, working, London, -, based, Fintech, company, ., interested, learning, Natural, Language, Processing, .]

在这里,您使用带有条件表达式的列表表达式来生成文本中所有非停用词的列表。

虽然没有停用词,您无法确定句子到底想表达什么,但您仍然可以获得很多关于它的大致含义的信息。

词形还原

词形还原是减少词的变形形式同时仍确保简化形式属于该语言的过程。 这种简化形式或词根称为引理(lemma)。

例如,organizes、organized 和 organizing 都是 organize 的不同形式。 在这里,组织是引理。 单词的变形可以让你表达不同的语法类别,比如时态(organized vs organize)、复数(trains vs train)等等。 词形还原是必要的,因为它可以帮助您减少单词的变形形式,以便可以将它们作为单个项目进行分析。 它还可以帮助您规范化文本。

spaCy 在 Token 类上放置了一个 lemma_ 属性。 此属性具有标记的词形还原形式:

>>> import spacy >>> nlp = spacy.load("en_core_web_sm") >>> conference_help_text = ( ... "Gus is helping organize a developer" ... " conference on Applications of Natural Language" ... " Processing. He keeps organizing local Python meetups" ... " and several internal talks at his workplace." ... ) >>> conference_help_doc = nlp(conference_help_text) >>> for token in conference_help_doc: ... if str(token) != str(token.lemma_): ... print(f"{str(token):>20} : {str(token.lemma_)}") ... is : be He : he keeps : keep organizing : organize meetups : meetup talks : talk

在此示例中,您检查原始单词是否与引理不同,如果不同,则打印原始单词及其引理。

例如,你会注意到 organization 归约到它的原始形式 organize。 如果你不对文本进行词形还原,那么 organized 和 organizing 将被视为不同的标记,即使它们都指的是同一个概念。 词形还原可帮助您避免在概念上可能重叠的重复词。

词频

您现在可以将给定的文本转换为标记并对其执行统计分析。 此分析可以为您提供各种见解,例如文本中的常用词或独特词:

>>> import spacy >>> from collections import Counter >>> nlp = spacy.load("en_core_web_sm") >>> complete_text = ( ... "Gus Proto is a Python developer currently" ... " working for a London-based Fintech company. He is" ... " interested in learning Natural Language Processing." ... " There is a developer conference happening on 21 July" ... ' 2019 in London. It is titled "Applications of Natural' ... ' Language Processing". There is a helpline number' ... " available at 44-1234567891. Gus is helping organize it." ... " He keeps organizing local Python meetups and several" ... " internal talks at his workplace. Gus is also presenting" ... ' a talk. The talk will introduce the reader about "Use' ... ' cases of Natural Language Processing in Fintech".' ... " Apart from his work, he is very passionate about music." ... " Gus is learning to play the Piano. He has enrolled" ... " himself in the weekend batch of Great Piano Academy." ... " Great Piano Academy is situated in Mayfair or the City" ... " of London and has world-class piano instructors." ... ) >>> complete_doc = nlp(complete_text) >>> words = [ ... token.text ... for token in complete_doc ... if not token.is_stop and not token.is_punct ... ] >>> print(Counter(words).most_common(5)) [('Gus', 4), ('London', 3), ('Natural', 3), ('Language', 3), ('Processing', 3)]

通过仅查看常用词,您可能可以假设该文本是关于 Gus、London 和自然语言处理(Natural Language Processing)的。 这是一个重大发现! 如果你能只看最常见的词,那可能会节省你很多阅读量,因为你可以立即判断文本是否是关于你感兴趣的东西。

这并不是说这个过程一定会给你带来好结果。 毕竟,您会在此过程中丢失一些信息。

为了说明为什么删除停用词很有用,下面是包含停用词的同一文本的另一个示例:

>>> Counter( ... [token.text for token in complete_doc if not token.is_punct] ... ).most_common(5) [('is', 10), ('a', 5), ('in', 5), ('Gus', 4), ('of', 4)]

五分之四的最常用词是停用词,它们并不能真正告诉您有关摘要文本的太多信息。 这就是为什么停用词在许多应用程序中通常被认为是噪音。

词性标注

词性或 POS 是一种语法角色,用于解释特定单词在句子中的使用方式。 通常有八个词性:

  1. Noun 名词
  2. Pronoun 代词
  3. Adjective 形容词
  4. Verb 动词
  5. Adverb 副词
  6. Preposition 介词
  7. Conjunction 连接词
  8. Interjection 感叹词

词性标注是根据每个标记在句子中的用法为其分配词性标记的过程。 词性标记可为每个词分配句法类别,如名词或动词。

在 spaCy 中,POS 标签作为 Token 对象的一个属性可用:

>>> import spacy >>> nlp = spacy.load("en_core_web_sm") >>> about_text = ( ... "Gus Proto is a Python developer currently" ... " working for a London-based Fintech" ... " company. He is interested in learning" ... " Natural Language Processing." ... ) >>> about_doc = nlp(about_text) >>> for token in about_doc: ... print( ... f""" ... TOKEN: {str(token)} ... ===== ... TAG: {str(token.tag_):10} POS: {token.pos_} ... EXPLANATION: {spacy.explain(token.tag_)}""" ... ) ... TOKEN: Gus ===== TAG: NNP POS: PROPN EXPLANATION: noun, proper singular TOKEN: Proto ===== TAG: NNP POS: PROPN EXPLANATION: noun, proper singular TOKEN: is ===== TAG: VBZ POS: AUX EXPLANATION: verb, 3rd person singular present TOKEN: a ===== TAG: DT POS: DET EXPLANATION: determiner TOKEN: Python ===== TAG: NNP POS: PROPN EXPLANATION: noun, proper singular ...

在这里,使用 f-strings 访问和打印 Token 类的两个属性:

  1. .tag_ 显示一个细粒度的标签。
  2. .pos_ 显示粗粒度标签,它是细粒度标签的缩减版本。

您还可以使用 spacy.explain() 来提供有关特定 POS 标签的详细描述,这可能是一个有价值的参考工具。

通过使用 POS 标签,您可以提取特定类别的单词:

>>> nouns = [] >>> adjectives = [] >>> for token in about_doc: ... if token.pos_ == "NOUN": ... nouns.append(token) ... if token.pos_ == "ADJ": ... adjectives.append(token) ... >>> nouns [developer, company] >>> adjectives [interested]

您可以使用这种类型的词分类来获得见解。 例如,您可以通过分析哪些形容词最常与名词一起使用来衡量情绪。

可视化:使用 displaCy

spaCy 带有一个名为 displaCy 的内置可视化工具。 您可以使用它在浏览器或 Jupyter 笔记本中可视化依赖项解析或命名实体。

您可以使用 displaCy 查找token的 POS 标签:

>>> import spacy >>> from spacy import displacy >>> nlp = spacy.load("en_core_web_sm") >>> about_interest_text = ( ... "He is interested in learning Natural Language Processing." ... ) >>> about_interest_doc = nlp(about_interest_text) >>> displacy.serve(about_interest_doc, style="dep")

上面的代码将启动一个简单的 Web 服务器。 然后,您可以在浏览器中访问 http://127.0.0.1:5000 来查看可视化效果:

python自然语言开发(在Python中使用)(2)

在上图中,每个token都分配有一个 POS 标签,写在token正下方。

您还可以在 Jupyter notebook 中使用 displaCy:

In [1]: displacy.render(about_interest_doc, style="dep", jupyter=True)

试一试不同的文本,看看 spaCy 如何解构句子。 此外,请查看一些可用于自定义可视化的显示选项。

预处理函数

要将您的文本转换为适合分析的格式,您可以编写预处理函数来封装您的清理过程。 例如,在本节中,您将创建一个应用以下操作的预处理:

预处理函数将文本转换为可分析的格式。 对于大多数 NLP 任务来说,这是典型的一个例子:

>>> import spacy >>> nlp = spacy.load("en_core_web_sm") >>> complete_text = ( ... "Gus Proto is a Python developer currently" ... " working for a London-based Fintech company. He is" ... " interested in learning Natural Language Processing." ... " There is a developer conference happening on 21 July" ... ' 2019 in London. It is titled "Applications of Natural' ... ' Language Processing". There is a helpline number' ... " available at 44-1234567891. Gus is helping organize it." ... " He keeps organizing local Python meetups and several" ... " internal talks at his workplace. Gus is also presenting" ... ' a talk. The talk will introduce the reader about "Use' ... ' cases of Natural Language Processing in Fintech".' ... " Apart from his work, he is very passionate about music." ... " Gus is learning to play the Piano. He has enrolled" ... " himself in the weekend batch of Great Piano Academy." ... " Great Piano Academy is situated in Mayfair or the City" ... " of London and has world-class piano instructors." ... ) >>> complete_doc = nlp(complete_text) >>> def is_token_allowed(token): ... return bool( ... token ... and str(token).strip() ... and not token.is_stop ... and not token.is_punct ... ) ... >>> def preprocess_token(token): ... return token.lemma_.strip().lower() ... >>> complete_filtered_tokens = [ ... preprocess_token(token) ... for token in complete_doc ... if is_token_allowed(token) ... ] >>> complete_filtered_tokens ['gus', 'proto', 'python', 'developer', 'currently', 'work', 'london', 'base', 'fintech', 'company', 'interested', 'learn', 'natural', 'language', 'processing', 'developer', 'conference', 'happen', '21', 'july', '2019', 'london', 'title', 'applications', 'natural', 'language', 'processing', 'helpline', 'number', 'available', ' 44', '1234567891', 'gus', 'help', 'organize', 'keep', 'organize', 'local', 'python', 'meetup', 'internal', 'talk', 'workplace', 'gus', 'present', 'talk', 'talk', 'introduce', 'reader', 'use', 'case', 'natural', 'language', 'processing', 'fintech', 'apart', 'work', 'passionate', 'music', 'gus', 'learn', 'play', 'piano', 'enrol', 'weekend', 'batch', 'great', 'piano', 'academy', 'great', 'piano', 'academy', 'situate', 'mayfair', 'city', 'london', 'world', 'class', 'piano', 'instructor']

请注意,complete_filtered_tokens 不包含任何停用词或标点符号,它完全由词形化的小写标记组成。

使用 spaCy 进行基于规则的匹配

基于规则的匹配是从非结构化文本中提取信息的步骤之一。 它用于根据模式(如小写)和语法特征(如词性)识别和提取标记和短语。

虽然您可以使用正则表达式来提取实体(例如电话号码),但 spaCy 中基于规则的匹配比单独使用正则表达式更强大,因为您可以包含语义或语法过滤器。

例如,使用基于规则的匹配,您可以提取名字和姓氏,它们始终是专有名词:

>>> import spacy >>> nlp = spacy.load("en_core_web_sm") >>> about_text = ( ... "Gus Proto is a Python developer currently" ... " working for a London-based Fintech" ... " company. He is interested in learning" ... " Natural Language Processing." ... ) >>> about_doc = nlp(about_text) >>> from spacy.matcher import Matcher >>> matcher = Matcher(nlp.vocab) >>> def extract_full_name(nlp_doc): ... pattern = [{"POS": "PROPN"}, {"POS": "PROPN"}] ... matcher.add("FULL_NAME", [pattern]) ... matches = matcher(nlp_doc) ... for _, start, end in matches: ... span = nlp_doc[start:end] ... yield span.text ... >>> next(extract_full_name(about_doc)) 'Gus Proto'

在这个例子中,pattern 是一个对象列表,它定义了要匹配的标记的组合。 其中的两个 POS 标签都是 PROPN(专有名词)。 因此,该模式由两个对象组成,其中两个令牌的 POS 标签都应该是 PROPN。 然后使用 .add() 方法将该模式添加到 Matcher,该方法采用键标识符和模式列表。 最后,获得匹配的开始和结束索引。

您还可以使用基于规则的匹配来提取电话号码:

>>> conference_org_text = ("There is a developer conference" ... " happening on 21 July 2019 in London. It is titled" ... ' "Applications of Natural Language Processing".' ... " There is a helpline number available" ... " at (123) 456-7891") ... >>> def extract_phone_number(nlp_doc): ... pattern = [ ... {"ORTH": "("}, ... {"SHAPE": "ddd"}, ... {"ORTH": ")"}, ... {"SHAPE": "ddd"}, ... {"ORTH": "-", "OP": "?"}, ... {"SHAPE": "dddd"}, ... ] ... matcher.add("PHONE_NUMBER", None, pattern) ... matches = matcher(nlp_doc) ... for match_id, start, end in matches: ... span = nlp_doc[start:end] ... return span.text ... >>> conference_org_doc = nlp(conference_org_text) >>> extract_phone_number(conference_org_doc) '(123) 456-7891'

在此示例中,更新模式以匹配电话号码。 在这里,还使用了token的一些属性:

将这些词典链接在一起可以让您更灵活地选择匹配条件。

注意:为简单起见,在示例中,假定电话号码具有特定格式:(123) 456-7891。 您可以根据您的用例更改此设置。

同样,基于规则的匹配通过根据词汇模式和语法特征进行匹配来帮助您识别和提取标记和短语。 当您想寻找特定实体时,这会很有用。

使用 spaCy 进行依赖分析

依赖分析是提取句子的依存关系图来表示其语法结构的过程。 它定义了词条与其从属词之间的依赖关系。 句首没有依存关系,称为句根。 动词通常是句子的词根。 所有其他词都链接到词条。

依赖关系可以映射到有向图表示中,其中:

依存分析可以帮助您了解一个词在文本中扮演什么角色以及不同词之间的关系。

以下是如何使用依存关系分析来查找单词之间的关系:

>>> import spacy >>> nlp = spacy.load("en_core_web_sm") >>> piano_text = "Gus is learning piano" >>> piano_doc = nlp(piano_text) >>> for token in piano_doc: ... print( ... f""" ... TOKEN: {token.text} ... ===== ... {token.tag_ = } ... {token.head.text = } ... {token.dep_ = }""" ... ) ... TOKEN: Gus ===== token.tag_ = 'NNP' token.head.text = 'learning' token.dep_ = 'nsubj' TOKEN: is ===== token.tag_ = 'VBZ' token.head.text = 'learning' token.dep_ = 'aux' TOKEN: learning ===== token.tag_ = 'VBG' token.head.text = 'learning' token.dep_ = 'ROOT' TOKEN: piano ===== token.tag_ = 'NN' token.head.text = 'learning' token.dep_ = 'dobj'

在这个例子中,句子包含三个关系:

  1. nsubj 是单词的主语,它的中心词是动词。
  2. aux 是助词,中心词是动词。
  3. dobj 是动词的直接宾语,它的中心词也是动词。

关系列表并不是 spaCy 特有的。 相反,它是语言学研究的一个不断发展的领域。

您还可以使用 displaCy 可视化句子的依赖树:

>>> displacy.serve(piano_doc, style="dep")

此代码将生成一个可视化效果,您可以通过在浏览器中打开 http://127.0.0.1:5000 来访问它:

python自然语言开发(在Python中使用)(3)

这张图片直观地告诉你,句子的主语是专有名词Gus,它和piano有learn关系。

树和子树导航

依赖图具有树型结构的所有属性。 其中包含有关句子结构和语法的信息,可以以不同的方式遍历以提取关系。

spaCy 提供了诸如 .children、.lefts、.rights 和 .subtree 之类的属性,以便更轻松地导航解析树。 以下是使用这些属性的几个示例:

>>> import spacy >>> nlp = spacy.load("en_core_web_sm") >>> one_line_about_text = ( ... "Gus Proto is a Python developer" ... " currently working for a London-based Fintech company" ... ) >>> one_line_about_doc = nlp(one_line_about_text) >>> #提取 `developer` 的子节点 >>> print([token.text for token in one_line_about_doc[5].children]) ['a', 'Python', 'working'] >>> # 提取 `developer` 的前一个相邻节点 >>> print (one_line_about_doc[5].nbor(-1)) Python >>> # 提取 `developer` 的下一个相邻节点 >>> print (one_line_about_doc[5].nbor()) currently >>> # 提取 `developer` 左侧的所有标记 >>> print([token.text for token in one_line_about_doc[5].lefts]) ['a', 'Python'] >>> #提取 `developer` 右侧的 token >>> print([token.text for token in one_line_about_doc[5].rights]) ['working'] >>> # 打印 `developer` 的子树 >>> print (list(one_line_about_doc[5].subtree)) [a, Python, developer, currently, working, for, a, London, -, based, Fintech company]

在这些示例中,您已经了解了导航句子依存关系树的各种方法。

浅解析

浅层解析或分块是从非结构化文本中提取短语的过程。 这涉及根据词性标签将相邻词组分块成短语。 有一些标准的众所周知的块,例如名词短语、动词短语和介词短语。

名词短语检测

名词短语是以名词为中心的短语。 它还可以包括其他种类的词,例如形容词、序数词和限定词。 名词短语对于解释句子的上下文很有用。 它们可以帮助您理解句子的含义。

spaCy 在 Doc 对象上有属性 .noun_chunks。 您可以使用此属性来提取名词短语:

>>> import spacy >>> nlp = spacy.load("en_core_web_sm") >>> conference_text = ( ... "There is a developer conference happening on 21 July 2019 in London." ... ) >>> conference_doc = nlp(conference_text) >>> # 提取名词短语 >>> for chunk in conference_doc.noun_chunks: ... print (chunk) ... a developer conference 21 July London

通过查看名词短语,您可以获得有关文本的信息。 例如,开发者大会表示文本提到了一个会议,而日期 21 July 让您知道会议定于 7 月 21 日举行。

这是另一种总结文本并获得最重要信息的方法,而无需实际阅读所有内容。

动词短语检测

动词短语是由至少一个动词组成的句法单位。 这个动词可以由其他组块连接,例如名词短语。 动词短语对于理解名词所涉及的动作很有用。

spaCy 没有提取动词短语的内置功能,因此您需要一个名为 textacy 的库。 您可以使用 pip 安装 textacy:

(venv) $ python -m pip install textacy

现在您已经安装了 textacy,使用它根据语法规则提取动词短语:

>>> import textacy >>> about_talk_text = ( ... "The talk will introduce reader about use" ... " cases of Natural Language Processing in" ... " Fintech, making use of" ... " interesting examples along the way." ... ) >>> patterns = [{"POS": "AUX"}, {"POS": "VERB"}] >>> about_talk_doc = textacy.make_spacy_doc( ... about_talk_text, lang="en_core_web_sm" ... ) >>> verb_phrases = textacy.extract.token_matches( ... about_talk_doc, patterns=patterns ... ) >>> # 打印所有动词短语 >>> for chunk in verb_phrases: ... print(chunk.text) ... will introduce >>> # Extract noun phrase to explain what nouns are involved >>> for chunk in about_talk_doc.noun_chunks: ... print (chunk) ... this talk the speaker the audience the use cases Natural Language Processing Fintech use interesting examples the way

在此示例中,动词短语 introduce 表示将介绍某事。 通过查看名词短语,您可以拼凑将要介绍的内容——同样,无需阅读整篇文章。

命名实体识别

命名实体识别 (NER) 是在非结构化文本中定位命名实体,然后将它们分类到预定义类别中的过程,例如人名、组织、位置、货币价值、百分比和时间表达式。

您可以使用 NER 来了解有关文本含义的更多信息。 例如,您可以使用它来填充一组文档的标签,以改进关键字搜索。 您还可以使用它来将客户支持票归类到相关类别中。

spaCy 在 Doc 对象上具有 .ents 属性。 您可以使用它来提取命名实体:

>>> import spacy >>> nlp = spacy.load("en_core_web_sm") >>> piano_class_text = ( ... "Great Piano Academy is situated" ... " in Mayfair or the City of London and has" ... " world-class piano instructors." ... ) >>> piano_class_doc = nlp(piano_class_text) >>> for ent in piano_class_doc.ents: ... print( ... f""" ... {ent.text = } ... {ent.start_char = } ... {ent.end_char = } ... {ent.label_ = } ... spacy.explain('{ent.label_}') = {spacy.explain(ent.label_)}""" ... ) ... ent.text = 'Great Piano Academy' ent.start_char = 0 ent.end_char = 19 ent.label_ = 'ORG' spacy.explain('ORG') = Companies, agencies, institutions, etc. ent.text = 'Mayfair' ent.start_char = 35 ent.end_char = 42 ent.label_ = 'LOC' spacy.explain('LOC') = Non-GPE locations, mountain ranges, bodies of water ent.text = 'the City of London' ent.start_char = 46 ent.end_char = 64 ent.label_ = 'GPE' spacy.explain('GPE') = Countries, cities, states

在上面的示例中,ent 是一个具有各种属性的 Span 对象:

spacy.explain 给出了每个实体标签的详细描述。 您还可以使用 displaCy 可视化这些实体:

>>> displacy.serve(piano_class_doc, style="ent")

如果您在浏览器中打开 http://127.0.0.1:5000,那么您将能够看到可视化效果:

python自然语言开发(在Python中使用)(4)

NER 的一个用例是从文本中提取人名。 例如,您可能希望这样做以隐藏在调查中收集的个人信息。 看看下面的例子:

>>> survey_text = ( ... "Out of 5 people surveyed, James Robert," ... " Julie Fuller and Benjamin Brooks like" ... " apples. Kelly Cox and Matthew Evans" ... " like oranges." ... ) >>> def replace_person_names(token): ... if token.ent_iob != 0 and token.ent_type_ == "PERSON": ... return "[REDACTED] " ... return token.text_with_ws ... >>> def redact_names(nlp_doc): ... with nlp_doc.retokenize() as retokenizer: ... for ent in nlp_doc.ents: ... retokenizer.merge(ent) ... tokens = map(replace_person_names, nlp_doc) ... return "".join(tokens) ... >>> survey_doc = nlp(survey_text) >>> print(redact_names(survey_doc)) Out of 5 people surveyed, [REDACTED] , [REDACTED] and [REDACTED] like apples. [REDACTED] and [REDACTED] like oranges.

在此示例中,replace_person_names() 使用 .ent_iob,它使用内外开始 (IOB) 标记给出命名实体标签的 IOB 代码。

redact_names() 函数使用重新标记器来调整标记化模型。 它获取所有标记并通过 map() 传递文本以用 [已编辑] 替换任何目标标记。

因此,就像那样,您可以在几秒钟内编辑大量文本,而手动完成可能需要几个小时。 也就是说,您始终需要小心编辑,因为模型并不完美!

总结

spaCy 是一个强大而先进的库,由于其速度、易用性、准确性和可扩展性,在 NLP 应用程序中广受欢迎。

在本教程中,您学习了:

您现在已经拥有了一些方便的工具,可以开始探索自然语言处理的世界。

,