{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"This notebook is copied from https://www.kaggle.com/code/viji1609/h-m-basic-retrieval-model-tf-recommender/notebook\nI used it to generate embeddings for customers and articles to be used in a hybrid recommendation model\nThe model is saved in the output files.\nEmbedding are save in pickle file in the same order of the input files.","metadata":{}},{"cell_type":"code","source":"!pip install -q tensorflow-recommenders\n!pip install -q scann","metadata":{"_kg_hide-output":true,"execution":{"iopub.status.busy":"2022-05-18T10:25:21.406905Z","iopub.execute_input":"2022-05-18T10:25:21.407244Z","iopub.status.idle":"2022-05-18T10:27:29.492363Z","shell.execute_reply.started":"2022-05-18T10:25:21.407157Z","shell.execute_reply":"2022-05-18T10:27:29.491262Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nimport tensorflow as tf\nimport tensorflow_recommenders as tfrs\nimport tempfile\nimport matplotlib.pyplot as plt\nfrom pathlib import Path\nfrom typing import Dict, Text\nimport warnings\nwarnings.filterwarnings('ignore')","metadata":{"_kg_hide-output":true,"execution":{"iopub.status.busy":"2022-05-18T10:29:43.428355Z","iopub.execute_input":"2022-05-18T10:29:43.428901Z","iopub.status.idle":"2022-05-18T10:29:43.436576Z","shell.execute_reply.started":"2022-05-18T10:29:43.428869Z","shell.execute_reply":"2022-05-18T10:29:43.434806Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# The Dataset","metadata":{}},{"cell_type":"code","source":"data_dir = Path('../input/h-and-m-personalized-fashion-recommendations')\ntrain0 = pd.read_csv(data_dir/'transactions_train.csv', dtype=str, usecols=['customer_id', 'article_id'])\ntrain0.head()","metadata":{"execution":{"iopub.status.busy":"2022-05-18T10:27:34.527616Z","iopub.execute_input":"2022-05-18T10:27:34.527946Z","iopub.status.idle":"2022-05-18T10:27:34.608306Z","shell.execute_reply.started":"2022-05-18T10:27:34.527905Z","shell.execute_reply":"2022-05-18T10:27:34.607121Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"customer_df = pd.read_csv(data_dir/'customers.csv', dtype=str, usecols=['customer_id'])\ncustomer_df.head()","metadata":{"execution":{"iopub.status.busy":"2022-05-18T10:27:34.61054Z","iopub.execute_input":"2022-05-18T10:27:34.61091Z","iopub.status.idle":"2022-05-18T10:27:40.495955Z","shell.execute_reply.started":"2022-05-18T10:27:34.610878Z","shell.execute_reply":"2022-05-18T10:27:40.494283Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"article_df = pd.read_csv(data_dir/'articles.csv', dtype=str, usecols=['article_id'])\narticle_df.head()","metadata":{"execution":{"iopub.status.busy":"2022-05-18T10:27:40.498176Z","iopub.execute_input":"2022-05-18T10:27:40.498671Z","iopub.status.idle":"2022-05-18T10:27:41.490361Z","shell.execute_reply.started":"2022-05-18T10:27:40.498637Z","shell.execute_reply":"2022-05-18T10:27:41.488756Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"article_ds = tf.data.Dataset.from_tensor_slices(dict(article_df[['article_id']]))\narticles = article_ds.map(lambda x: x['article_id'])","metadata":{"_kg_hide-output":true,"execution":{"iopub.status.busy":"2022-05-18T10:27:41.49185Z","iopub.execute_input":"2022-05-18T10:27:41.492083Z","iopub.status.idle":"2022-05-18T10:27:41.577351Z","shell.execute_reply.started":"2022-05-18T10:27:41.492059Z","shell.execute_reply":"2022-05-18T10:27:41.576364Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Query, Candidate and H&M model ","metadata":{}},{"cell_type":"code","source":"embedding_dimension = 64\n\n# Query Model\ncustomer_model = tf.keras.Sequential([\n  tf.keras.layers.StringLookup(\n      vocabulary=customer_df.customer_id.values, mask_token=None),  \n  tf.keras.layers.Embedding(len(customer_df) + 1, embedding_dimension)\n])","metadata":{"execution":{"iopub.status.busy":"2022-05-18T10:27:41.578889Z","iopub.execute_input":"2022-05-18T10:27:41.579865Z","iopub.status.idle":"2022-05-18T10:27:42.905124Z","shell.execute_reply.started":"2022-05-18T10:27:41.579819Z","shell.execute_reply":"2022-05-18T10:27:42.903973Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Candidate Model\narticle_model = tf.keras.Sequential([\n  tf.keras.layers.StringLookup(\n      vocabulary=article_df.article_id.values, mask_token=None),\n  tf.keras.layers.Embedding(len(article_df) + 1, embedding_dimension)\n])","metadata":{"execution":{"iopub.status.busy":"2022-05-18T10:27:42.906526Z","iopub.execute_input":"2022-05-18T10:27:42.906841Z","iopub.status.idle":"2022-05-18T10:27:42.970113Z","shell.execute_reply.started":"2022-05-18T10:27:42.906804Z","shell.execute_reply":"2022-05-18T10:27:42.968857Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Retrieval Model\n\nclass HandMModel(tfrs.Model):\n    \n    def __init__(self, customer_model, article_model):\n        super().__init__()\n        self.article_model: tf.keras.Model = article_model\n        self.customer_model: tf.keras.Model = customer_model\n        self.task = tfrs.tasks.Retrieval(\n        metrics=tfrs.metrics.FactorizedTopK(\n            candidates=articles.batch(128).map(self.article_model),            \n            ),\n        )        \n\n    def compute_loss(self, features: Dict[str, tf.Tensor], training=False) -> tf.Tensor:\n    \n        customer_embeddings = self.customer_model(features[\"customer_id\"])    \n        article_embeddings = self.article_model(features[\"article_id\"])\n\n        # The task computes the loss and the metrics.\n        return self.task(customer_embeddings, article_embeddings,compute_metrics=not training)","metadata":{"execution":{"iopub.status.busy":"2022-05-18T10:27:42.971301Z","iopub.execute_input":"2022-05-18T10:27:42.971537Z","iopub.status.idle":"2022-05-18T10:27:42.9799Z","shell.execute_reply.started":"2022-05-18T10:27:42.971511Z","shell.execute_reply":"2022-05-18T10:27:42.978703Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Train & Validate","metadata":{}},{"cell_type":"code","source":"model = HandMModel(customer_model, article_model)\nmodel.compile(optimizer=tf.keras.optimizers.Adagrad(learning_rate=0.1))","metadata":{"execution":{"iopub.status.busy":"2022-05-18T10:27:42.985095Z","iopub.execute_input":"2022-05-18T10:27:42.985403Z","iopub.status.idle":"2022-05-18T10:27:43.362355Z","shell.execute_reply.started":"2022-05-18T10:27:42.985364Z","shell.execute_reply":"2022-05-18T10:27:43.360983Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_size = int(0.8 *len(train0))","metadata":{"execution":{"iopub.status.busy":"2022-05-18T10:27:43.364547Z","iopub.execute_input":"2022-05-18T10:27:43.368364Z","iopub.status.idle":"2022-05-18T10:27:43.373479Z","shell.execute_reply.started":"2022-05-18T10:27:43.368299Z","shell.execute_reply":"2022-05-18T10:27:43.372356Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train = train0[:train_size]\ntest = train0[train_size:]\n\ntrain_ds = tf.data.Dataset.from_tensor_slices(dict(train[['customer_id','article_id']])).shuffle(100_000).batch(256).cache()\ntest_ds = tf.data.Dataset.from_tensor_slices(dict(test[['customer_id','article_id']])).batch(256).cache()\n\nnum_epochs = 5\nhistory = model.fit(\n    train_ds, \n    validation_data = test_ds,\n    validation_freq=5,\n    epochs=num_epochs,\n    verbose=1)","metadata":{"_kg_hide-output":true,"execution":{"iopub.status.busy":"2022-05-18T10:28:57.639272Z","iopub.execute_input":"2022-05-18T10:28:57.640193Z","iopub.status.idle":"2022-05-18T10:28:59.134047Z","shell.execute_reply.started":"2022-05-18T10:28:57.640146Z","shell.execute_reply":"2022-05-18T10:28:59.133325Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"article_embeds = article_model.predict(article_df)\ncustomer_embeds = customer_model.predict(customer_df)","metadata":{"execution":{"iopub.status.busy":"2022-05-18T09:12:53.566522Z","iopub.execute_input":"2022-05-18T09:12:53.566872Z","iopub.status.idle":"2022-05-18T09:12:58.242157Z","shell.execute_reply.started":"2022-05-18T09:12:53.56681Z","shell.execute_reply":"2022-05-18T09:12:58.241092Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import pickle\nwith open('customer_embeds.pickle', 'wb') as f:\n    pickle.dump(customer_embeds, f)\n    \nwith open('article_embeds.pickle', 'wb') as f:\n    pickle.dump(article_embeds, f)","metadata":{"execution":{"iopub.status.busy":"2022-05-18T10:28:54.025849Z","iopub.execute_input":"2022-05-18T10:28:54.026167Z","iopub.status.idle":"2022-05-18T10:28:54.030974Z","shell.execute_reply.started":"2022-05-18T10:28:54.026135Z","shell.execute_reply":"2022-05-18T10:28:54.029558Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"del article_embeds\ndel customer_embeds","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Retrieve & Submit","metadata":{}},{"cell_type":"code","source":"scann_index = tfrs.layers.factorized_top_k.ScaNN(model.customer_model, k = 20 )\nscann_index.index_from_dataset(\n  tf.data.Dataset.zip((articles.batch(100), articles.batch(100).map(model.article_model)))\n)","metadata":{"_kg_hide-output":true,"execution":{"iopub.status.busy":"2022-05-18T10:29:05.322201Z","iopub.execute_input":"2022-05-18T10:29:05.322958Z","iopub.status.idle":"2022-05-18T10:29:12.090923Z","shell.execute_reply.started":"2022-05-18T10:29:05.322925Z","shell.execute_reply":"2022-05-18T10:29:12.089558Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"scann_index.save('scann')","metadata":{"execution":{"iopub.status.busy":"2022-05-18T10:36:36.900498Z","iopub.execute_input":"2022-05-18T10:36:36.900765Z","iopub.status.idle":"2022-05-18T10:36:56.432325Z","shell.execute_reply.started":"2022-05-18T10:36:36.90074Z","shell.execute_reply":"2022-05-18T10:36:56.431228Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import pickle\nwith open('model.pickle', 'wb') as f:\n    pickle.dump(scann_index, f)","metadata":{"execution":{"iopub.status.busy":"2022-05-18T10:34:43.482856Z","iopub.execute_input":"2022-05-18T10:34:43.48369Z","iopub.status.idle":"2022-05-18T10:35:03.868695Z","shell.execute_reply.started":"2022-05-18T10:34:43.483648Z","shell.execute_reply":"2022-05-18T10:35:03.867301Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def plot_items(items):\n    path = \"../input/h-and-m-personalized-fashion-recommendations/images\"\n\n    k = len(items)\n    fig = plt.figure(figsize=(3*k, 10))\n    for item, i in zip(items, range(1, k+1)):\n        sub = item[:3]\n        image = path + \"/\"+ sub + \"/\"+ item +\".jpg\"\n        image = plt.imread(image)\n        fig.add_subplot(1, k, i)\n        plt.axis('off')\n        plt.imshow(image)","metadata":{"execution":{"iopub.status.busy":"2022-05-18T10:29:47.644895Z","iopub.execute_input":"2022-05-18T10:29:47.646735Z","iopub.status.idle":"2022-05-18T10:29:47.654301Z","shell.execute_reply.started":"2022-05-18T10:29:47.646648Z","shell.execute_reply":"2022-05-18T10:29:47.652943Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sample = train0.iloc[11:12]\nprev_items = train0[train0.customer_id == sample.customer_id.values[0]].article_id\n_,articles = scann_index(sample.customer_id)\npreds = articles.numpy().astype(str)[0][len(prev_items):][:6]","metadata":{"_kg_hide-output":true,"execution":{"iopub.status.busy":"2022-05-18T10:32:08.445517Z","iopub.execute_input":"2022-05-18T10:32:08.445817Z","iopub.status.idle":"2022-05-18T10:32:08.462121Z","shell.execute_reply.started":"2022-05-18T10:32:08.445787Z","shell.execute_reply":"2022-05-18T10:32:08.461065Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plot_items(prev_items.values)","metadata":{"execution":{"iopub.status.busy":"2022-05-18T10:32:08.833755Z","iopub.execute_input":"2022-05-18T10:32:08.836565Z","iopub.status.idle":"2022-05-18T10:32:11.738106Z","shell.execute_reply.started":"2022-05-18T10:32:08.836499Z","shell.execute_reply":"2022-05-18T10:32:11.737287Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plot_items(preds)","metadata":{"execution":{"iopub.status.busy":"2022-05-18T10:32:11.739616Z","iopub.execute_input":"2022-05-18T10:32:11.739863Z","iopub.status.idle":"2022-05-18T10:32:15.703111Z","shell.execute_reply.started":"2022-05-18T10:32:11.739831Z","shell.execute_reply":"2022-05-18T10:32:15.702324Z"},"trusted":true},"execution_count":null,"outputs":[]}]}