定义
朴素贝叶斯:是基于贝叶斯定理和特征条件独立假设的分类方法
优点:朴素贝叶斯实现简单,学习与预测效率都很高,易于实现
理论基础
- 联合概率分布
联合概率表示为包含多个条件并且所有的条件都同时成立的概率,记作 P ( X = a , Y = b ) P(X = a,Y = b) P(X=a,Y=b)或 P ( a , b ) P(a,b) P(a,b)或 P ( a b ) P(ab) P(ab)
联合概率分布就是联合概率在样本空间中的分布情况
- 条件概率
条件概率是指事件A在事件B发生的条件下发生的概率。条件概率表示为: P ( A ∣ B ) P(A|B) P(A∣B),读作“A在B发生的条件下发生的概率”。若只有两个事件A,B,那么, P ( A ∣ B ) = P ( A B ) P ( B ) P(A|B)=\frac {P(AB)}{P(B)} P(A∣B)=P(B)P(AB)。
- 贝叶斯定理
另外一种有效计算条件概率的方法称为贝叶斯定理。贝叶斯定理告诉我们如何交换条件概率中的条件与结果:
P
(
B
I
∣
A
)
=
P
(
B
i
)
P
(
A
∣
B
i
)
∑
j
=
1
n
P
(
B
j
)
P
(
A
∣
B
j
)
P(B_I|A) = \frac {P(B_i)P(A|B_i)}{\sum_{j=1}^nP(B_j)P(A|B_j)}
P(BI∣A)=∑j=1nP(Bj)P(A∣Bj)P(Bi)P(A∣Bi)
- 贝叶斯决策理论
贝叶斯决策理论方法是统计模型决策中的一个基本方法,其基本思想是:
- 已知类条件概率密度参数表达式和先验概率
- 利用贝叶斯公式转换成后验概率
- 根据后验概率大小进行决策分类
他要求计算两个概率P1(x,y)和P2(x,y):
- 如果P1(x,y)>P2(x,y),那么属于类别1
- 如果P2(x,y)>P1(x,y),那么属于类别2
使用P1()和P2()只是为了尽可能简化描述,而真正需要计算和比较的应该是 P ( c 1 ∣ x , y ) P(c1|x,y) P(c1∣x,y)和 P ( c 2 ∣ x , y ) P(c2|x,y) P(c2∣x,y)
实例分析
- 文档的自动分类
“朴素”的含义:
- 条件独立性假设的概率:特征之间的相互独立性假设,即一个特征或者单词出现的可能性与它和其它单词相邻没有关系。
- 特征通道重要的假设
虽然这些假设都有一定的问题,但是朴素贝叶斯的实际效果却很好。
具体操作:
(1)准备数据
- 自定义一个文档
def loadDataSet():
#词条切分后的文档集合,列表每一行代表一个文档
postingList = [['my', 'dog', 'has', 'flea', 'problem', 'help', 'please'],
['maybe', 'not', 'take', 'him', 'to', 'dog', 'park', 'stupid'],
['my', 'dalmation', 'is', 'so', 'cute', 'I', 'love', 'him'],
['stop', 'posting', 'stupid', 'worthless', 'garbage'],
['my', 'licks', 'ate', 'my', 'steak', 'how', 'to', 'stop', 'him'],
['quit', 'buying', 'worthless', 'dog', 'food', 'stupid']]
classVec = [0, 1, 0, 1, 0, 1]
return postingList, classVec
- 提取文档中的词条
def createVocabList(dataSet):
#新建一个存放词条的集合
vocabSet = set([])
#遍历文档集合中的每一篇文档
for document in dataSet:
#将文档列表转为集合的形式,保证每个词条的唯一性
#然后与vocabSet取并集,向vocabSet中添加没有出现的新的词条
vocabSet = vocabSet|set(document)
#再将集合转化为列表
return list(vocabSet)
- 将文档转化成词条向量
def setOfWords2Vec(vocabSet,inputSet):
#新建一个长度为vocabSet的列表,并且各维度元素初始值为0
returnVec = [0]*len(vocabSet)
#遍历文档中的每一个词条
for word in inputSet:
#如果词条再词条列表中出现
if word in vocabSet:
returnVec[vocabSet.index(word)] = 1
else:
print('the word:%s is not in my vocabulary!'%'word')
#返回inputSet转化后的词条向量
return returnVec
(2)从词条向量中计算可能性
def trainNB0(trainMatrix,trainCategory):
#获取文档矩阵中文档的数目
numTrainDocs = len(trainMatrix)
#获取词条向量的长度
numWords = len(trainMatrix[0])
#所有文档中属于类1所占的比例p(c=1)
pAbusive = sum(trainCategory)/float(numTrainDocs)
#创建一个长度为词条向量等长的列表
#p0Num = np.zeros(numWords)
#p1Num = np.zeros(numWords)
#p0Denom = 1.0
#p1Denom = 1.0
p0Num = np.ones(numWords)
p1Num = np.ones(numWords)
p0Denom = 2.0
p1Denom = 2.0
for i in range(numTrainDocs):
#如果该词条向量对应的标签为1
if trainCategory[i] == 1:
#统计所有类别为1的词条向量中各个词条出现的次数
p1Num += trainMatrix[i]
#统计类别为1的词条向量中出现的所有词条的总数,即统计类1所有文档中出现的单词数目
p1Denom += sum(trainMatrix[i])
else:
# 统计所有类别为0的词条向量中各个词条出现的次数
p0Num += trainMatrix[i]
# 统计类别为0的词条向量中出现的所有词条的总数,即统计类0所有文档中出现的单词数目
p0Denom += sum(trainMatrix[i])
#利用Numpy数组计算P(wi|c1)
# p1Vect = p1Num / p1Denom
# p0Vect = p0Num / p0Denom
# print(p1Vect)
p1Vect = np.log(p1Num/p1Denom)
p0Vect = np.log(p0Num/p0Denom)
return p0Vect,p1Vect,pAbusive
贝叶斯分类器对文档进行分类时,要计算多个概率的乘积以获得文档属于某个类别的概率,即是计算 p ( ω 0 ∣ 1 ) p ( ω 1 ∣ 1 ) p ( ω 2 ∣ 1 ) p(\omega_0|1)p(\omega_1|1)p(\omega_2|1) p(ω0∣1)p(ω1∣1)p(ω2∣1),如果其中一个概率为0,那么最终的乘积也为0,为了降低这种影响,可以将所有词的出现次数初始化为1,将分母初始化为2。
另外一个问题时下溢出(这是由于太多的很小的数相乘,最后四舍五入就会得到0)解决办法就是去自然对数
(3)结果测试
def classifyNB(vec2Classify,p0Vec,p1Vec,pClass1):
p1 = sum(vec2Classify * p1Vec) + math.log(pClass1)
p0 = sum(vec2Classify * p0Vec) + math.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(np.array(trainMat),np.array(listClasses))
testEntry = ['love', 'my', 'dalmation']
thisDoc = np.array(setOfWords2Vec(myVocabList, testEntry))
print(testEntry,'classified as:',classifyNB(thisDoc,p0v,p1v,pAb))
testEntry = ['stupid', 'garbage']
thisDoc = np.array(setOfWords2Vec(myVocabList, testEntry))
print(testEntry, 'classified as:', classifyNB(thisDoc, p0v, p1v, pAb))
- 垃圾邮件分类
(1)构建词袋模型
def bagOfWords2VecMN(vocabList,inputSet):
returnVec = [0]*len(vocabList)
for word in inputSet:
if word in vocabList:
returnVec[vocabList.index(word)] += 1
return returnVec
(2)切分文本
使用正则表达式来切分句子,其中分割符时除了单词和数字之外的其他任意字符。
def textParse(bigString):
listOfTokens = re.split(r'\W+',bigString)
return [tok.lower() for tok in listOfTokens if len(tok)>2]
(3)算法测试
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)
trainingSet = list(range(50))
testSet = []
# 随机构建测试集
for i in range(10):
randIndex = int(np.random.uniform(0, len(trainingSet)))
testSet.append(trainingSet[randIndex])
del (trainingSet[randIndex])
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])
if classifyNB(np.array(wordVector), p0V, p1V, pSpam) != classList[docIndex]:
errorCount += 1
print("classification error", docList[docIndex])
print('the erroe rate is: ', float(errorCount) / len(testSet))
- 从广告中提取地域倾向
(1)RSS源与高频词去除
def calcMostFreq(vocabList,fullTest):
#创建新的字典
freqDict = {}
#遍历词条列表中的每一个词
for token in vocabList:
#将单词/单词出现的次数作为键值对存入字典
freqDict[token] = fullTest.count(token)
#按照键值value(词条出现的次数)对字典进行排序,由大到小
sortedFreq = sorted(freqDict.items(),key = operator.itemgetter(1),reverse=True)
#返回出现次数最多的前30个单词
return sortedFreq[:30]
def localWords(feed1,feed0):
docList = []
classList = []
fullText = []
minLen = min(len(feed1['entries']),len(feed0['entries']))
for i in range(minLen):
wordList = textParse(feed1['entries'][i]['summary'])
docList.append(wordList)
fullText.extend(wordList)
classList.append(1)
wordList = textParse(feed0['entries'][i]['summary'])
docList.append(wordList)
fullText.extend(wordList)
classList.append(0)
vocabList = createVocabList(docList)
top30Words = calcMostFreq(vocabList,fullText)
for pairW in top30Words:
if pairW[0] in vocabList:
vocabList.remove(pairW[0])
trainingSet = range(2*minLen)
testSet = []
for i in range(20):
randIndex = int(np.random.uniform(0,len(trainingSet)))
testSet.append(trainingSet[randIndex])
del(trainingSet[randIndex])
trainMat = []
trainClasses = []
for docIndex in trainingSet:
trainMat.append(bagOfWords2VecMN(vocabList, docList[docIndex]))
trainClasses.append(classList[docIndex])
p0v,p1v,pSpam = trainNB0(np.array(trainMat),np.array(trainClasses))
errorCount = 0
for docIndex in testSet:
wordVector = bagOfWords2VecMN(vocabList,docList[docIndex])
if classifyNB(np.array(wordVector),p0v,p1v,pSpam) != classList[docIndex]:
errorCount += 1
print('the error rate is :', float(errorCount)/len(testSet))
return vocabList,p0v,p1v
(2)提取地域相关名词
#最具表征性的词汇显示函数
def getTopWords(ny,sf):
import operator
#利用RSS源分类器获取所有出现的词条列表,以及每个分类中每个单词出现的概率
vocabList,p0V,p1V=localWords(ny,sf)
#创建两个元组列表
topNY=[];topSF=[]
#遍历每个类中各个单词的概率值
for i in range(len(p0V)):
#往相应元组列表中添加概率值大于阈值的单词及其概率值组成的二元列表
if(p0V[i]>-6.0):topSF.append((vocabList[i],p0V[i]))
if(p1V[i]>-6.0):topNY.append((vocabList[i],p1V[i]))
#对列表按照每个二元列表中的概率值项进行排序,排序规则由大到小
sortedSF=sorted(topSF,key=lambda pair:pair[1],reverse=True)
print('SF**SF**SF**SF**SF**SF**SF**SF**SF**SF**SF**SF**')
#遍历列表中的每一个二元条目列表
for item in sortedSF:
#打印每个二元列表中的单词字符串元素
print(item[0])
#解析同上
sortedNY=sorted(topNY,key=lambda pair:pair[1],reverse=True)
print('SF**SF**SF**SF**SF**SF**SF**SF**SF**SF**SF**SF**')
for item in sortedNY:
print(item[0])
(3)测试
(由于网络问题,很大可能会报错。)
ny = feedparser.parse('http://newyork.craiglist.org/stp/index.rss')
sf = feedparser.parse('http://sfbay.craiglist.org/stp/index.rss')
vocabList, pSF, pNY = localWords(ny, sf)
总结
- 使用概率比硬规则概率比硬规则更为有效
- 朴素贝叶斯:独立性假设
- 下溢出 → \rightarrow →概率取对数
- 词集模型、词袋模型
- 朴素贝叶斯的基本假设是朴素贝叶斯的基本假设是:条件独立性,这是一个较强的假设。由于这一假设,模型包含的条件概率数量大为减少,朴素贝叶斯的学习和预测大为简化。高效、易于实现,缺点是分类性能不一定高
- 假设输入变量都是条件独立的条件独立的,如果它们之间
存在概率依赖关系,模型就变成了贝叶斯网络贝叶斯网络
基于scikit-learn的朴素贝叶斯
手写数据集的分类
#导⼊入需要的模块和库
from sklearn.naive_bayes import MultinomialNB
from sklearn import datasets
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
if __name__ == "__main__":
iris = datasets.load_digits()
x = iris.data
y = iris.target
x_train,x_test,y_train,y_test = train_test_split(x,y,test_size = 0.3,random_state = 0)
# 训练数据和测试数据进行标准化
sc = StandardScaler()
sc.fit(x_train)
x_train_std = sc.transform(x_train)
x_test_std = sc.transform(x_test)
# 建立一个模型
clf = MultinomialNB()
clf = clf.fit(x_train, y_train)
score = clf.score(x_test, y_test) # 返回预测的准确度
print(score)