recommenders icon indicating copy to clipboard operation
recommenders copied to clipboard

Please help! Can't get model together

Open joaoalveshenriques opened this issue 4 years ago • 3 comments

I am new to TensorFlow, in fact, I started exploring this library 2-3 weeks ago, because I am doing an internship where my project is a Recommender System.

I could follow all the tutorials available from the TF team and adapt them to my dataset, although, when I try to adapt the models to what I need, I can't manage to do it. I am doing a retail recommender system with features for the users and for the products, I would like to apply the Deep & Cross Network so the model can best learn the influence of each variable on the customers' habits.

I have tried this for these last weeks and can't manage to get it, I am starting to become very desperate, because I have a couple of weeks to finish this project. I have tried to ask for help on several places but I can't get any answer.

I know this is not the type of questions you use to answer but I am really desperate, so I will try my luck.

Here is an example of a code I tried (mixing the DCN tutorial with the DNN):

`class UserModel(tf.keras.Model):

def __init__(self):
    super().__init__()
    
    self.embedding_dimension = 32
    
    self.user_embedding = tf.keras.Sequential([
        tf.keras.layers.StringLookup(
            vocabulary=unique_user_ids, mask_token=None),
        tf.keras.layers.Embedding(len(unique_user_ids) + 1, 32),
    ])
    
    str_features = [
                                        'user_gender'
                                        
    ]
    int_features = [
                                        'timestamp',
                                        'user_age',
                                        'user_lat',
                                        'user_long'
    ]
    
    self._all_features = str_features + int_features
    self._embeddings = {}
    
    # Compute embeddings for string features.
    for feature_name in str_features:
        vocabulary = vocabularies[feature_name]
        self._embeddings[feature_name] = tf.keras.Sequential(
          [tf.keras.layers.experimental.preprocessing.StringLookup(
              vocabulary=vocabulary, mask_token=None),
           tf.keras.layers.Embedding(len(vocabulary) + 1,
                                     self.embedding_dimension)
    ])

    # Compute embeddings for int features.
    for feature_name in int_features:
        vocabulary = vocabularies[feature_name]
        self._embeddings[feature_name] = tf.keras.Sequential(
          [tf.keras.layers.experimental.preprocessing.IntegerLookup(
              vocabulary=vocabulary, mask_token=None),
           tf.keras.layers.Embedding(len(vocabulary) + 1,
                                     self.embedding_dimension)
    ])
def call(self,features):
    # Concatenate embeddings
    embeddings = []
    for feature_name in self._all_features:
        embedding_fn = self._embeddings[feature_name]
        embeddings.append(embedding_fn(features[feature_name]))

    return tf.concat([
        self.user_embedding(features["user_id"]),
        tf.concat(embeddings, axis=1)
    ], axis=1)`

`class QueryModel(tf.keras.Model): """Model for encoding user queries."""

def __init__(self, deep_layer_sizes, projection_dim=None):
    """Model for encoding user queries.

    Args:
        layer_sizes:
            A list of integers where the i-th entry represents the number of units
            the i-th layer contains.
    """
    super().__init__()

    # We first use the user model for generating embeddings.
    self.embedding_model = UserModel()

    self._cross_layer = tfrs.layers.dcn.Cross(
        projection_dim=projection_dim,
        kernel_initializer="glorot_uniform")
    
    # Then construct the layers.
    self.dense_layers = tf.keras.Sequential()

    # Use the ReLU activation for all but the last layer.
    self._deep_layers = [tf.keras.layers.Dense(layer_size, activation="relu")
        for layer_size in deep_layer_sizes]

    self._logit_layer = tf.keras.layers.Dense(1)

def call(self, features):
    feature_embedding = self.embedding_model(features)
    return self.dense_layers(feature_embedding)`

`class ProductModel(tf.keras.Model):

def __init__(self):
    super().__init__()
    
    self.product_embedding = tf.keras.Sequential([
        tf.keras.layers.StringLookup(
            vocabulary=unique_product_names,mask_token=None),
    tf.keras.layers.Embedding(len(unique_product_names) + 1, 32)
    ])

    
    str_features = [
                                        'product_colour',
                                        'product_tear',
                                        'product_tonality',
                                        'product_gender',
                                        'product_age',
                                        'product_category',
                                        'product_fit',
                                        'product_rise',
                                        'product_neckline',
                                        'product_sleeve',
                                        'product_denim',
                                        'product_stretch',
                                        'product_wash'
    ]
    
    int_features = [
                                        'price'
    ]
    
    self._all_features = str_features + int_features
    
    self._embeddings = {}
    
    # Compute embeddings for string features.
    for feature_name in str_features:
        vocabulary = vocabularies[feature_name]
        self._embeddings[feature_name] = tf.keras.Sequential(
          [tf.keras.layers.experimental.preprocessing.StringLookup(
              vocabulary=vocabulary, mask_token=None),
           tf.keras.layers.Embedding(len(vocabulary) + 1, 32)
    ])

    # Compute embeddings for int features.
    for feature_name in int_features:
        vocabulary = vocabularies[feature_name]
        self._embeddings[feature_name] = tf.keras.Sequential(
          [tf.keras.layers.experimental.preprocessing.IntegerLookup(
              vocabulary=vocabulary, mask_token=None),
           tf.keras.layers.Embedding(len(vocabulary) + 1, 32)
    ])
def call(self,features):
    # Concatenate embeddings
    embeddings = []
    for feature_name in self._all_features:
        embedding_fn = self._embeddings[feature_name]
        embeddings.append(embedding_fn(features[feature_name]))

    return tf.concat([
        self.product_embedding(features["product_id"]),
        tf.concat(embeddings, axis=1)
    ], axis=1)`

`class CandidateModel(tf.keras.Model): """Model for encoding movies."""

def __init__(self, deep_layer_sizes):
    """Model for encoding movies.

    Args:
        layer_sizes:
            A list of integers where the i-th entry represents the number of units
            the i-th layer contains.
    """
    super().__init__()

    self.embedding_model = ProductModel()

    self._cross_layer = tfrs.layers.dcn.Cross(
        projection_dim=None,
        kernel_initializer="glorot_uniform")
    
    # Then construct the layers.
    self.dense_layers = tf.keras.Sequential()

    # Use the ReLU activation for all but the last layer.
    self._deep_layers = [tf.keras.layers.Dense(layer_size, activation="relu")
        for layer_size in deep_layer_sizes]

    self._logit_layer = tf.keras.layers.Dense(1)

def call(self, features):
    feature_embedding = self.embedding_model(features)
    return self.dense_layers(feature_embedding)`

`class MainModel(tfrs.models.Model):

def __init__(self, deep_layer_sizes):
    super().__init__()
    self.query_model = QueryModel(deep_layer_sizes)
    self.candidate_model = CandidateModel(deep_layer_sizes)
    self.task = tfrs.tasks.Retrieval(
        metrics=tfrs.metrics.FactorizedTopK(
            candidates=products.batch(128).map(self.candidate_model),
        ),
    )

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.query_model(features["user_id"])
    # And pick out the movie features and pass them into the movie model,
    # getting embeddings back.
    product_embeddings = self.candidate_model(features["product_id"])

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

When I try to run this: `num_epochs = 300

model = MainModel(deep_layer_sizes=[192, 192]) model.compile(optimizer=tf.keras.optimizers.Adagrad(0.1))

one_layer_history = model.fit( cached_train, validation_data=cached_test, validation_freq=5, epochs=num_epochs, verbose=0)

accuracy = one_layer_history.history["val_factorized_top_k/top_100_categorical_accuracy"][-1] print(f"Top-100 accuracy: {accuracy:.2f}.")`

I get this error: TypeError: Only integers, slices (:), ellipsis (...), tf.newaxis (None) and scalar tf.int32/tf.int64 tensors are valid indices, got 'product_colour'

I don't know why the variables in the Candidate Model are not in the correct format, since I have done the same way on the Query Model. Even if I remove those features from the Product/Candidate Model I get different errors. I have been debuging code for the last 4 days, and every time I find something, I get another error.

And I also tried to embed every single feature manually and I get errors and errors without any sight of hope.

Basically all I want is a DCN Model that I can retrieve recommendations from.

I kindly ask for your help.

joaoalveshenriques avatar Oct 14 '21 16:10 joaoalveshenriques

Hey @joaoalveshenriques, That error is because you are trying to index a tensor with a string. Somewhere in the code you think you have a dictionary, where you don't.

my bet is here

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.query_model(features["user_id"])
    # And pick out the movie features and pass them into the movie model,
    # getting embeddings back.
    product_embeddings = self.candidate_model(features["product_id"])

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

You're only passing the user_id and the product_id to your query and candidate models, and your embedding_model looks like it should receive the complete dictionary of tensor slices.

patrickorlando avatar Nov 18 '21 21:11 patrickorlando

Ahoi @joaoalveshenriques , for debugging & dimension fitting you can try to call the model directly QueryModel(deep_layer_size)({features}) By changing or deleting some features, you can see how the Model's responds to ... e.g for the DCN tutorial:

hp = hyper_parameter
qm = QueryModel(hp['query'])({
    'user_id': tf.constant(['27']),
    'timestamp': tf.constant([893286638]),
    'raw_user_age': tf.constant([0]),
    'user_occupation_text': tf.constant(['something else']),
    'user_zip_code': tf.constant(['0'])})
cm = CandidateModel(hp['candidate'])(tf.constant(["Flipper (1963)"]))

print(qm)
print(cm)
print("---Ranks---")
print(RankingModel(hp['rank'])((qm, cm)))

Outputs:

tf.Tensor(
[[ 0.01111957 -0.00569402  0.0125189  -0.00817162 -0.01598354 -0.00456765
  -0.00278555 -0.00917748  0.00891546  0.00064161  0.0166869  -0.01235288
  -0.0066494  -0.00980284  0.00471782 -0.00196177  0.00076775 -0.00880383
  -0.01292935  0.02296012  0.0247306  -0.01458024  0.02450829 -0.02104228
   0.00308327  0.00611366  0.00268212 -0.0138782   0.00437069  0.00086666
   0.00729643 -0.00432106  0.02260074  0.00700762  0.00462822 -0.01425572
   0.00919784  0.00171392  0.01206899  0.02586481  0.00133068  0.00974165
   0.03375589  0.00320698  0.02395621  0.01023746  0.00383784 -0.01420912
   0.00319639  0.00188528 -0.00858403 -0.00972638 -0.00651975  0.01432493
  -0.01951187  0.00285484 -0.00766864 -0.01873302  0.03432124  0.00225209
   0.02280079 -0.00747533  0.01959535 -0.02121245]], shape=(1, 64), dtype=float32)
tf.Tensor(
[[ 0.00258152  0.02014538  0.00117832  0.02041735 -0.02042701 -0.02395442
  -0.01068465 -0.02712064  0.02140838  0.00242466 -0.00810128  0.01110235
  -0.00146593  0.01292215  0.00348439  0.01334237 -0.01953124  0.01687023
   0.0073176   0.00047215 -0.0129725  -0.01578198 -0.01907879  0.00742687
  -0.00760885  0.01814296 -0.01059827  0.01759749 -0.00437215  0.02974381
   0.01503188 -0.00359801 -0.00707248  0.00693541 -0.01820476 -0.01429193
   0.01161771  0.00879416  0.01932933 -0.00318049  0.01815645  0.00342331
   0.01157722  0.01645537  0.01013768 -0.01330452  0.00395723  0.00353524
  -0.00485211  0.00153592 -0.00889693 -0.00504893  0.00879492 -0.01121669
   0.01522924 -0.0181746  -0.02131601  0.0081171   0.00515315 -0.00096183
  -0.00070595 -0.01315595 -0.03314487 -0.00645945]], shape=(1, 64), dtype=float32)
---Ranks---
tf.Tensor([[-0.5163227]], shape=(1, 1), dtype=float32)

Flipper-afk avatar Nov 19 '21 07:11 Flipper-afk

@joaoalveshenriques did you solved the problem? I followed the above suggestion but I still can't solve it. Can you share what you did for this problem? Thanks!

cory1219 avatar Mar 24 '22 11:03 cory1219