本文索引:
- 分类基本概念
- 贝叶斯分类概述
- 垃圾邮件分类实战
一、分类基本概念
【什么是分类】
- 生活中常见的分类诸如超市物品分类(面包、鲜肉、蔬菜等)、垃圾分类(餐余垃圾、有害垃圾、其他垃圾、可回收物)和生活信息分类(房产、二手物品、车辆、招聘、宠物、票务、生活服务等等)
【分类在数据挖掘中的定义】
- 分类就是把一些新的数据项映射到给定类别的中的某一个类别。
- 分类属于有监督学习(supervised learning),与之相对的是无监督学习(unsupervised learning),比如聚类。
- 分类与聚类的最大区别在于,分类数据中的一部分的类别是已知的,即训练数据,而聚类的数据全部未知,即根据待分类数据的相似性进行聚类。
【分类问题】
- 动物分类问题
- 根据现有的知识,我们得到了一些关于哺乳动物和鸟类的信息,我们能否对新发现的物种,比如动物A,动物B进行分类?
动物种类 | 体型 | 翅膀数量 | 脚的只数 | 是否产蛋 | 是否有毛 | 类别 |
---|---|---|---|---|---|---|
狗 | 中 | 0 | 4 | 否 | 是 | 哺乳动物 |
猪 | 大 | 0 | 4 | 否 | 是 | 哺乳动物 |
牛 | 大 | 0 | 4 | 否 | 是 | 哺乳动物 |
麻雀 | 小 | 2 | 2 | 是 | 是 | 鸟类 |
天鹅 | 中 | 2 | 2 | 是 | 是 | 鸟类 |
大雁 | 中 | 2 | 2 | 是 | 是 | 鸟类 |
动物A | 大 | 0 | 2 | 是 | 否 | ? |
动物B | 中 | 2 | 2 | 否 | 是 | ? |
【分类的流程】
- 步骤一:将样本转化为等维的数据特征(特征提取)。所有样本必须具有相同数量的特征。兼顾特征的全面和独立性,独立性是指两个属性之间不能有相关性。
动物种类 | 体型 | 翅膀数量 | 脚的只数 | 是否产蛋 | 是否有毛 | 类别 |
---|---|---|---|---|---|---|
狗 | 中 | 0 | 4 | 否 | 是 | 哺乳动物 |
猪 | 大 | 0 | 4 | 否 | 是 | 哺乳动物 |
牛 | 大 | 0 | 4 | 否 | 是 | 哺乳动物 |
麻雀 | 小 | 2 | 2 | 是 | 是 | 鸟类 |
天鹅 | 中 | 2 | 2 | 是 | 是 | 鸟类 |
大雁 | 中 | 2 | 2 | 是 | 是 | 鸟类 |
- 步骤二:选择与类别相关的特征(特征选择)。比如这个例子,绿色代表与类别非常相关,黑色代表部分相关,浅黑色代表完全无关。
- 步骤三:建立分类模型或分类器。
- 分类器通常可以看做一个函数,他把特征映射到类的空间上。
【分类的方法】
- 常用分类算法主要包括相似函数,关联规则分类算法,K近邻分类算法,决策树分类算法,贝叶斯分类算法和基于模糊逻辑,遗传,粗糙集和神经网络的分类算法。
- 分类算法有很多种,都有各自的优缺点和应用范围,本课程我们以贝叶斯分类算法为例。
二、贝叶斯分类概述
【背景】
- 贝叶斯分类基于贝叶斯定理,贝叶斯定理是由18世纪概率论和决策论的研究者Thomas Bayes发明的,故用其名字命名为贝叶斯定理。
- 通过对分类算法的比较研究发现,朴素贝叶斯分类法可以与决策树和经过挑选的神经网络分类器相媲美。用于大型数据库,贝叶斯分类法也具有很高准确率和速度。
【先验概率和后验概率】

- 先验概率:由以往的数据分析得到的概率。
- 后验概率:得到信息之后再重新加以修正的概率。
【贝叶斯理论】
- 简单地说,贝叶斯定理是基于假设的先验概率、给定假设下观察到不同数据的概率,提供了一种计算后验概率的方法
- 在人工智能领域,贝叶斯方法是一种具有代表性的不确定性表示和推理方法。
【贝叶斯定理】
- P(A)是A的先验概率或边缘概率。之所以成为“先验”是因为它不考虑B方面的因素。
- P(A|B)是已知B发生后A的条件概率,即概率论中称为在B发生的条件下A发生的概率,也由于得自B的取值而被称作A的后验概率。
- P(B|A)是已知A发生B的条件概率,也由于得自A的取值而被称作B的后验概率。
- P(B)是B的先验概率或边缘概率,也做标准化常量(narmalized constant)。
- 贝叶斯共识提供了从先验概率P(A)、P(B)和P(B|A)计算后验概率P(A|B)的方法。
【朴素贝叶斯】
- 假设待分类项的各个属性相互独立的情况下构造的分类算法就是朴素贝叶斯算法。
- 基本思想:给定的待分类项X(
),求解在此项出现条件下各个类别
出现的概率,哪个
最大,就把此待分类想归属哪个类别。
【四种贝叶斯分类器】
- Naive Bayes:朴素贝叶斯,嘉定个特征变量x是相互独立的。
- TAN:对朴素贝叶斯进行了扩展,允许各特征变量所对应的结点构成一棵树。
- BAN:对TAN进行扩展,允许各特征变量所对应的节点间关系构成一个图,而不只是树。
- GBN:一种无约束的贝叶斯网络分类器。
【贝叶斯分类流程】
- 准备阶段:主要是依据具体情况确定特征属性,并且对特征属性进行适当划分。然后就是对一部分待分类想进行人工划分。以确定训练样本。输入时所有的待分类项,输出时特征属性和训练样本。
- 分类器训练阶段:计算每个类别在训练样本中出现频率以及每个特征属性划分对每个类别的条件概率估计。输入是特征属性和训练样本,输出是分类器。
- 应用阶段:所使用的分类器对待分类项进行分类,其输入是分类器和待分类项,输出时法分类项与类别的映射关系。
三、垃圾邮件分类实战
- 训练数据,垃圾邮件(0.txt至150.txt),其中0-127为垃圾邮件,128至150为正常邮件,151至156为测试邮件。
链接:https://pan.baidu.com/s/1mqQuNU7DTVOhU3M_cdx3IA
提取码:jc27
【问题分析】
- 读取全部训练集,删除其中的干扰字符,例如【】*。,等等,然后分词,再删除长度为1的单个字,这样的单个字对于文本分类没有贡献,剩下的词汇认为是有效词汇。
- 统计全部训练集中每个有效词汇的出现次数,截取出现次数最多的前N个。
- 根据第1步预处理后的垃圾邮件和非垃圾邮件内容生成特征向量,统计第2步中得到的N个词语分别在该邮件中的出现概率。
- 根据第3步中得到特征向量和已知邮件分类创建并训练朴素贝叶斯模型。
- 读取测试邮件,参考第1步,对邮件文本进行预处理,提取特征向量。
- 使用第4步中训练好的模型,根据第5步提取的特征向量对邮件进行分类。
接下来我们来加上代码实际操作一下:
- 导入扩展库
from re import sub # 正则表达式sub替换功能,主要删除无意义字符
# from os import listdir #用于返回指定的文件列表,邮件放在文件夹内
from collections import Counter # 计算功能
from itertools import chain # 迭代器,可一次返回可迭代对象的元素
from numpy import array
from jieba import cut
from sklearn.naive_bayes import MultinomialNB # 先验为多项式分布的朴素贝叶斯
- 获取文件中所有词
# 处理每个垃圾邮件中的词语
def getWordsFromFile(txtFile):
# 获取每一封邮件中的所有词语
words = []
# 所有存储邮件文本内容的记事本文件都使用UTF-8编码
with open(txtFile,encoding = 'utf8') as fp:
for line in fp:
# 遍历每一行,删除两端的空白字符
line = line.strip()
# 过滤干扰字符或无效字符
line = sub(r'[.【】0-9、-。,!~\*]',"",line)
# 分词
line = cut(line)
# 过滤长度为1的词
line = filter(lambda word:len(word)>1,line)
# 把本行文本与处理得到的词语添加到words列表中
words.extend(line)
# 返回包含当前邮件文本中所有有效词语的列表
return words
- 获取全部训练集中出现次数最多的词
allWords = []
def getTopNWords(topN):
# 按文件编号顺序处理当前文件夹中所有记事本文件
# 训练集中共151封邮件内容,0.txt到126.txt是垃圾邮件内容
# 127.txt到150.txt为正常邮件内容
txtFiles = [str(i)+'.txt' for i in range(151)] # 遍历所有训练集
# 获取训练集中所有邮件中的全部单词
for txtFile in txtFiles:
allWords.append(getWordsFromFile(txtFile))
# 获取并返回出现次数最多的前topN个单词
freq = Counter(chain(*allWords))
return [w[0] for w in freq.most_common(topN)]
# 全部训练集中出现次数最多的前600个单词
topWords = getTopNWords(600)
如果你是跟着一起用jupyter notebook来做的,运行到这一步时会有以下输出结果:
Building prefix dict from the default dictionary ...
Dumping model to file cache C:\Users\孔东帅\AppData\Local\Temp\jieba.cache
Loading model cost 0.895 seconds.
Prefix dict has been built successfully.
- 获取特征向量、创建模型训练
# 获取特征向量,前600个单词的每个单词在每个邮件中出现的频率
vectors = []
for words in allWords:
temp = list(map(lambda x: words.count(x),topWords))
vectors.append(temp)
vectors = array(vectors)
# 训练集中每个邮件的标签,1表示垃圾邮件,0表示正常邮件
labels = array([1]*127 + [0]*24)
#创建模型,使用已知训练集进行训练
model = MultinomialNB()
model.fit(vectors,labels)
这一段代码运行结果:
MultinomialNB(alpha=1.0, class_prior=None, fit_prior=True)
- 预测位置邮件,进行分类,看是否为垃圾邮件
def predict(txtFile):
words = getWordsFromFile(txtFile)
currentVector = array(tuple(map(lambda x: words.count(x),topWords)))
result = model.predict(currentVector.reshape(1,-1))[0]
return '垃圾邮件' if result == 1 else '正常邮件'
for mail in ('%d.txt'%i for i in range(151,156)):
print(mail,predict(mail),sep=':')
最终输出预测结果:
151.txt:垃圾邮件
152.txt:垃圾邮件
153.txt:垃圾邮件
154.txt:垃圾邮件
155.txt:正常邮件
下面我把我的笔记放出来给大家截个图参考一下:
注意,敲重点啦,下面是我学习的时候踩的一个坑,我相信看到这你一定也已经踩上了,如果你的代码没有抄错,但还是会有一个错误,我猜一定是这个:“no such file or directory”,这是为什么呢,你发现我们在之前关于文件操作的代码里面都包含有绝对路径指明文件所在位置,但是这里要循环100多次来执行对这些文件的操作,还记得吗?我们可以直接将ipynb文件与代码执行所需文件放在同一个文件夹下就可以不用指明绝对路径。
可是很多人使用jupyter notebook却忽略了这些,其实我们发现我们每次打开jupyter notebook时,主页面有好多文件以及我们每次写完的代码ipynb文件,这些都是默认在C盘的用户文件下的,如果想要自己指定打开时的默认地址和我们平时写的代码文件地址,看下面步骤,本人刚从坑里爬出来的:
1、win+R输入cmd打开命令行窗口,输入 jupyter notebook --generate-condig
2、找到输出的这个文件的地址这个jupyter_notebook_config.py文件
3、用记事本代开,找到下面这行代码,添加想要修改的地址,注意把这一行最前面的注释符“#”删掉,保存关闭。
4、此时再打开jupyter notebook时,主页面的文件夹位置就已经修改了,你可以将相同的类别放在其中的一个文件夹下,这样不用指定绝对路径即可运行,而且下次再打开的时候主页就是修改后的了。看下我的:我这节的笔记ipynb文件在这个文件夹下,双击点击去就可以找到,然后再运行就没有问题啦。
需要注意的是:我之前每次退出都是点的右上角的Logout,这是注销的意思,会将修改的主页地址还原,其实旁边quit就是退出。
至此,Python机器学习部分已全部更新完毕,后续的机器学习和深度学习部分还在酝酿,一起加油吧!!!