tensorflow / recommenders

TensorFlow Recommenders is a library for building recommender system models using TensorFlow.
Apache License 2.0
1.82k stars 273 forks source link

ValueError: The first argument to `Layer.call` must always be passed. #173

Closed igorkf closed 3 years ago

igorkf commented 3 years ago

Hi. I'm trying to reproduce example from: https://www.tensorflow.org/recommenders/examples/movielens_side_information

I'm doing exactly like the tutorial, defining classes, methods etc. I'm using Google Colab.
But when I try to fit the model (exactly like the tutorial): model.fit(cached_train, epochs=3)

I receive the error:

ValueError: The first argument to `Layer.call` must always be passed.

I think that this errors is due the __call__ method in the Models.
This are the models that the tutorial used.
User model

embedding_dimension = 32

class UserModel(tf.keras.Model):

  def __init__(self, embedding_dimension, timestamp_buckets):
    super(UserModel, self).__init__()
    # An embedding column for user ids.
    user_id_feature = tf.feature_column.embedding_column(
        tf.feature_column.categorical_column_with_vocabulary_list(
            "user_id", unique_user_ids,
        ),
        embedding_dimension,
    )

    # An embedding column for the bucketized timestamps: there will be a separate
    # embedding for each of the timestamp buckets.
    time_feature = tf.feature_column.embedding_column(
        tf.feature_column.bucketized_column(
            tf.feature_column.numeric_column("timestamp"),
            timestamp_buckets.tolist(),
        ),
        embedding_dimension,
    )
    self.embedding_layer = tf.keras.layers.DenseFeatures(
        [user_id_feature, time_feature],
        name="query_embedding",
    )
    self.dense_layer = tf.keras.layers.Dense(embedding_dimension)

   # maybe the error is here?
  def call(self, inputs):
    input_embedding = self.embedding_layer(inputs)
    return self.dense_layer(input_embedding)

Movie model

class MovieModel(tf.keras.Model):

  def __init__(self, embedding_dimension):
    super(MovieModel, self).__init__()
    movie_features = [tf.feature_column.embedding_column(
        tf.feature_column.categorical_column_with_vocabulary_list(
            "movie_id", unique_movie_ids,
        ),
        embedding_dimension,
    )]
    self.embedding_layer = tf.keras.layers.DenseFeatures(movie_features, name="movie_embedding")

  # maybe the error is here?
  def call(self, inputs):
    return self.embedding_layer(inputs)

And this is the model that user the two models above:

class MovielensModel(tfrs.models.Model):

  def __init__(self, embedding_dimension, timestamp_buckets):
    super().__init__()
    self.user_model: tf.keras.Model = UserModel(embedding_dimension, timestamp_buckets)
    self.movie_model: tf.keras.Model = MovieModel(embedding_dimension)
    self.task = tfrs.tasks.Retrieval(
        loss=tfrs.metrics.FactorizedTopK(
            candidates=movies.batch(128).map(self.movie_model)
        )
    )

  def compute_loss(self, features, training=False):
    user_embeddings = self.user_model({"user_id": features["user_id"],
                                       "timestamp": features["timestamp"]})
    positive_movie_embeddings = self.movie_model(
        {"movie_id": features["movie_id"]}
    )

    return self.task(user_embeddings, positive_movie_embeddings)
igorkf commented 3 years ago

I think the problem was this part:

self.task = tfrs.tasks.RetrievalTask(
        corpus_metrics=tfrs.metrics.FactorizedTopK(
            candidates=movies.batch(128).map(self.movie_model)
        )
    )

I changed to this, and works fine:

self.task = tfrs.tasks.Retrieval(
        metrics=tfrs.metrics.FactorizedTopK(
            candidates=movies.batch(128).map(self.movie_model)
        )
    )

Maybe the tutorial must be updated?