{"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":"# 1. Install & Import","metadata":{}},{"cell_type":"code","source":"!pip install recbole","metadata":{"execution":{"iopub.status.busy":"2022-04-28T03:13:56.314050Z","iopub.execute_input":"2022-04-28T03:13:56.314329Z","iopub.status.idle":"2022-04-28T03:14:03.918231Z","shell.execute_reply.started":"2022-04-28T03:13:56.314297Z","shell.execute_reply":"2022-04-28T03:14:03.917402Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import os\nimport numpy as np\nimport pandas as pd\nimport gc","metadata":{"execution":{"iopub.status.busy":"2022-04-28T03:14:03.921458Z","iopub.execute_input":"2022-04-28T03:14:03.921825Z","iopub.status.idle":"2022-04-28T03:14:03.927011Z","shell.execute_reply.started":"2022-04-28T03:14:03.921772Z","shell.execute_reply":"2022-04-28T03:14:03.926226Z"},"trusted":true},"execution_count":null,"outputs":[]},{"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 GRU4RecF, FDSA, BERT4Rec, GRU4Rec#, SASRecF\nfrom recbole.trainer import Trainer\nfrom recbole.utils import init_seed, init_logger\n\nimport random\n\nimport torch\nfrom torch import nn\n\nfrom recbole.model.abstract_recommender import SequentialRecommender\nfrom recbole.model.layers import FeedForward\n# from recbole.model.layers import FeatureSeqEmbLayer\n\nfrom recbole.utils import FeatureType\nimport copy\nimport math\n\nimport numpy as np\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as fn\nfrom torch.nn.init import normal_\n\nfrom recbole.utils import FeatureType, FeatureSource\nimport torch.nn.functional as F\nfrom recbole.data.interaction import Interaction","metadata":{"execution":{"iopub.status.busy":"2022-04-28T03:14:03.929552Z","iopub.execute_input":"2022-04-28T03:14:03.929770Z","iopub.status.idle":"2022-04-28T03:14:03.941163Z","shell.execute_reply.started":"2022-04-28T03:14:03.929740Z","shell.execute_reply":"2022-04-28T03:14:03.940313Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 2. Create atomic files for Recbole training","metadata":{}},{"cell_type":"markdown","source":"These datasets are all publicly available on kaggle. ","metadata":{}},{"cell_type":"code","source":"!mkdir /kaggle/working/hm_atomic_interation_with_item_feature\n# inter = pd.read_csv('../input/hm-atomic-interation-with-item-feature/hm_atomic_interation_with_item_feature.inter', sep='\\t')\n\ninter = pd.read_csv('../input/reduced-inter/recbox_data_post2020.inter', sep='\\t')\n# inter = inter[inter['timestamp:float'] > 1589620000 ]# 1595620000\ninter.to_csv('/kaggle/working/hm_atomic_interation_with_item_feature/hm_atomic_interation_with_item_feature.inter', index=False, sep='\\t')\ndel inter\ngc.collect()","metadata":{"execution":{"iopub.status.busy":"2022-04-28T03:14:03.943240Z","iopub.execute_input":"2022-04-28T03:14:03.943944Z","iopub.status.idle":"2022-04-28T03:14:30.455527Z","shell.execute_reply.started":"2022-04-28T03:14:03.943904Z","shell.execute_reply":"2022-04-28T03:14:30.454843Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"item = pd.read_csv('../input/bertembedding/out_bert_embed.csv')\n# item = pd.read_csv('../input/tfidf-embedding/out_2.csv')\n# item = pd.read_csv('../input/feature-bert-embed/bert_embed_feature.csv')\nitem = item.rename(columns={'article_id':'item_id:token', 'embed': 'item_emb:float_seq'})\nprint(item.head())\nprint(item.shape)\nitem.to_csv('/kaggle/working/hm_atomic_interation_with_item_feature/hm_atomic_interation_with_item_feature.item', index=False, sep='\\t')\ndel item\ngc.collect()","metadata":{"execution":{"iopub.status.busy":"2022-04-28T03:14:30.457820Z","iopub.execute_input":"2022-04-28T03:14:30.458235Z","iopub.status.idle":"2022-04-28T03:14:55.556990Z","shell.execute_reply.started":"2022-04-28T03:14:30.458195Z","shell.execute_reply":"2022-04-28T03:14:55.556326Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Create and train Recbole model","metadata":{}},{"cell_type":"markdown","source":"## Define BERT4RecF\n\nThis part adds feature embedding layer to Bert4Rec model so that the model could utilize item feature information.","metadata":{}},{"cell_type":"code","source":"\n\nimport random\n\nimport torch\nfrom torch import nn\n\nfrom recbole.model.abstract_recommender import SequentialRecommender\nfrom recbole.model.layers import TransformerEncoder\nfrom recbole.model.layers import FeatureSeqEmbLayer\n\n\nclass BERT4RecF(SequentialRecommender):\n\n    def __init__(self, config, dataset):\n        super(BERT4RecF, self).__init__(config, dataset)\n\n        # load parameters info\n        self.n_layers = config['n_layers']\n        self.n_heads = config['n_heads']\n        self.hidden_size = config['hidden_size']  # same as embedding_size\n        self.inner_size = config['inner_size']  # the dimensionality in feed-forward layer\n        self.hidden_dropout_prob = config['hidden_dropout_prob']\n        self.attn_dropout_prob = config['attn_dropout_prob']\n        self.hidden_act = config['hidden_act']\n        self.layer_norm_eps = config['layer_norm_eps']\n\n        self.mask_ratio = config['mask_ratio']\n\n        self.loss_type = config['loss_type']\n        self.initializer_range = config['initializer_range']\n\n        # add feature selection parameters\n        \n        # load dataset info\n        self.mask_token = self.n_items\n        self.mask_item_length = int(self.mask_ratio * self.max_seq_length)\n\n        # define layers and loss\n        self.item_embedding = nn.Embedding(self.n_items + 1, self.hidden_size,  padding_idx=0)  # mask token add 1\n        self.item_embedding_transformation = nn.Linear(in_features = dataset.item_feat['item_emb'].shape[1], out_features=self.hidden_size)\n       \n\n        self.position_embedding = nn.Embedding(self.max_seq_length + 1, self.hidden_size)  # add mask_token at the last\n        self.trm_encoder = TransformerEncoder(\n            n_layers=self.n_layers,\n            n_heads=self.n_heads,\n            hidden_size=self.hidden_size,\n            inner_size=self.inner_size,\n            hidden_dropout_prob=self.hidden_dropout_prob,\n            attn_dropout_prob=self.attn_dropout_prob,\n            hidden_act=self.hidden_act,\n            layer_norm_eps=self.layer_norm_eps\n        )\n\n        self.LayerNorm = nn.LayerNorm(self.hidden_size, eps=self.layer_norm_eps)\n        self.dropout = nn.Dropout(self.hidden_dropout_prob)\n\n        # we only need compute the loss at the masked position\n        try:\n            assert self.loss_type in ['BPR', 'CE']\n        except AssertionError:\n            raise AssertionError(\"Make sure 'loss_type' in ['BPR', 'CE']!\")\n\n        # parameters initialization\n        self.apply(self._init_weights)\n        # initialize from \n        unpadded_embeddings = dataset.item_feat['item_emb']\n        padded_embeddings = torch.zeros(unpadded_embeddings.shape[0]+1, unpadded_embeddings.shape[1])\n        padded_embeddings[1:, :] = unpadded_embeddings\n        self.item_feature_embedding = nn.Embedding(unpadded_embeddings.shape[0]+1, unpadded_embeddings.shape[1], padding_idx=0)\n        self.item_feature_embedding.weight.data.copy_(padded_embeddings)\n\n\n    def _init_weights(self, module):\n        \"\"\" Initialize the weights \"\"\"\n        if isinstance(module, (nn.Linear, nn.Embedding)):\n            # Slightly different from the TF version which uses truncated_normal for initialization\n            # cf https://github.com/pytorch/pytorch/pull/5617\n            module.weight.data.normal_(mean=0.0, std=self.initializer_range)\n        elif isinstance(module, nn.LayerNorm):\n            module.bias.data.zero_()\n            module.weight.data.fill_(1.0)\n        if isinstance(module, nn.Linear) and module.bias is not None:\n            module.bias.data.zero_()\n\n    def _neg_sample(self, item_set):\n        item = random.randint(1, self.n_items - 1)\n        while item in item_set:\n            item = random.randint(1, self.n_items - 1)\n        return item\n\n    def _padding_sequence(self, sequence, max_length):\n        pad_len = max_length - len(sequence)\n        sequence = [0] * pad_len + sequence\n        sequence = sequence[-max_length:]  # truncate according to the max_length\n        return sequence\n\n    def reconstruct_train_data(self, item_seq):\n        \"\"\"\n        Mask item sequence for training.\n        \"\"\"\n        device = item_seq.device\n        batch_size = item_seq.size(0)\n\n        sequence_instances = item_seq.cpu().numpy().tolist()\n\n        # Masked Item Prediction\n        # [B * Len]\n        masked_item_sequence = []\n        pos_items = []\n        neg_items = []\n        masked_index = []\n        for instance in sequence_instances:\n            # WE MUST USE 'copy()' HERE!\n            masked_sequence = instance.copy()\n            pos_item = []\n            neg_item = []\n            index_ids = []\n            for index_id, item in enumerate(instance):\n                # padding is 0, the sequence is end\n                if item == 0:\n                    break\n                prob = random.random()\n                if prob < self.mask_ratio:\n                    pos_item.append(item)\n                    neg_item.append(self._neg_sample(instance))\n                    masked_sequence[index_id] = self.mask_token\n                    index_ids.append(index_id)\n\n            masked_item_sequence.append(masked_sequence)\n            pos_items.append(self._padding_sequence(pos_item, self.mask_item_length))\n            neg_items.append(self._padding_sequence(neg_item, self.mask_item_length))\n            masked_index.append(self._padding_sequence(index_ids, self.mask_item_length))\n\n        # [B Len]\n        masked_item_sequence = torch.tensor(masked_item_sequence, dtype=torch.long, device=device).view(batch_size, -1)\n        # [B mask_len]\n        pos_items = torch.tensor(pos_items, dtype=torch.long, device=device).view(batch_size, -1)\n        # [B mask_len]\n        neg_items = torch.tensor(neg_items, dtype=torch.long, device=device).view(batch_size, -1)\n        # [B mask_len]\n        masked_index = torch.tensor(masked_index, dtype=torch.long, device=device).view(batch_size, -1)\n        return masked_item_sequence, pos_items, neg_items, masked_index\n\n    def reconstruct_test_data(self, item_seq, item_seq_len):\n        \"\"\"\n        Add mask token at the last position according to the lengths of item_seq\n        \"\"\"\n        padding = torch.zeros(item_seq.size(0), dtype=torch.long, device=item_seq.device)  # [B]\n        item_seq = torch.cat((item_seq, padding.unsqueeze(-1)), dim=-1)  # [B max_len+1]\n        for batch_id, last_position in enumerate(item_seq_len):\n            item_seq[batch_id][last_position] = self.mask_token\n        return item_seq\n\n    def forward(self, item_seq):\n        position_ids = torch.arange(item_seq.size(1), dtype=torch.long, device=item_seq.device)\n        position_ids = position_ids.unsqueeze(0).expand_as(item_seq)\n        position_embedding = self.position_embedding(position_ids)\n        item_emb = self.item_embedding(item_seq)\n        item_feature_emb = self.item_embedding_transformation(self.item_feature_embedding(item_seq))\n        input_emb = item_emb + position_embedding + item_feature_emb\n        input_emb = self.LayerNorm(input_emb)\n        input_emb = self.dropout(input_emb)\n        extended_attention_mask = self.get_attention_mask(item_seq, bidirectional=True)\n        trm_output = self.trm_encoder(input_emb, extended_attention_mask, output_all_encoded_layers=True)\n        output = trm_output[-1]\n        return output  # [B L H]\n\n    def multi_hot_embed(self, masked_index, max_length):\n        \"\"\"\n        For memory, we only need calculate loss for masked position.\n        Generate a multi-hot vector to indicate the masked position for masked sequence, and then is used for\n        gathering the masked position hidden representation.\n        Examples:\n            sequence: [1 2 3 4 5]\n            masked_sequence: [1 mask 3 mask 5]\n            masked_index: [1, 3]\n            max_length: 5\n            multi_hot_embed: [[0 1 0 0 0], [0 0 0 1 0]]\n        \"\"\"\n        masked_index = masked_index.view(-1)\n        multi_hot = torch.zeros(masked_index.size(0), max_length, device=masked_index.device)\n        multi_hot[torch.arange(masked_index.size(0)), masked_index] = 1\n        return multi_hot\n\n    def calculate_loss(self, interaction):\n        item_seq = interaction[self.ITEM_SEQ]\n        masked_item_seq, pos_items, neg_items, masked_index = self.reconstruct_train_data(item_seq)\n\n        seq_output = self.forward(masked_item_seq)\n        pred_index_map = self.multi_hot_embed(masked_index, masked_item_seq.size(-1))  # [B*mask_len max_len]\n        # [B mask_len] -> [B mask_len max_len] multi hot\n        pred_index_map = pred_index_map.view(masked_index.size(0), masked_index.size(1), -1)  # [B mask_len max_len]\n        # [B mask_len max_len] * [B max_len H] -> [B mask_len H]\n        # only calculate loss for masked position\n        seq_output = torch.bmm(pred_index_map, seq_output)  # [B mask_len H]\n\n        if self.loss_type == 'BPR':\n            pos_items_emb = self.item_embedding(pos_items)  # [B mask_len H]\n            neg_items_emb = self.item_embedding(neg_items)  # [B mask_len H]\n            pos_score = torch.sum(seq_output * pos_items_emb, dim=-1)  # [B mask_len]\n            neg_score = torch.sum(seq_output * neg_items_emb, dim=-1)  # [B mask_len]\n            targets = (masked_index > 0).float()\n            loss = - torch.sum(torch.log(1e-14 + torch.sigmoid(pos_score - neg_score)) * targets) \\\n                   / torch.sum(targets)\n            return loss\n\n        elif self.loss_type == 'CE':\n            loss_fct = nn.CrossEntropyLoss(reduction='none')\n#             test_item_emb = self.item_embedding_transformation(self.item_embedding.weight)[:self.n_items]  # [item_num H]\n            test_item_emb = self.item_embedding.weight[:self.n_items]\n            logits = torch.matmul(seq_output, test_item_emb.transpose(0, 1))  # [B mask_len item_num]\n            targets = (masked_index > 0).float().view(-1)  # [B*mask_len]\n\n            loss = torch.sum(loss_fct(logits.view(-1, test_item_emb.size(0)), pos_items.view(-1)) * targets) \\\n                   / torch.sum(targets)\n            return loss\n        else:\n            raise NotImplementedError(\"Make sure 'loss_type' in ['BPR', 'CE']!\")\n\n    def predict(self, interaction):\n        item_seq = interaction[self.ITEM_SEQ]\n        item_seq_len = interaction[self.ITEM_SEQ_LEN]\n        test_item = interaction[self.ITEM_ID]\n        item_seq = self.reconstruct_test_data(item_seq, item_seq_len)\n        seq_output = self.forward(item_seq)\n        seq_output = self.gather_indexes(seq_output, item_seq_len)  # [B H]\n        test_item_emb = self.item_embedding(test_item)\n        scores = torch.mul(seq_output, test_item_emb).sum(dim=1)  # [B]\n        return scores\n\n    def full_sort_predict(self, interaction):\n        item_seq = interaction[self.ITEM_SEQ]\n        item_seq_len = interaction[self.ITEM_SEQ_LEN]\n        item_seq = self.reconstruct_test_data(item_seq, item_seq_len)\n        seq_output = self.forward(item_seq)\n        seq_output = self.gather_indexes(seq_output, item_seq_len)  # [B H]\n        test_items_emb = self.item_embedding.weight[:self.n_items]  # delete masked token\n        scores = torch.matmul(seq_output, test_items_emb.transpose(0, 1))  # [B, item_num]\n        return scores","metadata":{"execution":{"iopub.status.busy":"2022-04-28T03:14:55.589968Z","iopub.execute_input":"2022-04-28T03:14:55.590368Z","iopub.status.idle":"2022-04-28T03:14:55.636195Z","shell.execute_reply.started":"2022-04-28T03:14:55.590329Z","shell.execute_reply":"2022-04-28T03:14:55.635453Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Model training\n\nThis part trains the model","metadata":{}},{"cell_type":"code","source":"parameter_dict = {\n    'data_path': '/kaggle/working',\n    'USER_ID_FIELD': 'user_id',\n    'ITEM_ID_FIELD': 'item_id',\n    'TIME_FIELD': 'timestamp',\n    'user_inter_num_interval': \"[40,inf)\",\n    'item_inter_num_interval': \"[40,inf)\",\n    'load_col': {'inter': ['user_id', 'item_id', 'timestamp'],\n                  'item': ['item_id', 'item_emb']\n             },\n    'selected_features': ['item_emb'],\n    'neg_sampling': None,\n    'epochs': 30,\n    'train_batch_size': 256,\n    'n_layers': 2,\n    'n_heads': 2,\n    'hidden_size': 64,\n    'inner_size': 256,\n    'hidden_dropout_prob': 0.5,\n    'attn_dropout_prob': 0.5,\n    'hidden_act': 'gelu',\n    'layer_norm_eps': 1e-12,\n    'initializer_range': 0.02,\n    'mask_ratio': 0.2,\n    'loss_type': 'CE',\n    'learning_rate': 0.002,\n    'pooling_mode': 'sum',\n    'eval_args': {\n        'split': {'RS': [10, 0, 0]},\n        'group_by': 'user',\n        'order': 'TO',\n        'mode': 'full'}\n}\n\nconfig = Config(model=BERT4RecF, dataset='hm_atomic_interation_with_item_feature', 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# Create handlers\nc_handler = logging.StreamHandler()\nc_handler.setLevel(logging.INFO)\nlogger.addHandler(c_handler)\n\n","metadata":{"execution":{"iopub.status.busy":"2022-04-28T03:14:55.637726Z","iopub.execute_input":"2022-04-28T03:14:55.638300Z","iopub.status.idle":"2022-04-28T03:14:55.721876Z","shell.execute_reply.started":"2022-04-28T03:14:55.638261Z","shell.execute_reply":"2022-04-28T03:14:55.721171Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"dataset = create_dataset(config)\nlogger.info(dataset)","metadata":{"execution":{"iopub.status.busy":"2022-04-28T03:14:55.723217Z","iopub.execute_input":"2022-04-28T03:14:55.723450Z","iopub.status.idle":"2022-04-28T03:16:31.733534Z","shell.execute_reply.started":"2022-04-28T03:14:55.723417Z","shell.execute_reply":"2022-04-28T03:16:31.732897Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_data, valid_data, test_data = data_preparation(config, dataset)","metadata":{"execution":{"iopub.status.busy":"2022-04-28T03:16:31.734757Z","iopub.execute_input":"2022-04-28T03:16:31.735655Z","iopub.status.idle":"2022-04-28T03:16:46.441420Z","shell.execute_reply.started":"2022-04-28T03:16:31.735607Z","shell.execute_reply":"2022-04-28T03:16:46.440839Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# # model loading and initialization\nmodel = BERT4RecF(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)","metadata":{"execution":{"iopub.status.busy":"2022-04-28T03:16:46.442713Z","iopub.execute_input":"2022-04-28T03:16:46.442955Z","iopub.status.idle":"2022-04-28T03:22:29.309802Z","shell.execute_reply.started":"2022-04-28T03:16:46.442920Z","shell.execute_reply":"2022-04-28T03:22:29.309064Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"The following commented code chunk was used for loading trained models.","metadata":{}},{"cell_type":"code","source":"# model_file = \"../input/onehot-bert-m/BERT4RecF-Apr-18-2022_02-18-35.pth\"\n# checkpoint = torch.load(model_file)\n# config = checkpoint['config']\n# init_seed(config['seed'], config['reproducibility'])\n# init_logger(config)\n# logger = getLogger()\n# logger.info(config)\n# model = BERT4RecF(config, train_data.dataset).to(config['device'])\n# model.load_state_dict(checkpoint['state_dict'])\n# model.load_other_parameter(checkpoint.get('other_parameter'))\n","metadata":{"execution":{"iopub.status.busy":"2022-04-28T03:22:29.311133Z","iopub.execute_input":"2022-04-28T03:22:29.311383Z","iopub.status.idle":"2022-04-28T03:22:29.316336Z","shell.execute_reply.started":"2022-04-28T03:22:29.311348Z","shell.execute_reply":"2022-04-28T03:22:29.315078Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Combine models\n\nThis part makes predictions and fills out the \"cold-start\" ones with 12 most frequence items.","metadata":{}},{"cell_type":"code","source":"from recbole.utils.case_study import full_sort_topk\nfrom recbole.quick_start.quick_start import load_data_and_model\n# config, model, dataset, train_data, valid_data, test_data = load_data_and_model(\n#     model_file='/kaggle/working/saved/SASRecF-Apr-05-2022_20-56-46.pth',\n# )\nexternal_user_ids = dataset.id2token(\n    dataset.uid_field, list(range(dataset.user_num)))[1:]#fist element in array is 'PAD'(default of Recbole) ->remove it ","metadata":{"execution":{"iopub.status.busy":"2022-04-28T03:22:29.317951Z","iopub.execute_input":"2022-04-28T03:22:29.318228Z","iopub.status.idle":"2022-04-28T03:22:29.335004Z","shell.execute_reply.started":"2022-04-28T03:22:29.318195Z","shell.execute_reply":"2022-04-28T03:22:29.334244Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import torch\nfrom recbole.data.interaction import Interaction\n\ndef add_last_item(old_interaction, last_item_id, max_len=50):\n    new_seq_items = old_interaction['item_id_list'][-1]\n    if old_interaction['item_length'][-1].item() < max_len:\n        new_seq_items[old_interaction['item_length'][-1].item()] = last_item_id\n    else:\n        new_seq_items = torch.roll(new_seq_items, -1)\n        new_seq_items[-1] = last_item_id\n    return new_seq_items.view(1, len(new_seq_items))\n\ndef predict_for_all_item(external_user_id, dataset, model):\n    model.eval()\n    with torch.no_grad():\n        uid_series = dataset.token2id(dataset.uid_field, [external_user_id])\n        index = np.isin(dataset.inter_feat[dataset.uid_field].numpy(), uid_series)\n        input_interaction = dataset[index]\n        test = {\n            'item_id_list': add_last_item(input_interaction, \n                                          input_interaction['item_id'][-1].item(), model.max_seq_length),\n            'item_length': torch.tensor(\n                [input_interaction['item_length'][-1].item() + 1\n                 if input_interaction['item_length'][-1].item() < model.max_seq_length else model.max_seq_length])\n        }\n        new_inter = Interaction(test)\n        new_inter = new_inter.to(config['device'])\n        new_scores = model.full_sort_predict(new_inter)\n        new_scores = new_scores.view(-1, test_data.dataset.item_num)\n        new_scores[:, 0] = -np.inf  # set scores of [pad] to -inf\n    return torch.topk(new_scores, 12)[1]","metadata":{"execution":{"iopub.status.busy":"2022-04-28T03:27:59.293682Z","iopub.execute_input":"2022-04-28T03:27:59.293944Z","iopub.status.idle":"2022-04-28T03:27:59.307044Z","shell.execute_reply.started":"2022-04-28T03:27:59.293916Z","shell.execute_reply":"2022-04-28T03:27:59.306276Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"topk_items = []\nfor external_user_id in external_user_ids:\n    topk_iid_list = predict_for_all_item(external_user_id, dataset, model)\n    last_topk_iid_list = topk_iid_list[-1]\n    external_item_list = dataset.id2token(dataset.iid_field, last_topk_iid_list.cpu()).tolist()\n    topk_items.append(external_item_list)\nprint(len(topk_items))","metadata":{"execution":{"iopub.status.busy":"2022-04-28T03:29:17.596015Z","iopub.execute_input":"2022-04-28T03:29:17.596275Z","iopub.status.idle":"2022-04-28T03:33:07.240639Z","shell.execute_reply.started":"2022-04-28T03:29:17.596245Z","shell.execute_reply":"2022-04-28T03:33:07.239885Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"external_item_str = [' '.join(x) for x in topk_items]\nresult = pd.DataFrame(external_user_ids, columns=['customer_id'])\nresult['prediction'] = external_item_str\nresult.head()","metadata":{"execution":{"iopub.status.busy":"2022-04-28T03:22:29.542681Z","iopub.status.idle":"2022-04-28T03:22:29.543193Z","shell.execute_reply.started":"2022-04-28T03:22:29.542960Z","shell.execute_reply":"2022-04-28T03:22:29.542984Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"del external_item_str\ndel topk_items\ndel external_user_ids\ndel train_data\ndel valid_data\ndel test_data\ndel model\ndel Trainer\ndel logger\ndel dataset\ngc.collect()","metadata":{"execution":{"iopub.status.busy":"2022-04-28T03:22:29.544531Z","iopub.status.idle":"2022-04-28T03:22:29.545108Z","shell.execute_reply.started":"2022-04-28T03:22:29.544870Z","shell.execute_reply":"2022-04-28T03:22:29.544894Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"result.head()","metadata":{"execution":{"iopub.status.busy":"2022-04-28T03:22:29.546428Z","iopub.status.idle":"2022-04-28T03:22:29.546853Z","shell.execute_reply.started":"2022-04-28T03:22:29.546622Z","shell.execute_reply":"2022-04-28T03:22:29.546647Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"reference = pd.read_csv('../input/uid-reference/reference.csv')\nreference.head()","metadata":{"execution":{"iopub.status.busy":"2022-04-28T03:22:29.548275Z","iopub.status.idle":"2022-04-28T03:22:29.548698Z","shell.execute_reply.started":"2022-04-28T03:22:29.548467Z","shell.execute_reply":"2022-04-28T03:22:29.548503Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"result.customer_id = result.customer_id.astype('int64')\nresult.dtypes","metadata":{"execution":{"iopub.status.busy":"2022-04-28T03:22:29.549920Z","iopub.status.idle":"2022-04-28T03:22:29.550561Z","shell.execute_reply.started":"2022-04-28T03:22:29.550312Z","shell.execute_reply":"2022-04-28T03:22:29.550337Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"new_result = pd.merge(result, reference, how='left', left_on='customer_id', right_on='new_id', indicator=False, suffixes=(\"_x\", \"\")).drop(columns=['customer_id_x', 'new_id'])","metadata":{"execution":{"iopub.status.busy":"2022-04-28T03:22:29.551871Z","iopub.status.idle":"2022-04-28T03:22:29.552268Z","shell.execute_reply.started":"2022-04-28T03:22:29.552052Z","shell.execute_reply":"2022-04-28T03:22:29.552074Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"new_result = new_result[['customer_id', 'prediction']]\nnew_result.head()","metadata":{"execution":{"iopub.status.busy":"2022-04-28T03:22:29.553717Z","iopub.status.idle":"2022-04-28T03:22:29.554121Z","shell.execute_reply.started":"2022-04-28T03:22:29.553904Z","shell.execute_reply":"2022-04-28T03:22:29.553926Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"submit_df = pd.read_csv('../input/cold-start/submission.csv')\nsubmit_df = pd.merge(submit_df, new_result, on='customer_id', how='outer')\nsubmit_df.head()","metadata":{"execution":{"iopub.status.busy":"2022-04-28T03:22:29.555406Z","iopub.status.idle":"2022-04-28T03:22:29.556204Z","shell.execute_reply.started":"2022-04-28T03:22:29.555961Z","shell.execute_reply":"2022-04-28T03:22:29.555989Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"submit_df = submit_df.fillna(-1)\nsubmit_df['prediction'] = submit_df.apply(\n    lambda x: x['prediction_y'] if x['prediction_y'] != -1 else x['prediction_x'], axis=1)\nsubmit_df.head()","metadata":{"execution":{"iopub.status.busy":"2022-04-28T03:22:29.557344Z","iopub.status.idle":"2022-04-28T03:22:29.558149Z","shell.execute_reply.started":"2022-04-28T03:22:29.557900Z","shell.execute_reply":"2022-04-28T03:22:29.557928Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"submit_df = submit_df.drop(columns=['prediction_y', 'prediction_x'])\nsubmit_df.head()","metadata":{"execution":{"iopub.status.busy":"2022-04-28T03:22:29.559370Z","iopub.status.idle":"2022-04-28T03:22:29.559780Z","shell.execute_reply.started":"2022-04-28T03:22:29.559565Z","shell.execute_reply":"2022-04-28T03:22:29.559586Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"submit_df.to_csv('submission.csv', index=False)","metadata":{"execution":{"iopub.status.busy":"2022-04-28T03:22:29.560891Z","iopub.status.idle":"2022-04-28T03:22:29.561729Z","shell.execute_reply.started":"2022-04-28T03:22:29.561501Z","shell.execute_reply":"2022-04-28T03:22:29.561526Z"},"trusted":true},"execution_count":null,"outputs":[]}]}