之前关于k-近邻和决策树的讲解没有一步一步具体的说明每个函数的实现功能和测试样例,让不是学习本书的人看起来一片代码一头雾水,这里开始,会仔细的讲解每个函数实现的功能和其对应的测试样例,之前的也会抽空修改。
跟概率的作用一样,如果A事件发生的概率大于B时间发生的概率那么我们的“决策机构”就选择A事件,反则选B。我们首先得有铺垫的知识点——条件概率:就是事件A在另外一个事件B已经发生条件下的发生概率。条件概率表示为P(A|B),读作“在B条件下A的概率”,其计算公式为:P(A|B) = P(AB)/P(B),证明及学习可自行搜索。
贝叶斯准则告诉我们如何交换条件概率中的条件与结果,如已知P(x|c),求P(c|x):,也就是说我们可以在已知量中计算未知量。在这里P(c1|x,y)和P(c2|x,y)的含义是:给定有x,y两个特征表示的数据,其属于c1的概率和属于c2的概率,那么,如果P(c1|x,y)大于P(c2|x,y),它就属于c1,反之属于c2。
根据上面的公式P(c|x) = (P(x|c)*P(c)) / P(x),我们知道P(c1|x) = (P(x|c1)*P(c1)) / P(x)和P(c2|x) = (P(x|c2)*P(c2)) / P(x),也就是在x特征下我们比较P(c1|x)和P(c2|x)的大小,由于这两个概率值都除了相同的值P(x),所以我们比较的时候除不除都无所谓,而且不除的话我们就少求一个已知量。所以比较P(c1|x)和P(c2|x)也就变成了比较(P(x|c1)*P(c1))和(P(x|c2)*P(c2)) 。
这里没听懂?没关系,通过下面的例子就很容易理解上面所说的什么P什么c1、c2、x之类的了。
例:假设我们有一个留言板,要其动判断某个用户的某个留言是否是属于侮辱性的,那么如何做呢?我们要训练一下机器,让它自己判断,不过这种判断是有误差的,而且在我们这个简单的例子中误差还是挺大的。我们根据单词来分类,比如“stupid”是属于侮辱性的,而“like”则不是,在这里我们考虑每个单词都是相互独立的,也就是说某个单词出现于否与其它单词无关联,这也就是我们这个朴素贝叶斯的“朴素”的意思,只考虑最简单的条件。实际上,某些单词与其它单词关联挺大的,举个例子,to这个单词跟want或者其它的be等词关联很大,往往出现在它们的后面,或者它也常常出现在动词前面。同时,书本上说朴素贝叶斯的另一个特征是每个次都同等重要,这明显是不符合实际的,但目前我们就将其视为同等重要来考虑,学习其基本概念。
这里我使用python3.6运行,后面打开文件的时候有地方编码方式没搞明白python3.6笔者无法解决,届时会改为2.7运行。注意2.7使用中文要在代码最前面加#encoding=utf-8
我们先创建一个bayes.py,存入下面代码:
# encoding=utf-8
from numpy import *
"""
创建两个列表, 一个是包含多句话的列表, 另一个则对应每句话的标签, 1是侮辱性的
我们可以理解为把一篇文章分总了6句话
"""
def loadDataSet():
postingList = [['my', 'dog', 'has', 'flea', 'problems', 'help', 'please'],
['maybe', 'not', 'take', 'him', 'to', 'dog', 'park', 'stupid'],
['my', 'dalmation', 'is', 'so', 'cute', 'I', 'love', 'him'],
['stop', 'posting', 'stupid', 'worthless', 'garbage'],
['mr', 'licks', 'ate', 'my', 'steak', 'how', 'to', 'stop', 'him'],
['quit', 'buying', 'worthless', 'dog', 'food', 'stupid']]
classVec = [0, 1, 0, 1, 0, 1]
return postingList, classVec
"""这个集合返回的是刚才6句话中所有的单词, 重复多次出现的只统计一次, 也就是统计所有出现的单词"""
def createVocabList(dataSet):
vacabSet = set([])
for document in dataSet:
vacabSet = vacabSet | set(document)
return vacabSet
"""
首先创建一个元素个数和刚才createVocabList函数得到的集合一样的列表, 全部初始化为0也就是没有出现过
对于输入inputSet中每个单词, 如果其在刚才的单词集合中则+1
"""
def setOfWords2Vec(vacabList, inputSet):
returnVec = [0] * len(vacabList)
for word in inputSet:
if word in vacabList:
"""此处要转换为list, set没有index这个属性, pyhton3不转换list提示错误"""
returnVec[list(vacabList).index(word)] += 1
else:
print("the word %s is not in my Vocabulary!" % word)
return returnVec
我们新建一个test.py
#encoding=utf-8
import bayes
listOfPosts, listClasses = bayes.loadDataSet()
print(listOfPosts)
print(listClasses)
"""
答案如下:
[['my', 'dog', 'has', 'flea', 'problems', 'help', 'please'],
['maybe', 'not', 'take', 'him', 'to', 'dog', 'park', 'stupid'],
['my', 'dalmation', 'is', 'so', 'cute', 'I', 'love', 'him'],
['stop', 'posting', 'stupid', 'worthless', 'garbage'],
['mr', 'licks', 'ate', 'my', 'steak', 'how', 'to', 'stop', 'him'],
['quit', 'buying', 'worthless', 'dog', 'food', 'stupid']]
[0, 1, 0, 1, 0, 1]
"""
myVocabList = bayes.createVocabList(listOfPosts)
print(myVocabList)
"""
答案如下:
set(['cute', 'love', 'help', 'garbage', 'quit', 'I', 'problems', 'is', 'park', 'stop', 'flea', 'dalmation', 'licks', 'food', 'not', 'him', 'buying', 'posting', 'has', 'worthless', 'ate', 'to', 'maybe', 'please', 'dog', 'how', 'stupid', 'so', 'take', 'mr', 'steak', 'my'])
"""
returnVec = bayes.setOfWords2Vec(myVocabList, listOfPosts[0])
print(returnVec)
"""
答案如下:
[0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1]
"""
接下来就是训练算法:从词向量计算概率
计算每个类别中的文档数目
对每个类别:
如果词条(单词)出现在文档中->增加该词条(单词)的计数值
增加所有词条(单词)的计数值
对每个类别:
对每个词条(单词):
将该词条(单词)的数目除以总词(单词)条数得到条件概率
返回每个类别的条件概率
很难理解上面的伪代码?我们来看看下面的函数(该函数在我们创建的bayes.py中)
def trainNB0(trainMatrix, trainCategory):
"""获取总的文档数目numTrainDocs"""
numTrainDocs = len(trainMatrix)
"""获取每个文档的词条(单词)的数目"""
numWords = len(trainMatrix[0])
"""
由于trainCategory中的分类要不就是1要不就是0, 所以sum(trainCategory)就是所有类别为1的数目
要转换为浮点数, 否则计算会有误差出错
"""
pAbusive = float(sum(trainCategory)) / numTrainDocs
"""
创建两个数量跟每个文档单词数量相同的向量, 全部初始化为1, 为什么初始化为1下面解释
其实应该理解为
p0num = zeros(numWords)
p1num = zeros(numWords)
一开始应该初始化为0, 但我们初始化为1
"""
p0num = ones(numWords)
p1num = ones(numWords)
"""
总数也初始化为2
其实这里也应该是
p0Denom = 0.0
p1Denom = 0.0
"""
p0Denom = 2.0
p1Denom = 2.0
"""
下面的if和else就是:
对每个类别:
如果词条(单词)出现在文档中->增加该词条(单词)的计数值
增加所有词条(单词)的计数值
"""
for i in range(numTrainDocs):
if trainCategory[i] == 1:
p1num += trainMatrix[i]
p1Denom += sum(trainMatrix[i])
else:
p0num += trainMatrix[i]
p0Denom += sum(trainMatrix[i])
"""
下面return就是:
对每个类别:
对每个词条(单词):
将该词条(单词)的数目除以总词(单词)条数得到条件概率
返回每个类别的条件概率
"""
return log(p0num / p0Denom), log(p1num / p1Denom), pAbusive
最后 p0num/p0Denom 和 p1num/p1Denom 应该是整个文档出现的单词中每个单词属于0(非侮辱性)和1(侮辱性)的概率,但是我们利用贝叶斯分类器对文档进行分类时,要计算多个概率的乘积以获得文档属于某个类别的概率,根