一、源數(shù)據(jù)
1.1、訓練數(shù)據(jù)
每行為一個樣本,元素分別為用戶id,用戶訪問的item id歷史列表,用戶本次訪問的item(正樣本為真實數(shù)據(jù),負樣本為隨機生成數(shù)據(jù)),正負樣本標識

1.2、測試數(shù)據(jù)
每行為一個樣本,元素分別為用戶id,用戶訪問的item id歷史列表,(用戶本次訪問正樣本item,系統(tǒng)隨機生成的負樣本item)

1.3、類別
每個元素為對應item的類別

1.4、其他參數(shù)
用戶數(shù)目:192403
item數(shù)目:63001
cate數(shù)目:801
二、模型
2.1、模型結(jié)構(gòu)

2.2、模型詳解
2.2.1、輸入?yún)?shù)
i:int,被推送的item
id列表,元素為曝光給每個用戶的item的id,長度為batch_size
y:float,正負樣本標識,0 or 1,長度為batch_size
hist_i:int,用戶點擊歷史數(shù)據(jù),元素為點擊的歷史item id,shape為batch_size*最大點擊歷史長度
sl:int,用戶實際點擊歷史長度,長度為batch_size
lr:訓練超參數(shù)
cate_iist:int,item類別轉(zhuǎn)換表,元素為item的cate id,長度為item總數(shù)
2.2.2、訓練參數(shù)
item_emb_w:item的embedding,shape為item數(shù)*64
item_b:item的bias,shape為item數(shù)
cate_emb_w:item類型的embedding,shape為cate數(shù)*64
2.2.3、整體流程
2.2.3.1、輸入數(shù)據(jù)轉(zhuǎn)換為嵌入向量形式
根據(jù)輸入數(shù)據(jù)的id(item id,cate id),從相應的訓練參數(shù)(嵌入表示查詢表)獲取匹配的嵌入表示。
c = tf.gather(cate_list, self.i)? #被推送的item的類型列表
i_emb = tf.concat(values = [
? ? ? ? tf.nn.embedding_lookup(item_emb_w, self.i),
? ? ? ? tf.nn.embedding_lookup(cate_emb_w, ic),
? ? ? ? ],axis=1)

i_b = tf.gather(item_b, self.i)

hc = tf.gather(cate_list, self.hist_i)
h_emb = tf.concat([
? ? ? ? tf.nn.embedding_lookup(item_emb_w, self.hist_i),
? ? ? ? tf.nn.embedding_lookup(cate_emb_w, hc),
? ? ? ? ], axis=2)

2.2.3.2、由i_emb,h_emb和sl根據(jù)注意力機制生成用戶的嵌入表示
用戶的嵌入表示的格式如下:

2.2.3.3、將u_emb與i_emb進行拼接,并作為MLP的輸入,最后輸出user對item的pctr,并與真實的點擊情況聯(lián)合生成損失函數(shù),用于優(yōu)化
din_i = tf.concat([u_emb, i_emb], axis=-1)
din_i = tf.layers.batch_normalization(inputs=din_i, name='b1')
d_layer_1_i = tf.layers.dense(din_i, 80, activation=None, name='f1')
d_layer_1_i = dice(d_layer_1_i, name='dice_1')
d_layer_2_i = tf.layers.dense(d_layer_1_i, 40, ctivation=None, name='f2')
d_layer_2_i = dice(d_layer_2_i, name='dice_2')
d_layer_3_i = tf.layers.dense(d_layer_2_i, 1, activation=None, name='f3')
self.logits = i_b + d_layer_3_i

self.loss = tf.reduce_mean(
? ? tf.nn.sigmoid_cross_entropy_with_logits(
? ? ? ? logits=self.logits,
? ? ? ? labels=self.y)
)
2.2.4、基于注意力機制的用戶嵌入表示
'''
? B指batch的大小,T指用戶歷史行為的最大長度,H指embedding的長度
? queries:? ? [B, H]
? keys:? ? ? ? [B, T, H]
? keys_length: [B]
'''
queries_hidden_units = queries.get_shape().as_list()[-1]? #最后一維大小queries = tf.tile(queries, [1, tf.shape(keys)[1]])
queries = tf.reshape(queries, [-1, tf.shape(keys)[1], queries_hidden_units])
din_all = tf.concat([queries, keys, queries-keys, queries*keys], axis=-1) #用戶點擊歷史與推薦的item拼接生成MLP的輸入
d_layer_1_all = tf.layers.dense(din_all, 80, activation=tf.nn.sigmoid, name='f1_att', reuse=tf.AUTO_REUSE)
d_layer_2_all = tf.layers.dense(d_layer_1_all, 40, activation=tf.nn.sigmoid, name='f2_att', reuse=tf.AUTO_REUSE)
d_layer_3_all = tf.layers.dense(d_layer_2_all, 1, activation=None, name='f3_att', reuse=tf.AUTO_REUSE)
d_layer_3_all = tf.reshape(d_layer_3_all, [-1, 1, tf.shape(keys)[1]])
outputs = d_layer_3_all
由queries(i_emb)拼貼成keys(h_emb)的形狀,并與keys(h_emb)進行差、點乘運算后生成MLP的輸入(din_all),經(jīng)MLP計算生成與各點擊歷史相關(guān)的權(quán)重系數(shù)。

# Mask
key_masks = tf.sequence_mask(keys_length, tf.shape(keys)[1])? # [B, T]
key_masks = tf.expand_dims(key_masks, 1) # [B, 1, T]
paddings = tf.ones_like(outputs) * (-2 ** 32 + 1)
outputs = tf.where(key_masks, outputs, paddings)? # [B, 1, T]
# Scale
outputs = outputs / (keys.get_shape().as_list()[-1] ** 0.5)
# Activation
outputs = tf.nn.softmax(outputs)? # [B, 1, T]
# Weighted sum
outputs = tf.matmul(outputs, keys)? # [B, 1, H]
hist_i =outpus hist_i = tf.layers.batch_normalization(inputs = hist_i)
hist_i = tf.reshape(hist_i, [-1, hidden_units], name='hist_bn')
hist_i = tf.layers.dense(hist_i, hidden_units, name='hist_fcn')
u_emb = hist_i
由于每個用戶的點擊歷史是不一樣長的,需要使用一個mask張量對無效的數(shù)據(jù)進行屏蔽。

在得到在推item與歷史點擊item的相關(guān)系數(shù)后,與點擊歷史的item_emb進行權(quán)重和后進行歸一化處理可以得到用戶的嵌入表示。

2.2.5、dice激活

def dice(_x,axis=-1,epsilon=0.0000001,name=''):
? ? alphas = tf.get_variable('alpha'+name,_x.get_shape()[-1],initializer = tf.constant_initializer(0.0), dtype=tf.float32)
? ? input_shape =list(_x.get_shape())? # [batch_size, hidden_unit_size]
? ? reduction_axes = list(range(len(input_shape)))? # [0, 1]
? ? del reduction_axes[axis] # [0]
? ? broadcast_shape = [1] * len(input_shape)? # [1, 1]
? ? broadcast_shape[axis] = input_shape[axis]? # [1, hidden_unit_size]
? ? # case: train mode (uses stats of the current batch)
? ? mean = tf.reduce_mean(_x, axis=reduction_axes)? # [1 * hidden_unit_size]
? ? brodcast_mean = tf.reshape(mean, broadcast_shape) # [1 * hidden_unit_size]
? ? std = tf.reduce_mean(tf.square(_x - brodcast_mean) + epsilon, axis=reduction_axes)? # [1 * hidden_unit_size]
? ? std = tf.sqrt(std)
? ? brodcast_std = tf.reshape(std, broadcast_shape) #[1 * hidden_unit_size]
? ? # x_normed = (_x - brodcast_mean) / (brodcast_std + epsilon)
? ? x_normed = tf.layers.batch_normalization(_x, center=False, scale=False, training=True)? # a simple way to use BN to calculate x_p
? ? x_p = tf.sigmoid(x_normed)
? ? return alphas * (1.0 - x_p) * _x + x_p * _x
不同alpha值得dice隨y值變化圖如下:

ReLU和Leaky ReLU的圖示如下:


三、參考文獻
1、Deep Interest Network for Click-Through Rate Prediction
2、github:https://github.com/zhougr1993/DeepInterestNetwork
3、http://www.itdecent.cn/p/73b6f5d00f46