基於用戶畫像進行廣告投放,是優化投放效果、實現精準營銷的基礎;而人口屬性中的性別、年齡等標籤,又是用戶畫像中的基礎信息。那該如何儘可能準確的爲數據打上這些標籤?python
這時候機器學習就派上用場了。本文將以性別標籤爲例,介紹人口屬性標籤預測的機器學習模型構建與優化。算法
一般狀況下,無監督學習不只很難學習到有用信息,並且對於學習到的效果較難評估。因此,若是能夠,咱們會盡量地把問題轉化成有監督學習。apache
對於性別標籤也是如此,咱們可使用可信的性別樣本數據,加上從TalkingData收集的原始數據中提取出來的有用信息,將性別標籤的生產任務轉化成有監督機器學習任務。更具體來講,男/女分別做爲1/0標籤(Label,也就是常說的Y值,爲了方便表達,咱們標記男/女分別爲1/0標籤),這樣性別標籤的任務就轉化成了二分類任務。網絡
性別標籤的生產流程圖以下:app
另外,面對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" }
缺點及優化方向:
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)
優勢及提高效果:
缺點及優化方向:
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)
優勢及提高效果:
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) ) )
優勢及提高效果:
模型探索小建議
從原始日誌當中抽取字段聚合成信息,須要通過不少步ETL,也會涉及不少優化方式,這部分有專門的ETL團隊負責,在這裏不作過多介紹。
模型團隊能夠直接使用按時間聚合以後的字段進行建模任務,儘管如此,ETL和特徵生成所花費的時間,也佔據了模型優化和迭代的大部分時間。
下面總結兩個優化方面的坑和解決經驗,但願能給你們一些參考。
1. 對於性別標籤預測,輸入的特徵大部分爲Array類型,好比近期採集到的設備應用信息。對於這種類型的字段,在訓練模型以前,咱們通常會調用CountVectorizer將Array轉成Vector,而後再做爲模型的輸入,可是CountVectorizer這一步很是耗時,這致使咱們在版本迭代時不能快速實驗。
針對該問題,咱們能夠事先完成這一步轉換,而後將生成的Vector列也存儲下來,這樣在每次實驗時,就能夠節省CountVectorizer消耗的時間。
在實際生產中,由於有不少標籤的生產都會用到一樣的字段,事先將Array轉成Vector存儲下來,後續不一樣任務便可直接調用Vector列,節省了不少時間。
2.雖然第一條可以節省很多時間,但Spark仍是更多用於生產。其實在模型前期的探索當中,咱們也能夠先用Spark生成訓練集——由於真實樣本一般不會不少,生成的訓練集每每不是很大,這時咱們就能夠用單機來進行快速實驗了。
在單機上,咱們可使用Python更方便的畫圖來更直觀的認識數據,更快的進行特徵篩選,更快的驗證想法。在對數據、對模型有了深刻的瞭解以後,咱們就能夠把實驗所得的結論快速應用到生產當中。
做者簡介:張小豔,TalkingData數據科學家,目前負責企業級用戶畫像平臺的搭建以及高效營銷投放算法的研發,長期關注互聯網廣告、用戶畫像、欺詐檢測等領域。