AI算法工程师 | 09机器学习-概率图模型(二)朴素贝叶斯算法与文本分类(1)

news2025/1/11 6:09:44

目录

  • 机器学习 - 概率图模型 之 朴素贝叶斯算法
    • 一、贝叶斯与朴素贝叶斯算法
      • 1、相关概念
      • 2、朴素贝叶斯分类的工作流程
      • 3、朴素贝叶斯的优缺点
    • 二、文本分类
      • 1、one-hot 编码
      • 2、TF-IDF 词频-逆文档频率
      • 3、代码:基于朴素贝叶斯实现文本分类(使用 Python 手动实现)

机器学习 - 概率图模型 之 朴素贝叶斯算法

一、贝叶斯与朴素贝叶斯算法

1、相关概念

贝叶斯公式

贝叶斯公式最早是由英国神学家贝叶斯提出来的,用来描述两个条件概率之间的关系。

贝叶斯公式

朴素贝叶斯算法

用示例理解朴素贝叶斯:

朴素贝叶斯

朴素贝叶斯的各特征间相互独立假设:

相互独立假设1

相互独立假设2

2、朴素贝叶斯分类的工作流程

朴素贝叶斯分类

朴素贝叶斯分类的工作流程

3、朴素贝叶斯的优缺点

朴素贝叶斯优点

  • 算法逻辑简单,易于实现(算法思路很简单,只要使用贝叶斯公式转化即可)
  • 分类过程中,时空开销小(假设特征相互独立,只会涉及到二维存储)

朴素贝叶斯缺点

  • 理论上,朴素贝叶斯模型与其他分类方法相比具有最小的误差率。但是实际上并非总是如此。
    • 这是因为朴素贝叶斯模型假设属性之间相互独立,这个假设在实际应用中往往是不成立的,在属性个数比较多或者属性之间相关性较大时,分类效果不好(改善:可先做数据的预处理,如 PCA,以减小相关性)。

:既然相互独立假设在实际应用中往往不成立,为何还需要朴素贝叶斯模型?

  • 朴素贝叶斯模型(Naive Bayesian Model,简称 NB) 的朴素(Naive)的含义是:“很简单很天真” 地假设样本特征彼此独立。

  • 这个假设现实中基本上不存在,但特征相关性很小的实际情况还是很多的,所以这个模型仍然能够工作得很好。

二、文本分类

在处理文本分类之前,引入两个概念:one-hot 编码、TF-IDF

1、one-hot 编码

one-hot 编码

one-hot 表达:

  • 说明:one-hot 表达是一种稀疏的表达方式。
  • 特点:相互独立地表示语料中的每个词。
    • 词与词在句子中的相关性被忽略了,这正符合朴素贝叶斯对文本的假设。

关于 one-hot 编码,推荐参考的文章有:

  • 参考1:https://www.cnblogs.com/shuaishuaidefeizhu/p/11269257.html(文中举例子说明了英文的 one-hot 编码与中文的 one-hot 编码)
  • 参考2: https://blog.csdn.net/qq_15192373/article/details/89552498(文中给出了多个样本多个特征的示例 + 代码)

2、TF-IDF 词频-逆文档频率

概念的引入:(one-hot 编码存在一定的不足,下面对其进行改进)
TF-IDF1
TF-IDF2

TF-IDF 权重策略:
TF-IDF 权重策略

TF-IDF:
TF-IDF

3、代码:基于朴素贝叶斯实现文本分类(使用 Python 手动实现)

回顾

  • 在进行算法实现前,先来回顾一下朴素贝叶斯分类的工作流程:

朴素贝叶斯的工作流程

  • 代码实现思路:

思路

代码:( 工具:PyCharm,基于:python3 )

import numpy as np

"""
对 朴素贝叶斯模型(Naive Bayesian Model,简称 NB) 的实现,并做文本分类
⭐ 学习时间:2023.1.4;再次梳理:2023.1.25
"""

def loadDataSet():
    """
    一个给定的数据集(此处:未使用文本分词,而是直接给出了每句话分好词后的内容)
    · postingList:一个二维数组(x),每一个列表(每行)代表一句话分完词后的情况;
    · classVec:一维数组(y),代表某文章是否是'stupid'(1:该篇文章是;0:否)
    """
    postingList = [['my', 'dog', 'has', 'flea', 'problems', 'help', 'please'],
                   ['maybe', 'not', 'take', 'him', 'to', 'dog', 'park', 'stupid'],
                   ['my', 'dalmatian', 'is', 'so', 'cute', 'I', 'love', 'him', 'my'],
                   ['stop', 'posting', 'stupid', 'worthless', 'garbage'],
                   ['mr', 'licks', 'ate', 'my', 'steak', 'how', 'to', 'stop', 'him'],
                   ['quit', 'buying', 'worthless', 'dog', 'food', 'stupid']]
    classVec = [0, 1, 0, 1, 0, 1]  # 1表示该文章属于辱骂性的('stupid'),0不是
    return postingList, classVec  # 相当于 返回 x, y


# 定义了一个类:朴素贝叶斯模型(Naive Bayesian Model,简称 NB)
class NBayes(object):
    def __init__(self):  # 构造函数
        self.vocabulary = []  # 词典
        self.idf = []
        self.tf = []
        self.tdm = 0  # p(x|yi)
        self.Pcates = {}  # p(yi)是一个类别词典
        self.labels = []  # 存放类别号
        self.doclength = 0  # 文档个数(文档长度)
        self.vocablen = 0  # 词典个数(词典长度)
        self.testset = 0  # 测试集

    def cate_prob(self, classVec):
        """
        计算在数据集中每个分类的概率 p(yi)
        """
        self.labels = classVec
        labeltemps = set(self.labels)  # 去重
        for labeltemp in labeltemps:  # 分别对每一种类别(分类)求概率,并赋值给 Pcates
            # p(yi) = 某类别的个数/类别总个数。其中:统计列表中重复的分类 self.labels.count(labeltemp) —— 这是 python 原生的一个方法
            self.Pcates[labeltemp] = float(self.labels.count(labeltemp)) / float(len(self.labels))

    def calc_wordfreq(self, trainset):
        """
        生成普通的词频向量(用的整型计数方式,未进行归一化,不是概率分布形式)
        """
        self.tf = np.zeros([self.doclength, self.vocablen])
        self.idf = np.zeros([1, self.vocablen])
        for index in range(self.doclength):
            for word in trainset[index]:
                self.tf[index, self.vocabulary.index(word)] += 1
            for signleword in set(trainset[index]):
                self.idf[0, self.vocabulary.index(signleword)] += 1

    def calc_tfidf(self, trainset):
        """
        TF-IDF (词频-逆文档频率)【目的:生成每篇文章的向量】
        """
        self.tf = np.zeros([self.doclength, self.vocablen])  # 定义 TF(文章行*词典列):初始化所有全为0,有多少篇文章就有多少行,列数为词典长度。
        self.idf = np.zeros([1, self.vocablen])  # 定义 IDF(1行*词典列):1 行,词典多长就有多少列。
        for index in range(self.doclength):  # 遍历每篇文章,取到每篇文章的索引号
            for word in trainset[index]:  # 取到每篇文章所对应的词的列表(遍历每篇文章的每个词)
                # 统计每篇文章中每个词所对应的在一篇文章中出现的次数。其中,self.vocabulary.index(word) 得到每个词所在词典中的索引号
                self.tf[index, self.vocabulary.index(word)] += 1
            # 消除不同句子长度导致的偏差。【求 TF 词频:一篇文章对应的向量(某词在文章中的出现次数) ÷ 一篇文章的长度 = 某一个给定的词语(某词)在该文章中出现的频率】
            self.tf[index] = self.tf[index] / float(len(trainset[index]))
            for singleword in set(trainset[index]):  # 把一篇文章内容拿出来后,做去重,得到一篇文章中不同的词
                # 统计整个语料库中每个词在几篇文章(文档)中出现
                self.idf[0, self.vocabulary.index(singleword)] += 1  # 对一篇文章中出现不同的词进行累加,加到 idf 中
        self.idf = np.log(float(self.doclength) / self.idf)  # IDF:逆文档频率。分子:语料库中文章的总数;分母:包含给定词语(某一个词)的文章数目; 之后取 log
        self.tf = np.multiply(self.tf, self.idf)  # 求的是 TF-IDF(= TF × IDF):词频-逆文档频率。

    def build_tdm(self):
        """
        按分类(类别)累计向量空间的每维值:p(x|yi)
        """
        self.tdm = np.zeros([len(self.Pcates), self.vocablen])  # 初始化,构建一个 类别行*词典列 的矩阵
        sumlist = np.zeros([len(self.Pcates), 1])  # 类别行(有多少类就有多少行)*1列
        for index in range(self.doclength):  # 遍历每篇文章
            # 将同一个类别的词向量空间值加和(相当于加权求和)
            self.tdm[self.labels[index]] += self.tf[index]  # labels[index]:某篇文章的类别;tf[index]:某篇文章的TF-IDF向量(包含每个词对应的权重)
            # 统计每个分类的总值,是一个标量
            sumlist[self.labels[index]] = np.sum(self.tdm[self.labels[index]])
        self.tdm = self.tdm / sumlist  # 生成 p(x|yi)

    def train_set(self, trainset, classVec):
        self.cate_prob(classVec)  # 计算每个分类在数据集中的概率:p(yi)
        self.doclength = len(trainset)  # 语料库中文档的个数
        tempset = set()  # 创建空的 set 是为了去重,生成词典
        # 生成词典
        # 下面是 python 列表生成式,其含义为:
        #   ·(双重for循环)先遍历 trainset 中的每篇文章doc,再遍历 每篇doc 得到里面的词word,最后将每个word 加入 tempset 中。
        #   · 添加完后,set 集合(tempset)中会有所有词取值完后的结果。由于生成完后列表本身是不需要的,所以未用变量去接它。
        [tempset.add(word) for doc in trainset for word in doc]
        self.vocabulary = list(tempset)  # 强转为 list 列表(列表带有顺序,我们可根据索引号去查询到列表中具体某个元素的值)
        self.vocablen = len(self.vocabulary)  # 求词典的个数
        # 计算词频数据集
        # self.calc_wordfreq(trainset) # 可用词频表达
        self.calc_tfidf(trainset)  # 也可用 TF-IDF 表达每篇文章中每个词的权重
        # 按分类累计向量空间的每维值 p(x|yi)
        self.build_tdm()

    def map2vocab(self, testdata):
        """
        将测试集映射到当前词典 【说明:此处是简化的方式,由于我们的测试集中只有一条样本,所以只计算了 count 值(词出现的次数)】
        (待改进之处:训练集中计算得是 TF-IDF,测试集中也应该计算 TF-IDF)
        """
        self.testset = np.zeros([1, self.vocablen])
        for word in testdata:  # 遍历文本中每个词,进行索引化赋值(累加:得到词在文本中出现的次数)
            self.testset[0, self.vocabulary.index(word)] += 1  # self.vocabulary.index(word):找到某词在词典中的索引号

    def predict(self, testset):
        """
        预测分类结果,输出预测的分类类别号
        """
        if np.shape(testset)[1] != self.vocablen:
            # 如果向量化的测试集长度与词典不相等,退出程序
            print('输入错误')
            exit(0)
        predvalue = 0
        preclass = ''  # 类别号
        for tdm_vect, keyclass in zip(self.tdm, self.Pcates):  # 相当于遍历字典
            # 遍历每个类别
            # P(x|yi) P(yi)
            print(testset, testset.shape)
            print(tdm_vect, len(tdm_vect))
            print(self.Pcates[keyclass])
            # temp 是对应位置的某个类别的一些概率乘积【P(x|yi)*P(yi)】的累加值(此处:可看成对数似然——原公式的连乘可变成添加log后的连加 又∵log是单调递增 ∴ 可不用管log,直接求累加值)
            temp = np.sum(testset * tdm_vect * self.Pcates[keyclass])
            # 计算最大分类值
            if temp > predvalue:
                predvalue = temp
                preclass = keyclass
        return preclass, temp


if __name__ == '__main__':
    dataSet, listClasses = loadDataSet()  # 加载数据,相当于 返回 x, y
    nb = NBayes()  # 通过构造函数构造了 nb 对象
    nb.train_set(dataSet, listClasses)  # 训练模型。调用对象中的 train_set 方法,并传入 x, y
    nb.map2vocab(dataSet[0])  # 使用模型,传入第一句话
    print(nb.predict(nb.testset))  # 预测

运行结果:

结果

补充:下面是上述代码中涉及的部分 python 知识点梳理

  • 在 NBayes 类里 train_set 函数中,[tempset.add(word) for doc in trainset for word in doc] 属于 python 的列表生成式

    • 含义:
      # 列表生成式:
      #(双重for循环)先遍历 trainset 中的每篇文章doc,再遍历 每篇doc 得到里面的词word;
      # 最后将每个word 加入 tempset(创建的set 集合)中。
      [tempset.add(word) for doc in trainset for word in doc]
      
    • 示例:列表生成式
      示例
  • 在 NBayes 类里 cate_prob 函数中,统计列表中重复的分类 self.labels.count(labeltemp) 涉及 python 的一个原生方法 count()

    • 含义:
      self.labels.count(labeltemp) # 统计列表(self.labels)中 labeltemp 类别 的个数
      
    • 示例: count() 用于统计某个元素在列表中出现的次数
      原生python

—— 说明:本文写于 2023.1.3、2023.1.24~1.25,文中内容基于 python3,使用工具 PyCharm 编写的代码

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

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

相关文章

力扣刷题| 239. 滑动窗口最大值、347.前 K 个高频元素

文章目录LeetCode 239. 滑动窗口最大值题目链接🔗思路代码实现LeetCode 347.前 K 个高频元素题目链接🔗思路代码实现LeetCode 239. 滑动窗口最大值 题目链接🔗 LeetCode 239. 滑动窗口最大值 思路 这是使用单调队列的经典题目 难点是如何…

html列表

1.无序列表: 场景:在网页中表示一组无顺序之分的列表 如:新闻列表 标签组成: ul: 表示无序列表的整体,用于包裹li标签 li:表示无序列表的每一项,用于包含每一行的内容 注意点&…

Makefile学习③:编译流程

Makefile学习③:编译流程 学习前准备 实现七个文件的编写,例如:实现加减乘三个函数的.c文件和.h文件的编写加上主函数main.c,方便后续使用Makefile将其分别编译。 博主的demo如下: 使用gcc 命令将所有文件编译出来生…

线程控制--Linux

文章目录线程理解线程的优点与缺点进程的多个线程共享线程控制线程创建线程终止线程等待线程分离总结线程理解 谈及线程,就不得不谈起进程与线程的关系了。学习完前面有关进程的知识,之前我们对进程的定义是:内核数据结构代码和数据。但是今…

C语言return的用法详解,C语言函数返回值详解

函数的返回值是指函数被调用之后,执行函数体中的代码所得到的结果,这个结果通过 return 语句返回。return 语句的一般形式为:return 表达式;或者:return (表达式);有没有( )都是正确的,为了简明,一般也不写…

python使用类装饰器生成函数的使用日志

1 什么是类装饰器 在了解类装饰器之前,建议大家先了解装饰器的概念。 装饰器知识快速入门链接 类装饰器是 Python 中的一种特殊类型的装饰器,它是一个类而不是一个函数。与函数装饰器不同,类装饰器可以在运行时接收参数并返回一个可调用的对…

Linux文件系统(IO缓冲区+磁盘+软硬链接)

目录 一、缓冲区 1.1 缓冲区是内存的一块存储空间 1.2 缓冲区的作用 1.3 C式缓冲区 1.3.1 C语言的FILE结构体 1.3.2 C式缓冲区刷新策略 二、OS与内核缓冲区 2.1 数据从缓冲区到磁盘 2.2 fsync() 数据免缓冲直接到磁盘文件 2.3 检验用户与内核缓冲区 三、文件系统 3…

11. 好客租房-ElasticSearch入门学习

Elaticsearch,简称为es, es是一个开源的高扩展的分布式全文检索引擎,它可以近乎实时的存储、检索数据;本身扩展性很好,可以扩展到上百台服务器,处理PB级别的数据。es也使用Java开发并使用Lucene作为其核心来…

分享144个ASP源码,总有一款适合您

ASP源码 分享144个ASP源码,总有一款适合您 下面是文件的名字,我放了一些图片,文章里不是所有的图主要是放不下..., 144个ASP源码下载链接:https://pan.baidu.com/s/15O9p6a8XlNN0u-wFKEkJqQ?pwd8354 提取码&#x…

Go 语言

Go语言是云计算时代的语言 Go语言2007年诞生于Google,2009年开源,Go语言与区块链技术一样年轻 本文是对Go语言基本语法的总结 目录 Go词法单元 token Go的token 标识符 内置数据类型标识符 常量值标识符 空白标识符 关键字 程序整体结构的关键字…

VBA提高篇_05日期时间函数

文章目录日期函数1. Date()2. Time()3. Now()时间数据解析函数时间运算函数DateDiff() 数据时间差DateAdd() 时间点指定跨越拓展日期函数 VBA中默认日期系统格式: #1/26/2023 12:20:25 # #月/日/年 时:分:秒# 1. Date() 获取当前系统的时间(年/月/日) 精度: 精确到秒 范围: 公…

Tomcat-HTTP服务器介绍、安装与使用

文章目录一、概述二、下载安装三、介绍四、启动Tomcat一、概述 Tomcat,是一个 HTTP 服务器,就是在 TCP 服务器的基础上,加上了一些额外的功能。使用 HTTP 服务器的 API。就可以来提取HTTP请求的内容,也可以构造HTTP响应 二、下载…

【GPLT 二阶题目集】L2-015 互评成绩

学生互评作业的简单规则是这样定的:每个人的作业会被k个同学评审,得到k个成绩。系统需要去掉一个最高分和一个最低分,将剩下的分数取平均,就得到这个学生的最后成绩。本题就要求你编写这个互评系统的算分模块。 输入格式&#xff…

HBase原理和设计

简介 HBase —— Hadoop Database的简称,Google BigTable的另一种开源实现方式,从问世之初,就为了解决用大量廉价的机器高速存取海量数据、实现数据分布式存储提供可靠的方案。从功能上来讲,HBase不折不扣是一个数据库&#xff0…

EcoStruxure Operator Terminal Expert 操作员终端专家

EcoStruxure Operator Terminal Expert(以前称为 Vijeo XD)是一款具有最新 UI 设计和手势的触摸屏配置软件,使您能够为 Magelis HMI 和 iPC 创建和编辑应用程序屏幕。 特点: 变量——内存中用于存储数据的命名空间。创建您需要的所…

MS-Model【3】:Medical Transformer

文章目录前言1. Abstract & Introduction1.1. Abstract1.2. Introduction2. Medical Transformer (MedT)2.1. Model structure2.2. Attention2.2.1. Self-Attention Overview2.2.2. Axial-Attention2.2.3. Gated Axial-Attention2.3. Local-Global Training2.4. Loss funct…

定位 position属性 相对定位 绝对定位 固定定位 定位下的居中 多个定位元素重叠时 补充

目录定位position属性相对定位绝对定位固定定位定位的做法: 定位下的居中多个定位元素重叠时补充定位 视觉格式化模型,大体上将页面中盒子的排列分为三种方式: 常规流浮动:float定位:position 定位:手动…

MySQL —— 数据类型

目录 一、数据类型的分类 二、数值类型 1. tinyint类型 2. bit类型 3. float类型 4. decimal类型 三、字符串类型 1. char类型 2. varchar类型 3. char和varchar的比较 四、时间日期类型 五、enum和set类型 一、数据类型的分类 分类数据类型说明数值类型BIT(M)位…

《深入浅出计算机组成原理》学习笔记 Day11

浮点数1. 浮点数的二进制转化2. 浮点数的加法和精度损失参考1. 浮点数的二进制转化 以 9.1109.1_{10}9.110​ 为例。910100129_{10} 1001_2910​10012​,再把小数位转换为二进制。以 0.100120.1001_20.10012​ 为例: 0.1001212−102−202−312−40.562…

吊打大厂:内核级安卓系统优化软件 | 雪豹速清app官网下载

雪豹速清app是当前非常热门的一款安卓系统垃圾清理优化工具,具有雪豹文件管理器、大文件查找、冗余文件/重复文件清理、安卓内核级垃圾清理、QQ微信专清、文件秒搜、M3U8视频合并、微信语音导出、伪装音视频查找、安装包提取等诸多特色实用功能,雪豹速清…