Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add pdn model #409

Merged
merged 12 commits into from
Aug 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .git_bin_path
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
{"leaf_name": "data/test/movielens_1m", "leaf_file": ["data/test/movielens_1m/ml_test_data", "data/test/movielens_1m/ml_train_data"]}
{"leaf_name": "data/test/mt_ckpt", "leaf_file": ["data/test/mt_ckpt/model.ckpt-100.data-00000-of-00001", "data/test/mt_ckpt/model.ckpt-100.index", "data/test/mt_ckpt/model.ckpt-100.meta"]}
{"leaf_name": "data/test/rtp", "leaf_file": ["data/test/rtp/taobao_fg_pred.out", "data/test/rtp/taobao_test_bucketize_feature.txt", "data/test/rtp/taobao_test_feature.txt", "data/test/rtp/taobao_test_input.txt", "data/test/rtp/taobao_train_bucketize_feature.txt", "data/test/rtp/taobao_train_feature.txt", "data/test/rtp/taobao_train_input.txt", "data/test/rtp/taobao_valid.csv", "data/test/rtp/taobao_valid_feature.txt"]}
{"leaf_name": "data/test/tb_data", "leaf_file": ["data/test/tb_data/taobao_ad_feature_gl", "data/test/tb_data/taobao_clk_edge_gl", "data/test/tb_data/taobao_multi_seq_test_data", "data/test/tb_data/taobao_multi_seq_train_data", "data/test/tb_data/taobao_noclk_edge_gl", "data/test/tb_data/taobao_test_data", "data/test/tb_data/taobao_test_data_compress.gz", "data/test/tb_data/taobao_test_data_for_expr", "data/test/tb_data/taobao_test_data_kd", "data/test/tb_data/taobao_test_data_remap_label", "data/test/tb_data/taobao_train_data", "data/test/tb_data/taobao_train_data_for_expr", "data/test/tb_data/taobao_train_data_kd", "data/test/tb_data/taobao_train_data_remap_label", "data/test/tb_data/taobao_user_profile_gl"]}
{"leaf_name": "data/test/tb_data", "leaf_file": ["data/test/tb_data/taobao_ad_feature_gl", "data/test/tb_data/taobao_clk_edge_gl", "data/test/tb_data/taobao_multi_seq_test_data", "data/test/tb_data/taobao_multi_seq_train_data", "data/test/tb_data/taobao_noclk_edge_gl", "data/test/tb_data/taobao_pdn_fake_test_data", "data/test/tb_data/taobao_pdn_fake_train_data", "data/test/tb_data/taobao_test_data", "data/test/tb_data/taobao_test_data_compress.gz", "data/test/tb_data/taobao_test_data_for_expr", "data/test/tb_data/taobao_test_data_kd", "data/test/tb_data/taobao_test_data_remap_label", "data/test/tb_data/taobao_train_data", "data/test/tb_data/taobao_train_data_for_expr", "data/test/tb_data/taobao_train_data_kd", "data/test/tb_data/taobao_train_data_remap_label", "data/test/tb_data/taobao_user_profile_gl"]}
{"leaf_name": "data/test/tb_data/hard_negative_sampler_edge", "leaf_file": ["data/test/tb_data/hard_negative_sampler_edge/taobao_noclk_edge_gl.csv"]}
{"leaf_name": "data/test/tb_data/hard_negative_sampler_item", "leaf_file": ["data/test/tb_data/hard_negative_sampler_item/taobao_ad_feature_gl.csv"]}
{"leaf_name": "data/test/tb_data/hard_negative_sampler_user", "leaf_file": ["data/test/tb_data/hard_negative_sampler_user/taobao_user_profile_gl.csv"]}
Expand Down
2 changes: 1 addition & 1 deletion .git_bin_url
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
{"leaf_path": "data/test/movielens_1m", "sig": "99badbeec64f2fcabe0dfa1d2bfd8fb5", "remote_path": "data/git_oss_sample_data/data_test_movielens_1m_99badbeec64f2fcabe0dfa1d2bfd8fb5"}
{"leaf_path": "data/test/mt_ckpt", "sig": "803499f48e2df5e51ce5606e9649c6d4", "remote_path": "data/git_oss_sample_data/data_test_mt_ckpt_803499f48e2df5e51ce5606e9649c6d4"}
{"leaf_path": "data/test/rtp", "sig": "76cda60582617ddbb7cd5a49eb68a4b9", "remote_path": "data/git_oss_sample_data/data_test_rtp_76cda60582617ddbb7cd5a49eb68a4b9"}
{"leaf_path": "data/test/tb_data", "sig": "f1279ca42de1734be321e88f85775d5f", "remote_path": "data/git_oss_sample_data/data_test_tb_data_f1279ca42de1734be321e88f85775d5f"}
{"leaf_path": "data/test/tb_data", "sig": "b1579db090d72b3b70b59ba3c7692701", "remote_path": "data/git_oss_sample_data/data_test_tb_data_b1579db090d72b3b70b59ba3c7692701"}
{"leaf_path": "data/test/tb_data/hard_negative_sampler_edge", "sig": "48f994681d719a2546ec4003fcbc638c", "remote_path": "data/git_oss_sample_data/data_test_tb_data_hard_negative_sampler_edge_48f994681d719a2546ec4003fcbc638c"}
{"leaf_path": "data/test/tb_data/hard_negative_sampler_item", "sig": "f23a9eb9457c14a8e57b455804b1f013", "remote_path": "data/git_oss_sample_data/data_test_tb_data_hard_negative_sampler_item_f23a9eb9457c14a8e57b455804b1f013"}
{"leaf_path": "data/test/tb_data/hard_negative_sampler_user", "sig": "23514156eae5a4250ac1d0a118883430", "remote_path": "data/git_oss_sample_data/data_test_tb_data_hard_negative_sampler_user_23514156eae5a4250ac1d0a118883430"}
Expand Down
2 changes: 2 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ data/test/inference/fg_export_multi/variables/variables.index filter=lfs diff=lf
data/test/inference/tb_multitower_export/assets/pipeline.config filter=lfs diff=lfs merge=lfs -text
data/test/latest_ckpt_test/model.ckpt-500.meta filter=lfs diff=lfs merge=lfs -text
data/test/tb_data/taobao_test_data filter=lfs diff=lfs merge=lfs -text
data/test/tb_data/taobao_pdn_fake_test_data filter=lfs diff=lfs merge=lfs -text
data/test/tb_data/taobao_multi_seq_test_data filter=lfs diff=lfs merge=lfs -text
data/test/test.csv filter=lfs diff=lfs merge=lfs -text
data/test/inference/tb_multitower_placeholder_rename_export/assets/pipeline.config filter=lfs diff=lfs merge=lfs -text
Expand All @@ -16,6 +17,7 @@ data/test/criteo_sample.tfrecord filter=lfs diff=lfs merge=lfs -text
data/test/rtp/taobao_valid.csv filter=lfs diff=lfs merge=lfs -text
data/test/rtp/taobao_train_feature.txt filter=lfs diff=lfs merge=lfs -text
data/test/tb_data/taobao_train_data filter=lfs diff=lfs merge=lfs -text
data/test/tb_data/taobao_pdn_fake_train_data filter=lfs diff=lfs merge=lfs -text
data/test/tb_data/taobao_multi_seq_train_data filter=lfs diff=lfs merge=lfs -text
data/test/inference/fg_export_single/variables/variables.index filter=lfs diff=lfs merge=lfs -text
data/test/inference/lookup_data_test80.csv filter=lfs diff=lfs merge=lfs -text
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ Running Platform:

### A variety of models

- [DSSM](docs/source/models/dssm.md) / [MIND](docs/source/models/mind.md) / [DropoutNet](docs/source/models/dropoutnet.md) / [CoMetricLearningI2I](docs/source/models/co_metric_learning_i2i.md)
- [DSSM](docs/source/models/dssm.md) / [MIND](docs/source/models/mind.md) / [DropoutNet](docs/source/models/dropoutnet.md) / [CoMetricLearningI2I](docs/source/models/co_metric_learning_i2i.md) / [PDN](docs/source/models/pdn.md)
- [W&D](docs/source/models/wide_and_deep.md) / [DeepFM](docs/source/models/deepfm.md) / [MultiTower](docs/source/models/multi_tower.md) / [DCN](docs/source/models/dcn.md) / [FiBiNet](docs/source/models/fibinet.md) / [MaskNet](docs/source/models/masknet.md)
- [DIN](docs/source/models/din.md) / [BST](docs/source/models/bst.md)
- [MMoE](docs/source/models/mmoe.md) / [ESMM](docs/source/models/esmm.md) / [DBMTL](docs/source/models/dbmtl.md) / [PLE](docs/source/models/ple.md)
Expand Down
Binary file added docs/images/models/pdn.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/models/pdn_1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion docs/source/intro.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ EasyRec implements state of the art machine learning models used in common recom

### A variety of models

- [DSSM](models/dssm.md) / [MIND](models/mind.md) / [DropoutNet](models/dropoutnet.md) / [CoMetricLearningI2I](models/co_metric_learning_i2i.md)
- [DSSM](models/dssm.md) / [MIND](models/mind.md) / [DropoutNet](models/dropoutnet.md) / [CoMetricLearningI2I](models/co_metric_learning_i2i.md) / [PDN](models/pdn.md)
- [W&D](models/wide_and_deep.md) / [DeepFM](models/deepfm.md) / [MultiTower](models/multi_tower.md) / [DCN](models/dcn.md) / [DIN](models/din.md) / [BST](models/bst.md)
- [MMoE](models/mmoe.md) / [ESMM](models/esmm.md) / [DBMTL](models/dbmtl.md) / [PLE](models/ple.md)
- [CMBF](models/cmbf.md) / [UNITER](models/uniter.md)
Expand Down
114 changes: 114 additions & 0 deletions docs/source/models/pdn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
# PDN

### 简介

在推荐系统的召回阶段,工业界常多采用 基于Item的协同过滤 (Item-based CF) 和 基于embedding的检索 (EBR)。然而,Item-based CF很难满足个性化,而EBR则很难满足多样性。

论文中提出了一种新颖的匹配架构——基于路径的深度网络(Path-based Deep Network, PDN), 它可以结合个性化和多样性来提高匹配性能。

具体来说,PDN由两个模块组成:Trigger Net和Similarity Net(SimNet,与下文一致)。 PDN 利用 Trigger Net 来捕获用户对其每个交互item的兴趣,并利用 SimNet 根据这些item的特征和共现信息来评估每个交互item与目标item之间的相似度。

用户和目标item之间的最终相关性是通过明确考虑用户的不同兴趣来计算的,即聚合相关两跳路径的相关权重(路径的一跳对应于user - trigger item交互,另一跳对应于trigger item - target item相关性)。
wwxxzz marked this conversation as resolved.
Show resolved Hide resolved

![pdn.png](../../images/models/pdn.jpg)
(图片来自参考论文)

### 配置说明

#### PDN

```protobuf
model_config {
model_class: "PDN"
feature_groups: {
group_name: 'u2i_seq'
feature_names: 'event_type_seq'
}
feature_groups: {
group_name: 'i_seq'
feature_names: 'tag_category_list'
}
feature_groups: {
group_name: 'i2i_seq'
feature_names: 'i2i_rnk'
}
feature_groups: {
group_name: 'user'
feature_names: 'user_id'
feature_names: 'cms_segid'
wide_deep:DEEP
}
feature_groups: {
group_name: "item"
feature_names: 'adgroup_id'
feature_names: 'cate_id'
wide_deep:DEEP
}
feature_groups {
group_name: 'bias'
feature_names: 'position'
}

pdn {
user_dnn {
hidden_units: [128, 64, 32]
}
item_dnn {
hidden_units: [128, 64, 32]
}
u2i_dnn {
hidden_units: [64, 32]
}
i2i_dnn {
hidden_units: [128, 64, 32]
}
trigger_dnn {
hidden_units: [64, 32, 1]
}
sim_dnn {
hidden_units: [64, 32, 1]
}
bias_dnn {
hidden_units: [32, 32, 1]
}

l2_regularization: 1e-6
}
embedding_regularization: 5e-6
}
```

- model_class: 'PDN', 不需要修改

- feature_groups: 需要六个feature_group: u2i_seq, i_seq, i2i_seq, user, item 和 bias.

**group name不能变, 且u2i_seq, i_seq, i2i_seq的输入均为Sequence Feature.**

特征说明可以参考下图
![pdn_1.png](../../images/models/pdn_1.png)

- u2i_seq: u用户对交互过的trigger物品的行为侧信息的序列,如 event_type, click_time等
wwxxzz marked this conversation as resolved.
Show resolved Hide resolved
- i_seq: 用户交互过的trigger物品的相关特征的序列,如 category, brand等
- i2i_seq: trigger物品到target物品的统计特征和共现特征,如 是否同品牌, swing_score等
- user: 用户侧相关特征,如 age, sex等
- item: 物品侧相关特征,如 category, brand等
- bias: 导致选择性偏差的特征(位置信息等),如 position等

- pdn: pdn模型相关的参数

- user_dnn:构造DNN编码user特征信息转换为向量
- item_dnn:构造DNN编码item特征信息转换为向量
- u2i_dnn:构造DNN编码user to trigger item的行为特征信息转换为向量
- i2i_dnn:构造DNN编码trigger item to target item特征转换为向量
- trigger_dnn:构造TriggerNet DNN计算user对每个交互item的喜爱程度来捕获user的多峰兴趣
- sim_dnn:构造SimNet DNN基于item信息和共现信息计算trigger item与target item的相似度
- bias_dnn (可选):基于会导致选择性偏差的特征(位置信息等)训练一个浅层DNN
- embedding_regularization: 对embedding部分加regularization,防止overfit

### 示例Config

[PDN_demo.config](../../../samples/model_config/pdn_on_taobao.config)

### 参考论文

[PDN](https://arxiv.org/abs/2105.08246)
203 changes: 203 additions & 0 deletions easy_rec/python/model/pdn.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
# -*- encoding:utf-8 -*-
# Copyright (c) Alibaba, Inc. and its affiliates.

import tensorflow as tf

from easy_rec.python.layers import dnn
from easy_rec.python.model.match_model import MatchModel
from easy_rec.python.protos.simi_pb2 import Similarity

if tf.__version__ >= '2.0':
tf = tf.compat.v1
losses = tf.losses
metrics = tf.metrics


class PDN(MatchModel):

def __init__(self,
model_config,
feature_configs,
features,
labels=None,
is_training=False):
super(PDN, self).__init__(model_config, feature_configs, features, labels,
is_training)
assert self._model_config.WhichOneof('model') == 'pdn', \
'invalid model config: %s' % self._model_config.WhichOneof('model')
self._model_config = self._model_config.pdn

self._user_features, _ = self._input_layer(self._feature_dict, 'user')
self._item_features, _ = self._input_layer(self._feature_dict, 'item')

if self._input_layer.has_group('bias'):
self._bias_features, _ = self._input_layer(self._feature_dict, 'bias')
else:
self._bias_features = None

self._u2i_seq, self._seq_len = self._get_seq_features('u2i_seq')
self._i_seq, _ = self._get_seq_features('i_seq')
self._i2i_seq, _ = self._get_seq_features('i2i_seq')

def build_predict_graph(self):
trigger_out = self._build_trigger_net()
sim_out = self._build_similarity_net()
logits = tf.multiply(sim_out, trigger_out)

seq_mask = tf.to_float(
tf.sequence_mask(self._seq_len,
tf.shape(sim_out)[1]))
logits = tf.reduce_sum(logits * seq_mask[:, :, None], axis=1)

direct_logits = self._build_direct_net()
if direct_logits is not None:
logits += direct_logits

bias_logits = self._build_bias_net()
if bias_logits is not None:
logits += bias_logits

logits = tf.squeeze(logits, axis=1)
probs = 1 - tf.exp(-logits) # map [0, inf) to [0, 1)

self._prediction_dict['probs'] = probs
self._prediction_dict['logits'] = tf.log(
tf.clip_by_value(probs, 1e-8, 1 - 1e-8))
return self._prediction_dict

def _get_seq_features(self, name):
seqs, _, _ = self._input_layer(self._feature_dict, name, is_combine=False)
seq_len = seqs[0][1]
seq = tf.concat([x[0] for x in seqs], axis=2)
return seq, seq_len

def _build_trigger_net(self):
user_dnn_layer = dnn.DNN(self._model_config.user_dnn, self._l2_reg,
'user_dnn', self._is_training)
user_fea = user_dnn_layer(self._user_features)

trigger_seq = tf.concat([self._u2i_seq, self._i_seq], axis=2)
u2i_dnn_layer = dnn.DNN(self._model_config.u2i_dnn, self._l2_reg, 'u2i_dnn',
self._is_training)
trigger_seq_fea = u2i_dnn_layer(trigger_seq)

trigger_merge_fea = trigger_seq_fea + user_fea[:, None, :]
trigger_dnn_layer = dnn.DNN(
self._model_config.trigger_dnn,
self._l2_reg,
'trigger_dnn',
self._is_training,
last_layer_no_activation=True,
last_layer_no_batch_norm=True)

# output: N x seq_len x d, d is usually set to 1
wwxxzz marked this conversation as resolved.
Show resolved Hide resolved
trigger_out = trigger_dnn_layer(trigger_merge_fea)
# exp(x): map (-inf, inf) to (0, inf)
trigger_out = tf.exp(trigger_out)

self._prediction_dict['trigger_out'] = tf.reduce_join(
tf.reduce_join(
tf.as_string(trigger_out, precision=4, shortest=True),
axis=2,
separator=','),
axis=1,
separator=';')
return trigger_out

def _build_similarity_net(self):
item_dnn_layer = dnn.DNN(self._model_config.item_dnn, self._l2_reg,
'item_dnn', self._is_training)
item_fea = item_dnn_layer(self._item_features)

sim_side_dnn_layer = dnn.DNN(self._model_config.i2i_dnn, self._l2_reg,
'i2i_dnn', self._is_training)
sim_seq_fea = sim_side_dnn_layer(self._i_seq)

sim_seq_cross = sim_seq_fea * item_fea[:, None, :]

item_fea_tile = tf.tile(item_fea[:, None, :],
[1, tf.shape(sim_seq_fea)[1], 1])

sim_seq_concat = tf.concat(
[sim_seq_cross, sim_seq_cross, self._i2i_seq, item_fea_tile], axis=2)
sim_dnn_layer = dnn.DNN(
self._model_config.sim_dnn,
self._l2_reg,
'sim_dnn',
self._is_training,
last_layer_no_activation=True,
last_layer_no_batch_norm=True)
# output: N x seq_len x 1
sim_out = sim_dnn_layer(sim_seq_concat)
# exp(x): map (-inf, inf) to (0, inf)
sim_out = tf.exp(sim_out)

self._prediction_dict['sim_out'] = tf.reduce_join(
tf.reduce_join(
tf.as_string(sim_out, precision=4, shortest=True),
axis=2,
separator=','),
axis=1,
separator=';')
return sim_out

def _build_direct_net(self):
if self._model_config.HasField('direct_user_dnn') and \
self._model_config.HasField('direct_item_dnn'):
direct_user_layer = dnn.DNN(
self._model_config.direct_user_dnn,
'direct_user_dnn',
self._is_training,
last_layer_no_activation=True,
last_layer_no_batch_norm=True)
direct_user_out = direct_user_layer(self._user_features)
direct_item_layer = dnn.DNN(
self._model_config.direct_item_dnn,
'direct_item_dnn',
self._is_training,
last_layer_no_activation=True,
last_layer_no_batch_norm=True)
direct_item_out = direct_item_layer(self._item_features)

if self._model_config.simi_func == Similarity.COSINE:
direct_user_out = self.norm(direct_user_out)
direct_item_out = self.norm(direct_item_out)

self._prediction_dict['direct_user_embedding'] = direct_user_out
self._prediction_dict['direct_item_embedding'] = direct_item_out
direct_logits = tf.reduce_sum(direct_user_out * direct_item_out, axis=1)

if self._model_config.scale_simi:
sim_w = tf.get_variable(
'direct_net/sim_w',
dtype=tf.float32,
shape=(1),
initializer=tf.ones_initializer())
sim_b = tf.get_variable(
'direct_net/sim_b',
dtype=tf.float32,
shape=(1),
initializer=tf.zeros_initializer())
direct_logits = direct_logits * tf.abs(sim_w) + sim_b

return tf.nn.softplus(direct_logits)
else:
return None

def _build_bias_net(self):
if self._model_config.HasField('bias_dnn'):
assert self._bias_features is not None, 'bias group must be defined'
bias_dnn_layer = dnn.DNN(
self._model_config.bias_dnn,
self._l2_reg,
'bias_dnn',
self._is_training,
last_layer_no_activation=True,
last_layer_no_batch_norm=True)
bias_logits = bias_dnn_layer(self._bias_features)
return tf.nn.softplus(bias_logits)
else:
return None

def get_outputs(self):
return ['logits', 'probs', 'trigger_out', 'sim_out']
2 changes: 2 additions & 0 deletions easy_rec/python/protos/easy_rec_model.proto
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import "easy_rec/python/protos/rocket_launching.proto";
import "easy_rec/python/protos/variational_dropout.proto";
import "easy_rec/python/protos/multi_tower_recall.proto";
import "easy_rec/python/protos/tower.proto";
import "easy_rec/python/protos/pdn.proto";

// for input performance test
message DummyModel {
Expand Down Expand Up @@ -82,6 +83,7 @@ message EasyRecModel {
MIND mind = 202;
DropoutNet dropoutnet = 203;
CoMetricLearningI2I metric_learning = 204;
PDN pdn = 205;

MMoE mmoe = 301;
ESMM esmm = 302;
Expand Down
Loading