全文共2621字,21张图,预计阅读时间15分钟。

原理

PNN,全称为Product-based Neural Network,认为在embedding输入到MLP之后学习的交叉特征表达并不充分,提出了一种product layer的思想,既基于乘法的运算来体现体征交叉的DNN网络结构,如下图:

按照论文的思路,我们也从上往下来看这个网络结构:

输出层

输出层很简单,将上一层的网络输出通过一个全链接层,经过sigmoid函数转换后映射到(0,1)的区间中,得到我们的点击率的预测值。

l2层

根据l1层的输出,经一个全链接层 ,并使用relu进行激活,得到我们l2的输出结果:

l1层

l1层的输出由如下的公式计算:

重点马上就要来了,我们可以看到在得到l1层输出时,我们输入了三部分,分别是lz,lp 和 b1,b1是我们的偏置项,这里可以先不管。lz和lp的计算就是PNN的精华所在了。我们慢慢道来

product Layer

product思想来源于,在ctr预估中,认为特征之间的关系更多是一种and“且”的关系,而非add"加”的关系。例如,性别为男且喜欢游戏的人群,比起性别男和喜欢游戏的人群,前者的组合比后者更能体现特征交叉的意义。

product layer可以分成两个部分,一部分是线性部分lz,一部分是非线性部分lp。二者的形式如下:

在这里,我们要使用到论文中所定义的一种运算方式,其实就是矩阵的点乘啦:

我们先继续介绍网络结构,有关Product Layer的更详细的介绍,我们在下一章中介绍。

Embedding Layer

Embedding Layer跟DeepFM中相同,将每一个field的特征转换成同样长度的向量,这里用f来表示。

损失函数

使用和逻辑回归同样的损失函数,如下:

Product Layer 详细介绍

前面提到了,product layer可以分成两个部分,一部分是线性部分lz,一部分是非线性部分lp。

看上面的公式,我们首先需要知道z和p,这都是由我们的embedding层得到的,其中z是线性信号向量,因此我们直接用embedding层得到:

论文中使用的等号加一个三角形,其实就是相等的意思,你可以认为z就是embedding层的复制。

对于p来说,这里需要一个公式进行映射:

不同的g的选择使得我们有了两种PNN的计算方法,一种叫做Inner PNN,简称IPNN,一种叫做Outer PNN,简称OPNN。

接下来,我们分别来具体介绍这两种形式的PNN模型,由于涉及到复杂度的分析,所以我们这里先定义Embedding的大小为M,field的大小为N,而lz和lp的长度为D1。

IPNN

IPNN的示意图如下:

IPNN中p的计算方式如下,即使用内积来代表pij:

所以,pij其实是一个数,得到一个pij的时间复杂度为M,p的大小为N*N,因此计算得到p的时间复杂度为N*N*M。而再由p得到lp的时间复杂度是N*N*D1。因此 对于IPNN来说,总的时间复杂度为N*N(D1+M)。文章对这一结构进行了优化,可以看到,我们的p是一个对称矩阵,因此我们的权重也可以是一个对称矩阵,对称矩阵就可以进行如下的分解:

因此:

因此:

从而得到:

可以看到,我们的权重只需要D1 * N就可以了,时间复杂度也变为了D1*M*N。

OPNN

OPNN的示意图如下:

OPNN中p的计算方式如下:

此时pij为M*M的矩阵,计算一个pij的时间复杂度为M*M,而p是N*N*M*M的矩阵,因此计算p的事件复杂度为N*N*M*M。从而计算lp的时间复杂度变为D1 * N*N*M*M。这个显然代价很高的。为了减少负责度,论文使用了叠加的思想,它重新定义了p矩阵:

这里计算p的时间复杂度变为了D1*M*(M+N)

代码实战

终于到了激动人心的代码实战环节了,一直想找一个实现比较好的代码,找来找去tensorflow没有什么合适的,倒是pytorch有一个不错的。没办法,只能自己来实现啦,因此本文的代码严格根据论文得到,有不对的的地方或者改进之处还望大家多多指正。

本文的github地址为:
https://github.com/princewen/tensorflow_practice/tree/master/Basic-PNN-Demo.

本文的代码根据之前DeepFM的代码进行改进,我们只介绍模型的实现部分,其他数据处理的细节大家可以参考我的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.label = tf.placeholder(tf.float32,shape=[None,1],name='label')self.dropout_keep_deep = tf.placeholder(tf.float32,shape=[None],name='dropout_deep_deep')

feat_index是特征的一个序号,主要用于通过embedding_lookup选择我们的embedding。feat_value是对应的特征值,如果是离散特征的话,就是1,如果不是离散特征的话,就保留原来的特征值。label是实际值。还定义了dropout来防止过拟合。

权重构建

权重由四部分构成,首先是embedding层的权重,然后是product层的权重,有线性信号权重,还有平方信号权重,根据IPNN和OPNN分别定义。最后是Deep Layer各层的权重以及输出层的权重。

对线性信号权重来说,大小为D1 * N * M
对平方信号权重来说,IPNN 的大小为D1 * N,OPNN为D1 * M * M。

def _initialize_weights(self):weights = dict()

#embeddingsweights['feature_embeddings'] = tf.Variable(tf.random_normal([self.feature_size,self.embedding_size],0.0,0.01),name='feature_embeddings')weights['feature_bias'] = tf.Variable(tf.random_normal([self.feature_size,1],0.0,1.0),name='feature_bias')#Product Layersif self.use_inner:weights['product-quadratic-inner'] = tf.Variable(tf.random_normal([self.deep_init_size,self.field_size],0.0,0.01))else:weights['product-quadratic-outer'] = tf.Variable(    tf.random_normal([self.deep_init_size, self.embedding_size,self.embedding_size], 0.0, 0.01))weights['product-linear'] = tf.Variable(tf.random_normal([self.deep_init_size,self.field_size,self.embedding_size],0.0,0.01))weights['product-bias'] = tf.Variable(tf.random_normal([self.deep_init_size,],0,0,1.0))#deep layersnum_layer = len(self.deep_layers)input_size = self.deep_init_sizeglorot = np.sqrt(2.0/(input_size + self.deep_layers[0]))

weights['layer_0'] = tf.Variable(np.random.normal(loc=0,scale=glorot,size=(input_size,self.deep_layers[0])),dtype=np.float32)weights['bias_0'] = tf.Variable(np.random.normal(loc=0,scale=glorot,size=(1,self.deep_layers[0])),dtype=np.float32)for i in range(1,num_layer):glorot = np.sqrt(2.0 / (self.deep_layers[i - 1] + self.deep_layers[i]))weights["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["bias_%d" % i] = tf.Variable(    np.random.normal(loc=0, scale=glorot, size=(1, self.deep_layers[i])),    dtype=np.float32)  # 1 * layer[i]

glorot = np.sqrt(2.0/(input_size + 1))weights['output'] = tf.Variable(np.random.normal(loc=0,scale=glorot,size=(self.deep_layers[-1],1)),dtype=np.float32)weights['output_bias'] = tf.Variable(tf.constant(0.01),dtype=np.float32)

return weights

Embedding Layer

这个部分很简单啦,是根据feat_index选择对应的weights['feature_embeddings']中的embedding值,然后再与对应的feat_value相乘就可以了:

# Embeddingsself.embeddings = tf.nn.embedding_lookup(self.weights['feature_embeddings'],self.feat_index) # N * F * Kfeat_value = tf.reshape(self.feat_value,shape=[-1,self.field_size,1])self.embeddings = tf.multiply(self.embeddings,feat_value) # N * F * K

Product Layer

根据之前的介绍,我们分别计算线性信号向量,二次信号向量,以及偏置项,三者相加同时经过relu激活得到深度网络部分的输入。

# Linear Singallinear_output = []for i in range(self.deep_init_size):linear_output.append(tf.reshape(tf.reduce_sum(tf.multiply(self.embeddings,self.weights['product-linear'][i]),axis=[1,2]),shape=(-1,1)))# N * 1

self.lz = tf.concat(linear_output,axis=1) # N * init_deep_size

# Quardatic Singalquadratic_output = []if self.use_inner:for i in range(self.deep_init_size):theta = tf.multiply(self.embeddings,tf.reshape(self.weights['product-quadratic-inner'][i],(1,-1,1))) # N * F * Kquadratic_output.append(tf.reshape(tf.norm(tf.reduce_sum(theta,axis=1),axis=1),shape=(-1,1))) # N * 1

else:embedding_sum = tf.reduce_sum(self.embeddings,axis=1)p = tf.matmul(tf.expand_dims(embedding_sum,2),tf.expand_dims(embedding_sum,1)) # N * K * Kfor i in range(self.deep_init_size):theta = tf.multiply(p,tf.expand_dims(self.weights['product-quadratic-outer'][i],0)) # N * K * Kquadratic_output.append(tf.reshape(tf.reduce_sum(theta,axis=[1,2]),shape=(-1,1))) # N * 1

self.lp = tf.concat(quadratic_output,axis=1) # N * init_deep_size

self.y_deep = tf.nn.relu(tf.add(tf.add(self.lz, self.lp), self.weights['product-bias']))self.y_deep = tf.nn.dropout(self.y_deep, self.dropout_keep_deep[0])

Deep Part

论文中的Deep Part实际上只有一层,不过我们可以随意设置,最后得到输出:

剩下的代码就不介绍啦!

好啦,本文只是提供一个引子,有关PNN的知识大家可以更多的进行学习呦。

参考文献

1 、https://zhuanlan.zhihu.com/p/33177517

2、https://cloud.tencent.com/developer/article/1104673?fromSource=waitui

3、https://arxiv.org/abs/1611.00144

原文链接:https://mp.weixin.qq.com/s/-WEGvWfsJGbWkQS0FbWZhQ

查阅更为简洁方便的分类文章以及最新的课程、产品信息,请移步至全新呈现的“LeadAI学院官网”:

www.leadai.org

请关注人工智能LeadAI公众号,查看更多专业文章

大家都在看

LSTM模型在问答系统中的应用

基于TensorFlow的神经网络解决用户流失概览问题

最全常见算法工程师面试题目整理(一)

最全常见算法工程师面试题目整理(二)

TensorFlow从1到2 | 第三章 深度学习革命的开端:卷积神经网络

装饰器 | Python高级编程

今天不如来复习下Python基础

推荐系统遇上深度学习(六)--PNN模型理论和实践相关推荐

  1. 推荐系统遇上深度学习(一)--FM模型理论和实践

    全文共2503字,15张图,预计阅读时间15分钟. FM背景 在计算广告和推荐系统中,CTR预估(click-through rate)是非常重要的一个环节,判断一个商品的是否进行推荐需要根据CTR预 ...

  2. 推荐系统遇上深度学习(八)--AFM模型理论和实践

    预计阅读时间10分钟. 引言 在CTR预估中,为了解决稀疏特征的问题,学者们提出了FM模型来建模特征之间的交互关系.但是FM模型只能表达特征之间两两组合之间的关系,无法建模两个特征之间深层次的关系或者 ...

  3. 推荐系统遇上深度学习(七)--NFM模型理论和实践

    预计阅读时间10分钟. 引言 在CTR预估中,为了解决稀疏特征的问题,学者们提出了FM模型来建模特征之间的交互关系.但是FM模型只能表达特征之间两两组合之间的关系,无法建模两个特征之间深层次的关系或者 ...

  4. 推荐系统遇上深度学习(二)--FFM模型理论和实践

    全文共1979字,6张图,预计阅读时间12分钟. FFM理论 在CTR预估中,经常会遇到one-hot类型的变量,one-hot类型变量会导致严重的数据特征稀疏的情况,为了解决这一问题,在上一讲中,我 ...

  5. 知识图谱论文阅读(八)【转】推荐系统遇上深度学习(二十六)--知识图谱与推荐系统结合之DKN模型原理及实现

    学习的博客: 推荐系统遇上深度学习(二十六)–知识图谱与推荐系统结合之DKN模型原理及实现 知识图谱特征学习的模型分类汇总 知识图谱嵌入(KGE):方法和应用的综述 论文: Knowledge Gra ...

  6. 推荐系统遇上深度学习,9篇阿里推荐论文汇总!

    作者 | 石晓文 转载自小小挖掘机(ID: wAIsjwj) 业界常用的推荐系统主要分为两个阶段,召回阶段和精排阶段,当然有时候在最后还会接一些打散或者探索的规则,这点咱们就不考虑了. 前面九篇文章中 ...

  7. 推荐系统遇上深度学习(三十九)-推荐系统中召回策略演进!

    推荐系统中的核心是从海量的商品库挑选合适商品最终展示给用户.由于商品库数量巨大,因此常见的推荐系统一般分为两个阶段,即召回阶段和排序阶段.召回阶段主要是从全量的商品库中得到用户可能感兴趣的一小部分候选 ...

  8. 推荐系统遇上深度学习(八十七)-[阿里]基于搜索的用户终身行为序列建模

    本文介绍的论文是<Search-based User Interest Modeling with Lifelong Sequential Behavior Data for Click-Thr ...

  9. 推荐系统遇上深度学习(九十二)-[腾讯]RecSys2020最佳长论文-多任务学习模型PLE

    今天介绍的是腾讯提出的一种新的多任务学习个性化推荐模型,该论文荣获了RecSys2020最佳长论文奖,一起来学习下! 1.背景 多任务学习通过在一个模型中同时学习多个不同的目标,如CTR和CVR,最近 ...

最新文章

  1. 送书 | 深入浅出,一起学习贝叶斯!
  2. 程序猿的日常——Java基础之equals与hashCode
  3. 关于WS_CLIPCHILDREN和WS_CLIPSIBLINGS的理解
  4. React路由组件传递参数
  5. linux kernel编译 undefined reference to `__ae
  6. 大屏设计-大数据综合展示可视化平台
  7. Oracle 12导出、导入数据
  8. Android ProGuard 还原堆栈
  9. android怎么设置iphone字体,冲浪阅读:安卓、苹果手机字体怎么改
  10. 异步电机matlab仿真 推荐 辅导书,异步电机模型Matlab仿真
  11. IIS7管理器设置网站首页
  12. 计算机组成原理笔记 (1):计算机组成概述
  13. 进阶级 - Git Hub 常用指南
  14. java能自学吗_java能自学吗?
  15. Vuex--mapState, mapGetters, mapActions, mapMutations--使用/教程/实例
  16. 心得体会标题大全_心得体会题目大全
  17. 29.递归三元表达式生成式匿名函数
  18. WPF DataGrid MVVM 绑定 SelectedCells
  19. [Excel]空白填充上一行数据
  20. 计算机组成原理 习题+知识点

热门文章

  1. 联想开机启动项按哪个_联想电脑开机按f12后,怎么设置默认启动项
  2. java读取复杂csv文件内容_java读取并导出多类型数据csv文件
  3. 【script】python使用cx_Oracle模块访问Oracle
  4. redis主从配置及无法连接处理
  5. 中职读计算机什么专业好,读职校选择什么专业好一些
  6. linux进程创建过程,Linux 进程创建过程的分析
  7. dotnet 找不到控制生成器_真的有自媒体原创文章生成器吗?
  8. list.php tid 1,DedeCMS栏目用目录名个性化伪静态
  9. android 获取4g信号_5G与1G、2G、3G、4G有何不同
  10. 部署knight项目