基于深度学习的推荐(五):CTR预测经典模型PNN

释放双眼,带上耳机,听听看~!

文章目录

  • 公众号

    • 前言
    • 1.向量内积与向量外积
  • 1.1 向量内积Inner Product
    * 1.2 向量外积Outer Product
    * 1.3 向量点积Dot Product
    * 1.4 向量叉积Cross Product

      1. 模型结构
  • 2.1 IPNN
    * 2.2 OPNN

      1. 代码实战
  • 3.1 数据
    * 3.3 内积和外积的实现

    • 参考

公众号

前言

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.00311https://arxiv.org/pdf/1611.00144.pdf

1.向量内积与向量外积

1.1 向量内积Inner Product

Inner Product就是a,b两个向量对应元素相乘的和[3],或者对a的转置与b使用矩阵乘法:

1.2 向量外积Outer Product

Outer Product就是列向量与行向量相乘,结果是一个矩阵[3]:

1.3 向量点积Dot Product

点积有个别名,叫内积,又叫标量积,向量的积。只不过,提起内积我们常想到的是1.1中的代数定义,而提起点击,常想到的是如下的几何定义[3]:

1.4 向量叉积Cross Product

向量积,数学中又称外积、叉积,物理中称矢积、叉乘,是一种在向量空间中向量的二元运算[2],外积的方向根据右手法则确定[3]:

2. 模型结构

首先是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上:

因此,使用Outer Product外积时,Product Layer的特征维度仍然为:field_sizeembed_size+field_size(field_size-1)/2

3. 代码实战

3.1 数据

论文中使用的是Criteo和iPinyou数据,太大了。本文使用一个小数据来测试,数据和之前fnn博客中的相同。共有22个field,各field中属性取值的可枚举个数为:


1
2
3
1FIELD_SIZES = [1037, 151, 59, 1603, 4, 333, 77890, 1857, 9, 8, 4, 7, 22, 3, 92, 56, 4, 920, 38176, 240, 2697, 4]
2
3

样本x则被划分为:
由于是模拟CTR预估,所以标签y是二分类的,实验中y∈{0,1}.

3.3 内积和外积的实现

设embedding的向量维度为k,num_inputs为n,num_pairs为p。内积的实现如下:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
1    row = [] # num_pairs
2    col = [] # num_pairs
3    for i in range(num_inputs-1):
4        for j in range(i+1, num_inputs):
5            row.append(i)
6            col.append(j)
7    
8    p = tf.transpose(
9            tf.gather(
10            tf.transpose(xw3d, [1,0,2]), # [num_inputs, batch, k]
11            row), # [num_pairs, batch, k]
12            [1,0,2]) # [batch, num_pairs, k]
13    
14    q = tf.transpose(
15            tf.gather(
16            tf.transpose(xw3d, [1,0,2]), # [num_inputs, batch, k]
17            col), # [num_pairs, batch, k]
18            [1,0,2]) # [batch, num_pairs, k]
19    
20    p = tf.reshape(p, [-1, num_pairs, embed_size]) # [batch, num_pairs, k]
21    q = tf.reshape(q, [-1, num_pairs, embed_size]) # [batch, num_pairs, k]
22    
23    ip = tf.reshape(tf.reduce_sum(p*q, [-1]), [-1, num_pairs])
24    l = tf.concat([xw, ip], 1) # [num_inputs*k + num_pairs]
25    
26    for i in range(len(layer_sizes)):
27        w = self.vars['w%d'%i]
28        b = self.vars['b%d'%i]
29        l = utils.activate(tf.matmul(l, w)+b, layer_acts[i])
30        l = tf.nn.dropout(l, self.layer_keeps[i])
31
32

外积部分并没有直接求field_size*(field_size-1)/2个矩阵,而是借助了一个中间矩阵W(shape为(k,p,k))来实现,这样求得的sum pooling是带有权重的,当w全为1时,才是公式中直接的sum pooling。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
1    row = [] # num_pairs
2    col = [] # num_pairs
3    for i in range(num_inputs-1):
4        for j in range(i+1, num_inputs):
5            row.append(i)
6            col.append(j)
7    
8    p = tf.transpose(
9            tf.gather(
10            tf.transpose(xw3d, [1,0,2]), # [num_inputs, batch, k]
11            row), # [num_pairs, batch, k]
12            [1,0,2]) # [batch, num_pairs, k]
13    
14    q = tf.transpose(
15            tf.gather(
16            tf.transpose(xw3d, [1,0,2]), # [num_inputs, batch, k]
17            col), # [num_pairs, batch, k]
18            [1,0,2]) # [batch, num_pairs, k]
19    
20    p = tf.reshape(p, [-1, num_pairs, embed_size]) # [b, p, k]
21    q = tf.reshape(q, [-1, num_pairs, embed_size]) # [b, p, k]
22    
23    # k全为1时,就是严格按照公式
24    p = tf.expand_dims(p, 1) # [batch, 1, p, k]
25    k = self.vars['kernel'] # [k, p, k]
26    ip = tf.multiply(k, p) # [batch, k, p, k]
27    ip = tf.reduce_sum(ip, axis=-1) # [batch, k, p]
28    ip = tf.transpose(ip, [0, 2, 1]) # [batch, p, k]
29    ip = tf.multiply(ip, q) # [batch, p, k]
30    ip = tf.reduce_sum(ip, axis=-1) # [batch, p]
31    
32    l = tf.concat([xw, ip], 1) # [num_inputs*k + num_pairs]
33    
34    for i in range(len(layer_sizes)):
35        w = self.vars['w%d'%i]
36        b = self.vars['b%d'%i]
37        l = utils.activate(tf.matmul(l, w)+b, layer_acts[i])
38        l = tf.nn.dropout(l, self.layer_keeps[i])
39
40

论文开源代码:https://github.com/Atomu2014/product-nets
本文完整数据和代码:https://github.com/wyl6/Recommender-Systems-Samples/tree/master/RecSys%20And%20Deep%20Learning/DNN/pnn

给TA打赏
共{{data.count}}人
人已打赏
安全运维

基于spring boot和mongodb打造一套完整的权限架构(二)【MAVEN依赖以及相应配置】

2021-12-11 11:36:11

安全运维

Ubuntu上NFS的安装配置

2021-12-19 17:36:11

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索