贝叶斯分类算法是统计学的一种概率分类方法,朴素贝叶斯(Naive Bayesian)分类是贝叶斯分类中最简单的一种。其分类原理就是利用贝叶斯公式根据某特征的先验概率计算出其后验概率,然后选择具有最大后验概率的类作为该特征所属的类。之所以称之为”朴素”,是因为贝叶斯分类只做最原始、最简单的假设:所有的特征之间是统计独立的。
优点:
缺点:
条件概率指在已知事件B发生的情况下,事件A发生的概率,用 P ( A ∣ B ) 来表示。
根据上图可知:在已知事件B发生的情况下,事件A发生的概率就是 P ( A ∩ B ) 除以 P ( B ) 。
对于全概率公式,如果事件 { A 1 , A 2 , … , A n }构成一个完备事件且都有正概率,那么对于任意一个事件B则有:P(B)=P(BA 1 )+P(BA 2 )+…+P(BA n)=P(B∣A 1)P(A 1 )+P(B∣A 2)P(A 2 )+…+P(B∣A n )P(A n)
则有
根据条件概率和全概率公式,可以得到贝叶斯公式如下
P(A) 称为先验概率(prior probability),即在B事件发生之前,我们对A事件概率的一个判断;
P ( A ∣ B ) 称为后验概率(posterior probability),即在B事件发生之后,我们对A事件概率的重新评估;
P ( B ∣ A ) /P(B) 称为可能性函数(Likely hood),这是一个调整因子,使得预估概率更接近真实概率。
所以条件概率可以理解为:后验概率 = 先验概率 × 调整因子
如果"可能性函数">1,意味着"先验概率"被增强,事件A的发生的可能性变大;
如果"可能性函数"=1,意味着B事件无助于判断事件A的可能性;
如果"可能性函数"<1,意味着"先验概率"被削弱,事件A的可能性变小。
下表收集了15天内某网球爱好者每天是否打网球的数据,其中从左往右每列的属性分别为:天数、天气情况、温度、湿度、风力、当天是否打网球。
现在假设有一个样例 x = {Sunny, Hot, High, Weak},那该种情况下应该去打网球还是不去呢,如何对其进行分类呢?
首先分别统计对15天中去打排球和没去打排球的天数以及各自情况下不同属性分类的的个数,如下图:
上图中的P©表示15天中打网球的概率,|Dc,Xi|表示去打网球的情况下各个属性的不同取值的个数,P(Xi|c)表示表示去打网球的情况下各个属性的不同取值的个数占总属性取值个数的比例。例如,9天去打网球的情况下天气为下雨的概率为 1 3 \frac {1} {3} 31
根据上述方法,统计出是否打网球的情况下各个属性取值的占比,如下图:
此时,根据贝叶斯公式,分别求解去打网球和不去打网球的概率,求解公式如下:
P ( y e s ∣ x ) = P ( x ∣ y e s ) P ( y e s ) P ( x ) P(yes∣x)=\frac{P(x∣yes)P(yes)}{P(x)} P(yes∣x)=P(x)P(x∣yes)P(yes)
P ( n o ∣ x ) = P ( x ∣ n o ) P ( n o ) P ( x ) P(no∣x)=\frac{P(x∣no)P(no)}{P(x)} P(no∣x)=P(x)P(x∣no)P(no)
其中,x={Sunny, Hot, High, Weak},由于分母相同,因此只需计算分子的结果来进行比较即可,计算结果如下图所示:
因此对于样例 x = {Sunny, Hot, High, Weak}所述的情况下应该不去打网球,上述的分类过程即为贝叶斯分类器工作的大致过程。下面根据该过程使用贝叶斯分类器对垃圾邮件进行过滤。
我们把文本看成单词向量或者词条向量,也就是说将句子转换为向量。考虑出现在所有文档中的所有单词,再决定将哪些词纳入词汇表或者说所要的词汇集合,然后必须要将每一篇文档转换为词汇表上的向量。
代码实现
# 创建实验样本
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
"""
函数说明:将实验样本中的词条进行合并(剔除重复项)
Parameters:
dataset:样本数据集列表
Rerurns:
vocabSet:包含所有文档中出现的不重复词的列表
"""
def createVocabList(dataset):
vocabSet = set([])
for document in dataset:
# 取并集
vocabSet = vocabSet | set(document)
return list(vocabSet)
"""
函数说明:将样本列表(inputSet)向量化(0或1)
Parameters:
vocabList:词集列表(createVocabList()函数返回后的列表)
inputSet:待向量化的词条列表
Rerurns:
returnVec:inputSet列表向量化后映射到vocabList中的列表
"""
# 将数据集转为词向量(创建一个包含在所有文档中出现的不重复的词集)
def setOfWords2Vec(vocabList, inputSet):
# 创建一个其中所含元素都为0的列表
returnVec = [0]*len(vocabList)
for word in inputSet:
if word in vocabList:
returnVec[vocabList.index(word)] = 1
else:
print('the word: %s is not in my Vocabulary!' % word)
return returnVec
if __name__ == '__main__':
postingList, classVec = loadDataSet()
myVocabList = createVocabList(postingList)
print('myVocabList:\n', myVocabList)
print(setOfWords2Vec(myVocabList, postingList[0]))
记w表示一个向量(可理解为经过向量化后的一封邮件),它由多个数值组成。在该例中数值个数与词汇表中的词条个数相同,则贝叶斯公式可表示为:
P ( c i ∣ w ) = P ( w ∣ c i ) p ( c i ) P ( w ) P(ci∣w)=\frac{P(w∣ci)p(ci)}{P(w)} P(ci∣w)=P(w)P(w∣ci)p(ci)
其中ci表示第i个属性的取值,由于分母不变,只用比较分子即可。
代码实现:
"""
函数说明:朴素贝叶斯分类器训练函数
Parameter:
trainMatrix — 训练文档矩阵,即setOfWord2Vec函数返回的returnVec构成的矩阵
trainCategory - 训练类别标签向量,即loadDataSet返回的classVec
Returns:
p0Vect - 非侮辱类的条件概率数组
p1Vect - 侮辱类的条件概率数组
pAbusive - 文档属于侮辱类的概率
"""
def trainNBO(trainMatrix,trainCategory):
#文档数目(6)
numTrainDocs = len(trainMatrix)
#词数
numWords = len(trainMatrix[0])
##初始化概率
pAbusive = sum(trainCategory) / float(numTrainDocs)
#使用zeros()初始化后的数组如果其中一个概率值为0则最后的结果也为0,因此使用ones()
# numpy中的ndarray对象用于存放同类型元素的多维数组。
p0Num = np.ones(numWords)
p1Num = np.ones(numWords)
p0Denom = 2.0
p1Denom = 2.0
for i in range(numTrainDocs):
if trainCategory[i] == 1:
#向量相加
p1Num += trainMatrix[i]
p1Denom += sum(trainMatrix[i])
else:
p0Num += trainMatrix[i]
p0Denom += sum(trainMatrix[i])
# 分别计算某个词条在某个类别(该类别中所有词条的总数)中的占比
# #避免程序输出下溢,采用自然对数处理
p1Vect = np.log(p1Num/p1Denom)
p0Vect = np.log(p0Num/p0Denom)
return p0Vect,p1Vect,pAbusive
if __name__ == '__main__':
postingList, classVec = loadDataSet()
myVocabList = createVocabList(postingList)
trainMat = []
for postinDoc in postingList:
trainMat.append(setOfWords2Vec(myVocabList, postinDoc))
p0Vect, p1Vect, pAbusive = trainNBO(trainMat, classVec)
print('p0Vect:\n', p0Vect)
print('p1Vect:\n', p1Vect)
print('文档属于侮辱性文档的概率:', pAbusive)
运行结果:
代码实现:
"""
函数说明:朴素贝叶斯分类函数
Parameter:
vec2Classify - 待分类的词条数组
p0Vec - 侮辱类的条件概率数组
p1Vec - 非侮辱类的条件概率数组
pClass1 - 文档属于侮辱类的概率
Returns:
1 - 属于侮辱类
0 - 属于非侮辱类
"""
def classifyNB(vec2Classify,p0Vec,p1Vec,pClass1):
p1 = sum(vec2Classify * p1Vec) + np.log(pClass1)
p0 = sum(vec2Classify * p0Vec) + np.log(1.0 - pClass1)
if p1 > p0:
return 1
else:
return 0
if __name__ == '__main__':
postingList, classVec = loadDataSet()
myVocabList = createVocabList(postingList)
trainMat = []
for postinDoc in postingList:
trainMat.append(setOfWords2Vec(myVocabList, postinDoc))
p0V,p1V,pAb = trainNBO(np.array(trainMat),np.array(classVec))
testEntry = ['love', 'my', 'dalmation']
thisDoc = np.array(setOfWords2Vec(myVocabList, testEntry))
if classifyNB(thisDoc,p0V,p1V,pAb):
print(testEntry,'属于侮辱类')
else:
print(testEntry,'属于非侮辱类')
testEntry = ['stupid', 'love', 'cute']
thisDoc = np.array(setOfWords2Vec(myVocabList, testEntry))
if classifyNB(thisDoc,p0V,p1V,pAb):
print(testEntry,'属于侮辱类')
else:
print(testEntry,'属于非侮辱类')
代码实现:
"""
函数说明:根据vocabList词汇表,构建词袋模型
Parameter:
vocabList - creatVocabList
inputSet - 切分的词条列表
Returns:
returnVec - 文档向量
"""
def bagOfWords2Vec(vocabList,inputSet):
returnVec = [0] * len(vocabList)
for word in inputSet:
if word in vocabList:
returnVec[vocabList.index(word)] += 1
return returnVec
"""
函数说明:对文本进行切分
Parameter:
bigString:长字符串(一封邮件)
Returns:
返回形式统一的、被切分后的词表
"""
def textParse(bigString):
#将非数字、单词外的任意字符串作为切分标志
listOfTokens = re.split('\\W*',bigString)
#为了使所有词的形式统一,除了单个字母,例如大写的I,其余的单词变成小写
return [tok.lower() for tok in listOfTokens if len(tok) > 2]
"""
函数说明: 垃圾邮件测试函数
"""
def spamTest():
docList = [];classList = [];fullTest = []
#导入并解析50个文件,并记录对应的标签值,垃圾邮件标记为1
for i in range(1,26):
wordList = textParse(open('C:/Users/cool/Desktop/mach/email/spam/%d.txt'% i,'r').read())
docList.append(wordList)
fullTest.append(wordList)
classList.append(1)
wordList = textParse(open('C:/Users/cool/Desktop/mach/email/ham/%d.txt'% i,'r').read())
docList.append(wordList)
fullTest.append(wordList)
classList.append(0)
#获得50个文件构成的词列表
vocabList = createVocabList(docList)
trainingSet = list(range(50))
testSet = []
#随机选取测试集合,并从训练集中去除,留存交叉验证
for i in range(10):
randIndex = int(random.uniform(0,len(trainingSet)))
testSet.append(trainingSet[randIndex])
del(trainingSet[randIndex])
trainMat = [];trainClass = []
#构建训练集词向量列表,训练集标签
for docIndex in trainingSet:
trainMat.append(setOfWords2Vec(vocabList,docList[docIndex]))
trainClass.append(classList[docIndex])
#训练算法,计算分类所需的概率
p0V,p1V,pSpam = trainNBO(np.array(trainMat),np.array(trainClass))
#分类错误个数初始化为0
errorCount = 0
#,遍历测试集,验证算法错误率
for docIndex in testSet:
wordVector = setOfWords2Vec(vocabList,docList[docIndex])
if classifyNB(np.array(wordVector),p0V,p1V,pSpam) != classList[docIndex]:
errorCount += 1
print('错误率:%.2f%%'%(float(errorCount)/len(testSet)*100))
if __name__ == '__main__':
spamTest()
运行结果:
"""
函数说明: 垃圾邮件测试函数
"""
def spamTest():
docList = [];classList = [];fullTest = []
#导入并解析50个文件,并记录对应的标签值,垃圾邮件标记为1
for i in range(1,26):
wordList = textParse(open('C:/Users/lenovo/Desktop/machinelearninginaction/Ch04/email/spam/%d.txt'% i,'r').read())
docList.append(wordList)
fullTest.append(wordList)
classList.append(1)
wordList = textParse(open('C:/Users/lenovo/Desktop/machinelearninginaction/Ch04/email/ham/%d.txt'% i,'r').read())
docList.append(wordList)
fullTest.append(wordList)
classList.append(0)
#获得50个文件构成的词列表
vocabList = createVocabList(docList)
trainingSet = list(range(50))
testSet = []
#随机选取测试集合,并从训练集中去除,留存交叉验证
for i in range(10):
randIndex = int(random.uniform(0,len(trainingSet)))
testSet.append(trainingSet[randIndex])
del(trainingSet[randIndex])
trainMat = [];trainClass = []
#构建训练集词向量列表,训练集标签
for docIndex in trainingSet:
trainMat.append(setOfWords2Vec(vocabList,docList[docIndex]))
trainClass.append(classList[docIndex])
#训练算法,计算分类所需的概率
p0V,p1V,pSpam = trainNBO(np.array(trainMat),np.array(trainClass))
#分类错误个数初始化为0
errorCount = 0
#,遍历测试集,验证算法错误率
for docIndex in testSet:
wordVector = setOfWords2Vec(vocabList,docList[docIndex])
if classifyNB(np.array(wordVector),p0V,p1V,pSpam) != classList[docIndex]:
errorCount += 1
print('错误率:%.2f%%'%(float(errorCount)/len(testSet)*100))
if __name__ == '__main__':
spamTest()
通过朴素贝叶斯我们可以对属性相对独立的数据进行比较好的分类,对于不同类型的数据集不会呈现出太大的差异性。但是对于数据集的属性之间存在着较强关联时,分类效果不好。