1. 用Python处理自然语言
1.1. 安装nltk
nltk是一个基于Python的自然语言处理工具集,主要用于英文的自然语言处理。
nltk为超过50个语料库和词汇资源(如WordNet)提供易于使用的接口,以及一套用于分类、标记化、词干化、标记、解析和语义推理的文本处理库,用于工业级NLP库的包装器,以及一个活跃的讨论论坛。
- 创建用于学习nlp的python虚拟环境
$ cd ~
$ mkdir venv
$ cd venv
$ python3 -m venv nlp
$ source nlp/bin/activate
- 安装nltk
我这里使用了豆瓣源来加速安装过程
$ pip install nltk -i https://pypi.douban.com/simple
- 验证并下载nltk_data
$ python
import nltk
nltk.download('all')
下载nltk_data对外网环境有一定要求,有条件的同学可以挂代理下载,我是通过proxychains来对Python解释器进行的代理。
1.2. 常用函数
-
concordance("china")
查找文本中的一个词汇,并且显示这个词汇的上下文。减少我们的统计工作。这是一个忽略大小写的查询。
<details>
<summary>示例 Click to expand</summary>>>> from nltk.book import * *** Introductory Examples for the NLTK Book *** Loading text1, ..., text9 and sent1, ..., sent9 Type the name of the text or sentence to view it. Type: 'texts()' or 'sents()' to list the materials. text1: Moby Dick by Herman Melville 1851 text2: Sense and Sensibility by Jane Austen 1811 ...... >>> text1.concordance("china") Displaying 9 of 9 matches: king over the bulwarks of ships from China ; some high aloft in the rigging , a h it overwhelmed all the millions in China . He lives on the sea , as prairie c ......
</details>
-
similar("china")
查找一个词汇的上下文结构相同的词汇。耗时较长,concordance只是做了一部分查询工作,similar函数首先要查询,然后进行上下文分析,然后在查询与上下文结构相似的词汇。
<details>
<summary>示例 Click to expand</summary>>>> text1.similar("china") the him oriental and all hand out which for case thee there deep length us me life head above greenland
</details>
-
common_contexts(["china", "is"])
查找两个词汇的上下文结构相似的词汇。我们需要寻找的是一个句子里的两个词汇。在一定程度上能够表达感情色彩。
<details>
<summary>示例 Click to expand</summary>>>> text2.common_contexts(["monstrous","very"]) am_glad a_pretty a_lucky is_pretty be_glad
</details>
len()
Python内置函数,查询文本中的词汇数量。set()
Python内置函数,可以将文本转化成集合的形式。(分词,是为了将一句话或者一段文章分成一个接一个的词汇,有利于词性标记。便于自然语言处理。不可以重复的,set的长度就是这个文本的词汇量。)sorted()
排序。由于自然语言处理基于大量的数据文本,为了提升性能,我们可以先将结果集进行排序,然后再汇总。例如,在hadoop和map-reduce中,map 与 reduce 之间还存在一个 可选的操作过程,即排序过程。如果数据量过大的话,我们可以加入排序,减少reduce(汇总)的时间,提升整体的效率。)count()
查找一个词汇在文本中出现的次数。(去除无用词汇,去除高频词汇,am is are what where,但是这些词汇不能够代表我的中心大意。去除超低频词汇,出现频率一次的词汇,去除这些无用词汇后,能够大大的提升我们的分析效率,减少我们的分析量,而且能够在一定成度上帮我们排除了干扰词汇,保障了我们准确的分析文章主旨大意。)index()
查询一个词汇首次在文本中出现的位置。
1.3. 简单的统计
FreqDist(text1)
查询当前文档中所有词汇的出现频率。hapaxes()
这个方法的调用,是基于FreqDist进行调用。查询文本中出现一次的超低频词汇。
<details>
<summary>示例 Click to expand</summary>
>>> fq = nltk.FreqDist(text1)
>>> fq
FreqDist({',': 18713, 'the': 13721, '.': 6862, 'of': 6536, 'and': 6024, 'a': 4569, 'to': 4542, ';': 4072, 'in': 3916, 'that': 2982, ...})
>>> hp = fq.hapaxes()
>>> hp[:10]
['Herman', 'Melville', ']', 'ETYMOLOGY', 'Late', 'Consumptive', 'School', 'threadbare', 'lexicons', 'mockingly']
>>>
</details>
通过两个方法的结合使用,我们可以排除大部分的无需参考的词汇,有助于减少我们的分析量,很好地帮助我们排除了干扰词汇,更好的去寻找文章的中心主旨。
1.4. 细粒度划分
细粒度划分是将剩余的重要词汇进行细粒度选择。
- 条件划分
[w for w in v if condition] - 双连词查询
text1.collocations():查找双连词,是为了我们进行分词和词性标记的时候,能够准确的标记我们的双连词,red wine的例子。
<details>
<summary>示例 Click to expand</summary>
>>> text1.collocations()
Sperm Whale; Moby Dick; White Whale; old man; Captain Ahab; sperm
whale; Right Whale; Captain Peleg; New Bedford; Cape Horn; cried Ahab;
years ago; lower jaw; never mind; Father Mapple; cried Stubb; chief
mate; white whale; ivory leg; one hand
>>> v = set(text1)
>>> word = [w for w in v if len(w) > 15]
>>> word
['superstitiousness', 'uncompromisedness', 'supernaturalness', 'hermaphroditical', 'apprehensiveness', 'cannibalistically', 'irresistibleness', 'uncomfortableness', 'characteristically', 'undiscriminating', 'subterraneousness', 'Physiognomically', 'responsibilities', 'physiognomically', 'simultaneousness', 'circumnavigating', 'preternaturalness', 'circumnavigation', 'circumnavigations', 'indispensableness', 'uninterpenetratingly', 'CIRCUMNAVIGATION', 'comprehensiveness', 'indiscriminately']
>>>
</details>
1.5. 细粒度划分的条件
- 运算符:
- >
- <
- ==
- >=
- <=
- !=
- 字母的控制:
- startswith() 以什么开头
- endswith() 以什么结尾(过去式多数是以ed结尾的单词)
- islower() 小写
- isupper() 大写(专有名词,可以进行检索)
- istitle() 查询所有首字母大写的词汇。
- isdigit() 只有数字的词汇。
- isalpha() 只有字母的词汇
2. 获取文本语料和词汇资源
2.1. 语料库操作相关函数
为了操作文本语料和词汇资源,介绍nltk语料库相关函数的使用。
- fileids() 获取语料库中的文件。
- categories() 获取语料库中的分类。
- fileids([categories]) 获取语料库中的分类
- categories(()[fileids]) 查询这些文件对应的分类
- raw() 获取语料库中的原始内容。
- raw(fileids=[f1,f2,f3]) 获取了指定文件的原始内容,f1,f2,f3
- raw(categories=[c1,c2]) 获取指定分类的原始内容
- words() 获取整个语料库中的词汇。
- words(fileids=[f1,f2,f3]) 指定文件中的所有词汇f1,f2,f3
- words(categories=[c1,c2]) 获取指定分类的所有词汇
- sents():获取语料库中的 句子
- sents(fileids=[f1,f2,f3]) 获取指定文件中的句子
- sents(categories=[c1,c2]) 获取指定分类中的句子
- abspath(fileid) 获取指定文件在磁盘上的位置
- encoding(fileid) 对指定文件进行编码。
- open(fileid) 打开指定文件。
- root() 获取到本地安装的语料库的根目录的路径。
2.2. 常用语料库介绍
-
古腾堡语料库:gutenberg
由古典名著组成,天文类占了较大部分
<details>
<summary>示例 Click to expand</summary>from nltk.corpus import gutenberg for fileid in gutenberg.fileids(): num_char = len(gutenberg.raw(fileid)) num_words = len(gutenberg.words(fileid)) num_sents = len(gutenberg.sents(fileid)) num_qc = len(set(w.lower() for w in gutenberg.words(fileid))) print(int(num_char/num_words), int(num_words/num_sents), int(num_words/num_qc), fileid)
</details>
-
网络与聊天文本库:webtext
从网络论坛汇总而成,包含了网络聊天的语料,比如you在网络聊天中常用u来代替,这就需要用专门的预料库来分析,否则容易偏离方向。
<details>
<summary>示例 Click to expand</summary>from nltk.corpus import webtext for f in webtext.fileids(): print(f) from nltk.corpus import nps_chat chatroom = nps_chat.posts("10-19-20s_706posts.xml") chatroom[123]
</details>
-
布朗语料库:brown
涵盖内容十分广泛且全面,泛用性较好。
<details>
<summary>示例 Click to expand</summary># 布朗语料库 import nltk from nltk.corpus import brown from nltk.inference.tableau import Categories brown.categories() brown.words(categories=['news']) brown.words(fileids=['ck09']) # 想要得到三种分类里的句子 news reviews editorial brown.sents(categories=['news', 'reviews', 'editorial']) # 实验1: 分类统计 新闻类 的文本中 一些词汇的出现频率 news_text = brown.words(categories=['news']) f = nltk.FreqDist([w.lower() for w in news_text]) models = ['can', 'could', 'must', 'will', 'may'] for m in models: print("{0} : {1}".format(m, f[m])) # 实验2: 分类统计 四种类型的文本中 # news hobbies humor romance cfd = nltk.ConditionalFreqDist( (categ, word) for categ in brown.categories() for word in brown.words(categories=categ) ) categs = ['news', 'hobbies', 'humor', 'romance'] words = ['can', 'could', 'must', 'will', 'may'] cfd.tabulate(conditions=categs, samples=words)
</details>
-
路透社语料库:reuters
路透社语料库包含10788个新闻文档,共计130万字,分成90个主题,按照训练集和测试集分成了两个部分。
-
就职演说语料库:inaugural
该语料库涵盖了美国每次总统的就职演说。
<details>
<summary>示例 Click to expand</summary>import nltk # 就职演说语料库 from nltk.corpus import inaugural inaugural.fileids() [fileid[:4] for fileid in inaugural.fileids()] # 实验: 查询语料库中america和citizen出现的频率,并且绘制成图标。ConditionFreqDIst() cdf = nltk.ConditionalFreqDist( (target, fileid[:4]) for fileid in inaugural.fileids() for w in inaugural.words(fileid) for target in ['america', 'citizen'] if w.lower().startswith(target) ) # 绘制图表 cdf.plot()
</details>
2.3. 导入自己的语料库
新建一个目录如test/,在test/目录下新建文件1.txt,文件内容为Hello world
from nltk.corpus import PlaintextReader
# 语料库目录
path = '/test'
# 导入
myself = PlaintextReader(path, '.*')
2.4. 简单的条件频率分布与图表绘制
nltk支持生成plot图表,参考如下实例:
# 实验:对比不同语言翻译的同一个文本中,每个词汇的字母个数的不同,并且绘制图表。
from nltk.corpus import udhr
import nltk
languages = ['Chickasaw', 'English', 'German_Deutsch', 'Greenlandic_Inuktikut', 'Hungarian_Magyar']
cfd = nltk.ConditionalFreqDist(
(lang, len(word))
for lang in languages
for word in udhr.words(lang+'-Latin1')
)
cfd.plot()
3. 原料文本的处理加工
3.1. 网络读取文件处理HTML
- urllib2 Python3处理URLs的库
- beautifulsoup4
是一个可以从HTML或XML文件中提取数据的Python库.它能够通过你喜欢的转换器实现惯用的文档导航,查找,修改文档的方式.Beautiful Soup会帮你节省数小时甚至数天的工作时间.
3.2. 对搜索引擎与RSS订阅的处理
- Universal Feed Parser
- beautifulsoup4
3.3. 读取本地文件与NLP流程介绍
读取本地文件
- 使用Python内置的open()与read()方法
- nltk提供的PlaintextReader()方法
NLP流程
参考如下截图:
使用Unicode进行文本处理
使用codecs库进行编码文件的读取。
import nltk
import codecs
path = nltk.data.find('corpora/unicode_samples/polish-lat2.txt')
f = codecs.open(path, encoding='latin2')
for line in f:
line = line.strip()
print(line.encode('unicode_escape'))
使用正则表达式进行词干提取
基本元字符
查找以ed结尾的单词
import re
import nltk
wl = [w for w in nltk.corpus.words.words('en') if w.islower()]
[w for w in wl if re.search('ed$', w)]
范围与闭包
模拟英文键盘九宫格联想输入法
[w for w in wl if re.search('^[ghi][mno][jlk][def]$', w)]
提取字符块
word = 'ajgsdjguwgnagjwglagvjdsajlkgkqj'
re.findall(r'[aeiou]', word)
wsj = sorted(set(nltk.corpus.treebank.words()))
fd = nltk.FreqDist(vs for word in wsj
for vs in re.findall(r'[aeiou]{2,}', word))
fd.items()
查找词干
def stem(word):
regexp = r'(.*?)(ing|ly|ed|ious|ies|ive|es|s|ment)?$'
stem, suffix = re.findall(regexp, word)[0]
return stem
raw = """DENNIS: Listen, strange women lying in ponds distributing swords
is no basis for a system of government. Supreme executive power derives from
a mandate from the messes, not from farcical aquatic cermony. """
tokens = nltk.word_tokenize(raw)
[stem(t) for t in tokens]
NLTK词干提取器
- Porter
- Lancaster
注意比较这两个提取器的异同
import nltk
porter = nltk.PorterStemmer()
lancaster = nltk.LancasterStemmer()
raw = """DENNIS: Listen, strange women lying in ponds distributing swords
is no basis for a system of government. Supreme executive power derives from
a mandate from the messes, not from farcical aquatic cermony. """
tokens = nltk.word_tokenize(raw)
[porter.stem(t) for t in tokens]
[lancaster.stem(t) for t in tokens]
实际中根据提取器在文本中的表现而选择,选择准确率较高的。
使用正则表达式为文本分词
了解一下NLTK的正则表达式分词器
text = 'That U.S.A. poster-print costs $12.40'
# 视频里正则表达式太复杂了,字体显示也不清楚,这里就随便敲个U代替了
pattern = r'U'
nltk.regexp_tokenize(text, pattern)
词汇分类与词汇标注
使用词性标注器
一个词性标注器(part-of-speech tagger或POS tagger)处理一个词序列,为每个词附加一个词性标记。
- CC 并列连词
- RB 副词
- IN 介词
- NN 名词
- JJ 形容词
import nltk
text = nltk.word_tokenize('And now for something completely different')
nltk.pos_tag(text)
对语料库进行词性标注
有的语料库自带了词性标注,比如brown语料库。
查询已标记的语料库:
import nltk
nltk.corpus.brown.tagged_words()
nltk.corpus.nps_chat.tagged_words()
- ADJ 形容词
- ADV 动词
- CNJ 连词
- DET 限定词
- EX 存在量词
- FW 外来词
- MOD 情态动词
- N 名词
- NP 专有名词
- NUM 数词
- PRO 代词
- P 介词
- TO 词to
- UH 感叹词
- V 动词
- VD 过去式
- VG 现在分词
- VN 过去分词
- WH Wh限定词
自动标注-默认标注
最基础的标注器,90%情况下不使用默认标注器。
10%情况,前提,尽可能对所有词进行标注,可以用默认标注器对陌生的词汇进行标注。
import nltk
from nltk.corpus import brown
tags = [tag for (word, tag) in brown.tagged_words(categories='news')]
nltk.FreqDist(tags).max()
raw = 'I do not like green eggs and han, I do not like the Sam I an!'
tokens = nltk.word_tokenize(raw)
f = nltk.DefaultTagger('NN')
# 会将所有tokens标注为NN
f.tag(tokens)
自动标注-正则表达式标注
import nltk
from nltk.corpus import brown
brown_sents = brown.sents(categories='news')
patterns = [
(r'.*ing$', 'VBG'),
(r'.*ed$', 'VBD'),
(r'.*', 'NN')
]
reg_tagger = nltk.RegexpTagger(patterns)
reg_tagger.tag(brown_sents[3])
N-gram标注
常用,利用已有的标注好的语料库,预测未知的文本的词性。
import nltk
from nltk.corpus import brown
brown_tagged_sents = brown.tagged_sents(categories='news')
brown_sents = brown.sents(categories='news')
f = nltk.UnigramTagger(brown_tagged_sents)
f.tag(brown_sents[2007])
从文本中提取有用的信息并分析
信息提取介绍
信息有很多种“形状”和“大小”。一个重要的形式是结构化数据:实体和关系的可预测的规范的结构。例如:我们可能对公司和地点之间的关系感兴趣。给定一个公司,我们希望能够确定它做业务的位置;反过来,给定位置,我们会想发现哪些公司在该位置做业务。
信息提取有许多应用,包括商业智能、简历收获、媒体分析、情感检测、专利检索、电子邮件扫描。当前研究的一个特别重要的领域是提取出电子科学文献的结构化数据。
名词短语分块
NP-分块信息最有用的来源之一是词性标记。这是在我们的信息提取系统中进行词性标注的动机之一。
这条规则是说一个NP-块由一个可选的限定词(DT)后面跟着任何数目的形容词(JJ)然后是一个名词(NN)组成。
sentence = [("the", "DT"), ("little", "JJ"), ("yellow", "JJ"), ("dog", "NN"), ("barked", "VBD"), ("at", "IN"), ("the", "DT"), ("cat", "NN")]
grammar = "NP: {<DT>?<JJ>*<NN>}"
cp = nltk.RegexpParser(grammar)
res = cp.parse(sentence)
print(res)
res.draw()
画出来,就看到了结果,狗在欺负猫。
标记模式
参考上节
grammar = "NP: {<DT>?<JJ>*<NN>}"
这条规则是说一个NP-块由一个可选的限定词(DT)后面跟着任何数目的形容词(JJ)然后是一个名词(NN)组成。
理解自然语言
本章的目的是要回答下列问题:
- 我们如何能表示自然语言的意思,使计算机能够处理这些表示?
- 我们怎样才能将意思表示与无限的句子集合关联?
- 我们怎样才能使用连接意思表示与句子的程序来存储知识?
import nltk
from nltk import load_parser
cp = load_parser('grammars/book_grammars/sql0.fcfg')
query = 'What cities are located in China'
trees = next(cp.parse(query.split()))
ans = trees[0].label()
print(ans)
生成sql语句,实现的高级的用法
>>> print(ans)
[ *type* = 'NP' ]
[ SEM = (SELECT, City FROM city_table) ]