{"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":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\n# import os\n# for dirname, _, filenames in os.walk('/kaggle/input'):\n#     for filename in filenames:\n#         print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","scrolled":true,"execution":{"iopub.status.busy":"2023-02-06T11:29:19.31136Z","iopub.execute_input":"2023-02-06T11:29:19.311908Z","iopub.status.idle":"2023-02-06T11:29:19.344657Z","shell.execute_reply.started":"2023-02-06T11:29:19.31178Z","shell.execute_reply":"2023-02-06T11:29:19.343436Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"C = pd.read_csv('/kaggle/input/h-and-m-personalized-fashion-recommendations/customers.csv')\nC.shape","metadata":{"execution":{"iopub.status.busy":"2023-02-06T11:31:16.892973Z","iopub.execute_input":"2023-02-06T11:31:16.893422Z","iopub.status.idle":"2023-02-06T11:31:23.92569Z","shell.execute_reply.started":"2023-02-06T11:31:16.893385Z","shell.execute_reply":"2023-02-06T11:31:23.924428Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"T = pd.read_csv('/kaggle/input/h-and-m-personalized-fashion-recommendations/transactions_train.csv', nrows=10000)\nT.shape","metadata":{"execution":{"iopub.status.busy":"2023-02-06T11:30:58.176171Z","iopub.execute_input":"2023-02-06T11:30:58.176776Z","iopub.status.idle":"2023-02-06T11:30:58.2307Z","shell.execute_reply.started":"2023-02-06T11:30:58.176728Z","shell.execute_reply":"2023-02-06T11:30:58.229312Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"A = pd.read_csv('/kaggle/input/h-and-m-personalized-fashion-recommendations/articles.csv')\nA.shape","metadata":{"execution":{"iopub.status.busy":"2023-02-06T11:31:23.927827Z","iopub.execute_input":"2023-02-06T11:31:23.928233Z","iopub.status.idle":"2023-02-06T11:31:25.19527Z","shell.execute_reply.started":"2023-02-06T11:31:23.928199Z","shell.execute_reply":"2023-02-06T11:31:25.193754Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"## filter customers and articles\n\nunq_customers = T.customer_id.unique()\nunq_articles  = T.article_id.unique()","metadata":{"execution":{"iopub.status.busy":"2023-02-06T11:33:03.622424Z","iopub.execute_input":"2023-02-06T11:33:03.622905Z","iopub.status.idle":"2023-02-06T11:33:03.632235Z","shell.execute_reply.started":"2023-02-06T11:33:03.622865Z","shell.execute_reply":"2023-02-06T11:33:03.631023Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"C = C[C.customer_id.isin(unq_customers)]","metadata":{"execution":{"iopub.status.busy":"2023-02-06T11:34:16.862903Z","iopub.execute_input":"2023-02-06T11:34:16.863958Z","iopub.status.idle":"2023-02-06T11:34:17.072017Z","shell.execute_reply.started":"2023-02-06T11:34:16.863888Z","shell.execute_reply":"2023-02-06T11:34:17.07038Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"A = A[A.article_id.isin(unq_articles)]\nC.shape, A.shape","metadata":{"execution":{"iopub.status.busy":"2023-02-06T11:34:49.993803Z","iopub.execute_input":"2023-02-06T11:34:49.994261Z","iopub.status.idle":"2023-02-06T11:34:50.01224Z","shell.execute_reply.started":"2023-02-06T11:34:49.994223Z","shell.execute_reply":"2023-02-06T11:34:50.010892Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Data Exploration","metadata":{}},{"cell_type":"code","source":"T","metadata":{"execution":{"iopub.status.busy":"2023-02-06T11:35:22.913287Z","iopub.execute_input":"2023-02-06T11:35:22.913714Z","iopub.status.idle":"2023-02-06T11:35:22.933353Z","shell.execute_reply.started":"2023-02-06T11:35:22.91368Z","shell.execute_reply":"2023-02-06T11:35:22.93179Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"A","metadata":{"execution":{"iopub.status.busy":"2023-02-06T11:35:37.622834Z","iopub.execute_input":"2023-02-06T11:35:37.623275Z","iopub.status.idle":"2023-02-06T11:35:37.667808Z","shell.execute_reply.started":"2023-02-06T11:35:37.623241Z","shell.execute_reply":"2023-02-06T11:35:37.666574Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"C.groupby('fashion_news_frequency').count()","metadata":{"execution":{"iopub.status.busy":"2023-02-06T11:40:14.586288Z","iopub.execute_input":"2023-02-06T11:40:14.586756Z","iopub.status.idle":"2023-02-06T11:40:14.612586Z","shell.execute_reply.started":"2023-02-06T11:40:14.586719Z","shell.execute_reply":"2023-02-06T11:40:14.611272Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Feature engineering","metadata":{}},{"cell_type":"markdown","source":"### Customers","metadata":{}},{"cell_type":"code","source":"# segment age into several bins\ndef age_fun(age):\n    if age<25:\n        return 0\n    if age<32:\n        return 1\n    if age<49:\n        return 2\n    else:\n        return 3\nC['age_cat']=C.age.map(age_fun)\nC.groupby('age_cat').count()","metadata":{"execution":{"iopub.status.busy":"2023-02-06T11:46:16.789631Z","iopub.execute_input":"2023-02-06T11:46:16.790148Z","iopub.status.idle":"2023-02-06T11:46:16.818895Z","shell.execute_reply.started":"2023-02-06T11:46:16.790105Z","shell.execute_reply":"2023-02-06T11:46:16.818019Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# convert fashion_news_frequency into a binary column\nC.loc[:,'news'] = C.fashion_news_frequency.map(lambda x: 1 if x=='Regularly' else 0)\nC.groupby('news').count()","metadata":{"execution":{"iopub.status.busy":"2023-02-06T11:42:47.35343Z","iopub.execute_input":"2023-02-06T11:42:47.354466Z","iopub.status.idle":"2023-02-06T11:42:47.376723Z","shell.execute_reply.started":"2023-02-06T11:42:47.354422Z","shell.execute_reply":"2023-02-06T11:42:47.375283Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"C = C[['customer_id','news','age_cat']]","metadata":{"execution":{"iopub.status.busy":"2023-02-06T11:47:21.699325Z","iopub.execute_input":"2023-02-06T11:47:21.699805Z","iopub.status.idle":"2023-02-06T11:47:21.707126Z","shell.execute_reply.started":"2023-02-06T11:47:21.699764Z","shell.execute_reply":"2023-02-06T11:47:21.705728Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Articles","metadata":{}},{"cell_type":"code","source":"A.columns","metadata":{"execution":{"iopub.status.busy":"2023-02-06T11:48:00.445052Z","iopub.execute_input":"2023-02-06T11:48:00.446395Z","iopub.status.idle":"2023-02-06T11:48:00.455675Z","shell.execute_reply.started":"2023-02-06T11:48:00.446329Z","shell.execute_reply":"2023-02-06T11:48:00.453928Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"A.nunique()","metadata":{"execution":{"iopub.status.busy":"2023-02-06T11:53:44.897284Z","iopub.execute_input":"2023-02-06T11:53:44.897926Z","iopub.status.idle":"2023-02-06T11:53:44.926547Z","shell.execute_reply.started":"2023-02-06T11:53:44.89787Z","shell.execute_reply":"2023-02-06T11:53:44.925143Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"A.garment_group_no.unique()-1001","metadata":{"execution":{"iopub.status.busy":"2023-02-06T11:57:50.117383Z","iopub.execute_input":"2023-02-06T11:57:50.117841Z","iopub.status.idle":"2023-02-06T11:57:50.127641Z","shell.execute_reply.started":"2023-02-06T11:57:50.117803Z","shell.execute_reply":"2023-02-06T11:57:50.126166Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"A=A[['article_id','perceived_colour_master_id','garment_group_no']]\nA['garment_group_no'] = A['garment_group_no']-1001","metadata":{"execution":{"iopub.status.busy":"2023-02-06T11:59:11.609079Z","iopub.execute_input":"2023-02-06T11:59:11.609563Z","iopub.status.idle":"2023-02-06T11:59:11.620609Z","shell.execute_reply.started":"2023-02-06T11:59:11.609524Z","shell.execute_reply":"2023-02-06T11:59:11.619031Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"A.describe()","metadata":{"execution":{"iopub.status.busy":"2023-02-06T11:59:23.217639Z","iopub.execute_input":"2023-02-06T11:59:23.218301Z","iopub.status.idle":"2023-02-06T11:59:23.256027Z","shell.execute_reply.started":"2023-02-06T11:59:23.218253Z","shell.execute_reply":"2023-02-06T11:59:23.254475Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Transactions","metadata":{}},{"cell_type":"code","source":"T.nunique()","metadata":{"execution":{"iopub.status.busy":"2023-02-06T12:05:40.746668Z","iopub.execute_input":"2023-02-06T12:05:40.747181Z","iopub.status.idle":"2023-02-06T12:05:40.762461Z","shell.execute_reply.started":"2023-02-06T12:05:40.747138Z","shell.execute_reply":"2023-02-06T12:05:40.761204Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"avg_price_per_cust = T.groupby('customer_id').price.mean()","metadata":{"execution":{"iopub.status.busy":"2023-02-06T12:08:27.889569Z","iopub.execute_input":"2023-02-06T12:08:27.890082Z","iopub.status.idle":"2023-02-06T12:08:27.904355Z","shell.execute_reply.started":"2023-02-06T12:08:27.890038Z","shell.execute_reply":"2023-02-06T12:08:27.902897Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from matplotlib import pyplot as plt\nplt.hist(np.log(avg_price_per_cust),bins=50)","metadata":{"execution":{"iopub.status.busy":"2023-02-06T12:09:49.877846Z","iopub.execute_input":"2023-02-06T12:09:49.878282Z","iopub.status.idle":"2023-02-06T12:09:50.113398Z","shell.execute_reply.started":"2023-02-06T12:09:49.878237Z","shell.execute_reply":"2023-02-06T12:09:50.112147Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"log_avg_price = np.log(avg_price_per_cust)","metadata":{"execution":{"iopub.status.busy":"2023-02-06T12:10:42.173851Z","iopub.execute_input":"2023-02-06T12:10:42.175126Z","iopub.status.idle":"2023-02-06T12:10:42.181561Z","shell.execute_reply.started":"2023-02-06T12:10:42.17507Z","shell.execute_reply":"2023-02-06T12:10:42.180146Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"main_channel = T.groupby(['customer_id','sales_channel_id']).count().reset_index()[['customer_id','sales_channel_id']].groupby('customer_id').first()-1","metadata":{"execution":{"iopub.status.busy":"2023-02-06T12:17:43.748529Z","iopub.execute_input":"2023-02-06T12:17:43.749038Z","iopub.status.idle":"2023-02-06T12:17:43.771544Z","shell.execute_reply.started":"2023-02-06T12:17:43.748996Z","shell.execute_reply":"2023-02-06T12:17:43.77048Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### avg cnt of transactions in rolling 7d period","metadata":{}},{"cell_type":"code","source":"avg_cnts = []\nfor cust in customers:\n    subT = T[T.customer_id==cust].groupby('t_dat').count().sort_values('t_dat',ascending=True)\n    avg_cnts.append ( subT.rolling(7, min_periods=0).mean().mean()['article_id'] )\navg_cnt_tr = pd.DataFrame({'customer_id':customers, 'avg_cnts':avg_cnts})","metadata":{"execution":{"iopub.status.busy":"2023-02-06T12:27:50.158201Z","iopub.execute_input":"2023-02-06T12:27:50.15873Z","iopub.status.idle":"2023-02-06T12:28:06.355613Z","shell.execute_reply.started":"2023-02-06T12:27:50.158683Z","shell.execute_reply":"2023-02-06T12:28:06.354352Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# plt.hist(avg_cnt_tr,bins=50)","metadata":{"execution":{"iopub.status.busy":"2023-02-06T12:30:37.203845Z","iopub.execute_input":"2023-02-06T12:30:37.204324Z","iopub.status.idle":"2023-02-06T12:30:37.210682Z","shell.execute_reply.started":"2023-02-06T12:30:37.204286Z","shell.execute_reply":"2023-02-06T12:30:37.209042Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"T2 = main_channel.join(log_avg_price).join(avg_cnt_tr.set_index('customer_id'))\nT2.head(10)","metadata":{"execution":{"iopub.status.busy":"2023-02-06T12:32:31.112798Z","iopub.execute_input":"2023-02-06T12:32:31.113403Z","iopub.status.idle":"2023-02-06T12:32:31.139887Z","shell.execute_reply.started":"2023-02-06T12:32:31.113352Z","shell.execute_reply":"2023-02-06T12:32:31.138014Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"T2.join(C.set_index('customer_id'))","metadata":{"execution":{"iopub.status.busy":"2023-02-06T12:33:48.373232Z","iopub.execute_input":"2023-02-06T12:33:48.373974Z","iopub.status.idle":"2023-02-06T12:33:48.399744Z","shell.execute_reply.started":"2023-02-06T12:33:48.373922Z","shell.execute_reply":"2023-02-06T12:33:48.398323Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Data Prep","metadata":{}},{"cell_type":"code","source":"from typing import Dict, Text\nimport tensorflow as tf\n\n# import tensorflow_datasets as tfds\n# import tensorflow_recommenders as tfrs","metadata":{"execution":{"iopub.status.busy":"2023-02-06T12:35:31.36898Z","iopub.execute_input":"2023-02-06T12:35:31.3694Z","iopub.status.idle":"2023-02-06T12:35:31.374674Z","shell.execute_reply.started":"2023-02-06T12:35:31.369363Z","shell.execute_reply":"2023-02-06T12:35:31.373479Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Model Definition","metadata":{}},{"cell_type":"code","source":"class UserModel(tf.keras.Model):\n    \n    def __init__(self):\n        super().__init__()\n\n    def call(self, inputs): # inputs is a dictionary passed by the retrieval model we build\n        return self.user_embedding(inputs[\"customer_id\"])\n    \n    \nclass ArticleModel(tf.keras.Model):\n\n    def __init__(self):\n        super().__init__()\n\n    def call(self, salad_hash_input): \n\n        return self.salad_embedding(salad_hash_input)\n    \nclass RecommendationModel(tfrs.models.Model):\n\n    def __init__(self):\n        super().__init__()\n        \n        self.query_model = tf.keras.Sequential([\n          UserModel(), # constructor\n          tf.keras.layers.Dense(32)\n        ])\n\n        self.candidate_model = tf.keras.Sequential([\n          ArticleModel(), # constructor\n          tf.keras.layers.Dense(32)\n        ])\n        \n        # this uses the pre-calculated salads list\n        self.task = tfrs.tasks.Retrieval(\n            metrics=tfrs.metrics.FactorizedTopK(\n                candidates=salads.batch(128).map(self.candidate_model),\n            ),\n        )\n\n    def compute_loss(self, features, training=False):\n        query_embeddings = self.query_model({ # it simply passes a dictionary \n            \"uid\": features[\"uid\"] # this comes from the main table, ds\n        })\n\n        article_embeddings = self.candidate_model( # this cannot be changed to dict\n            features[\"salad_hash\"]\n        )\n\n        return self.task(query_embeddings, article_embeddings)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Training","metadata":{}},{"cell_type":"code","source":"model = RecommendationModel()","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model.compile(optimizer=tf.keras.optimizers.Adagrad(0.118))\nEPOCHS=10\nmodel.fit(\n    cached_train, \n    epochs=EPOCHS,\n)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Evaluation","metadata":{}},{"cell_type":"code","source":"train_accuracy = model.evaluate(\n    cached_train, return_dict=True)[\"factorized_top_k/top_100_categorical_accuracy\"]\n\ntest_accuracies = model.evaluate(\n    cached_test, return_dict=True)\n\ntest_accuracy = test_accuracies[\"factorized_top_k/top_100_categorical_accuracy\"]\ntest_tot_loss = test_accuracies[\"total_loss\"]\n\nprint(f\"Top-100 accuracy (train): {train_accuracy:.2f}.\")\nprint(f\"Top-100 accuracy (test): {test_accuracy:.2f}.\")\n\nprint(f\"Test total loss: {test_tot_loss}\")","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":" factorized_top_k/top_1_categorical_accuracy: 0.0133 - factorized_top_k/top_5_categorical_accuracy: 0.0569 - factorized_top_k/top_10_categorical_accuracy: 0.1030 - factorized_top_k/top_50_categorical_accuracy: 0.3661 - factorized_top_k/top_100_categorical_accuracy: 0.5648 - loss: 7741.4458 - regularization_loss: 0.0000e+00 - total_loss: 7741.4458","metadata":{}}]}