推薦排序模型2—— wide&Deep及python(DeepCTR)實現

wide&Deep模型重點指出了Memorization和Generalization的概念,這個是從人類的認知學習過程中演化來的。人類的大腦很複雜,它可以記憶(memorize)下每天發生的事情(麻雀可以飛,鴿子可以飛)然後泛化(generalize)這些知識到之前沒有看到過的東西(有翅膀的動物都能飛)。但是泛化的規則有時候不是特別的準,有時候會出錯(有翅膀的動物都能飛嗎)。那怎麼辦那,沒關係,記憶(memorization)可以修正泛化的規則(generalized rules),叫做特例(企鵝有翅膀,但是不能飛)。wide&Deep就是以這個思想出發形成的。


1,wide&deep的概念

Wide也是一種特殊的神經網絡,他的輸入直接和輸出相連。屬於廣義線性模型的範疇。Deep就是指Deep Neural Network,這個很好理解。

Wide Linear Model用於memorization;Deep Neural Network用於generalization。
在這裏插入圖片描述

Memorization:
之前大規模稀疏輸入的處理是:通過線性模型 + 特徵交叉。所帶來的Memorization以及記憶能力非常有效和可解釋。但是Generalization(泛化能力)需要更多的人工特徵工程。

Generalization:
相比之下,DNN幾乎不需要特徵工程。通過對低緯度的dense embedding進行組合可以學習到更深層次的隱藏特徵。但是,缺點是有點over-generalize(過度泛化)。推薦系統中表現爲:會給用戶推薦不是那麼相關的物品,尤其是user-item矩陣比較稀疏並且是high-rank(高秩矩陣)

兩者區別:
Memorization趨向於更加保守,推薦用戶之前有過行爲的items。相比之下,generalization更加趨向於提高推薦系統的多樣性(diversity)。

Wide & Deep:
Wide & Deep包括兩部分:線性模型 + DNN部分。結合上面兩者的優點,平衡memorization和generalization。

2, wide&deep結構

wide&deep模型是一種組合思想。如上圖所示,所謂Wide&Deep就是整個模型結構由wide部分和deep部分共同組成,圖中左邊是wide模型,一個邏輯迴歸,右邊是deep模型,認爲大概3層左右的DNN即可。

假設輸入的都是類別特徵,deep模型的輸入是用one-hot表示的Sparse Features。比如,對手機型號這個特徵而言,市面上可能存在數千個手機型號,但是每個用戶只對應一個手機型號,那麼這個稀疏向量有幾千個維度,只有一個位置爲1,其餘位置爲0。將每個特徵以embedding表示,embedding是一個低維稠密向量。將高維稀疏特徵,映射到對應的embedding上去,將所有特徵的embedding水平拼接起來,生成圖中的Dense Embeddings,最後通過若干層神經網絡得到輸出。將wide和deep部分組合起來,就是圖中間的wide&deep模型。

公式化表達如下所示:
在這裏插入圖片描述

其中,b表示偏置bias,a(l)a^{(l)} 表示deep模型最後一層輸出,x表示原始的輸入特徵,注意還有一個ϕ(x)\phi(x),這個是原始特徵的特徵交叉,如下所示:

ϕk(x)=i=1ddicki      cki{0,1}\phi_k(x) = \prod_{i=1}^dd_i^{c_{ki}} \ \ \ \ \ \ c_{ki}\in \{0,1\}

k表示第k個組合特徵。i表示輸入X的第i維特徵。CkiC_{ki}表示這個第i維度特徵是否要參與第k個組合特徵的構造。d表示輸入X的維度。那麼到底有哪些維度特徵要參與構造組合特徵呢?這個是你之前自己定好的(人工部分),在公式中沒有體現。公式整的複雜了些,但是仍然是一種特徵交叉。

所以,原文中提到wide&deep仍然需要做特徵工程實現特徵交叉,但是相比於原生的邏輯迴歸,只需要做少量的特徵交叉,大大減少了特徵工程的工作量,並且效果優於邏輯迴歸。原文對wide和deep模型的含義做了介紹,wide模型的特徵交叉部分主要負責記憶功能,記住樣本中出現過的特徵交叉。deep部分有泛化功能,學習特徵的embedding,泛化到那些未曾出現過的特徵交叉。

Deep部分使用的特徵:連續特徵,Embedding後的離散特徵,Item特徵 Wide部分使用的特徵:Cross Product
Transformation生成的組合特徵
但是,官方給出的示例代碼中,Wide部分還使用了離散特徵(沒有one-hot)。也有大佬說不用特徵交叉效果也很好,這個大家在實際項目中就以實驗爲準吧。

總結一下:
線性模型無法學習到訓練集中未出現的組合特徵;FM或DNN通過學習embedding vector雖然可以學習到訓練集中未出現的組合特徵,但是會過度泛化。Wide & Deep Model通過組合這兩部分,解決了這些問題。

3,python(deepCTR)實現

本篇文章編寫在DeepFM之後,要晚一些,詳細的實現過程及解釋可以去參考DeepFM,這裏不再重複解釋。
同樣的,這裏使用的代碼主要採用開源的DeepCTR,相應的API文檔可以在這裏閱讀。DeepCTR是一個易於使用、模塊化和可擴展的深度學習CTR模型庫,它內置了很多核心的組件從而便於我們自定義模型,它兼容tensorflow 1.4+和2.0+
在這裏插入圖片描述
模型實現如下:

import pandas as pd
from sklearn.preprocessing import LabelEncoder, MinMaxScaler
from sklearn.model_selection import train_test_split
from deepctr.models.wdl import WDL
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 = WDL(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.wdl.html
https://mp.weixin.qq.com/s?__biz=MzU0NDgwNzIwMQ==&mid=2247483689&idx=1&sn=c6e55677fe4ee1983e8f51fb61dffab5&chksm=fb77c167cc004871347a79fddb1c70d44f2b4bb54cbf09c3d1ecc5473ff8653802354d65bb8d&scene=21#wechat_redirect

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