《机器学习(周志华)》学习笔记(七)

Q:什么是贝叶斯分类器?

一句话:以贝叶斯方法为核心原理构造出来的分类程序统称为贝叶斯分类器,主要包括朴素贝叶斯分类器,半朴素贝叶斯分类器,以及贝叶斯网络。

Q:那么,贝叶斯方法又是什么?

贝叶斯方法是一位英国数学家托马斯·贝叶斯在18世纪提出来的概率学方法。此法强大之处不多赘述,贝叶斯学派占据了概率江湖中的半壁江山(另外一半江山归于频率学派)。

举个例子:

已知
——100名男生中有99个短发,1个长发;
——100名女生中有90个长发,10个短发;

那么
——从这200名学生中抽取一个学生,已知是男生,则该男生是长发的概率是多少?答案很简单,是0.01000。
P(长发|男生) = \frac{P(长发,男生) }{ P(男生)}
——从中200名学生中抽取一个学生,已知是长发,则该长发学生是男生的概率是多少?答案稍难一点,是0.01099。
P(男生|长发)= \frac{P(长发|男生)*P(男生)}{P(长发)}
由此可见,大多数的概率计算方法(古典概率模型、几何概率模型、条件概率公式、全概率公式、联合概率公式······)都是已知前提条件发生的情况下求各种可能结果发生的概率。而贝叶斯公式则是用于已知一件事情已经发生,求导致其发生的各种可能前提条件成立的概率。

已知:A => a 或 b 或 c 求P(a)
用古典概率模型、几何概率模型、条件概率公式、全概率公式、联合概率公式······

已知:A 或 B 或 C => a 求P(A)
用贝叶斯公式

更多贝叶斯方法的知识,可以查阅刘未鹏《数学之美番外:贝叶斯方法》

Q:怎样应用贝叶斯方法构造分类器?

我们继续以西瓜分类问题为例,用下面的色泽、根蒂等8个特征的数据,判断一个西瓜是好瓜还是坏瓜。

西瓜数据集3.0

应用贝叶斯方法的基本思想是,分别求出待分类西瓜是好瓜以及坏瓜的概率,哪一个概率高,就将其归到那一类。

那么
P(好瓜=是|色泽=青绿,根蒂=蜷缩,······)\\ P(好瓜=否|色泽=青绿,根蒂=蜷缩,······)
这两个概率怎么求?

首先我们假设样本的所有属性相互独立,比如西瓜的色泽是黑是绿,与根蒂是直是曲无关。则根据贝叶斯方法,求一个西瓜是好瓜还是坏瓜的概率的基本公式是如下

其中c是类别,向量\vec{x}是待测样本。因为我们并不需要求出P(c|\vec{x})的确切值,只需要知道P(c=好瓜|x)P(c=坏瓜|x)谁更大就行。因为分母P(x)对于P(c=好瓜|x)P(c=坏瓜|x)是一样的,所以可以一并舍去。所以关键要求出P(c)以及P(x|c),也就是

亦即
P(好瓜=是) P(色泽=?|好瓜)P(根蒂=?|好瓜)··· \\ P(好瓜=否)P(色泽=?|坏瓜)P(根蒂=?|坏瓜)···
这两条式子中基本上每一项都是一个简单的条件概率。对于离散型变量,一般情况下可以直接用数数的方法加古典概型算出来。

数据集中的密度和含糖率是连续型变量,他们的条件概率怎么求?可以假设这些连续数据服从正态分布,然后算出训练数据集中这两项属性的均值和方差,代入下面概率密度函数,即可算出。

用训练数据集算出各个特征的条件概率以及各个类别出现的概率以后,就可以应用贝叶斯公式预测一个西瓜是好瓜以及坏瓜的概率了。

在上面的例子中,我们

  1. 用到了贝叶斯公式
  2. 假设各个属性互不影响(实际上不是,比如敲声可能受密度影响)

所以我们刚刚构造出的分类器叫做朴素贝叶斯分类器(Naive Bayesian classifier)

Q:我觉得朴素贝叶斯分类器太Naive,想要高逼格一点,如何改进?

虽然朴素贝叶斯算法很Naive,但是实践显示其性能很已经不错,尤其是在文本分类领域有广泛应用,很多垃圾邮件分类算法用的就是朴素贝叶斯。

当然,可以不Naive一点,用半朴素贝叶斯(semi-naive Bayesian classifier)。

朴素贝叶斯, naive之处在于假设各个属性互不影响。这在实践中不易见到,所以人们尝试放宽点这个条件,比如改成“假设各个属性最多受一个属性影响(最多依赖于一个属性)”

如此一改,整个模型就由


变成了


两条式子的唯一的不同之处在于各个条件概率项的条件部分,pai指的是被依赖的属性。举个例子——若敲声受密度影响,则有

P(敲声=沉闷|好瓜=是,密度>0.700)
P(敲声=沉闷|好瓜=否,密度>0.700)
P(敲声=沉闷|好瓜=是,密度<=0.700)
P(敲声=沉闷|好瓜=否,密度<=0.700)

······
除此之外,其他与朴素贝叶斯无大出入。

Q:半朴素贝叶斯中还是有点naive,有没有不naive的贝叶斯算法?

当然有,比如贝叶斯网络(Bayesian network),又称信念网络(Belief network),也就是当我们完全放开了“属性之间相互影响”的假设的时候,所诞生的算法。此时一个属性可以影响多个其他属性,也可以受多个属性影响,这就比朴素贝叶斯和半朴素贝叶斯复杂多了。

贝叶斯网络之间的属性的影响关系,可以用一个有向无环图描述,图中每一个节点存储着该属性的条件概率。如



所以一个贝叶斯网络由一个有向无环图和一个条件概率表组成。

贝叶斯网络是相当高级和复杂的技术,目前我还没有全面理解,在此留白,等以后有机会再来补充。


Talk is cheap, show me the code!

使用朴素贝叶斯算法计算离散型和连续性变量时求条件概率的方法略有不同,但方便起见,我还是将其完全拆开放到两个模型里。首先时离散型变量的朴素贝叶斯模型:

"""
Naive Bayes classifier for categorical variables

:file: supervised.py
:author: Richy Zhu
:email: rickyzhu@foxmail.com
"""
import numpy as np

class MyCategoricalNBC:
    '''categorical naive bayes classifier'''

    def __init__(self):
        self.X = None
        self.y = None

    def fit(self, X, y):
        '''
        Train the nominal naive bayes classifier model

        Parameters
        ----------
        X: ndarray of shape (m, n)
            sample data where row represent sample and column represent feature
        y: ndarray of shape (m,)
            labels of sample data

        Returns
        -------
        self
            trained model
        '''
        self.X = X
        self.y = y
        return self
    
    def _predict(self, x):
        '''
        compute probabilities and make prediction.
        '''
        probas = {}
        clss = list(set(self.y))

        # compute probability for each attributes in x
        for c in clss:
            probas[c] = []
            dat = self.X[self.y==c]
            for attr_id, attr_val in enumerate(x):
                count = 0
                for row in dat:
                    if attr_val == row[attr_id]:
                        count += 1
                # use laplace smoothing
                probas[c].append((count+1)/(len(dat)+len(x)))
            probas[c].append(len(dat)/len(self.X))

        final_probas = {}
        from functools import reduce
        for c, prbs in probas.items():
            # theoretically not final probability because not divided by Pr(x)
           final_probas[reduce(lambda x,y:x * y, prbs)] = c
        return final_probas[max(final_probas)]
    
    def predict(self, X):
        '''
        Make prediction by the trained model.

        Parameters
        ----------
        X: ndarray of shape (m, n)
            data to be predicted, the same shape as trainning data

        Returns
        -------
        C: ndarray of shape (m,)
            Predicted class label per sample.
        '''
        if self.X is None:
             raise Exception("Model haven't been trained!")
        return np.array([self._predict(x) for x in X])

测试代码如下——因为sklearn包没有离散变量的数据集,又不想动用新闻数据集(其实是我的代码处理不了,会出bug😥),所以只能将就着用一下重复10次的西瓜数据集:

import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score

print('\nDescrete Naive Bayes')
print('---------------------------------------------------------------------')
xigua2 = np.array([
        ['青绿', '蜷缩', '浊响', '清晰', '凹陷', '硬滑', '好瓜'],
        ['乌黑', '蜷缩', '沉闷', '清晰', '凹陷', '硬滑', '好瓜'],
        ['乌黑', '蜷缩', '浊响', '清晰', '凹陷', '硬滑', '好瓜'],
        ['青绿', '蜷缩', '沉闷', '清晰', '凹陷', '硬滑', '好瓜'],
        ['浅白', '蜷缩', '浊响', '清晰', '凹陷', '硬滑', '好瓜'],
        ['青绿', '稍蜷', '浊响', '清晰', '稍凹', '软粘', '好瓜'],
        ['乌黑', '稍蜷', '浊响', '稍糊', '稍凹', '软粘', '好瓜'],
        ['乌黑', '稍蜷', '浊响', '清晰', '稍凹', '硬滑', '好瓜'],
        # ----------------------------------------------------
        ['乌黑', '稍蜷', '沉闷', '稍糊', '稍凹', '硬滑', '坏瓜'],
        ['青绿', '硬挺', '清脆', '清晰', '平坦', '软粘', '坏瓜'],
        ['浅白', '硬挺', '清脆', '模糊', '平坦', '硬滑', '坏瓜'],
        ['浅白', '蜷缩', '浊响', '模糊', '平坦', '软粘', '坏瓜'],
        ['青绿', '稍蜷', '浊响', '稍糊', '凹陷', '硬滑', '坏瓜'],
        ['浅白', '稍蜷', '沉闷', '稍糊', '凹陷', '硬滑', '坏瓜'],
        ['乌黑', '稍蜷', '浊响', '清晰', '稍凹', '软粘', '坏瓜'],
        ['浅白', '蜷缩', '浊响', '模糊', '平坦', '硬滑', '坏瓜'],
        ['青绿', '蜷缩', '沉闷', '稍糊', '稍凹', '硬滑', '坏瓜']
    ])
from sklearn.preprocessing import LabelEncoder
for i in range(xigua2.shape[1]):
    le = LabelEncoder()
    xigua2[:,i] = le.fit_transform(xigua2[:,i])
xigua2 = xigua2.astype(np.int).repeat(20, axis=0)
X = xigua2[:, :-1]
y = xigua2[:, -1]
X_train, X_test, y_train, y_test = train_test_split(X, y)

mycnb = MyCategoricalNBC()
mycnb.fit(X_train, y_train)
print('My Accuracy:', accuracy_score(mycnb.predict(X_test), y_test))

from sklearn.naive_bayes import CategoricalNB 
skcnb = CategoricalNB()
skcnb.fit(X_train, y_train)
print('SK Accuracy:', accuracy_score(skcnb.predict(X_test), y_test))

测试结果如下

$ python supervised_examples.py
Descrete Naive Bayes
---------------------------------------------------------------------
My Accuracy: 0.8235294117647058
SK Accuracy: 0.8235294117647058

下面是处理连续型变量数据集的朴素贝叶斯模型(假设所有连续变量都服从高斯分布):

"""
Naive Bayes classifier for continuous variables

:file: supervised.py
:author: Richy Zhu
:email: rickyzhu@foxmail.com
"""
import numpy as np
class MyGaussianNBC:
    '''Gaussian continuous naive bayes classifier'''

    def __init__(self):
        self.X = None
        self.y = None

    def fit(self, X, y):
        '''
        Train the Gaussian continuous naive bayes classifier model

        Parameters
        ----------
        X: ndarray of shape (m, n)
            sample data where row represent sample and column represent feature
        y: ndarray of shape (m,)
            labels of sample data

        Returns
        -------
        self
            trained model
        '''
        self.X = X
        self.y = y
        return self

    def _predict(self, x):
        '''compute probabilities and make prediction.'''
        from scipy.stats import norm
        probas = {}
        clss = list(set(self.y))

        # compute probability for each attributes in x
        for c in clss:
            probas[c] = []
            dat = self.X[self.y==c]
            for i, attr in enumerate(x):
                probas[c].append(norm(np.mean(dat[:,i]), np.std(dat[:,i])).pdf(attr))
            probas[c].append(len(dat)/len(self.X))

        final_probas = {}
        from functools import reduce
        for c, prbs in probas.items():
            # theoretically not final probability because not divided by Pr(x)
           final_probas[reduce(lambda x,y:x * y, prbs)] = c
        return final_probas[max(final_probas)]
    
    def predict(self, X):
        return np.array([self._predict(x) for x in X])

测试代码如下

import numpy as np
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score

print('\nContinuous Naive Bayes')
print('---------------------------------------------------------------------')
from sklearn.datasets import load_iris
X, y = load_iris(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(X, y)

mygnb = MyGaussianNBC()
mygnb.fit(X_train, y_train)
print('My Accuracy:', accuracy_score(mygnb.predict(X_test), y_test))

from sklearn.naive_bayes import GaussianNB
skgnb = GaussianNB()
skgnb.fit(X_train, y_train)
print('Sk Accuracy:', accuracy_score(skgnb.predict(X_test), y_test))

测试结果如下

$ python supervised_examples.py
Continuous Naive Bayes
---------------------------------------------------------------------
My Accuracy: 0.9210526315789473
Sk Accuracy: 0.9210526315789473

更多代码请参考https://github.com/qige96/programming-practice/tree/master/machine-learning


本作品首发于简书博客园平台,采用知识共享署名 4.0 国际许可协议进行许可。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,835评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,598评论 1 295
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,569评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,159评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,533评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,710评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,923评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,674评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,421评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,622评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,115评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,428评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,114评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,097评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,875评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,753评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,649评论 2 271

推荐阅读更多精彩内容

  • 【博客的主要内容主要是自己的学习笔记,并结合个人的理解,供各位在学习过程中参考,若有疑问,欢迎提出;若有侵权,请告...
    Paullu阅读 2,148评论 0 11
  • 注:题中所指的『机器学习』不包括『深度学习』。本篇文章以理论推导为主,不涉及代码实现。 前些日子定下了未来三年左右...
    我偏笑_NSNirvana阅读 39,717评论 12 145
  • 积跬步以致千里,积怠惰以致深渊 注:本篇文章在整理时主要参考了 周志华 的《机器学习》。 主要内容 通过某对象的先...
    指尖上的魔术师阅读 1,093评论 0 2
  • 各位小伙伴们大家好,前些日子,我看了一些关于贝叶斯方法的文章,其中以今天这一篇文章觉得最好,不仅讲的简单通俗易懂并...
    云时之间阅读 5,598评论 4 72
  • 玄首序 驯乎玄,浑行无穷正象天。 阴阳坒参,以一阳乘一统,万物资形。 方州部家,三位疏成。 曰陈其九九,以为数生。...
    巫姊覡阅读 2,341评论 0 2