【参赛经验】开源一波就跑路——来自性别年龄预测参赛选手的匿名开源

TinyMind 2018-09-17 17:00
关注文章

比赛介绍

第二届易观算法大赛:性别年龄预测(https://www.tinymind.cn/competitions/43

关键点

该比赛有两个难点:

  1. 如何对每个device上app的start和close行为数据进行处理?

该数据较大,解压缩后约3个多G,一次性读入pandas.dataframe再处理不现实,有两种处理方式:
(1)分批次读入pandas进行分析处理
貌似群里很多人都是用的这种方式,但不清楚他们是如何实现的,都说处理起来非常简单,可能是他们提取构造特征没用这张巨表吧!但这张表包含的信息如何不用,肯定是数据的浪费,用户的性别和年龄一定与其操作APP的行为有必然的联系。
(2)用pyspark操作spark来处理大数据
众所周知,spark非常适合处理大数据,其提供了并行计算、流处理等方式。而且,python有专门的pyspark工具包用于操作spark处理数据,比如常见的pyspark.sql模块(用sql语句的形式来操作rdd和sql.dataframe)、pyspark.ml(基于sql.DataFrame的机器学习工具包)、pyspark.mllib(基于rdd的机器学习工具包)模块。

本文采用的是第二种方式,基于pyspark.dataframe处理大表数据,通过多表联立构造特征,然后采用pyspark.ml库和其中的PipeLine管道操作实现从训练、测试集格式化到模型拟合与预测一整套流程。

这里不得不提一下pyspark.ml.features模块中的Pipeline,其实在很多工具中都用到了这一概念,比如mongodb的聚合管道、pyspark.rdd的操作管道。一句话,Pipeline就是一个中间过程,是一种架构。平常没有水(数据)流动时没啥用,一旦有水(数据)时,它能传送水从一端到另一端(整理数据从一种格式到另一种格式)。

听说著名的机器学习库scikit-learn集成了pandas,还引入了ColumnTransformer、OneHotEncoder等估计器(实际上就是Pipeline),这下搞数据分析和机器学习的朋友有福了!感兴趣的朋友可以参考链接。强烈推荐大家看一下,会有深刻体会!

  1. 如何构造合适的特征?

(1)train数据集只有device_id、sex、age,要想实现机器学习建模,必须通过其它表构建合适(确实可能反应使用人性别和年龄)的特征,比如大多数女性使用者可能会频繁使用网购类app,那么我们可以使用某device上网购类app的开关记录次数作为特征男性可能经常玩游戏,那么我们可以采用某device上游戏类app的使用时长作为特征。只不过这样一些特征的构造过于复杂,涉及到多表的联立查询操作,费时!
(2)由于精力有限,本人尝试性的构造了三个特征,分别是:app_num(手机上安装app的数目),time_seconds(使用次数最多的app的使用时长),device_brand(虽然有几千种牌子,但实际上只需要20种的牌子就占总数的80%了,所以这里只对数目前20的手机牌子进行标记,剩余的牌子标记为“其它”)。

特征构造还有巨大的提升空间,构造越贴近实际场景的特征所需要的算力也越大。当然特征越接近实际场景,模型效果也会更好。另外,按理说随机森林算法要求提出异常值、平滑处理、归一化,此处均没做(在VectorAssembler管道前加一个归一化transformer管道即可)!

部分代码

此处是构造两个模型,分别对sex和age进行预测,以下为代码的核心部分,前面数据清洗与整理代码较多(下载本文附件可查看ipynb文件

 1#1、特征聚合
2import pyspark.ml.feature as ft
3import pyspark.ml.classification as cl
4import numpy as np
5
6featureCreator1=ft.VectorAssembler(
7            inputCols=["app_num_int","time_seconds_int","device_brand_int"],
8            outputCol='features'
9)
10featureCreator2=ft.VectorAssembler(
11            inputCols=["app_num_int","time_seconds_int","device_brand_int"],
12            outputCol='features'
13)
14RFClassifier1=cl.RandomForestClassifier(numTrees=3, maxDepth=2,featuresCol='features', labelCol='sex_int', seed=42)
15RFClassifier2=cl.RandomForestClassifier(numTrees=3, maxDepth=2,featuresCol='features',labelCol='age_level_int', seed=42)
16pipeline1=Pipeline(stages=[
17    featureCreator1,
18    RFClassifier1
19])
20pipeline2=Pipeline(stages=[
21    featureCreator2,
22    RFClassifier2
23])
24
25model1=pipeline1.fit(multi_label.na.drop())
26result=model1.transform(multi_label)
27model2=pipeline2.fit(multi_label.na.drop())
28result2=model2.transform(multi_label)

上面有一个问题,在管道拟合时,由于multi_label中特征存在空值,在进行VectorAssembler构造feature时会提示Py4J ERROR,所以在fit时采用了multi_label.na.drop()操作。当然,这对模型拟合影响不大,只不过在后面测试集上预测时也有这个问题,后面回查发现是部分device的device_brand特征为None,有可能是train/test数据集中部分device的型号在deviceid_brand表中没有,从而在join时也导致为None此处需要进一步查查原因,如确实特征数据缺失,可以舍弃这部分device的预测,也可以填充空值,还可以选择重新构造存在数据的特征,反正选择多多!

以下为test_dataset格式整理及模型预测代码:

 1#将device_test数据按构造的特征进行处理,然后倒入模型进行预测
2#训练数据:device_train_spark
3schema1=typ.StructType([typ.StructField('device_id',typ.StringType(),False)])
4device_test_spark=spark.createDataFrame(deviceid_test,schema=schema1)
5
6test_spark=device_test_spark.join(deviceid_packages_spark,on='device_id',how='left').join(feature_maxUseTime,on='device_id',how='left').join(deviceid_brand_spark,on='device_id',how='left')
7
8test_spark=test_spark.drop('app_id_lst')
9test_spark.take(5)
10
11#将测试数据集转换成数字格式
12test_dataset=test_spark.withColumn('device_brand_1',func.round('device_brand'))
13
14test_dataset=test_dataset.withColumn('app_num_int',func.col('app_num').cast(typ.IntegerType()))
15test_dataset=test_dataset.withColumn('time_seconds_int',func.col('max(count(time_seconds))').cast(typ.IntegerType()))
16test_dataset=test_dataset.withColumn('device_brand_int',func.col('device_brand_1').cast(typ.IntegerType()))
17test_dataset=test_dataset.select(['app_num_int','time_seconds_int','device_brand_int'])
18
19result_sex=model1.transform(test_dataset.dropna())
20result_sex.select('probability').collect()


本文由参赛选手匿名开源 😆
选手公众号:涛哥学数据分析
模型

暂无文件

{{panelTitle}}(1)
支持Markdown和数学公式,公式格式:\\(...\\)或\\[...\\]
flyfoxs 2018-09-18 15:39

发现性别和年龄分开预测然后在合并,效果不怎么好?

关注微信公众号