tensorflow / tensorflow/recommenders
How to handle items that can be purchased/used just once?
@maciejkula is already working on this.
Since Nov 29, 2020.
- Dominant language
- Python
- Stars
- 2k
- Forks
- 300
- PR merge metrics
- No merged PRs in 30d
Description
I'm training the model using purchase data for a certain period and validating it on what the same users purchased in the future.
How should I set up the model in case the items can be purchased just once? I don't want the model to recommend items present in the training set but only choose among the ones in the test set (which would be the ones available at that time in the future).
I was thinking of setting the candidates_dataset in the FactorizedTopK metric to use just the candidates from the test group but how would the model then be able to compute the loss on the training set?
self.task = tfrs.tasks.Retrieval(
metrics=tfrs.metrics.FactorizedTopK(
candidates=candidates_dataset.batch(8192).map(self.candidate_model).cache()
))
The candidates_dataset is defined as
candidates = tf.data.Dataset.from_tensor_slices(
dict(train_df[candidate_features] \
.append(test_df[candidate_features]) \
.drop_duplicates())) \
.cache(tempfile.NamedTemporaryFile().name)
EDIT:
I think the solution might be defining different train_step and test_step using 2 different tasks that use different candidates. This is my attempt:
# using input embedding layer for candidate model
class RetrievalModel(tfrs.models.Model):
def __init__(self, layer_sizes, train_dataset, candidates_dataset,
max_tokens=100_000, embed_dim=32):
super().__init__()
self.embed_dim = embed_dim
self.query_model = QueryModel(layer_sizes, train_dataset, max_tokens=max_tokens, embed_dim=embed_dim)
self.candidate_model = CandidateModel(layer_sizes, self.query_model)
self.training_task = tfrs.tasks.Retrieval(
metrics=tfrs.metrics.FactorizedTopK(
candidates=train.batch(8192).map(self.candidate_model).cache()
))
self.test_task = tfrs.tasks.Retrieval(
metrics=tfrs.metrics.FactorizedTopK(
candidates=test.batch(8192).map(self.candidate_model).cache()
))
# def compute_loss(self, features, training=False):
# return self.task(self.query_model(features),
# self.candidate_model(features),
# compute_metrics=not training)
def train_step(self, features) -> tf.Tensor:
# Set up a gradient tape to record gradients.
with tf.GradientTape() as tape:
# Loss computation.
query_vectors = self.query_model(features)
candidate_vectors = self.candidate_model(features)
loss = self.training_task(query_vectors, candidate_vectors, compute_metrics=False)
# Handle regularization losses as well.
regularization_loss = sum(self.losses)
total_loss = loss + regularization_loss
gradients = tape.gradient(total_loss, self.trainable_variables)
self.optimizer.apply_gradients(zip(gradients, self.trainable_variables))
metrics = {metric.name: metric.result() for metric in self.metrics}
metrics["loss"] = loss
metrics["regularization_loss"] = regularization_loss
metrics["total_loss"] = total_loss
return metrics
def test_step(self, features) -> tf.Tensor:
# Loss computation.
query_vectors = self.query_model(features)
candidate_vectors = self.candidate_model(features)
loss = self.test_task(query_vectors, candidate_vectors, compute_metrics=True)
# Handle regularization losses as well.
regularization_loss = sum(self.losses)
total_loss = loss + regularization_loss
metrics = {metric.name: metric.result() for metric in self.metrics}
metrics["loss"] = loss
metrics["regularization_loss"] = regularization_loss
metrics["total_loss"] = total_loss
return metrics
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.