spaCy:自然语言处理利器----基础篇
开门见山的问:spaCy是什么,适合什么用途?
----"spaCy is a free, open-source library for advanced Natural Language Processing (NLP) in Python."
----“spaCy是一个用Python编写的供高级自然语言处理(NLP)的免费、开源库。”
----"spaCy is designed specifically for production use and helps you build applications that process and 'understand' large volumes of text. It can be used to build information extraction or natural language understanding systems, or to pre-process text for deep learning."
----“spaCy经特别设计以适合生产用途,并可帮助您编写可用于处理及‘理解’大量文字的软件。它可以被用于编写信息提取和自然语言理解的系统,亦可预处理文字供深度学习使用。”
初步了解了spaCy的目的,画一下重点:
分析大量的文字,提取信息,处理自然语言,还有辅助养蛊
事不宜迟,不妨先研究一下如何安装spaCy吧!
众所周知,pip自Python 3.4起便默认自带安装;对于pip用户来说,可以通过在cmd输入以下指令以下指令安装spaCy:
pip install -U spacy
其实和CYaRon的安装方法很像,不是吗
如果想要安装额外的“词形还原”(Lemmatization)包的话,可以在spaCy基础包安装完成后输入以下指令:
pip install -U spacy-lookups-data
针对不同的语言,可以下载不同的语言模型,然而并没有官方中文版
以下以英语、德语和法语为例:
python -m spacy download en_core_web_sm
python -m spacy download de_core_news_sm
python -m spacy download fr_core_news_sm
一般来说,这些步骤做好,便大功告成了!
除非像我一样,往64位电脑上装32位Python,然后装spaCy这种64位Python限定的库
如果使用苹果系统或Conda,推荐在这里获取下载信息
和其他自然语言处理库/软件比,spaCy有什么功能?和其他各种各样的库/软件应该如何取舍?不如看spaCy官方提供的两张对比表吧:
以下是几款常用NLP的推荐用途列表:
以下是几款常用NLP的功能列表:
了解完spaCy的优劣,介绍spaCy各式功能的时候到了!以下部分内容参考了spaCy官网的程序,本人以翻译为主吧qaq
不妨先看个简单的程序,随程序讲解一些NLP相关词汇;请看以下程序:
import spacy #头文件
nlp = spacy.load("en_core_web_sm")
#还记得上面的语言模型安装指令吗?
doc = nlp("Apple is looking at buying U.K. startup for $1 billion")
#doc在调用nlp()后会成为一个数组,每个数组元素里装着一个"词语"(token)
#这里需要涉及到"流水线"(Pipeline)处理的知识,暂且按下不表
for token in doc:
print(token.text, token.lemma_, token.pos_, token.tag_, token.dep_,
token.shape_, token.is_alpha, token.is_stop)
#输出每一个“词语”的一些信息,下面按顺序解释
运行结果如下:
Apple Apple PROPN NNP nsubj Xxxxx True False
is be AUX VBZ aux xx True True
looking look VERB VBG ROOT xxxx True False
at at ADP IN prep xx True True
buying buy VERB VBG pcomp xxxx True False
U.K. U.K. PROPN NNP compound X.X. False False
startup startup NOUN NN dobj xxxx True False
for for ADP IN prep xxx True True
$ $ SYM $ quantmod $ False False
1 1 NUM CD compound d False False
billion billion NUM CD pobj xxxx True False
看似十分复杂,不妨逐个分析:
text:原文
lemma:词根,例如looking->look和is->be
POS:通用词类,缩写含义参见这里
Tag:更详细的词类
Dep:“语法依存”(Syntactic Dependency)关系的缩写
Shape:词形;可以大致理解为将数字替换为大小写对应的x
is alpha:一个布尔值,反映这个“词语”是否是一个由26位英语字母组成的词语
is stop:一个布尔值,反映这个“词语”是否是一个“停用词”(最常用的词汇)
如果有什么输出的词类看不懂,也不要紧,spaCy提供spacy.explain()指令,如有看不懂的词类,可直接查询,以下举个例子:
import spacy
spacy.explain("prep")
#输出:'prepositional modifier',意即介词修饰语
我们甚至可以利用自v2.0起正式加入spaCy库的dispaCy来绘制一张美妙的句子关系表;请看下图:
额外的,spaCy还可以识别“命名实体”----可以用专有名称表示的真实世界对象,例如人,位置,组织,产品等。不妨看一个例子:
import spacy
nlp = spacy.load("en_core_web_sm")
doc = nlp("NOI 2019 was held in Guangzhou.")
for ent in doc.ents: #ent即entity的缩写
print(ent.text, ent.start_char, ent.end_char, ent.label_)
运行结果:
2019 4 8 DATE
Guangzhou 21 30 GPE
和上面一样逐个分析每一个“命名实体”信息的意义:
text:原文
start_char:在原字符串中该原文所占的第一位字符的下标
end_char:在原字符串中该原文所占的最后一位字符的下标
label_:该“命名实体”的类型;类型缩写的含义可通过上面介绍的spacy.explain()查询,也可以在这里查看所有类型缩写的完整列表
然而细心的读者一定留意到了异样:NOI应该是一个“活动”(缩写为EVENT)才对!然而"Event"的官方定义为"Named hurricanes, battles, wars, sports events, etc.",即“命名过的飓风、战役、战争、体育赛事等”,似乎并不包含一个“考试”。虽然NOI在spaCy官方定义下不视为“活动”也无可指摘,但不影响我们利用这个例子学习spaCy的又一个功能:"Rule-based Matching",即“按规匹配”。
顾名思义,“按规匹配”的本质就是给定一些“规则”,然后找出符合“规则”的Doc部分;特别地,这些规则还可以利用模型的推断----例如,可以利用“按规匹配”将"Duck"作名词的情况筛选出来,而无视作动词的。不妨看一个例子:
import spacy
from spacy.matcher import Matcher
nlp = spacy.load("en_core_web_sm")
matcher = Matcher(nlp.vocab)
doc = nlp(
"Fun fact: none of the NOI exams has ever been held west of the Hu Line, "
"presumably to reduce the average traveling distance of the participants. The "
"several NOI exams that took place near the Hu Line incldes NOI 1993 held in "
"Taiyuan, NOI 2001 held in Xi'an, NOI 2006 & 2016 held in Mianyang, and NOI 2013 "
"held in Chengdu."
)
#这里的pattern描述了我们的“规则”
pattern = [{"TEXT": "NOI"}, {"IS_DIGIT": True}]
#将这个“规则”加入匹配器,然后对Doc使用匹配器
matcher.add("IOS_VERSION_PATTERN", None, pattern)
matches = matcher(doc)
print("Total matches found:", len(matches)) #输出匹配数
# Iterate over the matches and print the span text
for match_id, start, end in matches:
print("Match found:", doc[start:end].text) #输出所找到的每一个匹配
输出结果:
Total matches found: 4
Match found: NOI 1993
Match found: NOI 2001
Match found: NOI 2006
Match found: NOI 2013
然而我们发现,绵阳于2006和2016两次举办NOI,我们希望我们的“规则”能包括这种情况,而不是简单输出NOI 2006了事;这时,便可以引入运算符了。还是用刚才的NOI作例子:
import spacy
from spacy.matcher import Matcher
nlp = spacy.load("en_core_web_sm")
matcher = Matcher(nlp.vocab)
doc = nlp(
"Fun fact: none of the NOI exams has ever been held west of the Hu Line, "
"presumably to reduce the average traveling distance of the participants. The "
"several NOI exams that took place near the Hu Line incldes NOI 1993 held in "
"Taiyuan, NOI 2001 held in Xi'an, NOI 2006 & 2016 held in Mianyang, and NOI 2013 "
"held in Chengdu."
)
#这里的pattern描述了我们的“规则”;留意运算符的运用
pattern = [
{"TEXT": "NOI"},
{"IS_DIGIT": True},
{"LEMMA": "&", "OP": "*"},
{"IS_DIGIT": True, "OP": "?"}
]
#将这个“规则”加入匹配器,然后对Doc使用匹配器
matcher.add("IOS_VERSION_PATTERN", None, pattern)
matches = matcher(doc)
print("Total matches found:", len(matches)) #输出匹配数
# Iterate over the matches and print the span text
for match_id, start, end in matches:
print("Match found:", doc[start:end].text) #输出所找到的每一个匹配
输出结果:
Total matches found: 6
Match found: NOI 1993
Match found: NOI 2001
Match found: NOI 2006
Match found: NOI 2006 &
Match found: NOI 2006 & 2016
Match found: NOI 2013
好像略显尴尬----
不过,虽然对spaCy来说处理涉及多个元素逻辑关系的匹配来说略有力有不逮之感,但不妨碍我们通过“添加规则”法解决这个问题。请看以下程序:
import spacy
from spacy.matcher import Matcher
nlp = spacy.load("en_core_web_sm")
matcher = Matcher(nlp.vocab)
doc = nlp(
"Fun fact: none of the NOI exams has ever been held west of the Hu Line, "
"presumably to reduce the average traveling distance of the participants. The "
"several NOI exams that took place near the Hu Line incldes NOI 1993 held in "
"Taiyuan, NOI 2001 held in Xi'an, NOI 2006 & 2016 held in Mianyang, and NOI 2013 "
"held in Chengdu."
)
#一条规则写不下的,就写两条qwq
pattern1 = [
{"TEXT": "NOI"},
{"IS_DIGIT": True},
{"LEMMA": "&"},
{"IS_DIGIT": True}
]
pattern2 = [
{"TEXT": "NOI"},
{"IS_DIGIT": True},
]
#将这个“规则”加入匹配器,然后对Doc使用匹配器
matcher.add("IOS_VERSION_PATTERN", None, pattern1)
matcher.add("IOS_VERSION_PATTERN", None, pattern2)
matches = matcher(doc)
print("Total matches found:", len(matches)) #输出匹配数
# Iterate over the matches and print the span text
for match_id, start, end in matches:
print("Match found:", doc[start:end].text) #输出所找到的每一个匹配
输出结果:
Total matches found: 5
Match found: NOI 1993
Match found: NOI 2001
Match found: NOI 2006
Match found: NOI 2006 & 2016
Match found: NOI 2013
这样,我们就规避了那尴尬的"NOI 2006 &"啦!
另附运算符释义表:
| 运算符 | 意义 |
|---|---|
! |
该元素应当取0次 |
? |
该元素可取0或1次 |
+ |
该元素应当至少取1次 |
* |
该元素可取0次或更多 |
感谢各位大佬的阅读qwq