{"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 provides general idea of how we can train models to predict next transactions based on the previous ones.\n\nIt is just my experiemental model and probably one more idea how we can use data. According to my experiements this model can't get signal from the data and has a poor perfomance, but, maybe, someone could improve it and get better score. There are plenty variants to improve: hyperparameters, size of the dataset, transformer-decoder model, train model as masked language model and extract embeddings.","metadata":{}},{"cell_type":"code","source":"!pip install -q python-box","metadata":{"execution":{"iopub.status.busy":"2022-02-13T07:04:54.562042Z","iopub.execute_input":"2022-02-13T07:04:54.562336Z","iopub.status.idle":"2022-02-13T07:05:07.526680Z","shell.execute_reply.started":"2022-02-13T07:04:54.562305Z","shell.execute_reply":"2022-02-13T07:05:07.525567Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nfrom pathlib import Path\nimport matplotlib.pyplot as plt\nfrom IPython.display import display, HTML\nimport cv2\nfrom tqdm.notebook import tqdm\nfrom sklearn.model_selection import train_test_split\nfrom pytorch_lightning import LightningModule\nfrom pytorch_lightning.callbacks import ModelCheckpoint\nimport pytorch_lightning as pl\nimport seaborn as sns\nimport torch.nn as nn\nfrom torch.utils.data import Dataset, DataLoader\nfrom typing import List, Dict\nimport torch\nimport random\nimport torch.optim as optim\nfrom box import Box","metadata":{"execution":{"iopub.status.busy":"2022-02-13T07:05:09.057165Z","iopub.execute_input":"2022-02-13T07:05:09.057490Z","iopub.status.idle":"2022-02-13T07:05:15.081873Z","shell.execute_reply.started":"2022-02-13T07:05:09.057457Z","shell.execute_reply":"2022-02-13T07:05:15.080699Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"data_path = Path('../input/h-and-m-personalized-fashion-recommendations')\nimages_path = data_path/'images'","metadata":{"execution":{"iopub.status.busy":"2022-02-13T07:05:15.085522Z","iopub.execute_input":"2022-02-13T07:05:15.086125Z","iopub.status.idle":"2022-02-13T07:05:15.092379Z","shell.execute_reply.started":"2022-02-13T07:05:15.086069Z","shell.execute_reply":"2022-02-13T07:05:15.091099Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Provide general constants for the dataset. Columns just needed to extract only needed information.","metadata":{}},{"cell_type":"code","source":"COLUMNS = [\n    't_dat',\n    'customer_id',\n    'article_id',\n    'product_code',\n    'product_type_no',\n    'section_no',\n    'index_group_no'\n]\nMAX_LEN = 20\nPAD_IDX = 0\nN_SAMPLES = 5_000_000 #number of transactions","metadata":{"execution":{"iopub.status.busy":"2022-02-13T07:05:18.154559Z","iopub.execute_input":"2022-02-13T07:05:18.154888Z","iopub.status.idle":"2022-02-13T07:05:18.160940Z","shell.execute_reply.started":"2022-02-13T07:05:18.154856Z","shell.execute_reply":"2022-02-13T07:05:18.159683Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sample_sub = pd.read_csv(data_path/'sample_submission.csv')\ncutomers = pd.read_csv(data_path/'customers.csv')\narticles = pd.read_csv(data_path/'articles.csv')\ntransactions_train = pd.read_csv(data_path/'transactions_train.csv', parse_dates=['t_dat'])\ntransactions = transactions_train.iloc[:N_SAMPLES].merge(articles, on = ('article_id'))[COLUMNS]\n\ndel transactions_train","metadata":{"execution":{"iopub.status.busy":"2022-02-13T07:05:20.496219Z","iopub.execute_input":"2022-02-13T07:05:20.496782Z","iopub.status.idle":"2022-02-13T07:06:56.749916Z","shell.execute_reply.started":"2022-02-13T07:05:20.496732Z","shell.execute_reply":"2022-02-13T07:06:56.748869Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Mappings\nProvide useful mappings to convert articles to index, product code to the index of the embedding layer and etc...","metadata":{}},{"cell_type":"code","source":"articles_uniq = articles['article_id'].unique()\nproduct_type_uniq = articles['product_type_no'].unique()\nproduct_code_uniq = articles['product_code'].unique()\nsection_name_uniq = articles['section_no'].unique()\nindex_group_name_uniq = articles['index_group_no'].unique()\n\nn_articles = len(articles_uniq)\narticles_map = {0: PAD_IDX} #add padding to dictionary\narticles_map.update(dict(zip(range(1, n_articles+1), articles_uniq))) # indx - article_id\ninv_articles_map = dict(map(lambda x: (x[1], x[0]), articles_map.items())) # article_id - indx\n\nproduct_code_map = {0: PAD_IDX} #add padding to dictionary\nproduct_code_map.update(dict(zip(range(1, len(product_code_uniq)+1), product_code_uniq))) # indx - product_code_id\ninv_product_code_map = dict(map(lambda x: (x[1], x[0]), product_code_map.items())) # product_code_id - indx \n\nproduct_type_map = {0: PAD_IDX} #add padding to dictionary\nproduct_type_map.update(dict(zip(range(1, len(product_type_uniq)+1), product_type_uniq))) # indx - product_type\ninv_product_type_map = dict(map(lambda x: (x[1], x[0]), product_type_map.items())) # product_type - indx\n\nsection_name_map = {0: PAD_IDX} #add padding to dictionary\nsection_name_map.update(dict(zip(range(1, len(section_name_uniq)+1), section_name_uniq))) # indx - section_name\ninv_section_name_map = dict(map(lambda x: (x[1], x[0]), section_name_map.items())) # section_name - indx\n\nindex_group_map = {0: PAD_IDX} #add padding to dictionary\nindex_group_map.update(dict(zip(range(1, len(index_group_name_uniq)+1), index_group_name_uniq))) #indx - index_group\ninv_index_group_map = dict(map(lambda x: (x[1], x[0]), index_group_map.items())) # index_group - indx\n\n# another mapping\n# article_id - [product_type_no, product_code, section_no, index_group_no]\narticles_info = (\n    articles[[\n        'article_id', \n        'product_type_no', \n        'product_code', \n        'section_no', \n        'index_group_no'\n    ]]\n    .set_index('article_id')\n    .to_dict('index')\n)\n#add values for padding\narticles_info[0] = {\n    'index_group_no': PAD_IDX,\n    'product_code': PAD_IDX,\n    'product_type_no': PAD_IDX,\n    'section_no': PAD_IDX\n}","metadata":{"execution":{"iopub.status.busy":"2022-02-13T07:06:56.751959Z","iopub.execute_input":"2022-02-13T07:06:56.752276Z","iopub.status.idle":"2022-02-13T07:06:57.363214Z","shell.execute_reply.started":"2022-02-13T07:06:56.752232Z","shell.execute_reply":"2022-02-13T07:06:57.362133Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Model\nThis is a baseline model. I used only 4 types of embeddings: product_code, product_type, section_name, index_group_name.\n#### Training\nIn the training phase on each timestep model receives article_id and converts it to (product_code, product_type, section_name, index_group_name), then each value is translated into embedding and then concatenated. LSTM makes a forward step and outputs hidden states with output values. For the next step we can either take output value or true value (teacher forcing). And this step continues until model reaches the end of the batch.\n#### Inference\nIn the inference phase model receives train articles and then generate next article step by step.","metadata":{}},{"cell_type":"code","source":"class RnnModel(nn.Module):\n    def __init__(self, num_layers=2, hidden_size=80, device = torch.device('cuda:0')):\n        super().__init__()\n        self.device = device\n        self.n_layers = num_layers\n        self.hidden_size = hidden_size\n        # add +1 for padding\n        self.product_code = nn.Embedding(len(product_code_uniq)+1, 30, padding_idx = PAD_IDX)\n        self.product_type = nn.Embedding(len(product_type_uniq)+1, 10, padding_idx = PAD_IDX)\n        self.section_name = nn.Embedding(len(product_type_uniq)+1, 10, padding_idx = PAD_IDX)\n        self.index_group_name = nn.Embedding(len(product_type_uniq)+1, 5, padding_idx = PAD_IDX)\n        self.rnn = nn.LSTM(input_size=55, hidden_size=hidden_size, num_layers=num_layers, batch_first=True)\n        self.fc = nn.LazyLinear(n_articles)\n        \n    def forward(\n        self, \n        product_code = None,\n        product_type_no = None,\n        section_no = None,\n        index_group_no = None,\n        hidden = None, \n        cell = None,\n    ):\n        # basic forward pass\n        pr_code_emb = self.product_code(product_code)\n        pr_type_emb = self.product_type(product_type_no)\n        sec_name_emb = self.section_name(section_no)\n        indx_group_emb = self.index_group_name(index_group_no)\n\n        emb = torch.cat([pr_code_emb, pr_type_emb, sec_name_emb, indx_group_emb], dim = -1)\n        out, (h, c) = self.rnn(emb, (hidden, cell))\n        h_ = h.permute(1, 0, 2).reshape(out.shape[0], -1)\n        next_ = self.fc(h_).squeeze()\n        return next_, h, c\n\n    def _forward(\n        self, \n        product_code = None,\n        product_type_no = None,\n        section_no = None,\n        index_group_no = None,\n        t = 1\n        ):\n        # forward pass for training\n        MAX_LEN = product_code.shape[1]\n        \n        outputs = torch.zeros(product_code.shape[0], MAX_LEN, n_articles, requires_grad=True).to(self.device)\n        in_product_code = product_code[:, 0] #(batch_size,)\n        in_product_type_no = product_type_no[:, 0] #(batch_size,)\n        in_section_no = section_no[:, 0] #(batch_size,)\n        in_index_group_no = index_group_no[:, 0] #(batch_size,)\n\n        hidden, cell = self.init_state(product_code.shape[0])\n\n        for t in range(1, MAX_LEN-1):\n            in_product_code = in_product_code.unsqueeze(1)\n            in_product_type_no = in_product_type_no.unsqueeze(1)\n            in_section_no = in_section_no.unsqueeze(1)\n            in_index_group_no = in_index_group_no.unsqueeze(1)\n          \n            output, hidden, cell = self.forward(\n                                        in_product_code, \n                                        in_product_type_no,\n                                        in_section_no,\n                                        in_index_group_no,\n                                        hidden, \n                                        cell\n                                        )\n            outputs[:, t] = output\n            # decide whether to take the next value or the output\n            teacher_force = random.random() < t\n            top1 = output.max(1)[1].cpu().numpy()\n            articles = [articles_map[art] for art in top1]\n            \n            if teacher_force:\n              in_product_code = product_code[:, t]\n              in_product_type_no = product_type_no[:, t]\n              in_section_no = section_no[:, t]\n              in_index_group_no = index_group_no[:, t]\n            else:\n              info = CustomDataset._retrieve_info(articles)\n              in_product_code = torch.tensor(info['product_codes'], dtype = torch.long).to(self.device)\n              in_product_type_no = torch.tensor(info['product_types_no'], dtype = torch.long).to(self.device)\n              in_section_no = torch.tensor(info['sections_no'], dtype = torch.long).to(self.device)\n              in_index_group_no = torch.tensor(info['index_groups_no'], dtype = torch.long).to(self.device)\n        return outputs\n\n    def generate(\n        self, \n        product_code = None,\n        product_type_no = None,\n        section_no = None,\n        index_group_no = None,\n        ):\n      \n        MAX_LEN = product_code.shape[1]\n      \n        outputs = torch.zeros(product_code.shape[0], 12, n_articles).to(self.device)\n        hidden, cell = self.init_state(product_code.shape[0])\n        output, hidden, cell = self.forward(\n                                        product_code, \n                                        product_type_no,\n                                        section_no,\n                                        index_group_no,\n                                        hidden, \n                                        cell\n                                        )\n        \n        in_product_code = product_code[:, -1] #(batch_size,)\n        in_product_type_no = product_type_no[:, -1] #(batch_size,)\n        in_section_no = section_no[:, -1] #(batch_size,)\n        in_index_group_no = index_group_no[:, -1] #(batch_size,)\n        \n        for t in range(12):\n            in_product_code = in_product_code.unsqueeze(1)\n            in_product_type_no = in_product_type_no.unsqueeze(1)\n            in_section_no = in_section_no.unsqueeze(1)\n            in_index_group_no = in_index_group_no.unsqueeze(1)\n\n            output, hidden, cell = self.forward(\n                                        in_product_code, \n                                        in_product_type_no,\n                                        in_section_no,\n                                        in_index_group_no,\n                                        hidden, \n                                        cell\n                                        )\n            outputs[:, t] = output\n            top1 = output.max(1)[1].cpu().numpy()\n            articles = [articles_map[art] for art in top1]\n\n            info = CustomDataset._retrieve_info(articles)\n            in_product_code = torch.tensor(info['product_codes'], dtype = torch.long).to(self.device)\n            in_product_type_no = torch.tensor(info['product_types_no'], dtype = torch.long).to(self.device)\n            in_section_no = torch.tensor(info['sections_no'], dtype = torch.long).to(self.device)\n            in_index_group_no = torch.tensor(info['index_groups_no'], dtype = torch.long).to(self.device)\n        return outputs\n\n    def init_state(self, batch_size):\n        hidden = torch.zeros((self.n_layers, batch_size, self.hidden_size)).to(self.device)\n        cell = torch.zeros((self.n_layers, batch_size, self.hidden_size)).to(self.device)\n        return hidden, cell","metadata":{"execution":{"iopub.status.busy":"2022-02-13T07:06:57.364888Z","iopub.execute_input":"2022-02-13T07:06:57.365221Z","iopub.status.idle":"2022-02-13T07:06:57.401093Z","shell.execute_reply.started":"2022-02-13T07:06:57.365161Z","shell.execute_reply":"2022-02-13T07:06:57.400079Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"I used pytorch lightning as a wrapper. Basically, in LightningModule you need to define training_step, validation_step, loaders and optimizers.","metadata":{}},{"cell_type":"code","source":"class RnnLightningModel(LightningModule):\n  def __init__(self, cfg, model, train_df = None, val_df = None, test_df = None):\n    super().__init__()\n    self.cfg = cfg\n    self.train_df = train_df\n    self.val_df = val_df\n    self.model = model\n    self.criterion = nn.CrossEntropyLoss(ignore_index = 0)\n    self.save_hyperparameters(cfg, ignore = ['train_df', 'val_df', 'test_df', 'model', 'criterion'])\n\n  def forward(\n      self, \n      product_codes = None,\n      product_types_no = None,\n      sections_no = None,\n      index_groups_no = None,\n      t = 1\n      ):\n    \n    out = self.model._forward(\n        product_code = product_codes,\n        product_type_no = product_types_no,\n        section_no = sections_no,\n        index_group_no = index_groups_no,\n        t = t\n        )\n    return out\n\n  def generate(\n      self, \n      product_codes = None,\n      product_types_no = None,\n      sections_no = None,\n      index_groups_no = None,\n      ):\n        \n    out = self.model.generate(\n        product_code = product_codes,\n        product_type_no = product_types_no,\n        section_no = sections_no,\n        index_group_no = index_groups_no\n    )\n    top1 = out.max(-1)[1].cpu().numpy()\n    articles = [[articles_map[art] for art in client] for client in top1]\n    return articles\n\n  def training_step(self, batch, batch_idx):\n    y = batch.pop('labels')\n    output = self(**batch)\n\n    loss = self.criterion(output[:, 1:].reshape(-1, n_articles), y[:, 1:].reshape(-1))\n    self.log('train_loss', loss)\n    return {'loss': loss}\n\n  def validation_step(self, batch, batch_idx):\n    y = batch.pop('labels')\n    output = self(**batch, t = 0)\n\n    loss = self.criterion(output[:, 1:].reshape(-1, n_articles), y[:, 1:].reshape(-1))\n    self.log('val_loss', loss)\n    return {'loss': loss}\n\n  def train_dataloader(self):\n    dataset = CustomDataset(self.train_df)\n    loader = DataLoader(dataset, batch_size = self.cfg.batch_size, collate_fn=collate_fn, shuffle = True)\n    return loader\n\n  def val_dataloader(self):\n    dataset = CustomDataset(self.val_df)\n    loader = DataLoader(dataset, batch_size = self.cfg.batch_size, collate_fn=collate_fn, shuffle = False)\n    return loader\n\n  def __apply_weight_decay(self):\n    no_decay = []\n    decay = []\n    for n, p in self.named_parameters():\n        if 'bias' in n and 'LayerNorm' in n:\n            no_decay.append(p)\n        else:\n            decay.append(p)\n    return [{'params': no_decay, 'weight_decay': 0}, {'params': decay}]\n\n  def configure_optimizers(self):\n    optimizer = eval(self.cfg.optimizer.name)(\n        self.__apply_weight_decay(), **self.cfg.optimizer.params\n        )\n    scheduler = eval(self.cfg.scheduler.name)(\n        optimizer,\n        **self.cfg.scheduler.params\n        )\n    return [optimizer], [scheduler]\n\n  def validation_epoch_end(self, outputs):\n    avg_loss = torch.stack([x['loss'] for x in outputs]).mean()\n    self.log('val_loss', avg_loss)\n\n  def train_epoch_end(self, outputs):\n    avg_loss = torch.stack([x['loss'] for x in outputs]).mean()\n    self.log('train_loss', avg_loss)","metadata":{"execution":{"iopub.status.busy":"2022-02-13T07:06:57.404062Z","iopub.execute_input":"2022-02-13T07:06:57.405148Z","iopub.status.idle":"2022-02-13T07:06:57.432974Z","shell.execute_reply.started":"2022-02-13T07:06:57.405090Z","shell.execute_reply":"2022-02-13T07:06:57.431744Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Dataset","metadata":{}},{"cell_type":"code","source":"class CustomDataset(Dataset):\n    def __init__(self, df):\n        self.df = df\n      \n    def __len__(self):\n        return len(self.df)\n        \n    def __getitem__(self, indx):\n        item = self.df.iloc[indx]\n        article_id = item['article_id']\n        y = [inv_articles_map[x] for x in article_id]\n        info = self._retrieve_info(article_id)\n        return info, y\n\n    @staticmethod\n    def _retrieve_info(articles_id):\n        #for one customer\n        info = [articles_info[x] for x in articles_id]\n        product_codes = [inv_product_code_map[x['product_code']] for x in info]\n        product_types_no = [inv_product_type_map[x['product_type_no']] for x in info]\n        sections_no = [inv_section_name_map[x['section_no']] for x in info]\n        index_groups_no = [inv_index_group_map[x['index_group_no']] for x in info]\n        return {\n            'product_codes': product_codes,\n            'product_types_no': product_types_no,\n            'sections_no': sections_no,\n            'index_groups_no': index_groups_no\n        }\n\ndef collate_fn(batch: List[Dict[int, List]]):\n    max_len = min(max([len(b[1]) for b in batch]), MAX_LEN)\n    output_dict = {k: torch.zeros((len(batch), max_len), dtype = torch.long) for k in batch[0][0].keys()}\n    y = torch.zeros((len(batch), max_len), dtype = torch.long)\n\n    for i, b in enumerate(batch):\n        for k, v in b[0].items():\n            len_ = min(len(v), max_len)\n            output_dict[k][i][:len_] += torch.tensor(v[:len_])\n        y[i][:len_] = torch.tensor(b[1][:len_])\n\n    output_dict['labels'] = y\n    return output_dict","metadata":{"execution":{"iopub.status.busy":"2022-02-13T07:06:57.436833Z","iopub.execute_input":"2022-02-13T07:06:57.437150Z","iopub.status.idle":"2022-02-13T07:06:57.454234Z","shell.execute_reply.started":"2022-02-13T07:06:57.437082Z","shell.execute_reply":"2022-02-13T07:06:57.452915Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def split_data(df, validation_days=7):\n    validation_cut = df['t_dat'].max() - pd.Timedelta(validation_days)\n\n    df_train = df[df['t_dat'] < validation_cut]\n    df_val = df[df['t_dat'] >= validation_cut]\n    return df_train, df_val\n\ndef prepare_data(df):\n    # customer_id - articles, products_code, products_type_no ...\n    grouped = (\n        df\n        .sort_values(['t_dat'], ascending=True)\n        .groupby('customer_id')\n        .aggregate(lambda x: list(x))\n        .reset_index()\n    )\n    return grouped","metadata":{"execution":{"iopub.status.busy":"2022-02-13T07:06:57.456156Z","iopub.execute_input":"2022-02-13T07:06:57.456995Z","iopub.status.idle":"2022-02-13T07:06:57.468286Z","shell.execute_reply.started":"2022-02-13T07:06:57.456944Z","shell.execute_reply":"2022-02-13T07:06:57.467121Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_train, df_val = split_data(transactions)\ndf_train, df_val = prepare_data(df_train), prepare_data(df_val)\ndf_train = df_train[df_train['article_id'].apply(lambda x: len(x) > 10)] #train on the number of transactions > 10","metadata":{"execution":{"iopub.status.busy":"2022-02-13T07:06:57.470484Z","iopub.execute_input":"2022-02-13T07:06:57.471271Z","iopub.status.idle":"2022-02-13T07:08:50.437516Z","shell.execute_reply.started":"2022-02-13T07:06:57.471210Z","shell.execute_reply":"2022-02-13T07:08:50.436482Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"cfg = {\n    'batch_size': 64,\n    'optimizer': {\n        'name': 'optim.AdamW',\n        'params': {\n            'lr': 6e-3,\n        },\n    },\n    'scheduler': {\n        'name': 'optim.lr_scheduler.StepLR',\n        'params': {\n            'step_size': 1,\n            'gamma': 0.5\n        },\n    },\n}\n\ncfg = Box(cfg)","metadata":{"execution":{"iopub.status.busy":"2022-02-13T07:08:50.439527Z","iopub.execute_input":"2022-02-13T07:08:50.439902Z","iopub.status.idle":"2022-02-13T07:08:50.449280Z","shell.execute_reply.started":"2022-02-13T07:08:50.439847Z","shell.execute_reply":"2022-02-13T07:08:50.448271Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model = RnnModel()\nmodel = RnnLightningModel(cfg, model, df_train, df_val)","metadata":{"execution":{"iopub.status.busy":"2022-02-13T07:08:50.450947Z","iopub.execute_input":"2022-02-13T07:08:50.451380Z","iopub.status.idle":"2022-02-13T07:08:50.516170Z","shell.execute_reply.started":"2022-02-13T07:08:50.451321Z","shell.execute_reply":"2022-02-13T07:08:50.515140Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"checkpoint_callback = ModelCheckpoint(\n        dirpath = './',\n        filename = 'rnn_model',\n        monitor = 'train_loss',\n        mode = 'min'\n    )\n\ntrainer = pl.Trainer(\n    gpus = 1,\n    max_epochs=3,\n    callbacks = [checkpoint_callback]\n)\ntrainer.fit(model)","metadata":{"execution":{"iopub.status.busy":"2022-02-13T07:08:50.519168Z","iopub.execute_input":"2022-02-13T07:08:50.520273Z","iopub.status.idle":"2022-02-13T07:09:17.646951Z","shell.execute_reply.started":"2022-02-13T07:08:50.520215Z","shell.execute_reply":"2022-02-13T07:09:17.644657Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Check outputs\nLet's take one batch and see what model outputs.","metadata":{}},{"cell_type":"code","source":"model.cuda()\nmodel.eval()\nbatch = next(iter(model.train_dataloader()))\n\ntrue = batch['labels'][:, 4:].numpy().tolist()\ntrue = [[articles_map[art] for art in client] for client in true]\npred = model.generate(\n    product_codes = batch['product_codes'][:, :4].cuda(),\n    product_types_no = batch['product_types_no'][:, :4].cuda(),\n    sections_no = batch['sections_no'][:, :4].cuda(),\n    index_groups_no = batch['index_groups_no'][:, :4].cuda(),\n)","metadata":{"execution":{"iopub.status.busy":"2022-02-13T07:11:53.899007Z","iopub.execute_input":"2022-02-13T07:11:53.899345Z","iopub.status.idle":"2022-02-13T07:11:54.012521Z","shell.execute_reply.started":"2022-02-13T07:11:53.899313Z","shell.execute_reply":"2022-02-13T07:11:54.011435Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"for i, (truth, predictions) in enumerate(zip(true, pred)):\n    print('*'*130)\n    print(f'Customer {i}')\n    \n    truth = list(filter(lambda x: x != PAD_IDX, truth))\n    max_len = max(len(truth), len(predictions))\n    display_data = pd.DataFrame({'true articles': ['']*max_len, 'pred articles': ['']*max_len})\n    \n    truth_articles = articles.loc[articles.article_id.isin(truth), 'detail_desc'].values\n    predictions_articles = articles.loc[articles.article_id.isin(predictions), 'detail_desc'].values\n    \n    display_data.loc[range(len(truth_articles)),'true articles'] = truth_articles\n    display_data.loc[range(len(predictions_articles)),'pred articles'] = predictions_articles\n    display(HTML(display_data.to_html()))\n    print('\\n')","metadata":{"execution":{"iopub.status.busy":"2022-02-13T07:11:56.224516Z","iopub.execute_input":"2022-02-13T07:11:56.225192Z","iopub.status.idle":"2022-02-13T07:11:56.411206Z","shell.execute_reply.started":"2022-02-13T07:11:56.225155Z","shell.execute_reply":"2022-02-13T07:11:56.410145Z"},"collapsed":true,"jupyter":{"outputs_hidden":true},"trusted":true},"execution_count":null,"outputs":[]}]}