进行广告投放,是优化投放效果,实现精准营销的基础;而人口统计属性中的性别、年龄等标签也被用于用户画像。基本信息。那么如何尽可能准确地标注数据呢?这时候机器学习就派上用场了。本文将以性别标签为例,介绍用于人口属性标签预测的机器学习模型的构建与优化。性别标签预测过程通常,无监督学习不仅难以学习到有用的信息,而且难以评估学习效果。所以,如果可能的话,我们尽量把问题转化为监督学习。性别标签也是如此。我们可以利用可信的性别样本数据,加上从TalkingData收集的原始数据中提取的有用信息,将性别标签的生产任务转化为有监督的机器学习任务。更具体的,将male/female分别作为1/0标签(Label,也就是常说的Y值,为了表述方便,我们分别将male/female作为1/0标签),这样task性别标签转化为两个分类任务。性别标签的制作流程图如下:简单来说,输入是具有可信性别信息的样本数据,从最近活跃的原始数据中提取有用的特征;两者加入后,我们就可以得到一个模型,可以直接用于建模数据集;基于数据集建模学习性别预测模型;然后用模型对所有样本进行预测,从而得到所有样本的性别得分。至此,模型部分的工作基本完成;最后一步是确定阈值并输出男/女标签。这里我们不依赖模型来确定阈值,而是使用更可信的第三方工具来保证尽可能多的样本在预期的准确度(precision)下被召回。另外,面对TalkingData超过10亿的数据量,在标签制作过程中,为了加快计算速度,除了必须使用单机的情况,我们都会先使用Spark分布式来加速计算。特征和模型方法的版本迭代为了优化模型的效果,我们对性别标签预测模型进行了多次迭代。01性别预测模型V1最初使用四个维度:设备应用信息、SDK嵌入的应用程序包名称、SDK嵌入的应用内自定义事件日志、设备型号信息。该模型使用Xgboost(0.5版本),根据每个维度的特征训练模型,得到4个子模型。每个子模型将根据设备的男性/女性方向的特征维度输出一个分数。得分范围从0到1。得分高表示该设备是面向男性的,反之则表示该设备是面向女性的。模型代码示例如下:<左右滑动查看完整代码>importcom.talkingdata.utils.LibSVMimportml.dmlc.xgboost4j.scala.DMatriximportml.dmlc.xgboost4j.scala.spark.XGBoost//version0.5//trainstagevaltrainRDD=LibSVM.loadLibSVMFile(sc,trainPath)//sc是SparkContextvalmodel=XGBoost.train(trainRDD,paramMap,numRound,nWorkers=workers)//predictstagevaltestSet=LibSVM.loadLibSVMFilePred(sc,testPath,-1,sc.defaultMinPartitions)valpred=testSet.map(_._2).mapPartitions{iter=>model.value.predict(newDMatrix(iter)).map(_.head).toIterator}.zip(testSet).map{case(pred,(tdid,feauture))=>s"$tdid\t$pred"}缺点及优化方向:该模型是四个子模型的融合,结构复杂,运行效率低。考虑改用单一模型;SDK中嵌入的自定义事件日志的特征覆盖率低,ETL处理资源消耗大,需要重新评估该字段对模型的贡献;发现设备名称字段似乎有男/女区分——一些用户组会用名字或昵称来命名设备名称(例如,带有“兄弟”和“军队”等字段的往往是男性,有“姐姐”和“岚”的字段多为女性),验证效果考虑是否增加该字段。02性别预测模型V2将模型的使用特征4个维度调整为:SDK嵌入的应用包名称、SDK嵌入应用的AppKey、设备型号信息、设备名称。其中,对SDK中嵌入的应用包名和设备名进行分词。然后使用CountVectorizer将以上四类特征处理成稀疏向量(Vector),使用ChiSqSelector进行特征筛选。模型使用LR(LogisticRegression),代码示例如下:<左右滑动查看完整代码>importorg.apache.spark.ml.feature.VectorAssemblerimportorg.apache.spark.ml.PipelineModelimportorg.apache。spark.ml.classification.LogisticRegressionvaltransformedDF=spark.read.parquet("/traindata/path")//分词、CountVectorizer、ChiSqSelector操作后的特征,为向量列valfeatureCols=Array("packageName","appKey",“模型”,“设备名称”)valvectorizer=newVectorAssembler().setInputCols(featureCols).setOutputCol(“features”)vallr=newLogisticRegression()valpipeline=newPipeline().setStages(Array(vectorizer,lr))valmodel=pipeline.fit(transformedDF)//predictstagevaltransformedPredictionDF=spark.read.parquet("/predictData/path")//和train一致,是分词、CountVectorizer、ChiSqSelector处理后的特征,是向量列valpredictions=model.transform(transformedPredictionDF)单个模型,可以使用常用的模型评估指标(如ROC-AUC、Precision-Recall等)对模型进行度量,并作为后续版本迭代中的baseline,方便从模型的角度比较版本改进。缺点及优化方向:LR模型比较简单,学习能力有限。它将被更强大的模型所取代,例如Xgboost模型。03性别预测模型V3模型中使用的特征,除了之前版本包含的四个维度:SDK嵌入的应用包名、SDK嵌入的应用AppKey、设备型号信息、设备名称,以及最近聚合的设备应用信息的处理方法与上一版本类似,此处不再赘述。该模型从LR更改为Xgboost(版本0.82)。代码示例如下:<左右滑动查看完整代码>importorg.apache.spark.ml.feature.VectorAssemblerimportml.dmlc.xgboost4j.scala.spark.XGBoostClassifier//版本为0.82valtransformedDF=spark.read。parquet("/trainData/path")//分词和CountVectorizer操作后的特征,是向量列valfeatureCols=Array("packageName","appKey","model","deviceName")valvectorizer=newVectorAssembler().setInputCols(featureCols).setOutputCol("features")valassembledDF=vectorizer.transform(transformedDF)//traiinstage//xgboostparameterssettingvalxgbParam=Map("eta"->xxx,"max_depth"->xxx,"objective"->"binary:logistic","num_round"->xxx,"num_workers"->xxx)valxgbClassifier=newXGBoostClassifier(xgbParam).setFeaturesCol("features").setLabelCol("labelColname")model=xgbClassifier.fit(assembledDF)//predictstagevaltransformedPredictionDF=spark.read.parquet("/predictData/path")//和train一致,是分词和CountVectorizer操作后的特征,是向量列以及改进效果:与上面这个版本相比,AUC提升了6.5%,最终性别标签制作中的召回率提升了26%,考虑到TalkingData亿级数据的数据量,这个值还是04性别预测模型V4除了之前版本包含的5个特征维度外,还加入了TalkingData自己的三个广告类别维度的特征,虽然广告类别特征覆盖率只占20%,但也有很大的影响关于最终标签召回率的提升,模型由Xgboost替换为DNN,设置最大训练轮数(Epoch)为40,并设置早停参数。考虑到神经网络可以基于大数据工作,我们将训练的样本量增加了一倍,保证了神经网络的学习。DNN的结构如下:<左右滑动查看完整代码>pythonGenderNet_VLen((embeddings_appKey):Embedding(xxx,64,padding_idx=0)(embeddings_packageName):Embedding(xxx,32,padding_idx=0)(embeddings_model):Embedding(xxx,32,padding_idx=0)(embeddings_app):嵌入(xxx,512,padding_idx=0)(embeddings_deviceName):嵌入(xxx,32,padding_idx=0)(embeddings_adt1):嵌入(xxx,16,padding_idx=0)(embeddings_adt2):嵌入(xxx,16,padding_idx=0)(embeddings_adt3):嵌入(xxx,16,padding_idx=0)(fc):顺序((0):线性(in_features=720,out_features=64,bias=True)(1):BatchNorm1d(64,eps=1e-05,momentum=0.1,affine=True,track_running_stats=True)(2):ReLU()(3):Dropout(p=0.6)(4):线性(in_features=64,out_features=32,bias=True)(5):BatchNorm1d(32,eps=1e-05,momentum=0.1,affine=True,track_running_stats=True)(6):ReLU()(7):Dropout(p=0.6)(8):线性(in_features=32,out_features=16,bias=True)(9):BatchNorm1d(16,eps=1e-05,momentum=0.1,affine=True,track_running_stats=True)(10):ReLU()(11):Dropout(p=0.6)(12):林ear(in_features=16,out_features=2,bias=True)))优势及改进效果:与之前的版本相比,AUC仅提升了1.5%,但最终性别标签制作中的召回率提升了13%。考虑到数据量和已有的标签量,这个提升还是不错的。由此可见,在验证版本迭代的效果时,不能仅仅从模型的AUC这个单一指标来衡量,因为它影响到版本迭代。性能提升测量不够准确。我们应该验证最终的、真实的度量提升——在性别标签预测中,以所需精度召回的样本数量。但是,我们仍然可以在版本优化时使用AUC等模型相关指标来快速验证控制变量的实验效果。毕竟,这些指标很容易计算。模型探索的一个小建议是从原始日志中抽取字段聚合成信息,这需要ETL的步骤很多,也涉及到很多优化方法。这部分由专门的ETL团队负责,这里不做过多介绍。建模团队可以直接使用按时间聚合的字段进行建模任务。但是,花费在ETL和特征生成上的时间也占据了模型优化和迭代的大部分时间。下面总结了两个优化方面的陷阱和解决经验,希望能给大家一些参考。1.性别标签预测,大部分输入特征都是Array类型的,比如最近收集的设备应用信息。对于这种类型的字段,在训练模型之前,我们一般会调用CountVectorizer将Array转化为Vector,然后作为模型的输入,但是CountVectorizer这一步非常耗时,导致我们无法快速进行实验在版本迭代期间。为了解决这个问题,我们可以提前完成这一步转换,然后将生成的Vector列存储起来,这样在每次实验中,CountVectorizer消耗的时间就可以节省下来。在实际生产中,由于很多标签的生产会用到同一个字段,所以提前将Array转成Vector存储,后续任务可以直接调用Vector列,节省大量时间。2.虽然第一种可以节省很多时间,但Spark更多的还是用于生产。其实在模型的前期探索中,我们也可以先用Spark生成一个训练集——因为真实样本通常不多,生成的训练集往往不是很大,那么我们可以用单机用于快速实验。在单机上,我们可以更方便地使用Python画图更直观地理解数据,更快地进行特征筛选,更快地验证想法。在我们对数据和模型有了深刻的理解之后,我们可以快速地将实验得出的结论应用到生产中。作者简介:张小燕,TalkingData数据科学家,目前负责企业级用户画像平台建设和高效营销投放算法研发。长期关注互联网广告、用户画像、欺诈检测等领域。
