1 拉普拉斯平滑:
上一篇博客的最后留下了一个问题,那就是如果检测的词列表中包含概率为0的字那么最后结果总是0。那么此时可以引入拉普拉斯平滑,也就是说,可以将所有的字初始化为1,然后分母初始化为2.
还有一个问题就是要防止下溢出,即小数与小数项城越乘越小,到最后保留小数可能就成为了0,对此采用取对数的方式来进行解决,取对数不会有任何的损失。
图片出处
代码改进:
def Trainer(trans_list,classVec):
"""用来得出每个字的条件概率
Params:
trans_list是转码后的字分布列表
classVec是每个句子的类别列表
return 侮辱性句子中每个字的条件概率,非侮辱性句子中每个字的条件概率,侮辱性句子的概率"""
numSentences = len(trans_list) #计算出要遍历句子的数量
numBad = sum(classVec) #算出出现侮辱性句子的总数
numWords = len(trans_list[0]) #words_list的长度
pAbusive = numBad / numSentences #求出出现侮辱性句子的概率
p0list = np.ones(numWords) #初始化一个非侮辱的列表
p1list = np.ones(numWords)
p0Denom = 2.0 #初始化分母
p1Denom = 2.0
for class_ in range(numSentences): #进入遍历
if classVec[class_] == 1: #如果是侮辱性的句子进入下面的这个循环
p1list += trans_list[class_] #统计每个字出现的频率
p1Denom += sum(trans_list[class_])
else:
p0list += trans_list[class_]
p0Denom += sum(trans_list[class_])
p0Vec = np.log(p0list / p0Denom)
p1Vec = np.log(p1list / p1Denom)#防止下溢出
return p0Vec,p1Vec,pAbusive
def Classify(p0Vec,p1Vec,pAbusive,Test_sentence):
"""计算贝叶斯的分子比较大小
Params:
p0Vec:非侮辱性句子当中每个字出现的条件概率
p1Vec:侮辱性句子当中每个字出现的条件概率
pAbusive:出现侮辱性句子的概率
Test_sentence:输入的文字"""
trans_sentence = Encoder(Test_sentence,words_list) #首先对文字进行转码
p0 = sum(p0Vec * trans_sentence) +(1-pAbusive) #因为结果取了对数,log(a*b) = log(a) +log(b)
#reduce函数的功能为:按照前面的函数对列表中的值依此运算,这里reduce就是求独立的条件概率的乘积,本意是:P(has|非侮辱)*P(is|非侮辱)*P(非侮辱)
p1 = sum(p1Vec*trans_sentence) +pAbusive #
if p1>p0: #通过比较大小就可判断
print('该句带有侮辱性词汇')
elif p1<p0:
print('该句为非侮辱语句')
else:
print('抱歉,出现bug,暂时无法识别...')
2 实例二:垃圾邮件过滤器:
朴素贝叶斯对电子邮件进行分类的步骤:
- 收集数据:提供文本文件。
- 准备数据:将文本文件解析成词条向量。
- 分析数据:检查词条确保解析的正确性。
- 训练算法:使用我们之前建立的trainNB0()函数。
- 测试算法:使用classifyNB(),并构建一个新的测试函数来计算文档集的错误率。
- 使用算法:构建一个完整的程序对一组文档进行分类,将错分的文档输出到屏幕上。
- 有两个文件夹ham和spam,spam文件下的txt文件为垃圾邮件。
首先要将一个个文件差分为词条向量,因为数据都是英文字母或数字,所以可以用正则表达式的split来拆分。
import re
import numpy as np
def Words_Split(BigString):
words = re.split(r'\W',BigString) #利用正则表达式库的split \W表示非数字或字母
return [each.lower() for each in words if len(each)>2] #如果单词长度大于二,那么就将其变成小写,除了I这种单个的大
- 生成词汇表
def Words_Split(BigString):
words = re.split(r'\W',BigString) #利用正则表达式库的split \W表示非数字或字母
return [each.lower() for each in words if len(each)>2] #如果单词长度大于二,那么就将其变成小写,除了I这种单个的大
def Words_List(dataset):
vect = set([])
for each in dataset:
vect = vect | set(each)
return list(vect)
if __name__ == '__main__':
docList=[] #创建一个字典用来存放所有邮件的单词
classList=[] #创建一个存放分类的列表
for i in range(1,26):
doc = Words_Split(open('E:/Data/ham/%d.txt' % i,'r' ).read())
docList.append(doc)
classList.append(0)
for j in range(1,26):
doc2 = Words_Split(open('E:/Data/spam/%d.txt' % i,'r' ).read())
docList.append(doc2)
classList.append(1)
words_list = Words_List(docList)
print(words_list)
结果如下:
- 生成词袋模型:
def Encoder(words_list,Sentence):
"""讲一个句子当中出现过的字显示为1,没出现过的字显示为0,根据words_list,创建一个词袋模型
Params:
words_list:Words_List输出的词汇表
Sentence:放入的每一条词条向量
return:
转码为0-1分布的词袋模型"""
trans_list = [0]*len(words_list)
for each in Sentence:
if each in words_list:
trans_list[words_list.index(each)] = 1
else:
print('该文字不在目录当中')
return trans_list
- 贝叶斯训练器:
def Trainer(transform,classList):
"""得出垃圾邮件和普通邮件每个字的条件概率以及垃圾邮件出现的概率
Params:
transform:Encoder输出合成的列表
classList:分类列表
return:
普通邮件各个字的条件概率;垃圾邮件各个字的条件概率;总的垃圾邮件出现的概率"""
Line_num = len(transform)#确定要遍历句子的数量
Bad_num = sum(classList)
pAbusive = Bad_num / float(Line_num)
p0List = np.ones(len(transform[0])) #高斯平滑
p1List = np.ones(len(transform[0])) #高斯平滑
p0Denom = 2.0
p1Denom = 2.0
for i in range(Line_num):
if classList[i] == 1:
p1List+=transform[i]
p1Denom += sum(transform[i])
else:
p0List+=transform[i]
p0Denom += sum(transform[i])
p0 = np.log(p0List/p0Denom) #防止向下溢出
p1 = np.log(p1List/p1Denom)
return p0,p1,pAbusive
- 分类器:
def Classfier(p0,p1,pAbusive,Test):
"""对邮件进行分类
Params:
p0:普通邮件中每个字的条件概率
p1:垃圾邮件中每个字的条件概率
pAbusive:总的出现垃圾邮件的概率
Test:Encoder转码后的词袋模型"""
P0 = sum(p0*Test) + np.log((1-pAbusive))
P1=sum(p1*Test) + np.log(pAbusive)
if P0>P1:
return 0
elif P0<P1:
return 1
- 测试精确读:
import re
import numpy as np
import random
def Words_Split(BigString):
words = re.split(r'\W',BigString) #利用正则表达式库的split \W表示非数字或字母
return [each.lower() for each in words if len(each)>2] #如果单词长度大于二,那么就将其变成小写,除了I这种单个的大
def Words_List(dataset):
vect = set([])
for each in dataset:
vect = vect | set(each)
return list(vect)
def Encoder(words_list,Sentence):
"""讲一个句子当中出现过的字显示为1,没出现过的字显示为0,根据words_list,创建一个词袋模型
Params:
words_list:Words_List输出的词汇表
Sentence:放入的每一条词条向量
return:
转码为0-1分布的词袋模型"""
trans_list = [0]*len(words_list)
for each in Sentence:
if each in words_list:
trans_list[words_list.index(each)] = 1
else:
print('该文字不在目录当中')
return trans_list
def Trainer(transform,classList):
"""得出垃圾邮件和普通邮件每个字的条件概率以及垃圾邮件出现的概率
Params:
transform:Encoder输出合成的列表
classList:分类列表
return:
普通邮件各个字的条件概率;垃圾邮件各个字的条件概率;总的垃圾邮件出现的概率"""
Line_num = len(transform)#确定要遍历句子的数量
Bad_num = sum(classList)
pAbusive = Bad_num / float(Line_num)
p0List = np.ones(len(transform[0])) #高斯平滑
p1List = np.ones(len(transform[0])) #高斯平滑
p0Denom = 2.0
p1Denom = 2.0
for i in range(Line_num):
if classList[i] == 1:
p1List+=transform[i]
p1Denom += sum(transform[i])
else:
p0List+=transform[i]
p0Denom += sum(transform[i])
p0 = np.log(p0List/p0Denom) #防止向下溢出
p1 = np.log(p1List/p1Denom)
return p0,p1,pAbusive
def Classfier(p0,p1,pAbusive,Test):
"""对邮件进行分类
Params:
p0:普通邮件中每个字的条件概率
p1:垃圾邮件中每个字的条件概率
pAbusive:总的出现垃圾邮件的概率
Test:Encoder转码后的词袋模型"""
P0 = sum(p0*Test) + np.log((1-pAbusive))
P1=sum(p1*Test) + np.log(pAbusive)
if P0>P1:
return 0
elif P0<P1:
return 1
def spamTest():
"""测试贝叶斯的精确度"""
docList=[] #用来存放所有的文字
classList=[] #用来存放每个邮件的类别
fulltext=[]
for i in range(1,26): #遍历所有邮件
doc = Words_Split(open('E:/Data/ham/%d.txt' % i,'r' ).read())
docList.append(doc)
fulltext.append(doc)
classList.append(0)
doc = Words_Split(open('E:/Data/spam/%d.txt' % i,'r' ).read())
docList.append(doc)
fulltext.append(doc)
classList.append(1)
words_list = Words_List(docList) #输出词汇表
train_set = list(range(50)) #总共50个文件,用来存放40个训练集的索引
test_set = [] #存放10个测试集的索引
for i in range(10): #取十个作为测试集
index = int(random.uniform(0,len(train_set))) #用random.uniform来随机取出一个索引
test_set.append(train_set[index]) #加入到测试集当中
del(train_set[index]) #并且在训练集中删去
trainMat = [] #用来存放训练集单词转码后的词袋模型
trainClass = [] #用来存放训练集的类别
for each_index in train_set:
each_train = Encoder(words_list,docList[each_index]) #依此转码
trainMat.append(each_train)
trainClass.append(classList[each_index])
p0,p1,pAbusive = Trainer(np.array(trainMat),np.array(trainClass)) #得出训练集的数据
errocount=0 #初始化误差值
for index in test_set:
word = Encoder(words_list,docList[index])
if Classfier(p0,p1,pAbusive,word) != classList[index]: #如果不相等就表示出现了误差
errocount+=1
print('测试完毕,错误率为:{:.2f}%'.format((errocount/len(test_set))*100))
if __name__=='__mian__':
spamTest()
结果: