1. 程式人生 > >推薦系統遇上深度學習(五)--Deep&Cross Network模型理論和實踐

推薦系統遇上深度學習(五)--Deep&Cross Network模型理論和實踐

歡迎關注天善智慧,我們是專注於商業智慧BI,人工智慧AI,大資料分析與挖掘領域的垂直社群,學習,問答、求職一站式搞定!

對商業智慧BI、大資料分析挖掘、機器學習,python,R等資料領域感興趣的同學加微信:tsaiedu,並註明訊息來源,邀請你進入資料愛好者交流群,資料愛好者們都在這兒。

本文來自天善智慧社群專欄作者[wenwen](https://ask.hellobi.com/people/%E7%9F%B3%E6%99%93%E6%96%87)

配套學習視訊教程: 手把手教你用Python 實踐深度學習

1、原理

Deep&Cross Network模型我們下面將簡稱DCN模型:

一個DCN模型從嵌入和堆積層開始,接著是一個交叉網路和一個與之平行的深度網路,之後是最後的組合層,它結合了兩個網路的輸出。完整的網路模型如圖:

4155986-be27f3dd3e3b9237.png

嵌入和堆疊層

我們考慮具有離散和連續特徵的輸入資料。在網路規模推薦系統中,如CTR預測,輸入主要是分類特徵,如“country=usa”。這些特徵通常是編碼為獨熱向量如“[ 0,1,0 ]”;然而,這往往導致過度的高維特徵空間大的詞彙。

為了減少維數,我們採用嵌入過程將這些離散特徵轉換成實數值的稠密向量(通常稱為嵌入向量):

4155986-0ce1a9e0a5d32ca1.png

然後,我們將嵌入向量與連續特徵向量疊加起來形成一個向量:

4155986-dfc32419b13cac1a.png

拼接起來的向量X0將作為我們Cross Network和Deep Network的輸入

Cross Network

交叉網路的核心思想是以有效的方式應用顯式特徵交叉。交叉網路由交叉層組成,每個層具有以下公式:

4155986-feacc6f25a1a985d.png

一個交叉層的視覺化如圖所示:

4155986-8de32b6dab68c108.png

可以看到,交叉網路的特殊結構使交叉特徵的程度隨著層深度的增加而增大。多項式的最高程度(就輸入X0而言)為L層交叉網路L + 1。如果用Lc表示交叉層數,d表示輸入維度。然後,引數的數量參與跨網路引數為:d * Lc * 2 (w和b)

交叉網路的少數引數限制了模型容量。為了捕捉高度非線性的相互作用,模型並行地引入了一個深度網路。

Deep Network

深度網路就是一個全連線的前饋神經網路,每個深度層具有如下公式:

4155986-aa82753a2af4b2cf.png

Combination Layer

連結層將兩個並行網路的輸出連線起來,經過一層全連結層得到輸出:

4155986-3b2e83dee702d12d.png

如果採用的是對數損失函式,那麼損失函式形式如下:

4155986-6a3cad235da5dd61.png

總結

DCN能夠有效地捕獲有限度的有效特徵的相互作用,學會高度非線性的相互作用,不需要人工特徵工程或遍歷搜尋,並具有較低的計算成本。

論文的主要貢獻包括:

1)提出了一種新的交叉網路,在每個層上明確地應用特徵交叉,有效地學習有界度的預測交叉特徵,並且不需要手工特徵工程或窮舉搜尋。

2)跨網路簡單而有效。通過設計,各層的多項式級數最高,並由層深度決定。網路由所有的交叉項組成,它們的係數各不相同。

3)跨網路記憶體高效,易於實現。

4)實驗結果表明,交叉網路(DCN)在LogLoss上與DNN相比少了近一個量級的引數量。

這個是從論文中翻譯過來的,哈哈。

2、實現解析

本文的程式碼根據之前DeepFM的程式碼進行改進,我們只介紹模型的實現部分,其他資料處理的細節大家可以參考我的github上的程式碼:

不去下載也沒關係,我在github上保留了幾千行的資料用作模型測試。

模型輸入

模型輸入

模型的輸入主要有下面幾個部分:

self.feat_index = tf.placeholder(tf.int32,

shape=[None,None],

name='feat_index')

self.feat_value = tf.placeholder(tf.float32,

shape=[None,None],

name='feat_value')

self.numeric_value = tf.placeholder(tf.float32,[None,None],name='num_value')

self.label = tf.placeholder(tf.float32,shape=[None,1],name='label')

self.dropout_keep_deep = tf.placeholder(tf.float32,shape=[None],name='dropout_deep_deep')

可以看到,這裡與DeepFM相比,一個明顯的變化是將離散特徵和連續特徵分開,連續特徵不在轉換成embedding進行輸入,所以我們的輸入共有五部分。

feat_index是離散特徵的一個序號,主要用於通過embedding_lookup選擇我們的embedding。feat_value是對應離散特徵的特徵值。numeric_value是我們的連續特徵值。label是實際值。還定義了兩個dropout來防止過擬合。

權重構建

權重主要包含四部分,embedding層的權重,cross network中的權重,deep network中的權重以及最後連結層的權重,我們使用一個字典來表示:

def_initialize_weights(self):

weights = dict()

#embeddings

weights['feature_embeddings'] = tf.Variable(

tf.random_normal([self.cate_feature_size,self.embedding_size],0.0,0.01),

name=

'feature_embeddings')

weights[

'feature_bias'] = tf.Variable(tf.random_normal([self.cate_feature_size,1],0.0,1.0),name='feature_bias')

#deep layers

num_layer =len(

self.deep_layers)

glorot = np.sqrt(

2.0/(self.total_size + self.deep_layers[0]))

weights[

'deep_layer_0'] = tf.Variable(

np.random.normal(loc=

0,scale=glorot,size=(self.total_size,self.deep_layers[0])),dtype=np.float32

)

weights[

'deep_bias_0'] = tf.Variable(

np.random.normal(loc=

0,scale=glorot,size=(1,self.deep_layers[0])),dtype=np.float32

)

foriinrange(1,num_layer):

glorot = np.sqrt(

2.0 / (self.deep_layers[i-1] + self.deep_layers[i]))

weights[

"deep_layer_%d"%i] = tf.Variable(

np.random.normal(loc=

0, scale=glorot,size=(self.deep_layers[i-1], self.deep_layers[i])),

dtype=np.float32)

# layers[i-1] * layers[i]

weights[

"deep_bias_%d"%i] = tf.Variable(

np.random.normal(loc=

0, scale=glorot,size=(1, self.deep_layers[i])),

dtype=np.float32)

#1* layer[i]

foriinrange(self.cross_layer_num):

weights[

"cross_layer_%d"%i] = tf.Variable(

np.random.normal(loc=

0, scale=glorot,size=(self.total_size,1)),

dtype=np.float32)

weights[

"cross_bias_%d"%i] = tf.Variable(

np.random.normal(loc=

0, scale=glorot,size=(self.total_size,1)),

dtype=np.float32)

#1* layer[i]

#finalconcatprojection layer

input_size =

self.total_size + self.deep_layers[-1]

glorot = np.sqrt(

2.0/(input_size + 1))

weights[

'concat_projection'] = tf.Variable(np.random.normal(loc=0,scale=glorot,size=(input_size,1)),dtype=np.float32)

weights[

'concat_bias'] = tf.Variable(tf.constant(0.01),dtype=np.float32)

returnweights

計算網路輸入

這一塊我們要計算兩個並行網路的輸入X0,我們需要將離散特徵轉換成embedding,同時拼接上連續特徵:

# model

self.embeddings = tf.nn.embedding_lookup(self.weights['feature_embeddings'],self.feat_index)# N * F * K

feat_value = tf.reshape(self.feat_value,shape=[-1,self.field_size,1])

self.embeddings = tf.multiply(self.embeddings,feat_value)

self.x0 = tf.concat([self.numeric_value,

tf.reshape(self.embeddings,shape=[-1,self.field_size *self.embedding_size])]

,axis=1)

Cross Network

根據論文中的計算公式,一步步計算得到cross network的輸出:

# cross_part

self._x0= tf.reshape(self.x0, (-1,self.total_size,1))

x_l =self._x0

forlinrange(self.cross_layer_num):

x_l = tf.tensordot(tf.matmul(self._x0, x_l, transpose_b=True),

self.weights["cross_layer_%d"% l],1) +self.weights["cross_bias_%d"% l] + x_l

self.cross_network_out= tf.reshape(x_l, (-1,self.total_size))

Deep Network

這一塊就是一個多層全連結神經網路:

self.y_deep = tf.nn.dropout(self.x0,self.dropout_keep_deep[0])

foriinrange(0,len(self.deep_layers)):

self.y_deep = tf.add(tf.matmul(self.y_deep,self.weights["deep_layer_%d"%i]),self.weights["deep_bias_%d"%i])

self.y_deep = self.deep_layers_activation(self.y_deep)

self.y_deep = tf.nn.dropout(self.y_deep,self.dropout_keep_deep[i+1])

Combination Layer

最後將兩個網路的輸出拼接起來,經過一層全連結得到最終的輸出:

# concat_part

concat_input = tf.concat([self.cross_network_out,self.y_deep], axis=1)

self.out = tf.add(tf.matmul(concat_input,self.weights['concat_projection']),self.weights['concat_bias'])

定義損失

這裡我們可以選擇logloss或者mse,並加上L2正則項:

# loss

ifself.loss_type=="logloss":

self.out= tf.nn.sigmoid(self.out)

self.loss= tf.losses.log_loss(self.label,self.out)

elifself.loss_type=="mse":

self.loss= tf.nn.l2_loss(tf.subtract(self.label,self.out))

# l2 regularization on weights

ifself.l2_reg>0:

self.loss+= tf.contrib.layers.l2_regularizer(

self.l2_reg)(self.weights["concat_projection"])

foriinrange(len(self.deep_layers)):

self.loss+= tf.contrib.layers.l2_regularizer(

self.l2_reg)(self.weights["deep_layer_%d"% i])

foriinrange(self.cross_layer_num):

self.loss+= tf.contrib.layers.l2_regularizer(

self.l2_reg)(self.weights["cross_layer_%d"% i])

剩下的程式碼就不介紹啦!

好啦,本文只是提供一個引子,有關DCN的知識大家可以更多的進行學習呦。

參考文章: