文本分类是在给定的分类体系下,让计算机根据给定文本的内容,将其判定为事先给定的若干个文本类别中的某一类或几类的过程。
文本分类过程
一般来说,文本分类可以分为以下过程:
- 预处理:将原始语料格式化为同一格式,便于后续的统一处理;
- 索引:将文档分解为基本处理单元,同时降低后续处理的开销;
- 统计:词频统计,项(单词,概念)与分类的相关概率;
- 特征抽取:从文档中抽取出反应文档主题的特征;
- 分类器:分类器的训练;
- 评价:分类器的测试结果分析。
朴素贝叶斯分类算法
算法基本分析:贝叶斯定理
𝑝(𝑐𝑖/𝑥,𝑦)=(𝑝( 𝑥,𝑦/𝑐𝑖)*𝑝(𝑐𝑖))/𝑝(𝑥,𝑦)
算法基本思想:利用先验概率和条件概率估算后验概率
朴素贝叶斯分类算法步骤:
计算先验概率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)+'%' )
词汇表如下:
运行结果图如下:
判定结果如下: