diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index f660b34..7a0a0a6 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.6, 3.7] - - tensorflow version [e.g. 1.4.0, 1.14.0, 2.3.0] - - deepmatch version [e.g. 0.2.0,] + - python version [e.g. 3.6, 3.7, 3.8] + - tensorflow version [e.g. 1.4.0, 1.14.0, 2.5.0] + - deepmatch version [e.g. 0.2.1,] **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 f6d65c0..2cabbcb 100644 --- a/.github/ISSUE_TEMPLATE/question.md +++ b/.github/ISSUE_TEMPLATE/question.md @@ -15,6 +15,6 @@ A clear and concise description of what the question is. 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.2.0,] + - python version [e.g. 3.6, 3.7, 3.8] + - tensorflow version [e.g. 1.4.0, 1.14.0, 2.5.0] + - deepmatch version [e.g. 0.2.1,] diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 275b40d..ac474c3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,25 +17,46 @@ jobs: timeout-minutes: 120 strategy: matrix: - python-version: [3.5,3.6,3.7] - tf-version: [1.4.0,1.14.0,2.1.0,2.2.0,2.3.0] + python-version: [3.6,3.7,3.8] + tf-version: [1.4.0,1.14.0,2.5.0] exclude: - python-version: 3.7 tf-version: 1.4.0 + - python-version: 3.7 + tf-version: 1.15.0 + - python-version: 3.8 + tf-version: 1.4.0 + - python-version: 3.8 + tf-version: 1.14.0 + - python-version: 3.8 + tf-version: 1.15.0 + - python-version: 3.6 + tf-version: 2.7.0 + - python-version: 3.6 + tf-version: 2.8.0 + - python-version: 3.6 + tf-version: 2.9.0 + - python-version: 3.9 + tf-version: 1.4.0 + - python-version: 3.9 + tf-version: 1.15.0 + - python-version: 3.9 + tf-version: 2.2.0 steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v3 - name: Setup python environment - uses: actions/setup-python@v1 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - name: Install dependencies run: | pip3 install -q tensorflow==${{ matrix.tf-version }} + pip install -q protobuf==3.19.0 pip install -q requests pip install -e . - name: Test with pytest @@ -46,7 +67,7 @@ jobs: pip install -q python-coveralls pytest --cov=deepmatch --cov-report=xml - name: Upload coverage to Codecov - uses: codecov/codecov-action@v1.0.2 + uses: codecov/codecov-action@v3.1.0 with: token: ${{secrets.CODECOV_TOKEN}} file: ./coverage.xml diff --git a/README.md b/README.md index 75e184e..69dea2a 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,12 @@ Let's [**Get Started!**](https://deepmatch.readthedocs.io/en/latest/Quick-Start. Wang Zhe

Baidu Inc.

​ + + ​ pic
+ ​ Chen Leihui ​ +

+ Alibaba Group

​ + ​ pic
LeoCai @@ -57,6 +63,11 @@ Let's [**Get Started!**](https://deepmatch.readthedocs.io/en/latest/Quick-Start. ​ Yang Jieyu

Ant Group

​ + + ​ pic
+ ​ Meng Yifan +

DeepCTR

​ + diff --git a/deepmatch/__init__.py b/deepmatch/__init__.py index 0fa3e17..6ed5791 100644 --- a/deepmatch/__init__.py +++ b/deepmatch/__init__.py @@ -1,4 +1,4 @@ from .utils import check_version -__version__ = '0.2.0' +__version__ = '0.2.1' check_version(__version__) diff --git a/deepmatch/layers/__init__.py b/deepmatch/layers/__init__.py index 854c596..a6053ed 100644 --- a/deepmatch/layers/__init__.py +++ b/deepmatch/layers/__init__.py @@ -1,7 +1,8 @@ from deepctr.layers import custom_objects from deepctr.layers.utils import reduce_sum -from .core import PoolingLayer, Similarity, LabelAwareAttention, CapsuleLayer, SampledSoftmaxLayer, EmbeddingIndex +from .core import PoolingLayer, Similarity, LabelAwareAttention, CapsuleLayer, SampledSoftmaxLayer, EmbeddingIndex, \ + MaskUserEmbedding from .interaction import DotAttention, ConcatAttention, SoftmaxWeightedSum, AttentionSequencePoolingLayer, \ SelfAttention, \ SelfMultiHeadAttention, UserAttention @@ -23,7 +24,8 @@ 'SelfAttention': SelfAttention, 'SelfMultiHeadAttention': SelfMultiHeadAttention, 'UserAttention': UserAttention, - 'DynamicMultiRNN': DynamicMultiRNN + 'DynamicMultiRNN': DynamicMultiRNN, + 'MaskUserEmbedding': MaskUserEmbedding } custom_objects = dict(custom_objects, **_custom_objects) diff --git a/deepmatch/layers/core.py b/deepmatch/layers/core.py index 2bdd7c2..1267653 100644 --- a/deepmatch/layers/core.py +++ b/deepmatch/layers/core.py @@ -1,7 +1,7 @@ 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, glorot_normal +from tensorflow.python.keras.initializers import RandomNormal, Zeros, TruncatedNormal from tensorflow.python.keras.layers import Layer from tensorflow.python.keras.regularizers import l2 @@ -103,19 +103,19 @@ def call(self, inputs, training=None, **kwargs): weight = tf.pow(weight, self.pow_p) # [x,k_max,1] if len(inputs) == 3: - k_user = tf.cast(tf.maximum( - 1., - tf.minimum( - tf.cast(self.k_max, dtype="float32"), # k_max - tf.log1p(tf.cast(inputs[2], dtype="float32")) / tf.log(2.) # hist_len - ) - ), dtype="int64") + k_user = inputs[2] seq_mask = tf.transpose(tf.sequence_mask(k_user, self.k_max), [0, 2, 1]) padding = tf.ones_like(seq_mask, dtype=tf.float32) * (-2 ** 32 + 1) # [x,k_max,1] weight = tf.where(seq_mask, weight, padding) - weight = softmax(weight, dim=1, name="weight") - output = reduce_sum(keys * weight, axis=1) + if self.pow_p >= 100: + idx = tf.stack( + [tf.range(tf.shape(keys)[0]), tf.squeeze(tf.argmax(weight, axis=1, output_type=tf.int32), axis=1)], + axis=1) + output = tf.gather_nd(keys, idx) + else: + weight = softmax(weight, dim=1, name="weight") + output = tf.reduce_sum(keys * weight, axis=1) return output @@ -172,32 +172,59 @@ def __init__(self, input_units, out_units, max_len, k_max, iteration_times=3, super(CapsuleLayer, self).__init__(**kwargs) def build(self, input_shape): - self.routing_logits = self.add_weight(shape=[1, self.k_max, self.max_len], - initializer=RandomNormal(stddev=self.init_std), - trainable=False, name="B", dtype=tf.float32) self.bilinear_mapping_matrix = self.add_weight(shape=[self.input_units, self.out_units], - initializer=RandomNormal(stddev=self.init_std), name="S", dtype=tf.float32) super(CapsuleLayer, self).build(input_shape) def call(self, inputs, **kwargs): - behavior_embddings, seq_len = inputs - batch_size = tf.shape(behavior_embddings)[0] - seq_len_tile = tf.tile(seq_len, [1, self.k_max]) + + behavior_embedding = inputs[0] + seq_len = inputs[1] + batch_size = tf.shape(behavior_embedding)[0] + + mask = tf.reshape(tf.sequence_mask(seq_len, self.max_len, tf.float32), [-1, self.max_len, 1, 1]) + + behavior_embedding_mapping = tf.tensordot(behavior_embedding, self.bilinear_mapping_matrix, axes=1) + behavior_embedding_mapping = tf.expand_dims(behavior_embedding_mapping, axis=2) + + behavior_embdding_mapping_ = tf.stop_gradient(behavior_embedding_mapping) # N,max_len,1,E + try: + routing_logits = tf.truncated_normal([batch_size, self.max_len, self.k_max, 1], stddev=self.init_std) + except AttributeError: + routing_logits = tf.compat.v1.truncated_normal([batch_size, self.max_len, self.k_max, 1], + stddev=self.init_std) + routing_logits = tf.stop_gradient(routing_logits) + + k_user = None + if len(inputs) == 3: + k_user = inputs[2] + interest_mask = tf.sequence_mask(k_user, self.k_max, tf.float32) + interest_mask = tf.reshape(interest_mask, [batch_size, 1, self.k_max, 1]) + interest_mask = tf.tile(interest_mask, [1, self.max_len, 1, 1]) + + interest_padding = tf.ones_like(interest_mask) * -2 ** 31 + interest_mask = tf.cast(interest_mask, tf.bool) for i in range(self.iteration_times): - mask = tf.sequence_mask(seq_len_tile, self.max_len) - pad = tf.ones_like(mask, dtype=tf.float32) * (-2 ** 32 + 1) - routing_logits_with_padding = tf.where(mask, tf.tile(self.routing_logits, [batch_size, 1, 1]), pad) - weight = tf.nn.softmax(routing_logits_with_padding) - behavior_embdding_mapping = tf.tensordot(behavior_embddings, self.bilinear_mapping_matrix, axes=1) - Z = tf.matmul(weight, behavior_embdding_mapping) - interest_capsules = squash(Z) - delta_routing_logits = reduce_sum( - tf.matmul(interest_capsules, tf.transpose(behavior_embdding_mapping, perm=[0, 2, 1])), - axis=0, keep_dims=True - ) - self.routing_logits.assign_add(delta_routing_logits) + if k_user is not None: + routing_logits = tf.where(interest_mask, routing_logits, interest_padding) + try: + weight = softmax(routing_logits, 2) * mask + except TypeError: + weight = tf.transpose(softmax(tf.transpose(routing_logits, [0, 1, 3, 2])), + [0, 1, 3, 2]) * mask # N,max_len,k_max,1 + if i < self.iteration_times - 1: + Z = reduce_sum(tf.matmul(weight, behavior_embdding_mapping_), axis=1, keep_dims=True) # N,1,k_max,E + interest_capsules = squash(Z) + delta_routing_logits = reduce_sum( + interest_capsules * behavior_embdding_mapping_, + axis=-1, keep_dims=True + ) + routing_logits += delta_routing_logits + else: + Z = reduce_sum(tf.matmul(weight, behavior_embedding_mapping), axis=1, keep_dims=True) + interest_capsules = squash(Z) + interest_capsules = tf.reshape(interest_capsules, [-1, self.k_max, self.out_units]) return interest_capsules @@ -213,7 +240,7 @@ def get_config(self, ): def squash(inputs): vec_squared_norm = reduce_sum(tf.square(inputs), axis=-1, keep_dims=True) - scalar_factor = vec_squared_norm / (1 + vec_squared_norm) / tf.sqrt(vec_squared_norm + 1e-8) + scalar_factor = vec_squared_norm / (1 + vec_squared_norm) / tf.sqrt(vec_squared_norm + 1e-9) vec_squashed = scalar_factor * inputs return vec_squashed @@ -235,3 +262,27 @@ def get_config(self, ): config = {'index': self.index, } base_config = super(EmbeddingIndex, self).get_config() return dict(list(base_config.items()) + list(config.items())) + + +class MaskUserEmbedding(Layer): + + def __init__(self, k_max, **kwargs): + self.k_max = k_max + super(MaskUserEmbedding, self).__init__(**kwargs) + + def build(self, input_shape): + super(MaskUserEmbedding, self).build( + input_shape) # Be sure to call this somewhere! + + def call(self, x, training=None, **kwargs): + user_embedding, interest_num = x + if not training: + interest_mask = tf.sequence_mask(interest_num, self.k_max, tf.float32) + interest_mask = tf.reshape(interest_mask, [-1, self.k_max, 1]) + user_embedding *= interest_mask + return user_embedding + + def get_config(self, ): + config = {'k_max': self.k_max, } + base_config = super(MaskUserEmbedding, 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 1ac95cd..f00eb3f 100644 --- a/deepmatch/models/dssm.py +++ b/deepmatch/models/dssm.py @@ -15,8 +15,8 @@ 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, seed=1024, metric='cos'): + dnn_activation='relu', dnn_use_bn=False, + l2_reg_dnn=0, l2_reg_embedding=1e-6, dnn_dropout=0, gamma=10, 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,6 +28,7 @@ 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 gamma: float. Scaling factor. :param seed: integer ,to use as random seed. :param metric: str, ``"cos"`` for cosine or ``"ip"`` for inner product :return: A Keras model instance. @@ -55,12 +56,12 @@ def DSSM(user_feature_columns, item_feature_columns, user_dnn_hidden_units=(64, 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=seed)(user_dnn_input) + dnn_use_bn, output_activation='linear', seed=seed)(user_dnn_input) item_dnn_out = DNN(item_dnn_hidden_units, dnn_activation, l2_reg_dnn, dnn_dropout, - dnn_use_bn, seed=seed)(item_dnn_input) + dnn_use_bn, output_activation='linear', seed=seed)(item_dnn_input) - score = Similarity(type=metric, gamma = 10)([user_dnn_out, item_dnn_out]) + score = Similarity(type=metric, gamma=gamma)([user_dnn_out, item_dnn_out]) output = PredictionLayer("binary", False)(score) diff --git a/deepmatch/models/fm.py b/deepmatch/models/fm.py index 97bf339..5b25fe0 100644 --- a/deepmatch/models/fm.py +++ b/deepmatch/models/fm.py @@ -8,12 +8,13 @@ from ..layers.core import Similarity -def FM(user_feature_columns, item_feature_columns, l2_reg_embedding=1e-6, seed=1024, metric='cos'): +def FM(user_feature_columns, item_feature_columns, l2_reg_embedding=1e-6, gamma=10, 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 gamma: float. Scaling factor. :param seed: integer ,to use as random seed. :param metric: str, ``"cos"`` for cosine or ``"ip"`` for inner product :return: A Keras model instance. @@ -46,7 +47,7 @@ def FM(user_feature_columns, item_feature_columns, l2_reg_embedding=1e-6, seed=1 item_dnn_input = concat_func(item_sparse_embedding_list, axis=1) item_vector_sum = Lambda(lambda x: reduce_sum(x, axis=1, keep_dims=False))(item_dnn_input) - score = Similarity(type=metric)([user_vector_sum, item_vector_sum]) + score = Similarity(type=metric, gamma=gamma)([user_vector_sum, item_vector_sum]) output = PredictionLayer("binary", False)(score) diff --git a/deepmatch/models/mind.py b/deepmatch/models/mind.py index 3053dd1..556e318 100755 --- a/deepmatch/models/mind.py +++ b/deepmatch/models/mind.py @@ -11,12 +11,13 @@ 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.layers import Concatenate, Lambda from tensorflow.python.keras.models import Model -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, MaskUserEmbedding, LabelAwareAttention, SampledSoftmaxLayer, \ + EmbeddingIndex +from ..utils import get_item_embedding def shape_target(target_emb_tmp, target_emb_size): @@ -27,7 +28,24 @@ def tile_user_otherfeat(user_other_feature, k_max): return tf.tile(tf.expand_dims(user_other_feature, -2), [1, k_max, 1]) -def MIND(user_feature_columns, item_feature_columns, num_sampled=5, k_max=2, p=1.0, dynamic_k=False, +def adaptive_interest_num(seq_len, k_max): + try: + log_len = tf.log1p(tf.cast(seq_len, dtype="float32")) + log_2 = tf.log(2.) + except AttributeError: + log_len = tf.math.log1p(tf.cast(seq_len, dtype="float32")) + log_2 = tf.math.log(2.) + k_user = tf.cast(tf.maximum( + 1., + tf.minimum( + tf.cast(k_max, dtype="float32"), # k_max + log_len / log_2 # hist_len + ) + ), dtype="int32") + return k_user + + +def MIND(user_feature_columns, item_feature_columns, num_sampled=5, k_max=2, p=100, dynamic_k=True, 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, output_activation='linear', seed=1024): """Instantiates the MIND Model architecture. @@ -110,14 +128,20 @@ def MIND(user_feature_columns, item_feature_columns, num_sampled=5, k_max=2, p=1 # max_len = history_emb.get_shape()[1].value hist_len = features['hist_len'] - high_capsule = CapsuleLayer(input_units=item_embedding_dim, - out_units=item_embedding_dim, max_len=seq_max_len, - k_max=k_max)((history_emb, hist_len)) + if dynamic_k: + interest_num = Lambda(adaptive_interest_num, arguments={'k_max': k_max})(hist_len) + high_capsule = CapsuleLayer(input_units=item_embedding_dim, + out_units=item_embedding_dim, max_len=seq_max_len, + k_max=k_max)((history_emb, hist_len, interest_num)) + else: + high_capsule = CapsuleLayer(input_units=item_embedding_dim, + out_units=item_embedding_dim, max_len=seq_max_len, + k_max=k_max)((history_emb, hist_len)) if len(dnn_input_emb_list) > 0 or len(dense_value_list) > 0: user_other_feature = combined_dnn_input(dnn_input_emb_list, dense_value_list) - other_feature_tile = tf.keras.layers.Lambda(tile_user_otherfeat, arguments={'k_max': k_max})(user_other_feature) + other_feature_tile = Lambda(tile_user_otherfeat, arguments={'k_max': k_max})(user_other_feature) user_deep_input = Concatenate()([NoMask()(other_feature_tile), high_capsule]) else: @@ -125,7 +149,7 @@ def MIND(user_feature_columns, item_feature_columns, num_sampled=5, k_max=2, p=1 user_embeddings = DNN(user_dnn_hidden_units, dnn_activation, l2_reg_dnn, dnn_dropout, dnn_use_bn, output_activation=output_activation, seed=seed, - name="user_embedding")( + name="user_dnn")( user_deep_input) item_inputs_list = list(item_features.values()) @@ -138,9 +162,10 @@ def MIND(user_feature_columns, item_feature_columns, num_sampled=5, k_max=2, p=1 pooling_item_embedding_weight = PoolingLayer()([item_embedding_weight]) if dynamic_k: - user_embedding_final = LabelAwareAttention(k_max=k_max, pow_p=p, )((user_embeddings, target_emb, hist_len)) + user_embeddings = MaskUserEmbedding(k_max)([user_embeddings, interest_num]) + user_embedding_final = LabelAwareAttention(k_max=k_max, pow_p=p)((user_embeddings, target_emb, interest_num)) else: - user_embedding_final = LabelAwareAttention(k_max=k_max, pow_p=p, )((user_embeddings, target_emb)) + user_embedding_final = LabelAwareAttention(k_max=k_max, pow_p=p)((user_embeddings, target_emb)) output = SampledSoftmaxLayer(num_sampled=num_sampled)( [pooling_item_embedding_weight, user_embedding_final, item_features[item_feature_name]]) diff --git a/deepmatch/models/sdm.py b/deepmatch/models/sdm.py index 6d6ca96..5fbd5c4 100644 --- a/deepmatch/models/sdm.py +++ b/deepmatch/models/sdm.py @@ -15,10 +15,10 @@ from tensorflow.python.keras.layers import Dense, Lambda from tensorflow.python.keras.models import Model -from deepmatch.utils import get_item_embedding from ..layers.core import PoolingLayer, SampledSoftmaxLayer, EmbeddingIndex from ..layers.interaction import UserAttention, SelfMultiHeadAttention, AttentionSequencePoolingLayer from ..layers.sequence import DynamicMultiRNN +from ..utils import get_item_embedding def SDM(user_feature_columns, item_feature_columns, history_feature_list, num_sampled=5, units=64, rnn_layers=2, @@ -156,8 +156,3 @@ def SDM(user_feature_columns, item_feature_columns, history_feature_list, num_sa 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 3b96a70..f146e87 100644 --- a/deepmatch/models/youtubednn.py +++ b/deepmatch/models/youtubednn.py @@ -9,10 +9,9 @@ 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, create_embedding_matrix -from ..layers.core import SampledSoftmaxLayer, EmbeddingIndex +from ..layers.core import SampledSoftmaxLayer, EmbeddingIndex, PoolingLayer +from ..utils import get_item_embedding def YoutubeDNN(user_feature_columns, item_feature_columns, num_sampled=5, diff --git a/docs/pics/code2.jpg b/docs/pics/code2.jpg new file mode 100644 index 0000000..e191f29 Binary files /dev/null and b/docs/pics/code2.jpg differ diff --git a/docs/pics/planet_github.png b/docs/pics/planet_github.png new file mode 100644 index 0000000..67efe96 Binary files /dev/null and b/docs/pics/planet_github.png differ diff --git a/docs/requirements.readthedocs.txt b/docs/requirements.readthedocs.txt new file mode 100644 index 0000000..1094240 --- /dev/null +++ b/docs/requirements.readthedocs.txt @@ -0,0 +1,2 @@ +tensorflow==2.6.2 +recommonmark==0.7.1 \ No newline at end of file diff --git a/docs/source/Examples.md b/docs/source/Examples.md index 2e652e2..e7efaad 100644 --- a/docs/source/Examples.md +++ b/docs/source/Examples.md @@ -39,13 +39,19 @@ if __name__ == "__main__": # 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'] + features = ['user_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 + id_count = data['movie_id'].value_counts() + mapdict = {t[0]: i for i, t in + enumerate(sorted([(k, v) for k, v in id_count.to_dict().items()], key=lambda x: x[1], reverse=True))} + data['movie_id'] = data['movie_id'].map(mapdict) + feature_max_idx['movie_id'] = data['movie_id'].max() + 1 + user_profile = data[["user_id", "gender", "age", "occupation", "zip"]].drop_duplicates('user_id') item_profile = data[["movie_id"]].drop_duplicates('movie_id') @@ -76,10 +82,15 @@ if __name__ == "__main__": # 3.Define Model and train - K.set_learning_phase(True) + import tensorflow as tf + + if tf.__version__ >= '2.0.0': + tf.compat.v1.disable_eager_execution() + else: + K.set_learning_phase(True) 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=True,k_max=2,num_sampled=5,user_dnn_hidden_units=(64,embedding_dim),init_std=0.001) model.compile(optimizer="adam", loss=sampledsoftmaxloss) # "binary_crossentropy") @@ -165,13 +176,19 @@ if __name__ == "__main__": # 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'] + features = ['user_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 + id_count = data['movie_id'].value_counts() + mapdict = {t[0]: i for i, t in + enumerate(sorted([(k, v) for k, v in id_count.to_dict().items()], key=lambda x: x[1], reverse=True))} + data['movie_id'] = data['movie_id'].map(mapdict) + feature_max_idx['movie_id'] = data['movie_id'].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') @@ -210,12 +227,12 @@ if __name__ == "__main__": 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() + else: + K.set_learning_phase(True) # units must be equal to item embedding dim! model = SDM(user_feature_columns, item_feature_columns, history_feature_list=['movie_id', 'genres'], diff --git a/docs/source/History.md b/docs/source/History.md index d0c9f61..3fbc964 100644 --- a/docs/source/History.md +++ b/docs/source/History.md @@ -1,4 +1,5 @@ # History +- 06/17/2022 : [v0.2.1](https://github.com/shenweichen/DeepMatch/releases/tag/v0.2.1) released.Fix some bugs. - 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). diff --git a/docs/source/Quick-Start.md b/docs/source/Quick-Start.md index 3053598..271ee44 100644 --- a/docs/source/Quick-Start.md +++ b/docs/source/Quick-Start.md @@ -1,7 +1,7 @@ # Quick-Start ## Installation Guide -Now `deepmatch` is available for python `2.7 `and `3.5, 3.6, 3.7`. +Now `deepmatch` is available for python `2.7 `and `3.5, 3.6, 3.7, 3.8`. `deepmatch` depends on tensorflow, you can specify to install the cpu version or gpu version through `pip`. ### CPU version diff --git a/docs/source/conf.py b/docs/source/conf.py index 4e882a8..fdddcde 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.2.0' +release = '0.2.1' # -- General configuration --------------------------------------------------- diff --git a/docs/source/deepmatch.layers.interaction.rst b/docs/source/deepmatch.layers.interaction.rst new file mode 100644 index 0000000..5492bef --- /dev/null +++ b/docs/source/deepmatch.layers.interaction.rst @@ -0,0 +1,7 @@ +deepmatch.layers.interaction module +=================================== + +.. automodule:: deepmatch.layers.interaction + :members: + :no-undoc-members: + :no-show-inheritance: diff --git a/docs/source/deepmatch.layers.sequence.rst b/docs/source/deepmatch.layers.sequence.rst new file mode 100644 index 0000000..6e88433 --- /dev/null +++ b/docs/source/deepmatch.layers.sequence.rst @@ -0,0 +1,7 @@ +deepmatch.layers.sequence module +================================ + +.. automodule:: deepmatch.layers.sequence + :members: + :no-undoc-members: + :no-show-inheritance: diff --git a/docs/source/index.rst b/docs/source/index.rst index f5b25cc..7ebbb81 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -18,12 +18,12 @@ You can read the latest code at https://github.com/shenweichen/DeepMatch News ----- +06/17/2022 : Fix some bugs. `Changelog `_ + 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 `_ - DisscussionGroup ----------------------- diff --git a/examples/colab_MovieLen1M_YoutubeDNN.ipynb b/examples/colab_MovieLen1M_YoutubeDNN.ipynb index a5bd90e..8587653 100644 --- a/examples/colab_MovieLen1M_YoutubeDNN.ipynb +++ b/examples/colab_MovieLen1M_YoutubeDNN.ipynb @@ -3,17 +3,6 @@ { "cell_type": "markdown", "metadata": { - "colab_type": "text", - "id": "view-in-github" - }, - "source": [ - "\"Open" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", "id": "rtox72csOQUN" }, "source": [ @@ -25,7 +14,6 @@ { "cell_type": "markdown", "metadata": { - "colab_type": "text", "id": "bTWHz-heMkyw" }, "source": [ @@ -34,52 +22,46 @@ }, { "cell_type": "code", - "execution_count": 0, + "execution_count": 1, "metadata": { "colab": { - "base_uri": "https://localhost:8080/", - "height": 510 + "base_uri": "https://localhost:8080/" }, - "colab_type": "code", "id": "yTl6d6jO1oqf", - "outputId": "19f1902e-17a6-4a07-ab42-f012bd286eda" + "outputId": "6f4516af-0f03-4f35-8f0d-80e803021095" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "--2020-04-10 09:06:19-- http://files.grouplens.org/datasets/movielens/ml-1m.zip\n", + "--2022-06-17 04:30:51-- http://files.grouplens.org/datasets/movielens/ml-1m.zip\n", "Resolving files.grouplens.org (files.grouplens.org)... 128.101.65.152\n", "Connecting to files.grouplens.org (files.grouplens.org)|128.101.65.152|:80... connected.\n", "HTTP request sent, awaiting response... 200 OK\n", "Length: 5917549 (5.6M) [application/zip]\n", "Saving to: ‘./ml-1m.zip’\n", "\n", - "\r", - "./ml-1m.zip 0%[ ] 0 --.-KB/s \r", - "./ml-1m.zip 7%[> ] 443.20K 1.85MB/s \r", - "./ml-1m.zip 100%[===================>] 5.64M 14.4MB/s in 0.4s \n", + "./ml-1m.zip 100%[===================>] 5.64M 3.47MB/s in 1.6s \n", "\n", - "2020-04-10 09:06:19 (14.4 MB/s) - ‘./ml-1m.zip’ saved [5917549/5917549]\n", + "2022-06-17 04:30:54 (3.47 MB/s) - ‘./ml-1m.zip’ saved [5917549/5917549]\n", "\n", - "--2020-04-10 09:06:21-- https://raw.githubusercontent.com/shenweichen/DeepMatch/master/examples/preprocess.py\n", - "Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 151.101.0.133, 151.101.64.133, 151.101.128.133, ...\n", - "Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|151.101.0.133|:443... connected.\n", + "--2022-06-17 04:30:54-- https://raw.githubusercontent.com/shenweichen/DeepMatch/master/examples/preprocess.py\n", + "Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 185.199.108.133, 185.199.109.133, 185.199.110.133, ...\n", + "Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|185.199.108.133|:443... connected.\n", "HTTP request sent, awaiting response... 200 OK\n", - "Length: 2049 (2.0K) [text/plain]\n", + "Length: 5642 (5.5K) [text/plain]\n", "Saving to: ‘preprocess.py’\n", "\n", - "preprocess.py 100%[===================>] 2.00K --.-KB/s in 0s \n", + "preprocess.py 100%[===================>] 5.51K --.-KB/s in 0s \n", "\n", - "2020-04-10 09:06:21 (34.8 MB/s) - ‘preprocess.py’ saved [2049/2049]\n", + "2022-06-17 04:30:54 (71.1 MB/s) - ‘preprocess.py’ saved [5642/5642]\n", "\n", "Archive: ml-1m.zip\n", " inflating: ml-1m/movies.dat \n", " inflating: ml-1m/ratings.dat \n", " inflating: ml-1m/README \n", " inflating: ml-1m/users.dat \n", - "TensorFlow 1.x selected.\n", "\u001b[33mWARNING: Skipping tensorflow as it is not installed.\u001b[0m\n" ] } @@ -88,16 +70,14 @@ "! wget http://files.grouplens.org/datasets/movielens/ml-1m.zip -O ./ml-1m.zip \n", "! wget https://raw.githubusercontent.com/shenweichen/DeepMatch/master/examples/preprocess.py -O preprocess.py\n", "! unzip -o ml-1m.zip \n", - "%tensorflow_version 1.x\n", "! pip uninstall -y -q tensorflow\n", - "! pip install -q tensorflow-gpu==1.14.0\n", + "! pip install -q tensorflow-gpu==2.5.0\n", "! pip install -q deepmatch" ] }, { "cell_type": "markdown", "metadata": { - "colab_type": "text", "id": "p9UxNHuPMuW2" }, "source": [ @@ -106,54 +86,22 @@ }, { "cell_type": "code", - "execution_count": 0, + "execution_count": 2, "metadata": { "colab": { - "base_uri": "https://localhost:8080/", - "height": 496 + "base_uri": "https://localhost:8080/" }, - "colab_type": "code", "id": "C_ZR6gzp1E2N", - "outputId": "8401132a-5090-464a-879d-a27492416f4c" + "outputId": "903724ad-114b-4ea8-d0ce-151f9f6d4cdc" }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ - "/usr/local/lib/python3.6/dist-packages/tensorflow/python/framework/dtypes.py:516: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n", - " _np_qint8 = np.dtype([(\"qint8\", np.int8, 1)])\n", - "/usr/local/lib/python3.6/dist-packages/tensorflow/python/framework/dtypes.py:517: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n", - " _np_quint8 = np.dtype([(\"quint8\", np.uint8, 1)])\n", - "/usr/local/lib/python3.6/dist-packages/tensorflow/python/framework/dtypes.py:518: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n", - " _np_qint16 = np.dtype([(\"qint16\", np.int16, 1)])\n", - "/usr/local/lib/python3.6/dist-packages/tensorflow/python/framework/dtypes.py:519: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n", - " _np_quint16 = np.dtype([(\"quint16\", np.uint16, 1)])\n", - "/usr/local/lib/python3.6/dist-packages/tensorflow/python/framework/dtypes.py:520: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n", - " _np_qint32 = np.dtype([(\"qint32\", np.int32, 1)])\n", - "/usr/local/lib/python3.6/dist-packages/tensorflow/python/framework/dtypes.py:525: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n", - " np_resource = np.dtype([(\"resource\", np.ubyte, 1)])\n", - "/usr/local/lib/python3.6/dist-packages/tensorboard/compat/tensorflow_stub/dtypes.py:541: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n", - " _np_qint8 = np.dtype([(\"qint8\", np.int8, 1)])\n", - "/usr/local/lib/python3.6/dist-packages/tensorboard/compat/tensorflow_stub/dtypes.py:542: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n", - " _np_quint8 = np.dtype([(\"quint8\", np.uint8, 1)])\n", - "/usr/local/lib/python3.6/dist-packages/tensorboard/compat/tensorflow_stub/dtypes.py:543: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n", - " _np_qint16 = np.dtype([(\"qint16\", np.int16, 1)])\n", - "/usr/local/lib/python3.6/dist-packages/tensorboard/compat/tensorflow_stub/dtypes.py:544: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n", - " _np_quint16 = np.dtype([(\"quint16\", np.uint16, 1)])\n", - "/usr/local/lib/python3.6/dist-packages/tensorboard/compat/tensorflow_stub/dtypes.py:545: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n", - " _np_qint32 = np.dtype([(\"qint32\", np.int32, 1)])\n", - "/usr/local/lib/python3.6/dist-packages/tensorboard/compat/tensorflow_stub/dtypes.py:550: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n", - " np_resource = np.dtype([(\"resource\", np.ubyte, 1)])\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "WARNING:tensorflow:From /usr/local/lib/python3.6/dist-packages/tensorflow/python/keras/initializers.py:143: calling RandomNormal.__init__ (from tensorflow.python.ops.init_ops) with dtype is deprecated and will be removed in a future version.\n", - "Instructions for updating:\n", - "Call initializer instance with the dtype argument instead of passing it to the constructor\n" + "WARNING:root:\n", + "DeepCTR version 0.9.1 detected. Your version is 0.8.2.\n", + "Use `pip install -U deepctr` to upgrade.Changelog: https://github.com/shenweichen/DeepCTR/releases/tag/v0.9.1\n" ] } ], @@ -172,7 +120,6 @@ { "cell_type": "markdown", "metadata": { - "colab_type": "text", "id": "fQq6O9XAMzPF" }, "source": [ @@ -181,27 +128,21 @@ }, { "cell_type": "code", - "execution_count": 0, + "execution_count": 3, "metadata": { "colab": { - "base_uri": "https://localhost:8080/", - "height": 139 + "base_uri": "https://localhost:8080/" }, - "colab_type": "code", "id": "lcO29zFb21Od", - "outputId": "97d9023e-cbd5-43dd-d2ef-769e3a34cf2c" + "outputId": "ea095585-f5ec-4d1c-9ffa-117531a5ed3b" }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ - "/usr/local/lib/python3.6/dist-packages/ipykernel_launcher.py:4: ParserWarning: Falling back to the 'python' engine because the 'c' engine does not support regex separators (separators > 1 char and different from '\\s+' are interpreted as regex); you can avoid this warning by specifying engine='python'.\n", - " after removing the cwd from sys.path.\n", - "/usr/local/lib/python3.6/dist-packages/ipykernel_launcher.py:6: ParserWarning: Falling back to the 'python' engine because the 'c' engine does not support regex separators (separators > 1 char and different from '\\s+' are interpreted as regex); you can avoid this warning by specifying engine='python'.\n", - " \n", - "/usr/local/lib/python3.6/dist-packages/ipykernel_launcher.py:8: ParserWarning: Falling back to the 'python' engine because the 'c' engine does not support regex separators (separators > 1 char and different from '\\s+' are interpreted as regex); you can avoid this warning by specifying engine='python'.\n", - " \n" + "/usr/local/lib/python3.7/dist-packages/pandas/util/_decorators.py:311: ParserWarning: Falling back to the 'python' engine because the 'c' engine does not support regex separators (separators > 1 char and different from '\\s+' are interpreted as regex); you can avoid this warning by specifying engine='python'.\n", + " return func(*args, **kwargs)\n" ] } ], @@ -213,7 +154,7 @@ "rnames = ['user_id','movie_id','rating','timestamp']\n", "ratings = pd.read_csv(data_path+'ml-1m/ratings.dat',sep='::',header=None,names=rnames)\n", "mnames = ['movie_id','title','genres']\n", - "movies = pd.read_csv(data_path+'ml-1m/movies.dat',sep='::',header=None,names=mnames)\n", + "movies = pd.read_csv(data_path+'ml-1m/movies.dat',sep='::',header=None,names=mnames,encoding=\"unicode_escape\")\n", "\n", "data = pd.merge(pd.merge(ratings,movies),user)#.iloc[:10000]\n" ] @@ -221,7 +162,6 @@ { "cell_type": "markdown", "metadata": { - "colab_type": "text", "id": "L0yCWxQxM3se" }, "source": [ @@ -230,107 +170,96 @@ }, { "cell_type": "code", - "execution_count": 0, + "execution_count": 4, "metadata": { "colab": { - "base_uri": "https://localhost:8080/", - "height": 1000 + "base_uri": "https://localhost:8080/" }, - "colab_type": "code", "id": "BMOvk_de2ML3", - "outputId": "1e43a5e7-f71c-45a4-bab4-e1d6b1a73669" + "outputId": "24448edc-9100-4a01-c13f-c48d0a6632e0" }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ - "100%|██████████| 6040/6040 [00:12<00:00, 489.02it/s]\n" + "100%|██████████| 6040/6040 [00:17<00:00, 336.80it/s]\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ - "6 6\n", - "WARNING:tensorflow:Entity > could not be transformed and will be executed as-is. Please report this to the AutgoGraph team. When filing the bug, set the verbosity to 10 (on Linux, `export AUTOGRAPH_VERBOSITY=10`) and attach the full output. Cause: converting >: AttributeError: module 'gast' has no attribute 'Num'\n", - "WARNING: Entity > could not be transformed and will be executed as-is. Please report this to the AutgoGraph team. When filing the bug, set the verbosity to 10 (on Linux, `export AUTOGRAPH_VERBOSITY=10`) and attach the full output. Cause: converting >: AttributeError: module 'gast' has no attribute 'Num'\n", - "WARNING:tensorflow:From /usr/local/lib/python3.6/dist-packages/deepctr/layers/utils.py:167: calling reduce_sum_v1 (from tensorflow.python.ops.math_ops) with keep_dims is deprecated and will be removed in a future version.\n", - "Instructions for updating:\n", - "keep_dims is deprecated, use keepdims instead\n", - "WARNING:tensorflow:From /usr/local/lib/python3.6/dist-packages/deepctr/layers/utils.py:193: div (from tensorflow.python.ops.math_ops) is deprecated and will be removed in a future version.\n", - "Instructions for updating:\n", - "Deprecated in favor of operator or tf.math.divide.\n", - "WARNING:tensorflow:Entity > could not be transformed and will be executed as-is. Please report this to the AutgoGraph team. When filing the bug, set the verbosity to 10 (on Linux, `export AUTOGRAPH_VERBOSITY=10`) and attach the full output. Cause: converting >: AssertionError: Bad argument number for Name: 3, expecting 4\n", - "WARNING: Entity > could not be transformed and will be executed as-is. Please report this to the AutgoGraph team. When filing the bug, set the verbosity to 10 (on Linux, `export AUTOGRAPH_VERBOSITY=10`) and attach the full output. Cause: converting >: AssertionError: Bad argument number for Name: 3, expecting 4\n", - "WARNING:tensorflow:Entity > could not be transformed and will be executed as-is. Please report this to the AutgoGraph team. When filing the bug, set the verbosity to 10 (on Linux, `export AUTOGRAPH_VERBOSITY=10`) and attach the full output. Cause: converting >: AssertionError: Bad argument number for Name: 3, expecting 4\n", - "WARNING: Entity > could not be transformed and will be executed as-is. Please report this to the AutgoGraph team. When filing the bug, set the verbosity to 10 (on Linux, `export AUTOGRAPH_VERBOSITY=10`) and attach the full output. Cause: converting >: AssertionError: Bad argument number for Name: 3, expecting 4\n", - "WARNING:tensorflow:Entity > could not be transformed and will be executed as-is. Please report this to the AutgoGraph team. When filing the bug, set the verbosity to 10 (on Linux, `export AUTOGRAPH_VERBOSITY=10`) and attach the full output. Cause: converting >: AssertionError: Bad argument number for Name: 3, expecting 4\n", - "WARNING: Entity > could not be transformed and will be executed as-is. Please report this to the AutgoGraph team. When filing the bug, set the verbosity to 10 (on Linux, `export AUTOGRAPH_VERBOSITY=10`) and attach the full output. Cause: converting >: AssertionError: Bad argument number for Name: 3, expecting 4\n", - "WARNING:tensorflow:Entity > could not be transformed and will be executed as-is. Please report this to the AutgoGraph team. When filing the bug, set the verbosity to 10 (on Linux, `export AUTOGRAPH_VERBOSITY=10`) and attach the full output. Cause: converting >: AssertionError: Bad argument number for Name: 3, expecting 4\n", - "WARNING: Entity > could not be transformed and will be executed as-is. Please report this to the AutgoGraph team. When filing the bug, set the verbosity to 10 (on Linux, `export AUTOGRAPH_VERBOSITY=10`) and attach the full output. Cause: converting >: AssertionError: Bad argument number for Name: 3, expecting 4\n", - "WARNING:tensorflow:Entity > could not be transformed and will be executed as-is. Please report this to the AutgoGraph team. When filing the bug, set the verbosity to 10 (on Linux, `export AUTOGRAPH_VERBOSITY=10`) and attach the full output. Cause: converting >: AssertionError: Bad argument number for Name: 3, expecting 4\n", - "WARNING: Entity > could not be transformed and will be executed as-is. Please report this to the AutgoGraph team. When filing the bug, set the verbosity to 10 (on Linux, `export AUTOGRAPH_VERBOSITY=10`) and attach the full output. Cause: converting >: AssertionError: Bad argument number for Name: 3, expecting 4\n", - "WARNING:tensorflow:Entity > could not be transformed and will be executed as-is. Please report this to the AutgoGraph team. When filing the bug, set the verbosity to 10 (on Linux, `export AUTOGRAPH_VERBOSITY=10`) and attach the full output. Cause: converting >: AssertionError: Bad argument number for Name: 3, expecting 4\n", - "WARNING: Entity > could not be transformed and will be executed as-is. Please report this to the AutgoGraph team. When filing the bug, set the verbosity to 10 (on Linux, `export AUTOGRAPH_VERBOSITY=10`) and attach the full output. Cause: converting >: AssertionError: Bad argument number for Name: 3, expecting 4\n", - "WARNING:tensorflow:From /usr/local/lib/python3.6/dist-packages/tensorflow/python/ops/init_ops.py:1288: calling VarianceScaling.__init__ (from tensorflow.python.ops.init_ops) with dtype is deprecated and will be removed in a future version.\n", + "6 6\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/usr/local/lib/python3.7/dist-packages/tensorflow/python/keras/backend.py:435: UserWarning: `tf.keras.backend.set_learning_phase` is deprecated and will be removed after 2020-10-11. To update it, simply pass a True/False value to the `training` argument of the `__call__` method of your layer or model.\n", + " warnings.warn('`tf.keras.backend.set_learning_phase` is deprecated and '\n", + "WARNING:tensorflow:From /usr/local/lib/python3.7/dist-packages/tensorflow/python/ops/array_ops.py:5049: calling gather (from tensorflow.python.ops.array_ops) with validate_indices is deprecated and will be removed in a future version.\n", "Instructions for updating:\n", - "Call initializer instance with the dtype argument instead of passing it to the constructor\n", - "WARNING:tensorflow:Entity > could not be transformed and will be executed as-is. Please report this to the AutgoGraph team. When filing the bug, set the verbosity to 10 (on Linux, `export AUTOGRAPH_VERBOSITY=10`) and attach the full output. Cause: converting >: AttributeError: module 'gast' has no attribute 'Num'\n", - "WARNING: Entity > could not be transformed and will be executed as-is. Please report this to the AutgoGraph team. When filing the bug, set the verbosity to 10 (on Linux, `export AUTOGRAPH_VERBOSITY=10`) and attach the full output. Cause: converting >: AttributeError: module 'gast' has no attribute 'Num'\n", - "WARNING:tensorflow:Entity > could not be transformed and will be executed as-is. Please report this to the AutgoGraph team. When filing the bug, set the verbosity to 10 (on Linux, `export AUTOGRAPH_VERBOSITY=10`) and attach the full output. Cause: converting >: AssertionError: Bad argument number for Name: 3, expecting 4\n", - "WARNING: Entity > could not be transformed and will be executed as-is. Please report this to the AutgoGraph team. When filing the bug, set the verbosity to 10 (on Linux, `export AUTOGRAPH_VERBOSITY=10`) and attach the full output. Cause: converting >: AssertionError: Bad argument number for Name: 3, expecting 4\n", - "Epoch 1/24\n", - "988129/988129 [==============================] - 22s 23us/sample - loss: 4.0715\n", - "Epoch 2/24\n", - "988129/988129 [==============================] - 21s 21us/sample - loss: 3.7855\n", - "Epoch 3/24\n", - "988129/988129 [==============================] - 21s 22us/sample - loss: 3.5596\n", - "Epoch 4/24\n", - "988129/988129 [==============================] - 21s 21us/sample - loss: 3.4199\n", - "Epoch 5/24\n", - "988129/988129 [==============================] - 21s 22us/sample - loss: 3.3216\n", - "Epoch 6/24\n", - "988129/988129 [==============================] - 21s 21us/sample - loss: 3.2499\n", - "Epoch 7/24\n", - "988129/988129 [==============================] - 21s 22us/sample - loss: 3.1846\n", - "Epoch 8/24\n", - "988129/988129 [==============================] - 21s 22us/sample - loss: 3.1264\n", - "Epoch 9/24\n", - "988129/988129 [==============================] - 22s 22us/sample - loss: 3.0923\n", - "Epoch 10/24\n", - "988129/988129 [==============================] - 21s 22us/sample - loss: 3.0566\n", - "Epoch 11/24\n", - "988129/988129 [==============================] - 21s 22us/sample - loss: 3.0336\n", - "Epoch 12/24\n", - "988129/988129 [==============================] - 21s 22us/sample - loss: 3.0066\n", - "Epoch 13/24\n", - "988129/988129 [==============================] - 21s 22us/sample - loss: 2.9872\n", - "Epoch 14/24\n", - "988129/988129 [==============================] - 21s 22us/sample - loss: 2.9659\n", - "Epoch 15/24\n", - "988129/988129 [==============================] - 21s 22us/sample - loss: 2.9476\n", - "Epoch 16/24\n", - "988129/988129 [==============================] - 21s 22us/sample - loss: 2.9363\n", - "Epoch 17/24\n", - "988129/988129 [==============================] - 21s 21us/sample - loss: 2.9267\n", - "Epoch 18/24\n", - "988129/988129 [==============================] - 21s 22us/sample - loss: 2.9179\n", - "Epoch 19/24\n", - "988129/988129 [==============================] - 21s 22us/sample - loss: 2.9012\n", - "Epoch 20/24\n", - "988129/988129 [==============================] - 21s 21us/sample - loss: 2.8925\n", - "Epoch 21/24\n", - "988129/988129 [==============================] - 21s 21us/sample - loss: 2.8830\n", - "Epoch 22/24\n", - "988129/988129 [==============================] - 22s 22us/sample - loss: 2.8797\n", - "Epoch 23/24\n", - "988129/988129 [==============================] - 21s 22us/sample - loss: 2.8711\n", - "Epoch 24/24\n", - "988129/988129 [==============================] - 21s 22us/sample - loss: 2.8619\n", + "The `validate_indices` argument has no effect. Indices are always validated on CPU and never validated on GPU.\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Train on 988129 samples\n", + "Epoch 1/20\n", + "988129/988129 [==============================] - 24s 25us/sample - loss: 4.4995\n", + "Epoch 2/20\n", + "988129/988129 [==============================] - 23s 23us/sample - loss: 4.2307\n", + "Epoch 3/20\n", + "988129/988129 [==============================] - 25s 25us/sample - loss: 3.8902\n", + "Epoch 4/20\n", + "988129/988129 [==============================] - 24s 24us/sample - loss: 3.6825\n", + "Epoch 5/20\n", + "988129/988129 [==============================] - 23s 23us/sample - loss: 3.5604\n", + "Epoch 6/20\n", + "988129/988129 [==============================] - 23s 23us/sample - loss: 3.4642\n", + "Epoch 7/20\n", + "988129/988129 [==============================] - 23s 23us/sample - loss: 3.3803\n", + "Epoch 8/20\n", + "988129/988129 [==============================] - 23s 23us/sample - loss: 3.3126\n", + "Epoch 9/20\n", + "988129/988129 [==============================] - 23s 23us/sample - loss: 3.2583\n", + "Epoch 10/20\n", + "988129/988129 [==============================] - 23s 23us/sample - loss: 3.2177\n", + "Epoch 11/20\n", + "988129/988129 [==============================] - 23s 23us/sample - loss: 3.1791\n", + "Epoch 12/20\n", + "988129/988129 [==============================] - 23s 23us/sample - loss: 3.1472\n", + "Epoch 13/20\n", + "988129/988129 [==============================] - 23s 23us/sample - loss: 3.1246\n", + "Epoch 14/20\n", + "988129/988129 [==============================] - 23s 23us/sample - loss: 3.0992\n", + "Epoch 15/20\n", + "988129/988129 [==============================] - 24s 24us/sample - loss: 3.0796\n", + "Epoch 16/20\n", + "988129/988129 [==============================] - 23s 23us/sample - loss: 3.0601\n", + "Epoch 17/20\n", + "988129/988129 [==============================] - 23s 23us/sample - loss: 3.0418\n", + "Epoch 18/20\n", + "988129/988129 [==============================] - 23s 23us/sample - loss: 3.0265\n", + "Epoch 19/20\n", + "988129/988129 [==============================] - 23s 23us/sample - loss: 3.0119\n", + "Epoch 20/20\n", + "988129/988129 [==============================] - 23s 23us/sample - loss: 2.9994\n", "(6040, 32)\n", "(3706, 32)\n" ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/usr/local/lib/python3.7/dist-packages/tensorflow/python/keras/engine/training.py:2426: UserWarning: `Model.state_updates` will be removed in a future version. This property should not be used in TensorFlow 2.0, as `updates` are applied automatically.\n", + " warnings.warn('`Model.state_updates` will be removed in a future version. '\n" + ] } ], "source": [ @@ -342,13 +271,19 @@ "\n", "# 1.Label Encoding for sparse features,and process sequence features with `gen_date_set` and `gen_model_input`\n", "\n", - "features = ['user_id', 'movie_id', 'gender', 'age', 'occupation', 'zip']\n", + "features = ['user_id', 'gender', 'age', 'occupation', 'zip']\n", "feature_max_idx = {}\n", "for feature in features:\n", " lbe = LabelEncoder()\n", " data[feature] = lbe.fit_transform(data[feature]) + 1\n", " feature_max_idx[feature] = data[feature].max() + 1\n", "\n", + "id_count = data['movie_id'].value_counts() \n", + "mapdict = {t[0]: i for i, t in\n", + " enumerate(sorted([(k, v) for k, v in id_count.to_dict().items()], key=lambda x: x[1], reverse=True))}\n", + "data['movie_id'] = data['movie_id'].map(mapdict)\n", + "feature_max_idx['movie_id'] = data['movie_id'].max() + 1\n", + "\n", "user_profile = data[[\"user_id\", \"gender\", \"age\", \"occupation\", \"zip\"]].drop_duplicates('user_id')\n", "\n", "item_profile = data[[\"movie_id\"]].drop_duplicates('movie_id')\n", @@ -379,14 +314,14 @@ "\n", "# 3.Define Model and train\n", "\n", - "K.set_learning_phase(True)\n", - "\n", "import tensorflow as tf\n", "if tf.__version__ >= '2.0.0':\n", " tf.compat.v1.disable_eager_execution()\n", - "\n", + "else:\n", + " K.set_learning_phase(True)\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))\n", + "# model = MIND(user_feature_columns,item_feature_columns,dynamic_k=True,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", @@ -412,7 +347,6 @@ { "cell_type": "markdown", "metadata": { - "colab_type": "text", "id": "w_G3KWslKmJo" }, "source": [ @@ -422,30 +356,27 @@ { "cell_type": "markdown", "metadata": { - "colab_type": "text", "id": "5SvyQLNVKkcs" }, "source": [] }, { "cell_type": "code", - "execution_count": 0, + "execution_count": 5, "metadata": { "colab": { - "base_uri": "https://localhost:8080/", - "height": 51 + "base_uri": "https://localhost:8080/" }, - "colab_type": "code", "id": "j2ZNYNBOOqrN", - "outputId": "2938673c-ff81-49a2-86d8-4266d2060ea3" + "outputId": "442609e8-f94d-42c3-d945-582374a5fa77" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Requirement already satisfied: faiss-cpu in /usr/local/lib/python3.6/dist-packages (1.6.3)\n", - "Requirement already satisfied: numpy in /usr/local/lib/python3.6/dist-packages (from faiss-cpu) (1.18.2)\n" + "Looking in indexes: https://pypi.org/simple, https://us-python.pkg.dev/colab-wheels/public/simple/\n", + "Requirement already satisfied: faiss-cpu in /usr/local/lib/python3.7/dist-packages (1.7.2)\n" ] } ], @@ -455,22 +386,20 @@ }, { "cell_type": "code", - "execution_count": 0, + "execution_count": 6, "metadata": { "colab": { - "base_uri": "https://localhost:8080/", - "height": 85 + "base_uri": "https://localhost:8080/" }, - "colab_type": "code", "id": "6TY1l27iJU8U", - "outputId": "5316c37f-fef1-44b3-8c31-6600d1e44da5" + "outputId": "3070ad94-9f84-4b51-d095-18053b84f5ce" }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ - "6040it [00:01, 4290.79it/s]" + "6040it [00:02, 3004.17it/s]" ] }, { @@ -478,8 +407,8 @@ "output_type": "stream", "text": [ "\n", - "recall 0.26473509933774836\n", - "hit rate 0.26473509933774836\n" + "recall 0.3033112582781457\n", + "hit rate 0.3033112582781457\n" ] }, { @@ -524,10 +453,8 @@ }, { "cell_type": "code", - "execution_count": 0, + "execution_count": 6, "metadata": { - "colab": {}, - "colab_type": "code", "id": "a97TB0obOrRe" }, "outputs": [], @@ -537,13 +464,12 @@ "metadata": { "accelerator": "GPU", "colab": { - "authorship_tag": "ABX9TyOrqZNeC0DgyPX2JmJid1m7", "collapsed_sections": [], - "include_colab_link": true, "name": "colab_MovieLen1M_YoutubeDNN", "provenance": [], "toc_visible": true }, + "gpuClass": "standard", "kernelspec": { "display_name": "Python 3", "language": "python", diff --git a/examples/run_sdm.py b/examples/run_sdm.py index fdf1def..902ea17 100644 --- a/examples/run_sdm.py +++ b/examples/run_sdm.py @@ -18,13 +18,19 @@ # 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'] + features = ['user_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 + id_count = data['movie_id'].value_counts() + mapdict = {t[0]: i for i, t in + enumerate(sorted([(k, v) for k, v in id_count.to_dict().items()], key=lambda x: x[1], reverse=True))} + data['movie_id'] = data['movie_id'].map(mapdict) + feature_max_idx['movie_id'] = data['movie_id'].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') diff --git a/examples/run_youtubednn.py b/examples/run_youtubednn.py index d98a472..b9881ed 100644 --- a/examples/run_youtubednn.py +++ b/examples/run_youtubednn.py @@ -1,13 +1,12 @@ import pandas as pd from deepctr.feature_column import SparseFeat, VarLenSparseFeat +from deepmatch.models import * +from deepmatch.utils import sampledsoftmaxloss from preprocess import gen_data_set, gen_model_input from sklearn.preprocessing import LabelEncoder from tensorflow.python.keras import backend as K from tensorflow.python.keras.models import Model -from deepmatch.models import * -from deepmatch.utils import sampledsoftmaxloss - if __name__ == "__main__": data = pd.read_csvdata = pd.read_csv("./movielens_sample.txt") @@ -17,13 +16,19 @@ # 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'] + features = ['user_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 + id_count = data['movie_id'].value_counts() + mapdict = {t[0]: i for i, t in + enumerate(sorted([(k, v) for k, v in id_count.to_dict().items()], key=lambda x: x[1], reverse=True))} + data['movie_id'] = data['movie_id'].map(mapdict) + feature_max_idx['movie_id'] = data['movie_id'].max() + 1 + user_profile = data[["user_id", "gender", "age", "occupation", "zip"]].drop_duplicates('user_id') item_profile = data[["movie_id"]].drop_duplicates('movie_id') @@ -54,13 +59,15 @@ # 3.Define Model and train - K.set_learning_phase(True) import tensorflow as tf + if tf.__version__ >= '2.0.0': - tf.compat.v1.disable_eager_execution() + tf.compat.v1.disable_eager_execution() + else: + K.set_learning_phase(True) 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)) + # model = MIND(user_feature_columns,item_feature_columns,dynamic_k=True,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 8ab2c5c..d5fe36e 100644 --- a/setup.py +++ b/setup.py @@ -4,14 +4,14 @@ long_description = fh.read() REQUIRED_PACKAGES = [ - 'h5py', 'requests', "deepctr==0.8.2" + 'requests', "deepctr==0.9.1" ] setuptools.setup( name="deepmatch", - version="0.2.0", + version="0.2.1", author="Weichen Shen", - author_email="wcshen1994@163.com", + author_email="weichenswc@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**.", long_description=long_description, long_description_content_type="text/markdown", @@ -38,6 +38,7 @@ 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', 'Topic :: Scientific/Engineering', 'Topic :: Scientific/Engineering :: Artificial Intelligence', 'Topic :: Software Development', diff --git a/tests/models/MIND_test.py b/tests/models/MIND_test.py index 0b9509b..ea9c9a1 100644 --- a/tests/models/MIND_test.py +++ b/tests/models/MIND_test.py @@ -1,21 +1,28 @@ +import pytest import tensorflow as tf -from tensorflow.python.keras import backend as K - 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 -def test_MIND(): +@pytest.mark.parametrize( + 'dynamic_k,p', + [(False, 1), (True, 100) + ] +) +def test_MIND(dynamic_k, p): model_name = "MIND" x, y, user_feature_columns, item_feature_columns = get_xy_fd(False) - K.set_learning_phase(True) if tf.__version__ >= '2.0.0': tf.compat.v1.disable_eager_execution() - - model = MIND(user_feature_columns, item_feature_columns, num_sampled=2, user_dnn_hidden_units=(16, 4)) + else: + K.set_learning_phase(True) + model = MIND(user_feature_columns, item_feature_columns, num_sampled=2, p=p, dynamic_k=dynamic_k, + user_dnn_hidden_units=(16, 4)) model.compile('adam', sampledsoftmaxloss) check_model(model, model_name, x, y) diff --git a/tests/models/SDM_test.py b/tests/models/SDM_test.py index 60e3e5a..072caf8 100644 --- a/tests/models/SDM_test.py +++ b/tests/models/SDM_test.py @@ -9,13 +9,12 @@ def test_SDM(): model_name = "SDM" - tf.keras.backend.set_learning_phase(1) x, y, user_feature_columns, item_feature_columns, history_feature_list = get_xy_fd_sdm(False) - K.set_learning_phase(True) if tf.__version__ >= '2.0.0': tf.compat.v1.disable_eager_execution() - + else: + K.set_learning_phase(True) model = SDM(user_feature_columns, item_feature_columns, history_feature_list, units=8) # model.summary() diff --git a/tests/models/YoutubeDNN_test.py b/tests/models/YoutubeDNN_test.py index 46afb03..a014c16 100644 --- a/tests/models/YoutubeDNN_test.py +++ b/tests/models/YoutubeDNN_test.py @@ -10,10 +10,11 @@ def test_YoutubeDNN(): model_name = "YoutubeDNN" x, y, user_feature_columns, item_feature_columns = get_xy_fd(False) - K.set_learning_phase(True) if tf.__version__ >= '2.0.0': tf.compat.v1.disable_eager_execution() + else: + K.set_learning_phase(True) model = YoutubeDNN(user_feature_columns, item_feature_columns, num_sampled=2, user_dnn_hidden_units=(16, 4)) model.compile('adam', sampledsoftmaxloss)