排序模塊:模型選擇與原理

日萌社

人工智能AI:Keras PyTorch MXNet TensorFlow PaddlePaddle 深度學習實戰(不定時更新)


 

3.1 模型選擇與原理

學習目標

  • 目標
    • 瞭解推薦系統CTR模型的發展歷史
    • 說明Wide&Deep模型的結構以及原理
    • 瞭解TF Wide&Deep模型的接口設置和使用
  • 應用

3.1.1 CTR模型發展歷史

3.1.1.1 傳統CTR模型發展歷史

2012左右之後,各大中小公司的主流CTR模型無一例外全都是LR模型。

  • 優點:
    • 1、數學基礎:結合CTR模型的問題來說,x就是輸入的特徵向量,h(x)就是我們最終希望得到的點擊率,所以採用LR作爲CTR 模型是符合“點擊”這一事件的物理意義的。
    • 2、可解釋性:LR模型具有極強的可解釋性,算法工程師們可以輕易的解釋哪些特徵比較重要,在CTR模型的預測有偏差的時候,也可以輕易找到哪些因素影響了最後的結果。
    • 3、工程化需要:在GPU尚未流行開來的2012年之前,LR模型也憑藉其易於並行化、模型簡單、訓練開銷小等特點佔據着工程領域的主流
  • 爲了解決特徵交叉的問題,演化出PLOY2,FM,FFM等模型;
  • Google從online learning的角度解決模型時效性的問題,提出了FTRL

1、POLY2-特徵交叉的引入

由於LR僅使用單一特徵,無法利用高維信息,只用單一特徵進行判斷,甚至會得出錯誤的結論。針對這個問題,當時的算法工程師們經常採用手動組合特徵,再通過各種分析手段篩選特徵的方法。採用 PLOY2模型進行特徵的“暴力”組合成爲了可行的選擇。

  1. 爲什麼要考慮特徵之間的關聯信息?

    大量的研究和實際數據分析結果表明:某些特徵之間的關聯信息(相關度)對事件結果的的發生會產生很大的影響。從實際業務線的廣告點擊數據分析來看,也正式了這樣的結論。

  2. 如何表達特徵之間的關聯?

    表示特徵之間的關聯,最直接的方法的是構造組合特徵。樣本中特徵之間的關聯信息在one-hot編碼和淺層學習模型(如LR、SVM)是做不到的。目前工業界主要有兩種手段得到組合特徵:

    1. 人工特徵工程(數據分析+人工構造);
    2. 通過模型做組合特徵的學習(深度學習方法、FM/FFM方法)

缺點:

  • 由於在處理互聯網數據時,經常採用one-hot的方法處理id類數據,致使特徵向量極度稀疏,POLY2進行無選擇的特徵交叉使原本就非常稀疏的特徵向量更加稀疏,使得大部分交叉特徵的權重缺乏有效的數據進行訓練,無法收斂。

假設一個廣告分類的問題,根據用戶和廣告位相關的特徵,預測用戶是否點擊了廣告。元數據如下:

Clicked? Country Day Ad_type
1 USA 26/11/15 Movie
0 China 1/7/14 Game
1 China 19/2/15 Game

“Clicked?”是label,Country、Day、Ad_type是特徵。由於三種特徵都是categorical類型的,需要經過獨熱編碼(One-Hot Encoding)轉換成數值型特徵。

Clicked? Country=USA Country=China Day=26/11/15 Day=1/7/14 Day=19/2/15 Ad_type=Movie Ad_type=Game
1 1 0 1 0 0 1 0
0 0 1 0 1 0 0 1
1 0 1 0 0 1 0 1
  • 權重參數的數量由n直接上升到n^2,極大增加了訓練複雜度。
  • 特徵交叉:
    • 例子:年齡:[1990,2000],[2000,2010]
    • 性別:male, female
    • 交叉特徵:male and [1990,2000],female and [1990,2000] ,male and [2000,2010], female and [2000, 2010]
  • 特徵交叉問題:如果是簡單的進行組合,會造成特徵列數量過多,大量特徵列。
    • 交叉項中的組合特徵參數總共有n*(n−1)/ 2
    • 在數據稀疏性普遍存在的實際應用場景中,交叉項參數的訓練是很困難的。

3.1.1.2 深度CTR發展歷史

2010年FM被提出,特徵交叉的概念被引入CTR模型;2012年MLR在阿里大規模應用,其結構十分接近三層神經網絡;2014年Facebook用GBDT處理特徵,揭開了特徵工程模型化的篇章。這些概念都將在深度學習CTR模型中繼續應用,持續發光。

另一邊,Alex Krizhevsky 2012年提出了引爆整個深度學習浪潮的AlexNet,深度學習的大幕正式拉開,其應用逐漸從圖像擴展到語音,再到NLP領域,推薦和廣告也必然會緊隨其後,投入深度學習的大潮之中。

2016年,隨着FNN,Deep&Wide,Deep crossing等一大批優秀的CTR模型框架的提出,深度學習CTR模型逐漸席捲了推薦和廣告領域,成爲新一代CTR模型當之無愧的主流。

我們在學習使用一些廣告、推薦領域流行的深度學習CTR模型的結構特點時候。應當選擇模型的標準儘量遵循下面三個原則:

  • 1.模型的在業界影響力較大的
  • 2.已經被Google,微軟,阿里等知名互聯網公司成功應用的
  • 3.工程導向的,而不是僅用實驗數據驗證或學術創新用的

3.1.2 Wide&deep論文核心剖析

3.1.2.1 WDL論文使用場景

  • Google Wide&Deep(2016年):Wide & Deep Learning for Recommender Systems

    Heng-Tze Cheng, Levent Koc, Jeremiah Harmsen, Tal Shaked, Tushar Chandra,

    Hrishi Aradhye, Glen Anderson, Greg Corrado, Wei Chai, Mustafa Ispir, Rohan Anil,

    Zakaria Haque, Lichan Hong, Vihan Jain, Xiaobing Liu, Hemal Shah

  • 場景:Google Play,一款移動APP應用商店,關注應用的推薦問題

    • 10億活躍用戶,超100萬應用APP
  • 模型訓練:

    • 超500 Billion=5000億的樣本數據
  • 效果顯示:3周的線上A/B實驗,實踐表明wide&deep框架顯著提高了移動app score 的app下載率

3.1.2.2 爲什麼選擇WDL

  • 1、WDL,更好的解決了Memorization和Generalization的平衡問題(翻譯成記憶與泛化(還有其他一些中文翻譯,但這是最合適的))

Memorization:

面對擁有大規模離散sparse特徵的CTR預估問題時,將特徵進行非線性轉換,然後再使用線性模型是在業界非常普遍的做法,最流行的即LR+特徵叉乘,Memorization 通過一系列人工的特徵叉乘(cross-product)來構造這些非線性特徵,捕捉sparse特徵之間的高階相關性。

Memorization的缺點是:1、需要更多的人工設計;2、可能出現過擬合;3、無法捕捉訓練數據中未曾出現過的特徵對

One limitation of cross-product trans- formations is that they do not generalize to query-item fea- ture pairs that have not appeared in the training data.

Generalization

Generalization會學習新的特徵組合,優點是更少的人工參與,對歷史上沒有出現的特徵組合有更好的泛化性。在數學理論中可以擬合任何高階形式的非線性函數, 滿足推薦系統對多樣性的要求。

  • 2、模型結構簡單,容易理解和調優
  • 3、已廣泛應用於各個領域,其中Google Play成功提高應用下載率,作爲其他論文改進的基礎

3.1.2.3 WDL論文貢獻

  • 論文的主要貢獻:
    • 通用於具有稀疏輸入的推薦系統的wide&deep框架,聯合訓練帶有嵌入的前饋神經網絡和帶有特徵變換的線形模型
    • The Wide & Deep learning framework for jointly train-ing feed-forward neural networks with embeddings and linear model with feature transformations for generic recommender systems with sparse inputs.
    • 在Google Play上實施的Wide&Deep推薦系統的實施和評估,Google Play是一個擁有超過10億活躍用戶和超過100萬個應用的移動應用商店
    • The implementation and evaluation of the Wide & Deep recommender system productionized on Google Play, a mobile app store with over one billion active users and over one million apps.
    • 開源了基於Tensorflow的高級API的實現
    • We have open-sourced our implementation along with a high-level API in TensorFlow

3.1.2.4 模型結構

模型包含Wide和Deep兩個部分(LR+DNN的結合)

1 Wide模型

Wide模型就是一個廣義線性模型,根據人工經驗、業務背景,將我們認爲有價值的、顯而易見的特徵及特徵組合,喂入Wide側。模型如下:

理解:For binary features, a cross-product transformation (e.g.“AND(gender=female, language=en)”) is 1 if and only if the constituent features (“gender=female” and “language=en”) are all 1, and 0 otherwise.

Wide部分只用了一組特徵叉乘,即被推薦的app ☓ 用戶下載的app。作者爲什麼這麼做呢?結合業務思考,在Google Play商店的app下載中,不斷有新的app推出,並且有很多“非常冷門、小衆”的app,而現在的智能手機user幾乎全部會安裝一系列必要的app。這時Wide可以發揮了它“記憶”的優勢,作者在這裏選擇了“記憶”user下載的app與被推薦的app之間的相關性,有點類似“裝個這個app後還可能會裝什麼”

Wide模型參考Memorization解決記憶問題。

2 Deep模型

  • Deep模型是一個前饋神經網絡(MLP):論文中爲三層:1024,512,256

l爲具體第幾層網絡,f爲激活函數,通常定位 (ReLUs)

  • 默認使用relu, 相比sigmoid能有有效防止梯度消失,並且計算量相比sigmoid小,有助於模型快速收斂。使用relu函數時應注意: 學習率應該設置小一些, 防止出現“死節點”, 簡單解釋一下"死節點", 即由於relu函數特點, 當輸入<0時, 輸出爲0, 不會產生任何激活作用

上述參數第l層輸如激活值, 偏置和模型權重值

  • 對於輸入的特徵處理:連續型:歸一化到CDF;離散型:特徵向量

離散型特徵:

The dimensionality of the embeddings are usually on the order of O(10) to O(100). The embedding vectors are initialized ran-domly and then the values are trained to minimize the final loss function during model training. These low-dimensional dense embedding vectors are then fed into the hidden layers of a neural network in the forward pass. Specifically, each hidden layer performs the following computation:

每個特徵進行隨機初始化embedding向量通常維度爲10~100,並在模型的訓練過程中逐漸修改該向量的值,即將向量作爲參數參與模型的訓練

連續型特徵:通過CDF被歸一化到 [0,1] 之間

原論文中,Categorical 特徵映射到32維embeddings,和原始Continuous特徵共1200維作爲NN輸入。

Deep模型參考Generation解決泛化問題

3 WDL的聯合訓練(Joint Training)

聯合訓練是指同時訓練Wide模型和Deep模型,並將兩個模型的結果的加權和作爲最終的預測結果。聯合訓練(Joint Training)和集成(Ensemble)是不同的,集成是每個模型單獨訓練,再將模型的結果匯合。相比聯合訓練,集成的每個獨立模型都得學得足夠好纔有利於隨後的匯合,因此每個模型的model size也相對更大。而聯合訓練的wide部分只需要作一小部分的特徵叉乘來彌補deep部分的不足,不需要 一個full-size 的wide 模型。

訓練的方法:

  • 損失函數: 二分類交叉熵損失也叫邏輯損失(logistic loss)

  • Wide模型:FTRL

  • Deep模型:AdaGrad

問題1:什麼是FTRL?爲什麼用FTRL?解決什麼問題?

衝突:最優化求解問題可能是我們在工作中遇到的最多的一類問題了:從已有的數據中􏰁煉出最適合的模型參數,從而對未知的數據進行預測。當我們面對高維高數據量的場景時,常見的批量處理的方式已經顯得力不從心,需要有在線優化算法的方法來解決此類問題。

  • FTRL(Follow the Regularized Leader)算法,它由Google的H. Brendan McMahan在2010年提出.一種在線優化算法

    • 爲什麼使用FTRL:使用正則化和梯度截斷的方式更容易獲得稀疏解
  • 稀疏解的重要性: 在特徵維度極高的大規模數據下, 稀疏解能有效降低模型複雜度, 防止模型過擬合; 並減小模型預測時佔用內存大小, 有助於提升QPS。

注意:在2011年論文的公式中並沒有正則化,但是在其2013年發表的FTRL工程化實現的論文中卻使用到了L2正則項。但是該項的引入並不影響FRTL 的稀疏性,L2正則項的引入僅僅相當於對最優化過程多了一 個約束,使得結果求解結果更加“平滑”。

TensorFlow API:tf.train.FtrlOptimizer

__init__(
    learning_rate,
    learning_rate_power=-0.5,
    initial_accumulator_value=0.1,
    l1_regularization_strength=0.0,
    l2_regularization_strength=0.0,
    use_locking=False,
    name='Ftrl',
    accum_name=None,
    linear_name=None,
      # 這與上面的L2不同之處在於上面的L2是穩定性懲罰,而這種L2收縮是一個幅度懲罰
    l2_shrinkage_regularization_strength=0.0
)

問題2:爲什麼使用AdaGrad?

深度學習模型中往往涉及大量的參數,不同參數的更新頻率往往有所區別。對於更新不頻繁的參數(典型例子:更新 word embedding 中的低頻詞),我們希望單次步長更大,多學習一些知識;對於更新頻繁的參數,我們則希望步長較小,使得學習到的參數更穩定,不至於被單個樣本影響太多。因此,Adagrad非常適合處理稀疏數據。

AdaGrad優化算法特點:'引入二階動量',根據自變量在每個維度的梯度值的大小來調整各個維度上的學習率,從而避免統一的學習率難以適應所有維度的問題。

Adagrad的主要好處之一是它不需要手動調整學習率。 大多數實現使用默認值0.01並保留它。主要弱點是它在分母中積累了平方梯度:由於每個附加項都是正數,因此累積總和在訓練期間不斷增長。 這反過來導致學習率縮小過快,訓練很快停止。Adagrad會累加之前所有的梯度平方,而RMSprop等算法僅僅是移動平滑處理計算對應的平均值,因此可緩解Adagrad算法學習率下降較快的問題。(AdaGrad算法、RMSProp算法以及AdaDelta算法一樣,目標函數自變量中每個元素都分別擁有自己的學習率)

4 評價標準

Google應用場景中度量的指標有兩個,分別針對在線的度量和離線的度量,在線時,通過A/B test,最終利用安裝率(Acquisition);離線則使用AUC作爲評價模型的指標

3.1.3 WDL 模型實現源碼講解

  • tf.estimator.DNNLinearCombinedClassifier():
    • model_dir: 保存模型參數、圖等。如checkponits文件路徑
    • linear_feature_columns: 線性特徵列, wide模型需要輸入的特徵
    • linear_optimizer: 針對線性wide模型損失函數選擇的優化器, 默認FTRL optimizer,可選(one of 'Adagrad', 'Adam', 'Ftrl', 'RMSProp', 'SGD')
    • dnn_feature_columns: DNN特徵列, deep模型需要輸入的特徵
    • dnn_optimizer: 針對DNN模型損失函數選擇的優化器, 這裏默認使用'Adagrad'
    • dnn_hidden_units: DNN每層神經元數列表
    • dnn_activation_fn: DNN激活函數,默認使用RELU
    • dnn_dropout:模型訓練中隱藏層單元的 drop_out 比例
    • n_classes: 分類數,默認是二分類,>2 則進行多分類
    • weight_column: 用於增強/降低某列的權重, 該列會被執行weight_column.normalizer_fn函數
    • label_vocabulary: 目標標籤類別,如果給參數提供類別字符串列表,如果沒提供默認[0, 1,…, n_classes -1]
    • warm_start_from: 模型熱啓動的checkpoint文件路徑, 設置後模型將使用checkpoint數據進行權重初始化
    • loss_reduction: loss減小的表示方式
    • batch_norm: 每個隱藏層是否使用批標準化
    • linear_sparse_combiner: 如果線性模型中某些類別特徵是"多元"的, 該多元特徵將在最後時計算其對應的單一權重,將對其所有元權重做改種方式的處理,以便縮小線性模型的規格
    • config: RunConfig對象, 其中將寫入運行時必要配置
    • input_layer_partitioner: 輸入層分區, 這是一項與分佈式計算有關的參數, 用於指明對輸入層進行切片的方式
class DNNLinearCombinedClassifier(estimator.Estimator):
  __doc__ = DNNLinearCombinedClassifierV2.__doc__.replace(
      'SUM_OVER_BATCH_SIZE', 'SUM')

  def __init__(self,
               model_dir=None,
               linear_feature_columns=None,
               linear_optimizer='Ftrl',
               dnn_feature_columns=None,
               dnn_optimizer='Adagrad',
               dnn_hidden_units=None,
               dnn_activation_fn=nn.relu,
               dnn_dropout=None,
               n_classes=2,
               weight_column=None,
               label_vocabulary=None,
               input_layer_partitioner=None,
               config=None,
               warm_start_from=None,
               loss_reduction=losses.Reduction.SUM,
               batch_norm=False,
               linear_sparse_combiner='sum'):
    self._feature_columns = _validate_feature_columns(
        linear_feature_columns=linear_feature_columns,
        dnn_feature_columns=dnn_feature_columns)

    head = head_lib._binary_logistic_or_multi_class_head(  # pylint: disable=protected-access
        n_classes, weight_column, label_vocabulary, loss_reduction)

    def _model_fn(features, labels, mode, config):
      """Call the _dnn_linear_combined_model_fn."""
      return _dnn_linear_combined_model_fn(
          features=features,
          labels=labels,
          mode=mode,
          head=head,
          linear_feature_columns=linear_feature_columns,
          linear_optimizer=linear_optimizer,
          dnn_feature_columns=dnn_feature_columns,
          dnn_optimizer=dnn_optimizer,
          dnn_hidden_units=dnn_hidden_units,
          dnn_activation_fn=dnn_activation_fn,
          dnn_dropout=dnn_dropout,
          input_layer_partitioner=input_layer_partitioner,
          config=config,
          batch_norm=batch_norm,
          linear_sparse_combiner=linear_sparse_combiner)

    super(DNNLinearCombinedClassifier, self).__init__(
        model_fn=_model_fn,
        model_dir=model_dir,
        config=config,
        warm_start_from=warm_start_from)

注:源碼地址 tf.estimator.DNNLinearCombinedClassifier()

3.1.3.1 模型源碼解釋

源碼使用tf.estimator.Estimator高階API類型實現的Wide&Deep模型。Estimator允許開發者自定義任意的模型結構、損失函數、優化方法以及如何對這個模型進行訓練、評估和導出等內容,同時屏蔽了與底層硬件設備、分佈式網絡數據傳輸等相關的細節。

  • 1、model_fn

由於TF的自實現DNNLinearCombinedClassifier是繼承estimator.Estimator類型,所以需要有model_fn函數提供給估計器。如head, labels,mode,features,optimizer等等

def _model_fn(features, labels, mode, config):
      """Call the _dnn_linear_combined_model_fn."""
      return _dnn_linear_combined_model_fn(
          features=features,
          labels=labels,
          mode=mode,
          head=head,
          linear_feature_columns=linear_feature_columns,
          linear_optimizer=linear_optimizer,
          dnn_feature_columns=dnn_feature_columns,
          dnn_optimizer=dnn_optimizer,
          dnn_hidden_units=dnn_hidden_units,
          dnn_activation_fn=dnn_activation_fn,
          dnn_dropout=dnn_dropout,
          input_layer_partitioner=input_layer_partitioner,
          config=config,
          batch_norm=batch_norm,
          linear_sparse_combiner=linear_sparse_combiner)
  • 3、head_lib._binary_logistic_or_multi_class_head
    • 創建二分類,或者多分類head
    • BinaryClassHead: 二分類 Head Class,定義損失類型
      • 包括使用sigmoid_cross_entropy_with_logits進行創建損失
    • MultiClassHead:多分類Head Class,
      • 包括sparse_softmax_cross_entropy創建損失

Head API對網絡最後一個隱藏層之後的部分進行了抽象,它的主要設計目標是簡化模型函數(model_fn)的編寫。Head知道如何計算損失(loss)、評估度量標準(metric)、預測結果(prediction)。爲了支持不同的模型,Head接受logits和labels作爲參數,並生成表示loss、metric和prediction的張量。有時爲了避免計算完整的logit張量,Head也接受最後一個隱藏的激活值作爲輸入。

Head的使用API如下,通常會提供Estimator中head參數,可以簡化model_fn的編寫

def model_fn(features, target, mode, params)
  predictions = tf.stack(tf.fully_connected, [50, 50, 10])
  loss = tf.losses.sparse_softmax_cross_entropy(target, predictions)
  train_op = tf.train.create_train_op(
    loss, tf.train.get_global_step(),
    params[’learning_rate’], params[’optimizer’])
  return EstimatorSpec(mode=mode,
                       predictions=predictions,
                       loss=loss,
                       train_op=train_op)


def model_fn(features, target, mode, params):
  last_layer = tf.stack(tf.fully_connected, [50, 50])
  head = tf.multi_class_head(n_classes=10)
  return head.create_estimator_spec(
    features, mode, last_layer,
    label=target,
    train_op_fn=lambda loss: my_optimizer.minimize(loss, tf.train.get_global_step())

3.1.4 Wide&Deep API使用

tf.estimator.DNNLinearCombinedClassifier會結合tf.feature_column與tf.data進行使用

# 指定列特徵
a = tf.feature_column.categorical_column_with_identity('a', num_buckets=25)

b = tf.feature_column.numeric_column('b')
c = tf.feature_column.numeric_column('c', shape=)
d = tf.feature_column.numeric_column('d')

# wide側
wide_columns = [a]

# deep側
deep_columns = [
    tf.feature_column.embedding_column(a, dimension=25),
    b,
    c,
    d
]

# 構造模型
estimator = tf.estimator.DNNLinearCombinedClassifier(model_dir="./tmp/ckpt/...",
                                                     linear_feature_columns=wide_columns,
                                                     dnn_feature_columns=deep_columns,
                                                     dnn_hidden_units=[256, 128, 64])
# 輸入訓練與驗證數據集進行模型訓練,評估
# input_fn返回dataset類型數據,指定了Batch,epoch大小
estimator.train(input_fn=input_fn)
res = estimator.evaluate(input_fn=input_fn)

3.1.5 小結

  • 推薦系統CTR模型發展歷史
  • Wide&Deep模型的結構以及原理
    • Wide結構
    • Deep結構
    • 損失函數、優化算法
      • Wide模型:FTRL
      • Deep模型:AdaGrad
  • TF Wide&Deep模型的接口設置和使用

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章