目录
一、朴素贝叶斯概述
1.1 概述
朴素贝叶斯算法是一种基于贝叶斯定理和条件独立性假设的分类方法。
其中,朴素贝叶斯的朴素代表属性之间独立。
该算法的核心思想分为两个部分:
首先,基于特征条件独立性假设,算法学习输入和输出的联合概率分布。
然后,利用贝叶斯定理,对于给定的输入特征 x,计算各个输出类别 y 的后验概率。
1.2 贝叶斯公式
贝叶斯公式如下:
通俗来讲是以下形式:
而不同类别之间进行大小比较,其分母是相同的因此正比关系如下:
由于朴素贝叶斯的朴素代表属性独立性的假设因此式子也可以改写为这样:
1.3 先验概率&后验概率
先验概率指根据以往经验和分析。在实验或采样前就可以得到的概率。
上文中P(类别)就是先验概率
后验概率指某件事已经发生,想要计算这件事发生的原因是由某个因素引起的概率。
上文中P(类别|特征)就是后验概率
1.4 例子
假设我们现在有两个盒子,分别为红色和蓝色。在红色盒子中放着2个苹果和6个橙子,在蓝色盒子中放着1个橙子和3个苹果,如下图所示:
图中绿色表示苹果,橙色代表橙子。
假设我们每次实验的时候会随机从某个盒子里挑出一个水果,
随机变量B表示挑出的是哪个盒子,并且P(B=blue) = 0.6(蓝色盒子被选中的概率),P(B=red) = 0.4(红色盒子被选中的概率)。
随机变量F表示挑中的是哪种水果,F的取值为"a "和"o "。
假设我们已经得知某次实验中挑出的水果是orange,求这个orange是从红色盒子里挑出的概率是多大
以下是推导过程:
在上面的计算过程中,我们将 P(B=red)或者说 P(B)称为先验概率,因为我们在得到F是“a”或者“o”之前,就可以得到 P(B) 。
同理,将 P(B=blue|F=o)和 P(B=red|F=o)称为后验概率,因为我们在完整的一次实验之后也就是得到了F的具体取值之后才能得到这个概率。
1.5 拉普拉斯修正
由于实验数据存在某些原因导致数据有缺失值,例如没有记录某天是否去打球,导致过拟合。因此在计算概率的时候需要加减降低这种情况的影响。
1.6 对数似然防溢出
当属性数量多的情况下,导致累乘结果下溢。采用防溢出策略(累乘变累加)
二、实现
2.1 垃圾邮箱分类
2.1.1 数据集介绍
垃圾邮件分类数据集是一组用于训练和测试垃圾分类模型的数据,主要包括不同类型垃圾的分类信息和相关特征。垃圾分类数据集可以,实现对垃圾的自动分类。
2.1.2 代码介绍
createVocabList:根据输入的数据集创建词汇表,返回一个不重复的词条列表。
def createVocabList(dataSet):
vocabSet = set([])
for document in dataSet:
vocabSet = vocabSet | set(document) # 取并集
return list(vocabSet)
setOfWords2Vec:根据词汇表将输入的词条列表转换为向量,向量的每个元素为 1 或 0。
def setOfWords2Vec(vocabList, inputSet):
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
bagOfWords2VecMN:根据词汇表构建词袋模型,即将每个词条的出现次数转换为一个向量。
def bagOfWords2VecMN(vocabList, inputSet):
returnVec = [0] * len(vocabList)
for word in inputSet:
if word in vocabList:
returnVec[vocabList.index(word)] += 1
return returnVec
trainNB0:训练朴素贝叶斯分类器,输入为训练文档矩阵和训练类别标签向量,返回正常邮件类和垃圾邮件类的条件概率数组以及文档属于垃圾邮件类的概率。
def trainNB0(trainMatrix, trainCategory):
numTrainDocs = len(trainMatrix)
print("文档数目:", numTrainDocs)
numWords = len(trainMatrix[0])
print("词条数", numWords)
print("---:", trainCategory)
# 形状[40,37]
print("---:", trainMatrix)
pAbusive = sum(trainCategory) / float(numTrainDocs)
p0Num = np.ones(numWords)
p1Num = np.ones(numWords)
p0Denom = 2.0
p1Denom = 2.0
for i in range(numTrainDocs):
if trainCategory[i] == 1:
print("p1Num",p1Num)
p1Num += trainMatrix[i]
print("p1Denom",p1Denom)
p1Denom += sum(trainMatrix[i])
else:
p0Num += trainMatrix[i]
p0Denom += sum(trainMatrix[i])
print("1:",p1Num / p1Denom)
print("0:",p0Num / p1Denom)
p1Vect = np.log(p1Num / p1Denom)
p0Vect = np.log(p0Num / p0Denom)
return p0Vect, p1Vect, pAbusive
```
**classifyNB**:朴素贝叶斯分类器的分类函数,根据输入的词条向量和条件概率数组判断文档属于正常邮件还是垃圾邮件。
```python
def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1):
#p1 = reduce(lambda x, y: x * y, vec2Classify * p1Vec) * pClass1
#p0 = reduce(lambda x, y: x * y, vec2Classify * p0Vec) * (1.0 - 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
textParse:将大字符串解析为字符串列表。
def textParse(bigString):
listOfTokens = re.split(r'\W*', bigString)
return [tok.lower() for tok in listOfTokens if len(tok) > 0]
spamTest:主函数,测试朴素贝叶斯分类器的性能。首先读取垃圾邮件和正常邮件,然后创建词汇表,训练朴素贝叶斯分类器,最后进行交叉验证测试。
def spamTest():
docList = []
classList = []
fullText = []
listOfTokens1 = []
for i in range(1, 26):
wordList = textParse(open('./email/spam/%d.txt' % i, 'r', encoding='iso8859-1').read())
docList.append(wordList)
fullText.append(wordList)
classList.append(1)
wordList = textParse(open('./email/ham/%d.txt' % i, 'r', encoding='iso8859-1').read())
docList.append(wordList)
fullText.append(wordList)
classList.append(0)
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 = []
trainClasses = []
for docIndex in trainingSet:
trainMat.append(setOfWords2Vec(vocabList, docList[docIndex]))
trainClasses.append(classList[docIndex])
p0V, p1V, pSpam = trainNB0(np.array(trainMat), np.array(trainClasses))
errorCount = 0
for docIndex in testSet:
wordVector = setOfWords2Vec(vocabList, docList[docIndex])
if classifyNB(np.array(wordVector), p0V, p1V, pSpam) != classList[docIndex]:
errorCount += 1
print("分类错误的测试集:", docList[docIndex])
print('错误率:%.2f%%' % (float(errorCount) / len(testSet) * 100))
2.1.3 结果
三、总结
3.1 优点
生成式模型,通过计算概率来进行分类,可以用来处理多分类问题
对小规模的数据表现很好,适合多分类任务,适合增量式训练,算法较简单
3.2 缺点
对输入数据的表达形式很敏感
会带来一些准确率上的损失
需要计算先验概率,分类决策存在错误率