1. 前言
上文介紹了TensorFlow官方版的Attention API,本文講解兩個點擊預估模型中用到的Attention方法。
FiBiNet 模型中的 SENET Attention 方法;
DIN 模型中的 Activation Unit Attention 方法;
2. SENET Attention 方式
FiBiNET全稱 FiBiNET: Combining Feature Importance and Bilinear feature Interaction for Click-Through Rate Prediction,是新浪微博提出的一種基於深度學習的廣告推薦/點擊率預測算法。可以認爲FiBiNET是在Wide & Deep模型的基礎上對它的Wide部分進行了一些創新的改進,或者直接視爲FNN的一個變體。主要的創新點在於:
在傳統的Embedding Stage加入了一個SENET層對Embedding特徵升級爲新的一種Embedding,得到與特徵重要性(Feature Importance)相關的信息;
不單獨使用傳統的Inner Product或Hadamard Product方法,而是選擇了結合二者的一種新的Bilinear Interaction方法來獲得特徵之間的聯繫;
模型的整體架構圖如圖1所示:
圖1 FiBiNET模型結構圖
從圖1中可以看到,相比於我們熟悉的基於深度學習的CTR預估模型,主要增加了SENET Layer和創新升級了Bilinear-Interaction Layer兩個結構。該章節只講解SENET Layer部分。
2.1 SENET 理論介紹
SENET全稱Squeeze-and-Excitation Network,在CV中用的比較多,可以對特徵間的依賴關係進行一定的提取。SENET一共分爲三個部分:Squeeze、Excitation 和 Re-Weight,按照順序執行後從原始的Embedding特徵向量 得到加權後的 。如圖2所示。
圖2 SENET模型結構圖
(1)Squeeze
這一步主要是將每個特徵組的Embedding向量進行彙總統計,文中使用均值池化(也可以使用最大池化,但文中表示平均池化效果要好於最大池化)對Embedding向量 進行壓縮爲 ,其中 表示第 個特徵的全局信息, 是標量。 的具體計算方式如下:
(2)Excitation
這一步基於特徵組的壓縮統計量 來學習特徵組的重要性權重,文章使用兩層的神經網絡來學習。第一層爲一個維度縮減層,第二層爲維度提升層。公式表示爲:
其中:
是一個向量,形式上同 ;
爲激活函數;
(3)Re-Weight
最後一步是把 和 按照類似於Hadamard product的方法對其中的 個元素進行element-wise的相乘得到SENET的最終產出 ,如果把 視爲一個權重向量,那麼這一步也可以被叫做加權或rescale。新的Embedding 向量通過如下的方式計算得到:
2.2 代碼實踐
定義senet函數,此函數未使用激活函數。
def senet(inputs):
Z = tf.reduce_mean(inputs, axis=-1, )
w1 = np.array([[1., 1.], [2., 2.], [1., 1.]])
dot1 = tf.tensordot(Z, w1, axes=(-1, 0))
w2 = np.array([[1., 1., 2], [2., 2., 6]])
dot2 = tf.tensordot(dot1, w2, axes=(-1, 0))
return dot2
測試:
inputs = np.array([[[1., 1., 1., 1.], [2., 2., 2., 2.], [1., 1., 1., 2.]], \
[[1., 1., 1., 1.], [2., 2., 2., 2.], [1., 2., 1., 2.]]])
print("inputs_shape: {} \n".format(inputs.shape))
senet_result = senet(inputs)
sess = tf.InteractiveSession()
senet_result_sess = sess.run(senet_result)
print("senet_result_sess: \n", senet_result_sess)
"""
結果:
inputs_shape: (2, 3, 4)
senet_result_sess:
[[18.75 18.75 50. ]
[19.5 19.5 52. ]]
"""
3. Activation Unit Attention
DIN,Deep Interest Network for Click-Through Rate Prediction,深度興趣網絡。論文的關鍵在於對用戶歷史行爲的embedding向量的改進。模型結構如下:
圖3 DIN模型結構圖
改進的關鍵在於DIN提出了一個局部激活單元,用來產生用戶歷史行爲特徵的權重,從而根據候選商品進行自適應調整不同歷史行爲特徵對最終結果的影響程度。傳統方法中不管候選商品是什麼,經過Sum Pooling後得到的用戶歷史行爲特徵都是一樣的。但是從現實角度來看,對於不同的候選商品,用戶的不同歷史行爲商品的影響程度是不一樣的,而傳統深度模型並沒有體現出這一點。而DIN中的Local Activation Unit則會根據歷史行爲商品和候選商品算出一個權重作爲該歷史行爲商品對點擊率的影響程度,此時經過 Sum Pooling後得到的用戶歷史行爲特徵則會根據不同候選商品發生變化,模型的多樣化表徵能力也就更強了。
3.1 Activation Unit結構
(1)輸入層:
user 產生行爲(例如點擊、或購買)的 item embedding,作爲key;
候選 item embedding,作爲query;
(2)Out Product層:
計算矩陣之間的 element-wise 乘法;
(3)Concat層:
將 query, key, query-key, query*key (Out Product層,element-wise 乘法) 的結果進行拼接;
(4)Dense層:
全連接層,並以PRelu或Dice作爲激活函數;
(5)Linear層(輸出層):
全連接層,輸出單元爲1,即得到(query, key)相應的權重值;
3.2 Activation Unit 理論
其中:
是用戶 歷史行爲Item的embedding,長度爲 ;
是候選Item的embedding;
是前向網絡,矩陣以 進行計算,輸出結果爲權重值;
3.3 Activation Unit 代碼講解
(1)定義query, keys
query = tf.convert_to_tensor(np.asarray([[[1., 1., 1., 3.]]]), dtype=tf.double)
key_list = tf.convert_to_tensor(np.asarray([[[1., 1., 2., 4.],
[4., 1., 1., 3.],
[1., 1., 2., 1.]]]), dtype=tf.double)
queries = K.repeat_elements(query, key_list.get_shape()[1], 1)
print("queries: \n", queries)
print()
print("key_list: \n", key_list)
結果:
query爲1組向量,模擬1個候選Item的Embedding;
keys爲3組向量,模擬某用戶歷史購買的3個Item的Embedding;
(2)拼接 query, key, query-key, query*key
att_input = tf.concat(
[queries, key_list, queries-key_list, queries*key_list], axis=-1)
print("att_input: \n", att_input)
結果:
(3)定義第一層 Dense 的weights, bias,及運算
dense_16to4_weights = tf.convert_to_tensor([[1., 1., 1., 1],
[1., 1., 1., 1],
[1., 1., 1., 1],
[1., 1., 1., 1],
[1., 1., 1., 1],
[1., 1., 1., 1],
[1., 1., 2., 1],
[1., 1., 1., 1],
[1., 1., 1., 1],
[1., 1., 1., 1],
[1., 1., 1., 1],
[1., 1., 2., 1],
[1., 1., 1., 1],
[1., 1., 1., 1],
[1., 1., 1., 1],
[1., 1., 2., 1]], dtype=tf.double)
dense_4_bias = tf.convert_to_tensor([1., 2., 3., 10.], dtype=tf.double)
dense_16to4 = tf.nn.bias_add(
tf.tensordot(att_input, dense_16to4_weights, axes=(-1, 0)),
dense_4_bias)
print("dense_16to4: \n\n", dense_16to4)
結果:
將原16維的向量壓縮到4維;
(4)定義最後一層 Dense 的weights, bias,及運算
dense_4to1_weights = tf.convert_to_tensor([1., 2., 3., 10.], dtype=tf.double)
dense_1_bias = tf.constant([0.2], dtype=tf.double)
dense_4to1 = tf.tensordot(dense_16to4, dense_4to1_weights, axes=(-1, 0)) + dense_1_bias
print("dense_4to1: \n\n", dense_4to1)
結果:
[601.2, 576.2, 439.2] 分別爲 “某用戶歷史購買的3個Item的Embedding” 的權重值;
還需要對最終的結果做 Softmax 歸一化處理,原文中:That is, normalization with softmax on the output of a(·) is abandoned.
dense_4to1_softmax = tf.nn.softmax(dense_4to1)
attention_result = tf.matmul(dense_4to1_softmax, key_list)
print("dense_4to1_softmax: \n\n", dense_4to1_softmax)
print()
print("attention_result: \n\n", attention_result)
也就是說,某用戶最後的興趣Embeddign分佈如 attention_result 所示。
歡迎入羣交流學習。