贝叶斯分类算法实例 --根据姓名推测男女

一.从贝叶斯公式开始

贝叶斯分类其实是利用用贝叶斯公式,算出每种情况下发生的概率,再取概率较大的一个分类作为结果。我们先来看看贝叶斯公式:

P(A|B) = P(B|A) P(A) / P(B)

其中P(A|B)是指在事件B发生的情况下事件A发生的概率。

在贝叶斯定理中,每个名词都有约定俗成的名称:

  • P(A|B)是已知B发生后A的条件概率,也由于得自B的取值而被称作A的后验概率。
  • P(A)是A的先验概率(或边缘概率)。之所以称为"先验"是因为它不考虑任何B方面的因素。
  • P(B|A)是已知A发生后B的条件概率,也由于得自A的取值而被称作B的后验概率。
  • P(B)是B的先验概率或边缘概率。

这里可以用一个例子来说明这个公式。

看一个简单的小例子来展示贝叶斯定理

病人的例子:
某个医院早上收了八个门诊病人,如下表。

症状 职业 疾病
打喷嚏 护士   感冒
打喷嚏 农夫   过敏
头痛  建筑工人 脑震荡
头痛  建筑工人 感冒
打喷嚏  建筑工人 过敏
打喷嚏 教师   感冒
头痛  教师   脑震荡
打喷嚏 教师   过敏

现在又来了第九个病人,是一个打喷嚏的建筑工人。请问他患上感冒的概率有多大?

根据贝叶斯定理:

P(A|B) = P(B|A) P(A) / P(B)

可得满足“打喷嚏”和“建筑工人”两个条件下,感冒的概率如下:

 P(感冒|打喷嚏x建筑工人)
= P(打喷嚏x建筑工人|感冒) x P(感冒) / P(打喷嚏x建筑工人)

假定"打喷嚏"和"建筑工人"这两个特征是独立的(即这两个条件没有相关性,比如不存在说他是建筑工人他打喷嚏的概率比较大或者比较小这种关系),因此,上面的等式就变成了。

 P(感冒|打喷嚏x建筑工人) 
 = P(打喷嚏|感冒) x P(建筑工人|感冒) x P(感冒) /  P(打喷嚏) x P(建筑工人)

通过统计可得:

 P(感冒|打喷嚏x建筑工人) 
 = (2/3) x (1/3) x (3/8) / (5/8) x (3/8) 
 = (16/45)

通过贝叶斯公式算出了满足条件下感冒的概率,那么现在贝叶斯分类器如何实现呢?

接上面的例子,从上面我们得出了 P(感冒|打喷嚏x建筑工人) 的值,那么我们可以再算出
P(不感冒|打喷嚏x建筑工人) 的值,计算结果如下:

 P(不感冒|打喷嚏x建筑工人) 

 = P(打喷嚏|不感冒) x P(建筑工人|不感冒) x P(不感冒)  /  P(打喷嚏) x P(建筑工人)
 = (3/5) x (2/5) x (5/8) / (5/8) x (3/8) 
 = (16/25)

OK,现在我们知道来如果新来一个打喷嚏的建筑工人,他患感冒的几率是P(感冒|打喷嚏x建筑工人)= (16/45)。不患感冒的几率是P(不感冒|打喷嚏x建筑工人)= (16/25)。

通过对概率的比较,我们就可以将打喷嚏的建筑工人分类到“不感冒”人群中(不感冒的概率比较大)。 这就是朴素贝叶斯分类器的最简单的应用了。当然你也看到了,贝叶斯分类器需要我们应用到统计所得的结果,这需要数据量比较大,大到能满足大数定理(大数定理这里就不多解释啦,自行百度即可),以及样本数据足够客观。

接下来我们看一个实际的例子,是我在 github 上看到的一个项目例子,根据姓名来对性别进行分类。看上去觉得很不可思议吧,其实也是用了上述说的贝叶斯分类的方法。

二.贝叶斯分类器根据姓名判别男女 -python

项目github地址:https://github.com/observerss/ngender

先说一下主要思路,我们日常从一个人的名字中,基本上能大致判断这个名字的主人是男是女。比如李大志,这个名字一听就很男性。为什么呢?因为字和字男性名字用得比较多。虽然机器一眼看不出来,但它可以通过统计信息来判断。如果有足够多的数据,我们就可以统计出字和字用作男性名字的比例,计算概率信息。然后就可以用这些概率,运用上述的贝叶斯公式来进行计算,判定性别。

代码其实不难,各个字的统计数据已经计算好,在项目中给出。我们只需要读取文件数据,存储到 python 的字典中,计算出概率,然后预测的时候进行计算即可。我们先看核心代码,稍后会有例子说明。

里面核心代码文件为:

这里主要讲一下核心代码的内容:https://github.com/observerss/ngender/blob/master/ngender/ngender.py

class Guesser(object):

    //初始化函数,调用下面的_load_model()函数
    def __init__(self):
        self._load_model()

    //初始化一些参数
    def _load_model(self):
        self.male_total = 0
        self.female_total = 0
        self.freq = {}

        //这里加载charfreq.csv文件,这个文件存放的是一些汉字是男女的统计信息
        with open(os.path.join(os.path.dirname(__file__),
                               'charfreq.csv'),
                  'rb') as f:
            # skip first line
            next(f)
            //将文件中的信息存储,累加,以便稍后计算概率
            for line in f:
                line = line.decode('utf-8')
                char, male, female = line.split(',')
                char = py2compat(char)
                //计算男性总数
                self.male_total += int(male)
                //计算女性总数
                self.female_total += int(female)
                //一个汉字对应的那女数量
                self.freq[char] = (int(female), int(male))

        self.total = self.male_total + self.female_total

        //一个汉字是男女概率
        for char in self.freq:
            female, male = self.freq[char]
            self.freq[char] = (1. * female / self.female_total,
                               1. * male / self.male_total)

    def guess(self, name):
        name = py2compat(name)
         //去掉姓氏
        firstname = name[1:]
        //过滤掉不在这个unicode编码范围内的字符
        for char in firstname:
            assert u'\u4e00' <= char <= u'\u9fa0', u'姓名必须为中文'

        //贝叶斯分类器,分别计算出男的概率和女的概率
        pf = self.prob_for_gender(firstname, 0)
        pm = self.prob_for_gender(firstname, 1)

        //若名字为男的概率较大,则分类为男,反之则为女
        if pm  pf:
            return ('male', 1. * pm / (pm + pf))
        elif pm < pf:
            return ('female', 1. * pf / (pm + pf))
        else:
            return ('unknown', 0)

    //贝叶斯公式的应用
    def prob_for_gender(self, firstname, gender=0):
        p = 1. * self.female_total / self.total \
            if gender == 0 \
            else 1. * self.male_total / self.total

        for char in firstname:
            p *= self.freq.get(char, (0, 0))[gender]

        return p


guesser = Guesser()

上述代码还是比较简单的,首先在初始化的时候会调用 _load_model() 函数,这个函数完成的是一些概率计算工作,比如先将每个字对应是男是女的概率计算好存储在字典中。

然后在计算的时候,先过滤掉姓氏。然后分别计算出这个名字是男是女的概率,比如计算 P(男|李大志)和P(女|李大志),,对比哪个概率大一些,然后进行男女分类。

这里放上一个例子:判断

P(gender=男|name=本山) 
= P(name=本山|gender=男) * P(gender=男) / P(name=本山)
= P(name has 本|gender=男) * P(name has 山|gender=男) * P(gender=男) / P(name=本山)
  • 公式原理为贝叶斯公式,下面对公式中中各个项进行解答,首先明确我们已经统计得到P(gender=男),P(gender=女)的概率。

怎么算 P(name has 本|gender=男)?

  • “本”在男性名字中出现的次数 / 男性字出现的总次数

怎么算 P(gender=男)?

  • 男性名出现的次数 / 总次数

怎么算 P(name=本山)?

这个概率对男女来说都是一样的,所以没必要算出来,即我们只需要比较P(name=本山|gender=男) * P(gender=男)和P(name=本山|gender=女) * P(gender=女)两部分谁比较大即可做出判断。

以上就是贝叶斯分类器介绍的全部内容啦。

参考文章:
http://www.ruanyifeng.com/blog/2013/12/naive_bayes_classifier.html


推荐阅读:

python Kmeans算法解析
从分治算法到 MapReduce

你可能感兴趣的:(贝叶斯分类算法实例 --根据姓名推测男女)