0%

PythonDataAnalysis_5--使用贝叶斯算法分类垃圾邮件

  文本分类是在给定的分类体系下,让计算机根据给定文本的内容,将其判定为事先给定的若干个文本类别中的某一类或几类的过程。

文本分类过程

一般来说,文本分类可以分为以下过程:

  • 预处理:将原始语料格式化为同一格式,便于后续的统一处理;
  • 索引:将文档分解为基本处理单元,同时降低后续处理的开销;
  • 统计:词频统计,项(单词,概念)与分类的相关概率;
  • 特征抽取:从文档中抽取出反应文档主题的特征;
  • 分类器:分类器的训练;
  • 评价:分类器的测试结果分析。

朴素贝叶斯分类算法

  • 算法基本分析:贝叶斯定理

    𝑝(𝑐𝑖/𝑥,𝑦)=(𝑝( 𝑥,𝑦/𝑐𝑖)*𝑝(𝑐𝑖))/𝑝(𝑥,𝑦)

  • 算法基本思想:利用先验概率和条件概率估算后验概率

  • 朴素贝叶斯分类算法步骤:

    计算先验概率p(C=ck),k=1,2,……,K
    计算独立条件概率
    计算总条件概率
    计算后验概率
    选取最大后验概率确定x的类别

朴素贝叶斯算法分类垃圾邮件

  • 使用中文邮件数据集,正常邮件和垃圾邮件各100个;
  • 使用jieba分词,对200个邮件分词,并去除重复词与符号,作为词汇表使用;
  • 对每个邮件进行单独分词,并产生一个词汇表长度的向量(一个词只要出现,便将向量中的该词的词汇表索引位置的0变成1);
  • 对于上述200个向量,计算出两种邮件中每个词的条件概率(此处假设每个样本至少出现一次,,为了防止乘法运算下溢出,将全部乘法运算改为log运算)
  • 对测试的每一封邮件进行分词,计算其向量,然后得到其为两种邮件的概率,进行比较,取最大的一个去判断其类别

实例

读取两文件的所有邮件,进行分词,得到词汇表,存入’词汇表.txt’

import jieba
import jieba.analyse
import numpy as  np
import math
import re
#词汇表,存入 词汇表.txt
vtable = []

#打开两种邮件列表文件
fileName_1='ham_100.utf8'
fileName_2='spam_100.utf8'
source_1 = open(fileName_1, 'r',encoding='utf-8')
source_2 = open(fileName_2, 'r',encoding='utf-8')

#读取正常邮件
line = source_1.readline()
line=re.sub('[^\u4e00-\u9fa5]+','',line)
while line != "":
    line = line.rstrip('\n')
    seglist = list(jieba.cut(line, cut_all=False))
    for i in seglist:
        vtable.append(i)
    line = source_1.readline()
    line=re.sub('[^\u4e00-\u9fa5]+','',line)
source_1.close()
print("正常邮件列表读取分词完毕...")

#读取垃圾邮件
line = source_2.readline()
line=re.sub('[^\u4e00-\u9fa5]+','',line)#去除非中文字符
while line != "":
    line = line.rstrip('\n')
    seglist = list(jieba.cut(line, cut_all=False))
    for i in seglist:
        vtable.append(i)
    line = source_2.readline()
    line=re.sub('[^\u4e00-\u9fa5]+','',line)
source_2.close()
print("垃圾邮件列表读取分词完毕...")
#转为无重复元素数组来去重复
vtable=list(set(vtable))
#词汇表存入文件
with open("词汇表.txt",'w',encoding='utf-8') as f:
    f.write(str(vtable))

创建一个全部邮件的向量矩阵,和邮件类型矩阵,将每封邮件分词后计算得到其特征向量

#创建全部邮件的向量矩阵
trainMatrix=np.zeros((200,len(vtable)))
#创建邮件类型的向量,因为是按照顺序读取,前100项为0(正常邮件),后100项为1(垃圾邮件)
flagTwo=[0,1]
trainCategory=[flag for flag in flagTwo for i in range(100) ]

#重新打开每个邮件文件,计算每个邮件文件中各邮件的向量存入trainMatrix
#count计数,同时也是trainMatrix矩阵的索引
count = 0

source_1 = open(fileName_1, 'r',encoding='utf-8')
line = source_1.readline()
line=re.sub('[^\u4e00-\u9fa5]+','',line)
while line != "":
    line = line.rstrip('\n')
    seglist = list(jieba.cut(line, cut_all=False))
    #删除标点符号,否则找到在词汇表中的索引,对应到自己的向量矩阵
    for i in seglist:
        index=vtable.index(i)
        trainMatrix[count][index] = 1
    line = source_1.readline()
    line=re.sub('[^\u4e00-\u9fa5]+','',line)
    count=count+1
source_1.close()

source_2 = open(fileName_2, 'r',encoding='utf-8')
line = source_2.readline()
line=re.sub('[^\u4e00-\u9fa5]+','',line)
while line != "":
    line = line.rstrip('\n')
    seglist = list(jieba.cut(line, cut_all=False))
    #删除标点符号,否则找到在词汇表中的索引,对应到自己的向量矩阵
    for i in seglist:
        index=vtable.index(i)
        trainMatrix[count][index] = 1
    line = source_2.readline()
    line=re.sub('[^\u4e00-\u9fa5]+','',line)
    count=count+1
source_2.close()

定义函数trainNB去处理邮件的向量,训练得到先验概率、条件概率

def trainNB(trainMatrix,trainCategory):
    '''
        trainMatrix 表示所有邮件的向量,
        trainCategory 表示邮件种类的列表(第i项为0则是正常邮件,为1则是垃圾邮件)
    '''
    numTrainDocs=len(trainMatrix)#邮件总数量
    numWords=len(trainMatrix[0])#词汇表长度
    pSpam=sum(trainCategory)/float(numTrainDocs) #垃圾邮件比例
    p0Num=np.ones(numWords)#初始化正常邮件向量为全1(每个词至少出现一次)
    p1Num=np.ones(numWords)#初始化垃圾邮件向量为全1(每个词至少出现一次)
    p0Denom=2.0
    p1Denom=2.0
    for i in range(numTrainDocs):
        if trainCategory[i]==1:
            p1Num=p1Num+trainMatrix[i]#把属于同一类的文本向量相加,统计某个词条在该类文本中出现频率
            p1Denom=p1Denom+sum(trainMatrix[i])#把垃圾邮件向量的所有元素加起来,表示垃圾邮件中的所有词汇
        else:
            p0Num=p0Num+trainMatrix[i]
            p0Denom=p0Denom+sum(trainMatrix[i])
    p1=np.log(p1Num/p1Denom)#防止乘法溢出,取对数
    p0=np.log(p0Num/p0Denom)#防止乘法溢出,取对数
    return pSpam,p1,p0

判定结果

  读取测试文件,对每个邮件进行分词,计算得到其特征向量,与前面得到的值进行运算得到后验概率,取大者作为其类型判别,将结果存入文件’result.txt’:

#调用trainNB得到三个参数
pSpam,p1,p0=trainNB(trainMatrix,trainCategory)
#计算每种的后验概率
'''
    P_1表示垃圾邮件概率
    P_0表示正常邮件概率
'''
#读取目标文件
fileName_T='test.utf8'
source_T = open(fileName_T, 'r',encoding='utf-8')

#设置目标文件的邮件向量矩阵
targetMatrix=np.zeros((100,len(vtable)))

#读取每个邮件进行分词后按词汇表设置好向量矩阵
count_T = 0
line = source_T.readline()
line=re.sub('[^\u4e00-\u9fa5]+','',line)
while line != "":
    line = line.rstrip('\n')
    seglist = list(jieba.cut(line, cut_all=False))
    #删除标点符号,否则找到在词汇表中的索引,对应到自己的向量矩阵
    for i in seglist:
        if i in vtable:
            index=vtable.index(i)
            targetMatrix[count_T][index] = 1
    line = source_T.readline()
    line=re.sub('[^\u4e00-\u9fa5]+','',line)
    count_T=count_T+1
source_T.close()

#存放得到的邮件类型
resultMatrix = np.zeros(100)

#计算每封邮件的垃圾和正常的条件概率,分别表示为Pspam和Pham
Pspam=0.0
Pham=0.0
for i in range(len(targetMatrix)):
    #计算后验概率
    Pham = sum( targetMatrix[i] * p0 ) + math.log(1.0-pSpam)
    Pspam = sum( targetMatrix[i] * p1 ) + math.log(pSpam)
    if Pham >= Pspam:
        resultMatrix[i]=0
    else :
        resultMatrix[i]=1

#分类结果存入文件
with open("result.txt",'w',encoding='utf-8') as f:
    f.write(str(resultMatrix))

将判断结果与实际相比较得到判断正确率

#计算正确率
#用正确结果的矩阵去和得到的矩阵比较得到正确个数
countRight=0
right=[0 for i in range(50)]+[1 for i in range(50)]
for i in range(len(right)):
    if right[i]==resultMatrix[i]:
        countRight += 1
print('分类正确率: '+str(countRight)+'%' )

词汇表如下:

词汇表

运行结果图如下:

运行结果

判定结果如下:

判定结果