{"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":"import numpy as np\nimport pandas as pd\n\n\ndf = pd.read_csv(\"../input/h-and-m-personalized-fashion-recommendations/transactions_train.csv\", dtype={\"article_id\": str})\nprint(df.shape)\ndf.head()","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2022-07-30T14:09:54.621882Z","iopub.execute_input":"2022-07-30T14:09:54.622219Z","iopub.status.idle":"2022-07-30T14:10:58.234656Z","shell.execute_reply.started":"2022-07-30T14:09:54.622124Z","shell.execute_reply":"2022-07-30T14:10:58.233878Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df[\"t_dat\"] = pd.to_datetime(df[\"t_dat\"])\ndf[\"t_dat\"].max()","metadata":{"execution":{"iopub.status.busy":"2022-07-30T14:10:58.237048Z","iopub.execute_input":"2022-07-30T14:10:58.237259Z","iopub.status.idle":"2022-07-30T14:11:04.041064Z","shell.execute_reply.started":"2022-07-30T14:10:58.237233Z","shell.execute_reply":"2022-07-30T14:11:04.040311Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"active_articles = df.groupby(\"article_id\")[\"t_dat\"].max().reset_index()\nactive_articles = active_articles[active_articles[\"t_dat\"] >= \"2019-09-01\"].reset_index()\nactive_articles.shape","metadata":{"execution":{"iopub.status.busy":"2022-07-30T14:11:04.042343Z","iopub.execute_input":"2022-07-30T14:11:04.043674Z","iopub.status.idle":"2022-07-30T14:11:09.031213Z","shell.execute_reply.started":"2022-07-30T14:11:04.043627Z","shell.execute_reply":"2022-07-30T14:11:09.030437Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df = df[df[\"article_id\"].isin(active_articles[\"article_id\"])].reset_index(drop=True)\ndf.shape","metadata":{"execution":{"iopub.status.busy":"2022-07-30T14:11:09.033301Z","iopub.execute_input":"2022-07-30T14:11:09.033491Z","iopub.status.idle":"2022-07-30T14:11:16.435407Z","shell.execute_reply.started":"2022-07-30T14:11:09.033466Z","shell.execute_reply":"2022-07-30T14:11:16.434517Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df[\"week\"] = (df[\"t_dat\"].max() - df[\"t_dat\"]).dt.days // 7\ndf[\"week\"].value_counts()","metadata":{"execution":{"iopub.status.busy":"2022-07-30T14:11:16.436917Z","iopub.execute_input":"2022-07-30T14:11:16.437228Z","iopub.status.idle":"2022-07-30T14:11:17.813379Z","shell.execute_reply.started":"2022-07-30T14:11:16.437189Z","shell.execute_reply":"2022-07-30T14:11:17.812665Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from sklearn.preprocessing import LabelEncoder\n\n\narticle_ids = np.concatenate([[\"placeholder\"], np.unique(df[\"article_id\"].values)])\n\nle_article = LabelEncoder()\nle_article.fit(article_ids)\ndf[\"article_id\"] = le_article.transform(df[\"article_id\"])","metadata":{"execution":{"iopub.status.busy":"2022-07-30T14:11:17.814847Z","iopub.execute_input":"2022-07-30T14:11:17.815182Z","iopub.status.idle":"2022-07-30T14:12:31.329460Z","shell.execute_reply.started":"2022-07-30T14:11:17.815142Z","shell.execute_reply":"2022-07-30T14:12:31.328669Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"WEEK_HIST_MAX = 5\n\ndef create_dataset(df, week):\n    hist_df = df[(df[\"week\"] > week) & (df[\"week\"] <= week + WEEK_HIST_MAX)]\n    hist_df = hist_df.groupby(\"customer_id\").agg({\"article_id\": list, \"week\": list}).reset_index()\n    hist_df.rename(columns={\"week\": 'week_history'}, inplace=True)\n    \n    target_df = df[df[\"week\"] == week]\n    target_df = target_df.groupby(\"customer_id\").agg({\"article_id\": list}).reset_index()\n    target_df.rename(columns={\"article_id\": \"target\"}, inplace=True)\n    target_df[\"week\"] = week\n    \n    return target_df.merge(hist_df, on=\"customer_id\", how=\"left\")\n\nval_weeks = [0]\ntrain_weeks = [1, 2, 3, 4]\n\n\nval_df = pd.concat([create_dataset(df, w) for w in val_weeks]).reset_index(drop=True)\ntrain_df = pd.concat([create_dataset(df, w) for w in train_weeks]).reset_index(drop=True)\ntrain_df.shape, val_df.shape\ntrain_df.head()\nval_df.head()","metadata":{"execution":{"iopub.status.busy":"2022-07-30T14:12:31.330924Z","iopub.execute_input":"2022-07-30T14:12:31.331188Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from torch.utils.data import Dataset, DataLoader\nimport torch\nfrom tqdm import tqdm\n\nclass HMDataset(Dataset):\n    def __init__(self, df, seq_len, is_test=False):\n        self.df = df.reset_index(drop=True)\n        self.seq_len = seq_len\n        self.is_test = is_test\n    \n    def __len__(self):\n        return self.df.shape[0]\n    \n    def __getitem__(self, index):\n        row = self.df.iloc[index]\n        \n        if self.is_test:\n            target = torch.zeros(2).float()\n        else:\n            target = torch.zeros(len(article_ids)).float()\n            for t in row.target:\n                target[t] = 1.0\n            \n        article_hist = torch.zeros(self.seq_len).long()\n        week_hist = torch.ones(self.seq_len).float()\n        \n        \n        if isinstance(row.article_id, list):\n            if len(row.article_id) >= self.seq_len:\n                article_hist = torch.LongTensor(row.article_id[-self.seq_len:])\n                week_hist = (torch.LongTensor(row.week_history[-self.seq_len:]) - row.week)/WEEK_HIST_MAX/2\n            else:\n                article_hist[-len(row.article_id):] = torch.LongTensor(row.article_id)\n                week_hist[-len(row.article_id):] = (torch.LongTensor(row.week_history) - row.week)/WEEK_HIST_MAX/2\n        \n        return article_hist, week_hist, target\n    \nHMDataset(val_df,64)[1]","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def adjust_lr(optimizer, epoch):\n    if epoch < 1:\n        lr = 5e-5\n    elif epoch < 6:\n        lr = 1e-3\n    elif epoch < 9:\n        lr = 1e-4\n    else:\n        lr = 1e-5\n\n    for p in optimizer.param_groups:\n        p['lr'] = lr\n    return lr\n    \ndef get_optimizer(net):\n    optimizer = torch.optim.Adam(filter(lambda p: p.requires_grad, net.parameters()), lr=3e-4, betas=(0.9, 0.999),\n                                 eps=1e-08)\n    return optimizer","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import torch.nn as nn\nimport torch.nn.functional as F\n\nclass HMModel(nn.Module):\n    def __init__(self, article_shape):\n        super(HMModel, self).__init__()\n        \n        self.article_emb = nn.Embedding(article_shape[0], embedding_dim=article_shape[1])\n        \n        self.article_likelihood = nn.Parameter(torch.zeros(article_shape[0]), requires_grad=True)\n        self.top = nn.Sequential(nn.Conv1d(3, 8, kernel_size=1), nn.LeakyReLU(),\n                                 nn.Conv1d(8, 3, kernel_size=1), nn.LeakyReLU(),\n                                 nn.Conv1d(3, 1, kernel_size=1),nn.LeakyReLU(),)\n        \n    def forward(self, inputs):\n        article_hist, week_hist = inputs[0], inputs[1]\n        #print('output-1',article_hist.shape)  ###shape [256,16] #[batch_size,seq_len]\n        x = self.article_emb(article_hist)\n        x = F.normalize(x, dim=2)\n        #print('x',x,x.shape)\n        #print('output0',x,x.shape) ###shape [256,16,512] #[batch_size,seq_len,embedding_len]\n        \n        #x = x.mean(axis=1)\n        x = x@F.normalize(self.article_emb.weight).T\n        #print('output1',x,x.shape) ### [256, 16, 72582] #[batch_size,seq_len,all_articles]\n        \n        x, indices = x.max(axis=1)\n        #one purchased article compare with all articles. get purchased article index\n        #print('output2',x,x.shape) ### [256,72582]\n        \n        \n        x = x.clamp(1e-3, 0.999)\n        x = -torch.log(1/x - 1)\n        #print('output3',x,x.shape) ### [256,72582]\n        \n        max_week = week_hist.unsqueeze(2).repeat(1, 1, x.shape[-1]).gather(1, indices.unsqueeze(1).repeat(1, week_hist.shape[1], 1))\n        max_week = max_week.mean(axis=1).unsqueeze(1)\n        \n        x = torch.cat([x.unsqueeze(1), max_week,\n                       self.article_likelihood[None, None, :].repeat(x.shape[0], 1, 1)], axis=1)\n        \n        #print('x',x,x.shape)\n        x = self.top(x).squeeze(1)\n        #print('output4',x,x.shape)### [256,72582]\n        return x\n    \n#         [[-0.1248,  0.2905,  0.1383,  ...,  0.0856,  0.2905,  0.1722],\n#         [-0.1248, -0.0590,  0.0184,  ...,  0.0856, -0.0077, -0.0311],\n#         [-0.1248, -0.0867, -0.0757,  ..., -0.0688, -0.0009, -0.0469],\n#         ...,\n#         [-0.1248,  0.2905,  0.1383,  ...,  0.0856,  0.2905,  0.1722],\n#         [-0.1248,  0.2905,  0.1383,  ...,  0.0856,  0.2905,  0.1722],\n#         [-0.1248,  0.2905,  0.1383,  ...,  0.0856,  0.2905,  0.1722]]\n    \nmodel = HMModel((len(le_article.classes_), 512))\nmodel = model.cuda()\nprint(len(le_article.classes_))","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import sys\n\ndef calc_map(topk_preds, target_array, k=12):\n    metric = []\n    tp, fp = 0, 0\n    \n    for pred in topk_preds:\n        if target_array[pred]:\n            tp += 1\n            metric.append(tp/(tp + fp))\n        else:\n            fp += 1\n            \n    return np.sum(metric) / min(k, target_array.sum())\n\ndef read_data1(data):\n    return tuple(d.cuda() for d in data[:-1]), data[-1].cuda()\n\n\ndef validate(model, val_loader, k=12):\n    model.eval()\n    \n    tbar = tqdm(val_loader, file=sys.stdout)\n    \n    maps = []\n    \n    with torch.no_grad():\n        for idx, data in enumerate(tbar):\n            inputs, target = read_data1(data)\n            logits = model(inputs)\n            #print('logits',logits,logits.shape) ### [256, 72582]\n            \n            \n            _, indices = torch.topk(logits, k, dim=1)\n            #print('indices',indices,indices.shape) ### [256, 12]\n            \n#         indices =  [256, 12]\n#         [[21900, 22588, 22006,  ..., 16804, 57402, 11302],\n#         [21900, 22588, 22006,  ..., 16804, 57402, 11302],\n#         [21900, 22588, 22006,  ..., 16804, 57402, 11302],\n#         ...,\n#         [21900, 22588, 22006,  ..., 16804, 57402, 11302],\n#         [21900, 22588, 22006,  ..., 16804, 57402, 11302],\n#         [21900, 22588, 22006,  ..., 16804, 57402, 11302]]\n            \n            indices = indices.detach().cpu().numpy()\n            target = target.detach().cpu().numpy()  ### [256, 72582]\n#             target = \n#             [[0. 0. 0. ... 0. 0. 0.]\n#              [0. 0. 0. ... 0. 0. 0.]\n#              [0. 0. 0. ... 0. 0. 0.]\n#                        ...\n#              [0. 0. 0. ... 0. 0. 0.]\n#              [0. 0. 0. ... 0. 0. 0.]\n#              [0. 0. 0. ... 0. 0. 0.]]\n            for i in range(indices.shape[0]):\n                maps.append(calc_map(indices[i], target[i]))\n            #print('maps',maps,len(maps)) ### [256]\n        \n    \n    return np.mean(maps)\nSEQ_LEN = 16\n\nBS = 256\nNW = 8\n\nval_dataset = HMDataset(val_df, SEQ_LEN)\nval_loader = DataLoader(val_dataset, batch_size=BS, shuffle=False, num_workers=NW,\n                          pin_memory=False, drop_last=False)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Train and validate","metadata":{}},{"cell_type":"code","source":"def dice_loss(y_pred, y_true):\n    y_pred = y_pred.sigmoid()\n    intersect = (y_true*y_pred).sum(axis=1)\n    \n    return 1 - (intersect/(intersect + y_true.sum(axis=1) + y_pred.sum(axis=1))).mean()\n\n\ndef train(model, train_loader, val_loader, epochs):\n    np.random.seed(SEED)\n    \n    optimizer = get_optimizer(model)\n    scaler = torch.cuda.amp.GradScaler()\n\n    criterion = torch.nn.BCEWithLogitsLoss()\n    \n    for e in range(epochs):\n        model.train()\n        tbar = tqdm(train_loader, file=sys.stdout)\n        \n        lr = adjust_lr(optimizer, e)\n        \n        loss_list = []\n        index = 0\n        for idx, data in enumerate(tbar):\n            inputs, target = read_data1(data)\n            \n\n            optimizer.zero_grad()\n            \n            with torch.cuda.amp.autocast():\n                logits = model(inputs)\n                \n#                 logits =   ###[256, 72582]\n#         [[-0.0383, -0.0879, -0.0829,  ..., -0.0812, -0.0935, -0.0809],\n#         [-0.0383, -0.0736, -0.0736,  ..., -0.0736, -0.1058, -0.0736],\n#         [-0.0383, -0.0736, -0.0736,  ..., -0.0736, -0.1058, -0.0736],\n#         ...,\n#         [-0.0383, -0.0736, -0.0736,  ..., -0.0736, -0.1058, -0.0736],\n#         [-0.0383, -0.0759, -0.0752,  ..., -0.0768, -0.1058, -0.0773],\n#         [-0.0383, -0.0736, -0.0736,  ..., -0.0736, -0.1058, -0.0736]]\n        \n#                 target =   ###[256, 72582]\n#         [[0., 0., 0.,  ..., 0., 0., 0.],\n#         [0., 0., 0.,  ..., 0., 0., 0.],\n#         [0., 0., 0.,  ..., 0., 0., 0.],\n#         ...,\n#         [0., 0., 0.,  ..., 0., 0., 0.],\n#         [0., 0., 0.,  ..., 0., 0., 0.],\n#         [0., 0., 0.,  ..., 0., 0., 0.]]\n                \n                loss = criterion(logits, target) + dice_loss(logits, target)\n            #print('loss',loss) ### tensor(1.5361)\n            \n            #loss.backward()\n            scaler.scale(loss).backward() \n            #optimizer.step()\n            scaler.step(optimizer)\n            scaler.update()\n            \n            loss_list.append(loss.detach().cpu().item())\n            \n            ###loss_list [1.721466064453125, 1.718216896057129, .......]\n            \n            avg_loss = np.round(100*np.mean(loss_list), 4)\n            \n\n            tbar.set_description(f\"Epoch {e+1} Loss: {avg_loss} lr: {lr}\")\n                \n        \n        val_map = validate(model, val_loader)\n        \n\n        log_text = f\"Epoch {e+1}\\nTrain Loss: {avg_loss}\\nValidation MAP: {val_map}\\n\"\n            \n        print(log_text)\n        \n        #logfile = open(f\"models/{MODEL_NAME}_{SEED}.txt\", 'a')\n        #logfile.write(log_text)\n        #logfile.close()\n    return model\n\n\nMODEL_NAME = \"exp001\"\nSEED = 0\n\ntrain_dataset = HMDataset(train_df, SEQ_LEN)\ntrain_loader = DataLoader(train_dataset, batch_size=BS, shuffle=True, num_workers=NW,\n                          pin_memory=False, drop_last=True)\n\nmodel = train(model, train_loader, val_loader, epochs=10)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Train the retrieval model again with the most recent data and use this data for ranking**","metadata":{}},{"cell_type":"code","source":"train_df = train_df[train_df[\"week\"] < 2]\ntrain_dataset = HMDataset(train_df, SEQ_LEN)\ntrain_loader = DataLoader(train_dataset, batch_size=BS, num_workers=NW,\n                          pin_memory=False)\n\nmodel = train(model, train_loader, val_loader, epochs=10)","metadata":{"execution":{"iopub.status.busy":"2022-07-30T04:34:17.215924Z","iopub.execute_input":"2022-07-30T04:34:17.216168Z","iopub.status.idle":"2022-07-30T04:34:17.315471Z","shell.execute_reply.started":"2022-07-30T04:34:17.216127Z","shell.execute_reply":"2022-07-30T04:34:17.314705Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_df.head()\nprint(train_df.shape)","metadata":{"execution":{"iopub.status.busy":"2022-07-30T04:34:17.317114Z","iopub.execute_input":"2022-07-30T04:34:17.317578Z","iopub.status.idle":"2022-07-30T04:34:17.322675Z","shell.execute_reply.started":"2022-07-30T04:34:17.317541Z","shell.execute_reply":"2022-07-30T04:34:17.321907Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**To train ranking model, first get candidates for that**","metadata":{}},{"cell_type":"code","source":"def generate_candidates(model, loader, k=500):#Choose 500 candidates from retrievel\n    model.train()\n\n    tbar = tqdm(loader, file=sys.stdout)\n\n    candidates = []\n\n    with torch.no_grad():\n        for idx, data in enumerate(tbar):\n            inputs, target = read_data1(data)\n\n            logits = model(inputs)\n\n            _, indices = torch.topk(logits, k, dim=1)\n\n            indices = indices.detach().cpu().numpy()\n            target = target.detach().cpu().numpy()\n\n            for i in range(indices.shape[0]):\n                candidates.append(list(indices[i]))\n            \n\n\n    return candidates\ncandidates = generate_candidates(model,train_loader)","metadata":{"execution":{"iopub.status.busy":"2022-07-30T04:34:17.324177Z","iopub.execute_input":"2022-07-30T04:34:17.324642Z","iopub.status.idle":"2022-07-30T04:35:31.317578Z","shell.execute_reply.started":"2022-07-30T04:34:17.324605Z","shell.execute_reply":"2022-07-30T04:35:31.316756Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_df['candidates'] = candidates\ntrain_df.head()","metadata":{"execution":{"iopub.status.busy":"2022-07-30T05:03:36.339127Z","iopub.execute_input":"2022-07-30T05:03:36.339804Z","iopub.status.idle":"2022-07-30T05:03:36.395769Z","shell.execute_reply.started":"2022-07-30T05:03:36.339765Z","shell.execute_reply":"2022-07-30T05:03:36.394973Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Transform into standard data to feed the ranking model**","metadata":{}},{"cell_type":"code","source":"class HMRankDataset(Dataset):\n    def __init__(self, df, seq_len, is_test=False):\n        self.df = df.reset_index(drop=True)\n        self.seq_len = seq_len\n        self.is_test = is_test\n    \n    def __len__(self):\n        return self.df.shape[0]\n    \n    def __getitem__(self, index):\n        \n        row = self.df.iloc[index]\n    \n    \n        article_hist = torch.zeros(self.seq_len).long()\n        if self.is_test:\n            target = torch.zeros(2).float()\n            target_candidates = torch.zeros(2).float()\n        else:\n            target_candidates = torch.zeros(len(row.candidates)).float()\n            for t in row.target:\n                if t in row.candidates:\n                    target_candidates[row.candidates.index(t)] = 1.0\n        if isinstance(row.article_id, list):\n            if len(row.article_id) >= self.seq_len:\n                article_hist = torch.LongTensor(row.article_id[-self.seq_len:])\n            else:\n                article_hist[-len(row.article_id):] = torch.LongTensor(row.article_id)\n            \n        return article_hist, torch.LongTensor(row.candidates), target_candidates, torch.LongTensor(row.candidates)\n\n    \nprint(HMRankDataset(train_df,8)[1])","metadata":{"execution":{"iopub.status.busy":"2022-07-30T05:04:38.107329Z","iopub.execute_input":"2022-07-30T05:04:38.107588Z","iopub.status.idle":"2022-07-30T05:04:38.166489Z","shell.execute_reply.started":"2022-07-30T05:04:38.107559Z","shell.execute_reply":"2022-07-30T05:04:38.165748Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Train to ranking**","metadata":{}},{"cell_type":"code","source":"class HMRankModel(nn.Module):\n    def __init__(self, article_shape):\n        super(HMRankModel, self).__init__()\n        \n        self.article_emb = nn.Embedding(article_shape[0], embedding_dim=article_shape[1])\n        \n        self.top = nn.Sequential(nn.Conv1d(1, 8, kernel_size=1), nn.LeakyReLU(),\n                                 nn.Conv1d(8, 3, kernel_size=1), nn.LeakyReLU(),\n                                 nn.Conv1d(3, 1, kernel_size=1), nn.LeakyReLU())\n        \n    def forward(self, inputs):\n        article_hist, candidates = inputs[0], inputs[1]\n#         print('article_his',article_hist.shape)###[256,8]\n#         print('candidates',candidates.shape)###[256,500]\n\n        x = self.article_emb(article_hist)###[256,500,512]\n        y = self.article_emb(candidates) ###shape[256,500,512] # [batch_size,candidates_len,embedding_len]\n        customer_emb = x.mean(axis=1) ###shape[256, 512] # [batch_size,embedding_len]\n        \n        score = torch.multiply(y, customer_emb.reshape(y.shape[0],1,512)).sum(2,keepdims=False)###[256,500]\n    \n        score = F.normalize(score, dim=1)#decrease the range.\n        score = score.clamp(1e-4, 0.9999)#eliminate negative number.\n        score = -torch.log(1/score - 1)#logits\n        \n        #print(score) ###[256,500]\n#         [[-2.9936,  0.3114, -9.2102,  ..., -3.6603, -4.0293, -4.5075],\n#         [ 0.3496, -3.6411, -9.2102,  ..., -9.2102, -9.2102, -9.2102],\n#         [ 0.3496, -3.6411, -9.2102,  ..., -9.2102, -9.2102, -9.2102],\n#         ...,\n#         [ 0.3496, -3.6411, -9.2102,  ..., -9.2102, -9.2102, -9.2102],\n#         [ 0.3496, -3.6411, -9.2102,  ..., -9.2102, -9.2102, -9.2102],\n#         [-2.9248, -2.7940, -3.2359,  ..., -3.7172, -9.2102, -9.2102]]\n\n        score = torch.unsqueeze(score, 1)#add one channel to feed network\n        score = self.top(score)\n        score = torch.squeeze(score,1)\n        \n        #print(score) ###[256,1000]\n#         [[0.3872, 0.3806, 0.3806,  ..., 0.9624, 0.7007, 0.9624],\n#         [0.4612, 0.4805, 0.2556,  ..., 0.5366, 0.9624, 0.5576],\n#         [0.4617, 0.2727, 0.3862,  ..., 0.5752, 0.9624, 0.9624],\n#         ...,\n#         [0.4971, 0.2749, 0.7905,  ..., 0.6665, 0.7100, 0.7651],\n#         [0.4519, 0.4731, 0.4785,  ..., 0.9624, 0.9624, 0.9624],\n#         [0.2605, 0.4988, 0.9624,  ..., 0.9624, 0.9624, 0.6982]]\n\n        return score\n    \nRankmodel = HMRankModel((len(le_article.classes_), 512))\nRankmodel = Rankmodel.cuda()","metadata":{"execution":{"iopub.status.busy":"2022-07-30T05:10:53.716554Z","iopub.execute_input":"2022-07-30T05:10:53.716845Z","iopub.status.idle":"2022-07-30T05:10:53.922359Z","shell.execute_reply.started":"2022-07-30T05:10:53.716816Z","shell.execute_reply":"2022-07-30T05:10:53.921542Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def adjust_lr(optimizer, epoch):\n    if epoch < 1:\n        lr = 5e-5\n    elif epoch < 6:\n        lr = 1e-3\n    elif epoch < 9:\n        lr = 1e-4\n    else:\n        lr = 1e-5\n\n    for p in optimizer.param_groups:\n        p['lr'] = lr\n    return lr\n    \ndef get_optimizer(net):\n    optimizer = torch.optim.Adam(filter(lambda p: p.requires_grad, net.parameters()), lr=3e-4, betas=(0.9, 0.999),\n                                 eps=1e-08)\n    return optimizer","metadata":{"execution":{"iopub.status.busy":"2022-07-30T05:10:56.694871Z","iopub.execute_input":"2022-07-30T05:10:56.695144Z","iopub.status.idle":"2022-07-30T05:10:56.702819Z","shell.execute_reply.started":"2022-07-30T05:10:56.695113Z","shell.execute_reply":"2022-07-30T05:10:56.701864Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def read_data2(data):\n    return tuple(d.cuda() for d in data[:-2]),data[-2].cuda(), data[-1].cuda()\n\n\ndef Ranktrain(model, train_loader, epochs):\n    np.random.seed(SEED)\n    \n    optimizer = get_optimizer(model)\n    scaler = torch.cuda.amp.GradScaler()\n\n    criterion = torch.nn.BCEWithLogitsLoss()\n    \n    for e in range(epochs):\n        model.train()\n        tbar = tqdm(train_loader, file=sys.stdout)\n        \n        lr = adjust_lr(optimizer, e)\n        \n        loss_list = []\n        index = 0\n        for idx, data in enumerate(tbar):\n            inputs, target, candidates = read_data2(data)\n            \n            #print(target) ###[256,500]\n            optimizer.zero_grad()\n            \n            with torch.cuda.amp.autocast():\n                logits = model(inputs)\n                \n                loss = criterion(logits, target)\n            \n            #loss.backward()\n            scaler.scale(loss).backward()\n            #optimizer.step()\n            scaler.step(optimizer)\n            scaler.update()\n            \n            loss_list.append(loss.detach().cpu().item())\n            \n            ###loss_list [1.721466064453125, 1.718216896057129, .......]\n            \n            avg_loss = np.round(100*np.mean(loss_list), 4)\n            \n\n            tbar.set_description(f\"Epoch {e+1} Loss: {avg_loss} lr: {lr}\")\n        \n\n        log_text = f\"Epoch {e+1}\\nTrain Loss: {avg_loss}\\n\"\n            \n        print(log_text)\n        \n        #logfile = open(f\"models/{MODEL_NAME}_{SEED}.txt\", 'a')\n        #logfile.write(log_text)\n        #logfile.close()\n    return model\n\n\nMODEL_NAME = \"exp001\"\nSEED = 0\n\ntrain_dataset = HMRankDataset(train_df, SEQ_LEN)\ntrain_loader = DataLoader(train_dataset, batch_size=BS, shuffle=True, num_workers=NW,\n                          pin_memory=False)\n\nRankmodel = Ranktrain(Rankmodel, train_loader, epochs=10)","metadata":{"execution":{"iopub.status.busy":"2022-07-30T05:10:58.914729Z","iopub.execute_input":"2022-07-30T05:10:58.915268Z","iopub.status.idle":"2022-07-30T05:11:50.448019Z","shell.execute_reply.started":"2022-07-30T05:10:58.915231Z","shell.execute_reply":"2022-07-30T05:11:50.447112Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **Prediction**","metadata":{}},{"cell_type":"code","source":"test_df = pd.read_csv('../input/h-and-m-personalized-fashion-recommendations/sample_submission.csv').drop(\"prediction\", axis=1)\nprint(test_df.shape)\ntest_df.head()","metadata":{"execution":{"iopub.status.busy":"2022-07-30T05:28:41.176260Z","iopub.execute_input":"2022-07-30T05:28:41.176539Z","iopub.status.idle":"2022-07-30T05:28:46.193063Z","shell.execute_reply.started":"2022-07-30T05:28:41.176509Z","shell.execute_reply":"2022-07-30T05:28:46.192310Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def create_test_dataset(test_df):\n\n    week = -1\n    test_df[\"week\"] = week\n    \n    hist_df = df[(df[\"week\"] > week) & (df[\"week\"] <= week + WEEK_HIST_MAX)]\n    hist_df = hist_df.groupby(\"customer_id\").agg({\"article_id\": list, \"week\": list}).reset_index()\n    hist_df.rename(columns={\"week\": 'week_history'}, inplace=True)\n    \n    \n    return test_df.merge(hist_df, on=\"customer_id\", how=\"left\")\n\ntest_df = create_test_dataset(test_df)\ntest_ds = HMDataset(test_df, SEQ_LEN,is_test=True)\ntest_loader = DataLoader(test_ds, batch_size=BS, shuffle=False, num_workers=NW,\n                          pin_memory=False)","metadata":{"execution":{"iopub.status.busy":"2022-07-30T05:12:35.717521Z","iopub.execute_input":"2022-07-30T05:12:35.717887Z","iopub.status.idle":"2022-07-30T05:12:44.335213Z","shell.execute_reply.started":"2022-07-30T05:12:35.717844Z","shell.execute_reply":"2022-07-30T05:12:44.333410Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_ds[0]","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**retrievel and ranking model**","metadata":{}},{"cell_type":"code","source":"preds = []\ndef predict(model,Rankmodel, loader, k=500):\n    model.eval()\n\n    tbar = tqdm(loader, file=sys.stdout)\n\n    with torch.no_grad():\n        for idx, data in enumerate(tbar):\n            inputs, target = read_data1(data)\n            logits = model(inputs)\n        \n            _, indices = torch.topk(logits, k, dim=1)\n            indices = indices.detach().cpu().numpy()  \n\n            part = test_df.iloc[idx:idx+len(indices)].copy(deep=True)\n            part['candidates'] = indices.tolist()\n            rank_ds = HMRankDataset(part, 8,is_test=True)\n            \n            rank_loader = DataLoader(rank_ds, batch_size=256, num_workers=NW,pin_memory=False)\n            \n            def inference(model, loader, k=12):\n                model.eval()\n\n                tbar = tqdm(loader, file=sys.stdout)\n\n\n                with torch.no_grad():\n                    for idx, data in enumerate(tbar):\n                        tmp = []\n                        inputs, target,candidates = read_data2(data)\n\n                        logits = model(inputs)\n\n                        _, indices = torch.topk(logits, k, dim=1)\n\n                        indices = indices.detach().cpu().numpy()\n                        candidates = candidates.detach().cpu().numpy()\n\n                        for i in range(len(candidates)):\n                            tmp = []\n                            for j in range(12):\n                                tmp += [candidates[i][indices[i][j]]]\n                            preds.append(\" \".join(le_article.inverse_transform(tmp[:])))\n                        \n            inference(Rankmodel,rank_loader)\n\n        ","metadata":{"execution":{"iopub.status.busy":"2022-07-30T05:13:03.822620Z","iopub.execute_input":"2022-07-30T05:13:03.822896Z","iopub.status.idle":"2022-07-30T05:13:03.837179Z","shell.execute_reply.started":"2022-07-30T05:13:03.822866Z","shell.execute_reply":"2022-07-30T05:13:03.834367Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"predict(model,Rankmodel,test_loader, k=500)","metadata":{"execution":{"iopub.status.busy":"2022-07-30T05:13:09.042483Z","iopub.execute_input":"2022-07-30T05:13:09.042875Z","iopub.status.idle":"2022-07-30T05:21:57.213916Z","shell.execute_reply.started":"2022-07-30T05:13:09.042826Z","shell.execute_reply":"2022-07-30T05:21:57.212617Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"preds[20]","metadata":{"execution":{"iopub.status.busy":"2022-07-30T05:22:26.321955Z","iopub.execute_input":"2022-07-30T05:22:26.322479Z","iopub.status.idle":"2022-07-30T05:22:26.328435Z","shell.execute_reply.started":"2022-07-30T05:22:26.322438Z","shell.execute_reply":"2022-07-30T05:22:26.327750Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_df[\"prediction\"] = preds","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_df.to_csv(\"submission.csv\", index=False, columns=[\"customer_id\", \"prediction\"])","metadata":{},"execution_count":null,"outputs":[]}]}