推薦排序模型4—— Deep&Cross Network(DCN)及python(DeepCTR)實現

DCN全稱Deep & Cross Network,是谷歌和斯坦福大學在2017年提出的用於Ad Click Prediction的模型。DCN(Deep Cross Network)在學習特定階數組合特徵的時候效率非常高,而且同樣不需要特徵工程,引入的額外的複雜度也是微乎其微的。


1,DCN的基本結構和特點

  1. 使用cross network,在每一層都應用feature crossing。高效的學習了bounded degree組合特徵。不需要人工特徵工程。
  2. 網絡結構簡單且高效。多項式複雜度由layer depth決定。
  3. 相比於DNN,DCN的logloss更低,而且參數的數量將近少了一個數量級。

在這裏插入圖片描述
DCN架構圖如上圖所示:最開始是Embedding and stacking layer,然後是並行的Cross Network和Deep Network,最後是Combination Layer把Cross Network和Deep Network的結果組合得到Output。

接下來,我們對DCN網絡的各個部分分別總結分析。

2,Embedding and Stacking Layer

爲什麼要Embed?

這是CTR模型的常用套路了,稀疏數據變稠密,降維度。

具體的:在web-scale的推薦系統比如CTR預估中,輸入的大部分特徵都是類別型特徵,通常的處理辦法就是one-hot,但是one-hot之後輸入特徵維度非常高非常係數。

所以有了Embedding來大大的降低輸入的維度,就是把這些binary features轉換成dense vectors with real values。

Embedding操作其實就是用一個矩陣和one-hot之後的輸入相乘,也可以看成是一次查詢(lookup)。這個Embedding矩陣跟網絡中的其他參數是一樣的,是需要隨着網絡一起學習的。

爲什麼要Stack?

這也是常用套路了,數據有連續的,有稀疏的,稀疏的embedding,處理完了類別型特徵,還有連續型特徵沒有處理。所以我們把連續型特徵規範化之後,和嵌入向量stacking到一起,就得到了原始的輸入:

x0=[xembed,1T,...,xembed,kT,xdenseT]x_0=[x_{embed,1}^T,...,x_{embed,k}^T,x_{dense}^T]

3, Cross Network

Cross Network是整篇論文的核心。它被設計來高效的學習組合特徵,關鍵在於如何高效的進行feature crossing。形式化如下:

xl+1=x0xlTwl+bl+xl=f(xl,wl,bl)+xlx_{l+1} = x_0x_l^Tw_l + b_l + x_l = f(x_l,w_l,b_l)+x_l

xlx_lxl+1x_l+1 分別是第l層和第l+1l+1層cross layer的輸出,wlw_lblb_l是這兩層之間的連接參數。注意上式中所有的變量均是列向量,W也是列向量,並不是矩陣。每一層的輸出,都是上一層的輸出加上feature crossing f。而f就是在擬合該層輸出和上一層輸出的殘差。 針對one cross layer可視化如下:
在這裏插入圖片描述
Cross Network特殊的網絡結構使得cross feature的階數隨着layer depth的增加而增加。相對於輸入x0來說,一個l層的cross network的cross feature的階數爲l+1。

複雜度分析: 假設一共有Lc層cross layer,起始輸入x0的維度爲d。那麼整個cross network的參數個數爲:dLc2

因爲每一層的W和b都是d維度的。從上式可以發現,複雜度是輸入維度d的線性函數。所以相比於deep network,cross network引入的複雜度微不足道。這樣就保證了DCN的複雜度和DNN是一個級別的。論文中表示,Cross Network之所以能夠高效的學習組合特徵,就是因爲x0 * xT的秩爲1,使得我們不用計算並存儲整個的矩陣就可以得到所有的cross terms。

但是,正是因爲cross network的參數比較少導致它的表達能力受限,爲了能夠學習高度非線性的組合特徵,DCN並行的引入了Deep Network。

4, Combination Layer

Combination Layer把Cross Network和Deep Network的輸出拼接起來,然後經過一個加權求和後得到logits,然後經過sigmoid函數得到最終的預測概率。形式化如下:

p=σ([xL1T,hL2T]wlogits)p = \sigma([x_{L_1}^T,h_{L_2}^T]w_{logits})

p是最終的預測概率;XL1是d維的,表示Cross Network的最終輸出;hL2是m維的,表示Deep Network的最終輸出;Wlogits是Combination Layer的權重;最後經過sigmoid函數,得到最終預測概率。

另外,針對Cross Network和Deep Network,DCN是一起訓練的,這樣網絡可以知道另外一個網絡的存在。

FM是一個非常淺的結構,並且限制在表達二階組合特徵上,DeepCrossNetwork(DCN)把這種參數共享的思想從一層擴展到多層,並且可以學習高階的特徵組合。但是和FM的高階版本的變體不同,DCN的參數隨着輸入維度的增長是線性增長的。

5, pyhton(DeepCTR)實現

與前一篇DeepFM實現方式相同,使用的代碼主要採用開源的DeepCTR,相應的API文檔可以在這裏閱讀。DeepCTR是一個易於使用、模塊化和可擴展的深度學習CTR模型庫,它內置了很多核心的組件從而便於我們自定義模型,它兼容tensorflow 1.4+和2.0+
在這裏插入圖片描述
使用方法很簡單,生成指定格式的數據,得到linear_feature_columns與dnn_feature_columns,然後compile——fit——pre即可,感興趣的可以去看下我在DeepFM中的解釋

import pandas as pd
from sklearn.preprocessing import LabelEncoder, MinMaxScaler
from sklearn.model_selection import train_test_split
from deepctr.models.dcn import DCN
from deepctr.inputs import  SparseFeat, DenseFeat,get_feature_names

data = pd.read_csv('./criteo_sample.txt')

sparse_features = ['C' + str(i) for i in range(1, 27)]
dense_features = ['I'+str(i) for i in range(1, 14)]

data[sparse_features] = data[sparse_features].fillna('-1', )
data[dense_features] = data[dense_features].fillna(0,)
target = ['label']


for feat in sparse_features:
    lbe = LabelEncoder()
    data[feat] = lbe.fit_transform(data[feat])
    
mms = MinMaxScaler(feature_range=(0,1))
data[dense_features] = mms.fit_transform(data[dense_features])

sparse_feature_columns = [SparseFeat(feat, vocabulary_size=data[feat].nunique(),embedding_dim=4)
                           for i,feat in enumerate(sparse_features)]
# 或者hash,vocabulary_size通常要大一些,以避免hash衝突太多
# sparse_feature_columns = [SparseFeat(feat, vocabulary_size=1e6,embedding_dim=4,use_hash=True)
#                            for i,feat in enumerate(sparse_features)]#The dimension can be set according to data
dense_feature_columns = [DenseFeat(feat, 1)
                      for feat in dense_features]
                      
dnn_feature_columns = sparse_feature_columns + dense_feature_columns
linear_feature_columns = sparse_feature_columns + dense_feature_columns
feature_names = get_feature_names(linear_feature_columns + dnn_feature_columns)



train, test = train_test_split(data, test_size=0.2)

train_model_input = {name:train[name].values for name in feature_names}
test_model_input = {name:test[name].values for name in feature_names}

model = DCN(linear_feature_columns,dnn_feature_columns,task='binary')
model.compile("adam", "binary_crossentropy",
              metrics=['binary_crossentropy'], )

history = model.fit(train_model_input, train[target].values,
                    batch_size=256, epochs=10, verbose=2, validation_split=0.2, )
pred_ans = model.predict(test_model_input, batch_size=256)

參考文獻
https://deepctr-doc.readthedocs.io/en/latest/deepctr.models.dcn.html
https://mp.weixin.qq.com/s/lF_WLAn6JyQqf10076hsjA
https://www.cnblogs.com/wmx24/p/10341332.html

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