零、前言
在很多应用中,属性集和类变量之间的关系是不确定的。换句话说,尽管测试记录的属性集和某些训练样例相同,但是也不能正确地预测它的类标号。这种情况产生的原因可能是噪声,或者出现了某些影响分类的因素却没有包含在分析中。
例如,考虑根据一个人的饮食和锻炼的频率来预测他是否有患心脏病的危险。尽管大多数饮食健康、经常锻炼身体的人患心脏病的机率较小,但仍有人由于遗传、过量抽烟、酗酒等其他原因而患病。确定一个人的饮食是否健康、体育锻炼是否充分也是需要论证的课题,这反过来也会给学习问题带来不确定性。
贝叶斯分类是一种对属性集和类变量的概率关系建模的方法。贝叶斯定理(Bayestheorem),它是一种把类的先验知识和从数据中收集的新证据相结合的统计原理。
一、朴素贝叶斯
朴素贝叶斯分类器是一个以贝叶斯定理与特征条件独立假设为基础的多分类的分类器。
对于给定的训练数据集,首先基于特征的条件独立性假设,学习输入输出的联合概率分布,然后基于此模型,对给定的输入x,利用贝叶斯定理求出后 验概率最大的输出y
条件概率公式:
我们把P(A)称为"先验概率"(Prior probability),即在B事件发生之前,我们对A事件概率的一个判断。
P(A|B)称为"后验概率"(Posterior probability),即在B事件发生之后,我们对A事件概率的重新评估。
后验概率越大,说明某事物属于这个类别的可能性越大,我们越有理由把它归到这个类别下
全概率公式:
朴素贝叶斯分类的优缺点:
优点:
-
朴素贝叶斯模型发源于古典数学理论,有稳定的分类效率。
-
对缺失数据不太敏感,算法也比较简单,常用于文本分类。
-
分类准确度高,速度快.
缺点:
- 需要知道先验概率P(F1,F2…|C),因此在某些时候会由于假设的先验模型的原因导致预测效果不佳。
二、贝叶斯分类器:例
三、朴素贝叶斯分类的三个阶段
1:准备工作阶段,这个阶段的任务是为朴素贝叶斯分类做必要的准备,主要工作是根据具体情况确定特征属性,并对每个特征属性进行适当划分,然后由人工对一部分待分类项进行分类,形成训练样本集合。这一阶段的输入是所有待分类数据,输出是特征属性和训练样本。这一阶段是整个朴素贝叶斯分类中唯一需要人工完成的阶段,其质量对整个过程将有重要影响,分类器的质量很大程度上由特征属性、特征属性划分及训练样本质量决定。
2:分类器训练阶段,这个阶段的任务就是生成分类器,主要工作是计算每个类别在训练样本中的出现频率及每个特征属性划分对每个类别的条件概率估计,并将结果记录。其输入是特征属性和训练样本,输出是分类器。这一阶段是机械性阶段,根据前面讨论的公式可以由程序自动计算完成。
3:应用阶段,这个阶段的任务是使用分类器对待分类项进行分类,其输入是分类器和待分类项,输出是待分类项与类别的映射关系。这一阶段也是机械性阶段,由程序完成。
四、sklearn中的朴素贝叶斯
sklearn文档原话如下:
-
相比于其他更复杂的方法,朴素贝叶斯学习器和分类器非常快。
分类条件分布的解耦意味着可以独立单独地把每个特征视为一维分布来估计。这样反过来有助于缓解维度灾难带来的问题。 -
另一方面,尽管朴素贝叶斯被认为是一种相当不错的分类器,但却不是好的估计器,所以不能太过于重视从predict_proba输出的概率。
sklearn 的全称叫 Scikit-learn,它给我们提供了 3 个朴素贝叶斯分类算法,分别是高斯朴素贝叶斯(GaussianNB)、多项式朴素贝叶斯(MultinomialNB)和伯努利朴素贝叶斯(BernoulliNB)。
这三种算法适合应用在不同的场景下,我们应该根据特征变量的不同选择不同的算法:
高斯朴素贝叶斯 :特征变量是连续变量,符合高斯分布,比如说人的身高,物体的长度。
多项式朴素贝叶斯 :特征变量是离散变量,符合多项分布,在文档分类中特征变量体现在一个单词出现的次数,或者是单词的 TF-IDF 值等。
伯努利朴素贝叶斯 :特征变量是布尔变量,符合 0/1 分布(二项分布),在文档分类中特征是单词是否出现。
其使用方法如下:
from sklearn.naive_bayes import GaussianNB
gnb = GaussianNB()
y_pred = gnb.fit(iris.data, iris.target).predict(iris.data)
要注意的是其中,高斯朴素贝叶斯分类器没有参数可以调整,但是支持导入样本权重sample_weight函数,多项分布朴素贝叶斯分类器有一个参数先验平滑因子a,伯努利朴素贝叶斯分类器有先验平滑因子a与一个二值化参数binarize.
同时他们都支持增量训练方法:partial_fit函数
代码实现
#导入 numpy 库,并构造训练数据 X 和 y。
import numpy as np
X = np.array([[-1, -1], [-2, -1], [-3, -2], [1, 1], [2, 1], [3, 2]])
Y = np.array([1, 1, 1, 2, 2, 2])
#使用 import 语句导入朴素贝叶斯分类器。
from sklearn.naive_bayes import GaussianNB 使用默认参数,创建高斯朴素贝叶斯分类器,并将该分类器赋给变量clf
clf = GaussianNB(priors=None)
#使用 fit() 函数进行训练,并使用 predict() 函数进行预测,得到预测结果为 1。(测试时可以构造二维数组达到同时预测多个样本的目的)
clf = GaussianNB(priors=None)
clf.fit(X, Y)
print(clf.predict([[-0.8, -1]]))
结果如下:
五、拓展:贝叶斯实现拼写检查器-代码实现
import re, collections
# 把语料中的单词全部抽取出来,转成小写,并且取出单词中间的特殊符号
def words(text): return re.findall('[a-z]+', text.lower())
def train(features):
model = collections.defaultdict(lambda: 1)# 词频的默认出现数为1
for f in features:
model[f] += 1
return model
NWORDS = train(words(open('big.txt').read()))
alphabet = 'abcdefghijklmnopqrstuvwxyz'
# 返回所有与单词w编辑距离为1的集合
def edits1(word):
n = len(word)
return set([word[0:i]+word[i+1:] for i in range(n)] + # deletion
[word[0:i]+word[i+1]+word[i]+word[i+2:] for i in range(n-1)] + # transposition
[word[0:i]+c+word[i+1:] for i in range(n) for c in alphabet] + # alteration
[word[0:i]+c+word[i:] for i in range(n+1) for c in alphabet]) # insertion
# 返回所有与单词w编辑距离为2的集合
# 在这些编辑距离小于2的词中间,只把那些正确的词作为候选词
def known_edits2(word):
return set(e2 for e1 in edits1(word) for e2 in edits1(e1) if e2 in NWORDS)
def known(words): return set(w for w in words if w in NWORDS)
def correct(word):
candidates = known([word]) or known(edits1(word)) or known_edits2(word) or [word]
return max(candidates, key=lambda w: NWORDS[w])
效果如下:
六、拓展:贝叶斯实现拼写检查器-原理代码
P( c ),文章中出现一个正确拼写词c的概率,也就是说,在一篇英语文章中,c出现的概率有多大
P(w|c), 在用户想键入 c 的情况下敲成 w 的概率. 因为这个是代表用户会以多大的概率把 c 敲错成 w
argmaxc, 用来枚举所有可能的 c 并且选取概率最大的
第一步:读取语料库,求取先验概率
#把语料中的单词全部抽取出来, 转成小写, 并且去除单词中间的特殊符号
def words(text): return re.findall('[a-z]+', text.lower())
#运用以一个匿名函数表示所有的词统计完之后最小的出现次数是一次
def train(features):
model = collections.defaultdict(lambda: 1)
for f in features:
model[f] += 1
return model
#“big.txt”是语料库
NWORDS = train(words(open('big.txt').read()))
/102609433
若出现新词: 假如说一个词拼写完全正确, 但是语料库中没有包含这个词, 这个词也永远不会出现在训练集中。于是, 我们就要返回出现这个词的概率是0。
这个方法是错误的, 因为概率为0这个代表了这个事件绝对不可能发生, 而在我们的概率模型中, 我们期望用一个很小的概率来代表这种情况. lambda: 1
运行这段之后会生成一个字典结构,在当中会统计出所有的词频,即得出了先验概率
效果如下:
第二步:求P(w|c)的概率:
编辑距离:
两个词之间的编辑距离定义为是用了几次插入(在词中间插入一个单字母),删除(删除一个单字母),交换(交换相邻两个单字母),替换(把一个字母换成里另一个)的操作从一个变到另一个词
#返回所有与单词 w 编辑距离为 1 的集合
def edits1(word):
n = len(word)
return set([word[0:i]+word[i+1:] for i in range(n)] + # deletion
[word[0:i]+word[i+1]+word[i]+word[i+2:] for i in range(n-1)] + # transposition
[word[0:i]+c+word[i+1:] for i in range(n) for c in alphabet] + # alteration
[word[0:i]+c+word[i:] for i in range(n+1) for c in alphabet]) # insertion
与 something 编辑距离为2的单词达到了 114,324 个
优化:在这些编辑距离小于2的词中间, 只把那些正确的词作为候选词,只能返回 3 个单词: ‘smoothing’, ‘something’ 和 ‘soothing’
#返回所有与单词 w 编辑距离为 2 的集合
#在这些编辑距离小于2的词中间, 只把那些正确的词作为候选词
def edits2(word):
return set(e2 for e1 in edits1(word) for e2 in edits1(e1))
def known(words): return set(w for w in words if w in NWORDS)
#如果known(set)非空, candidate 就会选取这个集合, 而不继续计算后面的
def correct(word):
candidates = known([word]) or known(edits1(word)) or known_edits2(word) or [word]
return max(candidates, key=lambda w: NWORDS[w])
七、参考文档链接
《统计学习方法》,李航
http://norvig.com/spell-correct.html
https://blog.csdn.net/ch18328071580/article/details/94407134
https://www.cnblogs.com/jpcflyer/p/11069659.html
https://blog.csdn.net/FontThrone/article/details/78824576
https://blog.csdn.net/qq_35456045/article/details/104550913
https://blog.csdn.net/weixin_44533129/article/details/100170227