利用朴素贝叶斯对名字进行性别预测

640?wx_fmt=jpeg

3个小节,预计用时30分钟

请打开您的电脑,按照步骤一步步完成哦!

本教程基于Python 3.5

原创者:s3040608090 | 修改校对:SofaSofa TeamC |

原文链接:http://sofasofa.io/tutorials/naive_bayes_classifier/

1. 条件概率与贝叶斯定理

对于事件A和B,当B发生的情况下,A发生的条件概率为

640?wx_fmt=jpeg

如果把P(AB)表示为P(B|A)P(A),那么

640?wx_fmt=jpeg

2. 朴素贝叶斯

朴素贝叶斯是一个基于贝叶斯定理的分类算法,其基本假设是所有特征是相互独立的。 举个例子来说,有一个二元分类问题,每个样本只有两个二元特征X1和X2。若已知一个样本(X1=1,X2=0),我们要预测它的标签为1的概率,就是等价于去计算

P(Y=1|X1=1,X2=0)

根据贝叶斯定理,我们可得

640?wx_fmt=jpeg

其中P(Y=1)被称为先验(prior),P(X1=1,X2=0|Y=1)被称为似然(likelyhood),P(X1=1,X2=0)被成为证据(evidence)。

因为我们假设所有特征独立,所以我们可以把P(Y=1|X1=1,X2=0)写成

640?wx_fmt=jpeg

推广到更普遍的情况下,假设数据有k个特征,

640?wx_fmt=jpeg

其中Z是缩放因子,使得概率和为1。

对于一个分类问题,如果我们只需要得到其标签,我们只需要求解

640?wx_fmt=jpeg

3. 实战练习

下面我们利用朴素贝叶斯对“机器读中文:根据名字判断性别”中的数据进行预测。首先下载,并读取数据。

# -*- coding: utf-8 -*-import pandas as pd
from collections import defaultdict
import math

# 读取train.txt
train = pd.read_csv('train.txt')
test = pd.read_csv('test.txt')
submit = pd.read_csv('sample_submit.csv')

看看训练集中的数据长什么样

train.head(10)
idnamegender
01闳家1
12玉璎0
23于邺1
34越英0
45蕴萱0
56子颀0
67靖曦0
78鲁莱1
89永远1
910红孙1
# 把数据分为男女两部分
names_female = train[train['gender'] == 0]
names_male = train[train['gender'] == 1]# totals用来存放训练集中女生、男生的总数
totals = {'f': len(names_female),
          'm': len(names_male)}

分别计算在所有女生(男生)的名字当中,某个字出现的频率。这一步相当于是计算 P(Xi|女生)和P(Xi|男生)

frequency_list_f = defaultdict(int)for name in names_female['name']:
    for char in name:
        frequency_list_f[char] += 1. / totals['f']

frequency_list_m = defaultdict(int)for name in names_male['name']:
    for char in name:
        frequency_list_m[char] += 1. / totals['m']
print(frequency_list_f['娟'])
0.004144009000562539
print(frequency_list_m['钢'])
0.0006299685015749209

上面两个例子说明P(名字中含有娟|女生)=0.004144,P(名字中含有钢|男生)=0.0006299

考虑到预测集中可能会有汉字并没有出现在训练集中,所以我们需要对频率进行Laplace平滑。

def LaplaceSmooth(char, frequency_list, total, alpha=1.0):
    count = frequency_list[char] * total
    distinct_chars = len(frequency_list)
    freq_smooth = (count + alpha ) / (total + distinct_chars * alpha)
    return freq_smooth

回顾第2节中的式子

640?wx_fmt=jpeg

在性别预测中,每个样本中大量的特征都是0。比如说只有X2=1,其他都为0,那么

640?wx_fmt=jpeg

由于P(Xi)的数值通常较小,我们对整体取对数(防止浮点误差),可得

640?wx_fmt=jpeg

如果一个人的名字中有两个字,假设X5=1,X10=1,其余为0,那么该名字的对数概率表达式为

640?wx_fmt=jpeg

对于一种性别,

640?wx_fmt=jpeg

只需要计算一次。为了方面,我们将其数值存放在bases当中

base_f = math.log(1 - train['gender'].mean())
base_f += sum([math.log(1 - frequency_list_f[char]) for char in frequency_list_f])

base_m = math.log(train['gender'].mean())
base_m += sum([math.log(1 - frequency_list_m[char]) for char in frequency_list_m])

bases = {'f': base_f, 'm': base_m}

对于logP(Xi=1|Y)−logP(Xi=0|Y)部分,我们利用如下函数计算

def GetLogProb(char, frequency_list, total):
    freq_smooth = LaplaceSmooth(char, frequency_list, total)
    return math.log(freq_smooth) - math.log(1 - freq_smooth)

最后我们只需要组合以上函数,实现

640?wx_fmt=jpeg

def ComputeLogProb(name, bases, totals, frequency_list_m, frequency_list_f):
    logprob_m = bases['m']
    logprob_f = bases['f']
    for char in name:
        logprob_m += GetLogProb(char, frequency_list_m, totals['m'])
        logprob_f += GetLogProb(char, frequency_list_f, totals['f'])
    return {'male': logprob_m, 'female': logprob_f}def GetGender(LogProbs):
    return LogProbs['male'] > LogProbs['female']

result = []for name in test['name']:
    LogProbs = ComputeLogProb(name, bases, totals, frequency_list_m, frequency_list_f)
    gender = GetGender(LogProbs)
    result.append(int(gender))

submit['gender'] = result

submit.to_csv('my_NB_prediction.csv', index=False)

最后结果输出在'my_NB_prediction.csv'中。不如上传到比赛页面看看结果哦。

我们可以看看预测结果如何。

test['pred'] = result
test.head(20)

idnamepred
00辰君0
11佳遥0
22淼剑1
33浩苳1
44俪妍0
55秉毅1
66妍艺0
77海防1
88壬尧1
99珞千0
1010义元1
1111才君1
1212吉喆1
1313少竣1
1414创海1
1515熙兰0
1616家冬1
1717方荧1
1818介二1
1919钰泷1

完整代码如下:

# -*- coding: utf-8 -*-import pandas as pd
from collections import defaultdict
import math

# 读取train.txt
train = pd.read_csv('train.txt')
test = pd.read_csv('test.txt')
submit = pd.read_csv('sample_submit.csv')#把数据分为男女两部分
names_female = train[train['gender'] == 0]
names_male = train[train['gender'] == 1]

totals = {'f': len(names_female),
          'm': len(names_male)}

frequency_list_f = defaultdict(int)for name in names_female['name']:
    for char in name:
        frequency_list_f[char] += 1. / totals['f']

frequency_list_m = defaultdict(int)for name in names_male['name']:
    for char in name:
        frequency_list_m[char] += 1. / totals['m']def LaplaceSmooth(char, frequency_list, total, alpha=1.0):
    count = frequency_list[char] * total
    distinct_chars = len(frequency_list)
    freq_smooth = (count + alpha ) / (total + distinct_chars * alpha)
    return freq_smooth

def GetLogProb(char, frequency_list, total):
    freq_smooth = LaplaceSmooth(char, frequency_list, total)
    return math.log(freq_smooth) - math.log(1 - freq_smooth)def ComputeLogProb(name, bases, totals, frequency_list_m, frequency_list_f):
    logprob_m = bases['m']
    logprob_f = bases['f']
    for char in name:
        logprob_m += GetLogProb(char, frequency_list_m, totals['m'])
        logprob_f += GetLogProb(char, frequency_list_f, totals['f'])
    return {'male': logprob_m, 'female': logprob_f}def GetGender(LogProbs):
    return LogProbs['male'] > LogProbs['female']


base_f = math.log(1 - train['gender'].mean())
base_f += sum([math.log(1 - frequency_list_f[char]) for char in frequency_list_f])

base_m = math.log(train['gender'].mean())
base_m += sum([math.log(1 - frequency_list_m[char]) for char in frequency_list_m])

bases = {'f': base_f, 'm': base_m}


result = []for name in test['name']:
    LogProbs = ComputeLogProb(name, bases, totals, frequency_list_m, frequency_list_f)
    gender = GetGender(LogProbs)
    result.append(int(gender))

submit['gender'] = result

submit.to_csv('my_NB_prediction12.csv', index=False)

机器学习在线性回归中的应用

本来分享了一篇google的机器学习入门文章,感觉很不错,排版写了两个夜晚,突然发现被人分享出来过,关键那人还声明原创,转载google不应该注明来源吗,没办法,我同步到个人博客了,点击【阅读原文】可以观看。

猜你可能喜欢

640?wx_fmt=jpeg

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值