Python自然语言处理学习笔记

1. 用Python处理自然语言

1.1. 安装nltk

nltk是一个基于Python的自然语言处理工具集,主要用于英文的自然语言处理。

nltk为超过50个语料库和词汇资源(如WordNet)提供易于使用的接口,以及一套用于分类、标记化、词干化、标记、解析和语义推理的文本处理库,用于工业级NLP库的包装器,以及一个活跃的讨论论坛。

  1. 创建用于学习nlp的python虚拟环境
$ cd ~
$ mkdir venv
$ cd venv
$ python3 -m venv nlp
$ source nlp/bin/activate
  1. 安装nltk
    我这里使用了豆瓣源来加速安装过程
$ pip install nltk -i https://pypi.douban.com/simple
  1. 验证并下载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. 细粒度划分

细粒度划分是将剩余的重要词汇进行细粒度选择。

  1. 条件划分
    [w for w in v if condition]
  2. 双连词查询
    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订阅的处理

3.3. 读取本地文件与NLP流程介绍

读取本地文件

  • 使用Python内置的open()与read()方法
  • nltk提供的PlaintextReader()方法

NLP流程

参考如下截图:

image

使用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()

画出来,就看到了结果,狗在欺负猫。


image

标记模式

参考上节

grammar = "NP: {<DT>?<JJ>*<NN>}"

这条规则是说一个NP-块由一个可选的限定词(DT)后面跟着任何数目的形容词(JJ)然后是一个名词(NN)组成。

理解自然语言

本章的目的是要回答下列问题:

  1. 我们如何能表示自然语言的意思,使计算机能够处理这些表示?
  2. 我们怎样才能将意思表示与无限的句子集合关联?
  3. 我们怎样才能使用连接意思表示与句子的程序来存储知识?
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) ]
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345

推荐阅读更多精彩内容