朴素贝叶斯分类是一种常用的分类算法,他根据研究对象的某些特征,来推断出该研究对象属于该研究领域的哪个类别。
1. 概述
要了解朴素贝叶斯分类,就需要先知道贝叶斯分类定理,这就离不开条件概率,下面概率论中经典的条件概率公式:
根据上面的概率公式,下面我就举个例子简单的描述贝叶斯分类的工作原理。
例:某学校男性打篮球的概率为 2/3 ,女性打篮球的概率为 1/3 ,并且该公园中男女比例通常为 2:1 ,问题:若你在学校里随机遇到一个打篮球的人,请问他的性别为男性或女性的概率分别为多少?
由上面的条件可以得到:
,
,
,
,所以:
也就是说,在这个校园里,知道打篮球的前提下,这个人是男性的概率是
,是女性的概率是
。如果问题是“判断该人是男性还是女性”,此问题就是一个分类问题。由于上面计算出来概率是男性的概率大于是女性的概率,即由于
,那么我们就可以将其分类为男性。
这就是一个简单的贝叶斯分类器,在数据的基础上,根据找到的一些特征属性,来计算各个类别的概率,找到概率最大的类,从而实现分类。
2. 定义
朴素贝叶斯是基于贝叶斯定理和特征条件独立假设的分类方法,假设训练样本的属性集为
,共有
个属性,则朴素贝叶斯定理:
使用上面定义,可以定义贝叶斯分类准则为: 如果
, 那么属于类别
; 如果
, 那么属于类别
。
朴素贝叶斯比起真正的贝叶斯只是多了两个假设条件来降低了复杂度:
假设1,多个特性之间相互独立,特性之间没有关联关系也不会相互影响。
假设2,特性之间地位相同
3. 使用场景
优点:过程简单速度快,即使对于多分类问题也很有效,同时在分布独立这个假设成立的情况下,贝叶斯分类器效果会略胜于逻辑回归,而且需要的样本量也更少一点。
缺点:对于测试集中的一个类别变量特征,如果在训练集里没见过,直接算的话概率就是0了,预测功能就无效,朴素贝叶斯有分布独立的假设前提,而现实生活中是很难完全独立的。
由以上优缺点,我们将朴素贝叶斯使用在以下场景。
文本分类/垃圾文本过滤/情感判别
多分类实时预测
推荐系统:朴素贝叶斯和协同过滤一起,能增强推荐的覆盖度和效果。
4. 工作原理
- 提取所有文档中的词条并进行去重
- 获取文档的所有类别
- 计算每个类别中的文档数目
- 对每篇训练文档:
对每个类别:
如果词条出现在文档中–>增加该词条的计数值(for循环或者矩阵相加)
增加所有词条的计数值(此类别下词条总数) - 对每个类别:
对每个词条:
将该词条的数目除以总词条数目得到的条件概率(P(词条|类别)) - 返回该文档属于每个类别的条件概率(P(类别|文档的所有词条))
5. 案例分析
参考《机器学习实战》,通过算法来判断某篇文章是否是侮辱性的
- 搜集数据
这里我们构造自己的词表。
def loadDataSet():
postingList = [['落叶', '帅'],
['傻逼'],
['皮蛋', '瘦肉粥', '美味'],
['智障'],
['生活', '潇洒'],
['白痴']]
classVec = [0, 1, 0, 1, 0, 1] #1 侮辱言论 0正常言论
return postingList, classVec
- 准备数据
获取训练集中出现的所有词语。
def createVocabList(dataSet):
vocabSet = set([])
for document in dataSet:
vocabSet = vocabSet | set(document) #获取并集,去掉重复词语
return list(vocabSet) #返回所有词语
词语本身是不具备运算能力的,因此我们需要通过建模将词语向量化。
def setOfWords2Vec(vocabList, inputSet):
returnVec = [0] * len(vocabList) #创建一个填充值全部为0的向量
for word in inputSet:
if word in vocabList:
returnVec[vocabList.index(word)] = 1 #如果词语出现在数据集中,则将文档向量中的对应值设为1
else:
print ("%s 没有在数据集中" % word)
return returnVec
检查函数执行情况,检查词表,查看是否出现重复单词,同时创建向量矩阵。
['白痴', '落叶', '帅', '瘦肉粥', '生活', '智障', '傻逼', '皮蛋', '潇洒', '美味']
[[1, 0, 0, 0, 0, 0, 0, 1, 0, 0], [0, 0, 0, 1, 0, 0, 0, 0, 0, 0], [0, 1, 0, 0, 1, 0, 1, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 1], [0, 0, 0, 0, 0, 1, 0, 0, 1, 0], [0, 0, 1, 0, 0, 0, 0, 0, 0, 0]]
- 训练算法
在上面我们处理好了数据,下面就需要套用朴素贝叶斯公式来计算各个概率之间的关系,将之前的 替换为 , 表示这是一个向量,即它由多个值组成。在这个例子中, 为[[1, 0, 0, 0, 0, 0, 0, 1, 0, 0], [0, 0, 0, 1, 0, 0, 0, 0, 0, 0], [0, 1, 0, 0, 1, 0, 1, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 1], [0, 0, 0, 0, 0, 1, 0, 0, 1, 0], [0, 0, 1, 0, 0, 0, 0, 0, 0, 0]]
我们使用上述公式,对每个类计算该值,然后比较这两个概率值的大小。首先可以通过类别 (侮辱性留言或者非侮辱性留言)中的文档数除以总的文档数来计算概率 。接下来计算 ,这里就要用到朴素贝叶斯假设。如果将 展开为一个个独立特征,那么就可以将上述概率写作 。这里假设所有词都互相独立,它意味着可以使用 来计算上述概率,这样就极大地简化了计算的过程。
def trainNB0(trainMatrix, trainCategory):
numTrainDocs = len(trainMatrix) #总文件数
numWords = len(trainMatrix[0]) #总单词数
pAbusive = sum(trainCategory) / float(numTrainDocs) #侮辱性文件的出现概率
p0Num = ones(numWords) #p0Num 正常的统计
p1Num = ones(numWords) #p1Num 侮辱的统计
p0Denom = 2.0 #p0Denom 正常的统计, 2.0主要是为了避免分母为0
p1Denom = 2.0 #p1Denom 侮辱的统计
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 = log(p1Num / p1Denom) #侮辱性文档列表
p0Vect = log(p0Num / p0Denom) #正常文档列表
return p0Vect, p1Vect, pAbusive
- 朴素贝叶斯分类
def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1):
p1 = sum(vec2Classify * p1Vec) + log(pClass1) #侮辱性概率
p0 = sum(vec2Classify * p0Vec) + log(1.0 - pClass1) #正常概率
if p1 > p0:
return 1
else:
return 0
- 测试与结果
if __name__ == '__main__':
listOPosts, listClasses = loadDataSet() #1. 加载数据集
myVocabList = createVocabList(listOPosts) #2. 创建单词集合
trainMat = []
for postinDoc in listOPosts: # 3. 计算单词是否出现并创建数据矩阵
trainMat.append(setOfWords2Vec(myVocabList, postinDoc))
p0V, p1V, pAb = trainNB0(array(trainMat), array(listClasses)) #4. 训练数据
# 5. 测试数据
testEntry = ['落叶', '潇洒']
thisDoc = array(setOfWords2Vec(myVocabList, testEntry))
print (testEntry, 'classified as: ', classifyNB(thisDoc, p0V, p1V, pAb))
testEntry = ['傻逼', '智障']
thisDoc = array(setOfWords2Vec(myVocabList, testEntry))
print (testEntry, 'classified as: ', classifyNB(thisDoc, p0V, p1V, pAb))