From f9f5bb7208448dccd835793f3fc5078eb494d061 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B5=85=E6=A2=A6?= Date: Mon, 12 Oct 2020 22:23:54 +0800 Subject: [PATCH] v0.2.0 Support different initializers for different embedding weights and loading pretrained embeddings --- .github/ISSUE_TEMPLATE/bug_report.md | 6 +- .github/ISSUE_TEMPLATE/question.md | 2 +- .github/workflows/ci.yml | 2 +- README.md | 2 +- deepmatch/__init__.py | 2 +- deepmatch/inputs.py | 15 ++-- deepmatch/layers/__init__.py | 31 +++---- deepmatch/layers/core.py | 17 ++-- deepmatch/layers/sequence.py | 14 +-- deepmatch/models/dssm.py | 17 ++-- deepmatch/models/fm.py | 11 ++- deepmatch/models/mind.py | 21 +++-- deepmatch/models/ncf.py | 27 +++--- deepmatch/models/sdm.py | 27 ++++-- deepmatch/models/youtubednn.py | 21 +++-- docs/pics/code.png | Bin 0 -> 29368 bytes docs/pics/weichennote.png | Bin 36604 -> 70228 bytes docs/source/Examples.md | 4 +- docs/source/Features.md | 7 +- docs/source/History.md | 1 + docs/source/conf.py | 2 +- docs/source/index.rst | 6 +- examples/colab_MovieLen1M_YoutubeDNN.ipynb | 4 +- examples/preprocess.py | 4 +- examples/run_dssm_negsampling.py | 2 +- examples/run_ncf.py | 56 ++++++++++++ examples/run_sdm.py | 9 +- examples/run_youtubednn.py | 4 +- setup.py | 8 +- tests/models/DSSM_test.py | 3 - tests/models/FM_test.py | 4 - tests/models/MIND_test.py | 15 ++-- tests/models/NCF_test.py | 16 ++++ tests/models/SDM_test.py | 16 ++-- tests/models/YoutubeDNN_test.py | 16 ++-- tests/utils.py | 95 ++++++++++++++------- 36 files changed, 293 insertions(+), 194 deletions(-) create mode 100644 docs/pics/code.png create mode 100644 examples/run_ncf.py create mode 100644 tests/models/NCF_test.py diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 6d86640..f660b34 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -18,9 +18,9 @@ Steps to reproduce the behavior: 4. See error **Operating environment(运行环境):** - - python version [e.g. 3.4, 3.6] - - tensorflow version [e.g. 1.4.0, 1.12.0] - - deepmatch version [e.g. 0.1.1,] + - python version [e.g. 3.6, 3.7] + - tensorflow version [e.g. 1.4.0, 1.14.0, 2.3.0] + - deepmatch version [e.g. 0.2.0,] **Additional context** Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/question.md b/.github/ISSUE_TEMPLATE/question.md index 53e8451..f6d65c0 100644 --- a/.github/ISSUE_TEMPLATE/question.md +++ b/.github/ISSUE_TEMPLATE/question.md @@ -17,4 +17,4 @@ Add any other context about the problem here. **Operating environment(运行环境):** - python version [e.g. 3.6] - tensorflow version [e.g. 1.4.0,] - - deepmatch version [e.g. 0.1.1,] + - deepmatch version [e.g. 0.2.0,] diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ee1b5bc..275b40d 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,2.2.0] + tf-version: [1.4.0,1.14.0,2.1.0,2.2.0,2.3.0] exclude: - python-version: 3.7 diff --git a/README.md b/README.md index 74a346b..fc6d721 100644 --- a/README.md +++ b/README.md @@ -67,4 +67,4 @@ Please follow our wechat to join group: - 公众号:**浅梦的学习笔记** - wechat ID: **deepctrbot** - ![wechat](./docs/pics/weichennote.png) + ![wechat](./docs/pics/code.png) diff --git a/deepmatch/__init__.py b/deepmatch/__init__.py index b1af8c4..0fa3e17 100644 --- a/deepmatch/__init__.py +++ b/deepmatch/__init__.py @@ -1,4 +1,4 @@ from .utils import check_version -__version__ = '0.1.3' +__version__ = '0.2.0' check_version(__version__) diff --git a/deepmatch/inputs.py b/deepmatch/inputs.py index b37357a..fbb2d79 100644 --- a/deepmatch/inputs.py +++ b/deepmatch/inputs.py @@ -1,14 +1,17 @@ from itertools import chain -from deepctr.inputs import SparseFeat,VarLenSparseFeat,create_embedding_matrix,embedding_lookup,get_dense_input,varlen_embedding_lookup,get_varlen_pooling_list,mergeDict -def input_from_feature_columns(features, feature_columns, l2_reg, init_std, seed, prefix='', seq_mask_zero=True, - support_dense=True, support_group=False,embedding_matrix_dict=None): +from deepctr.feature_column import SparseFeat, VarLenSparseFeat, create_embedding_matrix, embedding_lookup, \ + get_dense_input, varlen_embedding_lookup, get_varlen_pooling_list, mergeDict + + +def input_from_feature_columns(features, feature_columns, l2_reg, seed, prefix='', seq_mask_zero=True, + support_dense=True, support_group=False, embedding_matrix_dict=None): sparse_feature_columns = list( filter(lambda x: isinstance(x, SparseFeat), feature_columns)) if feature_columns else [] varlen_sparse_feature_columns = list( filter(lambda x: isinstance(x, VarLenSparseFeat), feature_columns)) if feature_columns else [] - if embedding_matrix_dict is None: - embedding_matrix_dict = create_embedding_matrix(feature_columns, l2_reg, init_std, seed, prefix=prefix, + if embedding_matrix_dict is None: + embedding_matrix_dict = create_embedding_matrix(feature_columns, l2_reg, seed, prefix=prefix, seq_mask_zero=seq_mask_zero) group_sparse_embedding_dict = embedding_lookup(embedding_matrix_dict, features, sparse_feature_columns) @@ -22,4 +25,4 @@ def input_from_feature_columns(features, feature_columns, l2_reg, init_std, seed group_embedding_dict = mergeDict(group_sparse_embedding_dict, group_varlen_sparse_embedding_dict) if not support_group: group_embedding_dict = list(chain.from_iterable(group_embedding_dict.values())) - return group_embedding_dict, dense_value_list \ No newline at end of file + return group_embedding_dict, dense_value_list diff --git a/deepmatch/layers/__init__.py b/deepmatch/layers/__init__.py index bd94afe..854c596 100644 --- a/deepmatch/layers/__init__.py +++ b/deepmatch/layers/__init__.py @@ -1,28 +1,29 @@ from deepctr.layers import custom_objects from deepctr.layers.utils import reduce_sum -from .core import PoolingLayer, Similarity, LabelAwareAttention, CapsuleLayer,SampledSoftmaxLayer,EmbeddingIndex -from ..utils import sampledsoftmaxloss -from .interaction import DotAttention, ConcatAttention, SoftmaxWeightedSum, AttentionSequencePoolingLayer, SelfAttention,\ +from .core import PoolingLayer, Similarity, LabelAwareAttention, CapsuleLayer, SampledSoftmaxLayer, EmbeddingIndex +from .interaction import DotAttention, ConcatAttention, SoftmaxWeightedSum, AttentionSequencePoolingLayer, \ + SelfAttention, \ SelfMultiHeadAttention, UserAttention from .sequence import DynamicMultiRNN +from ..utils import sampledsoftmaxloss _custom_objects = {'PoolingLayer': PoolingLayer, 'Similarity': Similarity, 'LabelAwareAttention': LabelAwareAttention, 'CapsuleLayer': CapsuleLayer, - 'reduce_sum':reduce_sum, - 'SampledSoftmaxLayer':SampledSoftmaxLayer, - 'sampledsoftmaxloss':sampledsoftmaxloss, - 'EmbeddingIndex':EmbeddingIndex, - 'DotAttention':DotAttention, - 'ConcatAttention':ConcatAttention, - 'SoftmaxWeightedSum':SoftmaxWeightedSum, - 'AttentionSequencePoolingLayer':AttentionSequencePoolingLayer, - 'SelfAttention':SelfAttention, - 'SelfMultiHeadAttention':SelfMultiHeadAttention, - 'UserAttention':UserAttention, - 'DynamicMultiRNN':DynamicMultiRNN + 'reduce_sum': reduce_sum, + 'SampledSoftmaxLayer': SampledSoftmaxLayer, + 'sampledsoftmaxloss': sampledsoftmaxloss, + 'EmbeddingIndex': EmbeddingIndex, + 'DotAttention': DotAttention, + 'ConcatAttention': ConcatAttention, + 'SoftmaxWeightedSum': SoftmaxWeightedSum, + 'AttentionSequencePoolingLayer': AttentionSequencePoolingLayer, + 'SelfAttention': SelfAttention, + 'SelfMultiHeadAttention': SelfMultiHeadAttention, + 'UserAttention': UserAttention, + 'DynamicMultiRNN': DynamicMultiRNN } custom_objects = dict(custom_objects, **_custom_objects) diff --git a/deepmatch/layers/core.py b/deepmatch/layers/core.py index 293b885..2bdd7c2 100644 --- a/deepmatch/layers/core.py +++ b/deepmatch/layers/core.py @@ -1,7 +1,9 @@ import tensorflow as tf +from deepctr.layers.activation import activation_layer from deepctr.layers.utils import reduce_max, reduce_mean, reduce_sum, concat_func, div, softmax -from tensorflow.python.keras.initializers import RandomNormal, Zeros +from tensorflow.python.keras.initializers import RandomNormal, Zeros, glorot_normal from tensorflow.python.keras.layers import Layer +from tensorflow.python.keras.regularizers import l2 class PoolingLayer(Layer): @@ -216,21 +218,20 @@ def squash(inputs): return vec_squashed - - class EmbeddingIndex(Layer): - def __init__(self, index,**kwargs): - self.index =index + def __init__(self, index, **kwargs): + self.index = index super(EmbeddingIndex, self).__init__(**kwargs) def build(self, input_shape): - super(EmbeddingIndex, self).build( input_shape) # Be sure to call this somewhere! + def call(self, x, **kwargs): - return tf.constant(self.index) + return tf.constant(self.index) + def get_config(self, ): config = {'index': self.index, } base_config = super(EmbeddingIndex, self).get_config() - return dict(list(base_config.items()) + list(config.items())) \ No newline at end of file + return dict(list(base_config.items()) + list(config.items())) diff --git a/deepmatch/layers/sequence.py b/deepmatch/layers/sequence.py index a3bffc7..10e12b9 100644 --- a/deepmatch/layers/sequence.py +++ b/deepmatch/layers/sequence.py @@ -24,19 +24,19 @@ def build(self, input_shape): if self.rnn_type == "LSTM": try: single_cell = tf.nn.rnn_cell.BasicLSTMCell(self.num_units, forget_bias=self.forget_bias) - except: + except AttributeError: single_cell = tf.compat.v1.nn.rnn_cell.BasicLSTMCell(self.num_units, forget_bias=self.forget_bias) elif self.rnn_type == "GRU": try: single_cell = tf.nn.rnn_cell.GRUCell(self.num_units, forget_bias=self.forget_bias) - except: + except AttributeError: single_cell = tf.compat.v1.nn.rnn_cell.GRUCell(self.num_units, forget_bias=self.forget_bias) else: raise ValueError("Unknown unit type %s!" % self.rnn_type) dropout = self.dropout if tf.keras.backend.learning_phase() == 1 else 0 try: single_cell = tf.nn.rnn_cell.DropoutWrapper(cell=single_cell, input_keep_prob=(1.0 - dropout)) - except: + except AttributeError: single_cell = tf.compat.v1.nn.rnn_cell.DropoutWrapper(cell=single_cell, input_keep_prob=(1.0 - dropout)) cell_list = [] for i in range(self.num_layers): @@ -44,7 +44,7 @@ def build(self, input_shape): if residual: try: single_cell_residual = tf.nn.rnn_cell.ResidualWrapper(single_cell) - except: + except AttributeError: single_cell_residual = tf.compat.v1.nn.rnn_cell.ResidualWrapper(single_cell) cell_list.append(single_cell_residual) else: @@ -54,7 +54,7 @@ def build(self, input_shape): else: try: self.final_cell = tf.nn.rnn_cell.MultiRNNCell(cell_list) - except: + except AttributeError: self.final_cell = tf.compat.v1.nn.rnn_cell.MultiRNNCell(cell_list) super(DynamicMultiRNN, self).build(input_shape) @@ -66,7 +66,7 @@ def call(self, input_list, mask=None, training=None): 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: + except AttributeError: 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), @@ -86,6 +86,6 @@ def compute_output_shape(self, input_shape): 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, - 'num_residual_layers': self.num_residual_layers, 'dropout_rate': self.dropout} + 'num_residual_layers': self.num_residual_layers, 'dropout_rate': self.dropout, 'forget_bias':self.forget_bias} base_config = super(DynamicMultiRNN, self).get_config() return dict(list(base_config.items()) + list(config.items())) diff --git a/deepmatch/models/dssm.py b/deepmatch/models/dssm.py index 74ac3b1..d1bd455 100644 --- a/deepmatch/models/dssm.py +++ b/deepmatch/models/dssm.py @@ -5,8 +5,8 @@ Huang P S , He X , Gao J , et al. Learning deep structured semantic models for web search using clickthrough data[C]// Acm International Conference on Conference on Information & Knowledge Management. ACM, 2013. """ -from deepctr.inputs import build_input_features, combined_dnn_input, create_embedding_matrix -from deepctr.layers.core import PredictionLayer, DNN +from deepctr.feature_column import build_input_features, create_embedding_matrix +from deepctr.layers import PredictionLayer, DNN, combined_dnn_input from tensorflow.python.keras.models import Model from ..inputs import input_from_feature_columns @@ -16,7 +16,7 @@ def DSSM(user_feature_columns, item_feature_columns, user_dnn_hidden_units=(64, 32), item_dnn_hidden_units=(64, 32), dnn_activation='tanh', dnn_use_bn=False, - l2_reg_dnn=0, l2_reg_embedding=1e-6, dnn_dropout=0, init_std=0.0001, seed=1024, metric='cos'): + l2_reg_dnn=0, l2_reg_embedding=1e-6, dnn_dropout=0, seed=1024, metric='cos'): """Instantiates the Deep Structured Semantic Model architecture. :param user_feature_columns: An iterable containing user's features used by the model. @@ -28,7 +28,6 @@ def DSSM(user_feature_columns, item_feature_columns, user_dnn_hidden_units=(64, :param l2_reg_dnn: float. L2 regularizer strength applied to DNN :param l2_reg_embedding: float. L2 regularizer strength applied to embedding vector :param dnn_dropout: float in [0,1), the probability we will drop out a given DNN coordinate. - :param init_std: float,to use as the initialize std of embedding vector :param seed: integer ,to use as random seed. :param metric: str, ``"cos"`` for cosine or ``"ip"`` for inner product :return: A Keras model instance. @@ -36,14 +35,14 @@ def DSSM(user_feature_columns, item_feature_columns, user_dnn_hidden_units=(64, """ embedding_matrix_dict = create_embedding_matrix(user_feature_columns + item_feature_columns, l2_reg_embedding, - init_std, seed, + seed=seed, seq_mask_zero=True) user_features = build_input_features(user_feature_columns) user_inputs_list = list(user_features.values()) user_sparse_embedding_list, user_dense_value_list = input_from_feature_columns(user_features, user_feature_columns, - l2_reg_embedding, init_std, seed, + l2_reg_embedding, seed=seed, embedding_matrix_dict=embedding_matrix_dict) user_dnn_input = combined_dnn_input(user_sparse_embedding_list, user_dense_value_list) @@ -51,15 +50,15 @@ def DSSM(user_feature_columns, item_feature_columns, user_dnn_hidden_units=(64, item_inputs_list = list(item_features.values()) item_sparse_embedding_list, item_dense_value_list = input_from_feature_columns(item_features, item_feature_columns, - l2_reg_embedding, init_std, seed, + l2_reg_embedding, seed=seed, embedding_matrix_dict=embedding_matrix_dict) item_dnn_input = combined_dnn_input(item_sparse_embedding_list, item_dense_value_list) user_dnn_out = DNN(user_dnn_hidden_units, dnn_activation, l2_reg_dnn, dnn_dropout, - dnn_use_bn, seed, )(user_dnn_input) + dnn_use_bn, seed=seed)(user_dnn_input) item_dnn_out = DNN(item_dnn_hidden_units, dnn_activation, l2_reg_dnn, dnn_dropout, - dnn_use_bn, seed)(item_dnn_input) + dnn_use_bn, seed=seed)(item_dnn_input) score = Similarity(type=metric)([user_dnn_out, item_dnn_out]) diff --git a/deepmatch/models/fm.py b/deepmatch/models/fm.py index 3b5fa91..97bf339 100644 --- a/deepmatch/models/fm.py +++ b/deepmatch/models/fm.py @@ -1,4 +1,4 @@ -from deepctr.inputs import build_input_features +from deepctr.feature_column import build_input_features from deepctr.layers.core import PredictionLayer from deepctr.layers.utils import concat_func, reduce_sum from tensorflow.python.keras.layers import Lambda @@ -8,13 +8,12 @@ from ..layers.core import Similarity -def FM(user_feature_columns, item_feature_columns, l2_reg_embedding=1e-6, init_std=0.0001, seed=1024, metric='cos'): +def FM(user_feature_columns, item_feature_columns, l2_reg_embedding=1e-6, seed=1024, metric='cos'): """Instantiates the FM 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 l2_reg_embedding: float. L2 regularizer strength applied to embedding vector - :param init_std: float,to use as the initialize std of embedding vector :param seed: integer ,to use as random seed. :param metric: str, ``"cos"`` for cosine or ``"ip"`` for inner product :return: A Keras model instance. @@ -22,14 +21,14 @@ def FM(user_feature_columns, item_feature_columns, l2_reg_embedding=1e-6, init_s """ embedding_matrix_dict = create_embedding_matrix(user_feature_columns + item_feature_columns, l2_reg_embedding, - init_std, seed, + seed=seed, seq_mask_zero=True) user_features = build_input_features(user_feature_columns) user_inputs_list = list(user_features.values()) user_sparse_embedding_list, user_dense_value_list = input_from_feature_columns(user_features, user_feature_columns, - l2_reg_embedding, init_std, seed, + l2_reg_embedding, seed=seed, support_dense=False, embedding_matrix_dict=embedding_matrix_dict) @@ -37,7 +36,7 @@ def FM(user_feature_columns, item_feature_columns, l2_reg_embedding=1e-6, init_s item_inputs_list = list(item_features.values()) item_sparse_embedding_list, item_dense_value_list = input_from_feature_columns(item_features, item_feature_columns, - l2_reg_embedding, init_std, seed, + l2_reg_embedding, seed=seed, support_dense=False, embedding_matrix_dict=embedding_matrix_dict) diff --git a/deepmatch/models/mind.py b/deepmatch/models/mind.py index ddd1c2c..3053dd1 100755 --- a/deepmatch/models/mind.py +++ b/deepmatch/models/mind.py @@ -7,11 +7,10 @@ """ import tensorflow as tf -from deepctr.inputs import SparseFeat, VarLenSparseFeat, DenseFeat, \ - embedding_lookup, varlen_embedding_lookup, get_varlen_pooling_list, get_dense_input, build_input_features, \ - combined_dnn_input -from deepctr.layers.core import DNN -from deepctr.layers.utils import NoMask +from deepctr.feature_column import SparseFeat, VarLenSparseFeat, DenseFeat, \ + embedding_lookup, varlen_embedding_lookup, get_varlen_pooling_list, get_dense_input, build_input_features +from deepctr.layers import DNN +from deepctr.layers.utils import NoMask, combined_dnn_input from tensorflow.python.keras.layers import Concatenate from tensorflow.python.keras.models import Model @@ -30,8 +29,7 @@ def tile_user_otherfeat(user_other_feature, k_max): def MIND(user_feature_columns, item_feature_columns, num_sampled=5, k_max=2, p=1.0, dynamic_k=False, user_dnn_hidden_units=(64, 32), dnn_activation='relu', dnn_use_bn=False, l2_reg_dnn=0, l2_reg_embedding=1e-6, - dnn_dropout=0, - init_std=0.0001, seed=1024): + dnn_dropout=0, output_activation='linear', seed=1024): """Instantiates the MIND Model architecture. :param user_feature_columns: An iterable containing user's features used by the model. @@ -47,8 +45,8 @@ def MIND(user_feature_columns, item_feature_columns, num_sampled=5, k_max=2, p=1 :param l2_reg_dnn: L2 regularizer strength applied to DNN :param l2_reg_embedding: float. L2 regularizer strength applied to embedding vector :param dnn_dropout: float in [0,1), the probability we will drop out a given DNN coordinate. - :param init_std: float,to use as the initialize std of embedding vector :param seed: integer ,to use as random seed. + :param output_activation: Activation function to use in output layer :return: A Keras model instance. """ @@ -83,8 +81,7 @@ def MIND(user_feature_columns, item_feature_columns, num_sampled=5, k_max=2, p=1 inputs_list = list(features.values()) embedding_matrix_dict = create_embedding_matrix(user_feature_columns + item_feature_columns, l2_reg_embedding, - init_std, - seed, prefix="") + seed=seed, prefix="") item_features = build_input_features(item_feature_columns) @@ -127,7 +124,9 @@ def MIND(user_feature_columns, item_feature_columns, num_sampled=5, k_max=2, p=1 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) + dnn_dropout, dnn_use_bn, output_activation=output_activation, seed=seed, + name="user_embedding")( + user_deep_input) item_inputs_list = list(item_features.values()) item_embedding_matrix = embedding_matrix_dict[item_feature_name] diff --git a/deepmatch/models/ncf.py b/deepmatch/models/ncf.py index 2de4c5f..95be769 100644 --- a/deepmatch/models/ncf.py +++ b/deepmatch/models/ncf.py @@ -8,8 +8,8 @@ import math -from deepctr.inputs import input_from_feature_columns, build_input_features, combined_dnn_input, SparseFeat -from deepctr.layers.core import DNN +from deepctr.feature_column import input_from_feature_columns, build_input_features, SparseFeat +from deepctr.layers import DNN, combined_dnn_input from tensorflow.python.keras.layers import Lambda, Concatenate, Multiply from tensorflow.python.keras.models import Model @@ -17,7 +17,7 @@ def NCF(user_feature_columns, item_feature_columns, user_gmf_embedding_dim=20, item_gmf_embedding_dim=20, user_mlp_embedding_dim=20, item_mlp_embedding_dim=20, dnn_use_bn=False, dnn_hidden_units=(64, 32), dnn_activation='relu', l2_reg_dnn=0, l2_reg_embedding=1e-6, dnn_dropout=0, - init_std=0.0001, seed=1024): + seed=1024): """Instantiates the NCF Model architecture. :param user_feature_columns: A dict containing user's features and features'dim. @@ -32,7 +32,6 @@ def NCF(user_feature_columns, item_feature_columns, user_gmf_embedding_dim=20, i :param l2_reg_dnn: float. L2 regularizer strength applied to DNN :param l2_reg_embedding: float. L2 regularizer strength applied to embedding vector :param dnn_dropout: float in [0,1), the probability we will drop out a given DNN coordinate. - :param init_std: float,to use as the initialize std of embedding vector :param seed: integer ,to use as random seed. :return: A Keras model instance. @@ -51,8 +50,8 @@ def NCF(user_feature_columns, item_feature_columns, user_gmf_embedding_dim=20, i user_inputs_list = list(user_features.values()) user_gmf_sparse_embedding_list, user_gmf_dense_value_list = input_from_feature_columns(user_features, user_gmf_feature_columns, - l2_reg_embedding, init_std, - seed, prefix='gmf_') + l2_reg_embedding, seed=seed, + prefix='gmf_') user_gmf_input = combined_dnn_input(user_gmf_sparse_embedding_list, []) user_gmf_out = Lambda(lambda x: x, name="user_gmf_embedding")(user_gmf_input) @@ -62,8 +61,8 @@ def NCF(user_feature_columns, item_feature_columns, user_gmf_embedding_dim=20, i item_inputs_list = list(item_features.values()) item_gmf_sparse_embedding_list, item_gmf_dense_value_list = input_from_feature_columns(item_features, item_gmf_feature_columns, - l2_reg_embedding, init_std, - seed, prefix='gmf_') + l2_reg_embedding, seed=seed, + prefix='gmf_') item_gmf_input = combined_dnn_input(item_gmf_sparse_embedding_list, []) item_gmf_out = Lambda(lambda x: x, name="item_gmf_embedding")(item_gmf_input) @@ -74,8 +73,8 @@ def NCF(user_feature_columns, item_feature_columns, user_gmf_embedding_dim=20, i for feat, size in user_feature_columns.items()] user_mlp_sparse_embedding_list, user_mlp_dense_value_list = input_from_feature_columns(user_features, user_mlp_feature_columns, - l2_reg_embedding, init_std, - seed, prefix='mlp_') + l2_reg_embedding, seed=seed, + prefix='mlp_') user_mlp_input = combined_dnn_input( user_mlp_sparse_embedding_list, user_mlp_dense_value_list) user_mlp_out = Lambda(lambda x: x, name="user_mlp_embedding")(user_mlp_input) @@ -85,19 +84,19 @@ def NCF(user_feature_columns, item_feature_columns, user_gmf_embedding_dim=20, i item_mlp_sparse_embedding_list, item_mlp_dense_value_list = input_from_feature_columns(item_features, item_mlp_feature_columns, - l2_reg_embedding, init_std, - seed, prefix='mlp_') + l2_reg_embedding, seed=seed, + prefix='mlp_') item_mlp_input = combined_dnn_input( item_mlp_sparse_embedding_list, item_mlp_dense_value_list) item_mlp_out = Lambda(lambda x: x, name="item_mlp_embedding")(item_mlp_input) mlp_input = Concatenate(axis=1)([user_mlp_out, item_mlp_out]) mlp_out = DNN(dnn_hidden_units, dnn_activation, l2_reg_dnn, dnn_dropout, - dnn_use_bn, seed, name="mlp_embedding")(mlp_input) + dnn_use_bn, seed = seed, name="mlp_embedding")(mlp_input) # Fusion of GMF and MLP neumf_input = Concatenate(axis=1)([gmf_out, mlp_out]) - neumf_out = DNN(hidden_units=[1], activation='sigmoid')(neumf_input) + neumf_out = DNN(hidden_units=[1], activation='sigmoid',seed=seed)(neumf_input) output = Lambda(lambda x: x, name='neumf_out')(neumf_out) # output = PredictionLayer(task, False)(neumf_out) diff --git a/deepmatch/models/sdm.py b/deepmatch/models/sdm.py index 8119fcf..6d6ca96 100644 --- a/deepmatch/models/sdm.py +++ b/deepmatch/models/sdm.py @@ -8,10 +8,11 @@ """ import tensorflow as tf -from deepctr.inputs import build_input_features, SparseFeat, DenseFeat, get_varlen_pooling_list, VarLenSparseFeat, \ +from deepctr.feature_column 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.layers import Dense, Lambda from tensorflow.python.keras.models import Model from deepmatch.utils import get_item_embedding @@ -20,9 +21,10 @@ from ..layers.sequence import DynamicMultiRNN -def SDM(user_feature_columns, item_feature_columns, history_feature_list, num_sampled=5, units=64, rnn_layers=2, dropout_rate=0.2, +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): + num_head=4, l2_reg_embedding=1e-6, dnn_activation='tanh', seed=1024): """Instantiates the Sequential Deep Matching Model architecture. :param user_feature_columns: An iterable containing user's features used by the model. @@ -36,14 +38,13 @@ def SDM(user_feature_columns, item_feature_columns, history_feature_list, num_sa :param num_head: int int, the number of attention head :param l2_reg_embedding: float. L2 regularizer strength applied to embedding vector :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. :return: A Keras model instance. """ if len(item_feature_columns) > 1: - raise ValueError("Now MIND only support 1 item feature like item_id") + raise ValueError("Now SDM only support 1 item feature like item_id") item_feature_column = item_feature_columns[0] item_feature_name = item_feature_column.name item_vocabulary_size = item_feature_columns[0].vocabulary_size @@ -78,8 +79,7 @@ def SDM(user_feature_columns, item_feature_columns, history_feature_list, num_sa sparse_varlen_feature_columns.append(fc) embedding_matrix_dict = create_embedding_matrix(user_feature_columns + item_feature_columns, l2_reg_embedding, - init_std, seed, - prefix="") + seed=seed) item_features = build_input_features(item_feature_columns) item_inputs_list = list(item_features.values()) @@ -142,11 +142,22 @@ def SDM(user_feature_columns, item_feature_columns, history_feature_list, num_sa pooling_item_embedding_weight, gate_output_reshape, item_features[item_feature_name]]) model = Model(inputs=user_inputs_list + item_inputs_list, outputs=output) + # model.user_input = user_inputs_list + # model.user_embedding = gate_output_reshape + model.__setattr__("user_input", user_inputs_list) model.__setattr__("user_embedding", gate_output_reshape) + # model.item_input = item_inputs_list + # model.item_embedding = get_item_embedding(pooling_item_embedding_weight, item_features[item_feature_name]) + model.__setattr__("item_input", item_inputs_list) model.__setattr__("item_embedding", get_item_embedding(pooling_item_embedding_weight, item_features[item_feature_name])) return model + # , Model(inputs=user_inputs_list, outputs=gate_output_reshape), Model(inputs=item_inputs_list, + # outputs=get_item_embedding( + # pooling_item_embedding_weight, + # item_features[ + # item_feature_name])) diff --git a/deepmatch/models/youtubednn.py b/deepmatch/models/youtubednn.py index f17087f..3b96a70 100644 --- a/deepmatch/models/youtubednn.py +++ b/deepmatch/models/youtubednn.py @@ -4,21 +4,21 @@ Reference: Covington P, Adams J, Sargin E. Deep neural networks for youtube recommendations[C]//Proceedings of the 10th ACM conference on recommender systems. 2016: 191-198. """ -from deepctr.inputs import input_from_feature_columns, build_input_features, combined_dnn_input, create_embedding_matrix -from deepctr.layers.core import DNN -from deepctr.layers.utils import NoMask +from deepctr.feature_column import build_input_features +from deepctr.layers import DNN +from deepctr.layers.utils import NoMask, combined_dnn_input from tensorflow.python.keras.models import Model from deepmatch.layers import PoolingLayer from deepmatch.utils import get_item_embedding -from ..inputs import input_from_feature_columns +from ..inputs import input_from_feature_columns, create_embedding_matrix from ..layers.core import SampledSoftmaxLayer, EmbeddingIndex def YoutubeDNN(user_feature_columns, item_feature_columns, num_sampled=5, user_dnn_hidden_units=(64, 32), dnn_activation='relu', dnn_use_bn=False, - l2_reg_dnn=0, l2_reg_embedding=1e-6, dnn_dropout=0, init_std=0.0001, seed=1024, ): + l2_reg_dnn=0, l2_reg_embedding=1e-6, dnn_dropout=0, output_activation='linear', seed=1024, ): """Instantiates the YoutubeDNN Model architecture. :param user_feature_columns: An iterable containing user's features used by the model. @@ -30,8 +30,8 @@ def YoutubeDNN(user_feature_columns, item_feature_columns, num_sampled=5, :param l2_reg_dnn: float. L2 regularizer strength applied to DNN :param l2_reg_embedding: float. L2 regularizer strength applied to embedding vector :param dnn_dropout: float in [0,1), the probability we will drop out a given DNN coordinate. - :param init_std: float,to use as the initialize std of embedding vector :param seed: integer ,to use as random seed. + :param output_activation: Activation function to use in output layer :return: A Keras model instance. """ @@ -42,20 +42,19 @@ def YoutubeDNN(user_feature_columns, item_feature_columns, num_sampled=5, item_vocabulary_size = item_feature_columns[0].vocabulary_size embedding_matrix_dict = create_embedding_matrix(user_feature_columns + item_feature_columns, l2_reg_embedding, - init_std, seed, prefix="") + seed=seed) user_features = build_input_features(user_feature_columns) user_inputs_list = list(user_features.values()) - user_sparse_embedding_list, user_dense_value_list = input_from_feature_columns(user_features, - user_feature_columns, - l2_reg_embedding, init_std, seed, + user_sparse_embedding_list, user_dense_value_list = input_from_feature_columns(user_features, user_feature_columns, + l2_reg_embedding, seed=seed, embedding_matrix_dict=embedding_matrix_dict) user_dnn_input = combined_dnn_input(user_sparse_embedding_list, user_dense_value_list) item_features = build_input_features(item_feature_columns) item_inputs_list = list(item_features.values()) user_dnn_out = DNN(user_dnn_hidden_units, dnn_activation, l2_reg_dnn, dnn_dropout, - dnn_use_bn, seed, )(user_dnn_input) + dnn_use_bn, output_activation=output_activation, seed=seed)(user_dnn_input) item_index = EmbeddingIndex(list(range(item_vocabulary_size)))(item_features[item_feature_name]) diff --git a/docs/pics/code.png b/docs/pics/code.png new file mode 100644 index 0000000000000000000000000000000000000000..aa53cbf490a030e4ee2379f31094a1d11259c04f GIT binary patch literal 29368 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 BN`U|X literal 0 HcmV?d00001 diff --git a/docs/pics/weichennote.png b/docs/pics/weichennote.png index fec7b11b10ab0bf89c9d5d12ce24c5aa3511838e..0b60a2f3fc1c4ee4a0d3a6be0f7c16ee17a1854d 100644 GIT binary patch literal 70228 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 AX8-^I diff --git a/docs/source/Examples.md b/docs/source/Examples.md index dc2a7f4..2e652e2 100644 --- a/docs/source/Examples.md +++ b/docs/source/Examples.md @@ -20,7 +20,7 @@ This example shows how to use ``YoutubeDNN`` to solve a matching task. You can g ```python import pandas as pd -from deepctr.inputs import SparseFeat, VarLenSparseFeat +from deepctr.feature_column import SparseFeat, VarLenSparseFeat from preprocess import gen_data_set, gen_model_input from sklearn.preprocessing import LabelEncoder from tensorflow.python.keras import backend as K @@ -292,7 +292,7 @@ This example shows how to use ``DSSM`` to solve a matching task. You can get the ```python import pandas as pd -from deepctr.inputs import SparseFeat, VarLenSparseFeat +from deepctr.feature_column import SparseFeat, VarLenSparseFeat from preprocess import gen_data_set, gen_model_input from sklearn.preprocessing import LabelEncoder from tensorflow.python.keras.models import Model diff --git a/docs/source/Features.md b/docs/source/Features.md index d7acce9..ed3831a 100644 --- a/docs/source/Features.md +++ b/docs/source/Features.md @@ -2,15 +2,17 @@ ## Feature Columns ### SparseFeat -``SparseFeat`` is a namedtuple with signature ``SparseFeat(name, vocabulary_size, embedding_dim, use_hash, dtype,embedding_name, group_name)`` +``SparseFeat`` is a namedtuple with signature ``SparseFeat(name, vocabulary_size, embedding_dim, use_hash, dtype, embeddings_initializer, embedding_name, group_name, trainable)`` - name : feature name - vocabulary_size : number of unique feature values for sprase feature or hashing space when `use_hash=True` - embedding_dim : embedding dimension - use_hash : defualt `False`.If `True` the input will be hashed to space of size `vocabulary_size`. -- dtype : default `float32`.dtype of input tensor. +- dtype : default `int32`.dtype of input tensor. +- embeddings_initializer : initializer for the `embeddings` matrix. - embedding_name : default `None`. If None, the embedding_name will be same as `name`. - group_name : feature group of this feature. +- trainable: default `True`.Whether or not the embedding is trainable. ### DenseFeat ``DenseFeat`` is a namedtuple with signature ``DenseFeat(name, dimension, dtype)`` @@ -30,6 +32,7 @@ - weight_name : default `None`. If not None, the sequence feature will be multiplyed by the feature whose name is `weight_name`. - weight_norm : default `True`. Whether normalize the weight score or not. + ## Models diff --git a/docs/source/History.md b/docs/source/History.md index 1645b66..d0c9f61 100644 --- a/docs/source/History.md +++ b/docs/source/History.md @@ -1,4 +1,5 @@ # History +- 10/12/2020 : [v0.2.0](https://github.com/shenweichen/DeepMatch/releases/tag/v0.2.0) released.Support different initializers for different embedding weights and loading pretrained embeddings. - 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/conf.py b/docs/source/conf.py index 0a25c61..4e882a8 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.3' +release = '0.2.0' # -- General configuration --------------------------------------------------- diff --git a/docs/source/index.rst b/docs/source/index.rst index a774fe7..f5b25cc 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -18,18 +18,18 @@ You can read the latest code at https://github.com/shenweichen/DeepMatch News ----- +10/12/2020 : Support different initializers for different embedding weights and loading pretrained embeddings. `Changelog `_ + 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 . - DisscussionGroup ----------------------- 公众号:**浅梦的学习笔记** wechat ID: **deepctrbot** -.. image:: ../pics/weichennote.png +.. image:: ../pics/code.png .. toctree:: :maxdepth: 2 diff --git a/examples/colab_MovieLen1M_YoutubeDNN.ipynb b/examples/colab_MovieLen1M_YoutubeDNN.ipynb index 6f78370..a5bd90e 100644 --- a/examples/colab_MovieLen1M_YoutubeDNN.ipynb +++ b/examples/colab_MovieLen1M_YoutubeDNN.ipynb @@ -159,7 +159,7 @@ ], "source": [ "import pandas as pd\n", - "from deepctr.inputs import SparseFeat, VarLenSparseFeat\n", + "from deepctr.feature_column import SparseFeat, VarLenSparseFeat\n", "from preprocess import gen_data_set, gen_model_input\n", "from sklearn.preprocessing import LabelEncoder\n", "from tensorflow.python.keras import backend as K\n", @@ -386,7 +386,7 @@ " tf.compat.v1.disable_eager_execution()\n", "\n", "model = YoutubeDNN(user_feature_columns, item_feature_columns, num_sampled=100, user_dnn_hidden_units=(128,64, embedding_dim))\n", - "# model = MIND(user_feature_columns,item_feature_columns,dynamic_k=False,p=1,k_max=2,num_sampled=100,user_dnn_hidden_units=(128,64, embedding_dim),init_std=0.001)\n", + "# model = MIND(user_feature_columns,item_feature_columns,dynamic_k=False,p=1,k_max=2,num_sampled=100,user_dnn_hidden_units=(128,64, embedding_dim))\n", "\n", "model.compile(optimizer=\"adam\", loss=sampledsoftmaxloss) # \"binary_crossentropy\")\n", "\n", diff --git a/examples/preprocess.py b/examples/preprocess.py index 876bc12..4803749 100644 --- a/examples/preprocess.py +++ b/examples/preprocess.py @@ -104,8 +104,8 @@ def gen_model_input_sdm(train_set, user_profile, seq_short_len, seq_prefer_len): value=0) train_model_input = {"user_id": train_uid, "movie_id": train_iid, "short_movie_id": train_short_item_pad, - "prefer_movie_id": train_prefer_item_pad, "prefer_sess_length": train_short_len, "short_sess_length": - train_prefer_len, 'short_genres': train_short_genres_pad, 'prefer_genres': train_prefer_genres_pad} + "prefer_movie_id": train_prefer_item_pad, "prefer_sess_length": train_prefer_len, "short_sess_length": + train_short_len, 'short_genres': train_short_genres_pad, 'prefer_genres': train_prefer_genres_pad} for key in ["gender", "age", "occupation", "zip"]: train_model_input[key] = user_profile.loc[train_model_input['user_id']][key].values diff --git a/examples/run_dssm_negsampling.py b/examples/run_dssm_negsampling.py index e40a0c1..2ed6d83 100644 --- a/examples/run_dssm_negsampling.py +++ b/examples/run_dssm_negsampling.py @@ -1,5 +1,5 @@ import pandas as pd -from deepctr.inputs import SparseFeat, VarLenSparseFeat +from deepctr.feature_column import SparseFeat, VarLenSparseFeat from preprocess import gen_data_set, gen_model_input from sklearn.preprocessing import LabelEncoder from tensorflow.python.keras.models import Model diff --git a/examples/run_ncf.py b/examples/run_ncf.py new file mode 100644 index 0000000..29d0358 --- /dev/null +++ b/examples/run_ncf.py @@ -0,0 +1,56 @@ +import pandas as pd +from preprocess import gen_data_set, gen_model_input +from sklearn.preprocessing import LabelEncoder + +from deepmatch.models import NCF + +if __name__ == "__main__": + data = pd.read_csvdata = pd.read_csv("./movielens_sample.txt") + sparse_features = ["movie_id", "user_id", + "gender", "age", "occupation", "zip", ] + SEQ_LEN = 50 + negsample = 3 + + # 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'] + 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"]].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(data, negsample) + + 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) + + # 2.count #unique features for each sparse field and generate feature config for sequence feature + + user_feature_columns = {"user_id": feature_max_idx['user_id'], 'gender': feature_max_idx['gender'], + "age": feature_max_idx['age'], + "occupation": feature_max_idx["occupation"], "zip": feature_max_idx["zip"]} + + item_feature_columns = {"movie_id": feature_max_idx['movie_id']} + + # 3.Define Model,train,predict and evaluate + model = NCF(user_feature_columns, item_feature_columns, user_gmf_embedding_dim=20, + item_gmf_embedding_dim=20, user_mlp_embedding_dim=32, item_mlp_embedding_dim=32, + dnn_hidden_units=[128, 64, 32], ) + model.summary() + model.compile("adam", "binary_crossentropy", + metrics=['binary_crossentropy'], ) + + history = model.fit(train_model_input, train_label, + batch_size=64, epochs=20, verbose=2, validation_split=0.2, ) + pred_ans = model.predict(test_model_input, batch_size=64) + # print("test LogLoss", round(log_loss(test_label, pred_ans), 4)) + # print("test AUC", round(roc_auc_score(test_label, pred_ans), 4)) diff --git a/examples/run_sdm.py b/examples/run_sdm.py index e409414..fdf1def 100644 --- a/examples/run_sdm.py +++ b/examples/run_sdm.py @@ -1,5 +1,5 @@ import pandas as pd -from deepctr.inputs import SparseFeat, VarLenSparseFeat +from deepctr.feature_column 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 @@ -74,16 +74,13 @@ 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.compile(optimizer='adam', 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 + # 3.Define Model,train,predict and evaluate test_user_model_input = test_model_input all_item_model_input = {"movie_id": item_profile['movie_id'].values, } diff --git a/examples/run_youtubednn.py b/examples/run_youtubednn.py index aeb36c0..d98a472 100644 --- a/examples/run_youtubednn.py +++ b/examples/run_youtubednn.py @@ -1,5 +1,5 @@ import pandas as pd -from deepctr.inputs import SparseFeat, VarLenSparseFeat +from deepctr.feature_column import SparseFeat, VarLenSparseFeat from preprocess import gen_data_set, gen_model_input from sklearn.preprocessing import LabelEncoder from tensorflow.python.keras import backend as K @@ -60,7 +60,7 @@ tf.compat.v1.disable_eager_execution() model = YoutubeDNN(user_feature_columns, item_feature_columns, num_sampled=5, user_dnn_hidden_units=(64, embedding_dim)) - # model = MIND(user_feature_columns,item_feature_columns,dynamic_k=False,p=1,k_max=2,num_sampled=5,user_dnn_hidden_units=(64, embedding_dim),init_std=0.001) + #model = MIND(user_feature_columns,item_feature_columns,dynamic_k=False,p=1,k_max=2,num_sampled=5,user_dnn_hidden_units=(64, embedding_dim)) model.compile(optimizer="adam", loss=sampledsoftmaxloss) # "binary_crossentropy") diff --git a/setup.py b/setup.py index fa915ee..8ab2c5c 100644 --- a/setup.py +++ b/setup.py @@ -4,12 +4,12 @@ long_description = fh.read() REQUIRED_PACKAGES = [ - 'h5py','requests',"deepctr==0.7.5" + 'h5py', 'requests', "deepctr==0.8.2" ] setuptools.setup( name="deepmatch", - version="0.1.3", + version="0.2.0", 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**.", @@ -45,6 +45,6 @@ 'Topic :: Software Development :: Libraries :: Python Modules', ), license="Apache-2.0", - keywords=['match', 'matching','recommendation' - 'deep learning', 'tensorflow', 'tensor', 'keras'], + keywords=['match', 'matching', 'recommendation' + 'deep learning', 'tensorflow', 'tensor', 'keras'], ) diff --git a/tests/models/DSSM_test.py b/tests/models/DSSM_test.py index 90c5e24..c4210e8 100644 --- a/tests/models/DSSM_test.py +++ b/tests/models/DSSM_test.py @@ -2,9 +2,6 @@ from ..utils import check_model, get_xy_fd -# @pytest.mark.xfail(reason="There is a bug when save model use Dice") -# @pytest.mark.skip(reason="misunderstood the API") - def test_DSSM(): model_name = "DSSM" diff --git a/tests/models/FM_test.py b/tests/models/FM_test.py index 0ae1222..baa224e 100644 --- a/tests/models/FM_test.py +++ b/tests/models/FM_test.py @@ -2,10 +2,6 @@ from ..utils import check_model, get_xy_fd -# @pytest.mark.xfail(reason="There is a bug when save model use Dice") -# @pytest.mark.skip(reason="misunderstood the API") - - def test_FM(): model_name = "FM" diff --git a/tests/models/MIND_test.py b/tests/models/MIND_test.py index 9451630..0b9509b 100644 --- a/tests/models/MIND_test.py +++ b/tests/models/MIND_test.py @@ -1,12 +1,9 @@ -from deepmatch.models import MIND -from deepmatch.utils import sampledsoftmaxloss -from tensorflow.python.keras import backend as K -from ..utils import check_model,get_xy_fd import tensorflow as tf +from tensorflow.python.keras import backend as K - -#@pytest.mark.xfail(reason="There is a bug when save model use Dice") -#@pytest.mark.skip(reason="misunderstood the API") +from deepmatch.models import MIND +from deepmatch.utils import sampledsoftmaxloss +from ..utils import check_model, get_xy_fd def test_MIND(): @@ -16,12 +13,12 @@ def test_MIND(): K.set_learning_phase(True) if tf.__version__ >= '2.0.0': - tf.compat.v1.disable_eager_execution() + tf.compat.v1.disable_eager_execution() model = MIND(user_feature_columns, item_feature_columns, num_sampled=2, user_dnn_hidden_units=(16, 4)) model.compile('adam', sampledsoftmaxloss) - check_model(model,model_name,x,y) + check_model(model, model_name, x, y) if __name__ == "__main__": diff --git a/tests/models/NCF_test.py b/tests/models/NCF_test.py new file mode 100644 index 0000000..da609c0 --- /dev/null +++ b/tests/models/NCF_test.py @@ -0,0 +1,16 @@ +from deepmatch.models import NCF +from ..utils import get_xy_fd_ncf + + +def test_NCF(): + model_name = "NCF" + + x, y, user_feature_columns, item_feature_columns = get_xy_fd_ncf(False) + model = NCF(user_feature_columns, item_feature_columns, ) + + model.compile('adam', "binary_crossentropy") + model.fit(x, y, batch_size=10, epochs=2, validation_split=0.5) + + +if __name__ == "__main__": + pass diff --git a/tests/models/SDM_test.py b/tests/models/SDM_test.py index b4caf9b..60e3e5a 100644 --- a/tests/models/SDM_test.py +++ b/tests/models/SDM_test.py @@ -1,12 +1,10 @@ -from deepmatch.models import SDM -from deepmatch.utils import sampledsoftmaxloss -from tensorflow.python.keras import backend as K -from tests.utils import check_model, get_xy_fd_sdm import tensorflow as tf +from tensorflow.python.keras import backend as K +from deepmatch.models import SDM +from deepmatch.utils import sampledsoftmaxloss +from ..utils import check_model, get_xy_fd_sdm -#@pytest.mark.xfail(reason="There is a bug when save model use Dice") -#@pytest.mark.skip(reason="misunderstood the API") def test_SDM(): @@ -16,14 +14,14 @@ def test_SDM(): K.set_learning_phase(True) if tf.__version__ >= '2.0.0': - tf.compat.v1.disable_eager_execution() + tf.compat.v1.disable_eager_execution() model = SDM(user_feature_columns, item_feature_columns, history_feature_list, units=8) - #model.summary() + # model.summary() model.compile('adam', sampledsoftmaxloss) check_model(model, model_name, x, y) if __name__ == "__main__": - pass \ No newline at end of file + pass diff --git a/tests/models/YoutubeDNN_test.py b/tests/models/YoutubeDNN_test.py index ea7560c..46afb03 100644 --- a/tests/models/YoutubeDNN_test.py +++ b/tests/models/YoutubeDNN_test.py @@ -1,13 +1,9 @@ -from deepmatch.models import YoutubeDNN -from deepmatch.utils import sampledsoftmaxloss -from tensorflow.python.keras import backend as K import tensorflow as tf +from tensorflow.python.keras import backend as K -from ..utils import check_model,get_xy_fd - - -#@pytest.mark.xfail(reason="There is a bug when save model use Dice") -#@pytest.mark.skip(reason="misunderstood the API") +from deepmatch.models import YoutubeDNN +from deepmatch.utils import sampledsoftmaxloss +from ..utils import check_model, get_xy_fd def test_YoutubeDNN(): @@ -17,12 +13,12 @@ def test_YoutubeDNN(): K.set_learning_phase(True) if tf.__version__ >= '2.0.0': - tf.compat.v1.disable_eager_execution() + tf.compat.v1.disable_eager_execution() model = YoutubeDNN(user_feature_columns, item_feature_columns, num_sampled=2, user_dnn_hidden_units=(16, 4)) model.compile('adam', sampledsoftmaxloss) - check_model(model,model_name,x,y,check_model_io=True) + check_model(model, model_name, x, y, check_model_io=True) if __name__ == "__main__": diff --git a/tests/utils.py b/tests/utils.py index 08be5fb..b58e9db 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -6,12 +6,12 @@ import numpy as np import tensorflow as tf +from deepctr.feature_column import SparseFeat, DenseFeat, VarLenSparseFeat, DEFAULT_GROUP_NAME from numpy.testing import assert_allclose from tensorflow.python.keras import backend as K from tensorflow.python.keras.layers import Input, Masking from tensorflow.python.keras.models import Model, load_model, save_model -from deepctr.inputs import SparseFeat, DenseFeat, VarLenSparseFeat,DEFAULT_GROUP_NAME from deepmatch.layers import custom_objects SAMPLE_SIZE = 8 @@ -44,12 +44,13 @@ def get_test_data(sample_size=1000, embedding_size=4, sparse_feature_num=1, dens for i in range(sparse_feature_num): if use_group: - group_name = str(i%3) + group_name = str(i % 3) else: group_name = DEFAULT_GROUP_NAME dim = np.random.randint(1, 10) feature_columns.append( - SparseFeat(prefix + 'sparse_feature_' + str(i), dim, embedding_size, use_hash=hash_flag, dtype=tf.int32,group_name=group_name)) + SparseFeat(prefix + 'sparse_feature_' + str(i), dim, embedding_size, use_hash=hash_flag, dtype=tf.int32, + group_name=group_name)) for i in range(dense_feature_num): feature_columns.append(DenseFeat(prefix + 'dense_feature_' + str(i), 1, dtype=tf.float32)) @@ -340,11 +341,8 @@ def check_model(model, model_name, x, y, check_model_io=True): model.fit(x, y, batch_size=10, epochs=2, validation_split=0.5) - - print(model_name + " test train valid pass!") - user_embedding_model = Model(inputs=model.user_input, outputs=model.user_embedding) item_embedding_model = Model(inputs=model.item_input, outputs=model.item_embedding) @@ -366,47 +364,79 @@ def check_model(model, model_name, x, y, check_model_io=True): print(model_name + " test save load model pass!") print(model_name + " test pass!") + # print(1) + # + # save_model(item_embedding_model, model_name + '.user.h5') + # print(2) + # + # item_embedding_model = load_model(model_name + '.user.h5', custom_objects) + # print(3) + # + # item_embs = item_embedding_model.predict(x, batch_size=2 ** 12) + # print(item_embs) + # print("go") def get_xy_fd(hash_flag=False): + user_feature_columns = [SparseFeat('user', 3), SparseFeat( + 'gender', 2), VarLenSparseFeat( + SparseFeat('hist_item', vocabulary_size=3 + 1, embedding_dim=4, embedding_name='item'), maxlen=4, + length_name="hist_len")] + item_feature_columns = [SparseFeat('item', 3 + 1, embedding_dim=4, )] + + uid = np.array([0, 1, 2, 1]) + ugender = np.array([0, 1, 0, 1]) + iid = np.array([1, 2, 3, 1]) # 0 is mask value + + hist_iid = np.array([[1, 2, 3, 0], [1, 2, 3, 0], [1, 2, 0, 0], [3, 0, 0, 0]]) + hist_len = np.array([3, 3, 2, 1]) + feature_dict = {'user': uid, 'gender': ugender, 'item': iid, + 'hist_item': hist_iid, "hist_len": hist_len} + + # feature_names = get_feature_names(feature_columns) + x = feature_dict + y = np.array([1, 1, 1, 1]) + return x, y, user_feature_columns, item_feature_columns - user_feature_columns = [SparseFeat('user',3),SparseFeat( - 'gender', 2),VarLenSparseFeat(SparseFeat('hist_item', vocabulary_size=3 + 1,embedding_dim=4,embedding_name='item'), maxlen=4,length_name="hist_len") ] - item_feature_columns = [SparseFeat('item', 3 + 1,embedding_dim=4,)] +def get_xy_fd_ncf(hash_flag=False): + user_feature_columns = {"user": 3, "gender": 2, } + item_feature_columns = {"item": 4} - uid = np.array([0, 1, 2,1]) - ugender = np.array([0, 1, 0,1]) - iid = np.array([1, 2, 3,1]) # 0 is mask value + uid = np.array([0, 1, 2, 1]) + ugender = np.array([0, 1, 0, 1]) + iid = np.array([1, 2, 3, 1]) # 0 is mask value - hist_iid = np.array([[1, 2, 3, 0], [1, 2, 3, 0], [1, 2, 0, 0],[3, 0, 0, 0]]) - hist_len = np.array([3,3,2,1]) + hist_iid = np.array([[1, 2, 3, 0], [1, 2, 3, 0], [1, 2, 0, 0], [3, 0, 0, 0]]) + hist_len = np.array([3, 3, 2, 1]) feature_dict = {'user': uid, 'gender': ugender, 'item': iid, - 'hist_item': hist_iid, "hist_len":hist_len} + 'hist_item': hist_iid, "hist_len": hist_len} - #feature_names = get_feature_names(feature_columns) + # feature_names = get_feature_names(feature_columns) x = feature_dict - y = np.array([1, 1, 1,1]) - return x, y, user_feature_columns,item_feature_columns + y = np.array([1, 1, 1, 1]) + return x, y, user_feature_columns, item_feature_columns def get_xy_fd_sdm(hash_flag=False): - - user_feature_columns = [SparseFeat('user',3), + user_feature_columns = [SparseFeat('user', 3), SparseFeat('gender', 2), - VarLenSparseFeat(SparseFeat('prefer_item', vocabulary_size=100,embedding_dim=8, - embedding_name='item'), maxlen=6, length_name="prefer_sess_length"), + 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=8, - embedding_name='cate'), maxlen=6, length_name="prefer_sess_length"), - VarLenSparseFeat(SparseFeat('short_item', vocabulary_size=100,embedding_dim=8, - embedding_name='item'), maxlen=4, length_name="short_sess_length"), + embedding_name='cate'), maxlen=6, + length_name="prefer_sess_length"), + 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=8, - embedding_name='cate'), maxlen=4, length_name="short_sess_length"), + embedding_name='cate'), maxlen=4, + length_name="short_sess_length"), ] - item_feature_columns = [SparseFeat('item', 100, embedding_dim=8,)] - + item_feature_columns = [SparseFeat('item', 100, embedding_dim=8, )] uid = np.array([0, 1, 2, 1]) ugender = np.array([0, 1, 0, 1]) @@ -419,12 +449,13 @@ def get_xy_fd_sdm(hash_flag=False): prefer_len = np.array([6, 5, 4, 3]) short_len = np.array([3, 3, 2, 1]) - feature_dict = {'user': uid, 'gender': ugender, 'item': iid, 'prefer_item': prefer_iid, "prefer_cate":prefer_cate, - 'short_item': short_iid, 'short_cate': short_cate, 'prefer_sess_length': prefer_len, 'short_sess_length':short_len} + feature_dict = {'user': uid, 'gender': ugender, 'item': iid, 'prefer_item': prefer_iid, "prefer_cate": prefer_cate, + 'short_item': short_iid, 'short_cate': short_cate, 'prefer_sess_length': prefer_len, + 'short_sess_length': short_len} - #feature_names = get_feature_names(feature_columns) + # feature_names = get_feature_names(feature_columns) x = feature_dict y = np.array([1, 1, 1, 0]) history_feature_list = ['item', 'cate'] - return x, y, user_feature_columns, item_feature_columns, history_feature_list \ No newline at end of file + return x, y, user_feature_columns, item_feature_columns, history_feature_list