【机器学习实战】朴素贝叶斯:过滤垃圾邮件

news2024/11/25 11:38:35

【机器学习实战】朴素贝叶斯:过滤垃圾邮件

0.收集数据

这里采用的数据集是《机器学习实战》提供的邮件文件,该文件有hamspam 两个文件夹,每个文件夹中各有25条邮件,分别代表着 正常邮件垃圾邮件

这里需要注意的是需要将 email 文件夹放在 py 代码文件同一级的地方。若放在其他地方,需要修改博主代码中的相对地址才可正常执行。
在这里插入图片描述

1.解析数据

我们需要将我们的文本文件导入到程序里面,其中主要参数如下:

  • d o c L i s t : docList: docList 是一个二维数组,一共记录了50封邮件的内容。
  • f u l l T e x t : fullText: fullText是50封邮件中出现的所有单词的一个大的单词列表(有重复单词)
  • c l a s s L i s t : classList: classList用于记录当前邮件是 正常邮件 还是 垃圾邮件01 进行区分。
  • v o c a b L i s t : vocabList : vocabList是一个单词均不重复的词汇表。
# 导入并解析文本文件
    for i in range(1, 26):  # 因为每个文件夹各有文件25个
        wordList = textParse(open('email/spam/%d.txt' % i, encoding="ISO-8859-1").read())  # 读取文件
        docList.append(wordList)  # 用于存储所有文件的单词列表,二维数组
        fullText.extend(wordList)  # 这个列表用于存储所有文件的单词,将它们合并成一个大的单词列表
        classList.append(1)
        wordList = textParse(open('email/ham/%d.txt' % i, encoding="ISO-8859-1").read())
        docList.append(wordList)
        fullText.extend(wordList)
        classList.append(0)
    vocabList = createVocabList(docList)  # 创建不重复的词汇表

# 邮件裁词
def textParse(bigString):
    regEx = re.compile(r'\W')
    listOfTokens = regEx.split(bigString)
    var = [tok.lower() for tok in listOfTokens if len(tok) > 2]  # 只返回长度大于2的情况
    return var

# 创建词汇表
def createVocabList(dataSet):
    vocabSet = set([])  # 创建一个无序且不重复的集合对象
    for document in dataSet:
        vocabSet = vocabSet | set(document)  # 创建两个集合的并集
    return list(vocabSet)

ps:因为这里的邮件全是英文邮件,所以读取的时候编码是 encoding="ISO-8859-1"

2.区分“测试集”和“训练集”

这里我们一共创建了10个测试集,每取一个测试集就将其的索引从trainingSet 中删除。故trainingSet 最后剩下的40个索引就是训练集,testSet中的10个索引就是测试集。

  • 测试集:用于判断朴素贝叶斯分类是否有问题。
  • 训练集:用于训练朴素贝叶斯分类器的参数。
	trainingSet = list(range(50))  # 训练集的索引,0-49(因为ham和spam各有25个,故一共有50个)
    trainingSet = trainingSet
    testSet = []
    # 随机构造训练集
    for i in range(10):  # 表示只创建10个测试集、剩下的都是训练集
        # 函数生成一个0到len(trainingSet)之间的随机浮点数,然后取整,得到一个随机的索引值randIndex。
        randIndex = int(random.uniform(0, len(trainingSet)))
        testSet.append(trainingSet[randIndex])  # 将随机选择的索引值randIndex添加到测试集列表testSet中。
        trainingSet.pop(randIndex)  # 删除训练集中对应的测试集的索引值,以确保不会重复选择作为测试集。

3.训练算法

朴素贝叶斯算法往简单说,就是计算出当前参数为不同情况的概率,最后哪个可能性的概率大,那么他就是哪一类。

3.1 统计词汇出现的次数

# 朴素贝叶斯词袋模型
def bagOfWords2VecMN(vocabList, inputSet):
    returnVec = [0] * len(vocabList)  # 创建一个长度为vocabList的列表returnVec,并将列表中的每个元素都初始化为0
    for word in inputSet:
        if word in vocabList:
            returnVec[vocabList.index(word)] += 1  # 在词库里出现次数
    return returnVec

3.2.训练函数

将各个邮件中词汇出现次数的词袋,和最初记录每个邮件是 正常邮件 还是 垃圾邮件 的列表传入trainNB0计算出每个词汇是正常还是垃圾的可能性。

# 朴素贝叶斯分类器训练函数
def trainNB0(trainMatrix, trainCategory):
    numTrainDocs = len(trainMatrix)
    numWords = len(trainMatrix[0])
    pAbusive = sum(trainCategory) / float(numTrainDocs)  # 侮辱性单词出现的概率
    p0Num = ones(numWords)  # 为了防止出现 0 * x 的情况
    p1Num = ones(numWords)
    p0Denom = 2.0  # 为了防止分母为0的情况
    p1Denom = 2.0
    for i in range(numTrainDocs):
        if trainCategory[i] == 1:
            p1Num += trainMatrix[i]  # 计算每个词出现的次数
            p1Denom += sum(trainMatrix[i])  # 计算总共出现多少个词
        else:
            p0Num += trainMatrix[i]
            p0Denom += sum(trainMatrix[i])
    p1Vect = log(p1Num / p1Denom)  # 计算每个词语是侮辱性词汇的概率
    p0Vect = log(p0Num / p0Denom)  # log是为了防止数据太小导致下溢出
    return p0Vect, p1Vect, pAbusive

3.3分类器

根据我们确定的计算概率公式,计算出它各个分类的可能性然后进行比较,返回我们最好的决定。

# 朴素贝叶斯分类函数
def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1):
    p1 = sum(vec2Classify * p1Vec) + log(pClass1)
    p0 = sum(vec2Classify * p0Vec) + log(1.0 - pClass1)
    if p1 > p0:
        return 1
    else:
        return 0

4.完整代码

import re
from numpy import *


# 创建词汇表
def createVocabList(dataSet):
    vocabSet = set([])  # 创建一个无序且不重复的集合对象
    for document in dataSet:
        vocabSet = vocabSet | set(document)  # 创建两个集合的并集
    return list(vocabSet)


# 朴素贝叶斯词袋模型 setOfWords2Vec 升级版本
def bagOfWords2VecMN(vocabList, inputSet):
    returnVec = [0] * len(vocabList)  # 创建一个长度为vocabList的列表returnVec,并将列表中的每个元素都初始化为0
    for word in inputSet:
        if word in vocabList:
            returnVec[vocabList.index(word)] += 1  # 在词库里出现次数
    return returnVec


# 朴素贝叶斯分类器训练函数
def trainNB0(trainMatrix, trainCategory):
    numTrainDocs = len(trainMatrix)
    numWords = len(trainMatrix[0])
    pAbusive = sum(trainCategory) / float(numTrainDocs)  # 侮辱性单词出现的概率
    p0Num = ones(numWords)  # 为了防止出现 0 * x 的情况
    p1Num = ones(numWords)
    p0Denom = 2.0  # 为了防止分母为0的情况
    p1Denom = 2.0
    for i in range(numTrainDocs):
        if trainCategory[i] == 1:
            p1Num += trainMatrix[i]  # 计算每个词出现的次数
            p1Denom += sum(trainMatrix[i])  # 计算总共出现多少个词
        else:
            p0Num += trainMatrix[i]
            p0Denom += sum(trainMatrix[i])
    p1Vect = log(p1Num / p1Denom)  # 计算每个词语是侮辱性词汇的概率
    p0Vect = log(p0Num / p0Denom)  # log是为了防止数据太小导致下溢出
    return p0Vect, p1Vect, pAbusive


# 朴素贝叶斯分类函数
def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1):
    p1 = sum(vec2Classify * p1Vec) + log(pClass1)
    p0 = sum(vec2Classify * p0Vec) + log(1.0 - pClass1)
    if p1 > p0:
        return 1
    else:
        return 0


# 邮件裁词
def textParse(bigString):
    regEx = re.compile(r'\W')
    listOfTokens = regEx.split(bigString)
    var = [tok.lower() for tok in listOfTokens if len(tok) > 2]  # 只返回长度大于2的情况
    return var


def spamTest():
    docList = []
    classList = []
    fullText = []
    # 导入并解析文本文件
    for i in range(1, 26):  # 因为每个文件夹各有文件25个
        wordList = textParse(open('email/spam/%d.txt' % i, encoding="ISO-8859-1").read())  # 读取文件
        docList.append(wordList)  # 用于存储所有文件的单词列表,二维数组
        fullText.extend(wordList)  # 这个列表用于存储所有文件的单词,将它们合并成一个大的单词列表
        classList.append(1)
        wordList = textParse(open('email/ham/%d.txt' % i, encoding="ISO-8859-1").read())
        docList.append(wordList)
        fullText.extend(wordList)
        classList.append(0)
    vocabList = createVocabList(docList)  # 创建不重复的词汇表
    trainingSet = list(range(50))  # 训练集的索引,0-49(因为ham和spam各有25个,故一共有50个)
    trainingSet = trainingSet
    testSet = []
    # 随机构造训练集
    for i in range(10):  # 表示只创建10个测试集、剩下的都是训练集
        # 函数生成一个0到len(trainingSet)之间的随机浮点数,然后取整,得到一个随机的索引值randIndex。
        randIndex = int(random.uniform(0, len(trainingSet)))
        testSet.append(trainingSet[randIndex])  # 将随机选择的索引值randIndex添加到测试集列表testSet中。
        trainingSet.pop(randIndex)  # 删除训练集中对应的测试集的索引值,以确保不会重复选择作为测试集。
    trainMat = []
    trainClasses = []
    for docIndex in trainingSet:
        # 将选中的训练集索引的文本,和词汇表进行对比计算词汇出现的次数
        trainMat.append(bagOfWords2VecMN(vocabList, docList[docIndex]))
        trainClasses.append(classList[docIndex])  # 将文档的类别标签添加到trainClasses列表中
    # 调用trainNB0函数,传入训练数据的特征向量和类别标签,进行朴素贝叶斯训练,得到分类器的参数
    p0V, p1V, pSpam = trainNB0(array(trainMat), array(trainClasses))
    errorCount = 0  # 计算错误次数,初始值为0
    # 对测试集分类
    for docIndex in testSet:
        wordVector = bagOfWords2VecMN(vocabList, docList[docIndex])  # 计算出现次数
        if classifyNB(array(wordVector), p0V, p1V, pSpam) != classList[docIndex]:  # 计算结果并判断是否判断正确
            errorCount += 1
            print("classification error", docList[docIndex])
    print('the error rate is: ', float(errorCount) / len(testSet))


if __name__ == '__main__':
    spamTest()

结果截图:
在这里插入图片描述

5.总结

个人感觉该方法的分类准确率,对准备的数据以及概率计算方法很敏感,可能一点点的改变就会导致最后的统计结果天差地别。总的来说,朴素贝叶斯并不难,代码也相对比较好理解,这一章就顺利通过啦!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/904163.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Brain:背内侧前额叶/背侧前扣带皮层(dmPFC/dACC)的相关争议

摘要 背内侧前额叶皮层/背侧前扣带皮层(dmPFC/dACC)是一个功能存在诸多理论和争议的脑区。甚至其精确的解剖边界也饱受争议。在过去的几十年里,dmPFC/dACC与15种以上的认知过程相关联,这些过程有时看起来完全无关(例如,身体感知、认知冲突)。…

c++优先级队列的模拟实现代码

了解: 1.优先队列是一种容器适配器,根据严格的弱排序标准,它的第一个元素总是它所包含的元素中最大的。 2. 类似于堆,在堆中可以随时插入元素,并且只能检索最大堆元素(优先队列中位于顶部的元素)。 3. 优先队列被实现为…

开箱报告,Simulink Toolbox库模块使用指南(四)——S-Fuction模块

文章目录 前言 S-Fuction模块 电路方程模型 编写S函数 仿真验证 Tips 分析和应用 总结 前言 见《开箱报告,Simulink Toolbox库模块使用指南(一)——powergui模块》 见《开箱报告,Simulink Toolbox库模块使用指南&#xff…

三维重建 PyQt Python MRP 四视图(横断面,冠状面,矢状面,3D)

本文实现了 Python MPR 的 四视图,横断面,冠状面,矢状面,3D MPR(multi-planner reformation)也称多平面重建,多重面重建是将扫描范围内所有的轴位图像叠加起来再对某些标线标定的重组线所指定的组织进行冠状、矢状位、…

(win系统)MSVCP100/110/120/140.dll丢失 - 解决方案

首先我们来介绍一下什么是dll dll简称动态链接库它可以节省存储空间:由于DLL可以被多个程序共享,因此可以减少磁盘空间的使用。也能提高代码重用率:通过使用DLL,我们可以将一些常用的功能封装成独立的模块,从而提高代码的重用率。…

库克很高兴中国成全球最大的iPhone市场,人民日报的呼吁该重视了

日前苹果CEO库克在微博发文,庆祝苹果公司进入中国市场30周年,对员工、顾客和合作伙伴感谢,库克如此高兴的原因在于今年二季度iPhone在中国市场的销量超越美国市场,由此中国市场首次成为苹果的最大收入来源市场。 中国超越美国成为…

【C++_primary】类和对象 —— 类

类 ~ ~ ~ 一、面向过程和面向对象初步认识a. 面向过程编程b. 面向对象编程例如:无人机送货系统1、面向过程编程方式2、面向对象编程方式 二、类的引入1、定义类的关键字2、栈的手动实现a. C语言实现栈b. C实现栈 三、类的定义类的两种定义方式: 四、类的…

合宙Air724UG LuatOS-Air LVGL API--对象

对象 概念 在 LVGL 中,用户界面的基本构建块是对象。例如,按钮,标签,图像,列表,图表或文本区域。 属性 基本属性 所有对象类型都共享一些基本属性: Position (位置) Size (尺寸) Parent (父母…

ATTCK实战系列——红队实战(一)

目录 搭建环境问题 靶场环境 web 渗透 登录 phpmyadmin 应用 探测版本 写日志获得 webshell 写入哥斯拉 webshell 上线到 msf 内网信息收集 主机发现 流量转发 端口扫描 开启 socks 代理 服务探测 getshell 内网主机 浏览器配置 socks 代理 21 ftp 6002/700…

vue3 基础知识

vue3创建一个项目 PS D:\code> npm init vuelatestVue.js - The Progressive JavaScript Framework√ Add TypeScript? ... No / Yes √ Add JSX Support? ... No / Yes √ Add Vue Router for Single Page Application development? ... No / Yes √ Add Pinia for sta…

五、Spring MVC 接收请求参数以及数据回显、乱码问题

文章目录 一、Spring MVC 接收请求参数二、Spring MVC 数据回显三、SpringMVC 返回中文乱码问题 一、Spring MVC 接收请求参数 客户端或者前端通过 URL 请求传递过来的参数,在控制器中如何接收? 1、当参数和 Controller 中的方法参数一致时,无…

C语言:分支语句和循环语句(超详解)

目录 ​编辑 什么是语句? 分支语句(选择结构) if语句: 应该注意的是: switch语句: 运用练习: 循环语句 while循环: for循环: break和continue在for循环中&…

性能测试基础知识1

目录 1. 什么是性能测试1.1 生活中遇到的软件性能问题1.2 性能测试定义1.3 性能测试和功能测试的区别1.4 软件性能好 / 坏的表现1.5 影响软件性能的因素 2. 为什么要做性能测试3. 性能测试常见术语以及衡量指标(重点)并发用户数响应时间用户响应时间请求…

内网群晖NAS外网访问的3种方法?快解析内网穿透如何设置synology外网访问?

synology即是群晖nas, synology外网访问设置是大家比较关注的问题。我们通常会在本地搭建部署好存储服务器,然后在局域网内访问的同时,希望提供到互联网上实现连接访问。在外边可以通过网络随时随地的访问到NAS上的内容,这样的NAS…

【ARM v8】如何在ARM上实现x86的rdtsc()函数

博主未授权任何人或组织机构转载博主任何原创文章,感谢各位对原创的支持! 博主链接 本人就职于国际知名终端厂商,负责modem芯片研发。 在5G早期负责终端数据业务层、核心网相关的开发工作,目前牵头6G算力网络技术标准研究。 博客…

协程框架NtyCo的实现

一、为什么需要协程&#xff1f; 讨论协程之前&#xff0c;我们需要先了解同步和异步。以epoll多路复用器为例子&#xff0c;其主循环框架如下&#xff1a; while (1){int nready epoll_wait(epfd, events, EVENT_SIZE, -1);int i0;for (i0; i<nready; i){int sockfd ev…

【unity数据持久化】XML数据管理器知识点

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;Uni…

【周末闲谈】关于“数据库”你又知道多少?

个人主页&#xff1a;【&#x1f60a;个人主页】 系列专栏&#xff1a;【❤️周末闲谈】 系列目录 ✨第一周 二进制VS三进制 ✨第二周 文心一言&#xff0c;模仿还是超越&#xff1f; ✨第二周 畅想AR 文章目录 系列目录前言数据库数据库的五大特点数据库介绍数据库管理系统&a…

Linux命令200例:head用于显示文件的开头部分(常用)

&#x1f3c6;作者简介&#xff0c;黑夜开发者&#xff0c;全栈领域新星创作者✌。CSDN专家博主&#xff0c;阿里云社区专家博主&#xff0c;2023年6月csdn上海赛道top4。 &#x1f3c6;数年电商行业从业经验&#xff0c;历任核心研发工程师&#xff0c;项目技术负责人。 &…

2023-08-20 LeetCode每日一题(判断根结点是否等于子结点之和)

2023-08-20每日一题 一、题目编号 判断根结点是否等于子结点之和二、题目链接 点击跳转到题目位置 三、题目描述 给你一个 二叉树 的根结点 root&#xff0c;该二叉树由恰好 3 个结点组成&#xff1a;根结点、左子结点和右子结点。 如果根结点值等于两个子结点值之和&…