天天看點

基于深度學習的推薦(五):CTR預測經典模型PNN

文章目錄

    • 公衆号
    • 前言
    • 1.向量内積與向量外積
      • 1.1 向量内積Inner Product
      • 1.2 向量外積Outer Product
      • 1.3 向量點積Dot Product
      • 1.4 向量叉積Cross Product
    • 2. 模型結構
      • 2.1 IPNN
      • 2.2 OPNN
    • 3. 代碼實戰
      • 3.1 資料
      • 3.3 内積和外積的實作
    • 參考

公衆号

關注公衆号:推薦算法工程師,輸入"進群",加入交流群,和小夥伴們一起讨論機器學習,深度學習,推薦算法.

基于深度學習的推薦(五):CTR預測經典模型PNN

前言

PNN模型是上交大和UCL(University College London)在ICDM 2016上提出的。product思想來源于,在ctr預估中,認為特征之間的關系更多是一種and“且”的關系,而非add"加”的關系。例如,性别為男且喜歡遊戲的人群,比起性别男和喜歡遊戲的人群,前者的組合比後者更能展現特征交叉的意義[1]。這和FM的特征交叉思想類似。FM是進行one-hot編碼特征的交叉,而PNN則是将特征embedding後,加入向量積product層,進而進行特征間的交叉。那麼這個Product到底是如何實作的呢?論文的公式寫得有點複雜,本文從簡介紹,閱讀大概需要三分鐘。

論文連結:https://arxiv.org/abs/1807.00311 或 https://arxiv.org/pdf/1611.00144.pdf

1.向量内積與向量外積

1.1 向量内積Inner Product

Inner Product就是a,b兩個向量對應元素相乘的和[3],或者對a的轉置與b使用矩陣乘法:

基于深度學習的推薦(五):CTR預測經典模型PNN

1.2 向量外積Outer Product

Outer Product就是列向量與行向量相乘,結果是一個矩陣[3]:

基于深度學習的推薦(五):CTR預測經典模型PNN

1.3 向量點積Dot Product

點積有個别名,叫内積,又叫标量積,向量的積。隻不過,提起内積我們常想到的是1.1中的代數定義,而提起點選,常想到的是如下的幾何定義[3]:

基于深度學習的推薦(五):CTR預測經典模型PNN

1.4 向量叉積Cross Product

向量積,數學中又稱外積、叉積,實體中稱矢積、叉乘,是一種在向量空間中向量的二進制運算[2],外積的方向根據右手法則确定[3]:

基于深度學習的推薦(五):CTR預測經典模型PNN

2. 模型結構

基于深度學習的推薦(五):CTR預測經典模型PNN

首先是Input層,比如一個樣本有三個field,男/女,胖/瘦,高/矮,每個field的取值數分别有2,2,2個,那麼形如(0,1,0,1,0,1)的樣本就是一條輸入x。

Embedding layer就是将x進行映射,比如映射後的次元embed_size為k,假設每個field隻有一個非零值,那麼embed_x的次元就是field_size*k=3k維。

前兩層大家都很熟悉,重點看下product層。product層的左側z是線性部分,将embedding layer層獲得的特征串聯起來就是z。右邊p是特征交叉部分,根據交叉方式不同,product層分為兩種,第一種是采取向量内積的IPNN,第二種是采取向量外積的OPNN。

2.1 IPNN

回顧一下,向量内積,簡單說就是兩個向量相乘,傳回一個實數值的運算。假設field_size為n,那麼通過内積獲得的特征次元node_size=n(n-1)/2。是以,當使用Inner Product内積時,Product Layer的特征次元就是:field_sizeembed_size+field_size(field_size-1)/2*。

2.2 OPNN

回顧一下,向量外積Outer product,兩個向量相乘傳回一個矩陣。通過外積可以在Product Layer獲得的矩陣個數為:field_size*(field_size-1)/2。内積的結果可以直接串聯,拼接到特征上,矩陣該如何拼接呢?論文中使用了sum pooling的方法,對每個矩陣進行sum求和,得到實數值,然後拼接到z上:

基于深度學習的推薦(五):CTR預測經典模型PNN

是以,使用Outer Product外積時,Product Layer的特征次元仍然為:field_sizeembed_size+field_size(field_size-1)/2。

3. 代碼實戰

3.1 資料

論文中使用的是Criteo和iPinyou資料,太大了。本文使用一個小資料來測試,資料和之前fnn部落格中的相同。共有22個field,各field中屬性取值的可枚舉個數為:

樣本x則被劃分為:

基于深度學習的推薦(五):CTR預測經典模型PNN

由于是模拟CTR預估,是以标簽y是二分類的,實驗中y∈{0,1}.

3.3 内積和外積的實作

設embedding的向量次元為k,num_inputs為n,num_pairs為p。内積的實作如下:

row = [] # num_pairs
    col = [] # num_pairs
    for i in range(num_inputs-1):
        for j in range(i+1, num_inputs):
            row.append(i)
            col.append(j)
    
    p = tf.transpose(
            tf.gather(
            tf.transpose(xw3d, [1,0,2]), # [num_inputs, batch, k]
            row), # [num_pairs, batch, k]
            [1,0,2]) # [batch, num_pairs, k]
    
    q = tf.transpose(
            tf.gather(
            tf.transpose(xw3d, [1,0,2]), # [num_inputs, batch, k]
            col), # [num_pairs, batch, k]
            [1,0,2]) # [batch, num_pairs, k]
    
    p = tf.reshape(p, [-1, num_pairs, embed_size]) # [batch, num_pairs, k]
    q = tf.reshape(q, [-1, num_pairs, embed_size]) # [batch, num_pairs, k]
    
    ip = tf.reshape(tf.reduce_sum(p*q, [-1]), [-1, num_pairs])
    l = tf.concat([xw, ip], 1) # [num_inputs*k + num_pairs]
    
    for i in range(len(layer_sizes)):
        w = self.vars['w%d'%i]
        b = self.vars['b%d'%i]
        l = utils.activate(tf.matmul(l, w)+b, layer_acts[i])
        l = tf.nn.dropout(l, self.layer_keeps[i])
           

外積部分并沒有直接求field_size*(field_size-1)/2個矩陣,而是借助了一個中間矩陣W(shape為(k,p,k))來實作,這樣求得的sum pooling是帶有權重的,當w全為1時,才是公式中直接的sum pooling。

row = [] # num_pairs
    col = [] # num_pairs
    for i in range(num_inputs-1):
        for j in range(i+1, num_inputs):
            row.append(i)
            col.append(j)
    
    p = tf.transpose(
            tf.gather(
            tf.transpose(xw3d, [1,0,2]), # [num_inputs, batch, k]
            row), # [num_pairs, batch, k]
            [1,0,2]) # [batch, num_pairs, k]
    
    q = tf.transpose(
            tf.gather(
            tf.transpose(xw3d, [1,0,2]), # [num_inputs, batch, k]
            col), # [num_pairs, batch, k]
            [1,0,2]) # [batch, num_pairs, k]
    
    p = tf.reshape(p, [-1, num_pairs, embed_size]) # [b, p, k]
    q = tf.reshape(q, [-1, num_pairs, embed_size]) # [b, p, k]
    
    # k全為1時,就是嚴格按照公式
    p = tf.expand_dims(p, 1) # [batch, 1, p, k]
    k = self.vars['kernel'] # [k, p, k]
    ip = tf.multiply(k, p) # [batch, k, p, k]
    ip = tf.reduce_sum(ip, axis=-1) # [batch, k, p]
    ip = tf.transpose(ip, [0, 2, 1]) # [batch, p, k]
    ip = tf.multiply(ip, q) # [batch, p, k]
    ip = tf.reduce_sum(ip, axis=-1) # [batch, p]
    
    l = tf.concat([xw, ip], 1) # [num_inputs*k + num_pairs]
    
    for i in range(len(layer_sizes)):
        w = self.vars['w%d'%i]
        b = self.vars['b%d'%i]
        l = utils.activate(tf.matmul(l, w)+b, layer_acts[i])
        l = tf.nn.dropout(l, self.layer_keeps[i])
           

論文開源代碼:https://github.com/Atomu2014/product-nets

本文完整資料和代碼:https://github.com/wyl6/Recommender-Systems-Samples/tree/master/RecSys%20And%20Deep%20Learning/DNN/pnn

參考

[1] https://zhuanlan.zhihu.com/p/43693276

[2] https://baike.baidu.com/item/%E5%90%91%E9%87%8F%E5%A4%96%E7%A7%AF/11031485?fr=aladdin

[3] https://blog.csdn.net/minmindianzi/article/details/84820362

繼續閱讀