{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.7.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":38760,"databundleVersionId":4493939,"sourceType":"competition"},{"sourceId":4461402,"sourceType":"datasetVersion","datasetId":2611514},{"sourceId":4474043,"sourceType":"datasetVersion","datasetId":2601572}],"dockerImageVersionId":30302,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"Here is a sample code for gru4rec using recbole. \nI made it based on the following code from a past competition\nhttps://www.kaggle.com/code/astrung/recbole-lstm-sequential-for-recomendation-tutorial\n\nI think you can get a better score if you change the training data or epoch size, etc. Enjoy!","metadata":{}},{"cell_type":"code","source":"!pip install polars","metadata":{"execution":{"iopub.status.busy":"2024-12-29T05:49:56.129541Z","iopub.execute_input":"2024-12-29T05:49:56.130885Z","iopub.status.idle":"2024-12-29T05:50:04.060308Z","shell.execute_reply.started":"2024-12-29T05:49:56.130832Z","shell.execute_reply":"2024-12-29T05:50:04.059288Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!pip install recbole","metadata":{"execution":{"iopub.status.busy":"2024-12-29T05:50:04.062469Z","iopub.execute_input":"2024-12-29T05:50:04.062768Z","iopub.status.idle":"2024-12-29T05:50:12.043280Z","shell.execute_reply.started":"2024-12-29T05:50:04.062737Z","shell.execute_reply":"2024-12-29T05:50:12.042004Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import tqdm\nimport polars as pl\nimport numpy as np\nimport pandas as pd\nimport seaborn as sns\nimport random\nimport os \nimport h5py\nimport sys\nimport gc\n\nfrom matplotlib import pyplot as plt\nimport pyarrow.parquet as pq","metadata":{"execution":{"iopub.status.busy":"2024-12-29T05:50:12.044935Z","iopub.execute_input":"2024-12-29T05:50:12.045246Z","iopub.status.idle":"2024-12-29T05:50:12.051695Z","shell.execute_reply.started":"2024-12-29T05:50:12.045214Z","shell.execute_reply":"2024-12-29T05:50:12.049970Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 1. Create atomic file","metadata":{}},{"cell_type":"code","source":"train = pl.read_parquet('/kaggle/input/otto-train-and-test-data-for-local-validation/test.parquet')\ntest = pl.read_parquet('/kaggle/input/otto-full-optimized-memory-footprint/test.parquet')\n\ndf = pl.concat([train, test])\n#df = pl.read_parquet('../input/otto-train-and-test-data-for-local-validation/test.parquet')\n\ndf = df.sort(['session', 'aid', 'ts'])\ndf = df.with_columns((pl.col('ts') * 1e9).alias('ts'))\ndf = df.rename({'session': 'session:token', 'aid': 'aid:token', 'ts': 'ts:float'})","metadata":{"execution":{"iopub.status.busy":"2024-12-29T05:50:12.052893Z","iopub.execute_input":"2024-12-29T05:50:12.053149Z","iopub.status.idle":"2024-12-29T05:50:13.318583Z","shell.execute_reply.started":"2024-12-29T05:50:12.053126Z","shell.execute_reply":"2024-12-29T05:50:13.317868Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\n\n# Đường dẫn thư mục\ndirectory = \"/kaggle/working/recbox_data\"\n\n# Kiểm tra và tạo thư mục\nif not os.path.exists(directory):\n    os.makedirs(directory)\n    print(f\"Đã tạo thư mục: {directory}\")\nelse:\n    print(f\"Thư mục đã tồn tại: {directory}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-29T05:50:13.320610Z","iopub.execute_input":"2024-12-29T05:50:13.320905Z","iopub.status.idle":"2024-12-29T05:50:13.326195Z","shell.execute_reply.started":"2024-12-29T05:50:13.320878Z","shell.execute_reply":"2024-12-29T05:50:13.325324Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# View columns\nprint(df.columns)\n\n# Convert the relevant columns to a Pandas DataFrame\npandas_df = df[['session:token', 'aid:token', 'ts:float']].to_pandas()\n\n# Save to a tab-separated file using Pandas\npandas_df.to_csv(\n    '/kaggle/working/recbox_data/recbox_data.inter',\n    sep='\\t',\n    index=False\n)\n\n# Clean up memory\ndel df, pandas_df\ngc.collect()","metadata":{"execution":{"iopub.status.busy":"2024-12-29T05:50:13.327480Z","iopub.execute_input":"2024-12-29T05:50:13.327804Z","iopub.status.idle":"2024-12-29T05:50:43.542765Z","shell.execute_reply.started":"2024-12-29T05:50:13.327772Z","shell.execute_reply":"2024-12-29T05:50:43.541872Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 3. Create dataset and train model with Recbole\n\nFor anyone need instruction document, please check this link: https://recbole.io/docs/user_guide/usage/use_modules.html","metadata":{}},{"cell_type":"code","source":"import logging\nfrom logging import getLogger\nfrom recbole.config import Config\nfrom recbole.data import create_dataset, data_preparation\nfrom recbole.model.sequential_recommender import GRU4Rec\nfrom recbole.trainer import Trainer\nfrom recbole.utils import init_seed, init_logger\n\nfrom recbole.utils.case_study import full_sort_topk","metadata":{"execution":{"iopub.status.busy":"2024-12-29T05:50:43.543872Z","iopub.execute_input":"2024-12-29T05:50:43.544134Z","iopub.status.idle":"2024-12-29T05:50:43.548941Z","shell.execute_reply.started":"2024-12-29T05:50:43.544109Z","shell.execute_reply":"2024-12-29T05:50:43.548007Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"MAX_ITEM = 20\n\nparameter_dict = {\n    'data_path': '/kaggle/working/',\n    'USER_ID_FIELD': 'session',\n    'ITEM_ID_FIELD': 'aid',\n    'TIME_FIELD': 'ts',\n    'user_inter_num_interval': \"[5,Inf)\",\n    'item_inter_num_interval': \"[5,Inf)\",\n    'load_col': {'inter': ['session', 'aid', 'ts']},\n    'train_neg_sample_args': None,\n    'epochs': 20,\n    'stopping_step':3,\n    \n    'eval_batch_size': 1024,\n    'train_batch_size': 1024,\n#    'enable_amp':True,\n    'MAX_ITEM_LIST_LENGTH': MAX_ITEM,\n    'eval_args': {\n        'split': {'RS': [9, 1, 0]},\n        'group_by': 'user',\n        'order': 'TO',\n        'mode': 'full'}\n}\n\nconfig = Config(model='GRU4Rec', dataset='recbox_data', config_dict=parameter_dict)\n\n# init random seed\ninit_seed(config['seed'], config['reproducibility'])\n\n# logger initialization\ninit_logger(config)\nlogger = getLogger()\n\n# Create handlers\nc_handler = logging.StreamHandler()\nc_handler.setLevel(logging.INFO)\nlogger.addHandler(c_handler)\n\n# write config info into log\nlogger.info(config)","metadata":{"execution":{"iopub.status.busy":"2024-12-29T05:50:43.550103Z","iopub.execute_input":"2024-12-29T05:50:43.550380Z","iopub.status.idle":"2024-12-29T05:50:44.000618Z","shell.execute_reply.started":"2024-12-29T05:50:43.550341Z","shell.execute_reply":"2024-12-29T05:50:43.999752Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"dataset = create_dataset(config)\nlogger.info(dataset)","metadata":{"execution":{"iopub.status.busy":"2024-12-29T05:50:44.002000Z","iopub.execute_input":"2024-12-29T05:50:44.002633Z","iopub.status.idle":"2024-12-29T05:53:01.613972Z","shell.execute_reply.started":"2024-12-29T05:50:44.002596Z","shell.execute_reply":"2024-12-29T05:53:01.613113Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# dataset splitting\ntrain_data, valid_data, test_data = data_preparation(config, dataset)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-29T05:53:01.615262Z","iopub.execute_input":"2024-12-29T05:53:01.615541Z","iopub.status.idle":"2024-12-29T05:54:38.860314Z","shell.execute_reply.started":"2024-12-29T05:53:01.615515Z","shell.execute_reply":"2024-12-29T05:54:38.859355Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# model loading and initialization\nmodel = GRU4Rec(config, train_data.dataset).to(config['device'])\nlogger.info(model)\n\n# trainer loading and initialization\ntrainer = Trainer(config, model)\n\n# model training\nbest_valid_score, best_valid_result = trainer.fit(train_data, valid_data)\n#best_valid_score, best_valid_result = trainer.fit(train_data)","metadata":{"scrolled":true,"trusted":true,"execution":{"iopub.status.busy":"2024-12-29T05:54:38.861483Z","iopub.execute_input":"2024-12-29T05:54:38.861750Z","iopub.status.idle":"2024-12-29T07:09:05.621360Z","shell.execute_reply.started":"2024-12-29T05:54:38.861724Z","shell.execute_reply":"2024-12-29T07:09:05.620590Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import gc\ndel trainer, train_data, valid_data, test_data","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-29T07:09:05.622410Z","iopub.execute_input":"2024-12-29T07:09:05.622678Z","iopub.status.idle":"2024-12-29T07:09:05.627157Z","shell.execute_reply.started":"2024-12-29T07:09:05.622651Z","shell.execute_reply":"2024-12-29T07:09:05.626149Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"gc.collect()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-29T07:09:05.628122Z","iopub.execute_input":"2024-12-29T07:09:05.628435Z","iopub.status.idle":"2024-12-29T07:09:06.356392Z","shell.execute_reply.started":"2024-12-29T07:09:05.628378Z","shell.execute_reply":"2024-12-29T07:09:06.355384Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 4. Create recommendation result from trained model\n\nI note document here for any one want to customize it: https://recbole.io/docs/user_guide/usage/case_study.html","metadata":{}},{"cell_type":"code","source":"# https://qiita.com/fufufukakaka/items/e03df3a7299b2b8f99cf\nfrom typing import List, Tuple\nimport numpy as np\nimport torch\n\nfrom pydantic import BaseModel\nfrom recbole.data import create_dataset\nfrom recbole.data.dataset.sequential_dataset import SequentialDataset\nfrom recbole.data.interaction import Interaction\nfrom recbole.model.sequential_recommender.sine import SINE\nfrom recbole.utils import get_model, init_seed\n\nclass ItemHistory(BaseModel):\n    sequence: List[str]\n    topk: int\n\nclass RecommendedItems(BaseModel):\n    score_list: List[float]\n    item_list: List[str]\n\n\ndef pred_user_to_item(item_history: ItemHistory):\n    item_history_dict = item_history.dict()\n    item_sequence = item_history_dict[\"sequence\"]\n    item_length = len(item_sequence)\n    pad_length = MAX_ITEM  # pre-defined by recbole\n\n    padded_item_sequence = torch.nn.functional.pad(\n        torch.tensor(dataset.token2id(dataset.iid_field, item_sequence)),\n        (0, pad_length - item_length),\n        \"constant\",\n        0,\n    )\n\n    input_interaction = Interaction(\n        {\n            \"aid_list\": padded_item_sequence.reshape(1, -1),\n            \"item_length\": torch.tensor([item_length]),\n        }\n    )\n    scores = model.full_sort_predict(input_interaction.to(model.device))\n    scores = scores.view(-1, dataset.item_num)\n    scores[:, 0] = -np.inf  # pad item score -> -inf\n    topk_score, topk_iid_list = torch.topk(scores, item_history_dict[\"topk\"])\n\n    predicted_score_list = topk_score.tolist()[0]\n    predicted_item_list = dataset.id2token(\n        dataset.iid_field, topk_iid_list.tolist()\n    ).tolist()\n\n    recommended_items = {\n        \"score_list\": predicted_score_list,\n        \"item_list\": predicted_item_list,\n    }\n    return recommended_items","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-29T07:09:06.360240Z","iopub.execute_input":"2024-12-29T07:09:06.360501Z","iopub.status.idle":"2024-12-29T07:09:06.463535Z","shell.execute_reply.started":"2024-12-29T07:09:06.360479Z","shell.execute_reply":"2024-12-29T07:09:06.462547Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#test = pl.read_parquet('../input/otto-train-and-test-data-for-local-validation/test.parquet')\ntest = pl.read_parquet('../input/otto-full-optimized-memory-footprint/test.parquet')\n\nimport pandas as pd\nimport numpy as np\n\nfrom collections import defaultdict\n\n#sample_sub = pd.read_csv('../input/otto-recommender-system//sample_submission.csv')\n\nsession_types = ['clicks', 'carts', 'orders']\ntest_session_AIDs = test.to_pandas().reset_index(drop=True).groupby('session')['aid'].apply(list)\ntest_session_types = test.to_pandas().reset_index(drop=True).groupby('session')['type'].apply(list)\n\ndel test\ngc.collect()\n\nlabels = []\n\ntype_weight_multipliers = {0: 1, 1: 6, 2: 3}\nfor AIDs, types in zip(test_session_AIDs, test_session_types):\n    if len(AIDs) >= 20:\n        # if we have enough aids (over equals 20) we don't need to look for candidates! we just use the old logic\n        weights=np.logspace(0.1,1,len(AIDs),base=2, endpoint=True)-1\n        aids_temp=defaultdict(lambda: 0)\n        for aid,w,t in zip(AIDs,weights,types): \n            aids_temp[aid]+= w * type_weight_multipliers[t]\n            \n        sorted_aids=[k for k, v in sorted(aids_temp.items(), key=lambda item: -item[1])]\n        labels.append(sorted_aids[:20])\n    else:\n        AIDs = list(dict.fromkeys(AIDs))\n        item = ItemHistory(sequence=AIDs, topk=20)\n        try:\n            nns = [ int(v) for v in pred_user_to_item(item)['item_list']]\n        except:\n            nns = []\n\n        for word in nns:\n            if len(AIDs) == 20:\n                break\n            if int(word) not in AIDs:\n                AIDs.append(word)\n\n        labels.append(AIDs[:20])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-29T07:09:06.464688Z","iopub.execute_input":"2024-12-29T07:09:06.464997Z","execution_failed":"2024-12-29T08:45:28.985Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"pred_user_to_item(item)['item_list']","metadata":{"trusted":true,"execution":{"execution_failed":"2024-12-29T08:45:28.985Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"labels_as_strings = [' '.join([str(l) for l in lls]) for lls in labels]\npredictions = pd.DataFrame(data={'session_type': test_session_AIDs.index, 'labels': labels_as_strings})","metadata":{"trusted":true,"execution":{"execution_failed":"2024-12-29T08:45:28.985Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"labels_as_strings = [' '.join([str(l) for l in lls]) for lls in labels]\n\npredictions = pd.DataFrame(data={'session_type': test_session_AIDs.index, 'labels': labels_as_strings})\n\nprediction_dfs = []\n\nfor st in session_types:\n    modified_predictions = predictions.copy()\n    modified_predictions.session_type = modified_predictions.session_type.astype('str') + f'_{st}'\n    prediction_dfs.append(modified_predictions)\n\nsubmission = pd.concat(prediction_dfs).reset_index(drop=True)\nsubmission.to_csv('submission.csv', index=False)","metadata":{"trusted":true,"execution":{"execution_failed":"2024-12-29T08:45:28.985Z"}},"outputs":[],"execution_count":null}]}