機器學習實戰 | 性別預測模型的構建與優化

基於用戶畫像進行廣告投放,是優化投放效果、實現精準營銷的基礎;而人口屬性中的性別、年齡等標籤,又是用戶畫像中的基礎信息。那該如何儘可能準確的爲數據打上這些標籤?python

這時候機器學習就派上用場了。本文將以性別標籤爲例,介紹人口屬性標籤預測的機器學習模型構建與優化。算法

性別標籤預測流程

一般狀況下,無監督學習不只很難學習到有用信息,並且對於學習到的效果較難評估。因此,若是能夠,咱們會盡量地把問題轉化成有監督學習。apache

對於性別標籤也是如此,咱們可使用可信的性別樣本數據,加上從TalkingData收集的原始數據中提取出來的有用信息,將性別標籤的生產任務轉化成有監督機器學習任務。更具體來講,男/女分別做爲1/0標籤(Label,也就是常說的Y值,爲了方便表達,咱們標記男/女分別爲1/0標籤),這樣性別標籤的任務就轉化成了二分類任務。網絡

性別標籤的生產流程圖以下:app

  • 簡單來講,輸入爲具備可信性別信息的樣本數據,以及從近期活躍的原始數據中提取出有用特徵;
  • 將二者join以後,獲得能夠直接用於建模的數據集;
  • 基於該數據集進行建模,學習出性別預測模型;
  • 再用該模型對所有樣本進行預測,從而獲得全部樣本的性別打分。至此,模型部分的工做基本完成;
  • 最後一步是肯定閾值,輸出男/女標籤。這裏咱們不依賴模型肯定閾值,而是藉助比較可信的第三方工具,保證在指望準確度(precision)下,召回儘量多的樣本。

另外,面對TalkingData十幾億的數據體量,在標籤生產的過程當中,爲了加速運算,除了必須用單機的狀況下,咱們都會優先採用Spark分佈式來加速運算。機器學習

特徵與模型方法的版本迭代

爲了優化模型的效果,咱們又對該性別標籤預測模型進行了屢次迭代。分佈式

01性別預測模型V1工具

模型最初使用的特徵包括4個維度:設備應用信息、嵌入SDK的應用包名、嵌入SDK的應用內自定義事件日誌以及設備機型信息。學習

模型採用Xgboost(版本爲0.5),基於每一個維度的特徵分別訓練模型,獲得4個子模型。每一個子模型會輸出基於該特徵維度的設備男/女傾向的打分,分值區間從0到1,分值高表明設備爲男性傾向,反之則爲女性傾向。模型代碼示例以下:大數據

<左右滑動查看完整代碼>

import com.talkingdata.utils.LibSVM
   import ml.dmlc.xgboost4j.scala.DMatrix
   import ml.dmlc.xgboost4j.scala.spark.XGBoost//version 0.5

   //train stage
   val trainRDD = LibSVM.loadLibSVMFile(sc, trainPath)// sc爲SparkContext
   val model = XGBoost.train(trainRDD, paramMap, numRound, nWorkers = workers)


   //predict stage
   val testSet = LibSVM.loadLibSVMFilePred(sc,testPath,-1,sc.defaultMinPartitions)
   val pred = testSet.map(_._2).mapPartitions{ iter =>
           model.value.predict(new DMatrix(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將以上4類特徵處理成稀疏向量(Vector),同時用ChiSqSelector進行特徵篩選。

模型採用LR(Logistic Regression),代碼示例以下:

<左右滑動查看完整代碼>

import org.apache.spark.ml.feature.VectorAssembler
   import org.apache.spark.ml.PipelineModel
   import org.apache.spark.ml.classification.LogisticRegression

   val transformedDF = spark.read.parquet("/traindata/path")//分詞、CountVectorizer、ChiSqSelector操做以後的特徵,爲vector列

   val featureCols = Array("packageName","appKey", "model", "deviceName")                  
   val vectorizer = new VectorAssembler().
                     setInputCols(featureCols).
                     setOutputCol("features")
   val lr = new LogisticRegression()
   val pipeline = new Pipeline().setStages(Array(vectorizer, lr))
   val model = pipeline.fit(transformedDF)

   //predict stage
   val transformedPredictionDF = spark.read.parquet("/predictData/path")//同train一致,爲分詞、CountVectorizer、ChiSqSelector處理以後的特徵,爲vector列
   val predictions = model.transform(transformedPredictionDF)

優勢及提高效果:

  • 採用單一的模型,可以用常見的模型評估指標(好比ROC-AUC, Precision-Recall 等)衡量模型,並在後續的版本迭代中做爲baseline,方便從模型角度進行版本提高的比較。

缺點及優化方向:

  • LR模型較簡單,學習能力有限,後續仍是替換成更強大的模型,好比Xgboost模型。

03性別預測模型V3

模型所使用的特徵,除了上個版本包括的4個維度:嵌入SDK的應用包名、嵌入SDK的應用AppKey、設備機型信息以及設備名稱,又增長了近期的聚合後的設備應用信息,處理方式與上個版本相似,再也不贅述。

模型從LR更換成Xgboost(版本爲0.82),代碼示例以下:

<左右滑動查看完整代碼>

import org.apache.spark.ml.feature.VectorAssembler
   import ml.dmlc.xgboost4j.scala.spark.XGBoostClassifier//version 爲0.82

   val transformedDF = spark.read.parquet("/trainData/path")//分詞、CountVectorizer操做以後的特徵,爲vector列

   val featureCols = Array("packageName","appKey", "model", "deviceName")                  
   val vectorizer = new VectorAssembler().
                     setInputCols(featureCols).
                     setOutputCol("features")
   val assembledDF = vectorizer.transform(transformedDF)

   //traiin stage
   //xgboost parameters setting
   val xgbParam = Map("eta" -> xxx,
      "max_depth" -> xxx,
      "objective" -> "binary:logistic",
      "num_round" -> xxx,
      "num_workers" -> xxx)
   val xgbClassifier = new XGBoostClassifier(xgbParam).
       setFeaturesCol("features").
       setLabelCol("labelColname")

   model = xgbClassifier.fit(assembledDF)

   //predict stage
   val transformedPredictionDF = spark.read.parquet("/predictData/path")//同train一致,爲分詞、CountVectorizer操做以後的特徵,爲vector列
   val assembledpredicDF = vectorizer.transform(transformedPredictionDF)
   val predictions = model.transform(assembledpredicDF)

優勢及提高效果:

  • 相比上個版本,AUC提高了6.5%,在最終的性別標籤生產中召回率提高了26%。考慮到TalkingData的十幾億的數據體量,這個數值仍是很可觀的。

04性別預測模型V4

除了上個版本包括的5個特徵維度,還添加了TalkingData自有的三個廣告類別維度的特徵,雖然廣告類別特徵覆蓋率僅佔20%,但對最終標籤的召回率的提高也有着很大的影響。

模型由Xgboost替換成DNN,設置最大訓練輪數(Epoch)爲40,同時設置了early stopping參數。考慮到神經網絡能工做是基於大數據的,所以咱們將用於訓練的樣本量擴充了一倍,保證神經網絡的學習。

DNN的結構以下:

<左右滑動查看完整代碼>

python
   GenderNet_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): Embedding(xxx, 512, padding_idx=0)
     (embeddings_deviceName): Embedding(xxx, 32, padding_idx=0)
     (embeddings_adt1): Embedding(xxx, 16, padding_idx=0)
     (embeddings_adt2): Embedding(xxx, 16, padding_idx=0)
     (embeddings_adt3): Embedding(xxx, 16, padding_idx=0)
     (fc): Sequential(
       (0): Linear(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): Linear(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): Linear(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): Linear(in_features=16, out_features=2, bias=True)
     )
   )

優勢及提高效果:

  • 與上個版本對比,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數據科學家,目前負責企業級用戶畫像平臺的搭建以及高效營銷投放算法的研發,長期關注互聯網廣告、用戶畫像、欺詐檢測等領域。

相關文章
相關標籤/搜索