博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
可爱的 Python: 自然语言工具包入门
阅读量:5959 次
发布时间:2019-06-19

本文共 10662 字,大约阅读时间需要 35 分钟。

可爱的 Python: 自然语言工具包入门

在计算语言学中使用 Python

( ), 开发者, Gnosis Software, Inc.
David Mertz
David Mertz 根本不知道他一直在写
散文。 您可以通过 与 David 联系;您可以通过他的 了解他的生活。请阅读他的 一书。欢迎提出关于过去或将来专栏的意见和建议。

 

简介: 在本期文章中,David 向您介绍了自然语言工具包(Natural Language Toolkit),它是一个将学术语言技术应用于文本数据集的 Python 库。称为“文本处理”的程序设计是其基本功能;更深入的是专门用于研究自然语言的语法以及语义分析的能力。

发布日期: 2004 年 7 月 24 日

级别: 初级
访问情况 : 5939 次浏览
评论: 0 ( |  - 登录)

平均分 (4个评分)

 

鄙人并非见多识广,虽然写过很多关于 文本处理 方面的东西(例如,一本书),但是,对我来说, 语言处理(linguistic processing) 是一个相对新奇的领域。如果在对意义非凡的自然语言工具包(NLTK)的 说明中出现了错误,请您谅解。NLTK 是使用 Python 教学以及实践计算语言学的极好工具。此外,计算语言学与人工 智能、语言/专门语言识别、翻译以及语法检查等领域关系密切。

NLTK 会被自然地看作是具有栈结构的一系列层,这些层构建于彼此基础之上。那些熟悉人工语言(比如 Python)的文法 和解析的读者来说,理解自然语言模型中类似的 —— 但更深奥的 —— 层不会有太大困难。

术语表

全集(Corpora):相关文本的集合。例如,莎士比亚的作品可能被统称为一个 文集(corpus); 而若干个作者的作品称为 全集

直方图(Histogram):数据集中不同单词、字母或其他条目的出现频率的统计分布。

结构(Syntagmatic):对语段的研究;也就是全集中字母、单词或短语连续出现的统计关系。

上下文无关语法(Context-free grammar): 由四类形式语法构成的 Noam Chomsky 层级中的第二类。参阅 以获得 详尽描述。

尽管 NLTK 附带了很多已经预处理(通常是手工地)到不同程度的全集,但是概念上每一层 都是依赖于相邻的更低层次的处理。首先是断词;然后是为单词加上 标签;然后将成组 的单词解析为语法元素,比如名词短语或句子(取决于几种技术中的某一种,每种技术都有其优缺点); 最后对最终语句或其他语法单元进行分类。通过这些步骤,NLTK 让您可以生成关于不同元素出现情况 的统计,并画出描述处理过程本身或统计合计结果的图表。

在本文中,您将看到关于低层能力的一些相对完整的示例,而对大部分高层次能力将只是进行简单抽象的描述。 现在让我们来详细分析文本处理的首要步骤。


您可以使用 NLTK 完成的很多工作,尤其是低层的工作,与使用 Python 的基本数据结构来完成相比,并 没有 大的区别。不过,NLTK 提供了一组由更高的层所依赖和使用的系统化的接口,而不只是 简单地提供实用的类来处理加过标志或加过标签的文本。

具体讲, nltk.tokenizer.Token 类被广泛地用于存储文本的有注解的片断;这些 注解可以标记很多不同的特性,包括词类(parts-of-speech)、子标志(subtoken)结构、一个标志(token) 在更大文本中的偏移位置、语形词干 (morphological stems)、文法语句成分,等等。实际上,一个 Token 是一种 特别的字典 —— 并且以字典形式访问 —— 所以它可以容纳任何您希望的键。在 NLTK 中使用了一些专门的键, 不同的键由不同的子程序包所使用。

让我们来简要地分析一下如何创建一个标志并将其拆分为子标志:

>>> from nltk.tokenizer import *>>> t = Token(TEXT='This is my first test sentence')>>> WSTokenizer().tokenize(t, addlocs=True) # break on whitespace>>> print t['TEXT']This is my first test sentence>>> print t['SUBTOKENS'][
@[0:4c],
@[5:7c],
@[8:10c],
@[11:16c],
@[17:21c],
@[22:30c]]>>> t['foo'] = 'bar'>>> t
@[0:4c],
@[5:7c],
@[8:10c],
@[11:16c],
@[17:21c],
@[22:30c]]>>>> print t['SUBTOKENS'][0]
@[0:4c]>>> print type(t['SUBTOKENS'][0])

对于语言全集,您可能要做的一件相当简单的事情是分析其中各种 事件(events) 的 频率分布,并基于这些已知频率分布做出概率预测。NLTK 支持多种基于自然频率分布数据进行概率预测的方法。 我将不会在这里介绍那些方法(参阅 中列出的概率教程), 只要说明您肯定会 期望的那些与您已经 知道的 那些(不止是显而易见的 缩放比例/正规化)之间有着一些模糊的关系就够了。

基本来讲,NLTK 支持两种类型的频率分布:直方图和条件频率分布(conditional frequency)。 nltk.probability.FreqDist 类用于创建直方图;例如, 可以这样创建一个单词直方图:

>>> from nltk.probability import *>>> article = Token(TEXT=open('cp-b17.txt').read())>>> WSTokenizer().tokenize(article)>>> freq = FreqDist()>>> for word in article['SUBTOKENS']:...     freq.inc(word['TEXT'])>>> freq.B()1194>>> freq.count('Python')12

概率教程讨论了关于更复杂特性的直方图的创建,比如“以元音结尾的词后面的词的长度”。 nltk.draw.plot.Plot 类可用于直方图的可视化显示。当然, 您也可以这样分析高层次语法特性或者甚至是与 NLTK 无关的数据集的频率分布。

条件频率分布可能比普通的直方图更有趣。条件频率分布是一种二维直方图 —— 它按每个初始条件或者“上下文”为您显示 一个直方图。例如,教程提出了一个对应每个首字母的单词长度分布问题。我们就以这样分析:

>>> cf = ConditionalFreqDist()>>> for word in article['SUBTOKENS']:...     cf[word['TEXT'][0]].inc(len(word['TEXT']))...>>> init_letters = cf.conditions()>>> init_letters.sort()>>> for c in init_letters[44:50]:...     print "Init %s:" % c,...     for length in range(1,6):...         print "len %d/%.2f," % (length,cf[c].freq(n)),...     print...Init a: len 1/0.03, len 2/0.03, len 3/0.03, len 4/0.03, len 5/0.03,Init b: len 1/0.12, len 2/0.12, len 3/0.12, len 4/0.12, len 5/0.12,Init c: len 1/0.06, len 2/0.06, len 3/0.06, len 4/0.06, len 5/0.06,Init d: len 1/0.06, len 2/0.06, len 3/0.06, len 4/0.06, len 5/0.06,Init e: len 1/0.18, len 2/0.18, len 3/0.18, len 4/0.18, len 5/0.18,Init f: len 1/0.25, len 2/0.25, len 3/0.25, len 4/0.25, len 5/0.25,

条件频率分布在语言方面的一个极好应用是分析全集中的语段分布 —— 例如,给出一个特定的 词,接下来最可能出现哪个词。当然,语法会带来一些限制;不过,对句法选项的选择的研究 属于语义学、语用论和术语范畴。


nltk.stemmer.porter.PorterStemmer 类是一个用于从英文单词中 获得符合语法的(前缀)词干的极其便利的工具。这一能力尤其让我心动,因为我以前曾经用 Python 创建了一个公用的、全文本索引的 搜索工具/库(见 中的描述,它已经用于相当多的其他项目中)。

尽管对大量文档进行关于一组确切词的搜索的能力是非常实用的( gnosis.indexer 所做的工作), 但是,对很多搜索用图而言,稍微有一些模糊将会有所帮助。也许,您不能特别确定您正在寻找的电子邮件是否使用了单词 “complicated”、“complications”、“complicating”或者“complicates”,但您却记得那是大概涉及的内容(可能与其他一些 词共同来完成一次有价值的搜索)。

NLTK 中包括一个用于单词词干提取的极好算法,并且让您可以按您的喜好定制词干提取算法:

>>> from nltk.stemmer.porter import PorterStemmer>>> PorterStemmer().stem_word('complications')'complic'

实际上,您可以怎样利用 gnosis.indexer 及其衍生工具或者完全不同的索引工具中的词干 提取功能,取决于您的使用情景。幸运的是,gnosis.indexer 有一个易于进行专门定制的 开放接口。您是否需要一个完全由词干构成的索引?或者您是否在索引中同时包括完整的单词 和词干?您是否需要将结果中的词干匹配从确切匹配中分离出来?在未来版本的 gnosis.indexer 中我将引入一些种类词干的提取能力,不过,最终用户可能仍然希望进行不同的定制。

无论如何,一般来说添加词干提取是非常简单的:首先,通过特别指定 gnosis.indexer.TextSplitter 来从一个文档中获得词干;然后, 当然执行搜索时,(可选地)在使用搜索条件进行索引查找之前提取其词干,可能是通过定制 您的 MyIndexer.find() 方法来实现。

在使用 PorterStemmer 时我发现 nltk.tokenizer.WSTokenizer 类确实如教程所警告的那样不好用。它可以胜任概念上的角色,但是对于实际的文本而言,您可以更好地识别出什么是一个 “单词”。幸运的是, gnosis.indexer.TextSplitter 是一个健壮的断词工具。例如:

>>> from nltk.tokenizer import *>>> article = Token(TEXT=open('cp-b17.txt').read())>>> WSTokenizer().tokenize(article)>>> from nltk.probability import *>>> from nltk.stemmer.porter import *>>> stemmer = PorterStemmer()>>> stems = FreqDist()>>> for word in article['SUBTOKENS']:...     stemmer.stem(word)...     stems.inc(word['STEM'].lower())...>>> word_stems = stems.samples()>>> word_stems.sort()>>> word_stems[20:40]['"generator-bas', '"implement', '"lazili', '"magic"', '"partial','"pluggable"', '"primitives"', '"repres', '"secur', '"semi-coroutines."','"state', '"understand', '"weightless', '"whatev', '#', '#-----','#----------', '#-------------', '#---------------', '#b17:']

查看一些词干,集合中的词干看起来并不是都可用于索引。很多根本不是实际的单词,还有其他一些是 用破折号连接起来的组合词,单词中还被加入了一些不相干的标点符号。让我们使用更好的断词工具 来进行尝试:

>>> article = TS().text_splitter(open('cp-b17.txt').read())>>> stems = FreqDist()>>> for word in article:...     stems.inc(stemmer.stem_word(word.lower()))...>>> word_stems = stems.samples()>>> word_stems.sort()>>> word_stems[60:80]['bool', 'both', 'boundari', 'brain', 'bring', 'built', 'but', 'byte','call', 'can', 'cannot', 'capabl', 'capit', 'carri', 'case', 'cast','certain', 'certainli', 'chang', 'charm']

在这里,您可以看到有一些单词有多个可能的扩展,而且所有单词看起来都像是单词或者词素。 断词方法对随机文本集合来说至关重要;公平地讲,NLTK 捆绑的全集已经通过 WSTokenizer() 打包为易用且准确的断词工具。要获得健壮的实际可用的索引器,需要使用健壮的断词工具。


NLTK 的最大部分由复杂程度各不相同的各种解析器构成。在很大程度上,本篇介绍将不会 解释它们的细节,不过,我愿意大概介绍一下它们要达成什么目的。

不要忘记标志是特殊的字典这一背景 —— 具体说是那些可以包含一个 TAG 键以指明单词的语法角色的标志。NLTK 全集文档通常有部分专门语言已经预先添加了标签,不过,您当然可以 将您自己的标签添加到没有加标签的文档。

分块有些类似于“粗略解析”。也就是说,分块工作的进行,或者基于语法成分的已有标志,或者基于 您手工添加的或者使用正则表达式和程序逻辑半自动生成的标志。不过,确切地说,这不是真正的解析 (没有同样的生成规则)。例如:

>>> from nltk.parser.chunk import ChunkedTaggedTokenizer>>> chunked = "[ the/DT little/JJ cat/NN ] sat/VBD on/IN [ the/DT mat/NN ]">>> sentence = Token(TEXT=chunked)>>> tokenizer = ChunkedTaggedTokenizer(chunk_node='NP')>>> tokenizer.tokenize(sentence)>>> sentence['SUBTOKENS'][0](NP: 
)>>> sentence['SUBTOKENS'][0]['NODE']'NP'>>> sentence['SUBTOKENS'][0]['CHILDREN'][0]
>>> sentence['SUBTOKENS'][0]['CHILDREN'][0]['TAG']'DT'>>> chunk_structure = TreeToken(NODE='S', CHILDREN=sentence['SUBTOKENS'])(S: (NP:
)
(NP:
))

所提及的分块工作可以由 nltk.tokenizer.RegexpChunkParser 类使用伪正则表达式来描述 构成语法元素的一系列标签来完成。这里是概率教程中的一个例子:

>>> rule1 = ChunkRule('
?
*
',... 'Chunk optional det, zero or more adj, and a noun')>>> chunkparser = RegexpChunkParser([rule1], chunk_node='NP', top_node='S')>>> chunkparser.parse(sentence)>>> print sent['TREE'](S: (NP:
)
(NP:
))

真正的解析将引领我们进入很多理论领域。例如,top-down 解析器可以确保找到每一个可能的产品,但 可能会非常慢,因为要频繁地(指数级)进行回溯。Shift-reduce 效率更高,但是可能会错过一些产品。 不论在哪种情况下,语法规则的声明都类似于解析人工语言的语法声明。本专栏曾经介绍了其中的一些: SimpleParsemx.TextToolsSparkgnosis.xml.validity (参阅 )。

甚至,除了 top-down 和 shift-reduce 解析器以外,NLTK 还提供了“chart 解析器”,它可以创建部分假定, 这样一个给定的序列就可以继而完成一个规则。这种方法可以是既有效又完全的。举一个生动的(玩具级的)例子:

>>> from nltk.parser.chart import *>>> grammar = CFG.parse('''...    S -> NP VP...    VP -> V NP | VP PP...    V -> "saw" | "ate"...    NP -> "John" | "Mary" | "Bob" | Det N | NP PP...    Det -> "a" | "an" | "the" | "my"...    N -> "dog" | "cat" | "cookie"...    PP -> P NP...    P -> "on" | "by" | "with"...    ''')>>> sentence = Token(TEXT='John saw a cat with my cookie')>>> WSTokenizer().tokenize(sentence)>>> parser = ChartParser(grammar, BU_STRATEGY, LEAF='TEXT')>>> parser.parse_n(sentence)>>> for tree in sentence['TREES']: print tree(S:  (NP: 
) (VP: (VP: (V:
) (NP: (Det:
) (N:
))) (PP: (P:
) (NP: (Det:
) (N:
)))))(S: (NP:
) (VP: (V:
) (NP: (NP: (Det:
) (N:
)) (PP: (P:
) (NP: (Det:
) (N:
))))))

probabilistic context-free grammar(或者说是 PCFG)是一种上下文无关语法, 它将其每一个产品关联到一个概率。同样,用于概率解析的解析器也捆绑到了 NLTK 中。


NLTK 还有其他本篇简短介绍中不能涵盖的重要功能。例如,NLTK 有一个完整的框架,用于通过类似于“naive Bayesian” 和“maximum entropy”等模型的统计技术进行文本分类。 即使还有篇幅,现在我也还不能解释其本质。不过,我认为,即使是 NLTK 较低的层,也可以成为一个既可用于教学应用程序 也可用于实际应用程序的实用框架。

  • 您可以参阅本文在 developerWorks 全球站点上的 .
  • (NLTK)由 Sourceforge 托管,它的表示式主页和相关文档、下载以及各种其他参考资料 都可以在那里找到。NLTK 的文档可以自项目主页获得。在那里您可以找到库的一些版本的 API 参考指南。 本文完成时,1.3 版是稳定的,1.4 版仍处在测试中;不过当您阅读本文时,可能已经有了更新的版本。
  • 对 NLTK 的新用户来说 —— 包括作者,虽然他撰写了本文 —— 是非常有使用价值的。撰写本文时,它们概括地涵盖了 NLTK 各个子程序包; David 特别欣赏一个关于概率系统建模的 。 三本辅助教程为那些可能还不了解 Python 语言的语言学学生(或其他人)更概括地介绍了 Python。这些教程会 有所帮助而且写得很好,只是偶而有一些细节好像与最新的 API 版本不符。
  • 在一个较早的 可爱的 Python专栏中涵盖了 并介绍了 gnosis.indexer 工具。
  • 可爱的 Python 的其他几期文章已经介绍了用于人工语言的解析器:
  • 浏览 developerWorks 上 的所有文章。
  • David 的 (Addison Wesley,2003)介绍了正则表达式、形式解析器、文本处理还有声明机制。这是开始接触计算语言学的很 好的起点。此外,David 欣喜地发现,在在研究本文的过程中他的书中的一个效用函数已经被加入到 NLTK 程序包中。
  • 通过 和 按季度发行的 ACL 杂志 来深入学习计算语言学。
  • 免费的百科全书 Wikipedia 提供了对 和 的概要介绍,并且给出了关于它们两者的资料的有用链接。
  • 上下文无关(及其他形式的)语法是非常复杂的。另外参阅 Eli Project 的 以及关于 的 Wikipedia 文章。
  • 在 可以找到 更多为 Linux 开发者准备的参考资料。
  • 在 Developer Bookstore Linux 区中定购 。
  • 自 developerWorks 的 专区下载可以运行于 Linux 之上的经过挑选的 developerWorks Subscription 产品免费测试版本,包括 WebSphere Studio Site Developer、WebSphere SDK for Web services、WebSphere Application Server、DB2 Universal Database Personal Developers Edition、Tivoli Access Manager 和 Lotus Domino Server。要更快速地开始上手,请参阅针对各个产品的 how-to 文章和技术支持。

David Mertz

David Mertz 根本不知道他一直在写 散文。 您可以通过 与 David 联系;您可以通过他的 了解他的生活。请阅读他的 一书。欢迎提出关于过去或将来专栏的意见和建议。

转载地址:http://ihkax.baihongyu.com/

你可能感兴趣的文章
c语言中秋log10的函数,10本科生的C++成长轨迹7 - ACM培训:数组&函数&指针
查看>>
android 设备运营商,Android设备悲剧:新技术让运营商可以向设备“偷偷”安装软件...
查看>>
html语言link,HTML <link>标签
查看>>
html最小化打开新页面,【html相关】html中如何实现在新标签中打开另一个新的页面?...
查看>>
在html中加入tablestyle,html表格table的使用,以及表格的css样式
查看>>
android全屏监听,Android SurfaceView实现全屏播放例子
查看>>
html console 滚动条,JavaScript - 控制滚动条操作
查看>>
html5中按钮尺寸设计,UI设计中的按钮设计规范
查看>>
html方法介绍,jQuery html()等方法介绍
查看>>
Apache2月9日邮件:Tomcat请求漏洞(Request Smuggling)
查看>>
WPF外包技术分享—WPF的MVVM架构解析(分享)
查看>>
数字签名与数字证书
查看>>
GHOST -BATCH 参数的妙用
查看>>
控制反转 (Inversion of Control, IoC)
查看>>
Catalyst 3850 Series Switch Recovery
查看>>
python datetime模块的timedelta
查看>>
Spark笔记整理(二):RDD与spark核心概念名词
查看>>
定制带RAID阵列卡驱动的WINPE3.0系统
查看>>
Microsoft Office 2010 Service Pack 2
查看>>
Python 学习笔记 - Memcached
查看>>