声明:版权所有,转载请联系作者并注明出处  http://blog.csdn.net/u013719780?viewmode=contents



               90%的准确率,根据中文名字预测性别!


一、概述

  性别是人类差异最大的特征之一,不同的性别拥有不同的特征,譬如购物、电视剧、书籍等方面男生和女生的爱好有很大的不同。因此,知道了用户的性别就可以更加准确的判断用户的潜在行为和需求。由此可知,性别识别的重要性和价值性不言而喻,每个机器学习模型的构建,基本都会需要准确识别用户的性别。

  目前业内预测用户性别的方法有很多,大多数都是基于用户的行为数据、兴趣等方面进行性别判定,识别的准确性也参差不齐。但是,很多的时候我们拿不到用户的行为数据,这个时候用用户的行为数据、兴趣数据建立机器学习模型就显得力不从心了。同时,从用户的行为数据着手建立模型去预测用户的性别效果也并不会见得有多好,因为影响模型准确性的主要原因是这些用户的行为在性别上区分度有多大,如果区分度不明显,那模型和算法的准确性将会遇到明显的瓶颈。同时,基于用户行为的性别识别涉及的数据面非常广、数据依赖链条很长、数据计算复杂度很高,识别效能反而成为了痛点!


二、模型建立

  在这里,我们分享一下我们MSO用户性别预测模型:基于用户中文名字的用户性别预测方法!我们仅仅是根据用户的中文名字进行预测,没有用到用户的行为数据,但是实际识别准确率却高达 90% 以上,为什么准确率这么高?主要是因为男生、女生在取名上有很大的差异!下面我们分步骤来讲解下MSO的用户性别识别模型构建过程。

  首先,做一个声明,本文中送列举的名字均是泛指,如果正好与你名字相同,纯属巧合!在详细讲解模型之前,为了规范,我们来一个约定,本文中的姓名指的是包括姓和名字,比如‘谭文’,姓是‘谭’,名字是‘文’。名字不包括姓,例如‘谭文’的名字就是‘文’。首先,我们对用户的名字进行简单的处理,本文我们主要讨论两个字和三个字的姓名,超过三个字的姓名不予考虑。对于两个字的姓名(也就是只有一个名字的姓名)我们认为名字的第一个字是‘ ’,第二个字是名字的本身。为了阐述清楚,就以‘谭文’这个姓名为例,我们会将其处理成‘谭 文’。

  接下里,我们看看用户名字在第一个字和第二个字上面的差异:



In [26]:
def name_count_bygender(rdd):
    rdd = rdd.map(lambda x: (x[0][0], x[0][1], x[1]))

    rdd = pd.DataFrame(rdd.collect(), columns = ['first_name', 'gender', 'num'])

    rdd = rdd.sort(['num'], ascending=False)
    return rdd

first_name_count_bygender = name_count_bygender(first_name_count_bygender)

first_name_count_bygender[first_name_count_bygender.gender=='男'].head(30)


/opt/cloudera/parcels/anaconda3/lib/python3.5/site-packages/ipykernel/__main__.py:6: FutureWarning: sort(columns=....) is deprecated, use sort_values(by=.....)
Out[26]:
  first_name gender num
3352   170601
1379 33423
2869 12792
4546 10636
183 9411
5270 8954
4476 8861
4683 7605
5681 7508
5741 7399
5471 4857
4593 4733
3995 4580
4939 4315
3133 3612
4323 3580
1744 3317
2340 3157
5612 3089
3932 3082
150 3005
2575 2963
2045 2768
295 2737
3549 2691
3600 2669
1147 2601
3496 2580
4725 2519
2524 2464
In [28]:
first_name_count_bygender[first_name_count_bygender.gender=='女'].head(30)
Out[28]:
  first_name gender num
1871   139268
5896 15388
1929 10341
5131 9235
1756 8348
497 4937
2687 4815
1796 4604
3302 4387
4157 4015
5446 3693
2371 3561
5380 3501
3221 2852
1847 2787
5440 2691
517 2486
5429 2296
3611 2276
1527 2130
2209 2083
852 1904
3092 1864
4209 1843
5205 1786
698 1784
3204 1676
1283 1632
1063 1475
4374 1475
In [29]:
second_name_count_bygender = name_count_bygender(second_name_count_bygender)

second_name_count_bygender[second_name_count_bygender.gender=='男'].head(30)
/opt/cloudera/parcels/anaconda3/lib/python3.5/site-packages/ipykernel/__main__.py:6: FutureWarning: sort(columns=....) is deprecated, use sort_values(by=.....)
Out[29]:
  first_name gender num
4657 40584
3535 14480
358 14251
6285 12183
678 11729
800 10492
1189 9816
841 8866
4880 7998
5049 7810
4788 7650
1355 7460
2736 6939
1576 6677
435 6608
4366 6425
6558 6361
4903 6107
6080 5165
5519 5101
7525 5060
242 4677
4243 4643
6526 4545
2240 4536
1831 4306
5809 3926
2166 3823
5337 3624
3105 3566
In [30]:
second_name_count_bygender[second_name_count_bygender.gender=='女'].head(30)
Out[30]:
  first_name gender num
6101 11092
3548 8657
735 8441
1026 8186
5770 8159
7233 8051
4 8038
2167 8019
5381 7813
5159 7763
6758 7488
1115 7345
5587 6793
6662 5446
484 5419
3301 5287
3478 4763
3538 4444
7265 4415
5736 4320
6337 4273
966 4044
506 3845
4369 3441
630 3380
7389 3080
2156 3054
1034 2754
1740 2680
6070 2634



  从以上可以看出,男生、女生的名字中常用的字有很大的差别,同时在名字中第一个字和第二个字上又有不同,表明位置也是一个需要考虑的重要因素。因此,我们建立用户名字中每个字的‘男性偏好极性’,这里的男性偏好极性是我自己命名的,字的‘男性偏好极性’指的是用户名字中的每个字是拥有男生的概率,譬如说‘谭文’,前文已经说了,我们需要将其处理成‘谭 文’,则




'  '的  是'  '的'  '的

'文'的  


  这样,我们就得到了每个名字的第一个字和第二个字的男性偏好极性。则每个名字的性别预测概率就是第一个名字的男性偏好极性与第二个名字的男性偏好极性的加权平均。这样我们根据每个用户的名字最终得到了其性别。

  接下来我们看看测试结果:

            
test_rdd = test.rdd.map(lambda x: (x[0], x[1], drop_alnum(x[2]), x[3]))
test_rdd = test_rdd.filter(lambda x: len(x[2])<4 and len(x[2])>1)

def accuracy(rdd, p=0.5, threshold=0.5):
    rdd = rdd.map(lambda x: (predict(x[2], p), encode_label(x[3])))
    data = rdd.collect()
    label = [label for pre, label in data]
    pre = [1. if pre>0.5 else 0. for pre, label in data]
    count = 0
    for i, j in zip(label, pre):
        if i==j:
            count += 1
    
    return count/len(label)

accuracy(test_rdd, p=0.5, threshold=0.5)
Out[9]:
0.8180861340309921

  从上可以看出,我们的测试结果准确率达到了0.818,是不是不相信自己的眼睛,没看错,效果就是这么好,在我们仅仅根据名字并且没有用到任何算法的条件下得到这样好的结果,应该算是很满意的结果了。


  接下来,我们用一些算法来跑跑,看看准确率能否得到提升,我尝试了分别用randomforest、GBDT、LR和MLP神经网络模型进行预测,所用到的特征就是前文提取到的每个名字中第一个字、第二个字的男性偏好极性以及做了一些特征交互,得到的准确率分别如下:


In [44]:
depths = [2, 3, 4, 5, 6, 7, 8]
steps = [0.001, 0.01, 0.1]
best_gbdtAccuracy = 0
best_gbdtModel = None
for depth in depths:
    for step in steps:
        
        gbdt = GBTClassifier(maxIter=100, maxDepth=depth, stepSize=step, featuresCol="features", labelCol="label", 
                             predictionCol="prediction")
        gbdtModel = gbdt.fit(train)
        gbdtPredictions = gbdtModel.transform(test)
        gbdtAccuracy = evaluator.evaluate(gbdtPredictions)
        if gbdtAccuracy > best_gbdtAccuracy:
            best_gbdtAccuracy = gbdtAccuracy
            best_gbdtModel = gbdtModel
            
print("gbdtModel Test set accuracy = " + str(best_gbdtAccuracy))
gbdtModel Test set accuracy = 0.8849167482859941
In [47]:
reg_params = [0.001, 0.01]
reg_types = ['l1', 'l2']
best_lrAccuracy = 0
best_lrModel = None

for reg_param in reg_params:

    lr = LogisticRegression(maxIter=100, regParam=reg_param)
    lrModel = lr.fit(train)
    lrPredictions = lrModel.transform(test)
    lrAccuracy = evaluator.evaluate(lrPredictions)

    if lrAccuracy > best_lrAccuracy:
        best_lrAccuracy = lrAccuracy
        best_lrModel = lrModel
            
print("lrModel Test set accuracy = " + str(best_lrAccuracy))
lrModel Test set accuracy = 0.8888344760039177
In [48]:
steps=[0.005, 0.01, 0.03]
batchs = [32, 64, 128]
best_mlpAccuracy = 0
best_mlpModel = None

for step in steps:
    for batch_size in batchs:
        mlp = MultilayerPerceptronClassifier(maxIter=100, layers=[6, 32, 8, 2], 
                                              blockSize=batch_size, stepSize=step, seed=123)
        mlpModel = mlp.fit(train)
        mlpPredictions = mlpModel.transform(test)
        mlpAccuracy = evaluator.evaluate(mlpPredictions)
        
        if mlpAccuracy > best_mlpAccuracy:
            best_mlpAccuracy = mlpAccuracy
            best_mlpModel = mlpModel


print("mlpModel Test set accuracy = " + str(best_mlpAccuracy))
mlpModel Test set accuracy = 0.8834476003917727
In [49]:
depths = [2, 3, 4, 5, 6, 7, 8]
num_trees = [10, 20, 30]
best_rfAccuracy = 0
best_rfModel = None

for depth in depths:
    for num_tree in num_trees:
        rf = RandomForestClassifier(maxDepth=depth, numTrees=num_tree, 
                                    featuresCol="features", labelCol="label", 
                             predictionCol="prediction")
        rfModel = rf.fit(train)
        rfPredictions = rfModel.transform(test)
        rfAccuracy = evaluator.evaluate(rfPredictions)

        if rfAccuracy > best_rfAccuracy:
            best_rfAccuracy = rfAccuracy
            best_rfModel = rfModel
            
print("rfModel Test set accuracy = " + str(best_rfAccuracy))
rfModel Test set accuracy = 0.886119257086999

  从上述结果可以看到,我们训练的randomforest、GBDT、LR和MLP神经网络模型的准确率已经超过0.88了。最后,对前面做的5个模型做一个简单的ensemble,准确率超过了90%。


三、结果分析

  仅仅根据姓名达到0.9的准确率,应该算是很不错了,其实,另外将近百分之十的错误主要是一些中性名字、女生使用偏向男生的名字、男生使用偏向女生的名字导致的,比如我就看到原始数据里有个叫‘某莎莎’的,预测模型将其预测为女性,但是其真实性别是男生。这里我就不贴具体结果了,因为是公司项目。很显然,在没有行为数据的情况下,这样的名字总有一部分是错误的,譬如说“某莎莎”,大多数情况下是女生,但是也有一些男生取名某莎莎,这个时候仅仅根据姓名来预测的结果就是将名字叫莎莎的要么都预测为男生要么都预测为女生,当然,如果有了某莎莎的行为数据,那么模型的准确率应该能够得到进一步提升。譬如说,有两个用户,一个男生一个女生,都叫某莎莎,其中一个某莎莎经常购买女生的生活用品,另一个某莎莎经常购买一些男生的衣服之类的,那么这个时候我们就可以有很大的自信将两个某莎莎都预测准确。但是,有些童鞋可能会说,如果两个某莎莎都是女生,只不过经常购买男生衣服的那个某莎莎是帮她男朋友买的而已,那么这个时候模型也会预测错误。嗯~说的没错,这个时候算法也会预测为男生,但我们仔细想想,其实将其预测为男生应该更合理,因为我们我们更加关注的是其行为性别而不是生理性别,譬如说你给这个某莎莎推荐衣服,她对她男朋友特别好,就是经常给她男朋友买衣服,很少给自己买衣服,你给她推荐女生衣服很大的可能性不会点击进去看,然而你给她推荐男生衣服她反而很感兴趣,点击的可能性很大~



    
Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐