条件概率定义:
设与是样本空间中的两事件,若,则称
为“在发生下的条件概率”,简称条件概率。
条件概率特有的三个公式:
- 乘法公式:
若,则
若,则 - 全概率公式:
设为样本空间的一个分割,即互不相容,且,如果,则对任一事件有:
全概率公式的意义:事件的发生有各种原因,所以发生的概率即为全部引起发生的概率的总和 - 贝叶斯公式:
在乘法公式和全概率公式的基础上即可得到著名的贝叶斯公式。设为样本空间的一个分割,即互不相容,且,如果则
机器学习中的贝叶斯
其中对应于特征和所属类别,给定样本的特征,判断其所属类别,可以通过该公式转换为计算等式右边。
机器学习中的朴素贝叶斯算法,则假设样本的特征之间相互独立,那么上式可以转换为:
由于判断样本所属类别只需要比较属于不同类别概率的大小即可,而上式的分母只跟样本的特征相关而跟所属类别无关,所以实际应用中只需要计算分子的大小,即可判断出样本所属类别。
计算一下:
某个医院早上来了六个门诊的病人,他们的情况如下表所示:
现在又来了第七个病人,是一个打喷嚏的建筑工人。请问他患上感冒的概率有多大?
根据贝叶斯定理:
根据朴素贝叶斯条件独立性的假设可知,"打喷嚏"和"建筑工人"这两个特征是独立的,因此,上面的等式就变成了
代入计算得到:
实战:朴素贝叶斯之言论过滤器。
import numpy as np
from functools import reduce
# 构造样本
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] # 类别标签向量,1代表侮辱性词汇,0代表不是
return postingList, classVec
# 将样本转换为词条向量
def setOfWords2Vec(vocabList, inputSet):
returnVec = [0] * len(vocabList) # 创建一个其中所含元素都为0的向量
for word in inputSet: # 遍历每个词条
if word in vocabList: # 如果词条存在于词汇表中,则置1
returnVec[vocabList.index(word)] = 1
else:
print("the word: %s is not in my Vocabulary!" % word)
return returnVec
# 构造词典
def createVocabList(dataSet):
vocabSet = set([]) # 创建一个空的不重复列表
for document in dataSet:
vocabSet = vocabSet | set(document) # 取并集
return list(vocabSet)
def trainNB0(trainMatrix, trainCategory):
"""
贝叶斯条件概率公式:P(y|X)=P(X|y)P(y)/P(X),朴素假设特征之间相互独立
待计算的是P(侮辱|stupid,dog,my,has,...) = (P(stupid,dog,my,has,...|侮辱)*P(侮辱文档数|总文档数)/P(stupid,dog,my,has,...))
统计了侮辱和非侮辱文档中各自词汇出现的条件概率。比如P(stupid|侮辱文档数),P(dog|侮辱文档数),P(my|非侮辱文档数),P(has|非侮辱文档数)。
以及先验概率P(侮辱文档数|总文档数)。
"""
numTrainDocs = len(trainMatrix) # 计算训练的文档数目
numWords = len(trainMatrix[0]) # 计算每篇文档的词条数
pAbusive = sum(trainCategory) / float(numTrainDocs) # 文档属于侮辱类的概率
p0Num = np.zeros(numWords)
p1Num = np.zeros(numWords) # 创建numpy.zeros数组,词条出现数初始化为0
p0Denom = 0.0
p1Denom = 0.0 # 分母初始化为0
for i in range(numTrainDocs):
if trainCategory[i] == 1: # 统计属于侮辱类的条件概率所需的数据,即P(w0|1),P(w1|1),P(w2|1)···
p1Num += trainMatrix[i]
p1Denom += sum(trainMatrix[i])
else: # 统计属于非侮辱类的条件概率所需的数据,即P(w0|0),P(w1|0),P(w2|0)···
p0Num += trainMatrix[i]
p0Denom += sum(trainMatrix[i])
p1Vect = p1Num / p1Denom
p0Vect = p0Num / p0Denom
return p0Vect, p1Vect, pAbusive # 返回属于侮辱类的条件概率数组,属于非侮辱类的条件概率数组,文档属于侮辱类的概率
def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1):
"""
贝叶斯条件概率公式:P(y|X)=P(X|y)P(y)/P(X)。
朴素贝叶斯假设特征之间相互独立,此时上述公式变为:P(y|X)=P(x1|y)P(x2|y)...P(xn|y)P(y)/P(x1)P(x2)...P(xn)。
可以发现等式右边的分母P(x1)P(x2)...P(xn)只跟特征有关系,跟类别没有关系,也就是说比较不同类别之间的条件概率大小时,
由于分母相等,直接比较分子之间的大小关系即可。所以下面的实际计算过程中也只计算了分子。
"""
p1 = reduce(lambda x, y: x * y, vec2Classify * p1Vec) * pClass1 # 对应元素相乘
p0 = reduce(lambda x, y: x * y, vec2Classify * p0Vec) * (1.0 - pClass1)
print("p0:", p0)
print("p1:", p1)
if p1 > p0:
return 1
else:
return 0
def testingNB():
listOPosts, listClasses = loadDataSet() # 创建实验样本
myVocabList = createVocabList(listOPosts) # 创建词汇表
trainMat = []
for postinDoc in listOPosts:
trainMat.append(setOfWords2Vec(myVocabList, postinDoc)) # 将实验样本向量化
p0V, p1V, pAb = trainNB0(np.array(trainMat), np.array(listClasses)) # 训练朴素贝叶斯分类器
testEntry = ["love", "my", "dalmation"] # 测试样本1
thisDoc = np.array(setOfWords2Vec(myVocabList, testEntry)) # 测试样本向量化
if classifyNB(thisDoc, p0V, p1V, pAb):
print(testEntry, "属于侮辱类") # 执行分类并打印分类结果
else:
print(testEntry, "属于非侮辱类") # 执行分类并打印分类结果
testEntry = ["stupid", "garbage"] # 测试样本2
thisDoc = np.array(setOfWords2Vec(myVocabList, testEntry)) # 测试样本向量化
if classifyNB(thisDoc, p0V, p1V, pAb):
print(testEntry, "属于侮辱类") # 执行分类并打印分类结果
else:
print(testEntry, "属于非侮辱类")
if __name__ == "__main__":
testingNB()
执行结果
发现第二个样本明显包含了侮辱性的词汇,却误判为非侮辱类。
这是由于在代入样本的词条向量和训练集中得到的词条概率计算乘积时,只要有一个为0,就会导致结果为0。比如my没有出现在样本2中,计算分子乘积时就会导致结果为0。
解决办法:朴素贝叶斯改进之拉普拉斯平滑
可以将所有词的出现数初始化为1而不是0,并将分母初始化为2。这种做法就叫做拉普拉斯平滑(Laplace Smoothing)又被称为加1平滑,是比较常用的平滑方法,它就是为了解决0概率问题。
详细参考