自实现朴素贝叶斯分类器with案例:基于SMS Spam Collection数据集的广告邮件分类

这篇具有很好参考价值的文章主要介绍了自实现朴素贝叶斯分类器with案例:基于SMS Spam Collection数据集的广告邮件分类。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


贝叶斯分类器

首先要理解贝叶斯决策的理论依据,引用西瓜书上的原话:对于分类任务,在所有相关概率都已知的理想情形下,贝叶斯决策论考虑如何基于这些概率误判损失来选择最优的类别标记。

然后引入我们很熟悉的贝叶斯公式:
P ( c ∣ x ) = P ( c ) P ( x ∣ c ) P ( x ) P(c\mid \boldsymbol{x}) = \frac{P(c)P(\boldsymbol{x} \mid c)}{P(\boldsymbol{x})} P(cx)=P(x)P(c)P(xc)
其中 c c c 是类别标记, x x x 是样本点(一个包含n维属性的向量)。 P ( c ) P(c) P(c)就是所谓的“先验”概率,这个概率是可以通过数据集统计直接得到的,那么 P ( c ∣ x ) P(c \mid \boldsymbol{x}) P(cx)就是所谓的“后验“概率,即我们要在已有数据的信息背景下推断得到的。


与其它机器学习的算法不同,贝叶斯分类算法似乎看不出一个明显的待训练参数,但观察公式也能明白,我们要求出的 P ( c ∣ x ) P(c \mid \boldsymbol{x}) P(cx)是由 P ( c ) 、 P ( x ∣ c ) P(c)、P(\boldsymbol{x} \mid c) P(c)P(xc)以及 P ( x ) P(\boldsymbol{x}) P(x)三者变量所共同决定的,而这三者的现实意义其实就是给定的信息背景(数据集)——多数情况下,我们在不同的信息背景下总能得到不同的 P ( c ∣ x ) 、 P ( c ) 、 P ( x ∣ c ) P(c \mid \boldsymbol{x})、P(c)、P(\boldsymbol{x} \mid c) P(cx)P(c)P(xc),进而推出不同的 P ( c ∣ x ) P(c \mid \boldsymbol{x}) P(cx)

有些信息背景对于作出决策的贡献是“好的”,这时 P ( c ∣ x ) P(c \mid \boldsymbol{x}) P(cx)体现出来的意义能很真实地反映出作出某项决策的正确性,而在有些信息背景(比如样本过于稀疏)下得出的结果就并不能很好地反映待检测样本所属的真实类别,进而造成误分类。

于是Bayes分类器的训练意义在于寻求“好的”数据集,使得后验概率值能较好地反映出决策的真实性。


何为朴素

从概率学原理来讲,类条件概率 P ( x ∣ c ) P(\boldsymbol{x} \mid c) P(xc),是所有属性上的联合概率,很难从有限的训练样本直接估计而得。那么为避开这个障碍,朴素贝叶斯分类器采用了“属性条件独立性假设”:对已知类别假设所有属性之间相互独立

此时类条件概率满足:
P ( x ∣ c ) = ∏ i = 1 d P ( x i ∣ c ) P(\boldsymbol{x} \mid c)=\prod_{i=1}^{d}P(x_i \mid c) P(xc)=i=1dP(xic)
其中 d d d 代表样本点的属性个数, x i x_i xi 代表 x \boldsymbol{x} x的各个属性。

于是开头的贝叶斯公式进一步推:
P ( c ∣ x ) = P ( c ) P ( x ∣ c ) P ( x ) = P ( c ) ∏ i = 1 d P ( x i ∣ c ) P ( x ) P(c\mid \boldsymbol{x}) = \frac{P(c)P(\boldsymbol{x} \mid c)}{P(\boldsymbol{x})}=\frac{P(c)\prod_{i=1}^{d}P(x_i \mid c)}{P(\boldsymbol{x})} P(cx)=P(x)P(c)P(xc)=P(x)P(c)i=1dP(xic)
于是在此假设前提下,进而大大地简化了计算,这也正是“朴素(Naive)”一词修饰的由来。

而且在一般分类任务下,是不会计算 P ( x ) P(\boldsymbol x) P(x)的,而是只计算分子便进行比较。


案例:基于SMS Spam Collection数据集的广告邮件分类

SMS数据集

SMS Spam Collection是用于广告短信识别的经典数据集,完全来自真实短信内容,包括4831条正常短信和747条广告短信。

其内容如下,每一个line都表示一段邮件,"ham"和"spam"分别表示邮件的类别“正常邮件”和“广告邮件”,然后以'\t'为间隔右边的长字符串为邮件内容。

自实现朴素贝叶斯分类器with案例:基于SMS Spam Collection数据集的广告邮件分类


词向量表示

  • 每个词语的出现各看成一个事件,先分别计算单个词语的事件概率进行训练,然后将一个完整的邮件看成这些事件的交事件。

给定一系列邮件的文本,将每个邮件的关键词提取出来,我们认为长度>2的单词才为关键词,然后将这些关键词转换为小写,其组成的列表作为一个数据样本,所以加载数据集如下

# 创建数据集,加载数据
adClass = 1  # 广告,垃圾标识

def textParse(bigString):
    '''接收一个长字符串解析成字符串列表'''
    #将特殊符号作为划分标志进行字符串切分,即非字母,非数字
    listOfTokens = re.split(r'\W+', bigString)
    #除单字母  其它单词全变成小写
    return [tok.lower() for tok in listOfTokens if len(tok) > 2]

def loadDataSet():
    '''加载数据集合及其对应的分类'''
    classVec = []    #0-1列表 第i个元素标识了wordList第i行类别
    wordList = []    #提取出的词矩阵,每一行是对应一个邮件的单词列表
    
    smss = open("./SMSSpamCollection.txt", 'r', encoding = 'utf-8')
    data = csv.reader(smss, delimiter = '\t')
    for line in data:      #line:左边一个"ham" or "spam",右边一个大字符串
        if line[0] == "ham":
            classVec.append(0)
        else:
            classVec.append(1)
        wordList.append(textParse(line[1]))

    return wordList, classVec

打印数据样本,可以看到邮件文本以及它的关键词列表:

自实现朴素贝叶斯分类器with案例:基于SMS Spam Collection数据集的广告邮件分类


然后,将这些词全放在一起,构成一个“语料库”。

def doc2VecList(docList):		#docList是一个二维矩阵,每行表示一个邮件的关键词组成的列表
    """数据进行并集操作,最后返回一个词不重复的并集"""
    a = list(reduce(lambda x, y:set(x) | set(y), docList))
    return a

这么做的意义在于我们要改变这个数据样本的表示方式(否则不利于概率计算),在这里就是用词向量的表示方法:

对于一个数据样本,将其视作一个长度为 n = ∣ 语料库中词的个数 ∣ n=\left | 语料库中词的个数 \right | n=语料库中词的个数 的01向量,如果样本某个词在语料库中出现了,那就在这个词的对应位置记1,否则记0.

于是该词向量就有了n个属性,每个属性取值∈{0,1}。

def words2Vec(vecList, inputWords):     #vecList:语料库,inputWords:输入的词组
    '''把单词转化为词向量'''
    dimensions = len(vecList)
    resultVec = [0] * dimensions
    for i in range(dimensions):
        if vecList[i] in inputWords:
            resultVec[i] += 1
    #转化为一维数组
    return array(resultVec)

Laplacian平滑

接下来就是计算

  1. P ( c ) P(c) P(c)
  2. P ( x ∣ c ) = ∏ i = 1 d P ( x i ∣ c ) P(\boldsymbol{x} \mid c)=\prod_{i=1}^{d}P(x_i \mid c) P(xc)=i=1dP(xic)

但这里尤其需要注意的是待分类样本词向量中可能存在“词没有记录在语料库”中的情况,也即它属于任何类别的概率值为0,显然会导致 ∏ i = 1 d P ( x i ∣ c ) \prod_{i=1}^{d}P(x_i \mid c) i=1dP(xic)直接变成0不合理,因此进行**+1的Laplacian平滑处理**:
P ^ ( c ) = ∣ D c ∣ + 1 ∣ D ∣ + N \hat{P}{(c)} = \frac{|D_c| + 1} {|D|+N} P^(c)=D+NDc+1

P ^ ( x i ∣ c ) = ∣ D c , x i ∣ + 1 ∣ D c ∣ + N i \hat{P}({x_i\mid c}) = \frac{|D_{c,x_i}| + 1} {|D_c|+N_i} P^(xic)=Dc+NiDc,xi+1

  • 也就是令没出现过的词“所属类别0和类别1的次数均+1”,“类别0的样本数和类别1的样本数均+2(类别数)”。

在代码中体现为:

'''Laplacian +1平滑'''
# 全部都初始化为1(全1数组), 防止出现概率为0的情况
p0Num = ones(numWords)
p1Num = ones(numWords)
# 相应的单词初始化为2
p0Words = 2.0
p1Words = 2.0

训练过程

:这里的训练过程跟测试集的内容无关系,而是一种预存储*。

首先,根据训练集计算从 i ∈ i\in i[1~ n ] n] n]所有 P ( x i ∣ 0 ) P(x_i \mid 0) P(xi0) P ( x i ∣ 1 ) P(x_i \mid 1) P(xi1),在这里 x i x_i xi指代的语料库中第 i i i个词,然后存到两个数组里——p0Vecp1Vec,在这里有个小技巧就是在存储时对每个P值都取了一个log运算,这样可以扩大数值域,方便后面的计算。

# 统计每个分类的词的总数
    for i in range(numTrainClass):
        if trainClass[i] == 1:
            # 数组在对应的位置上相加
            p1Num += trainMatrix[i]
            p1Words += sum(trainMatrix[i])
        else:
            p0Num += trainMatrix[i]
            p0Words += sum(trainMatrix[i])

    # 计算每种类型里面, 每个单词出现的概率
    # 在计算过程中,由于概率的值较小,于是取对数扩大数值域
    p0Vec = log(p0Num / p0Words)	#P(所有xi|0)
    p1Vec = log(p1Num / p1Words)	#P(所有xi|1)
    # 计算在类别中1出现的概率,0出现的概率可通过1-p得到
    pClass1 = sum(trainClass) / float(numTrainClass)	#P(c=1)

分类过程

对于测试集的词向量testVec存在如下关系:
∏ i = 1 d P ( x i ∣ 1 或 0 ) = ∏ i ∈ { i ∣ t e s t V e c [ i ] = 1 } P ( x i ∣ 1 或 0 ) \prod_{i=1}^{d}P(x_i \mid 1或0)=\prod_{i\in\{i\mid testVec[i] = 1\}}P(x_i \mid 1或0) i=1dP(xi10)=i{itestVec[i]=1}P(xi10)
也就是说,只需要testVec * p0VectestVec * p1Vec 便可分别得到在testVec第i个位置上为1 P ( x i ∣ 0 ) P(x_i \mid 0) P(xi0) P ( x i ∣ 1 ) P(x_i \mid 1) P(xi1)了!

所以只要计算 P ( c ) ∏ i ∈ { i ∣ t e s t V e c [ i ] = 1 } P ( x i ∣ 1 或 0 ) P(c)\prod_{i\in\{i\mid testVec[i] = 1\}}P(x_i \mid 1或0) P(c)i{itestVec[i]=1}P(xi10)便得到两个后验概率,同样根据对数特性 l n [ P ( c ) × P ( X 1 ∣ c ) × P ( X 2 ∣ c ) × . . . × P ( X n ∣ c ) ] = l n P ( c ) + l n P ( X 1 ∣ c ) + . . . + l n P ( X n ∣ c ) ln[{ P(c)×P(X1|c)×P(X2|c)×...×P(Xn|c)}] = lnP(c) + lnP(X1|c) + ... + lnP(Xn|c) ln[P(c)×P(X1∣c)×P(X2∣c)×...×P(Xnc)]=lnP(c)+lnP(X1∣c)+...+lnP(Xnc),将乘法改成加法:文章来源地址https://www.toymoban.com/news/detail-465076.html

def classifyNB(testVec, p0Vec, p1Vec, pClass1):
    """分类, 返回分类结果 0 or 1"""
    # 因为概率的值太小了,所以乘法改加法,可以简化计算且不失精度
    p1 = sum(testVec * p1Vec) + log(pClass1)
    p0 = sum(testVec * p0Vec) + log(1 - pClass1)
    if p0 > p1:
        return 0
    return 1

完整代码

#导入所需库文件
from numpy import *
from functools import reduce
import re
import csv

def textParse(bigString):
    '''接收一个长字符串解析成字符串列表'''
    #将特殊符号作为划分标志进行字符串切分,即非字母,非数字
    listOfTokens = re.split(r'\W+', bigString)
    #除单字母  其它单词全变成小写
    return [tok.lower() for tok in listOfTokens if len(tok) > 2]

# 创建数据集,加载数据
adClass = 1  # 广告,垃圾标识

def loadDataSet():
    '''加载数据集合及其对应的分类'''
    classVec = []  # 0-1列表 第i个元素标识了wordList第i行类别
    wordList = []  # 提取出的词矩阵,每一行是对应一个邮件的单词列表

    smss = open("./SMSSpamCollection.txt", 'r', encoding='utf-8')
    data = csv.reader(smss, delimiter='\t')
    for line in data:  # line:左边一个"ham" or "spam",右边一个大字符串
        if line[0] == "ham":
            classVec.append(0)
        else:
            classVec.append(1)
        wordList.append(textParse(line[1]))

    return wordList, classVec

def doc2VecList(docList):
    """函数说明:数据进行并集操作,最后返回一个词不重复的并集"""
    #reduce(function, iterable[, initializer]): 从左至右积累地应用到 iterable 的条目,以便将该可迭代对象缩减为单一的值
    a = list(reduce(lambda x, y:set(x) | set(y), docList))
    return a  #['','',...,'']

def words2Vec(vecList, inputWords):     #所有词,输入的词组
    '''把单词转化为词向量'''
    dimensions = len(vecList)
    resultVec = [0] * dimensions
    for i in range(dimensions):
        if vecList[i] in inputWords:
            resultVec[i] += 1
    #转化为一维数组
    return array(resultVec)


def trainNB(trainMatrix, trainClass):
    """函数说明:计算,生成每个词对于类别上的概率"""
    # 类别行数
    numTrainClass = len(trainClass)
    # 列数
    numWords = len(trainMatrix[0])

    # 全部都初始化为1(全1数组), 防止出现概率为0的情况出现
    p0Num = ones(numWords)
    p1Num = ones(numWords)

    # 相应的单词初始化为2
    p0Words = 2.0
    p1Words = 2.0

    # 统计每个分类的词的总数
    for i in range(numTrainClass):
        if trainClass[i] == 1:
            # 数组在对应的位置上相加
            p1Num += trainMatrix[i]
            p1Words += sum(trainMatrix[i])
        else:
            p0Num += trainMatrix[i]
            p0Words += sum(trainMatrix[i])

    # 计算每种类型里面, 每个单词出现的概率
    # 在计算过程中,由于概率的值较小,所以我们就取对数进行比较,其中ln可替换为log的任意对数底
    p0Vec = log(p0Num / p0Words)
    p1Vec = log(p1Num / p1Words)

    # 计算在类别中1出现的概率,0出现的概率可通过1-p得到
    pClass1 = sum(trainClass) / float(numTrainClass)
    return p0Vec, p1Vec, pClass1

def classifyNB(testVec, p0Vec, p1Vec, pClass1):
    """分类, 返回分类结果 0 or 1"""
    # 因为概率的值太小了,所以乘法改加法
    # 根据对数特性ln{ P(c)×P(X1|c)×P(X2|c)×...×P(Xn|c) } = lnP(c) + lnP(X1|c) + ... + lnP(Xn|c)
    # 可以简化计算且不失精度
    '''test * pVec已经在trainNB中取过对数了直接相加'''
    p1 = sum(testVec * p1Vec) + log(pClass1)
    p0 = sum(testVec * p0Vec) + log(1 - pClass1)
    if p0 > p1:
        return 0
    return 1

def printClass(words, testClass):
    if testClass == adClass:
        print(words, '推测为:广告邮件')
    else:
        print(words, '推测为:正常邮件')


def tNB():
    # 加载训练数据集
    docList, classVec = loadDataSet()  # 单词矩阵、 01向量

    # 生成包含所有单词的list
    allWordsVec = doc2VecList(docList)

    # 构建词向量矩阵
    '''lambda中的x对应docList的每一行词组'''
    trainMat = list(map(lambda x: words2Vec(allWordsVec, x), docList))  # 和docList对应的行(词)向量组

    # 训练计算每个词在分类上的概率
    # 其中p0V:每个单词在“非”分类出现的概率, p1V:每个单词在“是”分类出现的概率  pClass1:类别中是1的概率
    p0V, p1V, pClass1 = trainNB(trainMat, classVec)

    # 测试数据集
    text1 = "As a valued cutomer, I am pleased to advise you that following recent review of your Mob No"
    testwords1 = textParse(text1)
    testVec1 = words2Vec(allWordsVec, testwords1)
    # 通过将单词向量testVec代入,根据贝叶斯公式,比较各个类别的后验概率,判断当前数据的分类情况
    testClass1 = classifyNB(testVec1, p0V, p1V, pClass1)
    # 打印出测试结果
    printClass(testwords1, testClass1)

    text2 = "Please don't text me anymore. I have nothing else to say"
    testwords2 = textParse(text2)
    testVec2 = words2Vec(allWordsVec, testwords2)
    testClass2 = classifyNB(testVec2, p0V, p1V, pClass1)
    printClass(testwords2, testClass2)

if __name__ == '__main__':
    tNB()

到了这里,关于自实现朴素贝叶斯分类器with案例:基于SMS Spam Collection数据集的广告邮件分类的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • python:基于朴素贝叶斯算法的垃圾邮件过滤分类

    目录 一、朴素贝叶斯算法 1.概述 2.推导过程 二、实现垃圾邮件过滤分类 1.垃圾邮件问题背景 2.朴素贝叶斯算法实现垃圾邮件分类的步骤 3.python实现 参考学习网址:https://blog.csdn.net/weixin_59450364/article/details/124343350 1.概述        朴素贝叶斯法(Naive Bayes model)是基于贝叶斯定

    2024年02月07日
    浏览(74)
  • 基于朴素贝叶斯分类器的西瓜数据集(实战)

    最近刚开始学习机器学习中的朴素贝叶斯分类器,用西瓜数据集做了一下,最后结果预测正确率75%,其中运用到的python语法并不复杂,适合小白观看。 目录 朴素贝叶斯分类器思想的自然语言描述: 详细步骤在代码中说明 朴素贝叶斯分类器其实就是计算先验概率和每一个属性

    2024年02月11日
    浏览(25)
  • 机器学习:基于朴素贝叶斯(Naive Bayes)的分类预测

    目录 一、简介和环境准备 简介: 环境: 二、实战演练 2.1使用葡萄(Wine)数据集,进行贝叶斯分类 1.数据导入 2.模型训练 3.模型预测 2.2模拟离散数据集–贝叶斯分类 1.数据导入、分析 2.模型训练、预测  三、原理解析 朴素贝叶斯算法 优缺点: 朴素贝叶斯(Naive Bayes, NB) 是

    2023年04月19日
    浏览(80)
  • 【机器学习实战】-基于概率论的分类方法:朴素贝叶斯

    【机器学习实战】读书笔记 **朴素贝叶斯:**称为“ 朴素 ”的原因,整个形式化过程只做最原始、最简单的假设,特征之间没有关联,是统计意义上的独立。 **优点:**在数据较少的情况下仍然有效,可以处理多类别问题。 **缺点:**对于输入数据的准备方式较为敏感。 **适

    2024年03月25日
    浏览(40)
  • 第四章 基于概率论的分类方法:朴素贝叶斯

    朴素贝叶斯 优点:在数据较少的情况下仍然有效,可以处理多类别问题。 缺点:对于输⼊数据的准备⽅式较为敏感。 适⽤数据类型:标称型数据。 假设类别为 c 1 , c 2 c_1,c_2 c 1 ​ , c 2 ​ : 如果 p 1 ( x , y ) p 2 ( x , y ) p1(x,y) p2(x,y) p 1 ( x , y ) p 2 ( x , y ) ,那么类别为 c 1 c_1 c

    2024年02月13日
    浏览(29)
  • 【Sklearn】基于朴素贝叶斯算法的数据分类预测(Excel可直接替换数据)

    模型原理: 朴素贝叶斯分类是基于贝叶斯定理的一种分类方法。它假设特征之间相互独立(朴素性),从而简化计算过程。朴素贝叶斯分类器适用于文本分类、垃圾邮件过滤、情感分析等多种应用场景。 贝叶斯定理: 在朴素贝叶斯分类中,我们使用贝叶斯定理来计算后验概

    2024年02月13日
    浏览(24)
  • 基于机器学习算法:朴素贝叶斯和SVM 分类-垃圾邮件识别分类系统(含Python工程全源码)

    本项目采用朴素贝叶斯和支持向量机(SVM)分类模型作为基础,通过对垃圾邮件和正常邮件的数据进行训练,旨在实现垃圾邮件的自动识别功能。 通过训练这两个分类模型,我们的目标是建立一个高效准确的垃圾邮件识别系统。当接收到新的邮件时,系统将对邮件文本进行预

    2024年02月09日
    浏览(38)
  • 机器学习——使用朴素贝叶斯分类器实现垃圾邮件检测(python代码+数据集)

    机器学习——scikit-learn库学习、应用 机器学习——最小二乘法拟合曲线、正则化 机器学习——使用朴素贝叶斯分类器实现垃圾邮件检测(python代码+数据集) 贝叶斯公式: P ( A ∣ B ) = P ( A ) P ( B ∣ A ) P ( B ) P(A mid B)=frac{P(A) P(B mid A)}{P(B)} P ( A ∣ B ) = P ( B ) P ( A ) P ( B ∣ A )

    2024年02月10日
    浏览(34)
  • 大数据分析案例-基于高斯朴素贝叶斯算法构建良恶性肿瘤识别器

    🤵‍♂️ 个人主页:@艾派森的个人主页 ✍🏻作者简介:Python学习者 🐋 希望大家多多支持,我们一起进步!😄 如果文章对你有帮助的话, 欢迎评论 💬点赞👍🏻 收藏 📂加关注+ 喜欢大数据分析项目的小伙伴,希望可以多多支持该系列的其他文章 大数据分析案例合集

    2024年02月04日
    浏览(30)
  • 机器学习算法——贝叶斯分类器3(朴素贝叶斯分类器)

    基于贝叶斯公式来估计后验概率P(c|x)的主要困难在于:类条件概率P(x|c)是所有属性上的联合概率,难以从有限的训练样本直接估计而得。 为避开这个障碍,朴素贝叶斯分类器(Naive Bayes classfier)采用了“ 属性条件独立性假设 ”:对已知类别,假设所有属性相互独立。换句话

    2023年04月22日
    浏览(44)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包