用wordpress建立导航网站,做网站颜色类型是啥,微网站 留言板,郑州网站推广怎么做朴素贝叶斯 朴素贝叶斯理论贝叶斯决策理论条件概率全概率公式贝叶斯公式朴素贝叶斯 言论屏蔽新浪新闻分类朴素贝叶斯算法的优缺点 朴素贝叶斯算法是一种基于贝叶斯定理的有监督的机器学习算法#xff0c;解决的是分类问题#xff0c;如文本分类、垃圾邮件过滤、客户是否流失解决的是分类问题如文本分类、垃圾邮件过滤、客户是否流失是否值得投资、信用等级评定等领域并在实际应用中表现出良好的性能。该算法简单易懂学习效率高在某些领域的分类问题中能够与决策树、神经网络等算法相媲美。但由于该算法以自变量之间的独立假设特征之间相互独立性和连续变量的正态性假设为前提就会导致算法精度在某种程度上受影响。
下面是对朴素贝叶斯算法的详细阐述
贝叶斯定理 朴素贝叶斯算法基于贝叶斯定理进行概率推断。贝叶斯定理描述了在已知先验概率的情况下如何根据新的证据来更新我们对事件发生概率的信念。特征独立性假设 朴素贝叶斯算法假设特征之间相互独立即给定类别特征之间的条件概率是相互独立的。虽然这个假设在实际中并不总是成立但在许多情况下这种简化可以带来较好的分类效果。类别的先验概率 在朴素贝叶斯算法中需要计算每个类别的先验概率即在没有任何信息的情况下每个类别发生的概率。特征的条件概率 对于给定的类别需要计算每个特征的条件概率即在该类别下每个特征取某个值的概率。这通常需要利用训练数据集来进行估计。计算后验概率 当有新的样本需要分类时利用贝叶斯定理可以计算出该样本属于每个类别的后验概率。最终选择具有最高后验概率的类别作为分类结果。处理连续特征 对于连续特征可以使用概率密度函数来进行条件概率的估计常见的方法包括高斯朴素贝叶斯和多项式朴素贝叶斯。
总体来说朴素贝叶斯算法简单易懂计算效率高对小规模的数据集表现良好但在处理特征之间相关性较强的情况下可能表现不佳。在实际应用中朴素贝叶斯算法通常作为其他更复杂算法的基准进行对比或者在特征独立性较强的问题上取得良好效果。
朴素贝叶斯理论
贝叶斯决策理论
朴素贝叶斯是贝叶斯决策理论的一部分所以我们先了解一下贝叶斯决策理论。假设现在有两个类别 p 1 ( x , y ) p1(x, y) p1(x,y) 表示数据点 ( x , y ) (x, y) (x,y) 属于类别一的概率 p 2 ( x , y ) p2(x, y) p2(x,y) 表示数据点 ( x , y ) (x, y) (x,y) 属于类别二的概率那么对于一个新的数据点 ( x , y ) (x, y) (x,y)我们就可以用下面的规则来判断它所属的类别
如果 p 1 ( x , y ) p 2 ( x , y ) p1(x, y) p2(x, y) p1(x,y)p2(x,y)那么判定 ( x , y ) (x, y) (x,y) 属于类别一如果 p 1 ( x , y ) p 2 ( x , y ) p1(x, y) p2(x, y) p1(x,y)p2(x,y)那么判定 ( x , y ) (x, y) (x,y) 属于类别二
也就是说我们会选择最高概率对应的类别作为最终的分类结果这就是贝叶斯决策理论的核心思想。
条件概率
条件概率指的是在事件 B B B 发生的情况下事件 A A A 发生的概率用 P ( A ∣ B ) P(A|B) P(A∣B) 来表示。 从上图可以清楚的得到条件概率 P ( A ∣ B ) P(A|B) P(A∣B) 的计算公式 P ( A ∣ B ) P ( A ∩ B ) P ( B ) P(A|B) \frac{P(A \cap B)}{P(B)} P(A∣B)P(B)P(A∩B) 因此 P ( A ∩ B ) P ( A ∣ B ) P ( B ) P(A \cap B) P(A|B)P(B) P(A∩B)P(A∣B)P(B) 同理 P ( A ∩ B ) P ( B ∣ A ) P ( A ) P(A \cap B) P(B|A)P(A) P(A∩B)P(B∣A)P(A) 推理可得 P ( A ∣ B ) P ( B ) P ( B ∣ A ) P ( A ) ⟹ P ( A ∣ B ) P ( B ∣ A ) P ( A ) P ( B ) P(A|B)P(B) P(B|A)P(A) \implies P(A|B) \frac{P(B|A)P(A)}{P(B)} P(A∣B)P(B)P(B∣A)P(A)⟹P(A∣B)P(B)P(B∣A)P(A) 这就是条件概率的计算公式。
全概率公式
假设样本空间 S S S是两个事件 A A A 与 A ′ A A′ 的总和如下图所示 事件 B B B 也可以划分成两个部分如下图所示 此时有如下公式 P ( B ) P ( B ∩ A ) P ( B ∩ A ′ ) P(B) P(B \cap A) P(B \cap A) P(B)P(B∩A)P(B∩A′) 因为 P ( B ∩ A ) P ( B ∣ A ) P ( A ) P ( B ∩ A ′ ) P ( B ∣ A ′ ) P ( A ′ ) P(B \cap A) P(B|A)P(A) \\ P(B \cap A) P(B|A)P(A) P(B∩A)P(B∣A)P(A)P(B∩A′)P(B∣A′)P(A′) 因此有 P ( B ) P ( B ∣ A ) P ( A ) P ( B ∣ A ′ ) P ( A ′ ) P(B) P(B|A)P(A) P(B|A)P(A) P(B)P(B∣A)P(A)P(B∣A′)P(A′) 这就是全概率的计算公式。
我进一步详细阐述什么是全概率
若 n n n 个事件两两互斥且这 n n n 个事件的总和为 Ω \varOmega Ω则称这 n n n 个事件组为完备事件组在完备事件组下 A 1 A 2 A 3 ⋅ ⋅ ⋅ A n Ω A_1 A_2 A_3··· A_n \varOmega A1A2A3⋅⋅⋅AnΩ此时有一事件 X X X要计算其发生概率一般情况下无法直接求出如果能找到一个伴随事件 X X X 发生的完备事件组则能借助全概率公式求出事件 X X X 的发生概率 P ( X ) ∑ i 1 n P ( X ∣ A i ) P ( A i ) P(X) \displaystyle\sum_{i1}^{n}P(X|A_i)P(A_i) P(X)i1∑nP(X∣Ai)P(Ai) 贝叶斯公式
全概率公式已知事件 X X X 与伴随事件 X X X 发生的完备事件组 A 1 、 A 2 、 . . . 、 A n A_1、A_2、...、A_n A1、A2、...、An根据完备事件组求出事件 X X X 发生的概率。
贝叶斯公式已知事件 X X X 与伴随事件 X X X 发生的完备事件组 A 1 、 A 2 、 . . . 、 A n A_1、A_2、...、A_n A1、A2、...、An根据事件 X X X 发生的概率求出完备事件组中某一事件 A i A_i Ai 发生的概率。
贝叶斯公式如下所示 P ( A i ∣ X ) P ( A i ∩ X ) P ( X ) P ( X ∣ A i ) P ( A i ) ∑ i 1 n P ( X ∣ A i ) P ( A i ) P(A_i|X) \frac{P(A_i \cap X)}{P(X)} \frac{P(X|A_i)P(A_i)}{\displaystyle\sum_{i1}^{n}P(X|A_i)P(A_i)} P(Ai∣X)P(X)P(Ai∩X)i1∑nP(X∣Ai)P(Ai)P(X∣Ai)P(Ai) 其中 P ( A i ) P(A_i) P(Ai) 叫做先验概率即在事件 X X X 发生之前我们对事件 A i A_i Ai 发生概率的一个判断 P ( A i ∣ X ) P(A_i|X) P(Ai∣X) 叫做后验概率即在事件 X X X 发生之后我们对事件 A i A_i Ai 发生概率的一个重新评估通俗点说就是在样本空间为 X X X 的情况下 A i A_i Ai 所占的比重大小 P ( X ∣ A i ) ∑ i 1 n P ( X ∣ A i ) P ( A i ) P ( X ∣ A i ) P ( X ) \frac{P(X|A_i)}{\displaystyle\sum_{i1}^{n}P(X|A_i)P(A_i)} \frac{P(X|A_i)}{P(X)} i1∑nP(X∣Ai)P(Ai)P(X∣Ai)P(X)P(X∣Ai) 叫做可能性函数这是一个调整因子使得预估概率更接近真实概率
条件概率可以理解成 后验概率 先验概率 × 调整因子这就是贝叶斯推断的含义。我们先预估一个“先验概率”然后加入实验结果看这个实验是增强还是削弱了“先验概率”由此得到更接近事实的“后验概率”。
接下来举一个例子来加深对贝叶斯推断的理解。假设有两个一模一样的碗一号碗有 30 颗水果糖和 10 颗巧克力糖二号碗有 20 颗水果糖和 20 颗巧克力糖如下图所示。现在随机选择一个碗从中摸出一颗糖发现是水果糖请问这颗水果糖来自一号碗的概率有多大 我们假定 H 1 H_1 H1 表示一号碗 H 2 H_2 H2 表示二号碗由于两个碗是一模一样的因此 P ( H 1 ) P ( H 2 ) 0.5 P(H_1) P(H_2) 0.5 P(H1)P(H2)0.5我们把这个概率就叫做“先验概率”即在没有做实验之前来自一号碗的概率是 0.5。
假定 E E E 表示水果糖问题就变成了求条件概率 P ( H 1 ∣ E ) P(H_1|E) P(H1∣E)我们把这个概率叫做“后验概率”即在事件 E E E 发生后对 P ( H 1 ) P(H_1) P(H1) 的修正。
根据条件概率公式得到 P ( H 1 ∣ E ) P ( E ∣ H 1 ) P ( H 1 ) P ( E ) P ( E ∣ H 1 ) P ( H 1 ) P ( E ∣ H 1 ) P ( H 1 ) P ( E ∣ H 2 ) P ( H 2 ) 0.375 0.625 0.6 P(H_1|E) \frac{P(E|H_1)P(H_1)}{P(E)} \frac{P(E|H_1)P(H_1)}{P(E|H_1)P(H_1)P(E|H_2)P(H_2)} \frac{0.375}{0.625} 0.6 P(H1∣E)P(E)P(E∣H1)P(H1)P(E∣H1)P(H1)P(E∣H2)P(H2)P(E∣H1)P(H1)0.6250.3750.6 计算结果表示这颗水果糖来自一号碗的概率为 0.6。也就是说取出水果糖后事件 H 1 H_1 H1 发生的可能性得到了增强。
这里进一步思考一点在使用该方法时如果不需要知道具体的类别概率而只需知道所属类别 H 1 H_1 H1 或 H 2 H_2 H2那我们就没必要计算事件 E E E 的全概率只需比较 P ( H 1 ∣ E ) P(H_1|E) P(H1∣E) 和 P ( H 2 ∣ E ) P(H_2|E) P(H2∣E) 的大小即可。
朴素贝叶斯
朴素贝叶斯与贝叶斯是两个不同的概念。贝叶斯算法是一种基于贝叶斯定理进行概率推断的统计学方法。贝叶斯算法通过利用先验概率和样本数据得到后验概率从而对未知参数或未来事件进行推断。朴素贝叶斯算法是贝叶斯算法家族中的一员它是基于贝叶斯定理和特征独立性假设的一种分类算法。与一般的贝叶斯算法相比朴素贝叶斯算法做出了特征独立性的假设简化了条件概率的计算使得算法更加高效并且适用于大规模的数据集。
朴素贝叶斯公式如下所示 P ( y ∣ X ) P ( X ∣ y ) P ( y ) P ( X ) P ( x 1 , x 2 , . . . , x n ∣ y ) P ( y ) P ( x 1 , x 2 , . . . , x n ) P ( x 1 ∣ y ) P ( x 2 ∣ y ) ⋅ ⋅ ⋅ P ( x n ∣ y ) P ( y ) P ( x 1 , x 2 , . . . , x n ) P(y|X) \frac{P(X|y)P(y)}{P(X)}\frac{P(x_1, x_2, ..., x_n|y)P(y)}{P(x_1, x_2, ..., x_n)}\frac{P(x_1|y)P(x_2|y)···P(x_n|y)P(y)}{P(x_1, x_2, ..., x_n)} P(y∣X)P(X)P(X∣y)P(y)P(x1,x2,...,xn)P(x1,x2,...,xn∣y)P(y)P(x1,x2,...,xn)P(x1∣y)P(x2∣y)⋅⋅⋅P(xn∣y)P(y) 其中 P ( x 1 ∣ y ) 、 P ( x 2 ∣ y ) 、 . . . P(x_1|y)、P(x_2|y)、... P(x1∣y)、P(x2∣y)、... 分别表示在类别为 y y y 的条件下特征 x 1 , x 2 , . . . , x n x_1, x_2, ..., x_n x1,x2,...,xn 的概率占比。
朴素贝叶斯算法通过计算每个类别的后验概率并选择具有最高后验概率的类别作为分类结果。为了进行分类需要事先估计先验概率和条件概率通常通过训练数据集来进行参数估计。
接下来举一个例子进一步理解朴素贝叶斯推断。某医院上午来了六个门诊病人情况如下表所示
症状职业疾病打喷嚏护士感冒打喷嚏农夫过敏头痛建筑工人脑震荡头痛建筑工人感冒打喷嚏教师感冒头痛教师脑震荡
现在来了第七个病人是一个打喷嚏的建筑工人请问他患上感冒的概率有多大
根据贝叶斯定理 P ( y ∣ X ) P ( X ∣ y ) P ( y ) P ( X ) P(y|X) \frac{P(X|y)P(y)}{P(X)} P(y∣X)P(X)P(X∣y)P(y) 推理可得 P ( 感冒 ∣ 打喷嚏 , 建筑工人 ) P ( 打喷嚏 , 建筑工人 ∣ 感冒 ) P ( 感冒 ) P ( 打喷嚏 , 建筑工人 ) P(感冒|打喷嚏, 建筑工人)\frac{P(打喷嚏, 建筑工人|感冒)P(感冒)}{P(打喷嚏, 建筑工人)} P(感冒∣打喷嚏,建筑工人)P(打喷嚏,建筑工人)P(打喷嚏,建筑工人∣感冒)P(感冒) 根据朴素贝叶斯的特征独立性假设可知“打喷嚏”和“建筑工人”这两个特征是相互独立的因此上述公式可写成如下 P ( 感冒 ∣ 打喷嚏 , 建筑工人 ) P ( 打喷嚏 ∣ 感冒 ) P ( 建筑工人 ∣ 感冒 ) P ( 感冒 ) P ( 打喷嚏 ) P ( 建筑工人 ) 0.66 × 0.33 × 0.5 0.5 × 0.33 0.66 P(感冒|打喷嚏, 建筑工人)\frac{P(打喷嚏|感冒)P(建筑工人|感冒)P(感冒)}{P(打喷嚏)P(建筑工人)}\frac{0.66×0.33×0.5}{0.5×0.33}0.66 P(感冒∣打喷嚏,建筑工人)P(打喷嚏)P(建筑工人)P(打喷嚏∣感冒)P(建筑工人∣感冒)P(感冒)0.5×0.330.66×0.33×0.50.66 计算结果表示这个打喷嚏的建筑工人有 66% 的概率患上感冒同理可计算这个人患上过敏和脑震荡的概率比较这几个概率就可以推测他最可能患上的疾病类别。
言论屏蔽
以在线社区留言为例为了营造一个健康发展的社区我们需要屏蔽一些带侮辱性的言论如果某条留言使用了负面或侮辱性的词汇我们就将其标记为内容不当。我们使用 1 和 2 分别表示内容不当和内容得当。
完整的代码如下
import numpy as np
from functools import reduce# 读取数据
def read_dataset() - (list, list)::return: 返回样本数据集和样本标签# 将留言进行单词切分并转换成词向量samples [[my, dog, has, flea, problems, help, please],[maybe, not, take, him, to, dog, park, stupid],[my, dalmation, is, so, cute, I, love, him],[stop, posting, stupid, worthless, garbage],[mr, licks, ate, my, steak, how, to, stop, him],[quit, buying, worthless, dog, food, stupid]]# 各样本对应标签1 代表内容不当2 代表内容得当labels [2, 1, 2, 1, 2, 1]return samples, labels# 依据数据集创建词汇表
def create_vocabulary(dataset: list) - list::param dataset: 样本数据集:return: 数据集中出现的所有词汇集合以列表形式返回vocab_set set([]) # 创建一个空集for sample in dataset:vocab_set vocab_set | set(sample) # 取并集return list(vocab_set)# 用词汇表的稀疏向量形式来表示每个词向量样本
def vocab_vector_to_vocabulary_vector(vocabulary: list, sample: list) - list::param vocabulary: 词汇表:param sample: 样本数据集中的一个样本:return: 词汇表的稀疏向量vocabulary_vector [0] * len(vocabulary) # 元素个数与词汇表 vocabulary 一致for vocab in sample:if vocab in vocabulary: # 如果该词汇出现在词汇表中则将 vocabulary_vector 对应位置的值置为 1vocabulary_vector[vocabulary.index(vocab)] 1else:print(f{vocab} is not in vocabulary!)return vocabulary_vector# 训练朴素贝叶斯分类器
def train_naive_bayes_classifier(train_mat: list, train_labels: list) - (np.ndarray, np.ndarray, float)::param train_mat: 训练样本数据都已转成词汇表的稀疏向量形式:param train_labels: 训练样本数据的对应标签:return: 返回在类别 1 和 2 情况下各个词汇出现的概率以及类别 1 占样本集的概率num_samples len(train_mat) # 训练样本数6num_vocabs len(train_mat[0]) # 每个样本向量含有多少个元素32p_1 float(sum([1 for label in train_labels if label 1]) / num_samples) # 在训练集中内容不当的概率p_1_vocab np.zeros(num_vocabs) # 当样本标签为 1 时每个词出现的次数p_2_vocab np.zeros(num_vocabs) # 当样本标签为 2 时每个词出现的次数p_1_vocabs 0.0 # 当样本标签为 1 时所有词出现的次数和p_2_vocabs 0.0 # 当样本标签为 2 时所有词出现的次数和for i in range(num_samples):if train_labels[i] 1:p_1_vocab train_mat[i]p_1_vocabs sum(train_mat[i])else:p_2_vocab train_mat[i]p_2_vocabs sum(train_mat[i])p_1_vector p_1_vocab / p_1_vocabs # 在类别 1 的情况下各个词出现的概率P(w1|1)、P(w2|1)、P(w3|1)、...p_2_vector p_2_vocab / p_2_vocabs # 在类别 2 的情况下各个词出现的概率P(w1|2)、P(w2|2)、P(w3|2)、...return p_1_vector, p_2_vector, p_1# 预测分类结果
def predict(predict_data: np.ndarray, p_1_vector: np.ndarray, p_2_vector: np.ndarray, p_1: float) - int::param predict_data: 预测数据已转成词汇表的稀疏向量形式:param p_1_vector: 在类别 1 的情况下各个词出现的条件概率:param p_2_vector: 在类别 2 的情况下各个词出现的条件概率:param p_1: 类别为 1 的先验概率:return: 预测的类别p1 reduce(lambda x, y: x * y, predict_data * p_1_vector) * p_1 # 类别为 1 的概率p2 reduce(lambda x, y: x * y, predict_data * p_2_vector) * (1 - p_1) # 类别为 2 的概率print(p1:, p1)print(p2:, p2)if p1 p2:return 1else:return 2if __name__ __main__:# 获取样本数据和对应标签samples, labels read_dataset()# 获取词汇表vocabulary create_vocabulary(samples)# 获取训练的样本数值向量train_mat []for sample in samples:train_mat.append(vocab_vector_to_vocabulary_vector(vocabulary, sample))# 获取在类别 1 和 2 情况下各个词汇出现的概率以及类别 1 占样本集的概率# p_1_vector 中存放的是各个单词在类别 1 情况下出现的条件概率# p_2_vector 中存放的是各个单词在类别 2 情况下出现的条件概率# p_1 就是类别为 1 的先验概率p_1_vector, p_2_vector, p_1 train_naive_bayes_classifier(train_mat, labels)# 测试predict_data [love, my, dalmation] # 预测样本predict_data_vector np.array(vocab_vector_to_vocabulary_vector(vocabulary, predict_data)) # 将预测样本转成词汇表的稀疏向量result predict(predict_data_vector, p_1_vector, p_2_vector, p_1)if result 1:print(f{predict_data} 属于内容不当)else:print(f{predict_data} 属于内容得当)
P(1|X)P(X)P(X|1)P(1) P(1|(w1, w2, ..., w32))P(w1, w2, ..., w32)P((w1, w2, ..., w32)|1)P(1)P(w1|1)P(w2|1)···P(w32|1)P(1)
P(2|X)P(X)P(X|2)P(2) P(2|(w1, w2, ..., w32))P(w1, w2, ..., w32)P((w1, w2, ..., w32)|2)P(2)P(w1|2)P(w2|2)···P(w32|2)P(2)
比较 P(1|X) 与 P(2|X) 的大小将更大值所属类别作为最终的分类结果---------
p1: 0.0
p2: 0.0
[love, my, dalmation] 属于内容得当上述代码中存在一个问题就是在计算 P ( 1 ∣ X ) P ( X ) P ( w 1 ∣ 1 ) P ( w 2 ∣ 1 ) ⋅ ⋅ ⋅ P ( w 32 ∣ 1 ) P ( 1 ) P(1|X)P(X) P(w1|1)P(w2|1)···P(w32|1)P(1) P(1∣X)P(X)P(w1∣1)P(w2∣1)⋅⋅⋅P(w32∣1)P(1) 时只要其中有一个 P ( w i ∣ 1 ) 0 P(wi|1) 0 P(wi∣1)0其最后的概率值都会等于 0这显然不是我们想要的结果。
为了解决上述问题我们可以将各单词的出现次数初始化为 1将所有单词出现的总次数初始化为 2这种做法就叫做拉普拉斯平滑Laplace Smoothing又称之为加 1 平滑是比较常用的平滑方法可以解决 0 概率问题。
除此之外还有一个下溢出的问题这是由于很多个很小的数相乘导致的。两个小于 1 的数值相乘其结果将比两个数值中的任何一个都小当很多个小于 1 的数值相乘时结果会非常小此时若对其进行四舍五入计算结果很可能就变成 0 了。为了解决这个问题我们可以对乘积结果取自然对数通过求对数可以避免下溢出或浮点数舍入导致的错误。同时采用自然对数进行处理不会产生什么损失。
函数 f ( x ) f(x) f(x) 与 l n f ( x ) lnf(x) lnf(x) 的曲线如下图所示 如上图所示函数 f ( x ) f(x) f(x) 与 l n f ( x ) lnf(x) lnf(x) 在相同区域内同增同减并在相同点上取到极值基于这些特性在某些问题上可以使用 l n f ( x ) lnf(x) lnf(x) 替代 f ( x ) f(x) f(x) 进行相应处理。
修改后的代码如下
import numpy as np# 读取数据
def read_dataset() - (list, list)::return: 返回样本数据集和样本标签# 将留言进行单词切分并转换成词向量samples [[my, dog, has, flea, problems, help, please],[maybe, not, take, him, to, dog, park, stupid],[my, dalmation, is, so, cute, I, love, him],[stop, posting, stupid, worthless, garbage],[mr, licks, ate, my, steak, how, to, stop, him],[quit, buying, worthless, dog, food, stupid]]# 各样本对应标签1 代表内容不当2 代表内容得当labels [2, 1, 2, 1, 2, 1]return samples, labels# 依据数据集创建词汇表
def create_vocabulary(dataset: list) - list::param dataset: 样本数据集:return: 数据集中出现的所有词汇集合以列表形式返回vocab_set set([]) # 创建一个空集for sample in dataset:vocab_set vocab_set | set(sample) # 取并集return list(vocab_set)# 用词汇表的稀疏向量形式来表示每个词向量样本
def vocab_vector_to_vocabulary_vector(vocabulary: list, sample: list) - list::param vocabulary: 词汇表:param sample: 样本数据集中的一个样本:return: 词汇表的稀疏向量vocabulary_vector [0] * len(vocabulary) # 元素个数与词汇表 vocabulary 一致for vocab in sample:if vocab in vocabulary: # 如果该词汇出现在词汇表中则将 vocabulary_vector 对应位置的值置为 1vocabulary_vector[vocabulary.index(vocab)] 1else:print(f{vocab} is not in vocabulary!)return vocabulary_vector# 训练朴素贝叶斯分类器
def train_naive_bayes_classifier(train_mat: list, train_labels: list) - (np.ndarray, np.ndarray, float)::param train_mat: 训练样本数据都已转成词汇表的稀疏向量形式:param train_labels: 训练样本数据的对应标签:return: 返回在类别 1 和 2 情况下各个词汇出现的概率以及类别 1 占样本集的概率num_samples len(train_mat) # 训练样本数6num_vocabs len(train_mat[0]) # 每个样本向量含有多少个元素32p_1 float(sum([1 for label in train_labels if label 1]) / num_samples) # 在训练集中内容不当的概率p_1_vocab np.ones(num_vocabs) # 当样本标签为 1 时每个词出现的次数p_2_vocab np.ones(num_vocabs) # 当样本标签为 2 时每个词出现的次数p_1_vocabs 2.0 # 当样本标签为 1 时所有词出现的次数和p_2_vocabs 2.0 # 当样本标签为 2 时所有词出现的次数和for i in range(num_samples):if train_labels[i] 1:p_1_vocab train_mat[i]p_1_vocabs sum(train_mat[i])else:p_2_vocab train_mat[i]p_2_vocabs sum(train_mat[i])p_1_vector np.log(p_1_vocab / p_1_vocabs) # 在类别 1 的情况下各个词出现的概率P(w1|1)、P(w2|1)、P(w3|1)、...p_2_vector np.log(p_2_vocab / p_2_vocabs) # 在类别 2 的情况下各个词出现的概率P(w1|2)、P(w2|2)、P(w3|2)、...return p_1_vector, p_2_vector, p_1# 预测分类结果
def predict(predict_data: np.ndarray, p_1_vector: np.ndarray, p_2_vector: np.ndarray, p_1: float) - int::param predict_data: 预测数据已转成词汇表的稀疏向量形式:param p_1_vector: 在类别 1 的情况下各个词出现的条件概率:param p_2_vector: 在类别 2 的情况下各个词出现的条件概率:param p_1: 类别为 1 的先验概率:return: 预测的类别p1 sum(predict_data * p_1_vector) np.log(p_1) # 类别为 1 的概率p2 sum(predict_data * p_2_vector) np.log(1.0 - p_1) # 类别为 2 的概率print(p1:, p1)print(p2:, p2)if p1 p2:return 1else:return 2if __name__ __main__:# 获取样本数据和对应标签samples, labels read_dataset()# 获取词汇表vocabulary create_vocabulary(samples)# 获取训练的样本数值向量train_mat []for sample in samples:train_mat.append(vocab_vector_to_vocabulary_vector(vocabulary, sample))# 获取在类别 1 和 2 情况下各个词汇出现的概率以及类别 1 占样本集的概率# p_1_vector 中存放的是各个单词在类别 1 情况下出现的条件概率# p_2_vector 中存放的是各个单词在类别 2 情况下出现的条件概率# p_1 就是类别为 1 的先验概率p_1_vector, p_2_vector, p_1 train_naive_bayes_classifier(train_mat, labels)# 测试predict_data [stupid, garbage] # 预测样本predict_data_vector np.array(vocab_vector_to_vocabulary_vector(vocabulary, predict_data)) # 将预测样本转成词汇表的稀疏向量result predict(predict_data_vector, p_1_vector, p_2_vector, p_1)if result 1:print(f{predict_data} 属于内容不当)else:print(f{predict_data} 属于内容得当)
---------
p1: -4.702750514326955
p2: -7.20934025660291
[stupid, garbage] 属于内容不当新浪新闻分类
我们可以使用 sklearn 来构建朴素贝叶斯分类器。在 scikit-learn 中有三个朴素贝叶斯分类算法分别为 GaussianNB、MultinomialNB、BernoulliNB。其中GaussianNB 是先验为高斯分布的朴素贝叶斯MultinomialNB 是先验为多项式分布的朴素贝叶斯BernoulliNB 是先验为伯努利分布的朴素贝叶斯。
sklearn.naive_bayes 模块实现了朴素贝叶斯算法其 MultinomialNB 函数实现如下所示
sklearn.naive_bayes.MultinomialNB(alpha1.0, force_alphaTrue, fit_priorTrue, class_priorNone)- alpha加法拉普拉斯/利德斯通平滑参数默认为 1.0如果设置为 0则表示不平滑- force_alpha默认为 True如果为 False且 alpha 小于 1e-10则会将 alpha 的值置为 1e-10如果为 Truealpha 将保持不变之所以设置这个参数是因为如果 alpha 太接近 0可能会导致数值错误- fit_prior是否学习类别先验概率默认为 True如果为 False则所有的样本类别都有相同的先验概率- class_prior类别的先验概率如果指定则不会根据数据调整先验概率默认为 None由 MultinomialNB 创建的实例对象 clf 具有以下方法
fit(X, y) # 根据训练集拟合 k 近邻分类器- X训练数据形状为 (n_samples, n_features)- y目标值训练样本对应的标签形状为 (n_samples,)- sample_weight样本权重如果为 None则样本权重相同返回拟合的朴素贝叶斯分类器get_params(deepTrue) # 以字典形式返回 MultinomialNB 类的参数- deep布尔值默认为 True返回参数partial_fit(X, y, classesNone, sample_weightNone) # 对一批样本进行拟合当整个数据集过大无法一次性放入内存时这种方法非常管用- X训练数据形状为 (n_samples, n_features)- y目标值训练样本对应的标签形状为 (n_samples,)- classesy 向量中可能出现的所有类别的列表默认为 None必须在第一次调用 partial_fit 时提供后续调用可以省略- sample_weight样本权重如果为 None则样本权重相同返回实例本身predict(X) # 预测所提供数据的类别标签- X预测数据形状为 (n_samples, n_features)以 np.ndarray 形式返回形状为 (n_samples,) 的每个数据样本的类别标签predict_proba(X) # 返回预测数据 X 在各类别标签中所占的概率- X预测数据形状为 (n_samples, n_features)返回该样本在各类别标签中的预测概率类别的顺序与属性 classes_ 中的顺序一致predict_log_proba(X) # 返回预测数据 X 在各类别标签中所占的对数概率- X预测数据形状为 (n_samples, n_features)返回该样本在各类别标签中的预测对数概率类别的顺序与属性 classes_ 中的顺序一致predict_joint_log_proba(X) # 返回预测数据 X 的联合对数概率估计值。对于 X 的每一行 x 和类别 y联合对数概率由 logP(x, y) logP(y) logP(x|y) 给出其中 logP(y) 是类别先验概率logP(x|y) 是类别条件概率- X预测数据形状为 (n_samples, n_features)返回该样本在各类别标签中的预测对数概率类别的顺序与属性 classes_ 中的顺序一致score(X, y, sample_weightNone) # 返回预测结果和标签之间的平均准确率- X预测数据形状为 (n_samples, n_features)- y预测数据的目标值真实标签- sample_weight默认为 None返回预测数据的平均准确率相当于先执行了 self.predict(X)而后再计算预测值和真实值之间的平均准确率完整的新浪新闻朴素贝叶斯分类模型代码实现如下
import os
import random
import jieba
import numpy as np
from sklearn.naive_bayes import MultinomialNB# 数据集处理
def text_process(dir_path: str, train_size0.8) - (list, list, list, list, list)::param dir_path: 数据集目录:param train_size: 从数据集中划分训练集的比例:return: 词汇表训练数据测试数据训练标签测试标签dir_list os.listdir(dir_path) # [C000008, ...]data_list []labels_list []# 遍历每个存放了 txt 文件的子目录for dir in dir_list:new_dir_path os.path.join(dir_path, dir)files os.listdir(new_dir_path) # [10.txt, ...]# 遍历每个存储了新闻文本的 txt 文件for file in files:file_path os.path.join(new_dir_path, file)with open(file_path, r, encodingutf-8) as f:raw f.read()word_cut jieba.cut(raw, cut_allFalse) # 精简模式返回一个可迭代的生成器word_list list(word_cut)data_list.append(word_list)labels_list.append(dir)# 划分训练集与测试集data_labels_list list(zip(data_list, labels_list)) # 将数据与标签对应压缩random.shuffle(data_labels_list) # 将 data_labels_list 乱序index int(len(data_labels_list) * train_size) 1 # 训练集与测试集划分的索引值train_list data_labels_list[:index] # 训练集包括数据与标签test_list data_labels_list[index:] # 测试集包括数据与标签train_data_list, train_labels_list zip(*train_list) # 解压训练集得到训练数据和标签train_data_list, train_labels_list list(train_data_list), list(train_labels_list) # 转成列表test_data_list, test_labels_list zip(*test_list) # 解压测试集得到测试数据和标签test_data_list, test_labels_list list(test_data_list), list(test_labels_list) # 转成列表# 统计数据集词频all_words_dict {}for words in train_data_list:for word in words:if word not in all_words_dict.keys():all_words_dict[word] 0all_words_dict[word] 1# 根据字典中的值进行键值对的排序排列顺序为降序all_words_zip sorted(all_words_dict.items(), keylambda x: x[1], reverseTrue) # 排序all_words_tuple, all_words_frequency_tuple zip(*all_words_zip) # 解压缩得到元组形式的词汇表和频次表all_words_list list(all_words_tuple) # 转成列表return all_words_list, train_data_list, test_data_list, train_labels_list, test_labels_list# 一些特定的词语如“的”、“在”、“当然”等对新闻分类无实际意义将这些词整理好并存储在了 stopwords_cn.txt 文件中
# 读取 stopwords_cn.txt 文件并进行去重处理
def stop_words_set(file_path: str) - set::param file_path: stopwords_cn.txt 的路径:return: 返回一个经过去重处理的词汇集合words_set set()with open(file_path, r, encodingutf-8) as f:for line in f.readlines():word line.strip()if len(word) 0:words_set.add(word)return words_set# 词频最高的往往是一些对于分类无意义的符号有必要删除它们
# 文本特征选取删除词频最高的 n 个词并选取合适的词作为特征词
def delete_words(all_words_list: list, stopwords_set: set, n10) - list::param all_words_list: 训练集的词汇表:param stopwords_set: 无意义的词汇集合:param n: 要删除的前多少个高频词汇数:return: 特征词汇表feature_words []for i in range(n, len(all_words_list)):if all_words_list[i].isdigit() or all_words_list[i] in stopwords_set or len(all_words_list[i]) 1 or len(all_words_list[i]) 5:continueelse:feature_words.append(all_words_list[i])return feature_words# 根据 feature_words 将训练数据和测试数据向量化
def data_vector(feature_words: list, train_data_list: list, test_data_list: list) - (list, list)::param feature_words: 数据集的特征词汇表:param train_data_list: 训练数据二维列表每个元素表示一个新闻样本:param test_data_list: 测试数据二维列表每个元素表示一个新闻样本:return: 向量化的训练数据和测试数据train_feature_list [] # train_data_list 的向量化形式test_feature_list [] # test_data_list 的向量化形式# 将训练数据向量化for sample in train_data_list:train_sample_list [] # 用于存储训练集单个样本的特征词汇元素个数与 feature_words 一致sample_set set(sample) # 将样本数据进行去重for word in feature_words:if word in sample_set:train_sample_list.append(1)else:train_sample_list.append(0)train_feature_list.append(train_sample_list)# 将测试数据向量化for sample in test_data_list:test_sample_list [] # 用于存储测试集单个样本的特征词汇元素个数与 feature_words 一致sample_set set(sample) # 将样本数据进行去重for word in feature_words:if word in sample_set:test_sample_list.append(1)else:test_sample_list.append(0)test_feature_list.append(test_sample_list)return train_feature_list, test_feature_listif __name__ __main__:dir_path rD:\MachineLearning\SogouC\Sample # 数据集存放目录# 获取词汇表、训练数据、测试数据、训练标签、测试标签all_words_list, train_data_list, test_data_list, train_labels_list, test_labels_list text_process(dir_path)# 生成 stopwords_setstopwords_file_path rD:\MachineLearning\stopwords_cn.txtstopwords_set stop_words_set(stopwords_file_path)# 获取数据集的特征词汇表feature_words delete_words(all_words_list, stopwords_set)# 获取向量化的训练数据和测试数据train_feature_list, test_feature_list data_vector(feature_words, train_data_list, test_data_list)# print(np.array(train_feature_list).shape) # (73, 8747)# print(np.array(test_feature_list).shape) # (17, 8747)# 实例化 MultinomialNB 对象clf MultinomialNB()# 使用训练数据和训练标签进行拟合clf.fit(train_feature_list, train_labels_list)# 预测predict_result clf.predict(test_feature_list)# 准确率accuracy clf.score(test_feature_list, test_labels_list)print(测试结果为, predict_result)print(准确率为, accuracy)
---------
测试结果为 [C000014 C000013 C000013 C000024 C000020 C000014 C000023 C000016 C000022 C000010 C000014 C000020 C000016 C000008 C000020 C000013 C000008]
准确率为 0.8235294117647058朴素贝叶斯算法的优缺点
优点
算法简单且易于实现。朴素贝叶斯算法做出了对特征之间条件独立性的假设这使得算法的计算复杂度较低适合处理大规模数据集。对小规模数据表现良好。即使在数据量较少的情况下朴素贝叶斯算法也能够有效地进行分类。对缺失数据不敏感。朴素贝叶斯算法能够处理缺失数据并利用已有的数据进行预测。
缺点
特征条件独立性假设限制了算法的表达能力。朴素贝叶斯算法无法考虑特征之间的相关性因此在特征之间存在强相关性的情况下算法的性能可能会受到影响。对输入数据的分布假设较强。朴素贝叶斯算法假设输入特征之间服从独立同分布但实际情况中可能存在违背这个假设的数据。需要估计先验概率。朴素贝叶斯算法需要根据训练数据估计先验概率如果样本量较小或者类别之间的先验概率差异较大可能会导致分类结果不准确。