Fun-Rec项目中的PNN模型详解:特征交叉的神经网络实现
fun-rec 项目地址: https://gitcode.com/gh_mirrors/fu/fun-rec
引言
在推荐系统领域,特征交叉是提升模型性能的重要手段。从传统的FM、FFM模型到深度神经网络,研究者们不断探索更有效的特征交叉方式。本文将详细介绍PNN(Product-based Neural Network)模型,这是一种在神经网络中显式进行特征交叉的创新方法。
PNN模型背景与动机
特征交叉的重要性
在推荐系统中,特征之间的交互往往比单一特征更能反映用户偏好。例如,在电影推荐中,"年轻女性"和"浪漫电影"这两个特征的组合比单独使用这两个特征更具预测力。
现有方法的局限性
传统方法如FM只能进行二阶特征交叉,而深度神经网络虽然能进行高阶交叉,但其隐式的"add"操作方式不能充分挖掘类别特征的交叉效果。FNN模型虽然将DNN引入到FM之上,但仍存在特征交叉不充分的问题。
PNN的创新点
PNN模型通过设计专门的Product层,在将特征送入DNN之前先进行显式的交叉组合,主要有两种实现方式:
- IPNN(Inner Product-based Neural Network):使用内积进行特征交叉
- OPNN(Outer Product-based Neural Network):使用外积进行特征交叉
PNN模型架构详解
整体结构
PNN模型共分为五层结构:
- 输入层:接收原始特征
- Embedding层:将稀疏特征转换为稠密向量
- Product层:核心创新层,进行特征交叉
- 全连接层:学习高阶特征组合
- 输出层:产生最终预测结果
Product层设计
Product层是PNN的核心,由两部分组成:
-
线性部分(lz):保留原始特征信息
- 计算方式:$l_z^n = W_z^n \odot{z} = \sum_{i=1}^N \sum_{j=1}^M (W_z^n){i,j}z{i,j}$
-
非线性部分(lp):进行特征交叉
- 基础计算:$l_p^n = W_p^n \odot{p} = \sum_{i=1}^N \sum_{j=1}^N (W_p^n){i,j}p{i,j}$
IPNN实现细节
IPNN使用内积进行特征交叉:
- 原始计算:$g(f_i,f_j) = <f_i, f_j>$
- 优化方法:通过矩阵分解降低计算复杂度
- 将权重矩阵分解:$W_p^n=\theta^n \theta^{nT}$
- 最终计算简化为:$l_p = (||\sum_{i=1}^N \theta^1 f_i||^2, ..., ||\sum_{i=1}^N \theta^{D_1} f_i||^2)$
OPNN实现细节
OPNN使用外积进行特征交叉:
- 原始计算:$g(i,j) = f_i f_j^T$
- 优化方法:先在特征维度求和再计算外积
- $p=\sum_{i=1}^{N} \sum_{j=1}^{N} f_{i} f_{j}^{T}=f_{\Sigma}(f_{\Sigma})^{T}$
- 复杂度从$O(D_1N^2M^2)$降低到可接受范围
代码实现解析
模型构建流程
- 输入层构建:处理各类特征输入
- Embedding层:将类别特征嵌入到稠密空间
- Product层实现:
- 线性部分:通过矩阵乘法实现
- 非线性部分:区分IPNN和OPNN两种方式
- DNN部分:学习高阶特征组合
ProductLayer关键代码
class ProductLayer(Layer):
def __init__(self, units, use_inner=True, use_outer=False):
super(ProductLayer, self).__init__()
self.use_inner = use_inner
self.use_outer = use_outer
self.units = units
def build(self, input_shape):
# 初始化各种权重矩阵
self.linear_w = self.add_weight(...) # 线性部分权重
if self.use_inner:
self.inner_w = self.add_weight(...) # IPNN权重
if self.use_outer:
self.outer_w = self.add_weight(...) # OPNN权重
def call(self, inputs):
# 计算线性部分lz
concat_embed = Concatenate(axis=1)(inputs)
lz = tf.matmul(concat_embed_, self.linear_w)
# 计算内积部分(IPNN)
if self.use_inner:
for i in range(self.units):
delta = tf.multiply(concat_embed, tf.expand_dims(self.inner_w[i], axis=1))
lp_list.append(tf.reduce_sum(tf.square(delta), ...)
# 计算外积部分(OPNN)
if self.use_outer:
feat_sum = tf.reduce_sum(concat_embed, axis=1)
product = tf.matmul(f1, f2) # 外积计算
for i in range(self.units):
lpi = tf.multiply(product, self.outer_w[i])
lp_list.append(tf.reduce_sum(lpi, ...))
# 合并结果
product_out = Concatenate(axis=1)([lz, lp])
return product_out
模型特点与适用场景
优势
- 显式特征交叉:相比纯DNN能更好地捕捉特征交互
- 灵活组合:可以同时使用内积和外积方式
- 优化实现:通过数学变换降低计算复杂度
局限性
- OPNN的优化方法会带来精度损失
- 对于超大规模特征,计算复杂度仍然较高
适用场景
- 特征交叉明显的推荐场景
- 具备足够计算资源的场景
- 需要平衡模型复杂度和效果的情况
总结
PNN模型通过创新的Product层设计,在深度推荐模型中实现了显式的特征交叉,既保留了DNN学习高阶特征的能力,又通过内积或外积操作强化了特征交互。理解PNN的关键在于掌握Product层的数学原理及其优化方法,这有助于在实际应用中根据场景特点选择合适的实现方式。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考