当前位置: 首页 > article >正文

NLP09-朴素贝叶斯问句分类(3/3)

首先有个问句分类类

class QuestionClassify:

以下均为该类中的属性

    def __init__(self):
        self.train_x = None
        self.train_y = None
        self.tfidf_vec = None
        self.train_vec = None
        self.model = None
        self.question_category_dict = None

__init__ 是 Python 中的一个特殊方法(也叫构造函数),它在创建类的实例时自动调用,用于初始化对象的属性。

  • self.train_x:实例属性,用于存储训练数据的文本(即问题文本)
  • self.train_y:实例属性,用于存储训练数据的标签(即每个问题的类别标签)。
  • self.tfidf_vec:实例属性,用于存储 TfidfVectorizer 对象。TfidfVectorizer 是一个用于文本向量化的工具。
  • self.train_vec:实例属性,用于存储文本经过 TfidfVectorizer 转换后的向量表示。
  • self.model:用于存储分类模型。
  • self.question_category_dict:用于存储问题类别与标签的映射字典。

以下均为该类中的方法。 

一、文本向量化

为什么要进行文本向量化?

因为计算机只能处理数字,不能直接理解文字。向量化后的结果是一个数值向量,通常是一个数组或列表,里面的每个数值代表文本的某种特征。具体是什么数值项,取决于使用的向量化方法。

1. 词袋模型(Bag of Words, BoW)

  • 结果:向量中的每个数值表示某个词在文本中出现的次数

2. TF-IDF(词频-逆文档频率)

  • 结果:向量中的每个数值表示某个词的重要性(考虑词频和全局文档频率)。详见 NLP05-jieba分词

代码呈现

    # 文本向量化
    def to_vect(self):
        if self.tfidf_vec is None:
            # 加载训练数据
            self.train_x, self.train_y = data_loader.load_train_data()
            # 初始化一个Tfidf
            self.tfidf_vec = TfidfVectorizer()
            # 确保 self.train_x 是字符串列表
            if isinstance(self.train_x[0], list):
                self.train_x = [" ".join(doc) for doc in self.train_x]
            self.train_vec = self.tfidf_vec.fit_transform(self.train_x).toarray()
            # print(self.train_vec)

难点代码解析:

 self.train_vec = self.tfidf_vec.fit_transform(self.train_x).toarray()
  • fit_transform 方法首先学习文本数据的词汇表和 IDF(逆文档频率),然后将文本数据转换为 TF-IDF 特征矩阵
  • toarray() 方法将稀疏矩阵转换为密集矩阵(二维数组),方便后续处理。

重点讲解 toarray() 方法是如何将稀疏矩阵转换为密集矩阵的?

首先,我们要了解稀疏矩阵的存储方式:

假设有一个稀疏矩阵sparse_matrix,如下所示:

from scipy.sparse import csr_matrix

# 创建一个CSR矩阵
row_indices = [0, 1, 2]
col_indices = [0, 2, 1]
data = [1, 3, 2]
sparse_matrix = csr_matrix((data, (row_indices, col_indices)), shape=(3, 3))

# 转换为NumPy数组
dense_matrix = sparse_matrix.toarray()

print(dense_matrix)

输出结果为:

[[1 0 0]
 [0 0 3]
 [0 2 0]]

通过这个栗子我们可以看出,稀疏矩阵是为了节省内存空间,只存储非零元素以及它们的位置,而对于零元素则不存储。这样,对于大多数元素是零的矩阵,稀疏矩阵比密集矩阵更节省内存。

其次,转换为密集矩阵

# 将稀疏矩阵转换为密集矩阵
dense_matrix = sparse_matrix.toarray()

print(dense_matrix)

输出结果为:

[[1 0 0]
 [0 0 3]
 [0 2 0]]

虽然矩阵的维度没有改变,都是从3x3到3x3矩阵的转变,但在内存中的存储方式发生了变化,导致内存占用从稀疏变为密集。

我自己的迷思又想明白的问题:

这个例子中,矩阵从3x3到3x3具有极大的迷惑性,让我刚开始难以理解为什么第一种更稀疏。是因为这个例子比较特殊,正好有三个非零值,所以两个矩阵维度是一样的,但是如果零值多了,远大于非零值,就很容易想明白稀疏矩阵更省内存了!稀疏矩阵的优势主要体现在当零值占大多数时,它能够显著节省内存,因为它只存储非零值及其位置,而忽略零值。

这样做有什么好处?

参考 【机器学习】.toarray()表示什么意思

 二、模型训练

模块代码

    # 模型训练
    def train_model(self):
        self.to_vect()
        nb_model = MultinomialNB(alpha=0.01)
        nb_model.fit(self.train_vec, self.train_y)
        self.model = nb_model

  先调用类中的 to_vect() 方法进行文本向量化。

 难点代码解析

nb_model = MultinomialNB(alpha=0.01)
  • MultinomialNB:朴素贝叶斯分类器,适用于多项式分布的特征,广泛用于文本分类问题。
  • alpha=0.01:这是一个平滑参数,通常用于防止出现零概率。alpha 的值越小,平滑的效果越小,越接近于原始数据分布。alpha=0.01 表示轻度平滑。

详细介绍 alpha 参数的底层原理:

(1) 朴素贝叶斯的概率计算

(2)当 alpha=0 时

        如果某个特征 xi​ 在训练数据中没有出现过(即出现次数为 0),那么 P(xi∣y)=0。

        由于朴素贝叶斯模型是概率是连乘的,只要有一个特征的概率为 0,整个概率就会变成 0。这会导致模型无法处理新数据中出现的未知特征,从而过拟合训练数据。

(3)当 alpha>0

        平滑 的作用是给所有特征一个很小的概率,确保即使某个特征在某个类别中没有出现,它也不会被赋值为零。这样可以避免模型过于依赖已知特征,增加模型的泛化能力。

举个栗子:

假设我们有一个文本分类任务,类别为“体育”和“科技”,词汇表为 ["篮球", "足球", "人工智能"]

训练数据:

  • 体育类文本:["篮球", "足球"]

  • 科技类文本:["人工智能"]

测试数据

  • 新文本:["篮球", "人工智能", "网球"](“网球”是训练数据中未出现过的词)

连乘不为 0:模型可以基于平滑后的概率值,综合考虑其他已知特征的信息,对新数据中的未知特征进行合理处理。

nb_model.fit(self.train_vec, self.train_y)

fit() 方法作用是训练模型,会根据 train_vec 中的特征和 train_y 中的标签,训练朴素贝叶斯模型,并调整模型的内部参数。

易混点:

fit_transform() 方法

  • 库: sklearn(scikit-learn)中的特征转换方法
  • 工作原理: 它先 fit 即根据输入数据学习一些信息(如计算特征的统计量),然后 transform 即根据学到的信息对数据进行转换(例如,归一化、标准化、向量化等)。
  • 数据预处理时(如文本向量化或标准化)用 fit_transform() 来进行数据转换。比如
# 对文本数据进行向量化
tfidf_vec = TfidfVectorizer()
train_vec = tfidf_vec.fit_transform(train_x)  # 学习并转换数据

fit() 方法

  • 库: sklearn(scikit-learn)中的模型训练方法
  • 工作原理: 模型会从给定的训练数据中学习,并根据数据调整其内部参数。
  • 模型训练时,用 fit() 来训练模型。比如
# 训练朴素贝叶斯模型
nb_model = MultinomialNB()
nb_model.fit(train_vec, train_y)  # 学习模型参数

三、模型预测

模块代码

    # 模型预测
    def predict(self, question):
        # 词性标注做电影相关实体的抽取
        question_cut = nlp_util.movie_pos(question)
        # 原问句列表(刘德华演过哪些电影)
        question_src_list = []
        # 转换后的问句(nr演过哪些电影)
        question_pos_list = []

        for item in question_cut:
            question_src_list.append(item.word)
            if item.flag in ['nr', 'nm', 'nnt']:
                question_pos_list.append(item.flag)
            else:
                question_pos_list.append(item.word)
        question_pos_text = [" ".join(question_pos_list)]
        # print(question_pos_text)
        # 文本向量化
        question_vect = self.tfidf_vec.transform(question_pos_text).toarray()

        # 输入模型进行预测,得到结果
        predict = self.model.predict(question_vect)[0]
        return predict

 难点代码解析

 # 文本向量化
question_vect = self.tfidf_vec.transform(question_pos_text).toarray()

transform() 方法

  •  库:sklearn(scikit-learn)中的特征转换方法。
  • 工作原理:它基于之前通过 fit() 方法学到的信息(如特征的统计量或规则),对输入数据进行转换(例如,归一化、标准化、向量化等)。
  • 数据预处理时,如果已经通过 fit() 方法学习了数据的特征信息,可以使用 transform() 方法对新的数据进行转换。

四、输出结果

模块代码

    def init_question_category_dict(self):
        # 读取问题(类别-描述)映射文件
        question_category_path = os.path.join(constant.DATA_DIR, "question_classification.txt")
        with open(question_category_path, "r", encoding="utf-8") as file:
            question_category_list = file.readlines()
        self.question_category_dict = {}
        for category_item in question_category_list:
            category_id, category_desc = category_item.strip().split(":")
            self.question_category_dict[int(category_id)] = category_desc

    def get_question_desc(self, category):
        if self.question_category_dict is None:
            self.init_question_category_dict()
        return self.question_category_dict[category]

代码思路

  • init_question_category_dict 方法

    功能:初始化一个字典,存储类别 ID 和类别描述的映射关系。

    步骤:

        从指定路径读取文件 question_classification.txt。

        解析文件的每一行,提取类别 ID 和类别描述。

        将类别 ID 和类别描述的映射关系存储到字典 self.question_category_dict 中。

  • get_question_desc 方法

    功能:根据类别 ID 获取对应的类别描述。

    步骤:

        检查字典 self.question_category_dict 是否已初始化。

        根据传入的类别 ID (预测结果 result)从字典中查找并返回对应的类别描述。

五、完整代码

import os.path

from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.naive_bayes import MultinomialNB
from common import constant

from ch import data_loader, nlp_util


class QuestionClassify:
    def __init__(self):
        self.train_x = None
        self.train_y = None
        self.tfidf_vec = None
        self.train_vec = None
        self.model = None
        self.question_category_dict = None

    # 文本向量化
    def to_vect(self):
        if self.tfidf_vec is None:
            # 加载训练数据
            self.train_x, self.train_y = data_loader.load_train_data()
            # 初始化一个Tfidf
            self.tfidf_vec = TfidfVectorizer()
            # 确保 self.train_x 是字符串列表
            if isinstance(self.train_x[0], list):
                self.train_x = [" ".join(doc) for doc in self.train_x]
            self.train_vec = self.tfidf_vec.fit_transform(self.train_x).toarray()
            # print(self.train_vec)
    # 模型训练
    def train_model(self):
        self.to_vect()
        nb_model = MultinomialNB(alpha=0.01)
        nb_model.fit(self.train_vec, self.train_y)
        self.model = nb_model

    # 模型预测
    def predict(self, question):
        # 词性标注做电影相关实体的抽取
        question_cut = nlp_util.movie_pos(question)
        # 原问句列表(刘德华演过哪些电影)
        question_src_list = []
        # 转换后的问句(nr演过哪些电影)
        question_pos_list = []

        for item in question_cut:
            question_src_list.append(item.word)
            if item.flag in ['nr', 'nm', 'nnt']:
                question_pos_list.append(item.flag)
            else:
                question_pos_list.append(item.word)
        question_pos_text = [" ".join(question_pos_list)]
        # print(question_pos_text)
        # 文本向量化
        question_vect = self.tfidf_vec.transform(question_pos_text).toarray()

        # 输入模型进行预测,得到结果
        predict = self.model.predict(question_vect)[0]
        return predict

    def init_question_category_dict(self):
        # 读取问题(类别-描述)映射文件
        question_category_path = os.path.join(constant.DATA_DIR, "question_classification.txt")
        with open(question_category_path, "r", encoding="utf-8") as file:
            question_category_list = file.readlines()
        self.question_category_dict = {}
        for category_item in question_category_list:
            category_id, category_desc = category_item.strip().split(":")
            self.question_category_dict[int(category_id)] = category_desc

    def get_question_desc(self, category):
        if self.question_category_dict is None:
            self.init_question_category_dict()
        return self.question_category_dict[category]

if __name__ == "__main__":
    classify = QuestionClassify()
    classify.train_model()
    result = classify.predict("刘德华和成龙合作演过哪些电影呢?&&")
    print(classify.get_question_desc(result))
    print(result)

    # classify.init_question_category_dict()
    # print(classify.question_category_dict)


http://www.kler.cn/a/567821.html

相关文章:

  • SpringBoot敏感数据脱敏怎么处理
  • 学生管理前端
  • 认知动力学视角下的生命优化系统:多模态机器学习框架的哲学重构
  • 船舶广播系统:航行中的信息枢纽和安全保障
  • Linux 动静态库和_make_进度条(一)
  • 论文笔记-NeurIPS2017-DropoutNet
  • 【手撕算法】支持向量机(SVM)从入门到实战:数学推导与核技巧揭秘
  • C++动态与静态转换区别详解
  • Qt显示一个hello world
  • 计算机毕业设计SpringBoot+Vue.js人口老龄化社区服务与管理平台 (源码+文档+PPT+讲解)
  • 萌新学 Python 之 with 文件操作语句
  • AVFormatContext
  • C#里使用MSMQ来实现跨进程通讯
  • mysql安装教程,超详细图文教程(附安装包)MySQL8.0安装教程
  • 使用宝塔管理服务器
  • DeepSeek效应初现:Grok-3补刀ChatGPT,OpenAI已在ICU?
  • 设计模式-(状态模式,策略模式,代理模式,责任链模式)
  • 虚拟机如何设置ip
  • Metal 学习笔记五:3D变换
  • 7.1.1 计算机网络的组成