前面标注器有基于默认标注器,正则表达式标注器以及查询标注器等等。在本章分类的基础上构建一个单词分类器。借助于上下文语境,这也可以是单词词性的一个特征。例如:标注词fly,如果知道它前面的词是“a”将使我们能够确定它是一个名词,而不是一个动词。
- 特征提取:正则表达式标注器主要是识别单词的后缀,这也可以作为单词词性的特征,特征就需要提取单词的后几个字母,以及在句子中的前一个单词。
- 数据:(特征集,词性)构成个链表,只要能实现这个形式的就行具体也没有特殊的要求。
直接上代码吧。
from nltk.corpus import brown
import nltk
#特征提取函数
def pos_feature(sent,i):
'''
:param sent: 由多个单词元组(单词,词性)组成的list链表
:param i: 获取第i个单词的词性特征
:return: 特征集
'''
features = {
"suffix(1)":sent[i][0][-1:],
"suffix(2)":sent[i][0][-2:],
"suffix(3)":sent[i][0][-3:]
}
if i == 0:
features["pre_word"] = '<START>'
else:
features["pre_word"] = sent[i-1][0]
return features
#获取每个单词的词性特征集,以及词性构成列表。
# 每个句子是由多个单词元组(单词,词性)组成的list链表
feature_words = [
(pos_feature(sent,i),sent[i][1])
#获取所有句子
for sent in brown.tagged_sents(categories="news")
#计算句子中的单词个数,逐个遍历
for i in range(len(sent))
]
#此语句是我用于查找错误的
print(feature_words[1])
#划分数据
train_set,test_set = feature_words[1000:],feature_words[:1000]
#词性分类器
classfier = nltk.NaiveBayesClassifier.train(train_set)
#分类books这个单词
print(classfier.classify(pos_feature(["books"],0)))
#评估分类器准确度
print(nltk.classify.accuracy(classfier,test_set))
运行结果
序列分类
序列分类模型用来为一个句子中的所有词共同选择词性标签。其中一种序列分类器称为连续分类或贪婪序列分类,是为第一个找到最有可能的标签,然后根据第一个的标签帮助找到下一个的最佳标签,如此重复,直到所有输入都贴上标签为止。体现在模型上我个人感觉就是训练数据不一样吧,其他的貌似没有什么区别。下面是序列分类器(连续分类器或者贪婪分类器)书上提供的代码。
import nltk
#特征提取函数
def pos_feature(sentence,i,history):
#特征集首先是提取单词后缀
features = {
"suffix(1)":sentence[i][-1:],
"suffix(2)":sentence[i][-2:],
"suffix(3)":sentence[i][-3:]
}
#提取单词的前一个单词和标签作为特征
if i == 0:
features["pre-word"] = "<start>"
features["pre-tag"] = "<start>"
else:
features["pre-word"] = sentence[i-1]
features["pre-tag"] = history[i-1]
return features
#连续词性标注器(实际是分类)
class ConsecutivePosTagger(nltk.TaggerI):
def __init__(self,train_sents):
#下面这个训练集的获取不是很明白为啥一会去标签不去标签的,
#直接在tagged_sents里面直接就包含了几乎所有需要的信息了呀
train_set = []
for tagged_sent in train_sents:
untagged_sent = nltk.tag.untag(tagged_sent)
history = []
for i ,(word,tag) in enumerate(tagged_sent):
featureset = pos_feature(untagged_sent,i,history)
train_set.append((featureset,tag))
history.append(tag)
#输出一条训练数据查看
print("第一条训练数据:",train_set[1])
#训练模型
self.classifier = nltk.NaiveBayesClassifier.train(train_set)
#该类下的标注方法
def tag(self,sentence):
history = []
for i,word in enumerate(sentence):
featureset = pos_feature(sentence,i,history)
tag = self.classifier.classify(featureset)
history.append(tag)
return zip(sentence,history)
tagged_sents = nltk.corpus.brown.tagged_sents(categories='news')
size = int(len(tagged_sents) * 0.1)
train_sents, test_sents = tagged_sents[size:], tagged_sents[:size]
#创建该类的对象
tagger = ConsecutivePosTagger(train_sents)
print(tagger.evaluate(test_sents))
写完这个代码的时候发现了序列分类,参照前一个输入的词性标注当前单词词性体现在哪里。体现在该类的标注方法tag()里面,里面的特征提取也是和训练数据的特征一样。介于朴素贝叶斯分类模型没有学也不好解释。
其他序列分类方法
上面这种方法的缺点是只参考前一个单词,而且一旦确定了单词的标签无法更改。这个问题需要采用转型策略。另一种方案是为所有可能的序列打分,选择总得分最高的序列。隐马尔可夫模型就是采用这种方法。
网友评论