参考教材:机器学习实战(人民邮电出版社)Peter Harrington【美】著
语言:python
软件:VS code
基于贝叶斯决策理论的分类方法
“朴素”是因为整个形式化过程只做最原始、最简单的假设。
- 优点:在数据较少的情况下任然有效,可以处理多类问题。
- 缺点:对于输入数据的准备方式较为敏感。
- 适用数据类型:标称型数据。
朴素贝叶斯的一般过程
- 收集数据:可以使用任何方法
- 准备数据:需要数值型或者布尔型数据
- 分析数据:有大量特征时,绘制特征作用不大,此时使用直方图效果更好
- 训练算法:计算不同的独立特征的条件概率
- 测试算法:计算错误率
- 使用算法:一个常见的朴素贝叶斯应用是文档分类。可以在任意的分类场景中使用朴素贝叶斯分类器,不一定非要是文本。
使用Python进行文档分类
准备数据:从文本中构建词向量
词表到向量的转换函数
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']]
classVec = [0,1,0,1,0,1] #1 is abusive, 0 not
return postingList,classVec
def createVocabList(dataSet):
vocabSet = set([]) #create empty set
for document in dataSet:
vocabSet = vocabSet | set(document) #union of the two sets
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
测试代码:
可以发现没有出现重复的单词
setOfWords2Vec()函数使用词汇表或者想要检查的所有单词作为输入,然后为其中每一个单词构建一个特征。一旦给定一篇文档,该文档就会被转换为词向量。
训练算法:从词向量计算概率
伪代码:
计算每个类别中的文档数目
对每篇训练文档:
对每个类别:
如果词条出现文档中→增加该词条的计数值
增加所有词条的计数值
对每个类别:
对每个词条:
将该词条的数目除以总词条数目得到条件概率
返回每个类别的条件概率
朴素贝叶斯分类器训练函数
def trainNB0(trainMatrix,trainCategory):
numTrainDocs = len(trainMatrix)
numWords = len(trainMatrix[0])
pAbusive = sum(trainCategory)/float(numTrainDocs)
p0Num = numpy.ones(numWords); p1Num = numpy.ones(numWords) #change to ones()
p0Denom = 2.0; p1Denom = 2.0 #change to 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 = numpy.log(p1Num/p1Denom) #change to log()
p0Vect = numpy.log(p0Num/p0Denom) #change to log()
return p0Vect,p1Vect,pAbusive
测试函数:
文档属于侮辱类的概率为0.5正确。词汇表第一个是cute在类别0中出现1次,在类别1中没有出现。对应的概率为0.04166667和0.0正确
测试算法:根据现实情况修改分类器
利用贝叶斯分类器对文档进行分类时,要计算多个概率的乘积以获得文档属于某个类别的概率。如果其中一个为0,那么最后的乘积也为0.为降低这种影响,可以将所有词的出现数初始化为1,并将分母初始化为2.。计算乘积时,由于大部分因子都很小,所以程序会下溢出或者得不到正确答案。解决办法是对乘积的自然数取自然对数。
朴素贝叶斯分类函数
def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1):
p1 = sum(vec2Classify * p1Vec) + numpy.log(pClass1) #element-wise mult
p0 = sum(vec2Classify * p0Vec) + numpy.log(1.0 - pClass1)
if p1 > p0:
return 1
else:
return 0
def testingNB():
listOPosts,listClasses = loadDataSet()
myVocabList = createVocabList(listOPosts)
trainMat=[]
for postinDoc in listOPosts:
trainMat.append(setOfWords2Vec(myVocabList, postinDoc))
p0V,p1V,pAb = trainNB0(numpy.array(trainMat),numpy.array(listClasses))
testEntry = ['love', 'my', 'dalmation']
thisDoc = numpy.array(setOfWords2Vec(myVocabList, testEntry))
print (testEntry,'classified as: ',classifyNB(thisDoc,p0V,p1V,pAb))
testEntry = ['stupid', 'garbage']
thisDoc = numpy.array(setOfWords2Vec(myVocabList, testEntry))
print (testEntry,'classified as: ',classifyNB(thisDoc,p0V,p1V,pAb))
测试函数
准备数据:文档词袋模型
我们将每个词的出现与否作为一个特征,这可以被描述为词集模型。
朴素贝叶斯词袋模型
def bagOfWords2VecMN(vocabList, inputSet):
returnVec = [0]*len(vocabList)
for word in inputSet:
if word in vocabList:
returnVec[vocabList.index(word)] += 1
return returnVec
实例:使用朴素贝叶斯过滤垃圾邮件
- 收集数据:课本所提供的文本文件
- 准备数据:将文本文件解析成词条向量
- 分析数据:检查词条确保解析的正确性
- 训练算法:使用我们之间建立的trainNB()函数
- 使用算法:构建一个完整的程序对一组文档进行分类,将错误的文档输出到屏幕上
准备数据:切分文本
输入以下代码
import re
regEx=re.compile('\\W*')
emailText=open('email/ham/6.txt')
listOfTokens=regEx.split(emailText)
测试算法:使用朴素贝叶斯进行交叉验证
文件解析及完整的垃圾邮件测试函数
def textParse(bigString): #input is big string, #output is word list
import re
listOfTokens = re.split(r'\W*', bigString)
return [tok.lower() for tok in listOfTokens if len(tok) > 2]
def spamTest():
docList=[]; classList = []; fullText =[]
for i in range(1,26):
wordList = textParse(open('email/spam/%d.txt' % i).read())
docList.append(wordList)
fullText.extend(wordList)
classList.append(1)
wordList = textParse(open('email/ham/%d.txt' % i).read())
docList.append(wordList)
fullText.extend(wordList)
classList.append(0)
vocabList = createVocabList(docList)#create vocabulary
trainingSet = range(50); testSet=[] #create test set
for i in range(10):
randIndex = int(numpy.random.uniform(0,len(trainingSet)))
testSet.append(trainingSet[randIndex])
del(trainingSet[randIndex])
trainMat=[]; trainClasses = []
for docIndex in trainingSet:#train the classifier (get probs) trainNB0
trainMat.append(bagOfWords2VecMN(vocabList, docList[docIndex]))
trainClasses.append(classList[docIndex])
p0V,p1V,pSpam = trainNB0(numpy.array(trainMat),numpy.array(trainClasses))
errorCount = 0
for docIndex in testSet: #classify the remaining items
wordVector = bagOfWords2VecMN(vocabList, docList[docIndex])
if classifyNB(numpy.array(wordVector),p0V,p1V,pSpam) != classList[docIndex]:
errorCount += 1
print ("classification error",docList[docIndex])
print ('the error rate is: ',float(errorCount)/len(testSet))
测试代码:
函数spamTest() 会输出在10封随机选择的电子邮件上的分类错误率。既然这些电子邮件是随机选择的,所以每次的输出结果可能有些差别。
如果发现错误的话,函数会输出错分文档的词表,这样就可以了解到底是哪篇文档发生了错误。如果想要更好地估计错误率,那么就应该将上述过程重复多次,比如说 10 次,然后求平均值。