通俗易懂的机器学习——筛选垃圾邮件(贝叶斯分类)

2023-11-13

背景及应用

利用贝叶斯公式,我们可以在计算机上验证和测试垃圾邮件的字词统计,计算词语出现的概率。并根据的得出来的概率来判断一封新的邮件是垃圾邮件还是正常邮件。

贝叶斯公式

公式:
P ( A ∣ B ) = P ( B ∣ A ) ⋅ P ( A ) P ( B ) P (A|B)= \frac {P (B|A) \cdot P (A)}{P (B)} P(AB)=P(B)P(BA)P(A)
解释:在B情况出现的条件下A情况出现的概率等于在A情况出现的情况下B情况出现的概率乘以A情况出现的概率,除以B情况出现的概率。
应用:看起来公式很麻烦,但在本问题的解决中并不会直接试用,后续代码中的使用是P(A|B)=(A和B同时满足的个数)/B的个数。这样使用是同等效力的,但是会更加简单。

数据集

首先我们需要使用60000条Email的数据集,数据集连接如下:
60000条Email数据集
数据集的结构如下:
数据集结构
full文件夹下的index:包含每一条Email的标签和对应的文件位置。
data文件夹下的每个文件夹都有若干条Email数据。

idex中数据组织形式:
index数据组织形式
Email:
Email

引入依赖包

import re
import jieba
import numpy as np

re:正则表达式用于过滤非中文字符
jieba:用于做中文分词
numpy:用于划分训练集和测试集

数据预处理

在进行统计之前我们要先进行数据预处理以保证统计能够顺利进行。

全局变量

EmailsIndex = "./full/index"

过滤所有非中文词语

def filterEmail(email):
    email = re.sub("[a-zA-Z0-9_]+|\W","",email)
    return email

使用正则表达式过滤掉非中文词
原理:按照字母,数字和下划线划分email中的文本

读取邮件

def readEmail(filename):
    with  open(filename, "r",encoding='GB2312', errors='ignore') as fp:
        content=fp.read()
        content = filterEmail(content) #把一封邮件提取所有中文内容
        words = list(jieba.cut(content))
    return words

读取一封Email:首先按照传进来的filename(文件路径)打开txt文件,然后调用filterEmail函数过滤掉非中文字符,过滤后content中不含非中文字符。最后使用jieba.cut对中文进行分词,分词后words中为中文词组。
注:数据集的编码方式要按照GB2312打开

加载邮件

def loadAllEmails(IndexFile):
    Emails=[]
    with  open(IndexFile, "r") as fp:
        lines=fp.readlines()
        for line in lines:
            spam,filename = line.split()
            Emails.append((spam,readEmail(filename)))
    return Emails

传进来的IndexFile参数是index的路径
打开index文件后读取每一行数据,每一行数据包含邮件类别和邮件路径,使用split将他们分离开,调用readEmail获得邮件路径对应的邮件的中文分词。

获取概率表

划分训练集和测试集

def train_test_split(Emails,testSize):
    arrayEmails = np.array(Emails)
    test_size = int(len(Emails)*testSize)
    shuffle_indexes = np.random.permutation(len(arrayEmails))
    test_indexs,train_indexs= np.split(shuffle_indexes,[test_size])
    return arrayEmails[train_indexs],arrayEmails[test_indexs]

计算分词的概率表

def calWordsFreqTable(Emails):
    wordsSet = []
    spamWords = dict()
    hamWords = dict()
    spamlength = 0
    hamlength = 0
    for elem in Emails:
        if elem[0] == "spam":
            spamlength += 1
            tempset = set()
            for word in elem[1]:
                if word not in tempset:
                    tempset.add(word)
                    if word in wordsSet:
                        spamWords[word] += 1
                    else:
                        wordsSet.append(word)
                        spamWords[word] = 1
                        hamWords[word] = 0
        elif elem[0] == "ham":
            hamlength += 1
            tempset = set()
            for word in elem[1]:
                if word not in tempset:
                    tempset.add(word)
                    if word in wordsSet:
                        hamWords[word] += 1
                    else:
                        wordsSet.append(word)
                        hamWords[word] = 1
                        spamWords[word] = 0

    wordsProbs = []  # 格式(word, spamprob, hamprob)

    for word in wordsSet:
        wordsProbs.append((word, spamWords[word] * 1.0 / (spamWords[word] + hamWords[word]), hamWords[word] * 1.0 / (spamWords[word] + hamWords[word])))

    return wordsProbs

变量含义:
wordsSet:词典,在遍历Email时逐渐将词典中没有的词添加到词典
spamWords:分词与其在垃圾邮件中出现的次数组成的字典
hamWords:分词与其在正常邮件中出现的次数组成的字典
spamlength:总垃圾邮件数
hamlength:总正常邮件数
tempset:用来判断在一封Email内某词是否已经出现过,如果已经出现,则不予统计。

保存概率表

def saveWordsTable(wordsProbs, filepath):
    import os
    with open(os.path.join(filepath, "probs.txt"), "w", encoding="utf-8") as f:
        for prob in wordsProbs:
            f.write(prob[0])
            f.write("\t")
            f.write(str(prob[1]))
            f.write("\t")
            f.write(str(prob[2]))
            f.write('\n')

检查邮件是否为垃圾文件

获取分词对应的概率字典和字典

def getProbDict_WordsSet(filepath):
    ProbsDict = dict()
    wordsSet = list()
    import os
    with open(os.path.join(filepath, "probs.txt"), "r", encoding="utf-8") as f:
        lines = f.readlines()
        for line in lines:
            word, spamprob, hamprob = line.split()
            wordsSet.append(word)
            ProbsDict[word] = (float(spamprob), float(hamprob))

    return ProbsDict, wordsSet

初步检查一封邮件

def checkOneEmail(ProbsDict, wordsSet, email):
    spamprob = 1
    hamprob = 1
    for word in email:
        if word in wordsSet:
            hamprob *= max((1 - ProbsDict[word][0]), 0.01)
            spamprob *= max((1 - ProbsDict[word][1]), 0.01)
        if hamprob == 0 or spamprob == 0:
            break
    if spamprob > hamprob:
        return spamprob, 1
    elif spamprob < hamprob:
        return hamprob, -1
    else:
        return 0, 0

hamprob *= max((1 - ProbsDict[word][0]), 0.01)中1 - ProbsDict[word][0]的含义是该词不是来着垃圾邮件的概率,使用max是为了防止获得的概率过低,同时用1来表示垃圾邮件,-1表示正常邮件。当垃圾邮件和正常邮件的概率相等时用0表示未能判断出是否为垃圾邮件。

检查多封邮件

def checkEmails(ProbsDict, wordsSet, emails):
    rates = []
    predict_flags = []
    for email in emails:
        rate, ans = checkOneEmail(ProbsDict, wordsSet, email)
        rates.append(rate)
        predict_flags.append(ans)
    return rates, predict_flags  # 返回准确率和每一封邮件的预测值

主程序

if __name__ == "__main__":
    opt = int(input("1.通过训练集获取概率并在测试集测试 2.单个邮件预测"))
    if opt == 1:
        Emails = loadAllEmails(EmailsIndex)
        trainEmails, testEmails = train_test_split(Emails, 0.2)
        wordsProbs = calWordsFreqTable(trainEmails)
        saveWordsTable(wordsProbs, filepath='')
        ProbsDict, wordsSet = getProbDict_WordsSet(filepath='')
        rate, ans = checkEmails(ProbsDict, wordsSet, testEmails[:,1])
        t = 0
        for i in range(len(ans)):
            if ans[i] == 1 and testEmails[i][0] == 'spam':
                t += 1
            elif ans[i] == -1 and testEmails[i][0] == 'ham':
                t += 1
        print("精度:", t / len(ans))
    else:
        opt = int(input("1.手动输入 2.打开"))
        if opt == 1:
            text = input("输入邮件:")
            Email = filterEmail(text)
            ProbsDict, wordsSet = getProbDict_WordsSet(filepath='')
            rate, ans = checkOneEmail(ProbsDict, wordsSet, Email)
            if ans == 1:
                print("垃圾邮件")
            elif ans == -1:
                print("有效邮件")
            else:
                print("未知邮件")
        else:
            path = input("请输入邮件的路径:")  # 路径用”/“而不是”\’“
            text = ""
            with open(path, encoding="utf-8") as f:
                for line in f:
                    text += line
            Email = filterEmail(text)
            ProbsDict, wordsSet = getProbDict_WordsSet(filepath='')
            rate, ans = checkOneEmail(ProbsDict, wordsSet, Email)
            if ans == 1:
                print("垃圾邮件")
            elif ans == -1:
                print("有效邮件")
            else:
                print("未知邮件")

注:在首次使用时必须先选择1来获取概率,生成时间比较长,可以在下方下载,下载后可以直接使用。
分词概率下载

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

通俗易懂的机器学习——筛选垃圾邮件(贝叶斯分类) 的相关文章

  • 【H5】canvas画布像素的设置与获取:

    H5 canvas画布像素的设置与获取 getImageData 从Canvas画板上取得指定位置的像素数据 putImageData 将所得到的像素数据描画到Canvas画板上 createImageData 方法创建新的空白像素 Ima
  • IPSec基础知识

    文章目录 IPSec基础知识 IPSec特性 IPSec组成部分 IPSec对等体 IPSec隧道 安全联盟 Security Association AH安全协议 AH包结构 ESP安全协议 ESP包结构 AH和ESP比较 封装模式 传输
  • 解决Rational Rose找不到suite objects.dll文件的问题

    问题描述 打开Rational Rose 2007时 发现有以下问题 提示找不到suite objects dll文件 需要重装软件 但是查看Rational Rose 2007安装文件夹 发现Common文件夹下有suite object
  • Windows系统管理七:本地组策略&注册表及其维护与优化

    本地组策略 概述 组策略 英语 Group Policy 是微软 Windows NT 家族操作系统的一个特性 它可以控制用户帐户和计算机帐户的工作环境 组策略提供了操作系统 应用程序和活动目录中用户设置的集中化管理和配置 组策略的其中个版
  • Ceph 集群在线迁移方案

    一 环境准备 1 1 场景介绍 最近收到一个需求 客户希望将运行了多年的ceph集群服务器全部更换掉 因为这些老服务器性能和容量都已经无法满足当前业务的需求 并希望在迁移到新服务器的过程中 业务不中断 在参考一些网上的方案后 选择了一个方案
  • 解决Error response from daemon: Get https://registry-1.docker.io/v2/library/hello-world/manifests/问题

    昨天在使用docker 时 将 image 文件从仓库抓取到本地一直报错 经过尝试 终于得以解决 错误信息如下 root archlinux docker image pull library hello world Using defau
  • ROS仿真机器人(安装、配置、测试、建图、定位、路径规划)

    ROS机器人仿真 安装 配置 测试 建图 定位 路径规划 1 ROS安装与配置 1 1 安装虚拟机软件 1 2 虚拟一台主机 1 3 安装ubuntu 1 4 在ubuntu中安装ROS机器人操作系统 1 4 1 配置ubuntu的软件和更
  • Exams/m2014 q4k

    Implement the following circuit module top module input clk input resetn synchronous reset input in output out reg 2 0 t
  • 香港经典古装电视剧

    楚留香 射雕英雄传 倚天屠龙记 金枝欲孽 大唐双龙传 寻秦记 萧十一郎 小李飞刀 云海玉弓缘 西游记 封神榜 洗冤录 锦绣前程 杨门女将 杨家将 笑傲江湖 苏乞儿 绝代双骄 鹿鼎记 成吉思汗 神雕侠侣 陆小凤 书剑恩仇录 啼笑姻缘 http
  • Java I/O (第二版) I/O基础 I/O概述

    第一部分 第一章 介绍I O 输入和输出 的简写I O 它是任何操作系统和程序设计语言所必须的基础功能 只有空想家才会喜欢没有输入输出的程序 同时 IO的话题似乎对程序员没有什么吸引力 其实不然 我们应该有很多有趣的东西需要学习在IO中 J
  • react从入门到精通总结(一)

    React简介 React 是一个用于构建用户界面的 JS 库 主要用于构建UI React由美国的公司Facebook在2011年诞生并于2013年开源发布 特点 1 声明式设计 React采用声明范式 可以轻松描述应用 2 高效使用虚拟
  • Django 连接redis

    安装 pip install django redis redis安装 docker pull redis latest docker run d name redis p 6379 6379 redis requirepass passw
  • STL容器

    这里写目录标题 三大组件介绍 1 容器 2 算法 3 迭代器 STL容器 string容器 vector容器 deque容器 stack容器 queue容器 list容器 set multiset容器 map multimap容器 常见面试
  • 【LeetCode第1场双周赛】

    第 1 场双周赛 A 模拟 class Solution public int fixedPoint vector
  • 计算机网络设备子系统,网络综合布线七大子系统详细讲解

    打开APP 查看更多精彩图片 在实施时主要注意以下几点 1 确定介质布线方法和线缆走向 2 双绞线的长度一般不超过90m 3 尽量避免水平线路长距离与供电线路平行走线 应保持一定的距离 非屏蔽缆线一般为30cm 屏蔽缆线一般为7cm 4 线
  • 基于STM32的LVGL的实时日历设置界面

    基于STM32的LVGL的实时日历设置界面 提示 STM32 RTC littlevgl GUI 设置时间代码 if obj Set time btn 点击对象为设置按钮 if event LV EVENT RELEASED uint8 t
  • 轻量级Transformer模型ConvBERT架构及完整源码实现

    第28章 使用Local dependency轻量级Transformer模型ConvBERT架构内幕及完整源码实现 1 BERT依赖global self attention而带来的问题分析 2 BERT不同Layer的computati
  • 向电脑(windows11 OS环境)的所有外挂移动硬盘写入数据提示目标文件夹访问被拒绝的解决方法

    向电脑 windows11 OS环境 的所有外挂移动硬盘写入数据 就是在粘贴文件 出现以下提示框 解决办法流程如下 运行窗口 输入 gpedit msc 命令 按 确定或回车 快速打开本地组策略编辑器 本地组策略编辑器窗口 依次展开 计算机

随机推荐

  • esp32-S3专题二:内存2之RTC内存、FLASH使用

    承接上文 讲一下esp32上剩下的几个存储空间的用途 目录 一 RTC存储器 一 RTC 快速存储器 二 RTC 慢速存储器 二 Flash 一 NVS 表 二 程序和OTA分区 三 SPIFFS 文件系统 三 总结 一 RTC存储器 es
  • 不安全的加密算法

    不安全的加密算法 SKIPJACK RC4 RSA 1024位以下 des md2 md4 md5 属于经过验证 安全的 公开的加密算法 RSA DSA ECDSA 加入盐值的SHA256 推荐使用的数字签名算法有 DSA ECDSA
  • 值得重点推荐的BI软件有哪些?全在这里了

    在数字化程度越来越高的趋势下 发挥着推动企业数字化转型重要作用的商业智能BI也在不断提升市场份额 也催生出不少的BI软件新星 那么 BI软件有哪些 哪些BI软件更普遍适用于不同行业 针对这些问题 我们做了以下总结 BI软件有哪些 国内比较老
  • goland missing dependency错误

    新建一个项目 里面引用到第三方的包 项目编译没有问题 可是在goland里按住ctrl 鼠标点击打算跳转到第三方包相应的实现时却跳转不了 import的第三方也标红 同时go mod文件该第三方包也是标红的 看这篇文章 出现的问题和我的比较
  • moviepy音视频开发:音频拼接函数concatenate_audioclips介绍

    前往老猿Python博文目录 concatenate audioclips函数用于将多个音频剪辑进行拼接合成一个顺序播放的剪辑 调用语法 concatenate audioclips clips 说明 clips参数用于存放需要顺序拼接的音
  • 【备忘】Linux上传文件,下载文件

    背景小故事 这次在迁移服务器的时候要把ssl文件传到Linux里面 但是我又不想用ide来传 之前看同事演示过 当时没记住 感觉他简单操作就可以了 这次打算学会它 这功能用得不多 平常都不怎么使用 我们也可以用客户端连接服务器进行上传 方法
  • Android 设备 设置adb自动监听tcp 5555端口,重启有效

    Android 设备调试有两种连线方式 有线和无线 有线是通过USB导线连接android设备和电脑端 无线方式是通过连接WIFI 通过TCP的方式 连接设备和电脑端 一般用 5555端口 有线的调节 只需要打开调试模式基本就可以了 无线连
  • VirtualBox 安装32位Windows 7和64位Windows Sever 2008R2 双系统图文教程——利用VHD技术

    VirtualBox 安装32位Windows 7和64位Windows Sever 2008R2 双系统图文教程 利用VHD技术 先说说为什么要装双系统 因为想用一个系统只是用来休闲娱乐上网 聊天 看视频等等 所以这个时候坚决抵制一切数据
  • 【问题解决记录】无法识别的标志“-sdlMode”,在“p2”中

    问题解决记录 无法识别的标志 sdlMode 在 p2 中 参考文章 1 问题解决记录 无法识别的标志 sdlMode 在 p2 中 2 https www cnblogs com dbylk p 5133739 html 3 https
  • 30天自制操作系统第4天harib01a

    30天自制操作系统 第4天 C语言与画面显示的练习 用C语言实现内存写入 harib01a harib01a 准备材料 windows环境 VMware Workstation Visual Studio Code 程序和源代码 https
  • 如何为你的网站添加二级域名?

    一般公司注册的域名支持添加20个二级域名 辅助域名的添加与www的主域名相同 填写二级域名的名称 如bbs 并填写二级域名对应的IP 如果你的域名是www 98yun com bbs 这是一个论坛 你想添加一个bbs 98yun com 这
  • 大数据之——Zookeeper个人讲解

    一 zookeeper的概念 1 ZooKeeper是一个分布式的 开放源码的分布式应用程序协调服务 是Google的Chubby一个开源的实现 是Hadoop和Hbase的重要组件 它是一个为分布式应用提供一致性服务的软件 提供的功能包括
  • 用IDEA编写第一个JAVA程序

    第一步 打开IDEA IDEA下载链接 IntelliJ IDEA The Capable Ergonomic Java IDE by JetBrains 第二步 选择新建一个空项目 第三步 java项目的文件层次是 项目 包 类 创建一个
  • 6.2小车循迹,跟随,摇头测距功能实现

    1 循迹小车 1循迹模块的使用 TCRT5000传感器的红外发射二极管不断发射红外线 当发射出的红外线没有被反射回来或被反射回来但强度不够大时 红外接收管一直处于关断状态 此时模块的输出端为高电平 指示二极管一直处于熄灭状态 被检测物体出现
  • <1> linux与shell的关系

    在正式学习shell命令之前 如果你还对linux与shell的关系不甚了解 请耐心看完这篇简介 本文简要梳理了操作系统 shell的关系和作用 一个完整的计算机 整体结构如下图 我们的计算机由硬件组成 包括CPU 内存 主板 磁盘 各种外
  • 防关联软件技术分析之跨境电商防关联云服务器之超级VPS管理器

    众所周知 亚马逊有一个非常坑爹的规定 那就是同一个人或企业只能运营一个账号 并采用严格的程序对此进行监控 如果发现账户关联 则存在封号的风险 这就意味着 在同一个外网IP环境下操作多个账号是非常危险的 然而 我相信很多卖家手里都会有多个亚马
  • Java IO流

    Java的IO通过java io包下的类和接口来支持 在java io包下主要包括输入流 输出流两种IO流 每种输入 输出流又可分为字节流和字符流两大类 其中字节流以字节为单位来处理输入 输出操作 而字符流则以字符来处理输入 输出操作 1
  • java IO框架分析

    jave io框架 2010 11 10 22 18 34 分类 默认分类 举报 字号 订阅 可从IO的类层次 IO框架的设计模式来论述 总体来说 IO可以分为字节流和字符流 不同在于字符流由字节流包装而来 在IO读入之后经过JVM处理 把
  • 编译语言、解释语言与脚本语言之间的区别

    资料一 1 解释型语言与编译型语言的区别翻译时间的不同 编译型语言在程序执行之前 有一个单独的编译过程 将程序翻译成机器语言 以后执行这个程序的时候 就不用再进行翻译了 解释型语言 是在运行的时候将程序翻译成机器语言 所以运行速度相对于编译
  • 通俗易懂的机器学习——筛选垃圾邮件(贝叶斯分类)

    筛选垃圾邮件 贝叶斯分类 背景及应用 贝叶斯公式 数据集 引入依赖包 数据预处理 全局变量 过滤所有非中文词语 读取邮件 加载邮件 获取概率表 划分训练集和测试集 计算分词的概率表 保存概率表 检查邮件是否为垃圾文件 获取分词对应的概率字典