DeepCrossing
1. 動(dòng)機(jī)
這個(gè)模型就是一個(gè)真正的把深度學(xué)習(xí)架構(gòu)應(yīng)用于推薦系統(tǒng)中的模型了, 2016年由微軟提出, 完整的解決了特征工程、稀疏向量稠密化, 多層神經(jīng)網(wǎng)絡(luò)進(jìn)行優(yōu)化目標(biāo)擬合等一系列深度學(xué)習(xí)再推薦系統(tǒng)的應(yīng)用問(wèn)題。 這個(gè)模型涉及到的技術(shù)比較基礎(chǔ),在傳統(tǒng)神經(jīng)網(wǎng)絡(luò)的基礎(chǔ)上加入了embedding,殘差連接等思想,且結(jié)構(gòu)比較簡(jiǎn)單,對(duì)初學(xué)者復(fù)現(xiàn)和學(xué)習(xí)都比較友好。
DeepCrossing模型應(yīng)用場(chǎng)景是微軟搜索引擎Bing中的搜索廣告推薦, 用戶在輸入搜索詞之后, 搜索引擎除了返回相關(guān)結(jié)果, 還返回與搜索詞相關(guān)的廣告,Deep Crossing的優(yōu)化目標(biāo)就是預(yù)測(cè)對(duì)于某一廣告, 用戶是否會(huì)點(diǎn)擊,依然是點(diǎn)擊率預(yù)測(cè)的一個(gè)問(wèn)題。
這種場(chǎng)景下,我們的輸入一般會(huì)有類別型特征,比如廣告id,和數(shù)值型特征,比如廣告預(yù)算,兩種情況。 對(duì)于類別型特征,我們需要進(jìn)行one-hot編碼處理,而數(shù)值型特征 一般需要進(jìn)行歸一化處理,這樣算是把數(shù)據(jù)進(jìn)行了一個(gè)簡(jiǎn)單清洗。 DeepCrossing模型就是利用這些特征向量進(jìn)行CRT預(yù)估,那么它的結(jié)構(gòu)長(zhǎng)啥樣, 又是怎么做CTR預(yù)估的呢? 這又是DeepCrossing的核心內(nèi)容。
以微軟搜索數(shù)據(jù)為例,微軟使用的特征如下表所示,這些特征可以分為三類:一類是可以被處理成one-hot或者multi-hot向量的類別型特征,包括用戶搜索詞(query)、廣告關(guān)鍵詞(keyword)、廣告標(biāo)題(title)、落地頁(yè)(landing page)、匹配類型(match type);一類是數(shù)值型特征,微軟稱其為計(jì)數(shù)型特征,包括點(diǎn)擊率、預(yù)估點(diǎn)擊率(click prediction);一類是需要進(jìn)一步處理的特征,包括廣告計(jì)劃(campaign)、曝光計(jì)劃(impression)、點(diǎn)擊樣例(click)等。

2. 模型結(jié)構(gòu)及原理
為了完成端到端的訓(xùn)練, DeepCrossing模型要在內(nèi)部網(wǎng)絡(luò)結(jié)構(gòu)中解決如下問(wèn)題:
- 離散類特征編碼后過(guò)于稀疏, 不利于直接輸入神經(jīng)網(wǎng)絡(luò)訓(xùn)練, 需要解決稀疏特征向量稠密化的問(wèn)題
- 如何解決特征自動(dòng)交叉組合的問(wèn)題
- 如何在輸出層中達(dá)成問(wèn)題設(shè)定的優(yōu)化目標(biāo)
DeepCrossing分別設(shè)置了不同神經(jīng)網(wǎng)絡(luò)層解決上述問(wèn)題。模型結(jié)構(gòu)如下

下面分別介紹一下各層的作用:
2.1 Embedding Layer
將稀疏的類別型特征轉(zhuǎn)成稠密的Embedding向量,Embedding的維度會(huì)遠(yuǎn)小于原始的稀疏特征向量。 Embedding是NLP里面常用的一種技術(shù),這里的Feature #1表示的類別特征(one-hot編碼后的稀疏特征向量), Feature #2是數(shù)值型特征,不用embedding, 直接到了Stacking Layer。 關(guān)于Embedding Layer的實(shí)現(xiàn), 往往一個(gè)全連接層即可,Tensorflow中有實(shí)現(xiàn)好的層可以直接用。 和NLP里面的embedding技術(shù)異曲同工, 比如Word2Vec, 語(yǔ)言模型等。
2.2 Stacking Layer
這個(gè)層是把不同的Embedding特征和數(shù)值型特征拼接在一起,形成新的包含全部特征的特征向量,該層通常也稱為連接層, 具體的實(shí)現(xiàn)如下,先將所有的數(shù)值特征拼接起來(lái),然后將所有的Embedding拼接起來(lái),最后將數(shù)值特征和Embedding特征拼接起來(lái)作為DNN的輸入,這里TF是通過(guò)Concatnate層進(jìn)行拼接。
#將所有的dense特征拼接到一起
dense_dnn_list = list(dense_input_dict.values())
dense_dnn_inputs = Concatenate(axis=1)(dense_dnn_list) # B x n (n表示數(shù)值特征的數(shù)量)
# 因?yàn)樾枰獙⑵渑cdense特征拼接到一起所以需要Flatten,不進(jìn)行Flatten的Embedding層輸出的維度為:Bx1xdim
sparse_dnn_list = concat_embedding_list(dnn_feature_columns, sparse_input_dict, embedding_layer_dict, flatten=True)
sparse_dnn_inputs = Concatenate(axis=1)(sparse_dnn_list) # B x m*dim (n表示類別特征的數(shù)量,dim表示embedding的維度)
# 將dense特征和Sparse特征拼接到一起
dnn_inputs = Concatenate(axis=1)([dense_dnn_inputs, sparse_dnn_inputs]) # B x (n + m*dim)
2.3 Multiple Residual Units Layer
該層的主要結(jié)構(gòu)是MLP, 但DeepCrossing采用了殘差網(wǎng)絡(luò)進(jìn)行的連接。通過(guò)多層殘差網(wǎng)絡(luò)對(duì)特征向量各個(gè)維度充分的交叉組合, 使得模型能夠抓取更多的非線性特征和組合特征信息, 增加模型的表達(dá)能力。殘差網(wǎng)絡(luò)結(jié)構(gòu)如下圖所示:

Deep Crossing模型使用稍微修改過(guò)的殘差單元,它不使用卷積內(nèi)核,改為了兩層神經(jīng)網(wǎng)絡(luò)。我們可以看到,殘差單元是通過(guò)兩層ReLU變換再將原輸入特征相加回來(lái)實(shí)現(xiàn)的。具體代碼實(shí)現(xiàn)如下:
# DNN殘差塊的定義
class ResidualBlock(Layer):
def __init__(self, units): # units表示的是DNN隱藏層神經(jīng)元數(shù)量
super(ResidualBlock, self).__init__()
self.units = units
def build(self, input_shape):
out_dim = input_shape[-1]
self.dnn1 = Dense(self.units, activation='relu')
self.dnn2 = Dense(out_dim, activation='relu') # 保證輸入的維度和輸出的維度一致才能進(jìn)行殘差連接
def call(self, inputs):
x = inputs
x = self.dnn1(x)
x = self.dnn2(x)
x = Activation('relu')(x + inputs) # 殘差操作
return x
2.4 Scoring Layer
這個(gè)作為輸出層,為了擬合優(yōu)化目標(biāo)存在。 對(duì)于CTR預(yù)估二分類問(wèn)題, Scoring往往采用邏輯回歸,而對(duì)于圖像分類等多分類問(wèn)題,Scoring層往往采用softmax模型。模型通過(guò)疊加多個(gè)殘差塊加深網(wǎng)絡(luò)的深度,最后將結(jié)果轉(zhuǎn)換成一個(gè)概率值輸出。
# block_nums表示DNN殘差塊的數(shù)量
def get_dnn_logits(dnn_inputs, block_nums=3):
dnn_out = dnn_inputs
for i in range(block_nums):
dnn_out = ResidualBlock(64)(dnn_out)
# 將dnn的輸出轉(zhuǎn)化成logits
dnn_logits = Dense(1, activation='sigmoid')(dnn_out)
return dnn_logits
3. 總結(jié)
這就是DeepCrossing的結(jié)構(gòu)了,比較清晰和簡(jiǎn)單,沒(méi)有引入特殊的模型結(jié)構(gòu),只是常規(guī)的Embedding+多層神經(jīng)網(wǎng)絡(luò)。但這個(gè)網(wǎng)絡(luò)模型的出現(xiàn),有革命意義。DeepCrossing模型中沒(méi)有任何人工特征工程的參與,只需要簡(jiǎn)單的特征處理,原始特征經(jīng)Embedding Layer輸入神經(jīng)網(wǎng)絡(luò)層,自主交叉和學(xué)習(xí)。 相比于FM,F(xiàn)FM只具備二階特征交叉能力的模型,DeepCrossing可以通過(guò)調(diào)整神經(jīng)網(wǎng)絡(luò)的深度進(jìn)行特征之間的“深度交叉”,這也是Deep Crossing名稱的由來(lái)。
如果是用于點(diǎn)擊率預(yù)估模型的損失函數(shù)就是對(duì)數(shù)損失函數(shù):
其中表示真實(shí)的標(biāo)簽(點(diǎn)擊或未點(diǎn)擊),
表示Scoring Layer輸出的結(jié)果。但是在實(shí)際應(yīng)用中,根據(jù)不同的需求可以靈活替換為其他目標(biāo)函數(shù)。
4. 代碼實(shí)現(xiàn)
從模型的代碼結(jié)構(gòu)上來(lái)看,DeepCrossing的模型輸入主要由數(shù)值特征和類別特征組成,并將經(jīng)過(guò)Embedding之后的類別特征及類別特征拼接在一起,詳細(xì)的拼接代碼如Staking Layer所示,下面是構(gòu)建模型的核心代碼,詳細(xì)代碼參考github。
def DeepCrossing(dnn_feature_columns):
# 構(gòu)建輸入層,即所有特征對(duì)應(yīng)的Input()層,這里使用字典的形式返回,方便后續(xù)構(gòu)建模型
dense_input_dict, sparse_input_dict = build_input_layers(dnn_feature_columns)
# 構(gòu)建模型的輸入層,模型的輸入層不能是字典的形式,應(yīng)該將字典的形式轉(zhuǎn)換成列表的形式
# 注意:這里實(shí)際的輸入與Input()層的對(duì)應(yīng),是通過(guò)模型輸入時(shí)候的字典數(shù)據(jù)的key與對(duì)應(yīng)name的Input層
input_layers = list(dense_input_dict.values()) + list(sparse_input_dict.values())
# 構(gòu)建維度為k的embedding層,這里使用字典的形式返回,方便后面搭建模型
embedding_layer_dict = build_embedding_layers(dnn_feature_columns, sparse_input_dict, is_linear=False)
#將所有的dense特征拼接到一起
dense_dnn_list = list(dense_input_dict.values())
dense_dnn_inputs = Concatenate(axis=1)(dense_dnn_list) # B x n (n表示數(shù)值特征的數(shù)量)
# 因?yàn)樾枰獙⑵渑cdense特征拼接到一起所以需要Flatten,不進(jìn)行Flatten的Embedding層輸出的維度為:Bx1xdim
sparse_dnn_list = concat_embedding_list(dnn_feature_columns, sparse_input_dict, embedding_layer_dict, flatten=True)
sparse_dnn_inputs = Concatenate(axis=1)(sparse_dnn_list) # B x m*dim (n表示類別特征的數(shù)量,dim表示embedding的維度)
# 將dense特征和Sparse特征拼接到一起
dnn_inputs = Concatenate(axis=1)([dense_dnn_inputs, sparse_dnn_inputs]) # B x (n + m*dim)
# 輸入到dnn中,需要提前定義需要幾個(gè)殘差塊
output_layer = get_dnn_logits(dnn_inputs, block_nums=3)
model = Model(input_layers, output_layer)
return model
為了方便大家的閱讀,我們這里還給大家畫(huà)了一個(gè)整體的模型架構(gòu)圖,幫助大家更好的了解每一塊以及前向傳播。

下面是一個(gè)通過(guò)keras畫(huà)的模型結(jié)構(gòu)圖,為了更好的顯示,數(shù)值特征和類別特征都只是選擇了一小部分,畫(huà)圖的代碼也在github中。

從歷史的角度看,Deep Crossing模型的出現(xiàn)是有革命意義的。Deep Crossing模型中沒(méi)有任何人工特征工程的參與,原始特征經(jīng)Embedding后輸入神經(jīng)網(wǎng)絡(luò)層,將全部特征交叉的任務(wù)交給模型。Deep Crossing模型可以通過(guò)調(diào)整神經(jīng)網(wǎng)絡(luò)的深度進(jìn)行特征之間的“深度交叉”,這也是Deep Crossing名稱的由來(lái)。