朴素贝叶斯分类是一种常用的分类算法,他根据研究对象的某些特征,来推断出该研究对象属于该研究领域的哪个类别。
1. 概述
要了解朴素贝叶斯分类,就需要先知道贝叶斯分类定理,这就离不开条件概率,下面概率论中经典的条件概率公式:
P
(
Y
∣
X
)
=
P
(
X
∣
Y
)
P
(
Y
)
P
(
X
)
P(Y|X) = \frac{{P(X|Y)P(Y)}}{{P(X)}}
P(Y∣X)=P(X)P(X∣Y)P(Y)根据上面的概率公式,下面我就举个例子简单的描述贝叶斯分类的工作原理。
例:某学校男性打篮球的概率为 2/3 ,女性打篮球的概率为 1/3 ,并且该学校中男女比例通常为 2:1 ,问题:若你在学校里随机遇到一个打篮球的人,请问他的性别为男性或女性的概率分别为多少?
由上面的条件可以得到:
P
(
篮
球
∣
男
)
=
2
3
P(篮球|男){\text{ = }}\frac{2}{3}
P(篮球∣男) = 32,
P
(
篮
球
∣
女
)
=
1
3
P(篮球|女){\text{ = }}\frac{1}{3}
P(篮球∣女) = 31,
P
(
男
)
=
2
3
P(男){\text{ = }}\frac{2}{3}
P(男) = 32,
P
(
女
)
=
1
3
P(女){\text{ = }}\frac{1}{3}
P(女) = 31,所以:
P ( 篮 球 ) = P ( 篮 球 ∣ 男 ) P ( 男 ) + P ( 篮 球 ∣ 女 ) P ( 女 ) = 2 3 ∗ 2 3 + 1 3 ∗ 1 3 = 5 9 P(篮球){\text{ = }}{P(篮球|男)P(男)}+{P(篮球|女)P(女)} =\frac{2}{3}*\frac{2}{3}+\frac{1}{3}*\frac{1}{3}=\frac{5}{9} P(篮球) = P(篮球∣男)P(男)+P(篮球∣女)P(女)=32∗32+31∗31=95
P ( 男 ∣ 篮 球 ) = P ( 篮 球 ∣ 男 ) P ( 男 ) P ( 篮 球 ) = 2 3 ∗ 2 3 5 9 = 4 5 P(男|篮球){\text{ = }}\frac{P(篮球|男)P(男)}{P(篮球)}=\frac{\frac{2}{3}*\frac{2}{3}}{\frac{5}{9}}=\frac{4}{5} P(男∣篮球) = P(篮球)P(篮球∣男)P(男)=9532∗32=54
P ( 女 ∣ 篮 球 ) = P ( 篮 球 ∣ 女 ) P ( 女 ) P ( 篮 球 ) = 1 3 ∗ 1 3 5 9 = 1 5 P(女|篮球){\text{ = }}\frac{P(篮球|女)P(女)}{P(篮球)}=\frac{\frac{1}{3}*\frac{1}{3}}{\frac{5}{9}}=\frac{1}{5} P(女∣篮球) = P(篮球)P(篮球∣女)P(女)=9531∗31=51
也就是说,在这个校园里,知道打篮球的前提下,这个人是男性的概率是
4
5
\frac{4}{5}
54 ,是女性的概率是
1
5
\frac{1}{5}
51 。如果问题是“判断该人是男性还是女性”,此问题就是一个分类问题。由于上面计算出来概率是男性的概率大于是女性的概率,即由于
P
(
男
∣
篮
球
)
>
P
(
女
∣
篮
球
)
P(男|篮球) > P(女|篮球)
P(男∣篮球)>P(女∣篮球),那么我们就可以将其分类为男性。
这就是一个简单的贝叶斯分类器,在数据的基础上,根据找到的一些特征属性,来计算各个类别的概率,找到概率最大的类,从而实现分类。
2. 定义
朴素贝叶斯是基于贝叶斯定理和特征条件独立假设的分类方法,假设训练样本的属性集为
(
x
i
,
y
i
)
({x_i},{y_i})
(xi,yi),共有
c
i
c_i
ci个属性,则朴素贝叶斯定理:
P
(
c
i
∣
x
,
y
)
=
p
(
x
,
y
∣
c
i
)
p
(
c
i
)
p
(
x
,
y
)
P({c_i}|x,y) = \frac{{p(x,y|{c_i})p({c_i})}}{{p(x,y)}}
P(ci∣x,y)=p(x,y)p(x,y∣ci)p(ci) 使用上面定义,可以定义贝叶斯分类准则为: 如果
P
(
c
1
∣
x
,
y
)
>
P
(
c
2
∣
x
,
y
)
P({c_1}|x,y)> P({c_2}|x,y)
P(c1∣x,y)>P(c2∣x,y), 那么属于类别
c
1
{c_1}
c1; 如果
P
(
c
2
∣
x
,
y
)
>
P
(
c
1
∣
x
,
y
)
P({c_2}|x,y)> P({c_1}|x,y)
P(c2∣x,y)>P(c1∣x,y), 那么属于类别
c
2
{c_2}
c2。
朴素贝叶斯比起真正的贝叶斯只是多了两个假设条件来降低了复杂度:
假设1,多个特性之间相互独立,特性之间没有关联关系也不会相互影响。
假设2,特性之间地位相同
3. 工作原理
- 提取所有文档中的词条并进行去重
- 获取文档的所有类别
- 计算每个类别中的文档数目
- 对每篇训练文档:
对每个类别:
如果词条出现在文档中–>增加该词条的计数值(for循环或者矩阵相加)
增加所有词条的计数值(此类别下词条总数) - 对每个类别:
对每个词条:
将该词条的数目除以总词条数目得到的条件概率(P(词条|类别)) - 返回该文档属于每个类别的条件概率(P(类别|文档的所有词条))
4. 优缺点
优点:
1. 朴素贝叶斯模型发源于古典数学理论,有着坚实的数学基础,以及稳定的分类效率
2. 对大数量训练和查询时具有较高的速度。即使使用超大规模的训练集,针对每个项目通常也只会有相对较少的特征数,并且对项目的训练和分类也仅仅是特征概率的数学运算而已
3. 对小规模的数据表现很好,能个处理多分类任务,适合增量式训练(即可以实时的对新增的样本进行训练)
4. 对缺失数据不太敏感,算法也比较简单,常用于文本分类
5. 朴素贝叶斯对结果解释容易理解
缺点:
1. 需要计算先验概率
2. 分类决策存在错误率
3. 对输入数据的表达形式很敏感
4. 由于使用了样本属性独立性的假设,所以如果样本属性有关联时其效果不好
5. 主要应用
欺诈检测、 邮件拦截、 文章分类、情感分析、人脸识别
6. 案例分析
参考《机器学习实战》,通过算法来判断某篇文章是否是侮辱性的
- 搜集数据
这里我们构造自己的词表。
def loadDataSet():
postingList = [['落叶', '帅'],
['傻逼'],
['皮蛋', '瘦肉粥', '美味'],
['智障'],
['生活', '潇洒'],
['白痴']]
classVec = [0, 1, 0, 1, 0, 1] #1 侮辱言论 0正常言论
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) #创建一个填充值全部为0的向量
for word in inputSet:
if word in vocabList:
returnVec[vocabList.index(word)] = 1 #如果词语出现在数据集中,则将文档向量中的对应值设为1
else:
print ("%s 没有在数据集中" % word)
return returnVec
检查函数执行情况,检查词表,查看是否出现重复单词,同时创建向量矩阵。
['白痴', '落叶', '帅', '瘦肉粥', '生活', '智障', '傻逼', '皮蛋', '潇洒', '美味']
[[1, 0, 0, 0, 0, 0, 0, 1, 0, 0], [0, 0, 0, 1, 0, 0, 0, 0, 0, 0], [0, 1, 0, 0, 1, 0, 1, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 1], [0, 0, 0, 0, 0, 1, 0, 0, 1, 0], [0, 0, 1, 0, 0, 0, 0, 0, 0, 0]]
- 训练算法
在上面我们处理好了数据,下面就需要套用朴素贝叶斯公式来计算各个概率之间的关系,将之前的 x , y x,y x,y替换为 w w w, w w w表示这是一个向量,即它由多个值组成。在这个例子中, w w w为[[1, 0, 0, 0, 0, 0, 0, 1, 0, 0], [0, 0, 0, 1, 0, 0, 0, 0, 0, 0], [0, 1, 0, 0, 1, 0, 1, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 1], [0, 0, 0, 0, 0, 1, 0, 0, 1, 0], [0, 0, 1, 0, 0, 0, 0, 0, 0, 0]]
P ( c i ∣ w ) = p ( w ∣ c i ) p ( c i ) p ( w ) P({c_i}|w) = \frac{{p(w|{c_i})p({c_i})}}{{p(w)}} P(ci∣w)=p(w)p(w∣ci)p(ci) 我们使用上述公式,对每个类计算该值,然后比较这两个概率值的大小。首先可以通过类别 i i i(侮辱性留言或者非侮辱性留言)中的文档数除以总的文档数来计算概率 p ( c i ) p({c_i}) p(ci) 。接下来计算 p ( w ∣ c i ) p(w | {c_i}) p(w∣ci) ,这里就要用到朴素贝叶斯假设。如果将 w w w展开为一个个独立特征,那么就可以将上述概率写作 p ( w 0 , w 1 , w 2 . . . w n ∣ c i ) p({w_0}, {w_1}, {w_2}...{w_n} | {c_i}) p(w0,w1,w2...wn∣ci)。这里假设所有词都互相独立,它意味着可以使用 p ( w 0 ∣ c i ) p ( w 1 ∣ c i ) p ( w 2 ∣ c i ) . . . p ( w n ∣ c i ) p({w_0} | {c_i})p({w_1} | {c_i})p({w_2} | {c_i})...p({w_n} | {c_i}) p(w0∣ci)p(w1∣ci)p(w2∣ci)...p(wn∣ci)来计算上述概率,这样就极大地简化了计算的过程。
def trainNB0(trainMatrix, trainCategory):
numTrainDocs = len(trainMatrix) #总文件数
numWords = len(trainMatrix[0]) #总单词数
pAbusive = sum(trainCategory) / float(numTrainDocs) #侮辱性文件的出现概率
p0Num = ones(numWords) #p0Num 正常的统计
p1Num = ones(numWords) #p1Num 侮辱的统计
p0Denom = 2.0 #p0Denom 正常的统计, 2.0主要是为了避免分母为0
p1Denom = 2.0 #p1Denom 侮辱的统计
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 = log(p1Num / p1Denom) #侮辱性文档列表
p0Vect = log(p0Num / p0Denom) #正常文档列表
return p0Vect, p1Vect, pAbusive
- 朴素贝叶斯分类
def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1):
p1 = sum(vec2Classify * p1Vec) + log(pClass1) #侮辱性概率
p0 = sum(vec2Classify * p0Vec) + log(1.0 - pClass1) #正常概率
if p1 > p0:
return 1
else:
return 0
- 测试与结果
if __name__ == '__main__':
listOPosts, listClasses = loadDataSet() #1. 加载数据集
myVocabList = createVocabList(listOPosts) #2. 创建单词集合
trainMat = []
for postinDoc in listOPosts: # 3. 计算单词是否出现并创建数据矩阵
trainMat.append(setOfWords2Vec(myVocabList, postinDoc))
p0V, p1V, pAb = trainNB0(array(trainMat), array(listClasses)) #4. 训练数据
# 5. 测试数据
testEntry = ['落叶', '潇洒']
thisDoc = array(setOfWords2Vec(myVocabList, testEntry))
print (testEntry, 'classified as: ', classifyNB(thisDoc, p0V, p1V, pAb))
testEntry = ['傻逼', '智障']
thisDoc = array(setOfWords2Vec(myVocabList, testEntry))
print (testEntry, 'classified as: ', classifyNB(thisDoc, p0V, p1V, pAb))
7.
s
k
l
e
a
r
n
sklearn
sklearn 实践
from sklearn.naive_bayes import GaussianNB
from sklearn.model_selection import train_test_split
from sklearn import datasets
# 加载iris数据集
iris = datasets.load_iris()
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=42)
clf = GaussianNB()
# 训练模型
clf.fit(X_train, y_train)
# 预测结果
ans = clf.predict(X_test)
# 计算准确率
cnt = 0
for i in range(len(y_test)):
if ans[i] - y_test[i] < 1e-1:
cnt += 1
# print(ans[i], ' ', y_test[i])
print("Accuracy: ", (cnt * 100.0 / len(y_test)), "%")