朴素贝叶斯算法
朴素贝叶斯法是基于贝叶斯定理与特征条件独立假设的分类方法。
最为广泛的两种分类模型是决策树模型(Decision Tree Model)和朴素贝叶斯模型(Naive Bayesian Model,NBM)。和决策树模型相比,朴素贝叶斯分类器(Naive Bayes Classifier 或 NBC)发源于古典数学理论,有着坚实的数学基础,以及稳定的分类效率。同时,NBC模型所需估计的参数很少,对缺失数据不太敏感,算法也比较简单。理论上,NBC模型与其他分类方法相比具有最小的误差率。但是实际上并非总是如此,这是因为NBC模型假设属性之间相互独立,这个假设在实际应用中往往是不成立的,这给NBC模型的正确分类带来了一定影响。
朴素贝叶斯算法(Naive Bayesian algorithm) 是应用最为广泛的分类算法之一。
朴素贝叶斯方法是在贝叶斯算法的基础上进行了相应的简化,即假定给定目标值时属性之间相互条件独立。也就是说没有哪个属性变量对于决策结果来说占有着较大的比重,也没有哪个属性变量对于决策结果占有着较小的比重。虽然这个简化方式在一定程度上降低了贝叶斯分类算法的分类效果,但是在实际的应用场景中,极大地简化了贝叶斯方法的复杂性。
贝叶斯方法
贝叶斯方法是以贝叶斯原理为基础,使用概率统计的知识对样本数据集进行分类。由于其有着坚实的数学基础,贝叶斯分类算法的误判率是很低的。贝叶斯方法的特点是结合先验概率和后验概率,即避免了只使用先验概率的主观偏见,也避免了单独使用样本信息的过拟合现象。贝叶斯分类算法在数据集较大的情况下表现出较高的准确率,同时算法本身也比较简单。
简单文档分类实例
import numpy as np
## 简单文档分类——朴素贝叶斯算法
'''
创建实验数据集(假设每个样本已经被分割完成)
返回值分别是 X_train, y_train
X_train:表示训练的样本
y_train:表示对应训练样本的分类结果
'''
def loadDataSet():
X_train = [
['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']
] # 切分好的词条
y_train = [0, 1, 0, 1, 0, 1] # 类别标签向量,1代表侮辱性词汇,0代表非侮辱性词汇
return X_train, y_train
'''
创建词汇表
trainData:训练集trainData(每句话代表一条数据),每句话已被切割成单词了
vocabList:训练集中所有的单词(无重复)
'''
def createVocabList(trainData):
vocabSet = set() # 创建一条空的集合
# 遍历训练集dataSet(每句话代表一条数据)中的每句话(已被切割成单词了)
for doc in trainData:
vocabSet = vocabSet | set(doc) # 取并集,重复的只记一遍
# print(vocabSet)
vocabList = list(vocabSet)
# print(len(vocabList))
return vocabList
'''
vocabList:词汇表
inputSet:当前样本(话,包含多个已被分割的单词)
returnVec:返回每个样本(话)所对应的结果向量
'''
def setofwords2Vec(vocabList, inputSet):
returnVec = [0] * len(vocabList) # 创建词汇表大小的零向量
for word in inputSet: # 遍历当前样本(话)中的每一个单词
if word in vocabList:
returnVec[vocabList.index(word)] += 1
else:
print(f'{word} is not in my vocabulary!')
return returnVec
'''
生成训练向量集
dataSet:训练集
vocabList:词汇表
trainMat:返回整个训练集的结果向量集
(n*m维,n表示样本数,m表示词汇表中词的个数)
'''
def get_trainMat(dataSet, vocabList):
trainMat = [] # 初始化向量列表
for inputSet in dataSet: # 遍历训练集中每句话(所有的单词)
returnVec = setofwords2Vec(vocabList, inputSet) # 将当前词条向量化
trainMat.append(returnVec)
return trainMat
'''
拉普拉斯平滑朴素贝叶斯分类器训练函数
要计算多个概率的乘积以获取文档属于哪个类别,如果其中一个概率为0,则最终计算结果为0,显然是不合理的。
因此使分子初始化为1,分母初始化为2,这种做法就叫做拉普拉斯平滑,又被称为加一平滑。它就是为了解决0概率问题
trainMat:训练结果向量集
classVec:每个训练样本的结果向量
'''
def trainNB(trainMat, classVec):
trainNum = len(trainMat) # 计算训练集的数目(总文档数)
ClassNum = len(trainMat[0]) # 计算训练集中每一条的大小(总单词数)
p0Num = np.ones(ClassNum) # 非侮辱类每个单词词频初始化为0,为了防止出现0而初始化为1
p1Num = np.ones(ClassNum) # 侮辱类每个单词词频初始化为0,为了防止出现0而初始化为1
p0Denom = 2 # 非侮辱类所有单词的词频,同理初始化为2
p1Denom = 2 # 侮辱类所有单词的词频
num0 = 0 # 各类训练集个数
num1 = 0 # 同上
pNumList = [p0Num, p1Num]
pDenomList = [p0Denom, p1Denom]
numList = [num0, num1]
for i in range(trainNum): # 遍历每一个训练样本
for j in range(2): # 遍历每个类别
if classVec[i] == j: # 如果第i个训练样本为j类样本
pNumList[j] += trainMat[i] # 在该样本出现的所有单词词频+1
pDenomList[j] += sum(trainMat[i]) # 同理对分母增加的此样本所有单词词频
numList[j] += 1 # 该类文档数目加1
# 取log对数仍然是拉普拉斯平滑原理
pVect, pNum = [], []
for index in range(2):
pVect.append(np.log(pNumList[index] / pDenomList[index]))
pNum.append(numList[index] / float(trainNum))
return pVect, pNum # 返回属于非侮辱类、侮辱类和整个训练样本属于侮辱类的概率
'''
测试朴素贝叶斯分类器
vec2Classify:向量化的测试样本
'''
def classifyNB(vec2Classify, pVect, pNum):
# 计算公式 log(P(F1|C))+log(P(F2|C))+....+log(P(Fn|C))+log(P(C))
vpl = [] # 文档分类到各类的概率
for x in range(2):
vpl.append(sum(vec2Classify * pVect[x]) + np.log(pNum[x]))
reslist = ['非侮辱类', '侮辱类']
index = [vpl.index(res) for res in vpl if res == max(vpl)]
return reslist[index[0]] # 返回分类值
'''
朴素贝叶斯测试函数
'''
def testingNB(testVec):
dataSet, classVec = loadDataSet() # 创建实验样本
vocabList = createVocabList(dataSet) # 创建词汇表
trainMat = get_trainMat(dataSet, vocabList) # 将训练样本向量化
pVect, vpl = trainNB(trainMat, classVec) # 训练朴素贝叶斯分类器
thisone = setofwords2Vec(vocabList, testVec) # 测试样本向量化
return classifyNB(thisone, pVect, vpl)
if __name__ == '__main__':
# 测试样本1
testVec1 = ['love', 'my', 'dalmation']
print('分类结果是:', testingNB(testVec1))
# 测试样本2
testVec2 = ['stupid', 'garbage']
print('分类结果是:', testingNB(testVec2))
步骤总结
- 1. 收集数据并预处理
针对本例来说,省略了收集数据和预处理的大多数步骤。若无则应对训练集文件夹内的文件读取,并去掉多余的符号(若有停用词则删去对应的词),最后采用分词如jieba进行切分,然后进行下一步骤。 - 2. 向量化处理
将得到的训练集(和测试集)列表构建不重复的单词集合,然后向量化每一个样本列表,构建训练数据的向量集。 - 3. 训练模型
利用贝叶斯公式计算对应的词频进行相应概率计算,注意可能出现0概率现象,因此需要拉普拉斯平滑处理,即特殊初始化操作。 - 4. 测试验证模型
将测试数据同样进行向量化处理,然后计算每类概率情况,取概率最大值分类即为预测的类别。本例样本少不再进行验证准确性。
朴素贝叶斯分类调用(sklearn)
'''以上涉及获取数据、求词汇表、求训练数据集等方法省略,即替代方法trainNB、classifyNB'''
from sklearn.naive_bayes import GaussianNB
from sklearn.naive_bayes import MultinomialNB
from sklearn.naive_bayes import BernoulliNB
'''调用sklearn'''
def sklearnTest(testVec, testDoc):
# 重复步骤
dataSet, classVec = loadDataSet()
vocabList = createVocabList(dataSet)
trainMat = get_trainMat(dataSet, vocabList)
testVec = setofwords2Vec(vocabList, testVec)
X = np.array(trainMat)
Y = np.array(classVec)
testVec = np.array(testVec)
if testDoc == 'GaussianNB': # 高斯朴素贝叶斯
clf = GaussianNB()
elif testDoc == 'MultinomialNB': # 多项分布贝叶斯
clf = MultinomialNB()
elif testDoc == 'BernoulliNB': # 伯努利分布贝叶斯
clf = BernoulliNB()
clf.fit(X, Y)
# 测试
input = [testVec]
index = clf.predict(input)
resList = ['非侮辱类', '侮辱类']
return resList[index[0]]
if __name__ == '__main__':
# 测试样本1
testVec1 = ['love', 'my', 'dalmation']
print('普通朴素贝叶斯分类结果是:', testingNB(testVec1))
print('高斯朴素贝叶斯分类结果是:', sklearnTest(testVec1, 'GaussianNB'))
print('多项朴素贝叶斯分类结果是:', sklearnTest(testVec1, 'MultinomialNB'))
print('伯努利朴素贝叶斯分类结果是:', sklearnTest(testVec1, 'BernoulliNB'))
# 测试样本2
testVec2 = ['stupid', 'garbage']
print('普通朴素贝叶斯分类结果是:', testingNB(testVec2))
print('高斯朴素贝叶斯分类结果是:', sklearnTest(testVec2, 'GaussianNB'))
print('多项朴素贝叶斯分类结果是:', sklearnTest(testVec2, 'MultinomialNB'))
print('伯努利朴素贝叶斯分类结果是:', sklearnTest(testVec2, 'BernoulliNB'))
运行结果