一:贝叶斯定理
P ( A ∣ B ) P(A|B) P(A∣B) = P ( A ) P ( B ∣ A ) P ( B ) \frac{P(A)P(B|A)}{P(B)} P(B)P(A)P(B∣A),其中 P ( A ∣ B ) P(A|B) P(A∣B)是在 B B B发生条件下 A A A发生的可能性。
二:特征条件独立性
如果 A A A、 B B B两事件相互独立的话,那么 P ( A B ) P(AB) P(AB) = P ( A ) P ( B ) P(A)P(B) P(A)P(B)
三:朴素贝叶斯模型
朴素贝叶斯模型是一种简单但非常强大的分类器,在垃圾邮件过滤、疾病诊断、文本分类等方面都显示出了巨大的成效。之所以说朴素,是因为我们假设特征之间是相互独立的(比如:在文本分类中,我们假设各个单词之间的出现与否是相互独立的)。"朴素"一词的含义,在现实中是很难成立的(比如:在文本分类中,某些单词往往会倾向于成双结对的出现),但是即使在不成立的条件下,它依然表现的很好。所以,朴素贝叶斯模型可以用两个词概括,那就是简单、有效。
四:算法思想
首先,我们通过一个例子来深入理解朴素贝叶斯模型的思想。通常一封邮件如果包含"伟哥"这样的单词,那么它就极有可能是一封垃圾邮件,但这还没完,我们需要考虑邮件中可能出现的其他形形色色的单词。
在这里,我们用"word"代表单词,用"spam"表示垃圾邮件,"ham"表示正常邮件。那么如果一个单词出现,该封邮件是垃圾邮件的概率为:
P
(
s
p
a
m
∣
w
o
r
d
)
=
P
(
w
o
r
d
∣
s
p
a
m
)
P
(
s
p
a
m
)
P
(
w
o
r
d
)
P(spam|word) = \frac{P(word|spam)P(spam)}{P(word)}
P(spam∣word)=P(word)P(word∣spam)P(spam)
现在,我们已经把问题简化成一个简单的数学问题了,我们只需要求出在垃圾邮件中出现单词word的概率 P ( w o r d ∣ s p a m ) P(word|spam) P(word∣spam)、垃圾邮件在总邮件数中出现的概率 P ( s p a m ) P(spam) P(spam)、单词word在所有邮件中出现的概率 P ( w o r d ) P(word) P(word)即可。
向量化
现在我们把一封邮件中所有单词的信息都利用起来,构建一个字典(训练集中所有邮件单词的并集),如果该封邮件出现某个单词,该单词对应的位置为1,否则为0。这样我们就得到了该封邮件中所有出现的单词。因为我们的特征是相互独立的,所以可以用下面的公式来计算该封邮件为来及邮件的概率:
P
(
s
p
a
m
∣
t
o
t
a
l
w
o
r
d
)
=
∏
i
P
(
w
o
r
d
i
∣
s
p
a
m
)
P
(
s
p
a
m
)
P
(
w
o
r
d
i
)
P(spam|totalword) = \prod_i \frac{P(word_i|spam)P(spam)}{P(word_i)}
P(spam∣totalword)=i∏P(wordi)P(wordi∣spam)P(spam)
现在,我们就可以得知一封邮件是否为垃圾邮件了。
我们可能在有些地方看到过词集模型和词袋模型,其实这两个模型都是很简单的,仅仅有一个地方不一样而已。下面我简单介绍一下这两个模型:
- 词集模型:如果该单词在文本中出现,对应位置为1,否则为0。用来标记文本中出现了哪些单词。
- 词袋模型:用来统计该单词在文本中出现的次数。
五:示例代码
import numpy as np
'''
function : 文本分类(正常文本和垃圾文本)
'''
'''
function : 加载数据集
返回值 :
TrainData : 训练的文本
classVec : 文本分类向量
'''
def loadDataSet():
TrainData = [['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', 'ate', 'my', 'steak', 'how', 'to', 'stop', 'him'],
['mr', 'licks', 'ate', 'my', 'steak', 'how', 'to', 'stop', 'him'],
['quit', 'buying', 'worthless', 'dog', 'food', 'stupid']]
classVec = [0, 1, 0, 1, 0, 1]
return TrainData, classVec
'''
function : 求文本中出现的所有词
返回值 :
setData : 以数组形式返回(已排序)
'''
def SetOfData(trainData):
setData = set([])
for data in trainData:
setData = setData | set(data) # 取并集
return sorted(list(setData))
'''
function : 统计各种概率(文档向量化)
P(ham) : 正常文本的概率
P(spam) : 垃圾文本的概率
P(word) : 某个单词在所有文本中的概率
P(word|spam) : 垃圾文本中某个单词出现的概率
参数 :
Dictionary : 字典
trainData : 训练数据
label : 分类标签
此函数构建一个矩阵 : Matrix形式如下(字典作为矩阵的列标签,剩下三行为相应的概率)
Dictionary word1 | word2 | word3
P(word)
P(word|spam)
P(word|ham)
'''
def TrainModel(Dictionary,trainData,label):
length = len(Dictionary) # 字典的长度
Matrix = np.zeros([3,length])
# 求P(ham) & P(spam)
P_ham = float(label.count(0)/len(label))
P_spam = float(1 - P_ham)
# P(word)
total_word = 0
total_ham_word = 0
total_spam_word = 0
# 统计文本中所有单词的数量、正常文本单词的数量、垃圾文本中单词的数量
row = len(trainData)
for i in range(row):
total_word += len(trainData[i])
if label[i] == 0:
total_ham_word += len(trainData[i])
elif label[i] == 1:
total_spam_word += len(trainData[i])
for i in range(length):
word_count = 0 # 字典中每个单词在文档中出现的次数
ham_word_count = 0 # 字典中每个单词在正常文档中出现的次数
spam_word_count = 0 # 字典中每个单词在垃圾文档中出现的次数
for j in range(row):
if label[j] == 0:
for k in range(len(trainData[j])):
if Dictionary[i] == trainData[j][k]:
word_count = word_count + 1
ham_word_count = ham_word_count + 1
elif label[j] == 1:
for k in range(len(trainData[j])):
if Dictionary[i] == trainData[j][k]:
word_count = word_count + 1
spam_word_count = spam_word_count + 1
Matrix[0][i] = float(word_count / total_word) # 每个单词在文档中出现的概率
Matrix[1][i] = float(spam_word_count / total_spam_word) # 每个单词在垃圾文档中出现的概率
Matrix[2][i] = float(ham_word_count / total_ham_word) # 每个单词在正常文档中出现的概率
return [P_spam,P_ham,Matrix]
'''
function : 判断文本是否为垃圾文本
'''
def Judge(Dictionary,Matrix,inputData,PSpam,PHam):
P_Word = [1] * len(Dictionary) # 用来存储测试文档中的单词在训练文档中出现的概率
P_Word_Spam = [1] * len(Dictionary) # 用来存储测试文档中的单词在垃圾训练文档中出现的概率
P_Word_Ham = [1] * len(Dictionary) # 用来存储测试文档中的单词在正常训练文档中出现的概率
for data in inputData:
if data in Dictionary:
P_Word[Dictionary.index(data)] = Matrix[0][Dictionary.index(data)] # P(word)
P_Word_Spam[Dictionary.index(data)] = Matrix[1][Dictionary.index(data)] # P(word|spam)
P_Word_Ham[Dictionary.index(data)] = Matrix[2][Dictionary.index(data)] # P(word|ham)
# 计算概率
P_text_spam = 1
P_text_ham = 1
for i in range(len(Dictionary)):
P_text_spam = P_text_spam * ((P_Word_Spam[i] * PSpam) / P_Word[i])
P_text_ham = P_text_ham * ((P_Word_Ham[i] * PHam) / P_Word[i])
if P_text_spam >= P_text_ham: # 该文档为垃圾文档的概率 >= 该文档为正常文档的概率
print('该文章为垃圾文章!!!')
print('概率为 : ' + str(P_text_spam))
else:
print('该文章为正常文章!!!')
print('概率为 : ' + str(P_text_ham))
def main():
trainData,label = loadDataSet()
Dictionary = SetOfData(trainData)
print('字典如下:')
print(Dictionary)
PSpam,PHam,Matrix = TrainModel(Dictionary,trainData,label)
inputData = ['quit', 'buying', 'worthless', 'dog', 'food', 'stupid']
Judge(Dictionary,Matrix,inputData,PSpam,PHam)
if __name__ == '__main__':
main()
运行结果:
六:总结
- 朴素贝叶斯是一种基于概率论的算法。
- 朴素贝叶斯模型的精度很高,并且非常"实惠"。如果已经准备好了一个已经做好标签的数据集,就可以立即搭建朴素贝叶斯模型了。
- 它的训练非常容易,即使是过滤成千上万封邮件,我们也只需要统计各个单词在垃圾邮件和正常邮件中出现的概率即可。