使用NLTK的朴素贝叶斯分类器来训练并完成分类工作
来源:互联网 发布:炭知天下价目表 编辑:程序博客网 时间:2024/06/06 03:15
NLTK是Python的一个自然语言处理的模块,其中实现了朴素贝叶斯分类算法。以下,就使用上一篇文中提到的数据,来应用这个模块实现朴素贝叶斯分类。NLTK的实现更加泛化,所以在应用到我们的数据上时需要做一点的转化。
首先来看一下NLTK官方文档中给出的一个简单明了的例子,在了解这个例子之后,再设法将同样的模型应用到自己的数据集上。官方给出的例子是英文名中,在知道名字中最后一个字母后,判断这个名字对应的人是男是女。
#coding=utf-8import random, nltkfrom nltk.corpus import namesdef gender_features(word): '''提取每个单词的最后一个字母作为特征''' return {'last_letter': word[-1]}# 先为原始数据打好标签labeled_names = ([(name, 'male') for name in names.words('male.txt')] + [(name, 'female') for name in names.words('female.txt')])# 随机打乱打好标签的数据集的顺序,random.shuffle(labeled_names)# 从原始数据中提取特征(名字的最后一个字母, 参见gender_features的实现)featuresets = [(gender_features(name), gender) for (name, gender) in labeled_names]# 将特征集划分成训练集和测试集train_set, test_set = featuresets[500:], featuresets[:500]# 使用训练集训练模型(核心就是求出各种后验概率)classifier = nltk.NaiveBayesClassifier.train(train_set)# 通过测试集来估计分类器的准确性print(nltk.classify.accuracy(classifier, test_set))# 如果一个人的名字的最后一个字母是‘a’,那么这个人是男还是女print(classifier.classify({'last_letter': 'a'}))# 找出最能够区分分类的特征值classifier.show_most_informative_features(5)
0.754femaleMost Informative Features last_letter = u'a' female : male = 35.6 : 1.0 last_letter = u'k' male : female = 30.7 : 1.0 last_letter = u'f' male : female = 16.6 : 1.0 last_letter = u'p' male : female = 12.5 : 1.0 last_letter = u'm' male : female = 11.1 : 1.0从结果中,我们可以看到,通过训练集训练出的模型,在应用到测试集上时,其准确率为75%;如果一个人的名字以字母‘a’结束,那么此分类器将其划分为女性;最后输出了最能区分男女的5个属性值的数据,比如,对于字母‘a’来说,它作为女性名的最后一个字母的可能性是男性的35倍。
可以看到NLTK的朴素贝叶斯实现之中,它的输入的训练集的输入是类似于以下的形式:
[
({'attr1':val1, 'attr2': val2, 'attr3': val3 ... 'attrn': valn}, label1),
({'attr1':val1, 'attr2': val2, 'attr3': val3 ... 'attrn': valn}, label2),
......
]
其中,每个特征对应一个标签,在以上的官方的例子中,特征就只有一个,last_letter;而特征的可能值是26个字母。对应到自己的数据,对应一个用户就不止有一个特征了,而是用户安装的APP名称列表,同时又由于每个用户安装的APP可能不同,所以不同的用户所对应的特征的长度也是可能不同的;而每个属性(APP名称)对应的值只有两个:安装或者没安装。
以下的代码中的注释以及输出说明了整个转化过程(使用了上篇文章中第三步中生成的数据):
#!/usr/local/bin/python2.7# encoding: utf-8from collections import defaultdictimport nltkdef gender_features(appnamelist): features = defaultdict(bool) for appname in appnamelist: features[appname] = True return featuresif __name__ == '__main__': raw_data = defaultdict(lambda: defaultdict(list)) with open('data/genderapplist.log') as f: for line in f: cells = line.strip().split('\t') if len(cells) == 3: imei, gender, appname = cells gender = 'male' if gender == '男性应用' else 'female' raw_data[gender][imei].append(appname) labeled_applist = [(appnamelist, 'male') for appnamelist in raw_data['male'].values()] + [(appnamelist, 'female') for appnamelist in raw_data['female'].values()] featuresets = [(gender_features(appnamelist), gender) for appnamelist, gender in labeled_applist] train_set, test_set = featuresets[500:], featuresets[:500] classifier = nltk.NaiveBayesClassifier.train(train_set) # 在训练生成的分类器classifier中,有两个属性存储着贝叶斯分类器所需要的先验和后验概率: # _label_probdist 保存了标签的分布 # _feature_probdist 保存了每个APPNAME对应的后验分布 # 通过下面的代码我们可以看到它们的值 print '以下是 _label_probdist的相关信息' print '1. 类型' print type(classifier._label_probdist) print '2. 标签的整体分布状况' classifier._label_probdist.freqdist().tabulate() print '3. 由第二步推出的标签的概率分布' print classifier._label_probdist.prob('female'), classifier._label_probdist.prob('male') print '*' * 32 # _feature_probdist的值 print '以下是 _feature_probdist的相关信息' print '1. 类型' print type(classifier._feature_probdist) print '2. 从1的输出中可以看到其类型为dict,我们看它的一个key和value即可' print classifier._feature_probdist.items()[6302] print '3. 从2中可以看到,其代表了,在标签为female的情况下,安装了支付宝钱包这个应用的概率分布' classifier._feature_probdist.items()[6302][1].freqdist().tabulate() print '4. 3的输出,我们非常熟悉,也就是在所有4910个female用户中,有77个安装了支付宝钱包,没有安装的有4833个' print '有了这个分布,我们就可以计算出P(True|female, 支付宝钱包),其意义就是,在female用户中,支付宝钱包这个属性为True的可能性为' print classifier._feature_probdist.items()[6302][1].prob(True) print '5. 然后你会发现4中输出的P(True|female, 支付宝钱包)并不正好等于77./4910,这是因为使用ELEProbDist' print '也就是“期望相似性概率估计”,这种方法避免了P(True|female, 支付宝钱包)=0情况的出现,从而避免模型失效' print '6. 通过在训练集上的训练,我们得到了以上的概率分布,然后就可以使用训练好的模型来分类了,我们看一下安装了蘑菇街和支付宝钱包的用户是男还是女' print classifier.classify({'蘑菇街':True, '支付宝钱包': True}) print '7. 让我们看一下安传过了蘑菇街和支付宝钱包的用户男女的可能性' print 'Prob(female)', classifier.prob_classify({'蘑菇街':True, '支付宝钱包': True}).prob('female') print 'Prob(male)', classifier.prob_classify({'蘑菇街':True, '支付宝钱包': True}).prob('male') print '8. 如果我们的输入中,有一个全新的应用“这个应用不存在”,这里的处理是不处理它' print 'Prob(female)', classifier.prob_classify({'蘑菇街':True, '支付宝钱包': True, '这个应用不存在':True}).prob('female') print 'Prob(male)', classifier.prob_classify({'蘑菇街':True, '支付宝钱包': True, '这个应用不存在':True}).prob('male')
以上程序的输出为:
以下是 _label_probdist的相关信息1. 类型<class 'nltk.probability.ELEProbDist'>2. 标签的整体分布状况female male 4910 4420 3. 由第二步推出的标签的概率分布0.526256564141 0.473743435859********************************以下是 _feature_probdist的相关信息1. 类型<type 'dict'>2. 从1的输出中可以看到其类型为dict,我们看它的一个key和value即可(('female', '\xe6\x94\xaf\xe4\xbb\x98\xe5\xae\x9d\xe9\x92\xb1\xe5\x8c\x85'), <ELEProbDist based on 4910 samples>)3. 从2中可以看到,其代表了,在标签为female的情况下,安装了支付宝钱包这个应用的概率分布None True 4833 77 4. 3的输出,我们非常熟悉,也就是在所有4910个female用户中,有77个安装了支付宝钱包,没有安装的有4833个有了这个分布,我们就可以计算出P(True|female, 支付宝钱包),其意义就是,在female用户中,支付宝钱包这个属性为True的可能性为0.01578090002045. 然后你会发现4中输出的P(True|female, 支付宝钱包)并不正好等于77./4910,这是因为使用ELEProbDist也就是“期望相似性概率估计”,这种方法避免了P(True|female, 支付宝钱包)=0情况的出现,从而避免模型失效6. 通过在训练集上的训练,我们得到了以上的概率分布,然后就可以使用训练好的模型来分类了,我们看一下安装了蘑菇街和支付宝钱包的用户是男还是女female7. 让我们看一下安传过了蘑菇街和支付宝钱包的用户男女的可能性Prob(female) 0.994878529146Prob(male) 0.005121470853578. 如果我们的输入中,有一个全新的应用“这个应用不存在”,这里的处理是不处理它Prob(female) 0.994878529146Prob(male) 0.00512147085357
这样通过使用NLTK,相比自己实现来说有了更简洁的代码,并且更容易维护,希望对有需要的同学有帮助。
2 0
- 使用NLTK的朴素贝叶斯分类器来训练并完成分类工作
- NLTK之朴素贝叶斯分类器
- 使用R完成朴素贝叶斯分类
- 分类-朴素的贝叶斯
- 朴素贝叶斯分类器
- 朴素贝叶斯分类器
- 朴素贝叶斯分类器
- 朴素贝叶斯分类器
- 朴素贝叶斯分类器
- 朴素贝叶斯分类器
- 朴素贝叶斯分类器
- 朴素贝叶斯分类器
- 朴素贝叶斯分类器
- 朴素贝叶斯分类器
- 朴素贝叶斯分类器
- 朴素贝叶斯分类器
- 朴素贝叶斯分类器
- 朴素贝叶斯分类器
- 发送带附件的邮件
- OSGI中引用外部包的几种方法
- 《Linux设备驱动开发详解(第3版)》(即《Linux设备驱动开发详解:基于最新的Linux 4.0内核》)网购链接
- 知识点三:PreferenceFragment的用法
- [最短路]使用优先队列优化的Dijkstra算法
- 使用NLTK的朴素贝叶斯分类器来训练并完成分类工作
- Android 内存优化总结
- android ListView奇偶行显示不同颜色
- hdu-2087 剪花布条
- mongodb pre-splitting sharding测试
- 知识点四:Menu和actionBar用法
- 自己动手实现数据结构——排序算法1(冒泡、插入、归并、简单选择)(C++实现)
- C++与Java的语法区别
- Linux内核启动中显示的logo的修改