如果觉得本篇文章对您的学习起到帮助作用,请 点赞 + 关注 + 评论 ,留下您的足迹💪💪💪
本篇文章为我对机器学习实战-朴素贝叶斯的理解与我在学习时所做笔记,一是为了日后查找方便并加深对代码的理解,二是希望能帮助到使用这本书遇到困难的人。
代码可在python3.7跑通
因此代码相对原书做了一些修改,增加了可读性,同时也解决了一些问题。
代码及详细注释如下:
import re
import numpy as np
def textParse(bigString):
'''
# 接受大字符串,并解析成为字符串列表。去掉字符少于2个的字符串,并将所有字符串转化为小写
:param bigString:
:return:
'''
# 此处 r'\W+' 与书中不一致
listOfTokens = re.split(r'\W+', bigString)
return [tok.lower() for tok in listOfTokens if len(tok) > 2]
def createVocabList(dataset):
'''
# 创建一个列表,用来记录无重复词条
:param dataset:
:return:
'''
# 建立集合,用于过滤掉重复的词条
vocabSet = set()
for document in dataset:
# 列表是不可以进行并操作的,集合可以
vocabSet = vocabSet | set(document)
# 转化为列表,但是此列表是没有顺序的
# 加了sorted() 排序后,列表是有顺序的
return sorted(list(vocabSet))
def setOfWordsVec(vocabList, inputSet):
'''
# 创建与词汇表等长向量,查看输入文档是否在词汇表中出现,出现了则在相应位置置1,否则保持0
:param vocabList:词汇表
:param inputSet:某个文档
:return:
'''
# 创建同 vocabList 词汇表等长,元素全为0的列表
returnVec = [0] * len(vocabList)
for word in inputSet:
if word in vocabList:
# 词集模型,index() 函数用于从列表中找出某个值第一个匹配项的索引位置
# returnVec[vocabList.index(word)] = 1
# # 词袋模型,可以返回单词出现的次数
returnVec[vocabList.index(word)] += 1
else:
print('单词 %s 不在我的词典里!' % word)
return returnVec
def trainNB0(trainMatrix, trainCategory):
'''
# 朴素贝叶斯分类器训练函数
:param trainMatrix: 训练文档矩阵,即setOfWordsVec返回的returnVec构成的 0,1 列表
多个0,1列表即可构成数据矩阵
:param trainCategory: 训练类别标签向量,即loadDataset返回的标签向量classVec
:return:
p0Vect:侮辱类的条件概率组数
p1Vect:非侮辱类的条件概率组数
pAbusive:文档属于侮辱类的概率,为先验3概率
'''
# 计算文档数目
numTrainDocs = len(trainMatrix)
# 计算无重复词汇表的单词数目
numWords = len(trainMatrix[0])
# 先验概率,侮辱性文档所占概率
pAbusive = sum(trainCategory) / float(numTrainDocs)
# 改进,防止出现概率为0的情况
p0Num = np.ones(numWords);p1Num = np.ones(numWords)
# lamda=1, numWords=n*lamda,为了保证概率加和为1
p0Denom = float(numWords); p1Denom = float(numWords)
for i in range(numTrainDocs):
if trainCategory[i] == 1:
# 计算侮辱性文档内各个单词出现的次数,p1Num是一个向量
p1Num += trainMatrix[i]
# 侮辱性文档不同词条的个数
p1Denom += sum(trainMatrix[i])
else:
# 计算非侮辱性文档内各个单词出现的次数
p0Num += trainMatrix[i]
# 非侮辱性文档不同词条的个数
p0Denom += sum(trainMatrix[i])
# 由于概率都是小于1的,多个数值相乘后会非常小,为了防止数值下溢出而得不到正确答案,使用log计算
p1Vect = np.log(p1Num / p1Denom)
p0Vect = np.log(p0Num / p0Denom)
return p0Vect, p1Vect, pAbusive
def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1):
'''
# 判断新邮件是否属于垃圾文件
:param vec2Classify: 需要预测邮件的词条分布
:param p0Vec: 非侮辱性各个词条出现的条件概率
:param p1Vec: 侮辱性各个词条出现的条件概率
:param pClass1: 侮辱性先验概率
:return:
'''
# 连乘加log变成相加
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 spamText():
docList = []; classList = []; fullText = []
# 读取每一个垃圾文件,大字符串转化为字符列表
for i in range(1, 26):
with open('email//spam//%d.txt' % i, encoding="ISO-8859-1") as frSpam:
wordList = textParse(frSpam.read())
# 弄明白 append() 和 extend() 的区别就可明白
docList.append(wordList)
fullText.extend(wordList)
# 垃圾邮件类别添加 1
classList.append(1)
with open('email//ham//%d.txt' % i, encoding="ISO-8859-1") as frHam:
wordList = textParse(frHam.read())
# 弄明白 append() 和 extend() 的区别就可明白
docList.append(wordList)
fullText.extend(wordList)
# 非垃圾邮件类别添加 0
classList.append(0)
print(docList)
# 生成无重复词条列表
vocabList = createVocabList(docList)
# 创建训练数据集索引列表
trainingSet = list(range(50))
# 创建测试数据集索引列表
testSet = []
for i in range(10):
# 0-50 不包含50,随机取数,int()取整作索引
randIndex = int(np.random.uniform(0, len(trainingSet)))
# 随机选取50封邮件的其中10件,索引存放于 testSet 中
testSet.append(trainingSet[randIndex])
# 删除属于测试集的索引
del(trainingSet[randIndex])
trainMat = []; trainClasses = []
for docIndex in trainingSet:
trainMat.append(setOfWordsVec(vocabList, docList[docIndex]))
trainClasses.append(classList[docIndex])
p0V, p1V, pSpam = trainNB0(trainMat, trainClasses)
errorCount = 0.0
for docIndex in testSet:
wordVector = setOfWordsVec(vocabList, docList[docIndex])
if classifyNB(wordVector, p0V, p1V, pSpam) != classList[docIndex]:
errorCount += 1
errorRate = errorCount / len(testSet)
print('错误率为:',errorRate)
if __name__ == '__main__':
spamText()
希望文章内容可以帮助到你,快来动手敲代码吧!!