pythonnltk语义分析_Python⾃然语⾔⼯具包(NLTK)⼊门在本期⽂章中,⼩⽣向您介绍了⾃然语⾔⼯具包(Natural Language Toolkit),它是⼀个将学术语⾔技术应⽤于⽂本数据集的 Python 库。称为“⽂本处理”的程序设计是其基本功能;更深⼊的是专门⽤于研究⾃然语⾔的语法以及语义分析的能⼒。
鄙⼈并⾮见多识⼴, 语⾔处理(linguistic processing) 是⼀个相对新奇的领域。如果在对意义⾮凡的⾃然语⾔⼯具包(NLTK)的说明中出现了错误,请您谅解。NLTK 是使⽤ Python 教学以及实践计算语⾔学的极好⼯具。此外,计算语⾔学与⼈⼯智能、语⾔/专门语⾔识别、翻译以及语法检查等领域关系密切。
NLTK 会被⾃然地看作是具有栈结构的⼀系列层,这些层构建于彼此基础之上。那些熟悉⼈⼯语⾔(⽐如 Python)的⽂法和解析的读者来说,理解⾃然语⾔模型中类似的 —— 但更深奥的 —— 层不会有太⼤困难。
尽管 NLTK 附带了很多已经预处理(通常是⼿⼯地)到不同程度的全集,但是概念上每⼀层都是依赖于相邻的更低层次的处理。⾸先是断词;然后是为单词加上 标签;然后将成组的单词解析为语法元素,⽐如名词短语或句⼦(取决于⼏种技术中的某⼀种,每种技术都有其优缺点);最后对最终语句或其他语法单元进⾏分类。通过这些步骤,NLTK 让您可以⽣成关于不同元素出现情况的统计,并画出描述处理过程本⾝或统计合计结果的图表。
在本⽂中,您将看到关于低层能⼒的⼀些相对完整的⽰例,⽽对⼤部分⾼层次能⼒将只是进⾏简单抽象的描述。现在让我们来详细分析⽂本处理的⾸要步骤。
断词(Tokenization)
您可以使⽤ NLTK 完成的很多⼯作,尤其是低层的⼯作,与使⽤ Python 的基本数据结构来完成相⽐,并没有太⼤的区别。不过,NLTK 提供了⼀组由更⾼的层所依赖和使⽤的系统化的接⼝,⽽不只是简单地提供实⽤的类来处理加过标志或加过标签的⽂本。具体
讲,kenizer.Token 类被⼴泛地⽤于存储⽂本的有注解的⽚断;这些注解可以标记很多不同的特性,包括词类(parts-of-speech)、⼦标志(subtoken)结构、⼀个标志(token)在更⼤⽂本中的偏移位置、语形词⼲(morphological stems)、⽂法语句成分,等等。实际上,⼀个 Token 是⼀种特别的字典 —— 并且以字典形式访问 —— 所以它可以容纳任何您希望的键。在 NLTK 中使⽤了⼀些专门的键,不同的键由不同的⼦程序包所使⽤。
让我们来简要地分析⼀下如何创建⼀个标志并将其拆分为⼦标志:
1 >>> kenize import *
2
3 >>> t = Token(TEXT='This is my first test sentence')4
冒泡排序为什么叫冒泡5 >>> WSTokenizer().tokenize(t, addlocs=True) #break on whitespace
6
7 >>> print t['TEXT'] This ismy first test sentence8
9 >>> print t['SUBTOKENS'] [@[0:4c], @[5:7c], @[8:10c], @[11:16c], @[17:21c], @[22:30c]]10
11 >>> t['foo'] = 'bar'
12
13 >>> t @[0:4c], @[5:7c], @[8:10c], @[11:16c], @[17:21c], @[22:30c]]>
14
15 >>> print t['SUBTOKENS'][0] @[0:4c]16
17 >>> print type(t['SUBTOKENS'][0])
概率(Probability)
对于语⾔全集,您可能要做的⼀件相当简单的事情是分析其中各种 事件(events) 的频率分布,并基于这些已知频率分布做出概率预测。NLTK ⽀持多种基于⾃然频率分布数据进⾏概率预测的⽅法。我将不会在这⾥介绍那些⽅法(参阅 参考资料 中列出的概率教程),只要说明您肯定会 期望的那些与您已经 知道的 那些(不⽌是显⽽易见的缩放⽐例/正规化)之间有着⼀些模糊的关系就够了。
基本来讲,NLTK ⽀持两种类型的频率分布:直⽅图和条件频率分布(conditional frequency)。 nltk.probability.FreqDist 类⽤于创建直⽅图;例如,可以这样创建⼀个单词直⽅图:
>>> from nltk.probability import *
>>> article = Token(TEXT=open('').read())
>>> WSTokenizer().tokenize(article) >>> freq = FreqDist()
html模板国内>>> for word in article['SUBTOKENS']: ... freq.inc(word['TEXT'])
excel表格制作教程入门视频 blbl>>> freq.B() 1194
>>> unt('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,
条件频率分布在语⾔⽅⾯的⼀个极好应⽤是分析全集中的语段分布 —— 例如,给出⼀个特定的词,接下来最可能出现哪个词。当然,语法会带来⼀些限制;不过,对句法选项的选择的研究属于语义学、语⽤论和术语范畴。
词⼲提取(Stemming)
nltk.stemmer.porter.PorterStemmer 类是⼀个⽤于从英⽂单词中获得符合语法的(前缀)词⼲的极其便利的⼯具。这⼀能⼒尤其让我⼼动,因为我以前曾经⽤ Python 创建了⼀个公⽤的、全⽂本索引的搜索⼯具/库(见 Developing a full-text indexer in 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 时我发现 kenizer.WSTokenizer 类确实如教程所警告的那样不好⽤。它可以胜任概念上的⾓⾊,但是对于实际的⽂本⽽⾔,您可以更好地识别出什么是⼀个 “单词”。幸运的
是, gnosis.indexer.TextSplitter 是⼀个健壮的断词⼯具。例如:
>>> kenizer import *
>>> article = Token(TEXT=open('').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('').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']
upload翻译在这⾥,您可以看到有⼀些单词有多个可能的扩展,⽽且所有单词看起来都像是单词或者词素。断词⽅法对随机⽂本集合来说⾄关重要;公平地讲,NLTK 捆绑的全集已经通过 WSTokenizer() 打包为易⽤且准确的断词⼯具。要获得健壮的实际可⽤的索引器,需要使⽤健壮的断词⼯具。
添加标签(tagging)、分块(chunking)和解析(parsing)
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')
>>> kenize(sentence)
>>> sentence['SUBTOKENS'][0] (NP: )
python入门教程非常详细word>>> 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: ))
所提及的分块⼯作可以由 kenizer.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 效率更⾼,但是可能会错过⼀些产品。不论在哪种情况下,语法规则的声明都类似于解析⼈⼯语⾔的语法声明。本专栏曾经介绍了其中的⼀些: SimpleParse 、 mx.TextTools 、 Spark 和 l.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 中。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论