一、实验内容
使用NLTK和结巴分词完成词性标注。
二、实验步骤
1.将字符串使用由标识符和标记组成的元组来表示
Division/nn three/cd will/md be/be headed/vbn by/in the/at
Marines/nns-tl followed/vbn by/in 12/cd states/nns ;/. ;/.
division/nn four/cd will/md be/be headed/vbn by/in the/at Navy/nn-tl
,/, followed/vbn by/in 11/cd states/nns ;/. ;/.
division/nn five/cd ,/, by/in the/at Air/nn-tl Force/nn-tl
followed/vbn by/in 11/cd states/nns ./.
a='Division/nn three/cd will/md be/be headed/vbn by/in the/at Marines/nns-tl followed/vbn by/in 12/cd states/nns ;/. ;/. division/nn four/cd will/md be/be headed/vbn by/in the/at Navy/nn-tl ,/, followed/vbn by/in 11/cd states/nns ;/. ;/. division/nn five/cd ,/, by/in the/at Air/nn-tl Force/nn-tl followed/vbn by/in 11/cd states/nns ./.' # 由标识符和标记组成的列表 b = a.split(" ") b
['Division/nn', 'three/cd', 'will/md', 'be/be', 'headed/vbn', 'by/in', 'the/at', 'Marines/nns-tl', 'followed/vbn', 'by/in', '12/cd', 'states/nns', ';/.', ';/.', 'division/nn', 'four/cd', 'will/md', 'be/be', 'headed/vbn', 'by/in', 'the/at', 'Navy/nn-tl', ',/,', 'followed/vbn', 'by/in', '11/cd', 'states/nns', ';/.', ';/.', 'division/nn', 'five/cd', ',/,', 'by/in', 'the/at', 'Air/nn-tl', 'Force/nn-tl', 'followed/vbn', 'by/in', '11/cd', 'states/nns', './.']
# 由标识符和标记组成的元组 tuple(b) b
['Division/nn', 'three/cd', 'will/md', 'be/be', 'headed/vbn', 'by/in', 'the/at', 'Marines/nns-tl', 'followed/vbn', 'by/in', '12/cd', 'states/nns', ';/.', ';/.', 'division/nn', 'four/cd', 'will/md', 'be/be', 'headed/vbn', 'by/in', 'the/at', 'Navy/nn-tl', ',/,', 'followed/vbn', 'by/in', '11/cd', 'states/nns', ';/.', ';/.', 'division/nn', 'five/cd', ',/,', 'by/in', 'the/at', 'Air/nn-tl', 'Force/nn-tl', 'followed/vbn', 'by/in', '11/cd', 'states/nns', './.']
# 由标识符和标记组成的字典 dic = dict() for i in b: c = i.split("/") dic[c[0]] = c[1] dic
{'Division': 'nn', 'three': 'cd', 'will': 'md', 'be': 'be', 'headed': 'vbn', 'by': 'in', 'the': 'at', 'Marines': 'nns-tl', 'followed': 'vbn', '12': 'cd', 'states': 'nns', ';': '.', 'division': 'nn', 'four': 'cd', 'Navy': 'nn-tl', ',': ',', '11': 'cd', 'five': 'cd', 'Air': 'nn-tl', 'Force': 'nn-tl', '.': '.'}
书上的做法:
#直接从一个字符串构造一个已标注的标识符的链表。 #第一步是对字符串分词以 便能访问单独的词/标记字符串,然后将每一个转换成一个元组(使用 str2tuple()) import nltk 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/PP said/VBD ``/`` ARE/BER well/QL operated/VBN and/CC follow/VB generally/R accepted/VBN practices/NNS which/WDT inure/VB to/IN the/AT best/JJT interest/NN of/IN both/ABX governments/NNS ''/'' ./.''' # print(sent.split()[:5]) # print([nltk.tag.str2tuple(t) for t in sent.split()][:5]) print(sent.split()) 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/PP', 'said/VBD', '``/``', 'ARE/BER', 'well/QL', 'operated/VBN', 'and/CC', 'follow/VB', 'generally/R', 'accepted/VBN', 'practices/NNS', 'which/WDT', 'inure/VB', 'to/IN', 'the/AT', 'best/JJT', 'interest/NN', 'of/IN', 'both/ABX', 'governments/NNS', "''/''", './.'] [('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', 'PP'), ('said', 'VBD'), ('``', '``'), ('ARE', 'BER'), ('well', 'QL'), ('operated', 'VBN'), ('and', 'CC'), ('follow', 'VB'), ('generally', 'R'), ('accepted', 'VBN'), ('practices', 'NNS'), ('which', 'WDT'), ('inure', 'VB'), ('to', 'IN'), ('the', 'AT'), ('best', 'JJT'), ('interest', 'NN'), ('of', 'IN'), ('both', 'ABX'), ('governments', 'NNS'), ("''", "''"), ('.', '.')]
这个方法很妙。用来标注语料库,表示已标注的标识符。
原理:
#按照 NLTK 的约定,一个已标注的标识符使用一个由标识符和标记组成的元组来表示。 #我们可以使用函数 str2tuple()从表示一个已标注的标识符的标准字符串创建一个这样的特殊元组: tagged_token = nltk.tag.str2tuple('fly/NN') print(tagged_token) print(tagged_token[0]) print(tagged_token[1])
('fly', 'NN') fly NN
使用由标识符和标记组成的元组来表示。
import nltk #NLTK 中包括的若干语料库已标注了词性 #NLTK 中的语料库阅读器提供了一个统一的接口,使你不必理会这些不同的文件格式 #注意:部分词性标记已转换为大写的;自从布朗语料库发布以来,这已成为标准的做法。 print(nltk.corpus.brown.tagged_words())
[('The', 'AT'), ('Fulton', 'NP-TL'), ...]
print(nltk.corpus.brown.tagged_words(tagset = 'universal')) #与原书不一样
[('The', 'DET'), ('Fulton', 'NOUN'), ...]
2.读入经过标注布朗语料库,并将单词的词性映射到简化的标记集。
#只要语料库包含已标注的文本,NLTK的语料库接口都将有一个 tagged_words()方法 print(nltk.corpus.nps_chat.tagged_words()) print(nltk.corpus.conll2000.tagged_words()) print(nltk.corpus.treebank.tagged_words())
[('now', 'RB'), ('im', 'PRP'), ('left', 'VBD'), ...] [('Confidence', 'NN'), ('in', 'IN'), ('the', 'DT'), ...] [('Pierre', 'NNP'), ('Vinken', 'NNP'), (',', ','), ...]
#NLTK 中还有其他几种语言的已标注语料库,包括中文,印地语,葡萄牙语,西班牙语, 荷兰语和加泰罗尼亚语。 print(nltk.corpus.sinica_treebank.tagged_words()) print(nltk.corpus.indian.tagged_words()) print(nltk.corpus.mac_morpho.tagged_words()) print(nltk.corpus.conll2002.tagged_words()) print(nltk.corpus.cess_cat.tagged_words())
[('一', 'Neu'), ('友情', 'Nad'), ('嘉珍', 'Nba'), ...] [('মহিষের', 'NN'), ('সন্তান', 'NN'), (':', 'SYM'), ...] [('Jersei', 'N'), ('atinge', 'V'), ('média', 'N'), ...] [('Sao', 'NC'), ('Paulo', 'VMI'), ('(', 'Fpa'), ...] [('El', 'da0ms0'), ('Tribunal_Suprem', 'np0000o'), ...]
3.统计布朗新闻语料库中词性的搭配,统计最常出现在动词前的词性是哪些。
# 统计布朗新闻语料库中词性的搭配,统计最常出现的词性是哪些 from nltk.corpus import brown brown_news_tagged = brown.tagged_words(categories='news', tagset='universal') fdist = nltk.FreqDist(tag for (word, tag) in brown_news_tagged) print(fdist.items())
dict_items([('DET', 11389), ('NOUN', 30654), ('ADJ', 6706), ('VERB', 14399), ('ADP', 12355), ('.', 11928), ('ADV', 3349), ('CONJ', 2717), ('PRT', 2264), ('PRON', 2535), ('NUM', 2166), ('X', 92)])
print(fdist.most_common())
[('NOUN', 30654), ('VERB', 14399), ('ADP', 12355), ('.', 11928), ('DET', 11389), ('ADJ', 6706), ('ADV', 3349), ('CONJ', 2717), ('PRON', 2535), ('PRT', 2264), ('NUM', 2166), ('X', 92)]
print(list(fdist))
['NOUN', 'VERB', 'ADP', '.', 'DET', 'ADJ', 'ADV', 'CONJ', 'PRON', 'PRT', 'NUM', 'X']
tag = [tag for (tag, freq) in fdist.most_common()] print(tag)
['NOUN', 'VERB', 'ADP', '.', 'DET', 'ADJ', 'ADV', 'CONJ', 'PRON', 'PRT', 'NUM', 'X']
freq = [freq for (tag, freq) in fdist.most_common()] print(freq)
[30654, 14399, 12355, 11928, 11389, 6706, 3349, 2717, 2535, 2264, 2166, 92]
#使用 tag_fd.plot(cumulative=True)为上面显示的频率分布绘图 fdist.plot(cumulative=True)
<AxesSubplot:xlabel='Samples', ylabel='Cumulative Counts'>
#检查一些已标注的文本,看看哪些词类出现在一个名词前,频率最高的在最前面。 #首先,我们构建一个双连词链表,它的成员是它们自己的词-标记对, #例如:(('The', 'DET '),('Fulton', 'NOUN'))和(('Fulton', 'NOUN'),('County', 'NOUN'))。 #然后,我们构建了一个双连词的标记部分的 FreqDist。 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()])
#动词是用来描述事件和行动的词,在一个句子中,动词通常表示涉及一个或多个名词短语所指示物的关系。 #新闻文本中最常见的动词是什么?按频率排序所有动词 wsj = nltk.corpus.treebank.tagged_words(tagset='universal') word_tag_fd = nltk.FreqDist(wsj) print([(word,tag) for (word, tag) in word_tag_fd.most_common()][:5]) print([wt[0] + '/' + wt[1] for (wt, _) in word_tag_fd.most_common() if wt[1] == 'VERB'][:5])
[((',', '.'), 4885), (('the', 'DET'), 4038), (('.', '.'), 3828), (('of', 'ADP'), 2319), (('to', 'PRT'), 2161)] ['is/VERB', 'said/VERB', 'was/VERB', 'are/VERB', 'be/VERB']
#检查一些已标注的文本,看看哪些词类出现在一个名词前,频率最高的在最前面。 #首先,我们构建一个双连词链表,它的成员是它们自己的词-标记对, #例如:(('The', 'DET '),('Fulton', 'NOUN'))和(('Fulton', 'NOUN'),('County', 'NOUN'))。 #然后,我们构建了一个双连词的标记部分的 FreqDist。 word_tag_pairs = nltk.bigrams(brown_news_tagged) noun_preceders = [a[1] for (a, b) in word_tag_pairs if b[1] == 'VERB'] fdist = nltk.FreqDist(noun_preceders) print([tag for (tag, _) in fdist.most_common()])
['NOUN', 'VERB', 'PRON', 'PRT', '.', 'ADV', 'DET', 'CONJ', 'ADP', 'ADJ', 'NUM', 'X']
4. 自拟字符串,将所有单词词性标注为形容词。
# 自动标注 # 加载将要使用的数据 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')] fdist = nltk.FreqDist(tags) print([fdist])
[FreqDist({'NN': 13162, 'IN': 10616, 'AT': 8893, 'NP': 6866, ',': 5133, 'NNS': 5066, '.': 4452, 'JJ': 4392, 'CC': 2664, 'VBD': 2524, ...})]
#找出哪个标记是最多的 print(fdist.max())
NN
JJ adjective ‘big’ 形容词
# 自拟字符串,将所有单词词性标注为形容词。 # 创建一个将所有词都标注成 NN 的标注器 raw = 'It is interesting to learn English.Comics are easy to draw.' tokens = nltk.word_tokenize(raw) default_tagger = nltk.DefaultTagger('JJ') print(default_tagger.tag(tokens))
[('It', 'JJ'), ('is', 'JJ'), ('interesting', 'JJ'), ('to', 'JJ'), ('learn', 'JJ'), ('English.Comics', 'JJ'), ('are', 'JJ'), ('easy', 'JJ'), ('to', 'JJ'), ('draw', 'JJ'), ('.', 'JJ')]
#不出所料,这种方法的表现相当不好 #在一个典型的语料库中,它只标注正确了0.04的标识符 print(default_tagger.evaluate(brown_tagged_sents))
0.04367802374843368
#评估 #对比专家分配的标记来评估一个标注器的性能。 #由于通常很难获得专业和公正的人的判断,所以使用黄金标准测试数据来代替。这是一个已经手动标注并作为自动系统评估标准而被接受的语料库。 #当标注器对给定词猜测的标记与黄金标准标记相同,标注器被视为是正确的。
#一元标注器基于一个简单的统计算法:对每个标识符分配这个独特的标识符最有可能的 标记 #一个一元标 注器的行为就像一个查找标注器(5.4 节),除了有一个更方便的建立它的技术,称为训练。 #在下面的代码例子中,我们训练一个一元标注器,用它来标注一个句子,然后评估 from nltk.corpus import brown brown_tagged_sents = brown.tagged_sents(categories='news') print(brown_tagged_sents[0][:5])
[('The', 'AT'), ('Fulton', 'NP-TL'), ('County', 'NN-TL'), ('Grand', 'JJ-TL'), ('Jury', 'NN-TL')]
brown_sents = brown.sents(categories='news') print(brown_sents[0][:5])
['The', 'Fulton', 'County', 'Grand', 'Jury']
#训练一个 UnigramTagger,通过在我们初始化标注器时指定已标注的句子数据作为参数 #训练过程中涉及检查每个词的标记,将所有词的最可能的标记存储在一个字典里面,这个字典存储在标注器内部。 unigram_tagger = nltk.UnigramTagger(brown_tagged_sents) print(unigram_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', 'QL'), ('that', 'CS'), ('entrance', 'NN'), ('is', 'BEZ'), ('direct', 'JJ'), ('.', '.')]
#评估 print(unigram_tagger.evaluate(brown_tagged_sents))
0.9349006503968017
5. 将布朗语料库中的数据分割为60%的训练数据和40%的测试数据。
#分割数据,60%为测试数据,其余 40%为测试数据 size = int(len(brown_tagged_sents) * 0.6) 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))
2773 0.7814155385520895
6. 利用2-gram模型,将上一题中的训练数据作为样本对测试数据进行词性标注。
#一个 n-gram 标注器是一个 unigram 标注器的一般化,它的上下文是当前词和它前面n- 1个标识符的词性标记 # 1-gram 一元标注器(unigram tagger)用于标注一个标识符的上下文的 只是标识符本身。 # 2-gram 二元标注器 (bigram taggers),3-gram 标注器也称为三元标注器(trigram taggers)。 #NgramTagger类使用一个已标注的训练语料库来确定对每个上下文哪个词性标记最有可能 bigram_tagger = nltk.BigramTagger(train_sents) print(bigram_tagger.tag(brown_sents[2007])[:5])
[('Various', 'JJ'), ('of', 'IN'), ('the', 'AT'), ('apartments', 'NNS'), ('are', 'BER')]
print(bigram_tagger.evaluate(test_sents)) #当 n 越大,上下文的特异性就会增加,我们要标注的数据中包含训练数据中不存在的上下文的几率也增大。 #这被称为数据稀疏问题,在 NLP 中是相当普遍的。 #因此,我们的研究结果的精度和覆盖范围之间需要有一个权衡(这与信息检索中的精度/召回权衡有关)。 #N-gram 标注器不应考虑跨越句子边界的上下文。 #因此,NLTK的标注器被设计用于句子链表,一个句子是一个词链表。 #在一个句子的开始,tn-1 和前面的标记被设置为 None。
0.08365214832254267
啊精度好低,试试一元和三元。
unigram_tagger = nltk.UnigramTagger(train_sents) print(unigram_tagger.tag(brown_sents[2007])[:5]) print(unigram_tagger.evaluate(test_sents))
[('Various', 'JJ'), ('of', 'IN'), ('the', 'AT'), ('apartments', 'NNS'), ('are', 'BER')] 0.7814155385520895
trigram_tagger = nltk.TrigramTagger(train_sents) print(trigram_tagger.tag(brown_sents[2007])[:5]) print(trigram_tagger.evaluate(test_sents))
[('Various', 'JJ'), ('of', 'IN'), ('the', 'AT'), ('apartments', 'NNS'), ('are', 'BER')] 0.05353639395722974
一元高一些,三元更低了
完善
一、实验内容
本实验旨在使用自然语言处理工具NLTK进行词性标注。词性标注是将文本中的每个单词与其在语法结构中的词性相对应的过程。
二、实验步骤
步骤一:将字符串使用由标识符和标记组成的元组来表示
NLTK中提供了标记化函数,可以将字符串标记为标识符和标记的元组。
import nltk text = "This is a sample sentence for tagging." tokens = nltk.word_tokenize(text) tags = nltk.pos_tag(tokens) print(tags)
步骤二:读入经过标注布朗语料库,并将单词的词性映射到简化的标记集
from nltk.corpus import brown from nltk.tag import simplify_tag # 读取布朗语料库并进行词性标注 tagged_words = brown.tagged_words(categories='news') # 将词性标签映射到简化的标记集 simplified_tags = [(word, simplify_tag(tag)) for word, tag in tagged_words] print(simplified_tags)
步骤三:统计布朗新闻语料库中词性的搭配,统计最常出现在动词前的词性是哪些
from collections import Counter # 提取动词前的词性 verb_preceding_tags = [simplified_tags[i-1][1] for i, (word, tag) in enumerate(simplified_tags) if 'VB' in tag] # 统计词性搭配 tag_combinations = list(zip(verb_preceding_tags, [tag for (word, tag) in simplified_tags if 'VB' in tag])) tag_counts = Counter(tag_combinations) # 最常出现在动词前的词性 most_common_tags = tag_counts.most_common(5) print(most_common_tags)
步骤四:自拟字符串,将所有单词词性标注为形容词
text = "The quick brown fox jumps over the lazy dog" tokens = nltk.word_tokenize(text) adjective_tags = [('adjective', 'JJ') for _ in tokens] print(adjective_tags)
步骤五:将布朗语料库中的数据分割为60%的训练数据和40%的测试数据
from nltk.corpus import brown from nltk.corpus import conll2000 from nltk.corpus import treebank from nltk.tag.util import untag # 选择布朗语料库作为示例 data = brown.tagged_sents(categories='news') # 按比例划分训练和测试数据 train_size = int(0.6 * len(data)) train_data = data[:train_size] test_data = data[train_size:]
步骤六:利用2-gram模型,将上一题中的训练数据作为样本对测试数据进行词性标注
from nltk import BigramTagger # 使用2-gram模型进行词性标注 bigram_tagger = BigramTagger(train_data) # 对测试数据进行词性标注 test_tagged = bigram_tagger.tag_sents([untag(sent) for sent in test_data]) print(test_tagged)
这些步骤将帮助你了解如何使用NLTK进行词性标注,分析语料库中的数据,并训练基本的2-gram模型进行标注。词性标注对自然语言处理任务非常重要,例如实体识别、句法分析和文本分类等。