朴素贝叶斯法是一种基于贝叶斯定定理和特征条件独立假设的分类方法。对于给定的训练数据集,首先基于特征条件独立假设学习输入/输出的联合概率分布;然后基于此模型,对于给定的输入$x$,利用贝叶斯定理求出后验概率最大输出的$y$。朴素贝叶斯法对条件概率分布作了条件独立假设,朴素贝叶斯法因为这个较强的假设而得名。
一、基本方法
假设输入空间$x\in \mathbb{R}^{n}$是n维向量的集合,输出空间为类别标签的集合$y= \left \{ c_{1},c_{2},...,c_{k} \right \}$,输入为特征向量$x\in \chi $,输出为类别标签$y\in \gamma$。$X$是定义在输入空间$\chi$上的随机向量。$Y$是定义在输出空间$\gamma$上的随机变量。$P\left ( X,Y \right )$是X和Y的联合概率分布。训练数据集:
$T=\left \{ \left ( x_{1}, y_{1} \right ), \left ( x_{2}, y_{2} \right ),...,\left ( x_{N}, y_{N} \right ) \right \}$
由$P\left ( X,Y \right )$独立同分布产生。
朴素贝叶斯法通过训练数据集学习联合概率分布$P\left ( X,Y \right )$。具体地,学习以下先验概率分布以及条件概率分布。
先验概率分布:
$P(Y=c_{k}),k=1,2,...,K$
条件概率分布:
$P(X=x|Y=c_{k})=P(X^{(1)}=x^{(1)},...,X^{(n)}=x^{(n)}|Y=c_{k}),k=1,2,...,K$ (1)
于是由条件概率可以学习到联合概率分布。
朴素贝叶斯法对条件概率分布作了独立性条件假设。因为这是一个较强的假设,朴素贝叶斯法也因此得名。具体地,条件独立性假设为:
$P(X=x|Y=c_{k})=P(X^{(1)}=x(1),...,X^{(n)}=x(n))$
$=\prod_{j=1}^{n}P(X^{(j)}=x^{(j)}|Y=c_{k})$ (2)
朴素贝叶斯法分类时,对给定的输入$x$,通过学习到的模型计算后验概率分布$P(Y=c_{k}|X=x)$,将后延概率最大的类作为$x$的类输出。后验概率计算根据贝叶斯定理(分母由全概率公式得到):
$P(Y=c_{k}|X=x)=\frac{P(X=x|Y=c_{k})P(Y=c_{k})}{\sum P(X=x|Y=c_{k})P(Y=c_{k})}$ (3)
将式(2)带入式(3),并且由全概率公式得:
$P(Y=c_{k}|X=x)=\frac{P(Y=c_{k})\prod P(X^{(j)}=x^{(j)}|Y=c_{k})}{\sum P(Y=c_{k})\prod P(X^{(j)=x^{(j)}}|Y=c_{k})},k=1,2,...,K$ (4)
这就是朴素贝叶斯法分类的基本公式。朴素贝叶斯分类器可表示为:
$y=argmax\frac{P(Y=c_{k})\prod P(X^{(j)}=x^{(j)}|Y=c_{k})}{\sum P(Y=c_{k})\prod P(X^{(j)=x^{(j)}}|Y=c_{k})}$ (5)
因为式(5)中分母对所有的$c_{k}$都是相同的,所以
$y=argmax{P(Y=c_{k})\prod P(X^{(j)}=x^{(j)}|Y=c_{k})}$
二、实践
朴素贝叶斯在机器学习中的一个重要应用就是文档分类。我们将把文本看成是单词向量,即将句子转换成向量。考虑出现在所有文档中的所有单词,再决定将哪些词纳入词汇表或者说所要的词汇集合,然后必须将每一篇文档转换为词汇表上的向量。
2.1词表转换为向量
# 加载训练样本:包括文档集合以及文档所属的类别 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] return postingList, classVec # 创建样本文档中所有不重复的单词列表 def createVocabList(dataSet): vocabSet = set([]) for document in dataSet: vocabSet = vocabSet | set(document) 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
计算概率
由贝叶斯公式:
$p(c_{i}|w)=\frac{p(w|c_{i})p(c_{i})}{p(w)}$ (1)
式中,$w$为由多个数值组成的向量。在此例中,数值的个数和词汇表中的词个数相同。
对于每个类别计算该值,然后比较概率的大小。如何计算:
首先通过类别$i$中文档数除以总的文档个数来计算$p(c_{i})$。接下来计算$p(w|c_{i})$。因为是朴素贝叶斯算法,所以可以将$w$展开成独立特征,可以写成:
$p(w|c_{i})=p(w_{0},w_{1},...,w_{n}|c_{i})=p(w_{0}|c_{i})p(w_{1}|c_{i})...p(w_{n}|c_{i})$ (2)
根据以上分析,可以使用一下伪代码实现:
计算每个类别中的文档个数
对每篇训练文档:
对每个类别:
如果词条出现在该文档中->增加该词条的计数值
增加所有词条的计数值
对每个类别:
对每个词条:
将该词条的数目除以总词条数目得到条件概率
返回每个类别的条件概率
import numpy as np def trainNB0(trainMatrix, trainCategory): numTrainDocs = len(trainMatrix) numWords = len(trainMatrix[0]) # 数目为转换成词向量中,是一个二维数组,每一维度的长度都一致 pAbusive = sum(trainCategory)/float(numTrainDocs) # 二分类,计算类别为1的概率 p0Num = np.zeros(numWords) p1Num = np.zeros(numWords) p0Denom = 2.0 p1Denom = 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]) p1Vec = np.log(p1Num/p1Denom) p0Vec = np.log(p0Num/p0Denom) return p0Vec, p1Vec, pAbusive
上述函数中,为了防止概率乘积为0的情况出现,上述p0Num和p1Num使用ones初始化,且p0Denom和p1Denom设置为2。另外一个情况就是概率乘积时,大部分概率都是很小的数值,相乘之后会得不到正确的结果。因此,这里对概率取对数,如函数中的p0Vec、p1Vec。
贝叶斯分类函数
def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1): p1 = sum(vec2Classify * p1Vec) + np.log(pClass1) p0 = sum(vec2Classify * p0Vec) + np.log(1-pClass1) if p1 > p0: return 1 else: return 0
因为在上面概率取对数进行计算,因此概率公式得到(乘积的对数等于对数的加法):
p1 = sum(vec2Classify * p1Vec) + math.log(pClass1)
测试函数
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 = array(setOfWords2Vec(myVocabList, testEntry)) print(testEntry, 'classified as:', classifyNB(thisDoc, p0V, p1V, pAb))
测试结果:
['love', 'my', 'dalmation'] classified as: 0