一、什么是朴素贝叶斯分类
朴素贝叶斯分类是一种基于概率的分类方法,它利用贝叶斯定理来计算给定特征下不同类别的概率,然后选择最大的概率对应的类别作为预测结果。朴素贝叶斯分类的朴素之处在于它假设特征之间是相互独立的,即每个特征对分类结果的影响是相同的。这个假设在实际应用中往往是不成立的,但是在一些场景下,例如文本分类、垃圾邮件过滤等,朴素贝叶斯分类仍然可以表现出较好的效果。
朴素贝叶斯分类的基本原理可以用以下公式表示:
其中,C表示类别,F表示特征,P(C∣F)表示后验概率,即在特征F的条件下,类别C出现的概率,P(F∣C)表示类条件概率,即在类别C的条件下,特征F出现的概率,P(C)表示先验概率,即类别C出现的概率,P(F)表示证据概率,即特征F出现的概率。
如果特征F由多个子特征F1,F2,...,Fn组成,那么根据朴素贝叶斯的假设,可以将类条件概率和证据概率分解为多个子特征的条件概率的乘积,即:
这样,就可以通过统计训练数据中每个类别和每个特征的频率来计算这些概率值,然后根据贝叶斯定理求出后验概率,最后选择最大的后验概率对应的类别作为预测结果。
二、朴素贝叶斯分类的原理和步骤
假设我们有一个数据集,它包含了一些水果的特征和类别,如下图所示:
我们想要用朴素贝叶斯分类来预测一个新的水果的类别,它的特征是:颜色为红色,大小为小,形状为长形。我们可以用以下的步骤来进行分类:
1.计算每个类别的先验概率,即在数据集中出现的频率。例如,樱桃的先验概率为P(C=樱桃)=0.2,香蕉的先验概率为P(C=香蕉)=0.2,以此类推。
2.计算每个特征在每个类别下的条件概率,即在数据集中出现的频率。例如,颜色为红色在樱桃类别下的条件概率为P(颜色=红色∣C=樱桃)=1,颜色为红色在香蕉类别下的条件概率为P(颜色=红色∣C=香蕉)=0,以此类推。
3.计算每个特征的证据概率,即在数据集中出现的频率。例如,颜色为红色的证据概率为P(颜色=红色)=0.4,大小为小的证据概率为P(大小=小)=0.4,以此类推。
4.根据贝叶斯定理,计算每个类别的后验概率,即在给定特征下的概率。例如,樱桃的后验概率为:
P(C=樱桃∣颜色=红色,大小=小,形状=长形)=
同理我们可以知道样本中所有水果的后验概率都为0,我们无法根据朴素贝叶斯分类来预测这个新水果的类别,这说明贝叶斯分类的一个缺点为:当某个特征在某个类别下没出现过时,会导致后验概率为0,从而影响分类结果,为了解决这个问题,我们可以采用拉普拉斯平滑来避免概率为0的情况。
平滑技术的公式如下:
其中,Fi表示第i个特征,Ck表示第k个类别,Nik表示在类别Ck下,特征Fi出现的次数,Nk表示类别Ck出现的次数,Si表示第i个特征的取值个数,α表示平滑参数,通常为1。
代码实现如下:
import numpy as np
dataset = np.array([
["红色", "小", "圆形", "樱桃"],
["绿色", "中", "长形", "香蕉"],
["黄色", "中", "圆形", "柠檬"],
["红色", "大", "圆形", "苹果"],
["绿色", "小", "圆形", "葡萄"]
])
new_fruit = np.array(["红色", "小", "长形"])
alpha = 1
n_features = dataset.shape[1] - 1
classes = np.unique(dataset[:, -1])
features = [np.unique(dataset[:, i]) for i in range(n_features)]
def prior_prob(class_):
freq = np.sum(dataset[:, -1] == class_)
prob = (freq + alpha) / (dataset.shape[0] + len(classes) * alpha)
return prob
def cond_prob(feature, feature_index, class_):
freq = np.sum((dataset[:, feature_index] == feature) & (dataset[:, -1] == class_))
class_freq = np.sum(dataset[:, -1] == class_)
prob = (freq + alpha) / (class_freq + len(features[feature_index]) * alpha)
return prob
def posterior_prob(class_):
prob = prior_prob(class_)
for i in range(n_features):
prob *= cond_prob(new_fruit[i], i, class_)
return prob
posterior_probs = {}
for class_ in classes:
posterior_probs[class_] = posterior_prob(class_)
print("后验概率:")
for class_, prob in posterior_probs.items():
print(f"{class_}: {prob}")
prediction = max(posterior_probs, key=posterior_probs.get) # type: ignore
print(f"预测结果:{prediction}")
运行结果如下:
三、朴素贝叶斯分类的应用
朴素贝叶斯分类是一种常用的垃圾邮件过滤方法,它可以根据邮件中的单词或者其他特征,计算邮件属于垃圾邮件或者正常邮件的概率,然后根据概率的大小,选择合适的类别作为预测结果。下面,我们将介绍如何使用朴素贝叶斯分类来实现垃圾邮件过滤的功能。
import numpy as np
import re
import random
import os
def textParse(bigString):
listOfTokens = re.split(r'\W+', bigString)
return [tok.lower() for tok in listOfTokens if len(tok) > 2]
def createVocabList(dataSet):
vocabSet = set([])
for document in dataSet:
vocabSet = vocabSet | set(document)
return list(vocabSet)
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
def trainNB0(trainMatrix, trainCategory):
numTrainDocs = len(trainMatrix)
numWords = len(trainMatrix[0])
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:
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
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
def spamTest():
docList = []
classList = []
fullText = []
spam_folder = "D: \\qq(data&file)\\1678745189\\FileRecv\\Ch04\\email\\spam"
ham_folder = "D:\\qq(data&file)\\1678745189\\FileRecv\\Ch04\\email\\ham"
for file in os.listdir(spam_folder):
if file.endswith(".txt"):
wordList = textParse(open(os.path.join(spam_folder, file), 'r').read())
docList.append(wordList)
fullText.extend(wordList)
classList.append(1)
for file in os.listdir(ham_folder):
if file.endswith(".txt"):
wordList = textParse(open(os.path.join(ham_folder, file), 'r').read())
docList.append(wordList)
fullText.extend(wordList)
classList.append(0)
vocabList = createVocabList(docList)
trainingSet = []
testSet = random.sample(range(len(docList)), 10)
for i in range(len(docList)):
if i not in testSet:
trainingSet.append(i)
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])
predicted = classifyNB(np.array(wordVector), p0V, p1V, pSpam)
if predicted != classList[docIndex]:
errorCount += 1
print("classification error", docList[docIndex])
errorRate = float(errorCount) / len(testSet)
print("the error rate is: ", errorRate)
return errorRate
运行结果如下
四、总结
朴素贝叶斯分类有很明显的优缺点。它的优点是逻辑简单,容易实现,时间和空间复杂度都比较小,它的缺点是只能处理分类任务,需要假设特征之间独立,对于特征间相关性较大或特征数量较多的情况效果不好等。