这两天看了SofaSofa上的新手赛,有一个根据名字预测性别的:参考标杆模型 自己也试着从头到尾写了一遍提交上去结果82% 嗯排名是很迷 53/104 ,(耗时一天)继续努力~
这里说一下思路,(说一下 这个是给我自己看的 写的太乱,主要是记录下来方便我以后理解)
就是首先需要导入数据 由于是使用了pandas的read_csv()函数 得到的数据类型是DataFrame 所以相关操作需要熟悉以免出错
另外就是可以逆向思维,由于使用的模型是贝叶斯 所以对于该模型需要有一个较为清晰的认识,每一项概率所表达的意思是什么,转化成代码 应该怎么操作数据集等
这里说几点 :
P1=log(P(Y=y))+(求和号i=1—>n)P(Xi=0|Y=y) : 是跟训练集有关的 直接在训练集上求相应概率ok,对于每一个样本 在预测为男或者女时(当然是表示为概率)都一样的操作 跟name中的char是无关的,所以可以直接先计算出来;
对于贝叶斯公式的后面几项 如果一个名字有两个字即对应两个char 也就是对应两个特征Xi,Xj 就是计算下面的公式:
P2=log(P(Xi=1|Y=0))-log(P(Xi=0|Y=0))+ log(P(Xj=1|Y=0))-log(P(Xj=0|Y=0))(主要是每一个测试集的特征不一样,这里的特征是指name里的char)需要根据每一个name的char具体带入计算 所以写到这里的时候 就需要先提前写一个函数 就是计算特定字符在被预测为男 或女时的相应条件概率 ,这里需要注意
有可能存在训练数据集中的某一个特征(字 char)在训练集中没有 所以对于这些特殊字符需要考虑Laplace平滑处理 ,同样是在训练集上进行的!!!,只不过这里的条件概率和前面的条件概率处理不一样 ,前面是直接使用defaultdict[key]的默认值对不存在的key用0代替 这里则使用Laplace进行平滑处理
最后需要注意一点就是:
在对每一个样本 name进行判断男女时 概率值都是从 P1开始计算的对所有的char遍历 也就是累加P2 然后在这里比较判断为男女的两个概率值!
等到判断下一个name时需要重新再进行上述步骤计算
我想说的是每一个name下概率的起点都是一样的 就是P1
接下来附上代码:
# -*- coding: utf-8 -*-
"""
Created on Mon Apr 16 20:00:53 2018
@author: xuanxuan
"""
#嗯 我现在要重新写一遍,再过一遍啦
import pandas as pd
import math
from collections import defaultdict
#首先导入数据,这里需要注意使用pd.read_csv()函数读取的结果是DataFrame数据类型 注意一下相关操作
train=pd.read_csv("E:/pyhtonworkspace/py3-pratice/bymyself_practice/python_game/Data/Sofasofa/20180415/data/train.txt")
test=pd.read_csv("E:/pyhtonworkspace/py3-pratice/bymyself_practice/python_game/Data/Sofasofa/20180415/data/test.txt")
submit=pd.read_csv("E:/pyhtonworkspace/py3-pratice/bymyself_practice/python_game/Data/Sofasofa/20180415/data/sample_submit.csv")
#导入数据之后可以看一下数据的样子
#print(train.head(10)) #这里可以查看前10条数据
#把训练数据集按照男女性别分为男女两类
names_female=train[train['gender']==0] #此时names_female仍然是一个DataFrame数据类型 只不过gender列全都为0也就是女生
names_male=train[train['gender']==1]
totals={'f':len(names_female),'m':len(names_male)} #存储了names_female 和names_male也就是男女生总共有多少条数据
#print(totals)
#接下来计算男女生中每一个字出现的概率 结果存成defaultdict类型的字典 比如{’璇‘:00002,'西':0.0004......}
frequency_list_f=defaultdict(int)
for name in names_female['name']:
for char in name:
frequency_list_f[char]+=1.0/totals['f']
frequency_list_m=defaultdict(int)
for name in names_male['name']:
for char in name:
frequency_list_m[char]+=1.0/totals['m']
#首先计算贝叶斯公式的第一项和第二项 因为对于每一个样本 计算的都一样
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} #该字典中存放的就是判断为男生的概率+判断为男生的概率条件下 其他所有字符(也就是特征)为0 也就是不出现的概率
#由于对于测试集上的数字 有可能训练数据集上并没有出现 所以我们需要使用Laplace平滑概率
#这个计算的其实就是条件概率P(x=1|Y=0)只需要让frequency_list为女生字频集就行
#得到的概率就是为女生时某一个字也就是特征的条件概率
def Laplace_smooth(char,frequency_list,total,alpha=1.0):
count=frequency_list[char]*total
distint_count=len(frequency_list)
return (count+alpha)/(total+distint_count*alpha)
#接下来我们可以计算贝叶斯公式的第二项 也就是刚才使用Laplace_smooth()函数得到的条件概率 做一个减法就ok
def getlogpro(char,frequency_list,total):
smoothpro=Laplace_smooth(char,frequency_list,total)
return math.log(smoothpro)-math.log(1-smoothpro)
if __name__=="__main__":
#需要注意的是对于每一个名字来说 得到的判断为男女的概率都需要重新累加!!
Labels=[] #存放的是对测试数据集的预测标签 也就是男女
for name in test['name']: #对于每一个名字来说 需要存放对于改名字的某一个字符或者某两个字符出现的条件下判断为男女的概率,,也就是一些列概率值的累加
prob_f=bases['f']
prob_m=bases['m']
value=1
for char in name:
prob_f+=getlogpro(char,frequency_list_f,totals['f'])
prob_m+=getlogpro(char,frequency_list_m,totals['m'])
if prob_f>prob_m:
value=0
Labels.append(int(value))
submit['gender']=Labels
submit.to_csv("E:/pyhtonworkspace/py3-pratice/bymyself_practice/python_game/Data/Sofasofa/20180415/data/my_submit_prediction2",index=False)
prediction=pd.read_csv("E:/pyhtonworkspace/py3-pratice/bymyself_practice/python_game/Data/Sofasofa/20180415/data/my_submit_prediction2")
print(prediction.head(100))
加油~