NLTK基础教程——用NLTK和Python库构建机器学习应用

978-7-115-45257-3
作者: [印度] Nitin Hardeniya
译者: 凌杰
编辑: 武晓燕陈冀康

图书目录:

详情

本书是一本介绍NLTK的书,书中也介绍了很多有关Python语言的知识。本书首先帮助读者掌握构建系统NLP的要点,并引导读者探索数据科学相关的领域。除此之外,书中还介绍了很多有关于自然语言处理的重要知识,让读者深入掌握一些Python里可以应用到自然语言处理中的实用的工具和库。通过学习本书,读者将对自然语言处理和机器学习有更加深入的认识,并能够更好地利用Python语言处理众多非结构化的数据。

图书摘要

版权信息

书名:NLTK基础教程——用NLTK和Python库构建机器学习应用

ISBN:978-7-115-45257-3

本书由人民邮电出版社发行数字版。版权所有,侵权必究。

您购买的人民邮电出版社电子书仅供您个人使用,未经授权,不得以任何方式复制和传播本书内容。

我们愿意相信读者具有这样的良知和觉悟,与我们共同保护知识产权。

如果购买者有侵权行为,我们可能对该用户实施包括但不限于关闭该帐号等维权措施,并可能追究法律责任。

• 著    [印度]Nitin Hardeniya

  译    凌 杰

  责任编辑 陈冀康

• 人民邮电出版社出版发行  北京市丰台区成寿寺路11号

  邮编 100164  电子邮件 315@ptpress.com.cn

  网址 http://www.ptpress.com.cn

• 读者服务热线:(010)81055410

  反盗版热线:(010)81055315


Copyright © Packt Publishing 2016. First published in the English language under the title Python Machine Learning Blueprints.

All Rights Reserved.

本书由美国Packt Publishing公司授权人民邮电出版社出版。未经出版者书面许可,对本书的任何部分不得以任何方式或任何手段复制和传播。

版权所有,侵权必究。


NLTK 库是当前自然语言处理(NLP)领域最为流行、使用最为广泛的库之一, 同时Python语言也已逐渐成为主流的编程语言之一。

本书主要介绍如何通过NLTK库与一些Python库的结合从而实现复杂的NLP任务和机器学习应用。全书共分为10章。第1章对NLP进行了简单介绍。第2章、第3章和第4章主要介绍一些通用的预处理技术、专属于NLP领域的预处理技术以及命名实体识别技术等。第5章之后的内容侧重于介绍如何构建一些NLP应用,涉及文本分类、数据科学和数据处理、社交媒体挖掘和大规模文本挖掘等方面。

本书适合 NLP 和机器学习领域的爱好者、对文本处理感兴趣的读者、想要快速学习NLTK的资深Python程序员以及机器学习领域的研究人员阅读。


Nitin Hardeniya数据科学家,拥有4年以上从业经验,期间分别任职于Fidelity、Groupon和[24]7等公司,其业务横跨各个不同的领域。此外,他还拥有IIIT-H的计算语言学硕士学位,并且是5项客户体验专利的作者。

他热衷于研究语言处理及大型非结构化数据,至少拥有5年日常使用Python的工作经验。他相信,用Python可以构建出大部分与数据科学相关问题的单点解决方案。

他将自己写这本书的经历看成是自己职业生涯的众多荣誉之一,希望用一种非常简单的形式为人们介绍与NLP和机器学习相关的、所有的这些复杂工具。在这本书中,他为读者提供了一种变通方法,即使用一些相关特定能力的Python库,如NLTK、scikit-learn、panda和NumPy等。


Afroz Hussain数据科学家,目前在PredictifyMe公司从事与美国基础数据科学、机器学习起步相关的研究。他在数据科学领域拥有丰富的项目经验、多年使用Python、scikit-learn,以及基于NLTK进行文本挖掘的工作经历。他拥有10年以上的编程经验以及与数据分析和商业智能项目相关的软件开发经验。此外,他还通过在线课程以及参加Kaggle比赛等活动,获得了不少数据科学领域的新技能。

Sujit Pal目前就职于Elsevier实验室,这是一个包含了Reed-Elsevier PLC工作组在内的研发团队。他的兴趣主要集中在信息检索、分布式处理、本体开发、自然语言处理和机器学习这几个领域。而且,他也很喜欢用Python、Scala和Java来编写自己的代码。他充分整合了自己在这些方面的技能,帮助公司改进了不同产品的一些特性并构建了一些新特性。他深信自己需要终身学习,并且也在博客:sujitpal.blogspot.com中分享其经验。

Kumar Raj第二代数据科学家,目前就职于惠普软件的研发部门,为其提供相关的解决方案。在那里,他主要负责开发以惠普软件产品为核心的分析层。他毕业于印度理工学院Kharagpur技术分校,并具有两年以上各种大数据分析领域的工作经验,涉及文本分析、网页抓取及检索、人力资源分析、虚拟系统的性能优化,以及气候变化的预测等。


说来也凑巧,在我签下这本书的翻译合同时,这个世界好像还不知道AlphaGo的存在。而在我完成这本书的翻译之时,Master已经对人类顶级高手连胜60局了。至少从媒体的热度来看,的确在近几年,人工智能似乎是越来越火了。其原因是Google在汽车驾驶和围棋这两个领域的项目得到了很好的进展和宣传,而这两个领域在过去被很多人想当然地认为是人类的专属领域。因此在专属领域接连被突破情况下,一些人得了“机器恐惧症”。例如高晓松先生的这段微博:

@高晓松 

作为自幼学棋,崇拜国手的业余棋手,看了Master50 : 0横扫中日韩顶尖高手的对局,难过极了。为所有的大国手伤心,路已经走完了。多少代大师上下求索,求道求术,全被破解。未来一个八岁少年只要一部手机就可以战胜九段,荣誉信仰灰飞烟灭。等有一天,机器做出了所有的音乐和诗歌,我们的路也会走完。

1月4日 16:21 来自 iPhone 7 Plus

其实之所以会有这样恐惧,大部分是因为人们在讨论人工智能的时候容易将机器“人格化”,很多科幻作品就是这么干的,这看起来很合理,但问题是机器无论如何都不是人。对于机器来说,围棋说穿了不过是一种基于统计学概率的决策模型,属于数学领域的问题,它本来就是机器的强项。用围棋对于人类的难度来推导机器智能的进步,其实是很没有逻辑的事情。而且事实上,今天所流行的这些人工智能方法都是在20世纪70年代前后提出的理论,今天的辉煌主要是由于硬件的进步为实现提供了基础,但在智能上并没有多大的实质突破。要知道,人们对于鉴定人工智能的主要标准早有定论,那就是图灵测试。

图灵测试关注的是人机对话能力,换句话说,什么时候机器能通过对话骗到你的一百块钱,也比它下棋下赢世界冠军更智能点。而想要增强人机对话能力,自然语言处理就是首当其冲的一个领域了。正如我们所说,机器的专长是数学领域,所以自然语言处理问题的目的就是要把我们人类的文本、音频转换成可被分析的数学模型,这对于机器来说是比围棋困难得多的事情。这也 是人类和机器的根本区别,对于这两种智能来说,困难的定义是截然不同的。

说实话,刚开始译这本书的时候,我对它的翻译难度有些估计不足,很多专业词汇国内还似乎还没有标准译法。有些甚至根本找不到对应的中文翻译。虽然对于每个小节我都期望查阅大量的资料,尽量保证翻译的质量,但实在有点太累人了,太费时了,妥协、遗憾在所难免。在这里向读者们致歉,还希望你们多多包涵。同时也感谢人民邮电出版社的陈冀康编辑对于我拖稿行为的容忍,其实我还想再拖上半年的。

2017年1月10日

于新安江畔


这是一本介绍NLTK库,以及如何将该库与其他Python库搭配运用的书。NLTK是当前自然语言处理(NLP)社区中最为流行、使用最为广泛的库之一。NLTK的设计充分体现了简单的魅力。也就是说,对于大多数复杂的NLP任务,它都可以用寥寥几行代码来实现。

本书的前半部分从介绍Python和NLP开始。在这部分内容中,你将会学到一些通用的预处理技术,例如标识化处理(tokenization)、词干提取(stemming)、停用词(stop word)去除;一些专属于NPL领域的预处理技术等,如词性标注(part-of-speech tagging);以及大多数文本相关的NLP任务都会涉及的命名实体识别(Named-entity recognition,简称NER)等技术。然后,我们会逐步将焦点转到更为复杂的NLP任务上,例如语法解析(parsing)以及其他NLP应用。

本书的后半部分则将更侧重于介绍如何构建一些NLP应用,如对于文本分类,可以用NLTK搭配scikit-learn库来进行。我们还会讨论一些其他的Python库,你应该了解一下这些与文本挖掘或自然语言处理任务相关的库。另外,也会带你看看如何从网页和社交媒体中采集数据,以及如何用NLTK进行大规模的文本处理。

第1章 自然语言处理简介。这一章将会涉及一些NLP中的基本概念,并对NLTK和Python做一些介绍。这一章的重点是让你快速了解NLTK,并介绍如何安装所需要的库,以便开始构建一个非常基本的单词云实例。

第2章 文本的歧义及其清理。这一章将会讨论在任何文本挖掘和NLP任务中所需的所有预处理步骤。这一章将会具体讨论断词处理、词干处理、停用词去除等技术。并且,还会为你详细介绍一些别的文本清理技术,以及如何用NLTK来简化它们的实现。

第3章 词性标注。这一章将重点对词性标注进行概述。在这一章中,我们将会为你介绍如何将NLTK运用到一些标注器中,并讨论NLTK中有哪些不同的NLP标注器可用。

第4章 文本结构解析。这一章将会带你继续深入NLP,讨论不同的语法解析方法,并介绍如何用NLTK来实现这些方法。在此过程中,我们会讨论语法解析在NLP语境中的,以及一些常见的信息提取技术(如实体提取)中的重要性。

第5章 NLP应用。这一章将会谈及各种不同的NLP应用,我们将会带领你利用一些当前已掌握的知识来构建出一个简单的NLP应用实例。

第6章 文本分类。这一章将会介绍一些机器学习领域中常见的分类方法。讨论重点将主要集中在文本语料库,以及如何用NLTK和scikit来构建管道,从而实现一个文本分类器。当然,也会讨论与文本聚类和主题模型相关的内容。

第7章 Web爬虫。这一章将讨论NLP、数据科学和数据收集中其他方面的处理任务,以及如何从最大的文本数据源之一——Web中获取相关的数据。在这里,我们将学习如何用Python库、Scrapy来建立一只运作良好的Web爬虫(crawler)。

第8章 NLTK与其他Python库的搭配运用。这一章将会谈及一些骨干的Python库,如NumPy和SciPy。另外,我们也会简单地介绍一下用于数据处理的panda和用于可视化处理的matplotlib。

第9章 Python中的社交媒体挖掘。这一章将致力于数据采集相关的内容。在这里,我们将会讨论社交媒体,以及与社交媒体相关的其他问题。当然,我们也会讨论具体应该如何收集、分析并可视化社交媒体中的数据。

第10章 大规模文本挖掘。这一章将讨论如何扩展NLTK,并配合一些别的Python库,使其适应大数据时代规模化执行的需要。我们将会给出一个简短的演示,以说明NLTK和scikit是如何与Hadoop搭配使用的。

在阅读这本书之前,我们建议你应该准备好下列软件:

所需软件(版本)

自由软件/专有软件

软件下载链接

硬件技术指标

所需操作系统

1~5

Python/Anaconda、NLTK

自由软件

https://www.python.org/
http://continuum.io/downloads
http://www.nltk.org/

通用UNIX打印系统

不限

6

scikit-learn、gensim

自由软件

http://scikit-learn.org/stable/
https://radimrehurek.com/gensim/

通用UNIX打印系统

不限

7

Scrapy

自由软件

http://scrapy.org/

通用UNIX打印系统

不限

8

NumPy、SciPy、pandas以及matplotlib

自由软件

http://www.numpy.org/
http://www.scipy.org/
http://pandas.pydata.org/
http://matplotlib.org/

通用UNIX打印系统

不限

9

Twitter Python API与Facebook Python API

自由软件

https://dev.twitter.com/overview/api/twitter-libraries
https://developers.facebook.com

通用UNIX打印系统

不限

只要你是NLP和机器学习领域的爱好者,无论之前有没有文本处理方面的经验,这本书都是为你准备的。当然,这本书也非常适合那些想要快速学习一下NLTK的资深Python程序员。

在本书中,我们会用不同的文本样式来突显不同类型信息之间的区别。下面,我们就通过几个例子来介绍一下这些样式,以及它们所代表的含义。

对于正文当中所涉及的代码、数据库表名、文件夹名、文件名、文件扩展名、路径名、伪URL、用户输入以及Twitter句柄,我们将采取如下形式:“我们需要创建一个名为NewsSpider.py文件,并将其路径设置为/tutorial/spiders。”

接下来是Python代码块:

>>>import nltk
>>>import numpy

还有一般性的代码块:

add FILE vectorizer.pkl;
add FILE classifier.pkl;

另外,在第7章中,我们还将会用到Scrapy shell中的IPython 记法,其样式如下:

In [1] : sel.xpath('//title/text()')
Out[1]: [<Selector xpath='//title/text()' data=u' Google News'>]

最后是所有命令行输入或输出信息的样式:

# cp /usr/src/asterisk-addons/configs/cdr_mysql.conf.sample
 /etc/asterisk/cdr_mysql.conf

 提示: 

这种形式表达的是一些需要读者警惕的或需要重点关注的内容。

 小技巧: 

这种形式所提供的是一些提示或小技巧。

我们始终欢迎任何来自读者的反馈信息。它能让我们了解你对于这本书的看法——无论是喜欢还是不喜欢。这些反馈对于我们的选题开发来说都是至关重要的。

对于一般的反馈,你只需简单地给feedback@packtpub.com发一份电子邮件,并在邮件的标题中注明这本书的书名即可。

如果你对某一话题有专长,并且有兴趣写(或奉献)一本这方面的书,请参考我们的作者指南:www.packtpub.com/authors。

你一直都是Packt图书的主人,我们将会尽一切努力来帮助你获取最好的图书资讯。

你可以在http://www.packtpub.com自己的账户页面中找到所有已购买的Packt图书,并下载相关的实例代码。如果你是在别处购买了我们的图书,也可以通过访问 http://www. packtpub.com/support注册有关文件,我们会通过电子邮件将其直接发给你。

尽管我们已经尽了最大的努力来确保书中内容的正确性,但错误始终是存在的。如果你在我们的书中发现了错误——无论是关于文字的还是代码的——只要你能告诉我们,我们都将不胜感激。因为这样可以大大减少其他读者在阅读方面所遇到的困难。因此,当你发现错误时,只需要访问http://www.packtpub.com/submit-errata,选择相应的书名,然后单击“errata submission form”链接并输入相关错误的详细信息即可。一旦你提供的信息获得了确认,相关的内容就被更新到我们的网站或对应图书勘误章节下面现有的勘误表中。

如果想要查看先前已提交的勘误信息,你只需访问 https://www.packtpub.com/books/ content/support,并在其搜索域中输入相关图书的名称,所需信息就会出现在下面的勘误部分中。

在互联网上,版权对于所有媒介而言一直是一个很大的问题。在Packet,我们向来对于版权许可非常重视。如果你在网络上发现任何形式的我们出版过的作品,都请马上将网址或网站名称告知我们,以便于我们采取补救措施。

请将你怀疑有侵权行为的文档链接发送到:copyright@packetpub.com。

你付出的帮助是对作者权利的保护,我们也由此才能继续为你带来有价值的内容。

如果你对本书有任何疑问,也可以通过 questions@packtpub.com 跟我们联系,我们会竭尽所能地帮你解决问题。


现在,让我们先从介绍自然语言处理(NLP)开始吧。众所周知,语言是人们日常生活的核心部分,任何与语言问题相关的工作都会显得非常有意思。希望这本书能带你领略到NLP的风采,并引起学习NLP的兴趣。首先,我们需要来了解一下该领域中的一些令人惊叹的概念,并在工作中实际尝试一些具有挑战性的NLP应用。

在英语环境中,语言处理研究这一领域通常被简称为NLP。对语言有深入研究的人通常被叫作语言学家,而“计算机语言学家”这个专用名词则指的是将计算机科学应用于语言处理领域的人。因此从本质上来说,一个计算机语言学家应该既有足够的语言理解能力,同时还可以用其计算机技能来模拟出语言的不同方面。虽然计算机语言学家主要研究的是语言处理理论,但NLP无疑是对计算机语言学的具体应用。

NLP多数情况下指的是计算机上各种大同小异的语言处理应用,以及用NLP技术所构建的实际应用程序。在实践中,NLP与教孩子学语言的过程非常类似。其大多数任务(如对单词、语句的理解,形成语法和结构都正确的语句等)对于人类而言都是非常自然的能力。但对于NLP来说,其中有一些任务就必须要转向标识化处理、语块分解、词性标注、语法解析、机器翻译及语音识别等这些领域的一部分,且这些任务有一大部分还仍是当前计算机领域中非常棘手的挑战。在本书中,我们将更侧重于讨论NLP的实用方面,因此我们会假设读者在NLP上已经有了一些背景知识。所以,读者最好在最低限度上对编程语言有一点了解,并对NLP和语言学有一定的兴趣。

在阅读完本章之后,我们希望读者能掌握以下内容。

如果你从来没有接触过NLP这个概念词,我们在下面给你推荐了两本书,请花一些时间阅读一下其中的任何一本——只需要看看它们的前几章即可。另外,你也应该快速浏览一下维基百科上与NLP相关的页面。

关于这个问题,我们可以先来看看Gartner公司新一轮的趋势报告,你可以很清晰地看到,NLP技术赫然高居榜首。目前,NLP已被认为是业界最为稀缺的技能之一。自大数据的概念问世之后,我们所面对的主要挑战是——业界需要越来越多不仅能处理结构化数据,同时也能处理半结构化或非结构化数据的人才。对于我们所生产出来的那些博客、微博、Facebook订阅、聊天信息、E-mail以及网络评论等,各公司都在致力于收集所有不同种类的数据,以便建立更好的客户针对性,形成有意义的见解。而要想处理所有的这些非结构化数据源,我们就需要掌握一些NLP技能的人员。

身处信息时代,我们甚至不能想象生活中没有Google会是什么样子。我们会因一些最基本的事情而用到Siri;我们会需要用垃圾过滤器来过滤垃圾邮件;我们会需要在自己的Word文档中用到拼写检查器等。在现实世界中所要用到的NLP应用数不胜数,如图1-1所示。

图1-1

在这里,我们可以再列举一些令人惊叹的NLP应用实例。虽然你很可能已经用过它们,但未必知道这些应用是基于NLP技术的。

构建上述这些应用都需要非常具体的技能,需要优秀的语言理解能力和能有效处理这些语言的工具。因此,这些不仅是各NLP最具优势领域的未来趋势,同时也是我们用NLP这种最独特技能所能创建的应用种类。

在实现上面提到的某些应用以及其他基本的NLP预处理时,我们有许多开源工具可用。这些工具有些是由相关组织在建立自己的NLP应用时开发的,而有些则纯粹属于开源项目。下面我们就来看一份NLP工具的小清单。

上述大多数工具都是用Java编写的,在功能上也都很相似。尽管这里有一些工具功能很强大,且提供了各种NLP实用工具,但如果我们考虑到易用性和其对相关概念的解释度的话,NLTK的得分就非常高了。NLTK库是一个非常易学的工具包,这得益于Python本身非常平缓的学习曲线(毕竟NLTK是用它编写的),人们学习起来会非常快。NLTK库中收纳了NLP领域中的绝大部分任务,它们都被实现得非常优雅,且易于使用。正是出于上述的这些原因,NLTK如今已成为了NLP社区最流行的库之一。

在这里,我们会假设读者已经对Python语言有了一定程度的了解。如果你还不了解的话,我们希望你先去学习一下Python。如今在互联网上可以找到大量的Python基础教程,并且能让你对该语言进行一个快速概览的图书也不在少数。当然,我们也会针对不同主题与你探讨Python的一些特性。但就目前而言,只要你掌握了基本的Python知识,如列表、字符串、正则表达式以及基本的I/O操作,就可以继续读下去了。

 提示: 

你可以从下列任意一网站中获取Python安装包。

这里我会推荐读者选用来自Anaconda或Canopy的Python发行版。因为这些发行版本身就具备了一些捆绑库,如SciPy、numpy、scikit等,它们可用于数据分析及其他与NLP相关领域的应用。甚至,NLTK也是该发行版的一部分。

 提示: 

请参照下面网址中的说明来安装NLTK与NLTK数据:

http://www.nltk.org/install.html

下面,让我们来测试一下。

请在操作系统中打开终端,并运行:

$ python

该命令应该会为你打开一个Python解释器:

Python 2.6.6 (r266:84292, Oct 15 2013, 07:32:41)
[GCC 4.4.7 20120313 (Red Hat 4.4.7-4)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>>

我希望你在这里会得到一个与上面情况类似的输出。当然,你也有可能会看到一个不太一样的输出,因此理想情况下,我们应该准备最新版本的Python(建议是2.7版)、GCC编译器,以及其他操作系统的细部安排。当然,我们知道Python目前最新的版本是3.0以上,但对于其他任意的开源系统来说,我们应该保守地选择一个更稳定的版本,而不是贸然跳到最新版本。如果你已经将项目迁移到Python 3.0+,那就务必参阅下面链接中的说明,以便了解那些被添加的新特性:https://docs.python.org/3/whatsnew/3.4.html。

对于基于UNIX的系统,Python属于默认程序(无须任何设置)。而Windows用户则需要通过设置相关路径来使Python进入正常工作状态。你可以通过以下方式来确认NLTK是否已经被正确安装:

>>>import nltk
>>>print "Python and NLTK installed successfully"
Python and NLTK installed successfully

好了,我们可以准备出发了!

虽然,我们在这里并不打算对Python进行任何太过深入的探讨,但带你快速浏览一下Python的基础要点还是很有必要的。当然,为了观众着想,我们最好将这次基础性的快速回顾之旅控制在5分钟之内。在此期间,我们将会讨论到数据结构的基本知识,一些常用函数,以及在接下来几节中将会用到的Python通用结构。

 提示:  

我强烈推荐你花两个小时看一下题为《Google Python class》的参考资料:https://developers.google.com/edu/ python,那对我们来说应该算是个不错的开始。当然,你还可以通过Python的官方网站https://www.python.org/来获取更多的教程及其他相关资源。

列表(list)是Python中最常用的数据结构之一。它们基本上相当于其他编程语言中的数组。下面,就让我们先从Python列表所提供的最重要的那些功能开始吧。

我们可以在Python控制台中进行如下尝试:

>>> lst=[1,2,3,4]
>>> # mostly like arrays in typical languages
>>>print lst
[1, 2, 3, 4]

当然,Python列表也可以用更为灵活的索引来进行访问。下面再来看一个例子:

>>>print 'First element' +lst[0]

在这里,你会得到如下所示的错误信息:

TypeError: cannot concatenate 'str' and 'int' objects

这是因为Python是一种解释型编程语言,它会在对其表达式进行计算的同时检查其中的变量类型。我们在声明这些变量时无需对其进行初始化和类型声明。在这里,我们的列表中所包含的是一些整数对象,它们不能被直接关联到这里的print函数上,后者只能接受一个String对象。出于这个原因,我们需要将该列表元素转换成字符串。这个过程也称为类型转换。

>>>print 'First element :' +str(lst[0])
>>>print 'last element :' +str(lst[-1])
>>>print 'first three elements :' +str(lst[0:2])
>>>print 'last three elements :'+str(lst[-3:])
First element :1
last element :4
first three elements :[1, 2,3]
last three elements :[2, 3, 4]

如果你想要详细了解Python中各种数据类型和函数,最好的方法就是调用其帮助函数,如help()和dir(lst)。

其中,我们可以通过dir(某Python对象)命令来列出指定Python对象中所有给定的属性。例如,如果我们像下面这样将一个列表对象传递给该函数,它就会列出所有我们可以用列表来做的很酷的事情:

>>>dir(lst)
>>>' , '.join(dir(lst))
'__add__ , __class__ , __contains__ , __delattr__ , __delitem__ , __
delslice__ , __doc__ , __eq__ , __format__ , __ge__ , __getattribute__
, __getitem__ , __getslice__ , __gt__ , __hash__ , __iadd__ , __imul__
, __init__ , __iter__ , __le__ , __len__ , __lt__ , __mul__ , __ne__ ,
__new__ , __reduce__ , __reduce_ex__ , __repr__ , __reversed__ , __rmul__
, __setattr__ , __setitem__ , __setslice__ , __sizeof__ , __str__ , __
subclasshook__ , append , count , extend , index , insert , pop , remove
, reverse , sort'

而通过help(某Python对象)命令,我们可以得到给定Python对象的详细文档,以及该对象的一些具体用例,如:

>>>help(lst.index)
Help on built-in function index:
index(...)
 L.index(value, [start, [stop]]) -> integer -- return first index of value.
This function raises a ValueError if the value is not present.

基本上来说,由于help和dir这两个函数可以运用在任何Python数据类型之上,因此它们是一个很好的学习函数和其他对象细节的方法。而且,它还提供了一些基本的使用范例,这在多数情况下都是非常有用的。

Python的字符串类型与其他语言非常类似,但字符串操作同时也是Python最主要的特性之一。即在Python中,处理字符串会是一件非常轻松的工作。即使是那些非常简单的操作,例如字符串的切割,你也会看到相较于Java和C的大费周章,它们在Python中是多么得简单明了。

通过之前用过的help函数,我们可以得到任何Python对象及函数的帮助信息。下面,我们就再来看一些对字符串这种数据类型来说最为常见的操作。

 >>> mystring="Monty Python ! And the holy Grail ! \n"
 >>> print mystring.split()
 ['Monty', 'Python', '!', 'and', 'the', 'holy', 'Grail', '!']
 >>> print mystring.strip()
 >>>Monty Python ! and the holy Grail !

你会注意到'\n'字符被剥离了。另外,你也可以通过rstrip()和lstrip()来选择是剥离字符串左边还是右边的尾部空白符。

 >>> print mystring.upper()
 >>>MONTY PYTHON !AND THE HOLY GRAIL !
   >>> print mystring.replace('!','''''')
   >>> Monty Python and the holy Grail

当然,字符串类型的函数可远不止这些。这里只是讨论了其中最常用的一些而已。

 提示: 

你可以通过下面的链接了解更多字符串函数及其用例:

https://docs.python.org/2/library/string.html

对NLP爱好者来说,正则表达式是另一个非常重要的技能。正则表达式(regular expression)是一种能对字符串进行有效匹配的模式。我们会大量使用这种模式,以求从大量凌乱的文本数据中提取出有意义的信息。下面,我们就来整体浏览一下你将会用到哪些正则表达式。其实,我这一生至今所用过的正则表达式无非也就是以下这些。

下面,我们来看一个用于查找东西的例子。在这里,myString是要进行相关模式查找的目标字符串对象。字符串的子串搜索是re模块中最常见的用例之一。我们可以来看看它是如何实现的:

>>># We have to import re module to use regular expression
>>>import re
>>>if re.search('Python',mystring):
>>> print "We found python "
>>>else:
>>>    print "NO "

只要我们执行了以上代码,就会立即收到如下信息:

We found python

我们还可以使用更多正则表达式模式来进行查找。例如,findall()就是一个常被用于对字符串进行全部模式查找的函数。它会按照给定模式对字符串进行查找,并列出其中所有匹配的对象:

>>>import re
>>>print re.findall('!',mystring)
['!', '!']

如你所见,myString中存在着两个“!”实例,findall返回了这两个对象的列表。

字典(dictionary)也是最常用到的一种数据结构。在其他编程语言中有时也被称为关联数组/存储。字典是一种键值索引型的数据结构,其索引键可以是任意一种不可变的类型,例如字符串和数字都经常被用来充当索引键。

字典是被多种编程语言广泛用于实现诸多算法的一种非常便利的数据结构。而且,Python的字典结构还是所有的这些编程语言中最为优雅的哈希表实现之一。哈希表是一种操作起来非常容易的字典结构,其优势在于,你只需通过寥寥几段代码就可以用它建立起一个非常复杂的数据结构,而同样的任务在其他语言中可能就需要花费更多的时间、写更多的代码。很显然,程序员们应该花更多时间在算法上,而不是数据结构本身。

下面,我打算用字典结构中常见的一个用例来获取某段既定文本中各单词的出现频率分布。你可以看到,只需短短几行代码,我们就取得了各单词在文本中的出现频率。如果你再用任意其他语言来尝试一下相同的任务,就会明白Python是何等得奇妙:

>>># declare a dictionary
>>>word_freq={}
>>>for tok in string.split():
>>>    if tok in word_freq:
>>>        word_freq [tok]+=1
>>>    else:
>>>        word_freq [tok]=1
>>>print word_freq
{'!': 2, 'and': 1, 'holy': 1, 'Python': 1, 'Grail': 1, 'the': 1, 'Monty':
1}

和其他编程语言一样,Python也有自己的函数编写方式。在Python中,函数的定义通常会从关键字def开始,后面紧跟着相应的函数名和括号()。而所有类似于其他编程语言中的参数和参数类型的声明都会被放在该括号内。其实际代码部分将会从冒号(:)后面开始,代码的初始行通常会是一个文档字符串(注释),接着是代码的主体部分,最后我们会以一个return语句来结束整个函数。下面来看个实例,这个函数实例wordfreq的开头是关键字def,它没有参数,最后以一个return语句作为结束。[2]

>>>import sys
>>>def wordfreq (mystring):
>>> '''
>>> Function to generated the frequency distribution of the given text
>>> '''
>>> print mystring
>>> word_freq={}
>>> for tok in mystring.split():
>>> if tok in word_freq:
>>> word_freq [tok]+=1
>>> else:
>>> word_freq [tok]=1
>>> print word_freq
>>>def main():
>>> str="This is my fist python program"
>>> wordfreq(str)
>>>if __name__ == '__main__':
>>> main()

如你所见,其代码主体与上一节中所写的完全相同,只不过我们这回以函数的形式使这段代码具备了可重用性和可读性。当然,用解释器风格来编写Python代码的做法也很常见,但从大型程序的编写实践来说,使用函数/类和某种成熟的编程范式是一个更佳的做法。而且,我们也希望用户能早日编写并运行自己的第一个Python程序。对此,你需要按照以下步骤来进行。

1.用你喜欢的文本编辑器创建一个空的Python文件mywordfreq.py。

2.将上面的代码写入或复制到该文件中。

3.在操作系统中打开命令行终端。

4.在该终端中执行以下命令:

 $ python mywordfreq,py "This is my fist python program !!" 。

5.最后,你应该会得到以下输出:

 {'This': 1, 'is': 1, 'python': 1, 'fist': 1, 'program': 1, 'my':1}。

现在,相信你对Python所提供的一些常见的数据结构有了一个非常基本的了解。你已经可以编写出一个完整的Python程序,并成功地执行了它。在我看来,这些Python引导知识已经足以让你面对本书最初这几章的挑战了。

 提示:  

你还可以通过下面网站中的一些Python教程了解更多相关的Python命令:

https://wiki.python.org/moin/BeginnersGuide

尽管在这里,我们并不打算深入探讨自然语言处理理论,但也会尽快让你实际接触一下NLTK。因此,我打算先介绍一些NLTK的基本用例,这是一个很好的机会,你可以先为今后做类似事情做一些准备。下面,我们会从一个Python程序员习惯的处理方式切入,演示如何用NLTK将该方式转换成一个更为高效、可靠、简洁的解决方案。

我们先来看一个纯文本分析的例子。这个例子是我们要从Python官方主页上摘取部分内容。

>>>import urllib2
>>># urllib2 is use to download the html content of the web link
>>>response = urllib2.urlopen('http://python.org/')
>>># You can read the entire content of a file using read() method
>>>html = response.read()
>>>print len(html)
47020

目前,我们还没有得到任何关于该URL所讨论话题的线索,所以接下来,我们要先做一次探索性数据分析(EDA)。通常对于一段文本域而言,EDA可能包含了多重含义,但这里只会涉及其中的一个简单用例,即该文档的主体术语类型。主题是什么?它们的出现频率如何?整个分析过程还会或多或少地涉及一些预处理层面的步骤。我们会试着先用纯Python的方式来实现它,然后用NLTK再将其实现一次。

我们先要清理掉其中的html标签。一种可行的做法是只选取其中的标记,包括数字和字符。如果之前有在工作中使用过正则表达式,你应该可以轻松地将这些html字符串转换成一个标记列表:

>>># Regular expression based split the string
>>>tokens = [tok for tok in html.split()]
>>>print "Total no of tokens :"+ str(len(tokens))
>>># First 100 tokens
>>>print tokens[0:100]
Total no of tokens :2860
['<!doctype', 'html>', '<!--[if', 'lt', 'IE', '7]>', '<html', 'class="no-
js', 'ie6', 'lt-ie7', 'lt-ie8', 'lt-ie9">', '<![endif]-->', '<!--[if',
'IE', '7]>', '<html', 'class="no-js', 'ie7', 'lt-ie8', 'lt-ie9">',
'<![endif]-->', ''type="text/css"', 'media="not', 'print,', 'braille,'
...]

如你所见,上面列出了我们在处理文本内容时用不到的HTML标签和其他多余字符。当然,这个任务还有个更为简洁的版本:

>>>import re
>>># using the split function
>>>#https://docs.python.org/2/library/re.html
>>>tokens = re.split('\W+',html)
>>>print len(tokens)
>>>print tokens[0:100]
5787
['', 'doctype', 'html', 'if', 'lt', 'IE', '7', 'html', 'class', 'no',
'js', 'ie6', 'lt', 'ie7', 'lt', 'ie8', 'lt', 'ie9', 'endif', 'if',
'IE', '7', 'html', 'class', 'no', 'js', 'ie7', 'lt', 'ie8', 'lt', 'ie9',
'endif', 'if', 'IE', '8', 'msapplication', 'tooltip', 'content', 'The',
'official', 'home', 'of', 'the', 'Python', 'Programming', 'Language',
'meta', 'name', 'apple' ...]

这样看上去已经简洁多了吧?但其实它还可以更简洁一点。在这里,我们所做的努力是尽可能地去除干扰,但那些被清理的HTML标记还是会如雨后春笋般地冒出来,而且我们可能也想以单词长度为标准,删除某一特定长度的单词——如说移除像7、8这样的元素,因为在目前情况下,这些都只是干扰词。现在,我们要做的不是用NLTK来重复相同的任务,完成这些预处理步骤。因为所有的清理工作都可以通过调用clean_html()函数[3]来完成:

>>>import nltk
>>># http://www.nltk.org/api/nltk.html#nltk.util.clean_html
>>>clean = nltk.clean_html(html)
>>># clean will have entire string removing all the html noise
>>>tokens = [tok for tok in clean.split()]
>>>print tokens[:100]
['Welcome', 'to', 'Python.org', 'Skip', 'to', 'content', '&#9660;',
'Close', 'Python', 'PSF', 'Docs', 'PyPI', 'Jobs', 'Community', '&#9650;',
'The', 'Python', 'Network', '≡', 'Menu', 'Arts', 'Business' ...]

很酷吧?而且,这无疑让我们的代码更简洁易行了。

下面再来看看如何获得这些术语的频率分布。当然,我们还是要从纯Python的方式做起,之后再告诉你NLTK的方式。

>>>import operator
>>>freq_dis={}
>>>for tok in tokens:
>>> if tok in freq_dis:
>>> freq_dis[tok]+=1
>>> else:
>>> freq_dis[tok]=1
>>># We want to sort this dictionary on values ( freq in this case )
>>>sorted_freq_dist= sorted(freq_dis.items(), key=operator.itemgetter(1), 
reverse=True)
>>>print sorted_freq_dist[:25]
[('Python', 55), ('>>>', 23), ('and', 21), ('to', 18), (',', 18), ('the',
14), ('of', 13), ('for', 12), ('a', 11), ('Events', 11), ('News', 11),
('is', 10), ('2014-', 10), ('More', 9), ('#', 9), ('3', 9), ('=', 8),
('in', 8), ('with', 8), ('Community', 7), ('The', 7), ('Docs', 6),
('Software', 6), (':', 6), ('3:', 5), ('that', 5), ('sum', 5)]

由于目标是Python的官方主页,Python和(>>>)解释器符号自然就成了最常用的术语,这也符合该网站给人的感觉。

当然,这个任务还有一个更好用、也更有效的方法,即调用NLTK中的FreqDist()函数。在此,我们可以来看看调用后前相同代码的比对:

>>>import nltk
>>>Freq_dist_nltk=nltk.FreqDist(tokens)
>>>print Freq_dist_nltk
>>>for k,v in Freq_dist_nltk.items():
>>> print str(k)+':'+str(v)
<FreqDist: 'Python': 55, '>>>': 23, 'and': 21, ',': 18, 'to': 18, 'the':
14, 'of': 13, 'for': 12, 'Events': 11, 'News': 11, ...>
Python:55
>>>:23
and:21
,:18
to:18
the:14
of:13
for:12
Events:11
News:11

 小技巧:  

下载示例代码 

你在http://www.packtpub.com中登录你的账户,从中可以下载你所购买的、由Packt出版的所有书籍的示例代码。如果你在别处购得此书,也可以在http://www. packtpub.com/support上注册相关文件,我们会用E-mail将其直接发送给你。

现在,让我们来做一些更时髦的事。我们来绘制这样的一张图,如图1-2所示。

>>>Freq_dist_nltk.plot(50, cumulative=False)
>>># below is the plot for the frequency distributions

图1-2

在图1-2中,我们可以看到累积频率的即时增长,在某些点上曲线会进入一条长长的尾巴。其中依然存在着一些干扰,有些类似于the、of、for以及=这样的词都是属于无用词,这些词有一个专用术语:停用词。如the、a、an这样的词也都属于停用词。由于冠词、代词在大多数文档中都是普遍存在的,因而对信息的识别没有帮助。在大多数NLP及信息检索任务中,人们通常都会先删除掉这些停用词。下面,让我们再次回到之前运行的那个例子中,绘制结果如图1-3所示。

>>>stopwords=[word.strip().lower() for word in open("PATH/english.stop.
txt")]
>>>clean_tokens=[tok for tok in tokens if len(tok.lower())>1 and (tok.
lower() not in stopwords)]
>>>Freq_dist_nltk=nltk.FreqDist(clean_tokens)
>>>Freq_dist_nltk.plot(50, cumulative=False)

 提示: 

如果想知道关于词云的更多信息,请访问http://www. wordle.net/advanced

图1-3

现在,代码看起来简洁多了吧!在完成这么多事后,你可以去Wordle网站上将其频率分布以CSV形式显示出来,可以得到如图1-4所示词云图。

图1-4

总而言之,本章致力于为自然语言处理这一领域提供一份简要概括。虽然,本书假定读者在NLP领域,以及使用Python编程方面具有一定的背景知识,但我们也提供了一份与Python和NLP相关的快速入门。我们带你安装了所有在NLTK工作中将会用到的程序。另外,我们还通过几行简单的代码给你演示了NLTK的使用思路。我们提供的是一个了不起的词云实例,这是在大量非结构化文本中进行可视化处理的一种好方法,同时也是文本分析领域中相当流行的一种运用。我们的目标是要围绕着NLTK构建起所需要的一切,并让Python在我们的系统上顺利地工作。为此,你也应该要能编写并运行基本的Python程序。除此之外,我也希望读者能亲身感受一下NLTK库的魅力,自行构建出一个能实际运行的、涉及云词的小型应用程序。只要读者能顺利地产生出云词,我们就认为自己功德圆满了。

在接下来的几章中,我们将更为详细地了解Python这门语言,及其与处理自然语言相关的特性。另外,我们还将探讨一些基本的NLP预处理步骤,并了解一些与NLP相关的基本概念。

[1] 译者注:IBM最新研制的人工智能系统Watson,它的运算更快,记忆力也更好,能读懂一些人类语言中的暗喻和双关。

[2] 译者注:原文如此,但正如你所见,代码中并没有return语句。在没有返回值的情况下,python的函数是不必以return结束的。

[3] 译者注:最新版的NLTK已经取消了这个函数,并鼓励用户使用BeautifulSoup的get_text()函数,因此对于:
clean = nltk.clean_html(html)
我们应该将其改成(当然,在此之前还必须导入bs4库中的BeautifulSoup模块):
soup = BeautifulSoup(html,"lxml")
clean = soup.get_text()


在上一章中,我们为Python以及NLTK库的学习开了一个不错的头,带你初步了解了一下如何针对一些文本资料进行一些有意义的EDA。我们用非常粗糙和简单的方式将预处理部分的所有工作都做了一遍。在本章,我们将具体来讨论标识化处理词干提取词形还原(lemmatization)以及停用词移除等这些预处理步骤。这些话题将会涉及NLTK中所有用于处理文本歧义的工具。届时,我们将会讨论现代NLP应用中会用到的所有预处理步骤,以及实现其中某些任务的不同方法,并说明我们通常该做什么、不该做什么。总而言之,我们会为你提供关于这些工具的足够信息,以便你可以自行决定在自己的应用程序中使用怎么样的预处理工具。我们希望读者在阅读完本章之后,可以掌握以下内容。

事实上,要想给文本/数据歧义这个术语一个定义是相当困难的。本书将它定义成从原生数据中获取一段机器可读的已格式化文本之前所要做的所有预处理工作,以及所有繁复的任务。该过程应该涉及数据再加工data munging)、文本清理特定预处理标识化处理词干提取词形还原以及停用词移除等操作。下面我们就先来看一个基本实例,解析一个csv文件:

>>>import csv
>>>with open('example.csv','rb') as f:
>>> reader = csv.reader(f,delimiter=',',quotechar='"') 
>>> for line in reader :
>>> print line[1] # assuming the second field is the raw sting

如你所见,上述代码在试图对csv文件进行解析,它将会csv文件中所有的列元素构造成一个列表。我们在这操作过程中可以自定义相关的分隔符和引用符(quoting character)。现在的问题是,这些原生字符串会涉及上一章中所学到的那些不同类型的文本歧义。而其中的关键是我们要提供能应付日常csv文件的足够细节的信息。

这些最常见文档类型通常都有一个清晰的处理流程,我们可以通过图2-1了解一下。

图2-1

在上图中,堆栈的第一层中列出了一些最常见的数据源。在大多数情况下,我们遇到的数据都属于这些数据格式中的某一个。接下来的这一层是Python对于这些数据格式最常见的封装方式。例如在之前的csv文件的例子中,Python的csv模块是处理csv文件最可靠的方法。通过该模块,我们可以使用各种不同的分离器和引用符等工具。

除此之外,json也是一种常见的文件格式。

下面来看一个具体的json实例:

{
  "array": [1,2,3,4],
 "boolean": True,
  "object": {
    "a": "b"
  },
  "string": "Hello World"
}

现在让我们来处理一下该字符串,其解析代码如下:

>>>import json
>>>jsonfile = open('example.json')
>>>data = json.load(jsonfile)
>>>print data['string']
"Hello World"

如你所见,这里只是用json模块加载了一个json文件。Python允许我们挑选相关原生字符串的形式并对其进行处理。关于其他所有数据源的更详细信息以及Python中相关的解析工具包,请读者自行参考我们上面列出的那个图表。当然,我们在这里只能指出相关的方向,至于这些工具包的详细信息,还需读者自己上网去搜索。

所以,在我们针对这些不同的文档格式编写自己的解析器之前,请再看一下上图第二行中所列出的Python解析器。当我们获得某一段原生字符串时,所有相关的预处理步骤都可以被当作是某一种管道,或者还可以选择性地忽略掉其中的部分内容。下一节,我们将会具体讨论标识化处理、词干提取以及词形还原的相关细节。并且,我们也会讨论一下这些应用的各种变化,以及何时适用于其他场景。

 提示: 

现在,既然我们对文本歧义是什么有了一点想法,就请试着用上述图表中所列出的某个Python模块连接任意一种数据库试试。

一旦我们将各种数据源解析成了文本形式,接下来所要面临的挑战就是要使这些原生数据体现出它们的意义。文本清理就泛指针对文本所做的绝大部分清理、与相关数据源的依赖关系、性能的解析和外部噪声等。从这个意义上来说,这些工作和我们在第1章——自然语言处理简介中调用html_clean()对HTML文档进行清理的工作是一样的。当然还有其他情况,如果我们要解析PDF文件,可能就需要清理掉一些不必要的干扰字符,移除非ASCII 字符等。总之在继续下一步骤之前,我们需要做一些清理以获得一个可以被进一步处理的干净文本。而对于像XML这样的数据源,我们可能就只需要关注一些特定的树元素即可。对于数据库,我们则有各种可操作的分离器,而且有时我们也只需要关注一些特定的列。总而言之,对于所有致力于净化文本、清理掉文本周围所有可能干扰的工作,我们称之为文本清理。数据再加工(data munging)、文本清理与数据歧义这几个术语之间并没有清晰的界限,它们在类似的语境中可以相互交替使用。在接下来的几节中,我们将会具体讨论一些在任何NLP任务中都极为常见的预处理步骤。

在某些NLP应用中,我们常常需要将一大段原生文本分割成一系列的语句,以便从中获取更多有意义的信息。直观地说,就是让语句成为一个可用的交流单元。当然,要想在计算机上实现这个任务可比它看上去要困难得多了。典型的语句分离器既可能是(.)[1]这样简单的字符串分割符,也有可能是某种预置分类器这样复杂的语句边界标识:

>>>inputstring = ' This is an example sent. The sentence splitter will split 
on sent markers. Ohh really !!' 
>>>from nltk.tokenize import sent_tokenize
>>>all_sent = sent_tokenize(inputstring)
>>>print all_sent
[' This is an example sent', 'The sentence splitter will split on markers.','Ohh really !!']

在这里,我们正试着将原生文本字符串分割到一个语句列表中。用的是预处理函数sent_tokenize(),这是一个内置在NLTK库中的语句边界检测算法。当然,如果我们在应用中需要自定义一个语句分离器的话,也可以用以下方式来训练出属于自己的语句分离器:

>>>import nltk.tokenize.punkt
>>>tokenizer = nltk.tokenize.punkt.PunktSentenceTokenizer()

该预置语句分离器可以支持17种语言。我们只需要为其指定相关的配方对象即可。根据我的经验,这里只要提供一个相关种类的文本语料就已经足够了,而且实际上也很少有机会需要我们自己来构建这些内容。

机器所要理解的最小处理单位是单词(即分词)。所以除了标识化处理之外,我们不宜再对这些文本字符串做更进一步的处理。这里所谓的标识化,实际上就是一个将原生字符串分割成一系列有意义的分词。标识化处理的复杂性因具体的NLP应用而异,当然目标语言本身的复杂性也会带来相关的变化。例如在英语中,我们可以通过正则表达式这样简单的方式来选取纯单词内容和数字。但在中文和日文中,这会成为一个非常复杂的任务。

>>>s = "Hi Everyone ! hola gr8" # simplest tokenizer
>>>print s.split()
['Hi', 'Everyone', '!', 'hola', 'gr8']
>>>from nltk.tokenize import word_tokenize
>>>word_tokenize(s)
['Hi', 'Everyone', '!', 'hola', 'gr8']
>>>from nltk.tokenize import regexp_tokenize, wordpunct_tokenize, blankline_ tokenize 
>>>regexp_tokenize(s, pattern='\w+')
['Hi', 'Everyone', 'hola', 'gr8']
>>>regexp_tokenize(s, pattern='\d+')
['8']
>>>wordpunct_tokenize(s)
['Hi', ',', 'Everyone', '!!', 'hola', 'gr8']
>>>blankline_tokenize(s)
['Hi, Everyone !! hola gr8']

在上述代码中,我们用到了各种标识器(tokenizer)。我们从最简单的——Python字符串类型的split()方法开始。这是一个最基本的标识器,使用空白符来执行单词分割。当然,split()方法本身也可以被配置成一些较为复杂的标识化处理过程。因此在上面的例子中,我们其实很难找出s.split()与word_tokenize()这两个方法之间的差异。

word_tokenize()方法则是一个通用的、更为强大的、可面向所有类型语料库的标识化处理方法。当然,word_tokenize()是NLTK库的内置方法。如果你不能访问它,那就说明在安装NLTK数据时出了些差错。请参照第1章“自然语言处理简介”中的内容来安装它。

通常情况下,我们有两个最常用的标识器。第一种是word_tokenize(),这是我们的默认选择,基本上能应付绝大多数的情况。另一选择是regex_tokenize(),这是一个为用户特定需求设计的、自定义程度更高的标识器。其他的大部分标识器都可以通过继承正则表达式的标识器来实现。我们也可以利用某种不同的模式来构建一个非常具体的标识器。如在上述代码的第8行,我们也可以基于正则表达式的标识器分割出相同的字符串。你可以用\w+这个正则表达式,它会从目标字符串中分隔出所有我们所需要的单词和数字,其他语义符号也可以通过类似的分割器来进行分离,如对于上述代码的第 10 行,我们可以使用\d+这个正则表达式。这样我们就能从目标字符串中提取出纯数字内容。

现在,你能为提取大小写单词、数字和金钱符号构建专用的正则表达式标识器吗?

提示:只需参考之前正则表达式的查询模式来使用regex_tokenize()即可。

 小技巧: 

你也可以去http://text-processing.com/demo这个网站找一些演示项目来参考一下。

所谓词干提取(stemming),顾名思义就是一个修剪枝叶的过程。这是很有效的方法,通过运用一些基本规则,我们可以在修剪枝叶的过程中得到所有的分词。词干提取是一种较为粗糙的规则处理过程,我们希望用它来取得相关分词的各种变化。例如eat这个单词就会有像eating、eaten、eats等变化。在某些应用中,我们是没有必要区分eat和eaten之间的区别的,所以通常会用词干提取的方式将这种语法上的变化归结为相同的词根。由此可以看出,我们之所以会用词干提取方法,就是因为它的简单,而对于更复杂的语言案例或更复杂的NLP任务,我们就必须要改用词形还原(lemmatization)的方法了。词形还原是一种更为健全、也更有条理的方法,以便用于应对相关词根的各种语法上的变化。

下面,我们就来看一段词干提取的具体过程:

>>>from nltk.stem import PorterStemmer # import Porter stemmer
>>>from nltk.stem.lancaster import LancasterStemmer
>>>from nltk.stem.Snowball import SnowballStemmer
>>>pst = PorterStemmer() # create obj of the PorterStemmer
>>>lst = LancasterStemmer() # create obj of LancasterStemmer 
>>>lst.stem("eating")
eat
>>>pst.stem("shopping")
shop

一个拥有基本规则的词干提取器,在像移除-s/es、-ing或-ed这类事情上都可以达到70%以上的精确度,而Porter词干提取器使用了更多的规则,自然在执行上会得到很不错的精确度。

我们创建了不同的词干提取器对象,并在相关字符串上调用了 stem()方法。结果如你所见,当用一个简单实例来查看时,它们之间并没有太大的差别,但当多种词干提取算法介入时,就会看到它们在精准度和性能上的差异了。关于这方面的更多细节,你可以去看看 http://www.nltk. org/api/nltk.stem.html 页面上的相关信息。通常情况下,我们使用的是Porter词干提取器,如果是在英语环境中工作,这个提取器已经够用了。当然,还有Snowball提取器这一整个提取器家族,可分别用于处理荷兰语、英语、法语、德语、意大利语、葡萄牙语、罗马尼亚语和俄语等语言。特别地,我也曾经遇到过可用来处理印地文的轻量级词干提取器:http://research.variancia.com/hindi_stemmer。

 小技巧: 

我们会建议那些希望对词干提取进行更深入研究的人去看看关于所有词干提取器的相关研究http://en.wikipedia. org/wiki/Stemming[2]

但是,对大多数用户而言,Porter和Snowball这两种词干提取器就足以应付大量的相关用例了。在现代的NLP应用中,人们有时候会将词干提取当作是一种预处理步骤从而将其忽略掉,因此这往往取决于我们所面对的具体领域和应用。在这里,我们想告诉你一个事实,即如果你希望用到某些NLP标注器,如词性标注(POS)、NER或某种依赖性解析器中的某些部分,那么就应该避免进行词干提取操作,因为词干提取会对相关分词进行修改,这有可能会导致不同的结果。

当讨论到一般标注器时,我们还会进一步对此展开讨论。

词形还原(lemmatization)是一种更条理化的方法,它涵盖了词根所有的文法和变化形式。词形还原操作会利用上下文语境和词性来确定相关单词的变化形式,并运用不同的标准化规则,根据词性来获取相关的词根(也叫lemma)。

>>>from nltk.stem import WordNetLemmatizer
>>>wlem = WordNetLemmatizer() 
>>>wlem.lemmatize("ate") 
eat

在这里,WordNetLemmatizer使用了wordnet,它会针对某个单词去搜索wordnet这个语义字典。另外,它还用到了变形分析,以便直切词根并搜索到特殊的词形(即这个单词的相关变化)。因此在我们的例子中,通过ate这个变量是有可能会得到eat这个单词的,而这是词干提取操作无法做到的事情。

停用词移除(Stop word removal)是在不同的NLP应用中最常会用到的预处理步骤之一。该步骤的思路就是想要简单地移除语料库中的在所有文档中都会出现的单词。通常情况下,冠词和代词都会被列为停用词。这些单词在一些NPL任务(如说关于信息的检索和分类的任务)中是毫无意义的,这意味着这些单词通常不会产生很大的歧义。恰恰相反的是,在某些NPL应用中,停用词被移除之后所产生的影响实际上是非常小的。在大多数时候,给定语言的停用词列表都是一份通过人工制定的、跨语料库的、针对最常见单词的停用词列表。虽然大多数语言的停用词列表都可以在相关网站上被找到,但也有一些停用词列表是基于给定语料库来自动生成的。有一种非常简单的方式就是基于相关单词在文档中出现的频率(即该单词在文档中出现的次数)来构建一个停用词列表,出现在这些语料库中的单词都会被当作停用词。经过这样的充分研究,我们就会得到针对某些特定语料库的最佳停用词列表。NLTK库中就内置了涵盖22种语言的停用词列表。

下面就来具体实现一下停用词移除的整个过程,这是一段用NLTK来处理停用词的代码。当然,你也可以像第1章“自然语言处理简介”中那样创建一个字典,然后通过查找的方法来解决这个问题。

>>>from nltk.corpus import stopwords
>>>stoplist = stopwords.words('english') # config the language name
# NLTK supports 22 languages for removing the stop words
>>>text = "This is just a test"
>>>cleanwordlist = [word for word in text.split() if word not in stoplist] 
# apart from just and test others are stopwords
['test']

在上述代码片段中,我们所做的是和第1章“自然语言处理简介”中一样的停用词移除操作,但这里部署的是一个更为简洁的版本。之前,我们是基于查表法来做的。即使在当前情况下,NLTK内部所采用的仍然是一个非常类似的方法。这里建议使用NLTK的停用词列表,因为这是一个更为标准化的列表,相比其他所有的实现都更为健全。而且,我们可以通过向该库的停用词构造器传递一个语言名称参数,来实现针对其他语言的类似方法。

这是一个非常直观的操作,因为该操作针对的单词都有很强的唯一性,如说名称、品牌、产品名称、某些噪音性字符(例如html代码的左缩进)等。这些词汇也都需要根据不同的NLP任务来进行清除。例如对于文本分类问题来说,对名词的使用执行预测是个很坏的想法,即使这些词汇在预测中有明确的意义。我们会在后面的章节进一步讨论这个问题。总而言之,我们绝对不希望看到所有噪音性质的分词出现。为此,我们通常会为单词设置一个标准长度,那些太短或太长的单词将会被移除:

>>># tokens is a list of all tokens in corpus
>>>freq_dist = nltk.FreqDist(token)
>>>rarewords = freq_dist.keys()[-50:]
>>>after_rare_words = [ word for word in token not in rarewords]

在这里,我们通过调用FreqDist()函数获取了相关术语在语料库中的分布情况,并选取了其中最稀有的一些词形成了一个列表,并用它来过滤我们的原始语料库。当然,我们也可以对单一文档执行同样的操作。

虽然并不是所有的NLP应用都会用到拼写检查器(spellchecker),但的确有些用例是需要执行基本的拼写检查的。我们可以通过纯字典查找的方式来创建一个非常基本的拼写检查器。业界也有专门为此类应用开发的一些增强型的字符串算法,用于一些模糊的字符串匹配。其中最常用的是edit-distance算法。NLTK也为我们提供了多种内置了edit-distance算法的度量模块。

>>>from nltk.metrics import edit_distance
>>>edit_distance("rain","shine")
3 

我们将会在后续章节中更具体地介绍该模块。我们还会看到拼写检查器最优雅的实现代码之一,它出自Peter Norvig之手,这是一段用纯Python实现的、非常易于理解的代码。

 小技巧: 

对于所有从事自然语言处理这一工作的,我都会推荐他们去看看下面这个链接中所介绍的拼写检查内容:http:// norvig.com/spell-correct.htm

下面是一些开放性答案的问题。

[\w+]将会选取所有的单词和数字,即[A-Z A-Z0-9],而[\$]则会匹配金钱符号。

词干提取操作更多时候是一套用于获取词干一般形式的规则方法。而词形还原主要考虑的是当前的上下文语境以及相关单词的POS,然后将规则应用到特定的语法变化中。通常来说,词干提取的操作实现起来较为简单,并且在处理时间上也要明显短于词形还原。

提示:http://Snowball.tartarus.org/algorithms/english/stemmer.html。

答案是否定的,这是不可能的。所有典型的NLP应用,如词性标注、断句处理等,都需要根据上下文语境来为既定文本生成相关的标签。一旦我们移除了停用词,其上下文环境也就不存在了。

因为印度语的词法很丰富,而中文则是标识化的难度很高,它们都在符号的标准化上遇到了一定的挑战,因此词干提取器实现起来要困难得多。我们会在后面的章节中详细讨论这些挑战。

在这一章中,我们讨论了所有与文本内容相关的数据挖掘与数据再加工话题。我们介绍了一些最常见的数据源,并用相关的Python包来对它们进行解析。其中,我们深入地探讨了标识化处理,从非常基本的字符串方法到自定义的基于正则表达式的标识器均有所涉及。

另外,我们还讨论了词干提取和词形还原。在这过程中,我们介绍了各种可用的词干提取器类型及它们各自的优缺点。我们还讨论了停用词移除的过程,这个操作的重要性,何时该执行停用词移除以及何时不需要执行它。我们还简单地讨论了如何清除文本中的罕见词,以及执行文本清理的重要性——这里包含了停用词和罕见词,我们会根据它们的频率分布来重点清除。最后,我们还提到了拼写纠错。我们在文本挖掘和文本清理上可以做的事情是无限的。每一种语料库都是一个新的挑战,并且都存在要除去某种新噪音的需要。我们需要花一点时间来了解一下自己的语料库需要执行什么类型的预处理操作,以及应该忽略掉什么东西。

在下一章中,我们将会看到一些与NLP相关的预处理,例如词性标注、断句处理以及NER等。我们会在下一章的某些开放性问题的提示和答案中作出解释。

[1] 译者注:由于原作是基于英文环境来说明的,所以本书的文本处理应该以英文标点为准。

[2] 译者注:相应的中文页面为:https://zh.wikipedia.org/wiki/词干提取。


相关图书

ChatGPT原理与应用开发
ChatGPT原理与应用开发
动手学机器学习
动手学机器学习
机器学习与数据挖掘
机器学习与数据挖掘
机器学习公式详解 第2版
机器学习公式详解 第2版
自然语言处理迁移学习实战
自然语言处理迁移学习实战
AI医学图像处理(基于Python语言的Dragonfly)
AI医学图像处理(基于Python语言的Dragonfly)

相关文章

相关课程