机器学习(三):贝叶斯分类器基本原理及代码实现

2019-06-16  本文已影响0人  风之舟

贝叶斯分类算法是统计学的一种分类方法,它是一类利用概率统计知识进行分类的算法。该算法的优点在于简单易懂、学习效率高、在某些领域的分类问题中能够与决策树、神经网络相媲美。接下来我们介绍一下这个算法:

一、基础知识

在我们讲贝叶斯分类器之前,我们先补充一下相关的数学知识:

1、条件概率

条件概率是指事件A在另一个事件B已经发生条件下发生的概率。条件概率表示为P(A/B),读作“在B条件下A的概率”。
若只有两个事件A,B,那么:P(AB) = P(A\B)P(B) = P(B\A)P(A)
即:p(A|B)=\frac{P(AB)}{P(B)}

2、全概率公式

表示若事件A1,A2,…,An构成一个完备事件组且都有正概率,则对任意一个事件B都有公式成立:
P(B)=P(AB+\bar{A}B) =P(AB)+P(\bar{A}B)=P(A)P(B|A)+P(\bar{A})P(B|\bar{A})同时P(B)也可以表示为:
P(B)=P(A_{1}B)+P(A_{2}B)+...+P(A_{n}B)
=\sum P(A_{i}B)
=\sum P(B|A_{i})*P(A_{i})

3、贝叶斯公式

由条件概率公式可以得到贝叶斯表达式
p(A|B)=\frac{P(AB)}{P(B)}=\frac{P(B|A)*P(A)}{P(B)}

将全概率公式代入到条件概率公式当中,对于事件A_{k}和事件B有:
P(A_{k}|B)=\frac{P(B|A_{k})*P(A_{k})}{\sum P(B|A_{i})*P(A_{i})}
a、P(A)是 A 的先验概率,之所以称为“先验”是因为它不考虑任何 B 方面的因素。

b、P(A|B)是已知 B 发生后 A 的条件概率,也由于得自 B 的取值而被称作 A 的后验概率。

c、P(B|A)是已知 A 发生后 B 的条件概率,也由于得自 A 的取值而被称作 B 的后验概率。

d、P(B)是 B 的先验概率,也作标淮化常量(normalizing constant)。

二、贝叶斯定理

设X是类标号未知的数据样本。设H为某种假定,如,数据样本X属于某特定的类C。对于分类问题,我们希望确定P(H\X)-给定观测数据样本,假定H成立的概率。P(H\X)是后验概率,或条件X下,H的后验概率。例如,假定数据样本世界是由水果组成,用它们的颜色和形状描述,假定X表示红色和圆的,H表示假定X是苹果,则P(H\X)反映当我们看到X是红色并是圆时,我们对X是苹果的确信程度。P(H)是先验概率,或H的先验概率,对于我们的例子,它是任意给定的数据样本为苹果的概率,而不管数据样本看上去如何。后验概率P(H\X)比先验概率P(H)基于更多的信息(如,背景知识)。P(H)是独立于X的。
类似的,P(X\H)是条件H下,X的后验概率。即,它是已知X是苹果,X是红色并且是圆的的概率。P(X)是X的先验概率。
“如何计算这些概率?”,给定数据集,P(X)、P(H)、P(X|H)我们是可以求出来的,这时候贝叶斯就发挥了作用,它提供了一种由P(X)、P(H)、P(X|H)计算后验概率P(H|X)的方法:
P(H|X)=\frac{P(X|H)(H)}{P(X)}

三、朴素贝叶斯分类器的公式

假设某个体有n项特征(Feature),分别为F1,F2,...,Fn。现在有m个类别(Category),分别为C1,C2,C3,...,Cm。贝叶斯分类器就是计算出概率最大的那个分类,也就是求下面这个算式的最大值:
P(C|F_{1}F_{2}...F_{n}) = \frac{P(F_{1}F_{2}...F_{n}|C)P(C)}{P(F_{1}F_{2}...F_{n})}
由于P(F1F2...Fn)对于所有的分类都是相同的,可以省略,问题就变成了求P(F_{1}F_{2}...F_{n}|C)P(C)
的最大值。
朴素贝叶斯分类器则是更进一步,假设所有特征都彼此独立,因此:
P(F_{1}F_{2}...F_{n}|C)P(C) = P(F_{1}|C)P(F_{2}|C)...P(F_{n}|C)P(C)
上式等号右边的每一项,都可以从统计资料中得到,由此就可以计算出每个类别对应的概率,从而找出最大概率的那个类。虽然"所有特征彼此独立"这个假设,在现实中不太可能成立,但是它可以大大简化计算。

四、拉普拉斯平滑(Laplace smoothing)

也就是参数为1时的贝叶斯估计,当某个分量在总样本某个分类中(观察样本库/训练集)从没出现过,会导致整个实例的计算结果为0。为了解决这个问题,使用拉普拉斯平滑进行处理。它的思想非常简单,就是对先验概率的分子(划分的计数)加1,分母加上类别数;对条件概率分子加1,分母加上对应特征的可能取值数量。这样在解决零概率问题的同时,也保证了概率和依然为1.

这里举个例子,假设在文本分类中,有3个类,C1,C2,C3,在指定的训练样本中,某个词语F1,在各个类中观测计数分别为0,990,10,则概率为P(F1|C1)=0,P(F1|C2)=0.99,P(F1|C3)=0.01,对这三个量使用拉普拉斯平滑的计算方法如下:1/1003=0.001,991/1003=0.988,11/1003=0.011

五、朴素贝叶斯定理的应用

贝叶斯算法在文本领域有重要的应用:
文本挖掘典型场景
1、网页自动分类
2、垃圾邮件判断
3、评论自动分析
4、通过用户访问内容判别用户喜好
这里我们进行两个实例的讲解:

#导包
from sklearn.datasets import fetch_20newsgroups
from sklearn.model_selection import train_test_split
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.naive_bayes import MultinomialNB
from sklearn.metrics.classification import classification_report

1、获取数据集

def naivebayes():
    """
    朴素贝叶斯进行分本分类
    :return:
    """
    #获取数据集
    news = fetch_20newsgroups(subset='all')
    print(news)
if __name__ == "__main__":
    naivebayes()

结果我就不展示了,有兴趣的同学加载一下看看。
2、进行数据集的分割和特征提取

def naivebayes():
    """
    朴素贝叶斯进行分本分类
    :return:
    """
    #获取数据集
    news = fetch_20newsgroups(subset='all')
    print(news)
    #对数据集进行分割
    x_train,x_test,y_train,y_test = train_test_split(news.data,news.target,test_size=0.25)
    #进行特征抽取
    tf = TfidfVectorizer()
    x_train = tf.fit_transform(x_train)
    print(tf.get_feature_names())#输出特征名
    x_test = tf.transform(x_test)
if __name__ == "__main__":
    naivebayes()

3、进行贝叶斯算法预测并输出准确率、精确率和召回率

def naivebayes():
    """
    朴素贝叶斯进行分本分类
    :return:
    """
    #获取数据集
    news = fetch_20newsgroups(subset='all')
    print(news)
    #对数据集进行分割
    x_train,x_test,y_train,y_test = train_test_split(news.data,news.target,test_size=0.25)
    #进行特征抽取
    tf = TfidfVectorizer()
    x_train = tf.fit_transform(x_train)
    print(tf.get_feature_names())#输出特征名
    x_test = tf.transform(x_test)
    #进行贝叶斯算法预测
    mlt = MultinomialNB(alpha=1.0)
    print(x_train.toarray())

    mlt.fit(x_train,y_train)
    y_predict = mlt.predict(x_test)
    print("预测的文章类型为:",y_predict)

    #得出准确率
    print("准确率为:",mlt.score(x_test,y_test))

    #得出精确率召回率
    print("每个类别的精确率和召回率:",classification_report(y_test,y_predict,target_names=news.target_names))#target_names表示每个类别的名称(比如文章分科技、娱乐等)

    return None


if __name__ == "__main__":
    naivebayes()
#正则匹配、字典
import re,collections
#把语料中的单词全部抽取出来,转成小写,并且去除单词中间的特殊符号
def words(text):#正则匹配
    return re.findall('[a-z]+',text.lower())#大写变小写
#如果遇到一个新词,拼写完全正确,但是语料库中没有包含这个词,那么这个词也永远不会出现在训练集中
#这时我们返回这个词的概率是0,但是这样不符合实际,因为概率为0就代表了这个事件绝对不会发生
#而在我们的概率模型中,我们期望用一个很小的概率来代表这种情况,词频设为1
def train(features):
    model=collections.defaultdict(lambda:1)#返回一个类似字典的对象
    for f in features:
        model[f]+=1#词频+1
    return model
NWORDS=train(words(open('E:\\学习文件\\机器学习\\数据集\\贝叶斯算法\\test.txt').read()))#返回字典类型
alphabet='abcdefghijklmnopqrstuvwxyz'
#编辑距离:两个词之间的编辑距离定义为使用了几次插入(在词中插入一个单字母),删除(删除一个单字母)
#交换(交换相邻两个字母),替换(一个字母换成另一个)的操作从一个词变成另外一个词
#返回所有与单词w编辑距离为1的姐
def edits(word):
    n=len(word)
    return set([word[0:i]+word[i+1:] for i in range(n)]+
              [word[0:i]+word[i+1:]+word[i]+word[i+2:] for i in range(n-1)]+
              [word[0:i]+c+word[i+1:] for i in range(n) for c in alphabet]+
              [word[0:i]+c+word[i:] for i in range(n+1) for c in alphabet])
#编辑距离为2的单词也会存在,但是单词数会非常多这里把编辑距离小于2的词中,只把那些正确的词作为候选词
def edits2(word):
    return set(e2 for e1 in edits(word) for e2 in edits(e1))
#正常来说把一个元音拼成另一个的概率要大于辅音(因为人常常把hello打成hallo这样)把单词的第一个字母拼错的概率会相对小,等等。
#但是为了简单起见,选择了一个简单的方法:编辑距离为1的正确单词比编辑距离为2的优先级高,而编辑距离为0的正确单词优先级比编辑距离为1的高
def known_edits(word):
    return set(e2 for e1 in edits(word) for e2 in edits(e1) if e2 in NWORDS)
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(edits(word)) or known_edits(word) or [word]
    return max(candidates,key=lambda w:NWORDS[w])
correct('tha')
实验结果

代码相对来说比较简单,相关说明都已经在代码中注释了,大家可以看一下~

六、算法总结

1、优点

2、缺点

上一篇 下一篇

猜你喜欢

热点阅读