快速掌握spacy在python中进行自然语言处理(附代码&链接)
摘要:本文与配套的Domino项目,简要介绍了如何使用spaCy和相关库在Python中处理自然语言(有时称为“文本分析”)。这有是一个用于理解文本的交互式可视化工具:scattertext(https://spacy.io/universe/project/scattertext),由Jason Kessler主导设计。
介绍
本文与配套的Domino项目,简要介绍了如何使用spaCy和相关库在Python中处理自然语言(有时称为“文本分析”)。业界的数据科学团队时常处理大量文本数据,这也是机器学习中使用的四大数据类别之一,通常是人为生成的文本,但也不全是这样。
想想看: 商业世界的“操作系统”是如何运行的? 通常,有合同(销售合同、工作协议、合作关系),发票,保险单,规章制度和其他法律条文等等。所有这些都被表示为文本。
你可能会遇到一些缩写词:自然语言处理(NLP),自然语言理解(NLU),自然语言生成(NLG),简单地说,分别是“阅读文本”、“理解意义”、“输出文本”。这些任务越来越多地重叠,而且很难分类。
spaCy框架——以及越来越多的插件和其他集成(包)——为各种各样的自然语言任务提供了支持。它已经成为Python中最广泛使用的工业级自然语言库之一,并且拥有相当大的社区,因此,随着该领域的快速发展,它为科研进展进展的商业化提供了足够地支持。
开始
我们已经在Domino中配置了默认的软件环境,以包含本教程所需的所有包、库、模型和数据。请查看Domino项目以运行代码。
如果您对Domino的计算环境如何工作感兴趣,请查看说明页面。
说明页面 https://support.dominodatalab.com/hc/en-us/articles/115000392643-Environment-management
现在让我们加载spaCy并运行一些代码:
<code>import spacy</code> <code>nlp = spacy.load("en_core_web_sm")</code>
该nlp变量现在是您通向所有spaCy的入口,并装载了en_core_web_sm英文模型。接下来,让我们通过自然语言解析器来运行一个小“文档”:
<code>text = "The rain in Spain falls mainly on the plain."</code> <code>doc = nlp(text)</code> <code>for token in doc:</code><code> </code> <code> print(token.text, token.lemma_, token.pos_, token.is_stop)</code>
<code>The the DET True</code><code>rain </code> <code>rain NOUN False</code><code>in </code> <code>in ADP True</code><code>Spain </code> <code>Spain PROPN False</code> <code>falls fall VERB False</code><code>mainly </code> <code>mainly ADV False</code> <code>on on ADP True</code> <code>the the DET True</code><code>plain </code> <code>plain NOUN False</code> <code>. . PUNCT False</code>
首先,我们从文本创建一个doc(注:spaCy中的一种数据结构)文档,它是一个容器,存放了文档以及文档对应的标注。然后我们遍历文档,看看spaCy解析了什幺。
由于信息有点多,读起来有点困难。让我们将这个句子的用spaCy解析结果重新格式化为pandas库的 dataframe:
<code>import pandas as pd</code> <code> </code><code>cols = ("text", "lemma", "POS", "explain", "stopword")</code> <code>rows = []</code> <code> </code><code>for t in doc:</code><code> </code> <code> row = [t.text, t.lemma_, t.pos_, spacy.explain(t.pos_), t.is_stop]</code><code> </code> <code> rows.append(row)</code> <code> </code><code>df = pd.DataFrame(rows, columns=cols)</code> <code> </code><code>df</code>
在这个简单的例子中,整个文档仅仅是一个简短的句子。对于这个句子中的每个单词,spaCy都创建了一个token,我们访问每个token中的字段来显示:
原始文本
词形(lemma)引理——这个词的词根形式
词性(part-of-speech)
是否是停用词的标志,比如一个可能会被过滤的常用词
接下来让我们使用displaCy库来可视化这个句子的解析树:
<code>from spacy import displacy</code> <code> </code><code>displacy.render(doc,)</code>
这会让你回想起小学时候的语文课吗? 坦率地说,对于我们这些来自计算语言学背景的人来说,这个图表会让我们感到开心。
我们先回顾一下,你是如何处理多个句子的?
比如,句边界检测(SBD)的功能,也称为句子分割,下例基于内置/默认的语句分析器:
<code>text = "We were all out at the zoo one day, I was doing some acting, walking on the railing of the gorilla exhibit. I fell in. Everyone screamed and Tommy jumped in after me, forgetting that he had blueberries in his front pocket. The gorillas just went wild."</code> <code> </code><code>doc = nlp(text)</code> <code> </code><code>for sent in doc.sents:</code><code> </code> <code> print(">", sent)</code>
<code>We were all out at the zoo one day, I was doing some acting, walking on the railing of the gorilla exhibit.</code> <code>I fell in.</code> <code>Everyone screamed and Tommy jumped in after me, forgetting that he had blueberries in his front pocket.</code> <code>The gorillas just went wild.</code>
当spaCy创建一个文档时,它使用了非破坏性标记原则,这意味着tokens、句子等只是长数组中的索引。换句话说,他们没有将文本切分成小段。因此,每个句子都是一个span(也是spaCy中的一种数据结构)单独,包含了它在文档数组中的开始和结束索引:
<code>for sent in doc.sents:</code><code> </code> <code> print(">", sent.start, sent.end)</code>
> 0 25
> 25 29
> 29 48
> 48 54
我们可以在文档数组上切片,取出一个句子的tokens:
<code>doc[48:54]</code> <code>The gorillas just went wild.</code>
或者只是找一个特定的token,例如最后一句话中的动词“went”:
<code>token = doc[51]</code> <code>print(token.text, token.lemma_, token.pos_)</code> <code>went go VERB</code>
此时,我们可以解析一个文档,将该文档分割成句子,然后查看每个句子中token的注释。这是一个好的开始。
获取文本
既然我们可以解析文本,那幺我们从哪里获得文本呢?一个便利的方法是利用互联网。当然,当我们下载网页时,我们会得到HTML文件,然后需要从文件中提取文本。这方面,Beautiful Soup是一个很流行的包。
首先将警告过滤掉:
<code>import sysimport </code> <code>warnings</code><code>warnings.filter</code> <code>warnings("ignore")</code>
在下面的函数get_text()中,我们将解析HTML以找到所有的<p/>标记,然后提取这些标记的文本:
<code>from bs4 import BeautifulSoup</code> <code>import requests</code> <code>import traceback</code> <code> </code><code>def get_text (url): </code><code> </code> <code> buf = []</code> <code> </code><code> try:</code><code> </code> <code> soup = BeautifulSoup(requests.get(url).text, "html.parser")</code> <code> </code><code> for p in soup.find_all("p"): </code><code> </code> <code> buf.append(p.get_text())</code> <code> </code><code> return "\n".join(buf)</code><code> </code> <code> except: </code><code> </code> <code> print(traceback.format_exc())</code><code> </code> <code> sys.exit(-1)</code>
现在让我们从网上获取一些文本。我们可以对比开源倡议上开源许可的情况。
开源倡议:
https://opensource.org/licenses/
<code>lic = {}</code> <code>lic["mit"] = nlp(get_text("https://opensource.org/licenses/MIT"))</code> <code>lic["asl"] = nlp(get_text("https://opensource.org/licenses/Apache-2.0"))</code> <code>lic["bsd"] = nlp(get_text("https://opensource.org/licenses/BSD-3-Clause"))</code><code>for sent in lic["bsd"].sents: print(">", sent)</code>
<code>> SPDX short identifier: BSD-3-Clause</code> <code>> Note: This license has also been called the "New BSD License" or "Modified BSD License"</code> <code>> See also the 2-clause BSD License</code><code>.</code> <code>…</code>
自然语言工作的一个常见用例是对比文本。
例如,有了这些开源许可,我们可以下载它们的文本,进行解析,然后比较它们之间的相似度:(https://spacy.io/api/doc#similarity)
<code>pairs = [</code><code> </code> <code> ["mit", "asl"], </code><code> </code> <code> ["asl", "bsd"], </code><code> </code> <code> ["bsd", "mit"]</code> <code>]</code> <code> </code><code>for a, b in pairs:</code> <code>print(a, b, lic[a].similarity(lic[b]))</code>
<code>mit asl 0.9482039305669306</code> <code>asl bsd 0.9391555350757145</code> <code>bsd mit 0.9895838089575453</code><code> </code>
这很有趣,因为BSD(https://opensource.org/licenses/BSD-3-Clause)和MIT(https://opensource.org/licenses/MIT)许可似乎是最相似的文档。
事实上,它们是密切相关的。
无可否认,由于OSI的免责声明,每个文档中都包含了一些额外的文本——但是这为比较许可证提供了一个合理的近似值。
自然语言理解
现在让我们深入了解一下spaCy中的NLU特性。假设我们要解析有一个文档,从纯语法的角度来看,我们可以提取名词块(https://spacy.io/usage/linguistic-features#noun-chunks),即每个名词短语:
<code>text = "Steve Jobs and Steve Wozniak incorporated Apple Computer on January 3, 1977, in Cupertino, California."</code> <code>doc = nlp(text)</code> <code> </code><code>for chunk in doc.noun_chunks: </code><code> </code> <code> print(chunk.text)</code>
<code>Steve Jobs</code> <code>Steve Wozniak</code> <code>Apple Computer</code> <code>January</code> <code>Cupertino</code> <code>California</code><code> </code>
句子中的名词短语通常提供更多的信息内容——作为一个简单的过滤器,可以将长文档简化为更“精练”的表达。
我们可以进一步采用这种方法,并在文本中标识命名实体(https://spacy.io/usage/linguistic-features#named-entities),即专有名词:
<code>for ent in doc.ents: </code> <code>print(ent.text, ent.label_)</code>
displacy.render(doc,)
如果你正在使用知识图谱(https://www.akbc.ws/2019/)的应用程序和其他关联数据(http://linkeddata.org/),那幺构建文档中的命名实体和其他相关信息的联系就是一种挑战,即文本链接
(http://nlpprogress.com/english/entity_linking.html)。
识别文档中的命名实体是这类型AI工作的第一步。例如,根据上面的文本,可以将“Steve Wozniak”这个命名实体链接到DBpedia中的查找链接(http://dbpedia.org/page/Steve_Wozniak)。
一般来说,人们还可以将词形与描述其含义的资源联系起来。例如,在前面的章节中,我们分析了“the gorillas just went wild”这个句子,并展示“went”这个词的词形是动词go。
此时,我们可以使用一个历史悠久的项目WordNet (https://wordnet.princeton.edu/),它为英语提供了一个词汇数据库——换句话说,它是一个可计算的近义词典。
有一个针对WordNet的spaCy集成,名为spaCy – WordNet(https://github.com/recognai/spacy-wordnet),作者是Daniel Vila Suero(https://twitter.com/dvilasuero),他是自然语言和知识图谱研究的专家。
然后我们将通过NLTK加载WordNet数据:
import nltk nltk.download("wordnet") [nltk_data] Downloading package wordnet to /home/ceteri/nltk_data... [nltk_data] Package wordnet is already up-to-date!True
请注意,spaCy像“管道(pipeline)”一样运行,并允许使用自定义的管道组件。这对于在数据科学中支持工作流是非常好的。在这里,我们将添加来自spacy-wordnet项目的Wordnet注释(器):
from spacy_wordnet.wordnet_annotator import WordnetAnnotator print("before", nlp.pipe_names) if "WordnetAnnotator" not in nlp.pipe_names: nlp.add_pipe(WordnetAnnotator(nlp.lang), after="tagger") print("after", nlp.pipe_names)before ['tagger', 'parser', 'ner']after ['tagger', 'WordnetAnnotator', 'parser', 'ner']
在英语中,有些词因为有多重含义而臭名昭着。
例如,在WordNet(http://wordnetweb.princeton.edu/perl/webwn?s=star&sub=Search+WordNet&o2&o0=1&o8=1&o1= 1&o1=1&o7&o5&o9&o6&o3&o4&h)搜索与单词withdraw相关的词义。
现在让我们使用spaCy执行自动查找:
token = nlp("withdraw")[0] token._.wordnet.synsets()
[Synset('withdraw.v.01'), Synset('retire.v.02'), Synset('disengage.v.01'), Synset('recall.v.07'), Synset('swallow.v.05'), Synset('seclude.v.01'), Synset('adjourn.v.02'), Synset('bow_out.v.02'), Synset('withdraw.v.09'), Synset('retire.v.08'), Synset('retreat.v.04'), Synset('remove.v.01')]
token._.wordnet.lemmas()
[Lemma('withdraw.v.01.withdraw'), Lemma('withdraw.v.01.retreat'), Lemma('withdraw.v.01.pull_away'), Lemma('withdraw.v.01.draw_back'), Lemma('withdraw.v.01.recede'), Lemma('withdraw.v.01.pull_back'), Lemma('withdraw.v.01.retire'), …
token._.wordnet.wordnet_domains()
['astronomy', 'school', 'telegraphy', 'industry', 'psychology', 'ethnology', 'ethnology', 'administration', 'school', 'finance', 'economy', 'exchange', 'banking', 'commerce', 'medicine', 'ethnology', 'university', …
同样,如果你使用的是知识图谱,那幺可以将来自WordNet的那些“词义”链接与图算法一起使用,以帮助识别特定单词的含义。还可以通过一种称为“摘要”的技术来为较大的文本段生成摘要。这些内容超出了本教程的范围,但它是目前工业中一个有趣的自然语言应用。
反过来说,如果你预先知道某个文档是关于某个特定领域或主题集的,则可以约束WordNet返回的含义。在下面的例子中,我们来考虑金融和银行领域数据的NLU结果:
domains = ["finance", "banking"] sentence = nlp("I want to withdraw 5,000 euros.") enriched_sent = [] for token in sentence: # get synsets within the desired domains synsets = token._.wordnet.wordnet_synsets_for_domain(domains) if synsets: lemmas_for_synset = [] for s in synsets: # get synset variants and add to the enriched sentence lemmas_for_synset.extend(s.lemma_names()) enriched_sent.append("({})".format("|".join(set(lemmas_for_synset)))) else: enriched_sent.append(token.text) print(" ".join(enriched_sent))
I (require|want|need) to (draw_off|withdraw|draw|take_out) 5,000 euros .
这个例子看起来很简单,但是,如果你修改domains列表,你会发现在没有合理约束的情况下,结果会产生组合爆炸。想象一下,有一个包含数百万元素的知识图谱:您希望在可能的地方限制搜索,以避免计算每个查询需要几天、几周、几个月、几年的时间。
有时在试图理解文本时遇到的问题—或者在试图理解语料库(包含许多相关文本的数据集)时遇到的问题—会变得非常复杂,您需要首先将其可视化。这有是一个用于理解文本的交互式可视化工具:scattertext(https://spacy.io/universe/project/scattertext),由Jason Kessler主导设计。
Jason Kessler
https://twitter.com/jasonkessler
让我们来分析一下2012年美国总统大选期间政党大会的文本数据。注意:这个部分可能需要几分钟来运行,但是所有这些数据处理的结果值得等待。
import scattertext as st if "merge_entities" not in nlp.pipe_names: nlp.add_pipe(nlp.create_pipe("merge_entities")) if "merge_noun_chunks" not in nlp.pipe_names: nlp.add_pipe(nlp.create_pipe("merge_noun_chunks")) convention_df = st.SampleCorpora.ConventionData2012.get_data() corpus = st.CorpusFromPandas(convention_df, category_col="party", text_col="text", nlp=nlp).build()
一旦语料库准备好了,就可以生成一个交互式可视化的HTML:
html = st.produce_scattertext_explorer( corpus, category="democrat", category_name="Democratic", not_category_name="Republican", width_in_pixels=1000, metadata=convention_df["speaker"] )
from IPython.display import IFrame file_name = "foo.html" with open(file_name, "wb") as f: f.write(html.encode("utf-8")) IFrame(src=file_name, width = 1200, height=700)
现在我们将渲染html:一到两分钟进行加载;
想象一下,如果你的组织中有过去三年客户支持某个特定产品的文本。假设您的团队需要了解客户是如何谈论该产品的? 这个scattertext库可能会非常方便! 您可以将(k=2)聚类在NPS得分(客户评估指标)上,然后用聚类中的前两个分类替换民主党/共和党维度。
总结
五年前,如果你询问用Python中的自然语言的开源库,许多数据科学工作者的默认答案是NLTK(https://www.nltk.org/)。这个项目几乎包括了所有的东西,除了一些细微的设置,还有一些相对学术的部分。
另一个流行的自然语言项目是来自斯坦福的CoreNLP (https://stanfordnlp.github)。尽管CoreNLP功能强大,但它也非常学术化,不过要将它与其他软件集成以供生产使用是很有挑战性的。
几年前,自然语言的一切都开始发生了变化。spaCy的两位主要作者——马修•洪尼巴尔(Matthew Honnibal, https://twitter.com/honnibal)和伊内斯•蒙塔尼(Ines Montani, https://twitter.com/_inesmontani)于2015年启动了该项目,该项目很快被业界采用。他们采用的是一种专注的方法(做需要做的,把它做好,不多也不少),这种方法能简单、快速地集成到Python中的数据科学工作集合中,并且比其他方法执行更快、准确性更好。
基于这些,spaCy成为了NLTK的对立面。自2015年以来,spaCy一直致力于成为一个开源项目(即,取决于其社区的方向,集成等)和商业级软件(而非学术研究)。也就是说,spaCy迅速地将机器学习方面的最前沿进展纳入中,有效地成为了将学术研究推广到工业领域的渠道。
值得注意的是,随着谷歌开始赢得国际语言翻译比赛,用于自然语言的的机器学习自2000年中期得到了很大的发展。2017年至2018年期间,随着深度学习的诸多成功,这些方法开始超越以前的机器学习模型,出现了另一个重大变化。
例如,经Allen AI研究提出的看到ELMo 语言嵌入模型, 随后是谷歌的BERT,(https://ai.googleblog.com/2018/11/open-sourcing-bert-state-of-art-pre.html),以及最近由
百度推出的ERNIE
(https://medium.com/syncedreview/baidus-ernie-tops-google-s-bert-in-chinese-nlp-tasks-d6a42b49223d)——换句话说,搜索引擎巨头为我们献上了一份基于深度学习的嵌入语言模型开源大礼的世界,目前是业界最先进的。
说到这里,为了紧随自然语言的SOTA,可以关注NLP-Progress(http://nlpprogress.com/)和Papers with Cod(https://paperswithcode.com/sota)。
在过去的两年里,随着深度学习技术的兴起,自然语言的用例发生了巨大的变化。大约在2014年,使用Python的自然语言教程可能还在教单词统计、关键字搜索或情感检测,而且目标用例相对显得平淡无奇。而在2019年,我们谈论的是在一个产业供应链的优化中分析成千上万的供应商合同文件,或者是为保险公司分析的投保人数亿份文件,又或者是大量关于财务数据披露的文件。更现代的自然语言工作倾向于在NLU,通常支持知识图谱的构建,在NLG领域,大量类似的文档可以被大规模地总结。
广阔的宇宙(https://spacy.io/universe)很不错,可以查找特定用例的深度,并查看这个领域是如何发展的。这个“宇宙”的一些选择包括:
blackstone(https://spacy.io/universe/project/blackstone)-解析非结构化法律信息文本
kindred(https://spacy.io/universe/project/kindred) -从生物医学文本(如Pharma)中提取实体
mordecai(https://spacy.io/universe/project/mordecai)-解析地理信息
Prodigy(https://spacy.io/universe/project/prodigy)-人机回圈的标签数据集注释spacy-raspberry (https://spacy.io/universe/project/spacy-raspberry) – 树莓派(Raspberry PI)图像,用于在边界设备上运行。
Rasa NLU(https://spacy.io/universe/project/rasa)聊天应用的集合
另外还有一些非常新的项目需要关注:
spacy-pytorch-transformers (https://explosion.ai/blog/spacy-pytorch-transformers)可以用来与BERT, GPT-2, XLNet,等等进行调整。
spaCy IRL 2019(https://irl.spacy.io/2019/)会议-宽大的IRL 2019(https://irl.spacy.io/2019/)会议-查看演讲视频!对于spaCy,我们可以做的还有很多——希望本教程能够提供介绍。我们祝愿你在自然语言学习方面一切顺利。
对于spaCy,我们可以做的还有很多——希望本教程能够提供介绍。我们祝愿你在自然语言学习方面一切顺利。
原文地址:
https://www.kdnuggets.com/2019/09/natural-language-python-using-spacy-introduction.html