Day06【贝叶斯实现文本分类】

摘要

本文基于朴素贝叶斯分类器实现给定的文本(例如新闻标题)的分类,并预测其所属的类别。

1. 贝叶斯分类的理论背景

"""
贝叶斯分类实践
P(A|B) = (P(A) * P(B|A)) / P(B)
事件A:文本属于类别x1。文本属于类别x1的概率,记做P(x1)
事件B:文本为s (s=w1w2w3..wn)
P(x1|s) = 文本为s,属于x1类的概率.   #求解目标#
P(x1|s) = P(x1|w1, w2, w3...wn) = P(w1, w2..wn|x1) * P(x1) / P(w1, w2, w3...wn)

P(x1) 任意样本属于x1的概率。x1样本数/总样本数
P(w1, w2..wn|x1) = P(w1|x1) * P(w2|x1)...P(wn|x1)  词的独立性假设
P(w1|x1) x1类样本中,w1出现的频率

公共分母的计算,使用全概率公式:
P(w1, w2, w3...wn) = P(w1,w2..Wn|x1)*P(x1) + P(w1,w2..Wn|x2)*P(x2) ... P(w1,w2..Wn|xn)*P(xn)
"""
  • P(A|B): 给定事件B发生的情况下,事件A发生的概率。
  • 在这里,A是文本属于某一类别(如x1),B是文本内容(即文本中包含的词语)。我们要计算的目标是P(x1|s),即给定文本s属于类别x1的概率。

为了计算这个概率,使用贝叶斯概率公式:
P ( x 1 ∣ s ) = P ( s ∣ x 1 ) ⋅ P ( x 1 ) P ( s ) P(x1|s) = \frac{P(s|x1) \cdot P(x1)}{P(s)} P(x1∣s)=P(s)P(sx1)P(x1)

  • P(x1) 是类别x1的先验概率,表示样本中属于类别x1的比例。
  • P(s|x1) 是在类别x1下,给定文本s的条件概率,可以通过每个词的条件概率来计算。
  • P(s) 是所有类别的条件概率加权平均,但在分类时它对不同类别的相对比较没有影响,所以可以忽略掉。

2.贝叶斯方法类

该类实现了整个朴素贝叶斯分类过程,包含数据加载、概率计算以及文本分类功能。

构造函数
def __init__(self, data_path):
    self.p_class = defaultdict(int)  # 存储每个类别的样本数量
    self.word_class_prob = defaultdict(dict)  # 存储每个类别下的词的条件概率
    self.load(data_path)
  • 初始化时,p_class用于记录每个类别的样本数量,word_class_prob用于存储每个类别下每个词的条件概率。
  • load(data_path)方法用于加载数据并初始化词频和类别信息。
数据加载
def load(self, path):
    self.class_name_to_word_freq = defaultdict(dict)  # 存储每个类别的词频
    self.all_words = set()  # 汇总所有词
    with open(path, encoding="utf8") as f:
        for line in f:
            line = json.loads(line)
            class_name = line["tag"]
            title = line["title"]
            words = jieba.lcut(title)  # 使用结巴分词对标题进行分词
            self.all_words = self.all_words.union(set(words))
            self.p_class[class_name] += 1  # 记录每个类别的样本数量
            word_freq = self.class_name_to_word_freq[class_name]
            for word in words:
                if word not in word_freq:
                    word_freq[word] = 1
                else:
                    word_freq[word] += 1
    self.freq_to_prob()  # 调用 freq_to_prob 计算词频概率
  • 该函数从指定的path加载训练数据,并对每个样本中的标题进行分词(jieba.lcut(title)),同时统计每个类别中的词频以及每个类别的样本数量。
  • self.all_words 用于存储所有出现过的词汇,用来计算词表的大小。
  • self.class_name_to_word_freq 用于记录每个类别下各个词的频率。
概率计算
def freq_to_prob(self):
    total_sample_count = sum(self.p_class.values())  # 总样本数
    self.p_class = {c: self.p_class[c] / total_sample_count for c in self.p_class}  # 类别的概率 P(x1)

    # 计算每个词在每个类别中的条件概率 P(w|x)
    for class_name, word_freq in self.class_name_to_word_freq.items():
        total_word_count = sum(count for count in word_freq.values())  # 每个类别下总词数
        for word in word_freq:
            prob = (word_freq[word] + 1) / (total_word_count + len(self.all_words))  # 加1平滑处理
            self.word_class_prob[class_name][word] = prob
        self.word_class_prob[class_name]["<unk>"] = 1 / (total_word_count + len(self.all_words))  # 未知词的平滑处理
  • p_class 存储每个类别的先验概率 (P(x))。
  • word_class_prob 存储每个词在各类别下的条件概率 (P(w|x)),并使用加1平滑处理来避免概率为0的情况。
计算词的条件概率
def get_words_class_prob(self, words, class_name):
    result = 1
    for word in words:
        unk_prob = self.word_class_prob[class_name]["<unk>"]  # 处理未知词
        result *= self.word_class_prob[class_name].get(word, unk_prob)  # 计算 P(w1|x1) * P(w2|x1) * ...
    return result
  • 该函数计算给定词语序列在某类别下的联合概率,即 P ( w 1 ∣ x 1 ) ⋅ P ( w 2 ∣ x 1 ) ⋅ … ⋅ P ( w n ∣ x 1 ) P(w1|x1) \cdot P(w2|x1) \cdot \ldots \cdot P(wn|x1) P(w1∣x1)P(w2∣x1)P(wnx1)
计算类别的联合概率
def get_class_prob(self, words, class_name):
    p_x = self.p_class[class_name]  # 类别的先验概率 P(x1)
    p_w_x = self.get_words_class_prob(words, class_name)  # 条件概率 P(w1, w2, ... wn | x1)
    return p_x * p_w_x
  • 计算某个类别的联合概率 P ( x 1 ) ⋅ P ( w 1 , w 2 , . . . w n ∣ x 1 ) P(x1) \cdot P(w1, w2, ... wn|x1) P(x1)P(w1,w2,...wnx1)
文本分类
def classify(self, sentence):
    words = jieba.lcut(sentence)  # 对输入文本进行分词
    results = []
    for class_name in self.p_class:
        prob = self.get_class_prob(words, class_name)  # 计算每个类别的概率
        results.append([class_name, prob])
    results = sorted(results, key=lambda x: x[1], reverse=True)  # 按概率排序

    # 计算公共分母 P(w1, w2, ... wn)
    pw = sum([x[1] for x in results])  # 计算 P(w1, w2, ... wn)
    results = [[c, prob/pw] for c, prob in results]  # 归一化概率

    for class_name, prob in results:
        print(f"属于类别[{class_name}]的概率为{prob:.6f}")
    return results
  • classify 方法首先对输入的文本进行分词,然后为每个类别计算其概率,最后按照概率排序。
  • 计算公共分母 P ( w 1 , w 2 , . . . w n ) P(w1, w2, ... wn) P(w1,w2,...wn)时,所有类别的概率会归一化,确保结果在0到1之间。

数据链接地址Day06【贝叶斯实现文本分类】-新闻文本数据

3. 主函数

if __name__ == "__main__":
    path = "../data/train_tag_news.json"
    ba = BayesApproach(path)
    query = "中国三款导弹可发射多弹头 美无法防御很急躁"
    ba.classify(query)
  • path 变量指定了训练数据文件的位置。
  • 通过 BayesApproach(path) 实例化一个贝叶斯分类器对象,加载训练数据。
  • 然后调用 classify(query) 方法对给定的查询文本(query)进行分类,并打印出各个类别的概率。

总结

  • 数据加载:读取数据并统计每个类别的样本数量和每个类别下的词频。
  • 概率计算:通过朴素贝叶斯定理计算每个类别的先验概率和每个词在每个类别下的条件概率,并进行平滑处理。
  • 文本分类:对输入文本进行分词,并计算每个类别的概率,最后返回概率最高的类别。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值