语言特征
注意以下代码示例都需要导入spacy
import spacy
nlp = spacy.load('en')
#导入模型库
使用 spaCy提取语言特征,比如说词性标签,语义依赖标签,命名实体,定制tokenizer并与基于规则的matcher一起工作。
处理原始文本是很困难的:大多数单词都是很少见的,而对于有些看起来完全不同的单词来说,它们的意思可能是一样的。相同的词在不同的顺序可以意味着完全不同的东西。在很多语言中将文本分割成类单词单元都是困难的。虽然从原始字符开始解决一些问题是可能的,但是最好使用语言知识来添加有用的信息。这正是spaCy的设计目的:您输入原始文本,然后返回一个Doc对象,它带有各种注释。
词性标注(PoS)
在标记化之后,spaCy可以解析和标记给定的Doc。这就是统计模型出现的地方,它使得spaCy能够预测在这个上下文中最有可能应用的标签或标签。一个模型由二进制数据组成,它通过显示一个系统足够的例子来进行预测,从而在整个语言中进行预测——例如,在英语中“the”后面的单词最有可能是名词。
语言标注可以作为Token的属性,与许多NLP库一样,spaCy将所有字符串编码为散列值,以减少内存使用量并提高效率。为了获得属性的可读字符串表示,我们需要在其名称中添加一个_:
doc = nlp(u'Apple is looking at buying U.K. startup for $1 billion')
for token in doc:
print(token.text, token.lemma_, token.pos_, token.tag_, token.dep_,
token.shape_, token.is_alpha, token.is_stop)
TEXT | LEMMA | POS | TAG | DEP | SHAPE | ALPHA | STOP |
---|---|---|---|---|---|---|---|
Apple | apple | PROPN | NNP | nsubj | Xxxxx | True | False |
is | be | VERB | VBZ | aux | xx | True | True |
at | at | ADP | IN | prep | xx | True | True |
使用spaCy's的内建可视化工具,还可以看到示例中的依存句法结构(这里不添加附图啦)
依存句法分析(Dependency parsing)
spaCy具有快速和准确的语法依赖解析器,并且有一个用于构建句法树的强大API。解析器有检测句子边界的强大能力,并允许您遍历基本名词短语或“块”。您可以检查Doc对象是否已经被is_parsed属性解析了,它(is_parsed)返回一个布尔值。如果此属性为False,则默认语句迭代器将引发异常。
名词块(Noun chunks)
名词块是“基本名词短语”——用名词作头部的扁平短语。你可以把名词块当作名词,再加上描述名词的词,例如“奢侈的绿草”或“世界上最大的科技基金”。要获取文档中的名词块,只需遍历Doc.noun_chunks。
nlp = spacy.load('en')
doc = nlp(u'Autonomous cars shift insurance liability toward manufacturers')
for chunk in doc.noun_chunks:
print(chunk.text, chunk.root.text, chunk.root.dep_,
chunk.root.head.text)
tips
text:原始的名词块文本
root text:名词的块的核心部分(见example)
命名实体识别(NER)
一个被命名的实体是一个“现实世界的对象”,它被赋予一个名字——例如,一个人,一个国家,一个产品或者一个书名。spaCy可以通过询问模型来识别文档中的各种类型的命名实体。因为模型是统计的,并且非常依赖于他们所接受的示例,所以这并不总是完美的,并且可能需要稍后进行一些调整,这取决于您的用例。
命名实体可以doc的ents中获取
doc = nlp(u'Apple is looking at buying U.K. startup for $1 billion')
for ent in doc.ents:
print(ent.text, ent.start_char, ent.end_char, ent.label_)
运用可视化工具可以看到如下效果:
规则匹配
spaCy有一个规则匹配引擎,即Matcher,它通过token进行操作,类似于正则表达式。规则可以参考token注释(例如,token文本或标记,以及标记(例如IS_PUNCT)。规则matcher还允许您通过自定义回调来执行匹配操作——例如,合并实体并应用自定义标签。您还可以将模式与实体id关联起来,以允许一些基本的实体链接或消除歧义。为了匹配大型术语表,可以使用PhraseMatcher,它接受Doc对象作为匹配模式。
添加模型(adding patterns)
假设我们想找到由以下三个token组成的字段:
1、以小写形式匹配‘hello’,例如:‘Hello’,‘HELLO’
2、is_punct 标签为真的token,例如:任何标点
3、以小写形式匹配‘world’,例如:‘World’,‘WORLD’
[{'LOWER': 'hello'}, {'IS_PUNCT': True}, {'LOWER': 'world'}]
首先,我们用vocab初始化Matcher。matcher必须始终与它将操作的文档共享同一个vocab。我们现在可以调用matcher.add()和一个ID和我们的定制模式。第二个参数允许您传递一个可选的回调函数,以调用成功的匹配。现在,我们将它设置为None。
import spacy
from spacy.matcher import Matcher
nlp = spacy.load('en')
matcher = Matcher(nlp.vocab)
# add match ID "HelloWorld" with no callback and one pattern
pattern = [{'LOWER': 'hello'}, {'IS_PUNCT': True}, {'LOWER': 'world'}]
matcher.add('HelloWorld', None, pattern)
#这里的“helloworld”可以理解为一种代号,和索引,可以取其他任何名字
doc = nlp(u'Hello, world! Hello world!')
matches = matcher(doc)
matcher返回一个(match_id, start, end)元组列表——在本例中,[(HelloWorld', 0,2)],映射到我们原始文档的span doc[0:2]。我们还可以选择添加多个模式,例如在“hello”和“world”之间不加标点的匹配序列。
matcher.add('HelloWorld', None,
[{'LOWER': 'hello'}, {'IS_PUNCT': True}, {'LOWER': 'world'}],
[{'LOWER': 'hello'}, {'LOWER': 'world'}])
默认情况下,matcher只会返回匹配项,而不会执行其他操作,比如合并实体或分配标签。这一切都取决于您,可以为每个模式单独定义,通过传入一个回调函数作为add()的on_match参数。这是有用的,因为它允许您编写完全自定义和模式特定的逻辑。例如,您可能希望将一些模式合并到一个令牌中,同时为其他模式类型添加实体标签。您不应该为每个流程创建不同的匹配器。
可获取的token属性
attribute | description |
---|---|
ORTH | token精确的文本 |
LOWER, UPPER | token文本的大小写形式 |
IS_ALPHA, IS_ASCII, IS_DIGIT | token文本由字母数字字符、ASCII字符、数字组成 |
IS_LOWER, IS_UPPER, IS_TITLE | token文本为大写,小写,标题格式 |
IS_PUNCT, IS_SPACE, IS_STOP | token文本是标点,空白,停用词 |
LIKE_NUM, LIKE_URL, LIKE_EMAIL | token文本类似于数字,网址,邮件地址 |
POS, TAG, DEP, LEMMA, SHAPE | token文本的词性之类的…… |
可获取的pattern就是大写的token属性,和匹配相关的大部分属性如下:
attribute | description |
---|---|
ORTH | token精确的文本 |
LOWER, UPPER | token文本的大小写形式 |
IS_ALPHA, IS_ASCII, IS_DIGIT | token文本由字母数字字符、ASCII字符、数字组成 |
IS_LOWER, IS_UPPER, IS_TITLE | token文本为大写,小写,标题格式 |
IS_PUNCT, IS_SPACE, IS_STOP | token文本是标点,空白,停用词 |
LIKE_NUM, LIKE_URL, LIKE_EMAIL | token文本类似于数字,网址,邮件地址 |
POS, TAG, DEP, LEMMA, SHAPE | token文本的词性之类的…… |
使用通配符token样式
虽然token属性提供了许多选项来编写高度特定的模式,但您也可以使用空的字典({})作为表示任何标记的通配符。如果你知道你想要匹配的内容的上下文,但是对于特定的token和它的字符非常少,那么通配符就显得很有用了。例如,假设您试图从数据中提取用户的用户名。您只知道它们被列为“用户名:{用户名}”。名称本身可能包含任何字符,但没有空格——因此您将知道它将作为一个token处理。
[{'ORTH': 'User'}, {'ORTH': 'name'}, {'ORTH': ':'}, {}]
使用类似正则表达式一样的通配符号
待补充
添加短语样式
如果您需要匹配大型术语表,您还可以使用PhraseMatcher并创建Doc对象,而不是token模式,这在总体上更有效。Doc模式可以包含单个或多个token。
import spacy
from spacy.matcher import PhraseMatcher
nlp = spacy.load('en')
matcher = PhraseMatcher(nlp.vocab)
terminology_list = ['Barack Obama', 'Angela Merkel', 'Washington, D.C.']
patterns = [nlp(text) for text in terminology_list]
matcher.add('TerminologyList', None, *patterns)
doc = nlp(u"German Chancellor Angela Merkel and US President Barack Obama "
u"converse in the Oval Office inside the White House in Washington, D.C.")
matches = matcher(doc)
由于spaCy用于处理模式和匹配的文本,因此您不必担心特定的标记化——例如,您可以简单地通过nlp(u“Washington, D.C.”),而不必编写一个复杂的token模式来覆盖术语的确切标记。
添加on-match规则
为了实现一个更现实的例子,假设您正在处理大量的博客文章,您想要匹配所有提到的"Google I/O"(spaCy将其标记为['Google', 'I', '/', 'O'])。为了安全起见,您只匹配大写版本,以防有人将其写成"Google i/o"。您还添加了一个附加的{IS_DIGIT: True}标记的第二个模式——这将确保您也匹配"Google I/O 2017"。如果您的模式匹配,spaCy应该执行您的自定义回调函数add_event_ent。
import spacy
from spacy.matcher import Matcher
nlp = spacy.load('en')
matcher = Matcher(nlp.vocab)
# Get the ID of the 'EVENT' entity type. This is required to set an entity.
EVENT = nlp.vocab.strings['EVENT']
def add_event_ent(matcher, doc, i, matches):
# Get the current match and create tuple of entity label, start and end.
# Append entity to the doc's entity. (Don't overwrite doc.ents!)
match_id, start, end = matches[i]
doc.ents += ((EVENT, start, end),)
matcher.add('GoogleIO', add_event_ent,
[{'ORTH': 'Google'}, {'UPPER': 'I'}, {'ORTH': '/'}, {'UPPER': 'O'}],
[{'ORTH': 'Google'}, {'UPPER': 'I'}, {'ORTH': '/'}, {'UPPER': 'O'}, {'IS_DIGIT': True}])
除了提到“谷歌I/O”之外,你的数据还包含一些恼人的预处理程序,比如剩下的HTML行中断(例如: <br>或<br />)。当您在它的时候,您希望将它们合并到一个标记中并标记它们,以确保您稍后可以轻松地忽略它们。因此,添加第二个模式并传入函数merge_and_flag:
BAD_HTML_FLAG = nlp.vocab.add_flag(lambda text: False)
def merge_and_flag(matcher, doc, i, matches):
match_id, start, end = matches[i]
span = doc[start : end]
span.merge(is_stop=True) # merge (and mark it as a stop word, just in case)
span.set_flag(BAD_HTML_FLAG, True) # set BAD_HTML_FLAG
matcher.add('BAD_HTML', merge_and_flag,
[{'ORTH': '<'}, {'LOWER': 'br'}, {'ORTH': '>'}],
[{'ORTH': '<'}, {'LOWER': 'br/'}, {'ORTH': '>'}])
我们现在可以在文档上调用matcher。模式将按照它们在文本中出现的顺序进行匹配。然后,matcher将迭代匹配,查找匹配的匹配ID的回调,并调用它。
doc = nlp(LOTS_OF_TEXT)
matcher(doc)
当调用回调时,它会传递四个参数:matcher本身、文档、当前匹配的位置和匹配的总列表。这让您可以编写回调函数来考虑所有匹配的短语,这样您就可以以您喜欢的方式解决重叠和其他冲突。
正则表达式
待补充
例子-01
假设你正在分析用户评论,你想知道人们对Facebook的看法。你想从“脸书”或“脸书”这样的形容词入手。这显然是一个非常简单的解决方案,但是它会很快,而且很好地理解了数据中的内容。你的模式可能是这样的:
[{'LOWER': 'facebook'}, {'LEMMA': 'be'}, {'POS': 'ADV', 'OP': '*'}, {'POS': 'ADJ'}]
这就转化为一个标记,它的小写形式与“facebook”(如facebook、facebook或facebook)相匹配,然后是一个带有引理“be”(例如,是,或者是)的标记,后面跟着一个可选的副词,后面跟着一个形容词。使用这里的语言注释特别有用,因为你可以告诉spaCy匹配“Facebook的烦人”,而不是“Facebook烦人的广告”。可选的副词确保你不会漏掉形容词,比如“pretty awful”或“very nice”。
为了快速了解结果,您可以收集包含匹配的所有句子,并使用displaCy可视化工具呈现它们。在回调函数中,您将访问每个匹配的开始和结束,以及父文档。这让您可以确定包含匹配的语句,doc[start: end]。发送,并计算在句子中匹配跨度的开始和结束。在“手动”模式中使用displaCy可以让您传入包含文本和实体的字典列表。