如何用机器学习模型,为十几亿数据预测性别

人工智能 机器学习 大数据
基于用户画像进行广告投放,是优化投放效果、实现精准营销的基础;而人口属性中的性别、年龄等标签,又是用户画像中的基础信息。那该如何尽量准确的为数据打上这些标签?

[[327734]]

基于用户画像进行广告投放,是优化投放效果、实现精准营销的基础;而人口属性中的性别、年龄等标签,又是用户画像中的基础信息。那该如何尽量准确的为数据打上这些标签?

这时候机器学习就派上用场了。本文将以性别标签为例,介绍人口属性标签预测的机器学习模型构建与优化。

性别标签预测流程

通常情况下,无监督学习不仅很难学习到有用信息,而且对于学习到的效果较难评估。所以,如果可以,我们会尽可能地把问题转化成有监督学习。

对于性别标签也是如此,我们可以使用可信的性别样本数据,加上从TalkingData收集的原始数据中提取出来的有用信息,将性别标签的生产任务转化成有监督机器学习任务。更具体来说,男/女分别作为1/0标签(Label,也就是常说的Y值,为了方便表达,我们标记男/女分别为1/0标签),这样性别标签的任务就转化成了二分类任务。

性别标签的生产流程图如下:

 

如何用机器学习模型,为十几亿数据预测性别
  • 简单来说,输入为具有可信性别信息的样本数据,以及从近期活跃的原始数据中提取出有用特征;
  • 将两者join之后,得到可以直接用于建模的数据集;
  • 基于该数据集进行建模,学习出性别预测模型;
  • 再用该模型对全部样本进行预测,从而得到所有样本的性别打分。至此,模型部分的工作基本完成;
  • 最后一步是确定阈值,输出男/女标签。这里我们不依赖模型确定阈值,而是借助比较可信的第三方工具,保证在期望准确度(precision)下,召回尽可能多的样本。

另外,面对TalkingData十几亿的数据体量,在标签生产的过程中,为了加速运算,除了必须用单机的情况下,我们都会优先采用Spark分布式来加速运算。

特征与模型方法的版本迭代

为了优化模型的效果,我们又对该性别标签预测模型进行了多次迭代。

01性别预测模型V1

模型最初使用的特征包括4个维度:设备应用信息、嵌入SDK的应用包名、嵌入SDK的应用内自定义事件日志以及设备机型信息。

模型采用Xgboost(版本为0.5),基于每个维度的特征分别训练模型,得到4个子模型。每个子模型会输出基于该特征维度的设备男/女倾向的打分,分值区间从0到1,分值高代表设备为男性倾向,反之则为女性倾向。模型代码示例如下:

<左右滑动查看完整代码>

  1. import com.talkingdata.utils.LibSVM 
  2.  import ml.dmlc.xgboost4j.scala.DMatrix 
  3.  import ml.dmlc.xgboost4j.scala.spark.XGBoost//version 0.5 
  4.  
  5.  //train stage 
  6.  val trainRDD = LibSVM.loadLibSVMFile(sc, trainPath)// sc为SparkContext 
  7.  val model = XGBoost.train(trainRDD, paramMap, numRound, nWorkers = workers) 
  8.  
  9.  
  10.  //predict stage 
  11.  val testSet = LibSVM.loadLibSVMFilePred(sc,testPath,-1,sc.defaultMinPartitions) 
  12.  val pred = testSet.map(_._2).mapPartitions{ iter => 
  13.          model.value.predict(new DMatrix(iter)).map(_.head).toIterator 
  14.      }.zip(testSet).map{case(pred, (tdid, feauture)) => 
  15.          s"$tdid\t$pred" 
  16.      } 

缺点及优化方向:

  • 模型为四个子模型的融合,结构较复杂,运行效率较低,考虑改为使用单一模型;
  • 嵌入SDK的应用内自定义事件日志特征覆盖率低,且ETL处理资源消耗大,需重新评估该字段对模型的贡献程度;
  • 发现设备名称字段看上去有男/女区分度——部分用户群体会以名字或者昵称命名设备名(例如带有“哥”“军”等字段的倾向为男性,带有“妹”“兰” 等字段的倾向为女性),验证效果并考虑是否加入该字段。

02性别预测模型V2

对模型使用特征的4个维度进行了调整,改为:嵌入SDK的应用包名、嵌入SDK的应用AppKey、设备机型信息以及设备名称。

其中,对嵌入SDK的应用包名和设备名称做分词处理。再使用CountVectorizer将以上4类特征处理成稀疏向量(Vector),同时用ChiSqSelector进行特征筛选。

模型采用LR(Logistic Regression),代码示例如下:

<左右滑动查看完整代码>

  1. import org.apache.spark.ml.feature.VectorAssembler 
  2.    import org.apache.spark.ml.PipelineModel 
  3.    import org.apache.spark.ml.classification.LogisticRegression 
  4.  
  5.    val transformedDF = spark.read.parquet("/traindata/path")//分词、CountVectorizer、ChiSqSelector操作之后的特征,为vector列 
  6.  
  7.    val featureCols = Array("packageName","appKey""model""deviceName")                   
  8.    val vectorizer = new VectorAssembler(). 
  9.                      setInputCols(featureCols). 
  10.                      setOutputCol("features"
  11.    val lr = new LogisticRegression() 
  12.    val pipeline = new Pipeline().setStages(Array(vectorizer, lr)) 
  13.    val model = pipeline.fit(transformedDF) 
  14.  
  15.    //predict stage 
  16.    val transformedPredictionDF = spark.read.parquet("/predictData/path")//同train一致,为分词、CountVectorizer、ChiSqSelector处理之后的特征,为vector列 
  17.    val predictions = model.transform(transformedPredictionDF) 

优点及提升效果:

采用单一的模型,能够用常见的模型评估指标(比如ROC-AUC, Precision-Recall 等)衡量模型,并在后续的版本迭代中作为baseline,方便从模型角度进行版本提升的比较。

缺点及优化方向:

LR模型较简单,学习能力有限,后续还是替换成更强大的模型,比如Xgboost模型。

03性别预测模型V3

模型所使用的特征,除了上个版本包括的4个维度:嵌入SDK的应用包名、嵌入SDK的应用AppKey、设备机型信息以及设备名称,又增加了近期的聚合后的设备应用信息,处理方式与上个版本类似,不再赘述。

模型从LR更换成Xgboost(版本为0.82),代码示例如下:

<左右滑动查看完整代码>

  1. import org.apache.spark.ml.feature.VectorAssembler 
  2.  import ml.dmlc.xgboost4j.scala.spark.XGBoostClassifier//version 为0.82 
  3.  
  4.  val transformedDF = spark.read.parquet("/trainData/path")//分词、CountVectorizer操作之后的特征,为vector列 
  5.  
  6.  val featureCols = Array("packageName","appKey""model""deviceName")                   
  7.  val vectorizer = new VectorAssembler(). 
  8.                    setInputCols(featureCols). 
  9.                    setOutputCol("features"
  10.  val assembledDF = vectorizer.transform(transformedDF) 
  11.  
  12.  //traiin stage 
  13.  //xgboost parameters setting 
  14.  val xgbParam = Map("eta" -> xxx, 
  15.     "max_depth" -> xxx, 
  16.     "objective" -> "binary:logistic"
  17.     "num_round" -> xxx, 
  18.     "num_workers" -> xxx) 
  19.  val xgbClassifier = new XGBoostClassifier(xgbParam). 
  20.      setFeaturesCol("features"). 
  21.      setLabelCol("labelColname"
  22.  
  23.  model = xgbClassifier.fit(assembledDF) 
  24.  
  25.  //predict stage 
  26.  val transformedPredictionDF = spark.read.parquet("/predictData/path")//同train一致,为分词、CountVectorizer操作之后的特征,为vector列 
  27.  val assembledpredicDF = vectorizer.transform(transformedPredictionDF) 
  28.  val predictions = model.transform(assembledpredicDF) 

优点及提升效果:

  • 相比上个版本,AUC提升了6.5%,在最终的性别标签生产中召回率提升了26%。考虑到TalkingData的十几亿的数据体量,这个数值还是很可观的。

04性别预测模型V4

除了上个版本包括的5个特征维度,还添加了TalkingData自有的三个广告类别维度的特征,虽然广告类别特征覆盖率仅占20%,但对最终标签的召回率的提升也有着很大的影响。

模型由Xgboost替换成DNN,设置最大训练轮数(Epoch)为40,同时设置了early stopping参数。考虑到神经网络能工作是基于大数据的,因此我们将用于训练的样本量扩充了一倍,保证神经网络的学习。

DNN的结构如下:

<左右滑动查看完整代码>

  1. python 
  2.    GenderNet_VLen( 
  3.      (embeddings_appKey): Embedding(xxx, 64, padding_idx=0) 
  4.      (embeddings_packageName): Embedding(xxx, 32, padding_idx=0) 
  5.      (embeddings_model): Embedding(xxx, 32, padding_idx=0) 
  6.      (embeddings_app): Embedding(xxx, 512, padding_idx=0) 
  7.      (embeddings_deviceName): Embedding(xxx, 32, padding_idx=0) 
  8.      (embeddings_adt1): Embedding(xxx, 16, padding_idx=0) 
  9.      (embeddings_adt2): Embedding(xxx, 16, padding_idx=0) 
  10.      (embeddings_adt3): Embedding(xxx, 16, padding_idx=0) 
  11.      (fc): Sequential( 
  12.        (0): Linear(in_features=720, out_features=64, bias=True
  13.        (1): BatchNorm1d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True
  14.        (2): ReLU() 
  15.        (3): Dropout(p=0.6) 
  16.        (4): Linear(in_features=64, out_features=32, bias=True
  17.        (5): BatchNorm1d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True
  18.        (6): ReLU() 
  19.        (7): Dropout(p=0.6) 
  20.        (8): Linear(in_features=32, out_features=16, bias=True
  21.        (9): BatchNorm1d(16, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True
  22.        (10): ReLU() 
  23.        (11): Dropout(p=0.6) 
  24.        (12): Linear(in_features=16, out_features=2, bias=True
  25.      ) 
  26.    ) 

优点及提升效果:

  • 与上个版本对比,AUC仅提升了1.5%,但在最终性别标签生产中的召回率提升了13%,考虑数据体量以及现有的标签体量,这个提升还是不错的。由此可以看出,在验证版本迭代效果的时候,我们不应该仅仅从模型的AUC这单一指标来衡量,因为这对版本迭代的效果提升程度衡量不够准确。我们应该验证最终的、真正的指标提升情况——在性别标签预测中,是期望准确度(precision)下召回的样本数量。但我们仍然可以在版本优化时使用AUC等模型相关指标,来快速验证控制变量的实验效果,毕竟这些指标容易计算。

模型探索小建议

从原始日志当中抽取字段聚合成信息,需要经过很多步ETL,也会涉及很多优化方式,这部分有专门的ETL团队负责,在这里不做过多介绍。

模型团队可以直接使用按时间聚合之后的字段进行建模任务,尽管如此,ETL和特征生成所花费的时间,也占据了模型优化和迭代的大部分时间。

下面总结两个优化方面的坑和解决经验,希望能给大家一些参考。

1.对于性别标签预测,输入的特征大部分为Array类型,比如近期采集到的设备应用信息。对于这种类型的字段,在训练模型之前,我们一般会调用CountVectorizer将Array转成Vector,然后再作为模型的输入,但是CountVectorizer这一步非常耗时,这导致我们在版本迭代时不能快速实验。

针对该问题,我们可以事先完成这一步转换,然后将生成的Vector列也存储下来,这样在每次实验时,就可以节省CountVectorizer消耗的时间。

在实际生产中,因为有很多标签的生产都会用到同样的字段,事先将Array转成Vector存储下来,后续不同任务即可直接调用Vector列,节省了很多时间。

2.虽然第一条能够节省不少时间,但Spark还是更多用于生产。其实在模型前期的探索当中,我们也可以先用Spark生成训练集——因为真实样本通常不会很多,生成的训练集往往不是很大,这时我们就可以用单机来进行快速实验了。

在单机上,我们可以使用Python更方便的画图来更直观的认识数据,更快的进行特征筛选,更快的验证想法。在对数据、对模型有了深入的了解之后,我们就可以把实验所得的结论快速应用到生产当中。

作者简介:张小艳,TalkingData数据科学家,目前负责企业级用户画像平台的搭建以及高效营销投放算法的研发,长期关注互联网广告、用户画像、欺诈检测等领域。

责任编辑:未丽燕 来源: 今日头条
相关推荐

2015-07-27 15:06:05

2020-11-17 17:28:29

机器学习技术人工智能

2020-07-14 08:34:07

AI机器学习预测股价

2017-03-24 08:58:23

机器学习算法预测

2018-03-20 15:33:05

深度学习加密货币

2020-08-28 07:00:00

机器学习预测股市人工智能

2013-11-08 17:57:01

SAP

2018-02-01 16:25:55

2017-07-17 15:02:51

大数据银行用户流失

2023-12-25 10:53:54

机器学习模型性能

2021-02-22 11:44:43

机器学习数据泄露学习

2023-07-21 12:48:37

2020-12-21 15:39:59

大数据机器学习数据湖

2018-03-16 11:50:33

大数据数据科学安全

2020-02-04 11:06:42

大数据技术存储

2017-12-26 10:48:37

深度学习原始数据

2019-11-28 09:23:17

Python机器学习数据库

2017-03-24 15:58:46

互联网

2017-07-13 10:12:58

机器学习

2020-09-22 14:59:52

机器学习人工智能计算机
点赞
收藏

51CTO技术栈公众号