jupyter 版请见我的github:https://github.com/JackKuo666/Python_nlp_notes

【Python自然语言处理】读书笔记:第五章:分类和标注词汇

本章原文:https://usyiyi.github.io/nlp-py-2e-zh/5.html

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

1. 什么是词汇分类,在自然语言处理中它们是如何使用?2. 一个好的存储词汇和它们的分类的Python数据结构是什么?3. 我们如何自动标注文本中词汇的词类?

一路上,我们将介绍NLP的一些基本技术,包括序列标注、N-gram模型、回退和评估。

1 使用词性标注器

from  nltk import *
import nltk
text = word_tokenize("And now for something completely different")
print(nltk.pos_tag(text))
[('And', 'CC'), ('now', 'RB'), ('for', 'IN'), ('something', 'NN'), ('completely', 'RB'), ('different', 'JJ')]

text.similar()方法接收一个单词w,找出所有上下文w1w w2,然后找出所有出现在相同上下文中的词w’,即w1w’w2。

text = nltk.Text(word.lower() for word in nltk.corpus.brown.words())
print(text.similar("woman"),"\n")
print(text.similar("bought"),"\n")
print(text.similar("over"),"\n")
print(text.similar("the"),"\n")
man time day year car moment world house family child country boy
state job place way war girl work word
None made said done put had seen found given left heard was been brought
set got that took in told felt
None in on to of and for with from at by that into as up out down through
is all about
None a his this their its her an that our any all one these my in your no
some other and
None

2 已经标注的语料库

2.1 表示已经标注的词符

按照NLTK的约定,一个已标注的词符使用一个由词符和标记组成的元组来表示。我们可以使用函数str2tuple()从表示一个已标注的词符的标准字符串创建一个这样的特殊元组:

tagged_token = nltk.tag.str2tuple("gly/NN")
print(tagged_token,"\n",tagged_token[0])
('gly', 'NN') gly

我们可以直接从一个字符串构造一个已标注的词符的列表。第一步是对字符串分词以便能访问单独的单词/标记字符串,然后将每一个转换成一个元组(使用str2tuple())。

sent = '''
The/AT grand/JJ jury/NN commented/VBD on/IN a/AT number/NN of/IN
other/AP topics/NNS ,/, AMONG/IN them/PPO the/AT Atlanta/NP and/CC
Fulton/NP-tl County/NN-tl purchasing/VBG departments/NNS which/WDT it/PPS
said/VBD ``/`` ARE/BER well/QL operated/VBN and/CC follow/VB generally/RB
accepted/VBN practices/NNS which/WDT inure/VB to/IN the/AT best/JJT
interest/NN of/IN both/ABX governments/NNS ''/'' ./.
'''
print([nltk.tag.str2tuple(t) for t in sent.split()])
[('The', 'AT'), ('grand', 'JJ'), ('jury', 'NN'), ('commented', 'VBD'), ('on', 'IN'), ('a', 'AT'), ('number', 'NN'), ('of', 'IN'), ('other', 'AP'), ('topics', 'NNS'), (',', ','), ('AMONG', 'IN'), ('them', 'PPO'), ('the', 'AT'), ('Atlanta', 'NP'), ('and', 'CC'), ('Fulton', 'NP-TL'), ('County', 'NN-TL'), ('purchasing', 'VBG'), ('departments', 'NNS'), ('which', 'WDT'), ('it', 'PPS'), ('said', 'VBD'), ('``', '``'), ('ARE', 'BER'), ('well', 'QL'), ('operated', 'VBN'), ('and', 'CC'), ('follow', 'VB'), ('generally', 'RB'), ('accepted', 'VBN'), ('practices', 'NNS'), ('which', 'WDT'), ('inure', 'VB'), ('to', 'IN'), ('the', 'AT'), ('best', 'JJT'), ('interest', 'NN'), ('of', 'IN'), ('both', 'ABX'), ('governments', 'NNS'), ("''", "''"), ('.', '.')]

2.2 读取已标注的语料库

NLTK中包括的若干语料库已标注了词性。

print(nltk.corpus.brown.tagged_words())
[('The', 'AT'), ('Fulton', 'NP-TL'), ...]

2.3 通用词性标记集

from nltk.corpus import brown
brown_news_tagged = brown.tagged_words(categories = "news", tagset = "universal")
tag_fd = nltk.FreqDist(tag for (word, tag) in brown_news_tagged)
print(tag_fd.most_common())
tag_fd.plot(cumulative = True)
[('NOUN', 30654), ('VERB', 14399), ('ADP', 12355), ('.', 11928), ('DET', 11389), ('ADJ', 6706), ('ADV', 3349), ('CONJ', 2717), ('PRON', 2535), ('PRT', 2264), ('NUM', 2166), ('X', 92)]

nltk.app.concordance()
**********************************************************************Resource 'corpora/cess_cat' not found.  Please use the NLTKDownloader to obtain the resource:  >>> nltk.download()Searched in:- '/home/kuo/nltk_data'- '/usr/share/nltk_data'- '/usr/local/share/nltk_data'- '/usr/lib/nltk_data'- '/usr/local/lib/nltk_data'
********************************************************************************************************************************************Resource 'corpora/alpino' not found.  Please use the NLTKDownloader to obtain the resource:  >>> nltk.download()Searched in:- '/home/kuo/nltk_data'- '/usr/share/nltk_data'- '/usr/local/share/nltk_data'- '/usr/lib/nltk_data'- '/usr/local/lib/nltk_data'
********************************************************************************************************************************************Resource 'corpora/sinica_treebank' not found.  Please use theNLTK Downloader to obtain the resource:  >>> nltk.download()Searched in:- '/home/kuo/nltk_data'- '/usr/share/nltk_data'- '/usr/local/share/nltk_data'- '/usr/lib/nltk_data'- '/usr/local/lib/nltk_data'
**********************************************************************

2.4 名词

名词一般指的是人、地点、事情或概念,例如: woman, Scotland, book, intelligence。

名词可能出现在限定词和形容词之后,可以是动词的主语或宾语,如2.2所示。

word_tag_pairs = nltk.bigrams(brown_news_tagged)
noun_preceders = [a[1] for (a,b) in word_tag_pairs if b[1] == "NOUN"]
fdist = nltk.FreqDist(noun_preceders)
print([tag for (tag, _) in fdist.most_common()])
['NOUN', 'DET', 'ADJ', 'ADP', '.', 'VERB', 'CONJ', 'NUM', 'ADV', 'PRT', 'PRON', 'X']

这证实了我们的断言,名词出现在限定词和形容词之后,包括数字形容词(数词,标注为NUM)。

2.5 动词

动词是用来描述事件和行动的词,例如2.3中的fall, eat。在一个句子中,动词通常表示涉及一个或多个名词短语所指示物的关系。

一些动词的句法模式

wsj = nltk.corpus.treebank.tagged_words(tagset = "universal")
word_tag_fd = nltk.FreqDist(wsj)
print([wt[0] for (wt, _) in word_tag_fd.most_common() if wt[1] == "VERB"][:10])
['is', 'said', 'was', 'are', 'be', 'has', 'have', 'will', 'says', 'would']

请注意,频率分布中计算的项目是词-标记对。由于词汇和标记是成对的,我们可以把词作作为条件,标记作为事件,使用条件-事件对的链表初始化一个条件频率分布。这让我们看到了一个给定的词的标记的频率顺序列表:

cfd1 = nltk.ConditionalFreqDist(wsj)
print(cfd1["yield"].most_common())
print(cfd1["cut"].most_common())
[('VERB', 28), ('NOUN', 20)]
[('VERB', 25), ('NOUN', 3)]

我们可以颠倒配对的顺序,这样标记作为条件,词汇作为事件。现在我们可以看到对于一个给定的标记可能的词。我们将用《华尔街日报 》的标记集而不是通用的标记集来这样做:

wsj = nltk.corpus.treebank.tagged_words()
cfd2 = nltk.ConditionalFreqDist((tag, word) for (word, tag) in wsj)
print(list(cfd2["VBN"])[:10])
['named', 'used', 'caused', 'exposed', 'reported', 'replaced', 'sold', 'died', 'expected', 'diagnosed']

要弄清VBD(过去式)和VBN(过去分词)之间的区别,让我们找到可以同是VBD和VBN的词汇,看看一些它们周围的文字:

print([w for w in cfd1.conditions() if "VERB" in cfd1[w] and "ADJ" in cfd1[w]])
['used', 'even', 'brief', 'total', 'open', 'top', 'yielding', 'resigned', 'complete', 'like', 'free', 'last', 'related', 'troubled', 'complicated', 'underlying', 'record', 'working', 'close', 'direct', 'own', 'favored', 'accelerated', 'growing', 'combined', 'slow', 'elaborate', 'preferred', 'excise', 'tender', 'sweeping', 'single', 'capped', 'head', 'mature', 'clear', 'leading', 'suspect', 'engaging', 'back', 'planned', 'double', 'sound', 'beaten', 'needed', 'confused', 'mean', 'devastating', 'following', 'forcing', 'known', 'parallel', 'integrated', 'alleged', 'winning', 'split', 'interested', 'correct', 'advanced', 'funded', 'controlled', 'damaged', 'detailed', 'organized', 'laughing', 'limited', 'extended', 'disappointed', 'scared', 'heated', 'impending', 'live', 'diversified', 'upset', 'closing', 'midsized', 'present', 'qualified']
idx1 = wsj.index(('kicked', 'VBD'))
print(wsj[idx1 - 4 : idx1 + 1])
idx2 = wsj.index(("kicked", "VBN"))
print(wsj[idx2 - 4 : idx2 + 1])
[('While', 'IN'), ('program', 'NN'), ('trades', 'NNS'), ('swiftly', 'RB'), ('kicked', 'VBD')]
[('head', 'NN'), ('of', 'IN'), ('state', 'NN'), ('has', 'VBZ'), ('kicked', 'VBN')]

在这种情况下,我们可以看到过去分词kicked前面是助动词have的形式。这是普遍真实的吗?

2.6 形容词和副词

另外两个重要的词类是形容词和副词。形容词修饰名词,可以作为修饰语(如the large pizza中的large),或者谓语(如the pizza is large)。英语形容词可以有内部结构(如the falling stocks中的fall+ing)。副词修饰动词,指定动词描述的事件的时间、方式、地点或方向(如the stocks fell quickly中的quickly)。副词也可以修饰的形容词(如Mary’s teacher was really nice中的really)。

英语中还有几个封闭的词类,如介词,冠词(也常称为限定词)(如the、a),情态动词(如should、may)和人称代词(如she、they)。每个词典和语法对这些词的分类都不同。

2.7 未简化的标记

让我们找出每个名词类型中最频繁的名词。

2.2中的程序找出所有以NN开始的标记,并为每个标记提供了几个示例单词。你会看到有许多NN的变种;最重要有$表示所有格名词,S表示复数名词(因为复数名词通常以s结尾),以及P表示专有名词。此外,大多数的标记都有后缀修饰符:-NC表示引用,-HL表示标题中的词,-TL表示标题(布朗标记的特征)。

def findtags(tag_prefix, tagged_text):cfd = nltk.ConditionalFreqDist((tag, word) for (word, tag) in tagged_text if tag.startswith(tag_prefix))return dict((tag, cfd[tag].most_common(5)) for tag in cfd.conditions())tagdict = findtags("NN", nltk.corpus.brown.tagged_words(categories = "news"))
for tag in sorted(tagdict):print(tag, tagdict[tag])
NN [('year', 137), ('time', 97), ('state', 88), ('week', 85), ('man', 72)]
NN$ [("year's", 13), ("world's", 8), ("state's", 7), ("nation's", 6), ("city's", 6)]
NN$-HL [("Golf's", 1), ("Navy's", 1)]
NN$-TL [("President's", 11), ("Administration's", 3), ("Army's", 3), ("League's", 3), ("University's", 3)]
NN-HL [('sp.', 2), ('problem', 2), ('Question', 2), ('cut', 2), ('party', 2)]
NN-NC [('ova', 1), ('eva', 1), ('aya', 1)]
NN-TL [('President', 88), ('House', 68), ('State', 59), ('University', 42), ('City', 41)]
NN-TL-HL [('Fort', 2), ('Mayor', 1), ('Commissioner', 1), ('City', 1), ('Oak', 1)]
NNS [('years', 101), ('members', 69), ('people', 52), ('sales', 51), ('men', 46)]
NNS$ [("children's", 7), ("women's", 5), ("men's", 3), ("janitors'", 3), ("taxpayers'", 2)]
NNS$-HL [("Dealers'", 1), ("Idols'", 1)]
NNS$-TL [("Women's", 4), ("States'", 3), ("Giants'", 2), ("Princes'", 1), ("Bombers'", 1)]
NNS-HL [('Wards', 1), ('deputies', 1), ('bonds', 1), ('aspects', 1), ('Decisions', 1)]
NNS-TL [('States', 38), ('Nations', 11), ('Masters', 10), ('Communists', 9), ('Rules', 9)]
NNS-TL-HL [('Nations', 1)]

2.8 探索已标注的语料库

让我们简要地回过来探索语料库,我们在前面的章节中看到过,这次我们探索词性标记。

假设我们正在研究词often,想看看它是如何在文本中使用的。我们可以试着看看跟在often后面的词汇

brown_learned_text = brown.words(categories = "learned")
print(sorted(set(b for (a, b) in nltk.bigrams(brown_learned_text) if a == "often")))
[',', '.', 'accomplished', 'analytically', 'appear', 'apt', 'associated', 'assuming', 'became', 'become', 'been', 'began', 'call', 'called', 'carefully', 'chose', 'classified', 'colorful', 'composed', 'contain', 'differed', 'difficult', 'encountered', 'enough', 'equate', 'extremely', 'found', 'happens', 'have', 'ignored', 'in', 'involved', 'more', 'needed', 'nightly', 'observed', 'of', 'on', 'out', 'quite', 'represent', 'responsible', 'revamped', 'seclude', 'set', 'shortened', 'sing', 'sounded', 'stated', 'still', 'sung', 'supported', 'than', 'to', 'when', 'work']

然而,使用tagged_words()方法查看跟随词的词性标记可能更有指导性:

brown_lrnd_tagged = brown.tagged_words(categories = "learned", tagset = "universal")
tags = [b[1] for (a, b) in nltk.bigrams(brown_lrnd_tagged) if a[0] == "often"]
fd = nltk.FreqDist(tags)
fd.tabulate()
VERB  ADV  ADP  ADJ    .  PRT 37    8    7    6    4    2

请注意often后面最高频率的词性是动词。名词从来没有在这个位置出现(在这个特别的语料中)。

接下来,让我们看一些较大范围的上下文,找出涉及特定标记和词序列的词(在这种情况下,“Verb to Verb”)。在code-three-word-phrase中,我们考虑句子中的每个三词窗口[1],检查它们是否符合我们的标准[2]。如果标记匹配,我们输出对应的词[3]。

from nltk.corpus import brown
def process(sentence):for (w1, t1), (w2, t2), (w3, t3) in nltk.trigrams(sentence):if (t1.startswith("V") and t2 == "TO" and t3.startswith("V")):print(w1, w2, w3)
count = 0
for tagged_sent in brown.tagged_sents():process(tagged_sent)count += 1if count >= 100:break
combined to achieve
continue to place
serve to protect
wanted to wait
allowed to place
expected to become
expected to approve
expected to make
intends to make
seek to set
like to see

最后,让我们看看与它们的标记关系高度模糊不清的词。了解为什么要标注这样的词是因为它们各自的上下文可以帮助我们弄清楚标记之间的区别。

brown_news_tagged = brown.tagged_words(categories = "news", tagset = "universal")
data = nltk.ConditionalFreqDist((word.lower(), tag) for (word, tag) in brown_news_tagged)
for word in sorted(data.conditions()):if len(data[word]) > 3:tags = [tag for (tag, _) in data[word].most_common()]print(word, " ".join(tags))
best ADJ ADV VERB NOUN
close ADV ADJ VERB NOUN
open ADJ VERB NOUN ADV
present ADJ ADV NOUN VERB
that ADP DET PRON ADV

3 使用Python字典映射单词到其属性

3.3 定义字典

我们可以使用键-值对格式创建字典。有两种方式做这个,我们通常会使用第一个:

pos = {"colorless": "ADJ", "ideas": "N", "sleep": "V", "furiously": "ADV"}
pos = dict(colorless = "ADJ", ideas = "N", sleep = "V", furiously = "ADV")

请注意,字典的键必须是不可改变的类型,如字符串和元组。如果我们尝试使用可变键定义字典会得到一个TypeError:

pos = {["ideas", "blogs", "adventures"]: "N"}
---------------------------------------------------------------------------TypeError                                 Traceback (most recent call last)<ipython-input-56-2ef56052ab4d> in <module>
----> 1 pos = {["ideas", "blogs", "adventures"]: "N"}TypeError: unhashable type: 'list'

3.4 默认字典

如果我们试图访问一个不在字典中的键,会得到一个错误。然而,如果一个字典能为这个新键自动创建一个条目并给它一个默认值,如0或者一个空链表,将是有用的。由于这个原因,可以使用一种特殊的称为defaultdict的字典。为了使用它,我们必须提供一个参数,用来创建默认值,如int, float, str, list, dict, tuple。

from collections import defaultdict
frequency = defaultdict(int)
frequency["colorless"] = 4
print(frequency["colorless"])
print(frequency["new"])
pos = defaultdict(list)
pos["sleep"] = ["NOUN", "VERB"]
print(pos["new"])
4
0
[]

这些默认值实际上是将其他对象转换为指定类型的函数(例如int(“2”), list(“2”))。当它们不带参数被调用时——int(), list()——它们分别返回0和[] 。

前面的例子中指定字典项的默认值为一个特定的数据类型的默认值。然而,也可以指定任何我们喜欢的默认值,只要提供可以无参数的被调用产生所需值的函数的名子。让我们回到我们的词性的例子,创建一个任一条目的默认值是’N’的字典[1]。当我们访问一个不存在的条目时[2],它会自动添加到字典[3]。

pos = defaultdict(lambda: "NOUN")
pos["colorless"] = "ADJ"
print(pos["blog"])
print([pos])
NOUN
[defaultdict(<function <lambda> at 0x7fa3e3cb51e0>, {'colorless': 'ADJ', 'blog': 'NOUN'})]

让我们来看看默认字典如何被应用在较大规模的语言处理任务中。许多语言处理任务——包括标注——费很大力气来正确处理文本中只出现过一次的词。如果有一个固定的词汇和没有新词会出现的保证,它们会有更好的表现。在一个默认字典的帮助下,我们可以预处理一个文本,替换低频词汇为一个特殊的“超出词汇表”词符UNK。(你能不看下面的想出如何做吗?)

我们需要创建一个默认字典,映射每个词为它们的替换词。最频繁的n个词将被映射到它们自己。其他的被映射到UNK。

alice = nltk.corpus.gutenberg.words("carroll-alice.txt")
vocab = nltk.FreqDist(alice)
v1000 = [word for (word, _) in vocab.most_common(1000)]
mapping = defaultdict(lambda: "UNK")
for v in v1000:mapping[v] = v
alice2 = [mapping[v] for v in alice]
print(alice2[:10])
print(len(alice))
print(len(alice2))
['[', 'Alice', "'", 's', 'Adventures', 'in', 'Wonderland', 'by', 'UNK', 'UNK']
34110
34110

3.5 递增地更新字典

我们可以使用字典计数出现的次数,模拟fig-tally所示的计数词汇的方法。首先初始化一个空的defaultdict,然后处理文本中每个词性标记。如果标记以前没有见过,就默认计数为零。每次我们遇到一个标记,就使用+=运算符递增它的计数。

from collections import defaultdict
counts = defaultdict(int)
from nltk.corpus import brown
for (word, tag) in brown.tagged_words(categories = "news", tagset = "universal"):counts[tag] += 1
print(counts["NOUN"])
print(sorted(counts))
30654
['.', 'ADJ', 'ADP', 'ADV', 'CONJ', 'DET', 'NOUN', 'NUM', 'PRON', 'PRT', 'VERB', 'X']
from operator import itemgetter
print(sorted(counts.items(), key = itemgetter(1), reverse = True))
print([t for t, c in sorted(counts.items(), key = itemgetter(1), reverse = True)])
[('NOUN', 30654), ('VERB', 14399), ('ADP', 12355), ('.', 11928), ('DET', 11389), ('ADJ', 6706), ('ADV', 3349), ('CONJ', 2717), ('PRON', 2535), ('PRT', 2264), ('NUM', 2166), ('X', 92)]
['NOUN', 'VERB', 'ADP', '.', 'DET', 'ADJ', 'ADV', 'CONJ', 'PRON', 'PRT', 'NUM', 'X']

列表演示了一个重要的按值排序一个字典的习惯用法,来按频率递减顺序显示词汇。sorted()的第一个参数是要排序的项目,它是由一个词性标记和一个频率组成的元组的列表。第二个参数使用函数itemgetter()指定排序的键。在一般情况下,itemgetter(n)返回一个函数,这个函数可以在一些其他序列对象上被调用获得这个序列的第n个元素,例如:

pair = ("NP", 8336)
print(pair[1])
print(itemgetter(1)(pair))
8336
8336

sorted()的最后一个参数指定项目是否应被按相反的顺序返回,即频率值递减。

在3.3的开头还有第二个有用的习惯用法,那里我们初始化一个defaultdict,然后使用for循环来更新其值。下面是一个示意版本:

>>> my_dictionary = defaultdict(function to create default value)>>> for item in sequence:
... my_dictionary[item_key] is updated with information about item

下面是这种模式的另一个示例,我们按它们最后两个字母索引词汇:

last_letters = defaultdict(list)
words = nltk.corpus.words.words("en")
for word in words:key = word[-2:]last_letters[key].append(word)
print(last_letters["ly"][:10])
print(last_letters["zy"][:10])
['abactinally', 'abandonedly', 'abasedly', 'abashedly', 'abashlessly', 'abbreviately', 'abdominally', 'abhorrently', 'abidingly', 'abiogenetically']
['blazy', 'bleezy', 'blowzy', 'boozy', 'breezy', 'bronzy', 'buzzy', 'Chazy', 'cozy', 'crazy']

下面的例子使用相同的模式创建一个颠倒顺序的词字典。(你可能会试验第3行来弄清楚为什么这个程序能运行。)

anagrams = defaultdict(list)
for word in words:key = "".join(sorted(word))anagrams[key].append(word)
print(anagrams["aeilnrt"])
['entrail', 'latrine', 'ratline', 'reliant', 'retinal', 'trenail']

由于积累这样的词是如此常用的任务,NLTK提供一个创建defaultdict(list)更方便的方式,形式为nltk.Index()。

anagrams = nltk.Index((''.join(sorted(w)), w) for w in words)
print(anagrams["aeilnrt"])
['entrail', 'latrine', 'ratline', 'reliant', 'retinal', 'trenail']

nltk.Index是一个支持额外初始化的defaultdict(list)。类似地,nltk.FreqDist本质上是一个额外支持初始化的defaultdict(int)(附带排序和绘图方法)。

3.6 复杂的键和值

我们可以使用具有复杂的键和值的默认字典。让我们研究一个词可能的标记的范围,给定词本身和它前一个词的标记。我们将看到这些信息如何被一个词性标注器使用。

pos = defaultdict(lambda: defaultdict(int))
brown_news_tagged = brown.tagged_words(categories = "news", tagset = "universal")
for ((w1, t1), (w2, t2)) in nltk.bigrams(brown_news_tagged):pos[(t1, w2)][t2] += 1
print(pos[("DET", "right")])
defaultdict(<class 'int'>, {'NOUN': 5, 'ADJ': 11})

这个例子使用一个字典,它的条目的默认值也是一个字典(其默认值是int(),即0)。请注意我们如何遍历已标注语料库的双连词,每次遍历处理一个词-标记对[1]。每次通过循环时,我们更新字典pos中的条目(t1, w2),一个标记和它后面的词[2]。当我们在pos中查找一个项目时,我们必须指定一个复合键[3],然后得到一个字典对象。一个词性标注器可以使用这些信息来决定词right,前面是一个限定词时,应标注为ADJ。

3.7 反转字典

字典支持高效查找,只要你想获得任意键的值。如果d是一个字典,k是一个键,输入d[k],就立即获得值。给定一个值查找对应的键要慢一些和麻烦一些:

counts = defaultdict(int)
for word in nltk.corpus.gutenberg.words("milton-paradise.txt"):counts[word] +=1
print([key for (key, value) in counts.items() if value == 32])
['mortal', 'Against', 'Him', 'There', 'brought', 'King', 'virtue', 'every', 'been', 'thine']

如果我们希望经常做这样的一种“反向查找”,建立一个映射值到键的字典是有用的。在没有两个键具有相同的值情况,这是一个容易的事。只要得到字典中的所有键-值对,并创建一个新的值-键对字典。下一个例子演示了用键-值对初始化字典pos的另一种方式。

pos = {"colorless": "ADj", "ideas": "N", "sleep": "V", "furiously": "ADV"}
pos2 = dict((value, key) for (key, value) in pos.items())
print(pos2["N"])
ideas

首先让我们将我们的词性字典做的更实用些,使用字典的update()方法加入再一些词到pos中,创建多个键具有相同的值的情况。这样一来,刚才看到的反向查找技术就将不再起作用(为什么不?)作为替代,我们不得不使用append()积累词和每个词性,如下所示:

pos.update({'cats': 'N', 'scratch': 'V', 'peacefully': 'ADV', 'old': 'ADJ'})
pos2 = defaultdict(list)
for key, value in pos.items():pos2[value].append(key)
print(pos2["N"])
['ideas', 'cats']

现在,我们已经反转字典pos,可以查任意词性找到所有具有此词性的词。可以使用NLTK中的索引支持更容易的做同样的事,如下所示:

pos2 = nltk.Index((value, key) for (key, value) in pos.items())
print(pos2["ADV"])
['furiously', 'peacefully']

4.1 默认标注器

最简单的标注器是为每个词符分配同样的标记。这似乎是一个相当平庸的一步,但它建立了标注器性能的一个重要的底线。为了得到最好的效果,我们用最有可能的标记标注每个词。让我们找出哪个标记是最有可能的(现在使用未简化标记集):

from nltk.corpus import brown
brown_tagged_sents = brown.tagged_sents(categories='news')
brown_sents = brown.sents(categories='news')
tags = [tag for (word, tag) in brown.tagged_words(categories = "news")]
print(nltk.FreqDist(tags).max())
NN

现在我们可以创建一个将所有词都标注成NN的标注器。

raw = "I do not like green eggs and ham, I do not like them Sam I am!"
tokens = word_tokenize(raw)
default_tagger = nltk.DefaultTagger("NN")
print(default_tagger.tag(tokens)[:10])
[('I', 'NN'), ('do', 'NN'), ('not', 'NN'), ('like', 'NN'), ('green', 'NN'), ('eggs', 'NN'), ('and', 'NN'), ('ham', 'NN'), (',', 'NN'), ('I', 'NN')]

不出所料,这种方法的表现相当不好。在一个典型的语料库中,它只标注正确了八分之一的标识符,正如我们在这里看到的:

default_tagger.evaluate(brown_tagged_sents)
0.13089484257215028

默认的标注器给每一个单独的词分配标记,即使是之前从未遇到过的词。碰巧的是,一旦我们处理了几千词的英文文本之后,大多数新词都将是名词。正如我们将看到的,这意味着,默认标注器可以帮助我们提高语言处理系统的稳定性。我们将很快回来讲述这个。

4.2 正则表达式标注器

正则表达式标注器基于匹配模式分配标记给词符。例如,我们可能会猜测任一以ed结尾的词都是动词过去分词,任一以’s结尾的词都是名词所有格。可以用一个正则表达式的列表表示这些:

>>> patterns = [
...     (r'.*ing$', 'VBG'),               # gerunds
...     (r'.*ed$', 'VBD'),                # simple past
...     (r'.*es$', 'VBZ'),                # 3rd singular present
...     (r'.*ould$', 'MD'),               # modals
...     (r'.*\'s$', 'NN$'),               # possessive nouns
...     (r'.*s$', 'NNS'),                 # plural nouns
...     (r'^-?[0-9]+(.[0-9]+)?$', 'CD'),  # cardinal numbers
...     (r'.*', 'NN')                     # nouns (default)
... ]

请注意,这些是顺序处理的,第一个匹配上的会被使用。现在我们可以建立一个标注器,并用它来标记一个句子。做完这一步会有约五分之一是正确的。

regexp_tagger = nltk.RegexpTagger(patterns)
print(regexp_tagger.tag(brown_sents[3]))
print(regexp_tagger.evaluate(brown_tagged_sents))
[('``', 'NN'), ('Only', 'NN'), ('a', 'NN'), ('relative', 'NN'), ('handful', 'NN'), ('of', 'NN'), ('such', 'NN'), ('reports', 'NNS'), ('was', 'NNS'), ('received', 'VBD'), ("''", 'NN'), (',', 'NN'), ('the', 'NN'), ('jury', 'NN'), ('said', 'NN'), (',', 'NN'), ('``', 'NN'), ('considering', 'VBG'), ('the', 'NN'), ('widespread', 'NN'), ('interest', 'NN'), ('in', 'NN'), ('the', 'NN'), ('election', 'NN'), (',', 'NN'), ('the', 'NN'), ('number', 'NN'), ('of', 'NN'), ('voters', 'NNS'), ('and', 'NN'), ('the', 'NN'), ('size', 'NN'), ('of', 'NN'), ('this', 'NNS'), ('city', 'NN'), ("''", 'NN'), ('.', 'NN')]
0.20326391789486245

最终的正则表达式«.*»是一个全面捕捉的,标注所有词为名词。这与默认标注器是等效的(只是效率低得多)。除了作为正则表达式标注器的一部分重新指定这个,有没有办法结合这个标注器和默认标注器呢?我们将很快看到如何做到这一点。

4.3 查询标注器

很多高频词没有NN标记。让我们找出100个最频繁的词,存储它们最有可能的标记。然后我们可以使用这个信息作为“查找标注器”(NLTK UnigramTagger)的模型:

fd = nltk.FreqDist(brown.words(categories = "news"))
cfd = nltk.ConditionalFreqDist(brown.tagged_words(categories = "news"))
most_freq_words = fd.most_common(100)
likely_tags = dict((word, cfd[word].max()) for (word, _) in most_freq_words)
print([cfd["the"].max()])
baseline_tagger = nltk.UnigramTagger(model = likely_tags)
baseline_tagger.evaluate(brown_tagged_sents)
['AT']0.45578495136941344

现在应该并不奇怪,仅仅知道100个最频繁的词的标记就使我们能正确标注很大一部分词符(近一半,事实上)。让我们来看看它在一些未标注的输入文本上做的如何:

sent = brown.sents(categories='news')[3]
print(baseline_tagger.tag(sent))
[('``', '``'), ('Only', None), ('a', 'AT'), ('relative', None), ('handful', None), ('of', 'IN'), ('such', None), ('reports', None), ('was', 'BEDZ'), ('received', None), ("''", "''"), (',', ','), ('the', 'AT'), ('jury', None), ('said', 'VBD'), (',', ','), ('``', '``'), ('considering', None), ('the', 'AT'), ('widespread', None), ('interest', None), ('in', 'IN'), ('the', 'AT'), ('election', None), (',', ','), ('the', 'AT'), ('number', None), ('of', 'IN'), ('voters', None), ('and', 'CC'), ('the', 'AT'), ('size', None), ('of', 'IN'), ('this', 'DT'), ('city', None), ("''", "''"), ('.', '.')]

许多词都被分配了一个None标签,因为它们不在100个最频繁的词之中。在这些情况下,我们想分配默认标记NN。换句话说,我们要先使用查找表,如果它不能指定一个标记就使用默认标注器,这个过程叫做回退(5)。我们可以做到这个,通过指定一个标注器作为另一个标注器的参数,如下所示。现在查找标注器将只存储名词以外的词的词-标记对,只要它不能给一个词分配标记,它将会调用默认标注器。

baseline_tagger = nltk.UnigramTagger(model = likely_tags, backoff = nltk.DefaultTagger("NN"))

让我们把所有这些放在一起,写一个程序来创建和评估具有一定范围的查找标注器 ,4.1。

def performance(cfd, wordlist):lt = dict((word, cfd[word].max()) for word in wordlist)baseline_tagger = nltk.UnigramTagger(model = lt, backoff = nltk.DefaultTagger("NN"))return baseline_tagger.evaluate(brown.tagged_sents(categories = "news"))def display():import pylabword_freqs = nltk.FreqDist(brown.words(categories = "news")).most_common()words_by_freq = [w for (w, _) in word_freqs]cfd = nltk.ConditionalFreqDist(brown.tagged_words(categories = "news"))sizes = 2 ** pylab.arange(15)perfs = [performance(cfd, words_by_freq[:size]) for size in sizes]pylab.plot(sizes, perfs, "-bo")pylab.title("Lookup Tagger Performancce with Varying Model Size")pylab.xlabel("Model Size")pylab.ylabel("Performance")pylab.show()
display()

可以观察到,随着模型规模的增长,最初的性能增加迅速,最终达到一个稳定水平,这时模型的规模大量增加性能的提高很小。(这个例子使用pylab绘图软件包,在4.8讨论过)。

4.4 评估

在前面的例子中,你会注意到对准确性得分的强调。事实上,评估这些工具的表现是NLP的一个中心主题。回想fig-sds中的处理流程;一个模块输出中的任何错误都在下游模块大大的放大。

我们对比人类专家分配的标记来评估一个标注器的表现。由于我们通常很难获得专业和公正的人的判断,所以使用黄金标准测试数据来代替。这是一个已经手动标注并作为自动系统评估标准而被接受的语料库。当标注器对给定词猜测的标记与黄金标准标记相同,标注器被视为是正确的。

当然,设计和实施原始的黄金标准标注的也是人。更深入的分析可能会显示黄金标准中的错误,或者可能最终会导致一个修正的标记集和更复杂的指导方针。然而,黄金标准就目前有关的自动标注器的评估而言被定义成“正确的”。

注意

开发一个已标注语料库是一个重大的任务。除了数据,它会产生复杂的工具、文档和实践,为确保高品质的标注。标记集和其他编码方案不可避免地依赖于一些理论主张,不是所有的理论主张都被共享,然而,语料库的创作者往往竭尽全力使他们的工作尽可能理论中立,以最大限度地提高其工作的有效性。我们将在11.讨论创建一个语料库的挑战。

5 N-gram标注

5.1 一元标注

一元标注器基于一个简单的统计算法:对每个标识符分配这个独特的标识符最有可能的标记。例如,它将分配标记JJ给词frequent的所有出现,因为frequent用作一个形容词(例如a frequent word)比用作一个动词(例如I frequent this cafe)更常见。一个一元标注器的行为就像一个查找标注器(4),除了有一个更方便的建立它的技术,称为训练。在下面的代码例子中,我们训练一个一元标注器,用它来标注一个句子,然后评估:

from nltk.corpus import brown
brown_tagged_sents = brown.tagged_sents(categories = "news")
brown_sents = brown.sents(categories = "news")
unigram_tagger = nltk.UnigramTagger(brown_tagged_sents)
print(unigram_tagger.tag(brown_sents[2007]))
print(unigram_tagger.evaluate(brown_tagged_sents))
[('Various', 'JJ'), ('of', 'IN'), ('the', 'AT'), ('apartments', 'NNS'), ('are', 'BER'), ('of', 'IN'), ('the', 'AT'), ('terrace', 'NN'), ('type', 'NN'), (',', ','), ('being', 'BEG'), ('on', 'IN'), ('the', 'AT'), ('ground', 'NN'), ('floor', 'NN'), ('so', 'QL'), ('that', 'CS'), ('entrance', 'NN'), ('is', 'BEZ'), ('direct', 'JJ'), ('.', '.')]
0.9349006503968017

我们训练一个UnigramTagger,通过在我们初始化标注器时指定已标注的句子数据作为参数。训练过程中涉及检查每个词的标记,将所有词的最可能的标记存储在一个字典里面,这个字典存储在标注器内部。

5.2 分离训练和测试数据

现在,我们正在一些数据上训练一个标注器,必须小心不要在相同的数据上测试,如我们在前面的例子中的那样。一个只是记忆它的训练数据,而不试图建立一个一般的模型的标注器会得到一个完美的得分,但在标注新的文本时将是无用的。相反,我们应该分割数据,90%为测试数据,其余10%为测试数据:

size = int(len(brown_tagged_sents) * 0.9)
print(size)
train_sents = brown_tagged_sents[:size]
test_sents = brown_tagged_sents[size:]
unigram_tagger = nltk.UnigramTagger(train_sents)
print(unigram_tagger.evaluate(test_sents))
4160
0.8121200039868434

虽然得分更糟糕了,但是现在我们对这种标注器的用处有了更好的了解,如它在之前没有遇见的文本上的表现。

5.3 一般的N-gram标注

在基于一元处理一个语言处理任务时,我们使用上下文中的一个项目。标注的时候,我们只考虑当前的词符,与更大的上下文隔离。给定一个模型,我们能做的最好的是为每个词标注其先验的最可能的标记。这意味着我们将使用相同的标记标注一个词,如wind,不论它出现的上下文是the wind还是to wind。

一个n-gram tagger标注器是一个一元标注器的一般化,它的上下文是当前词和它前面n-1个标识符的词性标记,如图5.1所示。要选择的标记是圆圈里的tn,灰色阴影的是上下文。在5.1所示的n-gram标注器的例子中,我们让n=3;也就是说,我们考虑当前词的前两个词的标记。一个n-gram标注器挑选在给定的上下文中最有可能的标记。


NgramTagger类使用一个已标注的训练语料库来确定对每个上下文哪个词性标记最有可能。这里我们看n-gram标注器的一个特殊情况,二元标注器。首先,我们训练它,然后用它来标注未标注的句子:

bigram_tagger = nltk.BigramTagger(train_sents)
print(bigram_tagger.tag(brown_sents[2007]))
[('Various', 'JJ'), ('of', 'IN'), ('the', 'AT'), ('apartments', 'NNS'), ('are', 'BER'), ('of', 'IN'), ('the', 'AT'), ('terrace', 'NN'), ('type', 'NN'), (',', ','), ('being', 'BEG'), ('on', 'IN'), ('the', 'AT'), ('ground', 'NN'), ('floor', 'NN'), ('so', 'CS'), ('that', 'CS'), ('entrance', 'NN'), ('is', 'BEZ'), ('direct', 'JJ'), ('.', '.')]
unseen_sent = brown_sents[4203]
print(bigram_tagger.tag(unseen_sent))
[('The', 'AT'), ('population', 'NN'), ('of', 'IN'), ('the', 'AT'), ('Congo', 'NP'), ('is', 'BEZ'), ('13.5', None), ('million', None), (',', None), ('divided', None), ('into', None), ('at', None), ('least', None), ('seven', None), ('major', None), ('``', None), ('culture', None), ('clusters', None), ("''", None), ('and', None), ('innumerable', None), ('tribes', None), ('speaking', None), ('400', None), ('separate', None), ('dialects', None), ('.', None)]

请注意,二元标注器能够标注训练中它看到过的句子中的所有词,但对一个没见过的句子表现很差。只要遇到一个新词(如13.5),就无法给它分配标记。它不能标注下面的词(如million),即使是在训练过程中看到过的,只是因为在训练过程中从来没有见过它前面有一个None标记的词。因此,标注器标注句子的其余部分也失败了。它的整体准确度得分非常低:

print(bigram_tagger.evaluate(test_sents))
0.10206319146815508

当n越大,上下文的特异性就会增加,我们要标注的数据中包含训练数据中不存在的上下文的几率也增大。这被称为数据稀疏问题,在NLP中是相当普遍的。因此,我们的研究结果的精度和覆盖范围之间需要有一个权衡(这与信息检索中的精度/召回权衡有关)。

小心!

N-gram标注器不应考虑跨越句子边界的上下文。因此,NLTK的标注器被设计用于句子列表,其中一个句子是一个词列表。在一个句子的开始,tn-1和前面的标记被设置为None。

5.4 组合标注器

解决精度和覆盖范围之间的权衡的一个办法是尽可能的使用更精确的算法,但却在很多时候落后于具有更广覆盖范围的算法。例如,我们可以按如下方式组合二元标注器、一元注器和一个默认标注器,如下:

1.尝试使用二元标注器标注标识符。
2.如果二元标注器无法找到一个标记,尝试一元标注器。
3.如果一元标注器也无法找到一个标记,使用默认标注器。

大多数NLTK标注器允许指定一个回退标注器。回退标注器自身可能也有一个回退标注器:

t0 = nltk.DefaultTagger("NN")
t1 = nltk.UnigramTagger(train_sents, backoff = t0)
t2 = nltk.BigramTagger(train_sents, backoff = t1)
print(t2.evaluate(test_sents))
0.8452108043456593

5.5 标注生词

我们标注生词的方法仍然是回退到一个正则表达式标注器或一个默认标注器。这些都无法利用上下文。因此,如果我们的标注器遇到词blog,训练过程中没有看到过,它会分配相同的标记,不论这个词出现的上下文是the blog还是to blog。我们怎样才能更好地处理这些生词,或词汇表以外的项目?

一个有用的基于上下文标注生词的方法是限制一个标注器的词汇表为最频繁的n 个词,使用3中的方法替代每个其他的词为一个特殊的词UNK。训练时,一个一元标注器可能会学到UNK通常是一个名词。然而,n-gram标注器会检测它的一些其他标记中的上下文。例如,如果前面的词是to(标注为TO),那么UNK可能会被标注为一个动词。

5.6 存储标注器

在大语料库上训练一个标注器可能需要大量的时间。没有必要在每次我们需要的时候训练一个标注器,很容易将一个训练好的标注器保存到一个文件以后重复使用。让我们保存我们的标注器t2到文件t2.pkl。

from pickle import dump
output = open('5.t2.pkl', 'wb')
dump(t2, output, -1)
output.close()

现在,我们可以在一个单独的Python进程中,我们可以载入保存的标注器。

from pickle import load
input = open("5.t2.pkl", "rb")
tagger = load(input)
input.close()

现在让我们检查它是否可以用来标注.

text = """The board's action shows what free enterprise
...     is up against in our complex maze of regulatory laws ."""
tokens = text.split()
print(tagger.tag(tokens))
[('The', 'AT'), ("board's", 'NN$'), ('action', 'NN'), ('shows', 'NNS'), ('what', 'WDT'), ('free', 'JJ'), ('enterprise', 'NN'), ('is', 'BEZ'), ('up', 'RP'), ('against', 'IN'), ('in', 'IN'), ('our', 'PP$'), ('complex', 'JJ'), ('maze', 'NN'), ('of', 'IN'), ('regulatory', 'NN'), ('laws', 'NNS'), ('.', '.')]

5.7 准确性的极限

一个n-gram标注器准确性的上限是什么?考虑一个三元标注器的情况。它遇到多少词性歧义的情况?我们可以根据经验决定这个问题的答案:

>>> cfd = nltk.ConditionalFreqDist(
...            ((x[1], y[1], z[0]), z[1])
...            for sent in brown_tagged_sents
...            for x, y, z in nltk.trigrams(sent))ambiguous_contexts = [c for c in cfd.conditions() if len(cfd[c]) > 1]
print(sum(cfd[c].N() for c in ambiguous_contexts) / cfd.N())
0.049297702068029296

因此,1/20的三元是有歧义的[示例]。给定当前单词及其前两个标记,根据训练数据,在5%的情况中,有一个以上的标记可能合理地分配给当前词。假设我们总是挑选在这种含糊不清的上下文中最有可能的标记,可以得出三元标注器准确性的一个下界。

调查标注器准确性的另一种方法是研究它的错误。有些标记可能会比别的更难分配,可能需要专门对这些数据进行预处理或后处理。一个方便的方式查看标注错误是混淆矩阵。它用图表表示期望的标记(黄金标准)与实际由标注器产生的标记:

test_tags = [tag for sent in brown.sents(categories  = "editorial") for (word, tag) in t2.tag(sent)]
gold_tags = [tag for (word, tag) in brown.tagged_words(categories='editorial')]
#print(nltk.ConfusionMatrix(gold_tags, test_tags))

基于这样的分析,我们可能会决定修改标记集。或许标记之间很难做出的区分可以被丢弃,因为它在一些较大的处理任务的上下文中并不重要。

分析标注器准确性界限的另一种方式来自人类标注者之间并非100%的意见一致。[更多]

一般情况下,标注过程会损坏区别:例如当所有的人称代词被标注为PRP时,词的特性通常会失去。与此同时,标注过程引入了新的区别从而去除了含糊之处:例如deal标注为VB或NN。这种消除某些区别并引入新的区别的特点是标注的一个重要的特征,有利于分类和预测。当我们引入一个标记集的更细的划分时,在n-gram标注器决定什么样的标记分配给一个特定的词时,可以获得关于左侧上下文的更详细的信息。然而,标注器同时也将需要做更多的工作来划分当前的词符,只是因为有更多可供选择的标记。相反,使用较少的区别(如简化的标记集),标注器有关上下文的信息会减少,为当前词符分类的选择范围也较小。

我们已经看到,训练数据中的歧义导致标注器准确性的上限。有时更多的上下文能解决这些歧义。然而,在其他情况下,如(Church, Young, & Bloothooft, 1996)中指出的,只有参考语法或现实世界的知识,才能解决歧义。尽管有这些缺陷,词性标注在用统计方法进行自然语言处理的兴起过程中起到了核心作用。1990年代初,统计标注器令人惊讶的精度是一个惊人的示范,可以不用更深的语言学知识解决一小部分语言理解问题,即词性消歧。这个想法能再推进吗?第7.中,我们将看到,它可以。

6 基于转换的标注

n-gram标注器的一个潜在的问题是它们的n-gram表(或语言模型)的大小。如果使用各种语言技术的标注器部署在移动计算设备上,在模型大小和标注器准确性之间取得平衡是很重要的。使用回退标注器的n-gram标注器可能存储trigram和bigram表,这是很大的稀疏阵列,可能有数亿条条目。

第二个问题是关于上下文。n-gram标注器从前面的上下文中获得的唯一的信息是标记,虽然词本身可能是一个有用的信息源。n-gram模型使用上下文中的词的其他特征为条件是不切实际的。在本节中,我们考察Brill标注,一种归纳标注方法,它的性能很好,使用的模型只有n-gram标注器的很小一部分。

Brill标注是一种基于转换的学习,以它的发明者命名。一般的想法很简单:猜每个词的标记,然后返回和修复错误。在这种方式中,Brill标注器陆续将一个不良标注的文本转换成一个更好的。与n-gram标注一样,这是有监督的学习方法,因为我们需要已标注的训练数据来评估标注器的猜测是否是一个错误。然而,不像n-gram标注,它不计数观察结果,只编制一个转换修正规则列表。

Brill标注的的过程通常是与绘画类比来解释的。假设我们要画一棵树,包括大树枝、树枝、小枝、叶子和一个统一的天蓝色背景的所有细节。不是先画树然后尝试在空白处画蓝色,而是简单的将整个画布画成蓝色,然后通过在蓝色背景上上色“修正”树的部分。以同样的方式,我们可能会画一个统一的褐色的树干再回过头来用更精细的刷子画进一步的细节。Brill标注使用了同样的想法:以大笔画开始,然后修复细节,一点点的细致的改变。让我们看看下面的例子:

>>> nltk.tag.brill.demo()
Training Brill tagger on 80 sentences...
Finding initial useful rules...Found 6555 useful rules.B      |S   F   r   O  |        Score = Fixed - Brokenc   i   o   t  |  R     Fixed = num tags changed incorrect -> correcto   x   k   h  |  u     Broken = num tags changed correct -> incorrectr   e   e   e  |  l     Other = num tags changed incorrect -> incorrecte   d   n   r  |  e
------------------+-------------------------------------------------------12  13   1   4  | NN -> VB if the tag of the preceding word is 'TO'8   9   1  23  | NN -> VBD if the tag of the following word is 'DT'8   8   0   9  | NN -> VBD if the tag of the preceding word is 'NNS'6   9   3  16  | NN -> NNP if the tag of words i-2...i-1 is '-NONE-'5   8   3   6  | NN -> NNP if the tag of the following word is 'NNP'5   6   1   0  | NN -> NNP if the text of words i-2...i-1 is 'like'5   5   0   3  | NN -> VBN if the text of the following word is '*-1'...
>>> print(open("errors.out").read())left context |    word/test->gold     | right context
--------------------------+------------------------+--------------------------|      Then/NN->RB       | ,/, in/IN the/DT guests/N
, in/IN the/DT guests/NNS |       '/VBD->POS       | honor/NN ,/, the/DT speed
'/POS honor/NN ,/, the/DT |    speedway/JJ->NN     | hauled/VBD out/RP four/CD
NN ,/, the/DT speedway/NN |     hauled/NN->VBD     | out/RP four/CD drivers/NN
DT speedway/NN hauled/VBD |      out/NNP->RP       | four/CD drivers/NNS ,/, c
dway/NN hauled/VBD out/RP |      four/NNP->CD      | drivers/NNS ,/, crews/NNS
hauled/VBD out/RP four/CD |    drivers/NNP->NNS    | ,/, crews/NNS and/CC even
P four/CD drivers/NNS ,/, |     crews/NN->NNS      | and/CC even/RB the/DT off
NNS and/CC even/RB the/DT |    official/NNP->JJ    | Indianapolis/NNP 500/CD a|     After/VBD->IN      | the/DT race/NN ,/, Fortun
ter/IN the/DT race/NN ,/, |    Fortune/IN->NNP     | 500/CD executives/NNS dro
s/NNS drooled/VBD like/IN |  schoolboys/NNP->NNS   | over/IN the/DT cars/NNS a
olboys/NNS over/IN the/DT |      cars/NN->NNS      | and/CC drivers/NNS ./.

7 如何确定一个词的分类

我们已经详细研究了词类,现在转向一个更基本的问题:我们如何首先决定一个词属于哪一类?在一般情况下,语言学家使用形态学、句法和语义线索确定一个词的类别。

7.1 形态学线索

一个词的内部结构可能为这个词分类提供有用的线索。举例来说:-ness是一个后缀,与形容词结合产生一个名词,如happy → happiness, ill → illness。如果我们遇到的一个以-ness结尾的词,很可能是一个名词。同样的,-ment是与一些动词结合产生一个名词的后缀,如govern → government和establish → establishment。

英语动词也可以是形态复杂的。例如,一个动词的现在分词以-ing结尾,表示正在进行的还没有结束的行动(如falling, eating)。-ing后缀也出现在从动词派生的名词中,如the falling of the leaves(这被称为动名词)。

7.2 句法线索

另一个信息来源是一个词可能出现的典型的上下文语境。例如,假设我们已经确定了名词类。那么我们可以说,英语形容词的句法标准是它可以立即出现在一个名词前,或紧跟在词be或very后。根据这些测试,near应该被归类为形容词:

Statement  User117 Dude..., I wanted some of that
ynQuestion User120 m I missing something?
Bye        User117 I'm gonna go fix food, I'll be back later.
System     User122 JOIN
System     User2   slaps User122 around a bit with a large trout.
Statement  User121 18/m pm me if u tryin to chat

【Python自然语言处理】读书笔记:第五章:分类和标注词汇相关推荐

  1. 《数据挖掘导论》Pangaea-Ning Tan 读书笔记 ----第五章 分类其他技术

    文章目录 第五章 分类:其他技术 5.1 基于规则的分类 5.1.1 基于规则的分类器的工作原理 5.1.2 规则的排序方案 5.2 最近邻算法(KNN) 无监督最近邻 KDTree和BallTree ...

  2. 《Python从入门到实践》读书笔记——第五章 if语句

    <Python从入门到实践>读书笔记--第五章 if语句 1. 一个简单示例 cars = ['audi', 'bwm', 'subaru', 'toyota']for car in ca ...

  3. 《Python自然语言处理(第二版)-Steven Bird等》学习笔记:第05章 分类和标注词汇

    第05章 分类和标注词汇 5.1 使用词性标注器 5.2 标注语料库 表示已标注的标识 读取已标注的语料库 简化的词性标记集 名词 动词 形容词和副词 未简化的标记 探索已标注的语料库 5.3 使用P ...

  4. 《深度探索C++对象模型》读书笔记第五章:构造析构拷贝语意学

    <深度探索C++对象模型>读书笔记第五章:构造析构拷贝语意学 对于abstract base class(抽象基类),class中的data member应该被初始化,并且只在constr ...

  5. python第三章上机实践_《机器学习Python实践》读书笔记-第三章

    <机器学习Python实践>,第三章,第一个机器学习项目 以往目录:橘猫吃不胖:<机器学习Python实践>读书笔记-第一章​zhuanlan.zhihu.com 书中介绍了一 ...

  6. 《Microsoft Sql server 2008 Internals》读书笔记--第五章Table(6)

      <Microsoft Sql server 2008 Internals>读书笔记订阅地址: http://www.cnblogs.com/downmoon/category/2303 ...

  7. 《Microsoft Sql server 2008 Internals》读书笔记--第五章Table(4)

    <Microsoft Sql server 2008 Internals>索引目录: <Microsoft Sql server 2008 Internal>读书笔记--目录索 ...

  8. 《用Python进行自然语言处理》第 5 章 分类和标注词汇

    1. 什么是词汇分类,在自然语言处理中它们是如何使用? 2. 一个好的存储词汇和它们的分类的 Python 数据结构是什么? 3. 我们如何自动标注文本中词汇的词类? 将词汇按它们的词性(parts- ...

  9. python基础语言与应用第五章_《Python基础教程》 读书笔记 第五章(下)循环语句...

    5.5.1while循环 x=1 while x<=100: print x x+=1 确保用户输入了名字: name="" while not name: name=raw ...

  10. python基础教程读书笔记_《Python基础教程》 读书笔记 第五章(下)循环语句

    导读热词 5.5.1while循环 x=1 while x<=100: print x x+=1 确保用户输入了名字: name="" while not name: nam ...

最新文章

  1. 配置hadoop集群一
  2. VTK:绘图之AreaPlot
  3. POJ 1932 XYZZY (差分约束+传递闭包)
  4. 汇编 --- 从磁盘(扇区2到18)上读取数据到内存中
  5. 74. Search a 2D Matrix
  6. 存储极客谈“SPC-1负载分析与AFA寿命评估”
  7. 关于ENVI-FLAASH大气校正报 .. bin\flaash\scriptfile.006系统找不到指定的路径的错误
  8. 一文介绍完整:python猴子补丁python monkey patch 没听过?
  9. Road Extraction by Deep Residual U-Net
  10. 302重定向:302 Moved Temporarily
  11. 最简单的搭建及使用本地服务器
  12. CGB2202API基础第1天
  13. Uva 10559 消除方块
  14. Windows下Zookeeper启动zkServer.cmd报错闪退,找不到zoo.cfg文件
  15. 华为的海外员工生活写实
  16. 构之以技术,付之以匠心——读《构建之法》有感
  17. oracle存储过程的赋值,ORACLE_存储过程_赋值
  18. 职场低情商就这三句话
  19. 辅助设计是用计算机进行绘图和过程设计,高中通用技术 技术与设计1《第三节 “计算机辅助制图”教学设计》...
  20. Error reading Prometheus: An error occurred within the plugin

热门文章

  1. 十二星座、超完美解析!
  2. PicGo+github搭建免费图床
  3. ubuntu 20 安装 velodyne_simulator
  4. yolov3-tiny原始weights模型转onnx模型并进行推理
  5. 微信小程序制作全流程(1)
  6. linux上centos镜像磁盘,VirtualBox中配置linuxCentOS的本地磁盘镜像iso作为其软件源
  7. Linux系统load average异常值处理的trick
  8. 阿里云储道深度解析存储系统设计——NVMe SSD性能影响因素一探究竟
  9. 质量保证QA与质量控制QC
  10. 如何激发孩子的想象力_如何激发孩子的创造力,想象力?