Tensorflow Recommenders: what is features in computed_loss method in tfrs.Model class (from Retrieval tutorial)

Viewed 294

I'm following the Retrieval tutorial from the TFRS (TensorFlow Recommenders) library, and I'm getting confused in this part:

class MovielensModel(tfrs.Model):

  def __init__(self, user_model, movie_model):
    super().__init__()
    self.movie_model: tf.keras.Model = movie_model
    self.user_model: tf.keras.Model = user_model
    self.task: tf.keras.layers.Layer = task

  def compute_loss(self, features: Dict[Text, tf.Tensor], training=False) -> tf.Tensor:
    # We pick out the user features and pass them into the user model.
    user_embeddings = self.user_model(features["user_id"])
    # And pick out the movie features and pass them into the movie model,
    # getting embeddings back.
    positive_movie_embeddings = self.movie_model(features["movie_title"])

    # The task computes the loss and the metrics.
    return self.task(user_embeddings, positive_movie_embeddings)

Which is followed by this:

model = MovielensModel(user_model, movie_model)
model.compile(optimizer=tf.keras.optimizers.Adagrad(learning_rate=0.1))

I have a question about these chunks of code:

  • when it says user_embeddings = self.user_model(features["user_id"]) (and also (self.movie_model(features["movie_title"])) it seems that features is a dictionary, but it's not defined in any part before in the tutorial. I also checked in compute_loss source code here, to see if it's an attribute of that method or something, but I don't find anything either... So my question here is, what is features? How can the code work well, running that that was not defined before? I tried it outside of the class, by running just this: user_model(features["user_id"]) and of course it doesn't work saying that features is not defined. But why does it work then, when the class is instantiated and later when it's compiled? (second chunk of code above).

Thank you very much!

3 Answers

Features is a line of a tf.data.Dataset constructed with a dictionnary. You could create the same dataset by using using tf.data.Dataset.from_tensor_slices on a dictionnary.

For example :

dict_ = {'user_id' : [1, 2], 'movie_title' : ['foo', 'bar']}
dataset = tf.data.Dataset.from_tensor_slices(dict_)

for features in dataset :
    print(features)

Will return :

{'user_id': <tf.Tensor: shape=(), dtype=int32, numpy=1>, 'movie_title': <tf.Tensor: shape=(), dtype=string, numpy=b'foo'>}
{'user_id': <tf.Tensor: shape=(), dtype=int32, numpy=2>, 'movie_title': <tf.Tensor: shape=(), dtype=string, numpy=b'bar'>}

Note that, dataset['user_id'] will return an error because a tf.data.Dataset isn't callable in such a way. The compute_loss function of the MovielensModel(tfrs.Model) is receiving the items of the tf.data.Dataset but it needs tensors to work properly. Thus, the function keep only the tensor associated to the 'user_id' part (or the 'movie_title').

This :

for features in dataset:
    print(features['user_id'])

Will return a tensor :

tf.Tensor(1, shape=(), dtype=int32)
tf.Tensor(2, shape=(), dtype=int32)

In your model.fit() you send in training data. The "features" is that data, that is being fed to the "call" function of your class: "user_model".

You don't have this class in your example here, but if you look into it, you will find the call function there.

If you look carefully at the MovieLens class, you will see that it defines a key-value dictionary called "features".

 def compute_loss(self, features: Dict[Text, tf.Tensor], training=False) -> tf.Tensor:
Related