失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > 机器学习监督学习之分类算法---朴素贝叶斯代码实践

机器学习监督学习之分类算法---朴素贝叶斯代码实践

时间:2024-04-28 10:25:46

相关推荐

机器学习监督学习之分类算法---朴素贝叶斯代码实践

目录

1. 言论过滤器1.1 项目描述1.2 朴素贝叶斯 工作原理:1.2.1 词条向量1.3 开发流程:1.4 代码实现1.4.1 创建样本1.4.2 构建词汇表,用于建立词集向量1.4.3 构建词集向量1.4.4 构建训练函数:求解相关条件概率,先验概率1.4.5 分类测试函数:返回概率大的分类结果1.4.6 main()函数1.4.7 运行结果1.4.8 存在问题1.5 平滑处理:三模型之一:多项式模型1.6 解决下溢出1.7 调整代码传送门

朴素贝叶斯分类常用于文本分类,尤其是对于英文等语言来说,分类效果很好。它常用于垃圾文本过滤、情感预测、推荐系统等

1. 言论过滤器

1.1 项目描述

以在线社区留言为例。为了不影响社区的发展,我们要屏蔽侮辱性的言论,所以要构建一个快速过滤器,如果某条留言使用了负面或者侮辱性的语言,那么就将该留言标志为内容不当。

过滤这类内容是一个很常见的需求,对此问题建立两个类型:侮辱类和非侮辱类,使用1和0分别表示

1.2 朴素贝叶斯 工作原理:

提取所有文档中的词条并进行去重获取文档的所有类别计算每个类别中的文档数目对每篇训练文档: 对每个类别: 如果词条出现在文档中-->增加该词条的计数值(for循环或者矩阵相加)增加所有词条的计数值(此类别下词条总数)对每个类别: 对每个词条: 将该词条的数目除以总词条数目得到的条件概率(P(词条|类别))返回该文档属于每个类别的条件概率(P(类别|文档的所有词条))

1.2.1 词条向量

词向量:一般是一个布尔类型的集合,该集合中每个元素都表示其对应的单词是否在文档中出现。比如说,词汇表只有三个单词:‘apple’, ‘orange’, ‘melo’,某文档中,apple和melo出现过,那么其对应的词向量就是 {1, 0, 1}。

这种模型通常称为词集模型,如果词向量元素是整数类型,每个元素表示相应单词在文档中出现的次数(0表示不出现),那这种模型就叫做词袋模型

词集模型(Set Of Words): 单词构成的集合,集合自然每个元素都只有一个,也即词集中的每个单词都只有一个。词袋模型(Bag Of Words): 如果一个单词在文档中出现不止一次,并统计其出现的次数(频数)。

1.3 开发流程:

收集数据: 可以使用任何方法准备数据: 从文本中构建词向量分析数据: 检查词条确保解析的正确性训练算法: 从词向量计算概率测试算法: 根据现实情况修改分类器使用算法: 对社区留言板言论进行分类

1.4 代码实现

1.4.1 创建样本

"""函数说明:创建实验样本Parameters:无Returns:postingList - 实验样本切分的词条classVec - 类别标签向量"""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']]#类别标签向量,1代表使用了侮辱性语言,0代表未使用了侮辱性语言classVec = [0,1,0,1,0,1]return postingList,classVec

1.4.2 构建词汇表,用于建立词集向量

"""函数说明:将切分的实验样本词条整理成不重复的词条列表,也就是词汇表Parameters:dataSet - 整理的样本数据集Returns:vocabSet - 返回不重复的词条列表,也就是词汇表"""def createVocabList(dataSet):vocabSet = set([]) #创建一个空的不重复列表for document in dataSet: # list不支持取并集,而set支持 vocabSet = vocabSet | set(document) # 取并集 return list(vocabSet)

1.4.3 构建词集向量

"""函数说明:根据vocabList词汇表,将inputSet向量化,向量的每个元素为1或0Parameters:inputSet - 切分的词条列表vocabList - createVocabList返回的列表Returns:returnVec - 文档向量,词集模型"""def setOfWords2Vec(inputSet,vocabList):#创建一个其中所含元素都为0的向量returnVec = [0] * len(vocabList) #遍历每个词条 for word in inputSet: #如果词条存在于词汇表中,则置1 if word in vocabList: returnVec[vocabList.index(word)] = 1else: print("the word: %s is not in my Vocabulary!" % word)return returnVec

1.4.4 构建训练函数:求解相关条件概率,先验概率

"""函数说明:朴素贝叶斯分类器训练函数Parameters:trainMatrix - 训练文档矩阵,即setOfWords2Vec返回的returnVec构成的矩阵trainCategory - 训练类别标签向量,即loadDataSet返回的classVecReturns:p0Vect - 侮辱类的条件概率数组p1Vect - 非侮辱类的条件概率数组pAbusive - 文档属于侮辱类的概率"""def trainNB0(trainMatrix,trainCategory):# 计算训练的文档数目 : 6numTrainDocs = len(trainMatrix) # 计算每篇文档的词条数numWords = len(trainMatrix[0]) # 文档属于侮辱类的概率 # [0,1,0,1,0,1]:即侮辱类和非侮辱类各占 1/2 pAbusive = sum(trainCategory) / float(numTrainDocs) # 创建numpy.zeros数组,词条出现数初始化为0 p0Num = np.zeros(numWords); p1Num = np.zeros(numWords) # 分母初始化为0 p0Denom = 0.0; p1Denom = 0.0 # 遍历各篇文章 for i in range(numTrainDocs):# 统计属于侮辱类的条件概率所需的数据,即P(w0|1),P(w1|1),P(w2|1)···if trainCategory[i] == 1: # 计算在侮辱类这个盒子中各个类别的数量,没有即为0 p1Num += trainMatrix[i]# 计算在侮辱类这个盒子中所有单词数量p1Denom += sum(trainMatrix[i])#统计属于非侮辱类的条件概率所需的数据,即P(w0|0),P(w1|0),P(w2|0)···else: p0Num += trainMatrix[i]p0Denom += sum(trainMatrix[i])p1Vect = p1Num/p1Denom p0Vect = p0Num/p0Denom #返回属于 侮辱类的条件概率数组,属于非侮辱类的条件概率数组,文档属于侮辱类的概率return p0Vect,p1Vect,pAbusive

1.4.5 分类测试函数:返回概率大的分类结果

"""函数说明:朴素贝叶斯分类器分类函数Parameters:vec2Classify - 待分类的词条数组p0Vec - 侮辱类的条件概率数组p1Vec -非侮辱类的条件概率数组pClass1 - 文档属于侮辱类的概率Returns:0 - 属于非侮辱类1 - 属于侮辱类"""def classifyNB0(vec2Classify, p0Vec, p1Vec, pClass1):# reduce从左到右对一个序列的项累计地应用有两个参数的函数,以此合并序列到一个单一值。# reduce(lambda x, y: x * y, [1, 2, 3, 4, 5]),其实就是((((1*2)*3)*4)*5)=120# p(y/w)=p(ws/y)*p(y) / p(w)# p(ws/y)=p(w1/y)*p(w2/y)*...*p(wn/y)p1 = reduce(lambda x,y : x * y, vec2Classify * p1Vec) * pClass1 p0 = reduce(lambda x,y : x * y, vec2Classify * p0Vec) * (1 - pClass1)# 由于相乘的概率向量中存在零元素,则多个概率乘积也为零,即以上验证方法存在重大缺陷# 后面会提出修改方案print('p0:', p0)print('p1', p1)if p1 > p0:return 1else:return 0

1.4.6 main()函数

def main():postingList, classVec = loadDataSet()print('postingList:\n',postingList)myVocabList = createVocabList(postingList)# myVocabList shape: (32,)print('myVocabList:\n',myVocabList)# trainMat是所有的词条向量组成的列表。# 它里面存放的是根据myVocabList向量化的词条向量# 记录每篇文档单词在词条中的位置trainMat = []for postinDoc in postingList:trainMat.append(setOfWords2Vec(postinDoc,myVocabList))# trainMat shape:# (6, 32)print('trainMat:\n', np.array(trainMat))p0V, p1V, pAb = trainNB0(trainMat, classVec)print('p0V:\n', p0V)print('p1V:\n', p1V)print('classVec:\n', classVec)print('pAb:\n', pAb)testEntry = ['love', 'my', 'dalmation'] # 测试样本1thisDoc = np.array(setOfWords2Vec(myVocabList, testEntry)) # 测试样本向量化if classifyNB0(thisDoc, p0V, p1V, pAb):print(testEntry, '属于侮辱类') # 执行分类并打印分类结果else:print(testEntry, '属于非侮辱类') # 执行分类并打印分类结果#测试样本2testEntry = ['stupid', 'garbage']# 测试样本向量话thisDoc = np.array(setOfWords2Vec(testEntry,myVocabList))if classifyNB0(thisDoc,p0V,p1V,pAb):print(testEntry,'属于侮辱类')else:print(testEntry,'属于非侮辱类')if __name__ == '__main__':main()

1.4.7 运行结果

1.4.8 存在问题

可以看出,侮辱类和非侮辱类的概率均为零,上述代码问题主要在于:1、某些单词属于对应某类别概率为0,导致整体乘积为0,例如“stupid”;解决方法:做平滑处理2、下溢出(underflow)解决方法:取对数

1.5 平滑处理:三模型之一:多项式模型

1.6 解决下溢出

下溢出是由于太多很小的数相乘造成的。学过数学的人都知道,两个小数相乘,越乘越小,这样就造成了下溢出。在程序中,在相应小数位置进行四舍五入,计算结果可能就变成0了。为了解决这个问题,对乘积结果取自然对数。通过求对数可以避免下溢出或者浮点数舍入导致的错误。同时,采用自然对数进行处理不会有任何损失。下图给出函数f(x)和ln(f(x))的曲线:

检查这两条曲线,会发现它们在相同区域内同时增加或减少,并且在相同点上取到极值。它们的取值虽然不同,但不影响最终结果

1.7 调整代码

"""函数说明:朴素贝叶斯分类器训练函数Parameters:trainMatrix - 训练文档矩阵,即setOfWords2Vec返回的returnVec构成的矩阵trainCategory - 训练类别标签向量,即loadDataSet返回的classVecReturns:p0Vect - 侮辱类的条件概率数组p1Vect - 非侮辱类的条件概率数组pAbusive - 文档属于侮辱类的概率"""def trainNB(trainMatrix,trainCategory):# 计算训练的文档数目 : 6numTrainDocs = len(trainMatrix) # 计算每篇文档的词条数numWords = len(trainMatrix[0]) # 文档属于侮辱类的概率 # [0,1,0,1,0,1]:即侮辱类和非侮辱类各占 1/2 pAbusive = sum(trainCategory) / float(numTrainDocs) ''' # 创建numpy.zeros数组,词条出现数初始化为0 # p0Num = np.zeros(numWords); p1Num = np.zeros(numWords) # 分母初始化为0 # p0Denom = 0.0; p1Denom = 0.0 '''# Laplace平滑α=1,总类别数为 2 (侮辱类,非侮辱类)# 分子初始化为1p0Num = np.ones(numWords); p1Num = np.ones(numWords)# 分母初始化调整为2:p0Denom = 2.0; p1Denom = 2.0 # 遍历各篇文章 for i in range(numTrainDocs):# 统计属于侮辱类的条件概率所需的数据,即P(w0|1),P(w1|1),P(w2|1)···if trainCategory[i] == 1: # 计算在侮辱类这个盒子中各个类别的数量,没有即为0 p1Num += trainMatrix[i]# 计算在侮辱类这个盒子中所有单词数量和,即盒子内总数量p1Denom += sum(trainMatrix[i])#统计属于非侮辱类的条件概率所需的数据,即P(w0|0),P(w1|0),P(w2|0)···else: p0Num += trainMatrix[i]p0Denom += sum(trainMatrix[i])'''# p1Vect = p1Num/p1Denom # p0Vect = p0Num/p0Denom '''# 取对数防止下溢出p1Vect = np.log(p1Num/p1Denom) p0Vect = np.log(p0Num/p0Denom)#返回属于 侮辱类的条件概率数组,属于非侮辱类的条件概率数组,文档属于侮辱类的概率return p0Vect,p1Vect,pAbusive """函数说明:朴素贝叶斯分类器分类函数Parameters:vec2Classify - 待分类的词条数组p0Vec - 侮辱类的条件概率数组p1Vec -非侮辱类的条件概率数组pClass1 - 文档属于侮辱类的概率Returns:0 - 属于非侮辱类1 - 属于侮辱类"""def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1):'''# reduce从左到右对一个序列的项累计地应用有两个参数的函数,以此合并序列到一个单一值。# reduce(lambda x, y: x * y, [1, 2, 3, 4, 5]),其实就是((((1*2)*3)*4)*5)=120# p(y/w)=p(ws/y)*p(y) / p(w)# p(ws/y)=p(w1/y)*p(w2/y)*...*p(wn/y)# p1 = reduce(lambda x,y : x * y, vec2Classify * p1Vec) * pClass1 # p0 = reduce(lambda x,y : x * y, vec2Classify * p0Vec) * (1 - pClass1)'''p1=sum(vec2Classify * p1Vec)+np.log(pClass1)p0=sum(vec2Classify * p0Vec)+np.log(1-pClass1)print('p0:', p0)print('p1:', p1)if p1 > p0:return 1else:return 0

运行结果:

传送门

机器学习实战教程(四):朴素贝叶斯基础篇之言论过滤器

朴素贝叶斯理论推导与三种常见模型

朴素贝叶斯|屏蔽社区留言板的侮辱性言论项目汇总

朴素贝叶斯(Naive Bayes)

朴素贝叶斯的应用----文档分类、垃圾邮件过滤

NLP基础——词集模型(SOW)和词袋模型(BOW)

python3中 reduce 方法的使用

operands could not be broadcast together with shapes

如果觉得《机器学习监督学习之分类算法---朴素贝叶斯代码实践》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。