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

比较好的开源包observerss/ngender,注意使用~~~~~

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

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

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

本教程基于Python 3.5

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


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

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

P(A|B)=P(AB)P(B).P(A|B)=P(AB)P(B).

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

P(A|B)=P(B|A)P(A)P(B).P(A|B)=P(B|A)P(A)P(B).

2. 朴素贝叶斯

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

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

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

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

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

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

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

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

P(Y|X1,X2,⋯,Xn)=1ZP(Y)∏i=1nP(Xi|Y)P(Y|X1,X2,⋯,Xn)=1ZP(Y)∏i=1nP(Xi|Y)

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

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

ypred=argmaxyP(Y=y)∏i=1nP(Xi|Y=y)ypred=arg⁡maxyP(Y=y)∏i=1nP(Xi|Y=y)

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|女生)和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.004144P(名字中含有娟|女生)=0.004144,P(名字中含有钢|男生)=0.0006299P(名字中含有钢|男生)=0.0006299

考虑到预测集中可能会有汉字并没有出现在训练集中,所以我们需要对频率进行Laplace平滑(什么是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节中的式子

P(Y)∏i=1nP(Xi|Y),P(Y)∏i=1nP(Xi|Y),

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

ypred=argmaxyP(Y=y)P(X2=1|Y=y)∏ni=1P(Xi=0|Y=y)P(X2=0|Y=y)ypred=arg⁡maxyP(Y=y)P(X2=1|Y=y)∏i=1nP(Xi=0|Y=y)P(X2=0|Y=y)

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

logP(Y=y)+∑i=1nlogP(Xi=0|Y=y)+logP(X2=1|Y=y)−logP(X2=0|Y=y)log⁡P(Y=y)+∑i=1nlog⁡P(Xi=0|Y=y)+log⁡P(X2=1|Y=y)−log⁡P(X2=0|Y=y)

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

logP(Y=y)+∑i=1nlogP(Xi=0|Y=y)log⁡P(Y=y)+∑i=1nlog⁡P(Xi=0|Y=y)

+logP(X5=1|Y=y)−logP(X5=0|Y=y)+logP(X10=1|Y=y)−logP(X10=0|Y=y)+log⁡P(X5=1|Y=y)−log⁡P(X5=0|Y=y)+log⁡P(X10=1|Y=y)−log⁡P(X10=0|Y=y)

对于一种性别,logP(Y=y)+∑ni=1logP(Xi=0|Y=y)log⁡P(Y=y)+∑i=1nlog⁡P(Xi=0|Y=y)只需要计算一次。为了方面,我们将其数值存放在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)log⁡P(Xi=1|Y)−log⁡P(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)

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

ypred=argmaxyP(Y=y)P(X2=1|Y=y)∏ni=1P(Xi=0|Y=y)P(X2=0|Y=y)ypred=arg⁡maxyP(Y=y)P(X2=1|Y=y)∏i=1nP(Xi=0|Y=y)P(X2=0|Y=y)

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)
  • 1
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值