From fe1e7b3e97250f80849b41dbff37a7256ef8b796 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B5=85=E6=A2=A6?= Date: Sun, 17 May 2020 22:04:25 +0800 Subject: [PATCH] version compatibility --- .github/workflows/ci.yml | 2 +- README.md | 3 +- deepmatch/__init__.py | 2 +- deepmatch/layers/interaction.py | 61 ++++--- deepmatch/layers/sequence.py | 21 +-- deepmatch/models/mind.py | 19 +-- deepmatch/models/sdm.py | 55 ++++--- deepmatch/models/youtubednn.py | 12 +- deepmatch/utils.py | 2 + docs/pics/SDM.jpg | Bin 0 -> 132656 bytes docs/source/Examples.md | 152 +++++++++++++++++- docs/source/Features.md | 13 ++ docs/source/History.md | 1 + docs/source/Models.rst | 1 + docs/source/Quick-Start.md | 3 +- docs/source/conf.py | 2 +- docs/source/deepmatch.models.rst | 1 + docs/source/deepmatch.models.sdm.rst | 7 + docs/source/index.rst | 2 + .../{run_sdm_sampledsoftmax.py => run_sdm.py} | 26 +-- ...nn_sampledsoftmax.py => run_youtubednn.py} | 3 +- setup.py | 4 +- tests/models/SDM_test.py | 4 +- tests/utils.py | 12 +- 24 files changed, 307 insertions(+), 101 deletions(-) create mode 100644 docs/pics/SDM.jpg create mode 100644 docs/source/deepmatch.models.sdm.rst rename examples/{run_sdm_sampledsoftmax.py => run_sdm.py} (88%) rename examples/{run_youtubednn_sampledsoftmax.py => run_youtubednn.py} (98%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 00096cb..ee1b5bc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,7 +18,7 @@ jobs: strategy: matrix: python-version: [3.5,3.6,3.7] - tf-version: [1.4.0,1.14.0,2.1.0] + tf-version: [1.4.0,1.14.0,2.1.0,2.2.0] exclude: - python-version: 3.7 diff --git a/README.md b/README.md index b574fea..74a346b 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,8 @@ Let's [**Get Started!**](https://deepmatch.readthedocs.io/en/latest/Quick-Start. | DSSM | [CIKM 2013][Deep Structured Semantic Models for Web Search using Clickthrough Data](https://www.microsoft.com/en-us/research/publication/learning-deep-structured-semantic-models-for-web-search-using-clickthrough-data/) | | YoutubeDNN | [RecSys 2016][Deep Neural Networks for YouTube Recommendations](https://www.researchgate.net/publication/307573656_Deep_Neural_Networks_for_YouTube_Recommendations) | | NCF | [WWW 2017][Neural Collaborative Filtering](https://arxiv.org/abs/1708.05031) | -| MIND | [CIKM 2019][Multi-interest network with dynamic routing for recommendation at Tmall](https://arxiv.org/pdf/1904.08030) | +| SDM | [CIKM 2019][SDM: Sequential Deep Matching Model for Online Large-scale Recommender System](https://arxiv.org/abs/1909.00385) | +| MIND | [CIKM 2019][Multi-interest network with dynamic routing for recommendation at Tmall](https://arxiv.org/pdf/1904.08030) | ## Contributors([welcome to join us!](./CONTRIBUTING.md)) diff --git a/deepmatch/__init__.py b/deepmatch/__init__.py index 4c4a1c1..b1af8c4 100644 --- a/deepmatch/__init__.py +++ b/deepmatch/__init__.py @@ -1,4 +1,4 @@ from .utils import check_version -__version__ = '0.1.2' +__version__ = '0.1.3' check_version(__version__) diff --git a/deepmatch/layers/interaction.py b/deepmatch/layers/interaction.py index c47390b..6c6f5ab 100644 --- a/deepmatch/layers/interaction.py +++ b/deepmatch/layers/interaction.py @@ -1,11 +1,8 @@ -import itertools - import tensorflow as tf -from tensorflow.python.keras import backend as K -from tensorflow.python.keras.initializers import TruncatedNormal -from tensorflow.python.keras.layers import Layer, Dense, Dropout from deepctr.layers.normalization import LayerNormalization from deepctr.layers.utils import softmax, reduce_mean +from tensorflow.python.keras.initializers import TruncatedNormal +from tensorflow.python.keras.layers import Layer, Dense, Dropout class DotAttention(Layer): @@ -14,6 +11,7 @@ class DotAttention(Layer): :param key: [batch_size, T, C] :return: [batch_size, 1, T] """ + def __init__(self, scale=True, **kwargs): self.scale = scale super(DotAttention, self).__init__(**kwargs) @@ -47,6 +45,7 @@ class ConcatAttention(Layer): :return: [batch_size, 1, T] query_size should keep the same dim with key_size """ + def __init__(self, scale=True, **kwargs): self.scale = scale super(ConcatAttention, self).__init__(**kwargs) @@ -85,6 +84,7 @@ class SoftmaxWeightedSum(Layer): :return: weighted sum vector [batch_size, 1, units] """ + def __init__(self, dropout_rate=0.2, future_binding=False, seed=2020, **kwargs): self.dropout_rate = dropout_rate self.future_binding = future_binding @@ -137,6 +137,7 @@ class AttentionSequencePoolingLayer(Layer): :param keys_length: [batch_size, 1] :return: [batch_size, 1, C_k] """ + def __init__(self, dropout_rate=0, **kwargs): self.dropout_rate = dropout_rate super(AttentionSequencePoolingLayer, self).__init__(**kwargs) @@ -144,7 +145,7 @@ def __init__(self, dropout_rate=0, **kwargs): def build(self, input_shape): if not isinstance(input_shape, list) or len(input_shape) != 3: raise ValueError('A `SequenceFeatureMask` layer should be called ' - 'on a list of 3 inputs') + 'on a list of 3 inputs') self.concat_att = ConcatAttention() self.softmax_weight_sum = SoftmaxWeightedSum(dropout_rate=self.dropout_rate, future_binding=False) super(AttentionSequencePoolingLayer, self).build(input_shape) @@ -178,6 +179,7 @@ class SelfAttention(Layer): :param key_masks: A 3d tensor with shape of [batch_size, 1] :return: A 3d tensor with shape of [batch_size, 1] """ + def __init__(self, scale=True, dropout_rate=0.2, future_binding=True, use_layer_norm=True, seed=2020, **kwargs): self.scale = scale self.dropout_rate = dropout_rate @@ -192,7 +194,8 @@ def build(self, input_shape): 'on a list of 2 tensors') self.layer_norm = LayerNormalization() self.attention = DotAttention(scale=self.scale) - self.softmax_weight_sum = SoftmaxWeightedSum(dropout_rate=self.dropout_rate, future_binding=self.future_binding, seed=self.seed) + self.softmax_weight_sum = SoftmaxWeightedSum(dropout_rate=self.dropout_rate, future_binding=self.future_binding, + seed=self.seed) super(SelfAttention, self).build(input_shape) def call(self, inputs, mask=None, **kwargs): @@ -217,7 +220,9 @@ class SelfMultiHeadAttention(Layer): :param key_masks: A 3d tensor with shape of [batch_size, 1] :return: A 3d tensor with shape of [batch_size, T, C] """ - def __init__(self, num_units=8, head_num=4, scale=True, dropout_rate=0.2, future_binding=True, use_layer_norm=True, use_res=True, + + def __init__(self, num_units=8, head_num=4, scale=True, dropout_rate=0.2, future_binding=True, use_layer_norm=True, + use_res=True, seed=2020, **kwargs): if head_num <= 0: raise ValueError('head_num must be a int > 0') @@ -240,16 +245,17 @@ def build(self, input_shape): embedding_size = int(input_shape[0][-1]) if self.num_units == None: self.num_units = embedding_size - self.W = self.add_weight(name='Q_K_V', shape=[embedding_size, self.num_units*3], - dtype=tf.float32, - initializer=TruncatedNormal(seed=self.seed)) + self.W = self.add_weight(name='Q_K_V', shape=[embedding_size, self.num_units * 3], + dtype=tf.float32, + initializer=TruncatedNormal(seed=self.seed)) self.W_output = self.add_weight(name='output_W', shape=[self.num_units, self.num_units], - dtype=tf.float32, - initializer=TruncatedNormal(seed=self.seed)) + dtype=tf.float32, + initializer=TruncatedNormal(seed=self.seed)) self.layer_norm = LayerNormalization() self.attention = DotAttention(scale=self.scale) - self.softmax_weight_sum = SoftmaxWeightedSum(dropout_rate=self.dropout_rate, future_binding=self.future_binding, seed=self.seed) + self.softmax_weight_sum = SoftmaxWeightedSum(dropout_rate=self.dropout_rate, future_binding=self.future_binding, + seed=self.seed) self.dropout = Dropout(self.dropout_rate, seed=self.seed) self.seq_len_max = int(input_shape[0][1]) # Be sure to call this somewhere! @@ -267,7 +273,7 @@ def call(self, inputs, mask=None, training=None, **kwargs): # head_num None F D querys = tf.concat(tf.split(querys, self.head_num, axis=2), axis=0) # (h*N, T_q, C/h) - keys = tf.concat(tf.split(keys, self.head_num, axis=2), axis=0) # (h*N, T_k, C/h) + keys = tf.concat(tf.split(keys, self.head_num, axis=2), axis=0) # (h*N, T_k, C/h) values = tf.concat(tf.split(values, self.head_num, axis=2), axis=0) # (h*N, T_k, C/h) # (h*N, T_q, T_k) @@ -276,10 +282,10 @@ def call(self, inputs, mask=None, training=None, **kwargs): key_masks = tf.tile(key_masks, [self.head_num, 1]) # (h*N, T_k) key_masks = tf.tile(tf.expand_dims(key_masks, 1), [1, tf.shape(input_info)[1], 1]) # (h*N, T_q, T_k) - outputs = self.softmax_weight_sum([align, values, key_masks]) # (h*N, T_q, C/h) - outputs = tf.concat(tf.split(outputs, self.head_num, axis=0), axis=2) # (N, T_q, C) + outputs = self.softmax_weight_sum([align, values, key_masks]) # (h*N, T_q, C/h) + outputs = tf.concat(tf.split(outputs, self.head_num, axis=0), axis=2) # (N, T_q, C) - outputs = tf.tensordot(outputs, self.W_output, axes=(-1, 0))# (N, T_q, C) + outputs = tf.tensordot(outputs, self.W_output, axes=(-1, 0)) # (N, T_q, C) outputs = self.dropout(outputs, training=training) if self.use_res: outputs += input_info @@ -292,8 +298,10 @@ def compute_output_shape(self, input_shape): return (None, input_shape[0][1], self.num_units) def get_config(self, ): - config = {'num_units':self.num_units, 'head_num':self.head_num, 'scale': self.scale, 'dropout_rate': self.dropout_rate, - 'future_binding': self.future_binding, 'use_layer_norm':self.use_layer_norm, 'use_res': self.use_res,'seed': self.seed} + config = {'num_units': self.num_units, 'head_num': self.head_num, 'scale': self.scale, + 'dropout_rate': self.dropout_rate, + 'future_binding': self.future_binding, 'use_layer_norm': self.use_layer_norm, 'use_res': self.use_res, + 'seed': self.seed} base_config = super(SelfMultiHeadAttention, self).get_config() return dict(list(base_config.items()) + list(config.items())) @@ -308,7 +316,9 @@ class UserAttention(Layer): :param key_masks: A 3d tensor with shape of [batch_size, 1] :return: A 3d tensor with shape of [batch_size, 1, C] """ - def __init__(self, num_units=None, activation='tanh', use_res=True, dropout_rate=0, scale=True, seed=2020, **kwargs): + + def __init__(self, num_units=None, activation='tanh', use_res=True, dropout_rate=0, scale=True, seed=2020, + **kwargs): self.scale = scale self.num_units = num_units self.activation = activation @@ -346,4 +356,11 @@ def compute_output_shape(self, input_shape): return (None, 1, input_shape[1][2]) def compute_mask(self, inputs, mask): - return mask \ No newline at end of file + return mask + + def get_config(self, ): + config = {'num_units': self.num_units, 'activation': self.activation, 'use_res': self.use_res, + 'dropout_rate': self.dropout_rate, + 'scale': self.scale, 'seed': self.seed, } + base_config = super(UserAttention, self).get_config() + return dict(list(base_config.items()) + list(config.items())) diff --git a/deepmatch/layers/sequence.py b/deepmatch/layers/sequence.py index 151175a..a3bffc7 100644 --- a/deepmatch/layers/sequence.py +++ b/deepmatch/layers/sequence.py @@ -1,12 +1,10 @@ - -import itertools - import tensorflow as tf from tensorflow.python.keras.layers import Layer class DynamicMultiRNN(Layer): - def __init__(self, num_units=None, rnn_type='LSTM', return_sequence=True, num_layers=2, num_residual_layers=1, dropout_rate=0.2, + def __init__(self, num_units=None, rnn_type='LSTM', return_sequence=True, num_layers=2, num_residual_layers=1, + dropout_rate=0.2, forget_bias=1.0, **kwargs): self.num_units = num_units @@ -62,13 +60,17 @@ def build(self, input_shape): def call(self, input_list, mask=None, training=None): rnn_input, sequence_length = input_list + try: with tf.name_scope("rnn"), tf.variable_scope("rnn", reuse=tf.AUTO_REUSE): - rnn_output, hidden_state = tf.nn.dynamic_rnn(self.final_cell, inputs=rnn_input, sequence_length=tf.squeeze(sequence_length), + rnn_output, hidden_state = tf.nn.dynamic_rnn(self.final_cell, inputs=rnn_input, + sequence_length=tf.squeeze(sequence_length), dtype=tf.float32, scope=self.name) except: - rnn_output, hidden_state = tf.compat.v1.nn.dynamic_rnn(self.final_cell, inputs=rnn_input,sequence_length=tf.squeeze(sequence_length), - dtype=tf.float32, scope=self.name) + with tf.name_scope("rnn"), tf.compat.v1.variable_scope("rnn", reuse=tf.compat.v1.AUTO_REUSE): + rnn_output, hidden_state = tf.compat.v1.nn.dynamic_rnn(self.final_cell, inputs=rnn_input, + sequence_length=tf.squeeze(sequence_length), + dtype=tf.float32, scope=self.name) if self.return_sequence: return rnn_output else: @@ -82,9 +84,8 @@ def compute_output_shape(self, input_shape): return (None, 1, rnn_input_shape[2]) def get_config(self, ): - config = {'num_units': self.num_units, 'rnn_type': self.rnn_type, 'return_sequence':self.return_sequence, 'num_layers': self.num_layers, + config = {'num_units': self.num_units, 'rnn_type': self.rnn_type, 'return_sequence': self.return_sequence, + 'num_layers': self.num_layers, 'num_residual_layers': self.num_residual_layers, 'dropout_rate': self.dropout} base_config = super(DynamicMultiRNN, self).get_config() return dict(list(base_config.items()) + list(config.items())) - - diff --git a/deepmatch/models/mind.py b/deepmatch/models/mind.py index 4a8afb2..ddd1c2c 100755 --- a/deepmatch/models/mind.py +++ b/deepmatch/models/mind.py @@ -17,7 +17,7 @@ from deepmatch.utils import get_item_embedding from ..inputs import create_embedding_matrix -from ..layers.core import CapsuleLayer, PoolingLayer, LabelAwareAttention,SampledSoftmaxLayer,EmbeddingIndex +from ..layers.core import CapsuleLayer, PoolingLayer, LabelAwareAttention, SampledSoftmaxLayer, EmbeddingIndex def shape_target(target_emb_tmp, target_emb_size): @@ -59,7 +59,7 @@ def MIND(user_feature_columns, item_feature_columns, num_sampled=5, k_max=2, p=1 item_feature_name = item_feature_column.name item_vocabulary_size = item_feature_columns[0].vocabulary_size item_embedding_dim = item_feature_columns[0].embedding_dim - #item_index = Input(tensor=tf.constant([list(range(item_vocabulary_size))])) + # item_index = Input(tensor=tf.constant([list(range(item_vocabulary_size))])) history_feature_list = [item_feature_name] @@ -82,8 +82,9 @@ def MIND(user_feature_columns, item_feature_columns, num_sampled=5, k_max=2, p=1 seq_max_len = history_feature_columns[0].maxlen inputs_list = list(features.values()) - embedding_matrix_dict = create_embedding_matrix(user_feature_columns + item_feature_columns, l2_reg_embedding, init_std, - seed, prefix="") + embedding_matrix_dict = create_embedding_matrix(user_feature_columns + item_feature_columns, l2_reg_embedding, + init_std, + seed, prefix="") item_features = build_input_features(item_feature_columns) @@ -108,8 +109,8 @@ def MIND(user_feature_columns, item_feature_columns, num_sampled=5, k_max=2, p=1 history_emb = PoolingLayer()(NoMask()(keys_emb_list)) target_emb = PoolingLayer()(NoMask()(query_emb_list)) - #target_emb_size = target_emb.get_shape()[-1].value - #max_len = history_emb.get_shape()[1].value + # target_emb_size = target_emb.get_shape()[-1].value + # max_len = history_emb.get_shape()[1].value hist_len = features['hist_len'] high_capsule = CapsuleLayer(input_units=item_embedding_dim, @@ -125,7 +126,6 @@ def MIND(user_feature_columns, item_feature_columns, num_sampled=5, k_max=2, p=1 else: user_deep_input = high_capsule - user_embeddings = DNN(user_dnn_hidden_units, dnn_activation, l2_reg_dnn, dnn_dropout, dnn_use_bn, seed, name="user_embedding")(user_deep_input) item_inputs_list = list(item_features.values()) @@ -144,13 +144,14 @@ def MIND(user_feature_columns, item_feature_columns, num_sampled=5, k_max=2, p=1 user_embedding_final = LabelAwareAttention(k_max=k_max, pow_p=p, )((user_embeddings, target_emb)) output = SampledSoftmaxLayer(num_sampled=num_sampled)( - inputs=(pooling_item_embedding_weight,user_embedding_final, item_features[item_feature_name])) + [pooling_item_embedding_weight, user_embedding_final, item_features[item_feature_name]]) model = Model(inputs=inputs_list + item_inputs_list, outputs=output) model.__setattr__("user_input", inputs_list) model.__setattr__("user_embedding", user_embeddings) model.__setattr__("item_input", item_inputs_list) - model.__setattr__("item_embedding", get_item_embedding(pooling_item_embedding_weight, item_features[item_feature_name])) + model.__setattr__("item_embedding", + get_item_embedding(pooling_item_embedding_weight, item_features[item_feature_name])) return model diff --git a/deepmatch/models/sdm.py b/deepmatch/models/sdm.py index aca8fe1..8119fcf 100644 --- a/deepmatch/models/sdm.py +++ b/deepmatch/models/sdm.py @@ -8,24 +8,27 @@ """ import tensorflow as tf -from collections import OrderedDict -from tensorflow.python.keras.models import Model -from tensorflow.python.keras.layers import Dense, Input, Lambda from deepctr.inputs import build_input_features, SparseFeat, DenseFeat, get_varlen_pooling_list, VarLenSparseFeat, \ create_embedding_matrix, embedding_lookup, varlen_embedding_lookup, concat_func from deepctr.layers.utils import NoMask +from tensorflow.python.keras.layers import Dense, Input, Lambda +from tensorflow.python.keras.models import Model + +from deepmatch.utils import get_item_embedding from ..layers.core import PoolingLayer, SampledSoftmaxLayer, EmbeddingIndex -from ..layers.sequence import DynamicMultiRNN from ..layers.interaction import UserAttention, SelfMultiHeadAttention, AttentionSequencePoolingLayer -from deepmatch.utils import get_item_embedding +from ..layers.sequence import DynamicMultiRNN -def SDM(user_feature_columns, item_feature_columns, history_feature_list, units=64, rnn_layers=2, dropout_rate=0.2,rnn_num_res=1, - num_head=4, l2_reg_embedding=1e-6, dnn_activation='tanh', init_std=0.0001, seed=1024, num_sampled=5): +def SDM(user_feature_columns, item_feature_columns, history_feature_list, num_sampled=5, units=64, rnn_layers=2, dropout_rate=0.2, + rnn_num_res=1, + num_head=4, l2_reg_embedding=1e-6, dnn_activation='tanh', init_std=0.0001, seed=1024): """Instantiates the Sequential Deep Matching Model architecture. + :param user_feature_columns: An iterable containing user's features used by the model. :param item_feature_columns: An iterable containing item's features used by the model. :param history_feature_list: list,to indicate short and prefer sequence sparse field + :param num_sampled: int, the number of classes to randomly sample per batch. :param units: int, dimension for each output layer :param rnn_layers: int, layer number of rnn :param dropout_rate: float in [0,1), the probability we will drop out a given DNN coordinate. @@ -35,8 +38,8 @@ def SDM(user_feature_columns, item_feature_columns, history_feature_list, units= :param dnn_activation: Activation function to use in deep net :param init_std: float,to use as the initialize std of embedding vector :param seed: integer ,to use as random seed. - :param num_sampled: int, the number of classes to randomly sample per batch. :return: A Keras model instance. + """ if len(item_feature_columns) > 1: @@ -74,33 +77,34 @@ def SDM(user_feature_columns, item_feature_columns, history_feature_list, units= else: sparse_varlen_feature_columns.append(fc) - embedding_matrix_dict = create_embedding_matrix(user_feature_columns+item_feature_columns, l2_reg_embedding, init_std, seed, + embedding_matrix_dict = create_embedding_matrix(user_feature_columns + item_feature_columns, l2_reg_embedding, + init_std, seed, prefix="") item_features = build_input_features(item_feature_columns) item_inputs_list = list(item_features.values()) prefer_emb_list = embedding_lookup(embedding_matrix_dict, features, prefer_history_columns, prefer_fc_names, - prefer_fc_names, to_list=True) # L^u + prefer_fc_names, to_list=True) # L^u short_emb_list = embedding_lookup(embedding_matrix_dict, features, short_history_columns, short_fc_names, - short_fc_names, to_list=True) # S^u + short_fc_names, to_list=True) # S^u # dense_value_list = get_dense_input(features, dense_feature_columns) user_emb_list = embedding_lookup(embedding_matrix_dict, features, sparse_feature_columns, to_list=True) sequence_embed_dict = varlen_embedding_lookup(embedding_matrix_dict, features, sparse_varlen_feature_columns) sequence_embed_list = get_varlen_pooling_list(sequence_embed_dict, features, sparse_varlen_feature_columns, to_list=True) - user_emb_list += sequence_embed_list #e^u + user_emb_list += sequence_embed_list # e^u # if len(user_emb_list) > 0 or len(dense_value_list) > 0: # user_emb_feature = combined_dnn_input(user_emb_list, dense_value_list) user_emb = concat_func(user_emb_list) user_emb_output = Dense(units, activation=dnn_activation, name="user_emb_output")(user_emb) - prefer_sess_length = features['prefer_sess_length'] prefer_att_outputs = [] for i, prefer_emb in enumerate(prefer_emb_list): - prefer_attention_output = AttentionSequencePoolingLayer(dropout_rate=0)([user_emb_output, prefer_emb, prefer_sess_length]) + prefer_attention_output = AttentionSequencePoolingLayer(dropout_rate=0)( + [user_emb_output, prefer_emb, prefer_sess_length]) prefer_att_outputs.append(prefer_attention_output) prefer_att_concat = concat_func(prefer_att_outputs) prefer_output = Dense(units, activation=dnn_activation, name="prefer_output")(prefer_att_concat) @@ -109,19 +113,23 @@ def SDM(user_feature_columns, item_feature_columns, history_feature_list, units= short_emb_concat = concat_func(short_emb_list) short_emb_input = Dense(units, activation=dnn_activation, name="short_emb_input")(short_emb_concat) - short_rnn_output = DynamicMultiRNN(num_units=units, return_sequence=True, num_layers=rnn_layers, num_residual_layers=rnn_num_res, + short_rnn_output = DynamicMultiRNN(num_units=units, return_sequence=True, num_layers=rnn_layers, + num_residual_layers=rnn_num_res, dropout_rate=dropout_rate)([short_emb_input, short_sess_length]) - short_att_output = SelfMultiHeadAttention(num_units=units, head_num=num_head, dropout_rate=dropout_rate, future_binding=True, - use_layer_norm=True)([short_rnn_output, short_sess_length]) # [batch_size, time, num_units] + short_att_output = SelfMultiHeadAttention(num_units=units, head_num=num_head, dropout_rate=dropout_rate, + future_binding=True, + use_layer_norm=True)( + [short_rnn_output, short_sess_length]) # [batch_size, time, num_units] - short_output = UserAttention(num_units=units, activation=dnn_activation, use_res=True, dropout_rate=dropout_rate)\ + short_output = UserAttention(num_units=units, activation=dnn_activation, use_res=True, dropout_rate=dropout_rate) \ ([user_emb_output, short_att_output, short_sess_length]) gate_input = concat_func([prefer_output, short_output, user_emb_output]) gate = Dense(units, activation='sigmoid')(gate_input) - gate_output = Lambda(lambda x: tf.multiply(x[0], x[1]) + tf.multiply(1-x[0], x[2]))([gate, short_output, prefer_output]) + gate_output = Lambda(lambda x: tf.multiply(x[0], x[1]) + tf.multiply(1 - x[0], x[2]))( + [gate, short_output, prefer_output]) gate_output_reshape = Lambda(lambda x: tf.squeeze(x, 1))(gate_output) item_index = EmbeddingIndex(list(range(item_vocabulary_size)))(item_features[item_feature_name]) @@ -130,14 +138,15 @@ def SDM(user_feature_columns, item_feature_columns, history_feature_list, units= pooling_item_embedding_weight = PoolingLayer()([item_embedding_weight]) - output = SampledSoftmaxLayer(num_sampled=num_sampled)( - inputs=(pooling_item_embedding_weight, gate_output_reshape, item_features[item_feature_name])) + output = SampledSoftmaxLayer(num_sampled=num_sampled)([ + pooling_item_embedding_weight, gate_output_reshape, item_features[item_feature_name]]) model = Model(inputs=user_inputs_list + item_inputs_list, outputs=output) model.__setattr__("user_input", user_inputs_list) model.__setattr__("user_embedding", gate_output_reshape) model.__setattr__("item_input", item_inputs_list) - model.__setattr__("item_embedding", get_item_embedding(pooling_item_embedding_weight, item_features[item_feature_name])) + model.__setattr__("item_embedding", + get_item_embedding(pooling_item_embedding_weight, item_features[item_feature_name])) - return model \ No newline at end of file + return model diff --git a/deepmatch/models/youtubednn.py b/deepmatch/models/youtubednn.py index a4ada3c..f17087f 100644 --- a/deepmatch/models/youtubednn.py +++ b/deepmatch/models/youtubednn.py @@ -9,10 +9,10 @@ from deepctr.layers.utils import NoMask from tensorflow.python.keras.models import Model -from deepmatch.utils import get_item_embedding from deepmatch.layers import PoolingLayer +from deepmatch.utils import get_item_embedding from ..inputs import input_from_feature_columns -from ..layers.core import SampledSoftmaxLayer,EmbeddingIndex +from ..layers.core import SampledSoftmaxLayer, EmbeddingIndex def YoutubeDNN(user_feature_columns, item_feature_columns, num_sampled=5, @@ -57,7 +57,6 @@ def YoutubeDNN(user_feature_columns, item_feature_columns, num_sampled=5, user_dnn_out = DNN(user_dnn_hidden_units, dnn_activation, l2_reg_dnn, dnn_dropout, dnn_use_bn, seed, )(user_dnn_input) - item_index = EmbeddingIndex(list(range(item_vocabulary_size)))(item_features[item_feature_name]) item_embedding_matrix = embedding_matrix_dict[ @@ -67,13 +66,14 @@ def YoutubeDNN(user_feature_columns, item_feature_columns, num_sampled=5, pooling_item_embedding_weight = PoolingLayer()([item_embedding_weight]) output = SampledSoftmaxLayer(num_sampled=num_sampled)( - inputs=(pooling_item_embedding_weight, user_dnn_out, item_features[item_feature_name])) - model = Model(inputs=user_inputs_list + item_inputs_list , outputs=output) + [pooling_item_embedding_weight, user_dnn_out, item_features[item_feature_name]]) + model = Model(inputs=user_inputs_list + item_inputs_list, outputs=output) model.__setattr__("user_input", user_inputs_list) model.__setattr__("user_embedding", user_dnn_out) model.__setattr__("item_input", item_inputs_list) - model.__setattr__("item_embedding", get_item_embedding(pooling_item_embedding_weight, item_features[item_feature_name])) + model.__setattr__("item_embedding", + get_item_embedding(pooling_item_embedding_weight, item_features[item_feature_name])) return model diff --git a/deepmatch/utils.py b/deepmatch/utils.py index cbbe126..ad24242 100644 --- a/deepmatch/utils.py +++ b/deepmatch/utils.py @@ -19,9 +19,11 @@ import tensorflow as tf + from tensorflow.python.keras import backend as K from tensorflow.python.keras.layers import Lambda + def recall_N(y_true, y_pred, N=50): return len(set(y_pred[:N]) & set(y_true)) * 1.0 / len(y_true) diff --git a/docs/pics/SDM.jpg b/docs/pics/SDM.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ddf6ab2737e44dd4f88bc0302a040da1ee7f26b1 GIT binary patch literal 132656 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 literal 0 HcmV?d00001 diff --git a/docs/source/Examples.md b/docs/source/Examples.md index ae9440f..dc2a7f4 100644 --- a/docs/source/Examples.md +++ b/docs/source/Examples.md @@ -6,13 +6,13 @@ [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/shenweichen/DeepMatch/blob/master/examples/colab_MovieLen1M_YoutubeDNN.ipynb) -## YoutubeDNN with sampled softmax +## YoutubeDNN/MIND with sampled softmax The MovieLens data has been used for personalized tag recommendation,which contains 668, 953 tag applications of users on movies. Here is a small fraction of data include only sparse field. -![image](../pics/movielens_sample.png) +![](../pics/movielens_sample.png) This example shows how to use ``YoutubeDNN`` to solve a matching task. You can get the demo data @@ -132,13 +132,159 @@ if __name__ == "__main__": ``` +## SDM with sampled softmax + +The MovieLens data has been used for personalized tag recommendation,which +contains 668, 953 tag applications of users on movies. +Here is a small fraction of data include only sparse field. + +![](../pics/movielens_sample.png) + + +This example shows how to use ``SDM`` to solve a matching task. You can get the demo data +[movielens_sample.txt](https://github.com/shenweichen/DeepMatch/tree/master/examples/movielens_sample.txt) and run the following codes. + +```python +import pandas as pd +from deepctr.inputs import SparseFeat, VarLenSparseFeat +from preprocess import gen_data_set_sdm, gen_model_input_sdm +from sklearn.preprocessing import LabelEncoder +from tensorflow.python.keras import backend as K +from tensorflow.python.keras import optimizers +from tensorflow.python.keras.models import Model + +from deepmatch.models import SDM +from deepmatch.utils import sampledsoftmaxloss + +if __name__ == "__main__": + data = pd.read_csvdata = pd.read_csv("./movielens_sample.txt") + sparse_features = ["movie_id", "user_id", + "gender", "age", "occupation", "zip", "genres"] + SEQ_LEN_short = 5 + SEQ_LEN_prefer = 50 + + # 1.Label Encoding for sparse features,and process sequence features with `gen_date_set` and `gen_model_input` + + features = ['user_id', 'movie_id', 'gender', 'age', 'occupation', 'zip', 'genres'] + feature_max_idx = {} + for feature in features: + lbe = LabelEncoder() + data[feature] = lbe.fit_transform(data[feature]) + 1 + feature_max_idx[feature] = data[feature].max() + 1 + + user_profile = data[["user_id", "gender", "age", "occupation", "zip", "genres"]].drop_duplicates('user_id') + + item_profile = data[["movie_id"]].drop_duplicates('movie_id') + + user_profile.set_index("user_id", inplace=True) + # + # user_item_list = data.groupby("user_id")['movie_id'].apply(list) + + train_set, test_set = gen_data_set_sdm(data, seq_short_len=SEQ_LEN_short, seq_prefer_len=SEQ_LEN_prefer) + + train_model_input, train_label = gen_model_input_sdm(train_set, user_profile, SEQ_LEN_short, SEQ_LEN_prefer) + test_model_input, test_label = gen_model_input_sdm(test_set, user_profile, SEQ_LEN_short, SEQ_LEN_prefer) + + # 2.count #unique features for each sparse field and generate feature config for sequence feature + + embedding_dim = 32 + # for sdm,we must provide `VarLenSparseFeat` with name "prefer_xxx" and "short_xxx" and their length + user_feature_columns = [SparseFeat('user_id', feature_max_idx['user_id'], 16), + SparseFeat("gender", feature_max_idx['gender'], 16), + SparseFeat("age", feature_max_idx['age'], 16), + SparseFeat("occupation", feature_max_idx['occupation'], 16), + SparseFeat("zip", feature_max_idx['zip'], 16), + VarLenSparseFeat(SparseFeat('short_movie_id', feature_max_idx['movie_id'], embedding_dim, + embedding_name="movie_id"), SEQ_LEN_short, 'mean', + 'short_sess_length'), + VarLenSparseFeat(SparseFeat('prefer_movie_id', feature_max_idx['movie_id'], embedding_dim, + embedding_name="movie_id"), SEQ_LEN_prefer, 'mean', + 'prefer_sess_length'), + VarLenSparseFeat(SparseFeat('short_genres', feature_max_idx['genres'], embedding_dim, + embedding_name="genres"), SEQ_LEN_short, 'mean', + 'short_sess_length'), + VarLenSparseFeat(SparseFeat('prefer_genres', feature_max_idx['genres'], embedding_dim, + embedding_name="genres"), SEQ_LEN_prefer, 'mean', + 'prefer_sess_length'), + ] + + item_feature_columns = [SparseFeat('movie_id', feature_max_idx['movie_id'], embedding_dim)] + + K.set_learning_phase(True) + + import tensorflow as tf + + if tf.__version__ >= '2.0.0': + tf.compat.v1.disable_eager_execution() + + # units must be equal to item embedding dim! + model = SDM(user_feature_columns, item_feature_columns, history_feature_list=['movie_id', 'genres'], + units=embedding_dim, num_sampled=100, ) + + optimizer = optimizers.Adam(lr=0.001, clipnorm=5.0) + + model.compile(optimizer=optimizer, loss=sampledsoftmaxloss) # "binary_crossentropy") + + history = model.fit(train_model_input, train_label, # train_label, + batch_size=512, epochs=1, verbose=1, validation_split=0.0, ) + # model.save_weights('SDM_weights.h5') + + K.set_learning_phase(False) + # 4. Generate user features for testing and full item features for retrieval + test_user_model_input = test_model_input + all_item_model_input = {"movie_id": item_profile['movie_id'].values, } + + user_embedding_model = Model(inputs=model.user_input, outputs=model.user_embedding) + item_embedding_model = Model(inputs=model.item_input, outputs=model.item_embedding) + + user_embs = user_embedding_model.predict(test_user_model_input, batch_size=2 ** 12) + # user_embs = user_embs[:, i, :] # i in [0,k_max) if MIND + item_embs = item_embedding_model.predict(all_item_model_input, batch_size=2 ** 12) + + print(user_embs.shape) + print(item_embs.shape) + + # 5. [Optional] ANN search by faiss and evaluate the result + + # test_true_label = {line[0]: [line[3]] for line in test_set} + # + # import numpy as np + # import faiss + # from tqdm import tqdm + # from deepmatch.utils import recall_N + # + # index = faiss.IndexFlatIP(embedding_dim) + # # faiss.normalize_L2(item_embs) + # index.add(item_embs) + # # faiss.normalize_L2(user_embs) + # D, I = index.search(np.ascontiguousarray(user_embs), 50) + # s = [] + # hit = 0 + # for i, uid in tqdm(enumerate(test_user_model_input['user_id'])): + # try: + # pred = [item_profile['movie_id'].values[x] for x in I[i]] + # filter_item = None + # recall_score = recall_N(test_true_label[uid], pred, N=50) + # s.append(recall_score) + # if test_true_label[uid] in pred: + # hit += 1 + # except: + # print(i) + # print("") + # print("recall", np.mean(s)) + # print("hit rate", hit / len(test_user_model_input['user_id'])) + + +``` + + ## DSSM with negative sampling The MovieLens data has been used for personalized tag recommendation,which contains 668, 953 tag applications of users on movies. Here is a small fraction of data include only sparse field. -![image](../pics/movielens_sample.png) +![](../pics/movielens_sample.png) This example shows how to use ``DSSM`` to solve a matching task. You can get the demo data diff --git a/docs/source/Features.md b/docs/source/Features.md index 904854c..d7acce9 100644 --- a/docs/source/Features.md +++ b/docs/source/Features.md @@ -69,6 +69,19 @@ [Neural Collaborative Filtering](https://arxiv.org/abs/1708.05031) +### SDM (Sequential Deep Matching Model) + + + +[**SDM Model API**](./deepmatch.models.sdm.html) + +![SDM](../pics/sdm.jpg) + +[SDM example](https://github.com/shenweichen/DeepMatch/tree/master/examples/run_sdm.py) + +[SDM: Sequential Deep Matching Model for Online Large-scale Recommender System](https://arxiv.org/abs/1909.00385) + + ### MIND (Multi-Interest Network with Dynamic routing) diff --git a/docs/source/History.md b/docs/source/History.md index a4279aa..1645b66 100644 --- a/docs/source/History.md +++ b/docs/source/History.md @@ -1,3 +1,4 @@ # History +- 05/17/2020 : [v0.1.3](https://github.com/shenweichen/DeepMatch/releases/tag/v0.1.3) released.Add `SDM` model . - 04/10/2020 : [v0.1.2](https://github.com/shenweichen/DeepMatch/releases/tag/v0.1.2) released.Support [saving and loading model](./FAQ.html#save-or-load-weights-models). - 04/06/2020 : DeepMatch first version is released on [PyPi](https://pypi.org/project/deepmatch/) \ No newline at end of file diff --git a/docs/source/Models.rst b/docs/source/Models.rst index 1dc95be..4ac464f 100644 --- a/docs/source/Models.rst +++ b/docs/source/Models.rst @@ -7,6 +7,7 @@ DeepMatch Models API DSSM YoutubeDNN NCF + SDM MIND \ No newline at end of file diff --git a/docs/source/Quick-Start.md b/docs/source/Quick-Start.md index f370263..3053598 100644 --- a/docs/source/Quick-Start.md +++ b/docs/source/Quick-Start.md @@ -17,5 +17,6 @@ $ pip install deepmatch[gpu] ## Run examples !! - [Run YoutubeDNN on MovieLen1M on Google colab](https://colab.research.google.com/github/shenweichen/DeepMatch/blob/dev_shenweichen/examples/colab_MovieLen1M_YoutubeDNN.ipynb) +- [YoutubeDNN/MIND with sampled softmax](./Examples.html#youtubednn-mind-with-sampled-softmax) +- [SDM with sampled softmax](./Examples.html#sdm-with-sampled-softmax) - [DSSM with negative sampling](./Examples.html#dssm-with-negative-sampling) -- [YoutubeDNN with sampled softmax](./Examples.html#youtubednn-with-sampled-softmax) \ No newline at end of file diff --git a/docs/source/conf.py b/docs/source/conf.py index bbff490..0a25c61 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -26,7 +26,7 @@ # The short X.Y version version = '' # The full version, including alpha/beta/rc tags -release = '0.1.2' +release = '0.1.3' # -- General configuration --------------------------------------------------- diff --git a/docs/source/deepmatch.models.rst b/docs/source/deepmatch.models.rst index 0ff1641..fb069ed 100644 --- a/docs/source/deepmatch.models.rst +++ b/docs/source/deepmatch.models.rst @@ -10,6 +10,7 @@ Submodules deepmatch.models.fm deepmatch.models.mind deepmatch.models.ncf + deepmatch.models.sdm deepmatch.models.youtubednn Module contents diff --git a/docs/source/deepmatch.models.sdm.rst b/docs/source/deepmatch.models.sdm.rst new file mode 100644 index 0000000..11612ba --- /dev/null +++ b/docs/source/deepmatch.models.sdm.rst @@ -0,0 +1,7 @@ +deepmatch.models.sdm module +============================ + +.. automodule:: deepmatch.models.sdm + :members: + :no-undoc-members: + :no-show-inheritance: diff --git a/docs/source/index.rst b/docs/source/index.rst index bb0f6ac..a774fe7 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -18,6 +18,8 @@ You can read the latest code at https://github.com/shenweichen/DeepMatch News ----- +05/17/2020 : Add ``SDM`` model. `Changelog `_ + 04/10/2020 : Support `saving and loading model <./FAQ.html#save-or-load-weights-models>`_ . `Changelog `_ 04/06/2020 : DeepMatch first version . diff --git a/examples/run_sdm_sampledsoftmax.py b/examples/run_sdm.py similarity index 88% rename from examples/run_sdm_sampledsoftmax.py rename to examples/run_sdm.py index ad5c24b..e409414 100644 --- a/examples/run_sdm_sampledsoftmax.py +++ b/examples/run_sdm.py @@ -1,10 +1,10 @@ import pandas as pd from deepctr.inputs import SparseFeat, VarLenSparseFeat -from examples.preprocess import gen_data_set_sdm, gen_model_input_sdm +from preprocess import gen_data_set_sdm, gen_model_input_sdm from sklearn.preprocessing import LabelEncoder from tensorflow.python.keras import backend as K -from tensorflow.python.keras.models import Model from tensorflow.python.keras import optimizers +from tensorflow.python.keras.models import Model from deepmatch.models import SDM from deepmatch.utils import sampledsoftmaxloss @@ -41,20 +41,24 @@ # 2.count #unique features for each sparse field and generate feature config for sequence feature embedding_dim = 32 - + # for sdm,we must provide `VarLenSparseFeat` with name "prefer_xxx" and "short_xxx" and their length user_feature_columns = [SparseFeat('user_id', feature_max_idx['user_id'], 16), SparseFeat("gender", feature_max_idx['gender'], 16), SparseFeat("age", feature_max_idx['age'], 16), SparseFeat("occupation", feature_max_idx['occupation'], 16), SparseFeat("zip", feature_max_idx['zip'], 16), VarLenSparseFeat(SparseFeat('short_movie_id', feature_max_idx['movie_id'], embedding_dim, - embedding_name="movie_id"), SEQ_LEN_short, 'mean', 'short_sess_length'), + embedding_name="movie_id"), SEQ_LEN_short, 'mean', + 'short_sess_length'), VarLenSparseFeat(SparseFeat('prefer_movie_id', feature_max_idx['movie_id'], embedding_dim, - embedding_name="movie_id"), SEQ_LEN_prefer, 'mean', 'prefer_sess_length'), + embedding_name="movie_id"), SEQ_LEN_prefer, 'mean', + 'prefer_sess_length'), VarLenSparseFeat(SparseFeat('short_genres', feature_max_idx['genres'], embedding_dim, - embedding_name="genres"), SEQ_LEN_short, 'mean', 'short_sess_length'), + embedding_name="genres"), SEQ_LEN_short, 'mean', + 'short_sess_length'), VarLenSparseFeat(SparseFeat('prefer_genres', feature_max_idx['genres'], embedding_dim, - embedding_name="genres"), SEQ_LEN_prefer, 'mean', 'prefer_sess_length'), + embedding_name="genres"), SEQ_LEN_prefer, 'mean', + 'prefer_sess_length'), ] item_feature_columns = [SparseFeat('movie_id', feature_max_idx['movie_id'], embedding_dim)] @@ -67,13 +71,13 @@ tf.compat.v1.disable_eager_execution() # units must be equal to item embedding dim! - model = SDM(user_feature_columns, item_feature_columns, history_feature_list=['movie_id', 'genres'], units=embedding_dim, num_sampled=100,) + model = SDM(user_feature_columns, item_feature_columns, history_feature_list=['movie_id', 'genres'], + units=embedding_dim, num_sampled=100, ) - # 梯度裁剪 optimizer = optimizers.Adam(lr=0.001, clipnorm=5.0) model.compile(optimizer=optimizer, loss=sampledsoftmaxloss) # "binary_crossentropy") - model.summary() + history = model.fit(train_model_input, train_label, # train_label, batch_size=512, epochs=1, verbose=1, validation_split=0.0, ) # model.save_weights('SDM_weights.h5') @@ -119,4 +123,4 @@ # print(i) # print("") # print("recall", np.mean(s)) - # print("hit rate", hit / len(test_user_model_input['user_id'])) \ No newline at end of file + # print("hit rate", hit / len(test_user_model_input['user_id'])) diff --git a/examples/run_youtubednn_sampledsoftmax.py b/examples/run_youtubednn.py similarity index 98% rename from examples/run_youtubednn_sampledsoftmax.py rename to examples/run_youtubednn.py index b5fe42a..aeb36c0 100644 --- a/examples/run_youtubednn_sampledsoftmax.py +++ b/examples/run_youtubednn.py @@ -14,7 +14,6 @@ sparse_features = ["movie_id", "user_id", "gender", "age", "occupation", "zip", ] SEQ_LEN = 50 - negsample = 0 # 1.Label Encoding for sparse features,and process sequence features with `gen_date_set` and `gen_model_input` @@ -33,7 +32,7 @@ user_item_list = data.groupby("user_id")['movie_id'].apply(list) - train_set, test_set = gen_data_set(data, negsample) + train_set, test_set = gen_data_set(data, 0) train_model_input, train_label = gen_model_input(train_set, user_profile, SEQ_LEN) test_model_input, test_label = gen_model_input(test_set, user_profile, SEQ_LEN) diff --git a/setup.py b/setup.py index d0b822b..fa915ee 100644 --- a/setup.py +++ b/setup.py @@ -4,12 +4,12 @@ long_description = fh.read() REQUIRED_PACKAGES = [ - 'h5py','requests',"deepctr==0.7.4" + 'h5py','requests',"deepctr==0.7.5" ] setuptools.setup( name="deepmatch", - version="0.1.2", + version="0.1.3", author="Weichen Shen", author_email="wcshen1994@163.com", description="Deep matching model library for recommendations, advertising. It's easy to train models and to **export representation vectors** for user and item which can be used for **ANN search**.", diff --git a/tests/models/SDM_test.py b/tests/models/SDM_test.py index 401a79f..b4caf9b 100644 --- a/tests/models/SDM_test.py +++ b/tests/models/SDM_test.py @@ -18,8 +18,8 @@ def test_SDM(): if tf.__version__ >= '2.0.0': tf.compat.v1.disable_eager_execution() - model = SDM(user_feature_columns, item_feature_columns, history_feature_list, units=64) - model.summary() + model = SDM(user_feature_columns, item_feature_columns, history_feature_list, units=8) + #model.summary() model.compile('adam', sampledsoftmaxloss) check_model(model, model_name, x, y) diff --git a/tests/utils.py b/tests/utils.py index e01a58f..08be5fb 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -338,7 +338,7 @@ def check_model(model, model_name, x, y, check_model_io=True): :return: """ - model.fit(x, y, batch_size=10, epochs=10, validation_split=0.5) + model.fit(x, y, batch_size=10, epochs=2, validation_split=0.5) @@ -396,16 +396,16 @@ def get_xy_fd_sdm(hash_flag=False): user_feature_columns = [SparseFeat('user',3), SparseFeat('gender', 2), - VarLenSparseFeat(SparseFeat('prefer_item', vocabulary_size=100,embedding_dim=64, + VarLenSparseFeat(SparseFeat('prefer_item', vocabulary_size=100,embedding_dim=8, embedding_name='item'), maxlen=6, length_name="prefer_sess_length"), - VarLenSparseFeat(SparseFeat('prefer_cate', vocabulary_size=100, embedding_dim=64, + VarLenSparseFeat(SparseFeat('prefer_cate', vocabulary_size=100, embedding_dim=8, embedding_name='cate'), maxlen=6, length_name="prefer_sess_length"), - VarLenSparseFeat(SparseFeat('short_item', vocabulary_size=100,embedding_dim=64, + VarLenSparseFeat(SparseFeat('short_item', vocabulary_size=100,embedding_dim=8, embedding_name='item'), maxlen=4, length_name="short_sess_length"), - VarLenSparseFeat(SparseFeat('short_cate', vocabulary_size=100, embedding_dim=64, + VarLenSparseFeat(SparseFeat('short_cate', vocabulary_size=100, embedding_dim=8, embedding_name='cate'), maxlen=4, length_name="short_sess_length"), ] - item_feature_columns = [SparseFeat('item', 100, embedding_dim=64,)] + item_feature_columns = [SparseFeat('item', 100, embedding_dim=8,)] uid = np.array([0, 1, 2, 1])