朴素贝叶斯
朴素贝叶斯
这个算法是建立在贝叶斯理论上的分类方法。
它的假设条件是自变量之间相互独立。
简言之,朴素贝叶斯假定某一特征的出现与其它特征无关。即给定类别,特征之间没有相关性。这个假设是“朴素”的来源。
比如说,如果一个水果它是红色的,圆状的,直径大概 7cm 左右,我们可能猜测它为苹果。即使这些特征之间存在一定关系,在朴素贝叶斯算法中我们都认为红色,圆状和直径在判断一个水果是苹果的可能性上是相互独立的。
一个二分类的案例假设:
我今天收到了 100 封邮件,其中有 80 封是垃圾邮件,20 封是正常邮件。
P(垃圾邮件) = 80/100 = 0.8
P(正常邮件) = 20/100 = 0.2
我选定了一些词作为特征,这些词可能出现在邮件中,也可能不出现。这些词有“免费”,“恭喜”,“辛苦”等。
我发现垃圾邮件中有 20 封含有“免费”这个词,50 封含有“恭喜”这个词,0 封含有“辛苦”这个词。
P(免费|垃圾邮件) = 20/80 = 0.25
P(恭喜|垃圾邮件) = 50/80 = 0.625
P(辛苦|垃圾邮件) = 0/80 = 0
正常邮件中有 5 封含有“免费”这个词。6 封含有“恭喜”这个词,2 封含有“辛苦”这个词。
P(免费|正常邮件) = 5/20 = 0.25
P(恭喜|正常邮件) = 6/20 = 0.3
P(辛苦|正常邮件) = 2/20 = 0.1
现在我收到了一封邮件,这封邮件内容为:“恭喜您获得了一次免费的机会”,我想知道这封邮件是垃圾邮件的概率是多少?
P(垃圾邮件|免费,恭喜) = P(免费|垃圾邮件)* P(恭喜|垃圾邮件)* P(垃圾邮件)= 0.25 * 0.625 * 0.8 = 0.125
P(正常邮件|免费,恭喜) = P(免费|正常邮件)* P(恭喜|正常邮件)* P(正常邮件)= 0.25 * 0.3 * 0.2 = 0.015
因为 P(垃圾邮件|免费,恭喜) > P(正常邮件|免费,恭喜),所以这封邮件被判定为垃圾邮件。
如果狡猾的垃圾邮件制造者把邮件内容改为:“恭喜您获得了一次免费的机会,辛苦您动动手指参加我们的免费活动”,那么这封邮件被判定为垃圾邮件的概率就会变成 0,因为“辛苦”这个词在正常邮件中有出现,在垃圾邮件中没有出现。
改进:拉普拉斯平滑法
在每个关键词上人为的增加一个出现的次数,这样就不会出现概率为 0 的情况了。(下面的公式免费的平方表示这个关键词出现 2 次)
P(垃圾邮件|免费,恭喜,辛苦) = P(免费|垃圾邮件)* P(恭喜|垃圾邮件)* P(辛苦|垃圾邮件)* P(垃圾邮件)= (20+1/80)² * (50+1/80) * (0+1/80) * 0.8 = 0.0351421875
P(正常邮件|免费,恭喜,辛苦) = P(免费|正常邮件)* P(恭喜|正常邮件)* P(辛苦|正常邮件)* P(正常邮件)= (5+1/20)² * (6+1/20) * (2+1/20) * 0.2 =0.012885
# 参考答案
import numpy as np
class NaiveBayes:
def __init__(self):
self.class_probs = {} # 存储每个类别的先验概率 P(c)
self.word_probs = {} # 存储每个类别中单词的条件概率 P(w|c)
self.vocab = set() # 保存所有出现的单词构成的词汇表
self.smooth = 1 # 拉普拉斯平滑参数
def fit(self, X, y):
# 获取唯一类别和其数量
classes, class_counts = np.unique(y, return_counts=True)
self.class_probs = {label: count / len(y) for label, count in zip(classes, class_counts)} # 先验概率
# 初始化词汇表和词频统计
word_count = {label: {} for label in classes} # 每个类别的词频表
class_word_totals = {label: 0 for label in classes} # 每个类别单词总数
# 遍历每个样本进行分词和统计
for text, label in zip(X, y):
words = text.split(" ")
for word in words:
self.vocab.add(word) # 添加到词汇表
if word not in word_count[label]:
word_count[label][word] = 0
word_count[label][word] += 1 # 更新词频
class_word_totals[label] += 1 # 当前类别单词总数加1
# 计算条件概率 P(w|c) 加拉普拉斯平滑
vocab_size = len(self.vocab) # 词汇表大小
self.word_probs = {label: {} for label in classes}
for label in classes:
for word in self.vocab:
count = word_count[label].get(word, 0) # 获取词频,若未出现则为0
self.word_probs[label][word] = (count + self.smooth) / (
class_word_totals[label] + vocab_size * self.smooth
)
def predict(self, X):
predictions = [] # 存储所有样本的预测结果
for text in X:
words = text.split(" ")
class_scores = {} # 存储每个类别的后验概率
# 计算后验概率 P(c|w1,w2,...,wn)
for label in self.class_probs:
class_scores[label] = self.class_probs[label]
for word in words:
if word in self.word_probs[label]: # 如果词在词汇表中
class_scores[label] *= self.word_probs[label][word]
else:
# 若单词未 在词汇表中,跳过计算
class_scores[label] *= 1/len(self.vocab)
# 选择后验概率最大的类别作为预测结果
predictions.append(max(class_scores, key=class_scores.get))
return predictions
def score(self, X, y):
predictions = self.predict(X)
return np.mean(predictions == y)
# 数据
data = np.array([
("恭喜 你 赢得 了 大奖 !","诈骗"),
("请 立即 更新 您 的 账户 信息","诈骗"),
("您的 账户 存在 异常 ,请 尽快 处理","诈骗"),
("这是 您 的 账单 ,请 查看","正常"),
("您的 订单 已 发货","正常"),
("请 确认 您 的 注册 信息","正常"),
("您 有 新的 消息 ,请 查看","正常"),
("点击 此 链接 获取 优惠券","诈骗"),
("您的 账户 已 被 锁定 ,请 立即 联系","诈骗"),
("恭喜 您 获得 免费 试用 !","诈骗"),
("请 不要 分享 您 的 密码","正常"),
("您的 订阅 即将 到期 ,请 续费","正常"),
("您 有 未 读 邮件 ,请 查看","正常"),
("立即 行动 ,获取 限时 优惠 !","诈骗"),
("您的 信用卡 信息 需要 更新","诈骗"),
])
X = data[:, 0] # 文本数据
y = data[:, 1] # 标签数据
# 创建模型并训练
model = NaiveBayes()
model.fit(X, y)
# 输出结果,比较预测类别与实际类别
print(model.score(X, y))
使用sklearn模块完成
from sklearn.naive_bayes import GaussianNB
import numpy as np
# 创建一些示例数据
X = np.array([[1], [2], [3], [4], [5]]) # 特征
y = np.array([0, 0, 1, 1, 1]) # 目标标签
# 创建朴素贝叶斯分类器 (高斯朴素贝叶斯)
model = GaussianNB()
# .fit() 方法用于拟合模型
model.fit(X, y)
# 要预测的新数据点
new_data_point = np.array([[6]])
# .predict() 方法返回预测的类别
predicted_class = model.predict(new_data_point)
# .predict_proba() 方法返回每个类别的概率
predicted_proba = model.predict_proba(new_data_point)
print("预测类别:", predicted_class)
print("类别概率:", predicted_proba)
简单示例
from sklearn.naive_bayes import GaussianNB
from sklearn.model_selection import train_test_split
import sklearn.datasets
# 加载数据
data = sklearn.datasets.load_iris()
# .data 属性包含特征
X = data.data
# .target 属性包含目标标签
y = data.target
# 划分数据集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3)
# 创建朴素贝叶斯分类器 (高斯朴素贝叶斯)
model = GaussianNB()
# 拟合模型
model.fit(X_train, y_train)
效果评估
from sklearn.metrics import accuracy_score
# 计算准确率
accuracy = accuracy_score(y_test, model.predict(X_test))
accuracy
查看分类错误的样本信息
import pandas as pd
# 把测试数据、目标标签、预测结果合并到一起
# pd.DataFrame()函数用于创建DataFrame
# pd.concat()函数用于合并多个DataFrame
# axis=1 表示按列合并
df = pd.concat(
[pd.DataFrame(X_test,columns=data.feature_names),
pd.DataFrame(y_test,columns=['target']),
pd.DataFrame(model.predict(X_test),columns=['predict'])
],axis=1 )
# 筛选target列与predict列不相等的数据
df.loc[df['target']!=df['predict']]