PNN算法原理&实现
💦

PNN算法原理&实现

text
PNN算法地Python代码实现
Tags
机器学习
深度学习
推荐系统
Created
Aug 2, 2022 04:22 PM

原理

notion image
  • Embedding Layer:嵌入层,用于将input中每个Field特征映射为低维稠密特征,然后每个特征的embedding横向拼接,作为下一层的输入;
  • Product Layer:特征交互层,由z和p两部分组成,其中z为上层的输出结果,p为上层输出的特征交互结果,低维与高维特征的直接拼接;以Field为粒度进行特征之间的交叉,交叉方式分为:内积IPNN外积OPNN。输出的张量形状为[None, field, k],其中None表示batch_size大小,field为原始输入的特征个数,k为每个特征嵌入后对应稠密向量维度;
    • Inner Product:field个特征两两内积,每两个k维特征的内积可以得到一个一维向量,总共得到field*(field-1)/2个变量,拼接在一起即为特征交互的结果p,形状为[None, field*(field-1)/2]
    • Outer Product:和内积不同,每两个k维特征的外积为[k, k]的二维向量,所以引入等形状的权重矩阵W,与二维张量进行对应元素乘积,然后求和得到一维变量。
  • Hidden Layer:三层全连接,最后一层映射到一维接sigmoid得到概率输出

优缺点

优点:

  1. 显示的进行特征交互,提高模型的表达能力;
  1. 以field为粒度进行特征交互,保留了域的概念;
  1. 同时保留了低维和高维的特征;

缺点:

  1. 外积交互方式参数量较大,随着特征维度平方级增加;

代码实现

class InnerProductLayer(Layer): def __init__(self): super(InnerProductLayer, self).__init__() def call(self, inputs, **kwargs): field_num = inputs.shape[1] row, col = [], [] # 先将要相乘的emb找出来,存在两个矩阵中,然后进行点乘即可 for i in range(field_num - 1): for j in range(i + 1, field_num): row.append(i) col.append(j) # tf.gather根据indices的参数值获取切片 p = tf.transpose(tf.gather(tf.transpose(inputs, [1, 0, 2]), row), [1, 0, 2]) q = tf.transpose(tf.gather(tf.transpose(inputs, [1, 0, 2]), col), [1, 0, 2]) innerProduct = tf.reduce_sum(p * q, axis=-1) return innerProduct
class OuterProductLayer(Layer): def __init__(self): super(OuterProductLayer, self).__init__() def build(self, input_shape): self.field_num = input_shape[1] self.k = input_shape[2] self.pair_num = self.field_num * (self.field_num - 1) // 2 # 每个外积矩阵对应一个w矩阵,共有pair个 self.w = self.add_weight(name='W', shape=(self.k, self.pair_num, self.k), initializer=tf.random_normal_initializer(), regularizer=l2(1e-4), trainable=True) def call(self, inputs, **kwargs): row, col = [], [] for i in range(self.field_num - 1): for j in range(i + 1, self.field_num): row.append(i) col.append(j) p = tf.transpose(tf.gather(tf.transpose(inputs, [1, 0, 2]), row), [1, 0, 2]) # [None, pair_num, k] q = tf.transpose(tf.gather(tf.transpose(inputs, [1, 0, 2]), col), [1, 0, 2]) # [None, pair_num, k] p = tf.expand_dims(p, axis=1) # [None, 1, pair_num, k] tmp = tf.multiply(p, self.w) # [None, 1, pair_num, k] * [k, pair, k] = [None, k, pair_num, k] tmp = tf.reduce_sum(tmp, axis=-1) # [None, k, pair_num] tmp = tf.multiply(tf.transpose(tmp, [0, 2, 1]), q) # [None, pair_num, k] outputProduct = tf.reduce_sum(tmp, axis=-1) # [None, pair_num] return outputProduct

参考