- 人工智能
- 深度学习
- 机器学习
【免费下载链接】DeepCTR
Easy-to-use,Modular and Extendible package of deep-learning based CTR models .
导读
本文以 DeepCTR 官方 API 文档中 deepctr.models.pnn 模块 为核心,系统讲解基于乘积的神经网络 PNN(Product-based Neural Network)在 DeepCTR 中的实现与使用。你将掌握 PNN 的完整构造参数与默认值、内积/外积两种特征交互层(IPNN / OPNN)的底层实现原理、四种产品层组合模式,以及基于 Keras 与 TensorFlow Estimator 两种接口的建模、训练、评估与模型持久化全流程。文末还会结合仓库测试用例说明如何快速验证模型可用性。
PNN 模型是什么:从论文到 DeepCTR 实现
PNN(Product-based Neural Network)由 Qu 等人提出("Product-based neural networks for user response prediction",ICDM 2016),其核心思想是在传统 DNN 之前显式建模特征域之间的乘积交互。与仅仅拼接 embedding 向量的 FNN、DeepFM 不同,PNN 在 embedding 层之上增加了一个Product Layer,对任意两两特征域计算内积(inner product)或外积(outer product),把高阶特征交互信号注入深度网络。
在 DeepCTR 中,PNN 由三部分协作完成:
- 模型入口函数 deepctr/models/pnn.py 中的
PNN(),负责组装整个网络; - 乘积交互层 deepctr/layers/interaction.py 中的
InnerProductLayer与OutterProductLayer; - DNN 与输出层 deepctr/layers/core.py 中的
DNN与PredictionLayer。
该模块在官方模型索引 Models.rst 中被列为 DeepCTR 的核心模型之一(IPNN 与 OPNN 两种变体可通过参数切换)。
PNN API 与完整参数详解
PNN是典型的 Keras 函数式 API 构造器,函数签名(见 deepctr/models/pnn.py)如下:
def PNN(dnn_feature_columns, dnn_hidden_units=(256, 128, 64), l2_reg_embedding=0.00001, l2_reg_dnn=0, seed=1024, dnn_dropout=0, dnn_activation='relu', use_inner=True, use_outter=False, kernel_type='mat', task='binary'): ... return model调用后返回一个tensorflow.keras.models.Model实例。下表逐参数说明其含义、默认值与取值范围:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
dnn_feature_columns | iterable | 无(必填) | 供深度部分使用的全部特征列(SparseFeat/DenseFeat等),由deepctr.feature_column构造 |
dnn_hidden_units | list | (256, 128, 64) | DNN 隐层神经元数量列表,[]表示去掉 DNN,只保留产品层输出 |
l2_reg_embedding | float | 0.00001 | 施加在 embedding 向量上的 L2 正则强度 |
l2_reg_dnn | float | 0 | 施加在 DNN 权重上的 L2 正则强度 |
seed | int | 1024 | 随机种子,用于权重初始化、Dropout 等随机过程复现 |
dnn_dropout | float | 0 | DNN 神经元丢弃概率,取值范围[0, 1) |
dnn_activation | str | 'relu' | DNN 激活函数,如'relu'、'tanh'、'sigmoid'等 |
use_inner | bool | True | 是否启用内积(Inner Product)交互 |
use_outter | bool | False | 是否启用外积(Outer Product)交互 |
kernel_type | str | 'mat' | 外积核矩阵类型,只能是'mat'、'vec'或'num' |
task | str | 'binary' | 'binary'使用二分类 logloss,'regression'使用回归损失 |
两个值得注意的实现细节:
非法参数会直接抛错。
PNN入口处对kernel_type做了白名单校验(deepctr/models/pnn.py),OutterProductLayer构造时同样校验(deepctr/layers/interaction.py),传入kernel_type之外的字符串会抛出ValueError("kernel_type must be mat,vec or num")。默认组合是 IPNN:
use_inner=True, use_outter=False,这也是论文中最常用的配置。
模型结构与数据流:源码级解析
PNN的完整数据流可以拆成六个步骤(对应 deepctr/models/pnn.py):
第 1 步:构建输入层。build_input_features(dnn_feature_columns)把特征列转换为 KerasInput字典,inputs_list = list(features.values())作为最终Model(inputs=..., outputs=...)的输入列表。
第 2 步:生成 embedding 与稠密值。input_from_feature_columns(features, dnn_feature_columns, l2_reg_embedding, seed)返回sparse_embedding_list(每个稀疏域一个(batch, 1, embedding_size)的 3D 张量列表)和dense_value_list。
第 3 步:计算乘积特征。两个分支并行:
inner_product = Flatten()(InnerProductLayer()(sparse_embedding_list)) outter_product = OutterProductLayer(kernel_type)(sparse_embedding_list)第 4 步:拼接线性信号。把所有域的 embedding 首尾相接并Reshape成一维向量:
linear_signal = Reshape( [sum(map(lambda x: int(x.shape[-1]), sparse_embedding_list))])(concat_func(sparse_embedding_list))第 5 步:按开关组合深度输入。use_inner/use_outter的四种组合决定deep_input的内容(详见下一节),随后combined_dnn_input([deep_input], dense_value_list)把产品层输出与稠密原始特征拼在一起喂给 DNN。
第 6 步:DNN + 输出层。DNN(dnn_hidden_units, dnn_activation, l2_reg_dnn, dnn_dropout, False, seed=seed)得到dnn_out,经Dense(1, use_bias=False)映射为 logit,最后由PredictionLayer(task)输出:
task='binary'时输出 sigmoid 概率;task='regression'时输出线性 logit。
内积与外积交互层:两个关键算子的实现原理
InnerProductLayer(IPNN 核心)
InnerProductLayer(deepctr/layers/interaction.py)接收 N 个(batch, 1, embedding_size)的 embedding 张量,对全部N*(N-1)/2个特征域两两组合:
row/col通过双重循环生成所有i < j的下标对;- 按行、列分组拼接后逐元素相乘得到
p * q; - 默认
reduce_sum=True,沿 embedding 维度求和,输出形状为(batch_size, N*(N-1)/2, 1); - 若
reduce_sum=False,则输出逐元素乘积(batch_size, N*(N-1)/2, embedding_size)。
在PNN中该层输出随后被Flatten()展开成一维向量,即 IPNN 论文中"内积池化"的结果。层构建时还会校验所有输入形状必须一致且为(None, 1, embedding_size)形态,否则抛ValueError。
OutterProductLayer(OPNN 核心)
OutterProductLayer(deepctr/layers/interaction.py)的输出形状固定为(batch_size, N*(N-1)/2),其实现参考了论文作者发布的 product-nets 开源代码。关键在于kernel 权重的三种参数化方式,这也是kernel_type参数的本源:
kernel_type | kernel 形状 | 计算方式 |
|---|---|---|
'mat' | (embed_size, num_pairs, embed_size) | 对每对特征用一个完整的k×k矩阵做双线性变换:p^T · W · q,最强大但参数量最大 |
'vec' | (num_pairs, embed_size) | 每对特征共用一个向量做加权求和:sum(p * q * k) |
'num' | (num_pairs, 1) | 每对特征仅用一个标量做加权:sum(p * q * k),参数量最小 |
'mat'分支在call中通过p扩维后与 kernel 逐元素相乘、两次归约与转置实现p^T W q(见 deepctr/layers/interaction.py);'vec'与'num'分支则统一走kp = reduce_sum(p * q * k, -1)的简洁路径(deepctr/layers/interaction.py)。
四种产品层组合模式
use_inner与use_outter的组合决定了送入 DNN 的deep_input(deepctr/models/pnn.py):
use_inner | use_outter | deep_input 内容 | 对应模型 |
|---|---|---|---|
True | False | linear_signal + inner_product | IPNN(默认) |
False | True | linear_signal + outter_product | OPNN |
True | True | linear_signal + inner_product + outter_product | IPNN+OPNN 混合 |
False | False | 仅linear_signal | 退化为纯 embedding 拼接 + DNN |
注意linear_signal始终保留:即使关闭全部乘积,embedding 拼接信号依然作为 DNN 的输入,保证特征信息不丢失。
实战:用 PNN 完成一次完整的训练与评估
DeepCTR 各模型共享统一的使用范式:先构造特征列,再实例化模型,最后compile+fit。仓库测试工具 tests/utils.py 中的check_model展示了标准流程(tests/utils.py):
import numpy as np from deepctr.models import PNN from deepctr.feature_column import SparseFeat, DenseFeat # 1. 构造特征列(稀疏域 + 稠密域) feature_columns = [ SparseFeat('sparse_feature_0', vocabulary_size=4, embedding_dim=4), SparseFeat('sparse_feature_1', vocabulary_size=4, embedding_dim=4), DenseFeat('dense_feature_0', 1), ] # 2. 实例化模型(IPNN 默认配置) model = PNN(feature_columns, dnn_hidden_units=(256, 128, 64), dnn_dropout=0.5, use_inner=True, use_outter=False) # 3. 编译并训练 model.compile('adam', 'binary_crossentropy', metrics=['binary_crossentropy']) x = { 'sparse_feature_0': np.random.randint(0, 4, 8), 'sparse_feature_1': np.random.randint(0, 4, 8), 'dense_feature_0': np.random.random(8), } y = np.random.randint(0, 2, 8) model.fit(x, y, batch_size=100, epochs=1, validation_split=0.5) # 4. 权重与整模型持久化 model.save_weights('pnn_weights.h5') model.load_weights('pnn_weights.h5') model.save('pnn.h5')关于持久化有两点补充:
- 重载完整模型时需要使用 DeepCTR 提供的
custom_objects(from deepctr.layers import custom_objects),以保证InnerProductLayer、OutterProductLayer等自定义层能被正确反序列化; PNN_test用例正是依次验证了 "train/valid 通过 → save/load weights → save/load 整模型" 的完整闭环(tests/utils.py)。
PNNEstimator:TensorFlow Estimator 接口版本
除 Keras 版外,仓库还提供deepctr.estimator.models.pnn模块下的PNNEstimator(deepctr/estimator/models/pnn.py),面向 TensorFlow Estimator 生态。其核心参数与PNN完全一致(dnn_hidden_units、l2_reg_embedding、use_inner、use_outter、kernel_type、task等),额外扩展了训练运行时配置:
| 新增参数 | 默认值 | 说明 |
|---|---|---|
model_dir | None | 模型参数、计算图与 checkpoint 的保存目录,也用于断点续训 |
config | None | tf.RunConfig运行时配置 |
linear_optimizer | 'Ftrl' | 线性部分使用的优化器 |
dnn_optimizer | 'Adagrad' | 深度部分使用的优化器 |
training_chief_hooks | None | 训练时在 chief worker 上运行的SessionRunHook列表 |
结构上的差异(deepctr/estimator/models/pnn.py):内部_model_fn中,稀疏/稠密特征通过input_from_feature_columns得到 embedding,内积/外积与linear_signal的拼接逻辑与 Keras 版逐行对应;get_linear_logit(features, [], ...)以空线性特征列表构造线性 logit(即默认不叠加线性项);最后deepctr_model_fn统一封装训练、评估与预测。使用方式遵循 Estimator 惯例:model.train(input_fn)/model.evaluate(input_fn),见 tests/models/PNN_test.py 中的PNNEstimator用例。
需要留意:Estimator 接口的可用性依赖 TensorFlow 版本(测试工具中仅对<2.0或[2.0, 2.6)区间启用,见 tests/utils.py),选用前请确认运行环境。
测试与验证:仓库如何保障 PNN 可用
PNN_test.py 使用 pytest 参数化覆盖两类关键配置(tests/models/PNN_test.py):
(use_inner=True, use_outter=True, sparse_feature_num=3):双乘积混合模式;(use_inner=False, use_outter=False, sparse_feature_num=1):关闭全部乘积的退化模式。
测试数据由get_test_data随机生成(SAMPLE_SIZE=8,embedding 维度 4),每个用例均以dnn_hidden_units=[4, 4], dnn_dropout=0.5构造模型并跑通check_model全流程。这从侧面印证了:任何use_inner/use_outter组合与稀疏特征数量下,PNN 都能完成编译、训练、评估与序列化,可作为自定义实验的参照模板。
小结
PNN 通过产品层把"特征域两两交互"显式注入深度网络,在 DeepCTR 中落地为PNN/PNNEstimator两个入口,配合InnerProductLayer、OutterProductLayer两个可复用算子,可一键切换 IPNN、OPNN 及混合模式。想要进一步了解与其他深度 CTR 模型的差异,可对照同目录下的 FNN、DeepFM、NFM 等模型文档;若需在实验中验证梯度流与训练行为,可从 tests/models/PNN_test.py 出发快速搭建回归用例。
- 人工智能
- 深度学习
- 机器学习
【免费下载链接】DeepCTR
Easy-to-use,Modular and Extendible package of deep-learning based CTR models .
相关推荐
DeepCTR 中的 DCN(Deep & Cross Network)模型:架构原理、参数详解与实战指南
DeepCTR 中的 DCN(Deep & Cross Network)模型:架构原理、参数详解与实战指南 本文聚焦 DeepCTR 开源仓库中的 deepct
人工智能深度学习机器学习Relay 数据刷新实战:useRefetchableFragment 完整 API 参考与源码级原理剖析
Relay 数据刷新实战:useRefetchableFragment 完整 API 参考与源码级原理剖析 导读 useRefetchableFragment
前端开发工具Extism Go SDK 实战指南:在 Go 宿主应用中加载与调用 WebAssembly 插件
Extism Go SDK 实战指南:在 Go 宿主应用中加载与调用 WebAssembly 插件 Extism 是一套跨语言的插件运行时体系,其 Go SDK
人工智能深度学习机器学习
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考