{"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":"Hi there! It's my first time to share notebook here. Though this notebook doesn't do well in this competition, I want to share it as a reference here(Maybe it may do some help for someone uh?) Upvote if you think it's useful :)✌","metadata":{}},{"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport torch\nimport torch.nn as nn\nimport tqdm","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"customer_df = pd.read_csv('./data/customers.csv')\nsub_df = pd.read_csv('./data/sample_submission.csv')\ntrans_df = pd.read_csv('./data/transactions_train.csv', usecols=['article_id', 'customer_id', 't_dat'])\narticle_df = pd.read_csv('./data/articles.csv', )\ntrans_df['t_dat'] = pd.to_datetime(trans_df['t_dat'])","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"article_id_dict = dict(zip(article_df.article_id.values, article_df.index))\nid_article_dict = dict(zip(article_df.index, article_df.article_id.values))\n\ncustomer_id_dict = dict(zip(customer_df.customer_id.values, customer_df.index))\nid_customer_dict = dict(zip(customer_df.index, customer_df.customer_id.values))","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"trans_df1 = trans_df[['article_id', 'customer_id']].copy()\ntrans_df1['val'] = 1","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Negative sampling","metadata":{}},{"cell_type":"markdown","source":"Negative sample with popularity theory that popular items are recommended while user does not choose","metadata":{}},{"cell_type":"code","source":"def get_general_item(df:pd.DataFrame, N:int):\n    \"\"\"\n    To get popular items\n    \"\"\"\n    last_ts = df.t_dat.max()\n    df['day'] = df.t_dat.dt.dayofweek\n    df['new_t_dat'] = df.t_dat - pd.TimedeltaIndex(df.day - 1, 'D')\n    df.loc[df.day >= 2, 'new_t_dat'] = df.loc[df.day >= 2, 'new_t_dat'] + pd.TimedeltaIndex(np.ones(len(df.loc[df.day >= 2, 'new_t_dat'])) * 7, 'D')\n    \n    tmp = df.groupby(['article_id', 'new_t_dat'])['day'].count().reset_index().rename(columns={'day': 'cnt'})\n    tmp1 = tmp[tmp.new_t_dat == last_ts][['article_id', 'cnt']]\n    df = pd.merge(df, tmp, on=['article_id', 'new_t_dat'], how='left')\n    df = pd.merge(df, tmp1, on=['article_id'], how='left', suffixes=('', '_target'))\n\n    df['cnt_target'].fillna(0, inplace=True)\n    df['quotient'] = df['cnt_target'] / df['cnt']\n    \n    general_pred = df.groupby(['article_id'])['quotient'].sum().nlargest(N).index.tolist()\n    return general_pred","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"general_items2000 = get_general_item(trans_df.copy(), 2000)\ngeneral_items200 = get_general_item(trans_df.copy(), 200)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"tmp = trans_df1.groupby(['customer_id']).agg({'article_id':list}).reset_index()","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Negative sample with popularity theory that popular items are recommended while user does not choose\ndef negative_sample(df, N=5):\n    add_items = []\n    add_users = []\n    temp = pd.DataFrame(columns=['article_id', 'customer_id'])\n    for i in tqdm.tqdm(range(len(tmp))):\n        j = 0\n        items = df.loc[i, 'article_id']\n        cur_user = df.loc[i, 'customer_id']\n        for general_item in general_items200:\n            if j == N:\n                break\n            if general_item not in items:\n                add_items.append(general_item)\n                add_users.append(cur_user)\n                j += 1\n        if j < N:\n            for general_item in general_items2000:\n                if j == N:\n                    break\n            if general_item not in items:\n                add_items.append(general_item)\n                add_users.append(cur_user)\n                j += 1\n    temp['article_id'] = add_items\n    temp['customer_id'] = add_users\n    temp['val'] = 0\n\n    return temp","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"trans_df1 = trans_df1.append(negative_sample(tmp, 5))","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Funk-SVD","metadata":{}},{"cell_type":"code","source":"class FunkSVD(nn.Module):\n    def __init__(self, user_len, item_len, emb_len):\n        super().__init__()\n        self.user_len = user_len\n        self.item_len = item_len\n        self.emb_len = emb_len\n        self.item_embeddings = nn.Embedding(self.item_len+1, self.emb_len)\n        self.user_embeddings = nn.Embedding(self.user_len, self.emb_len)\n    \n    def forward(self, x):\n        user_emb = self.user_embeddings(x[:, 1])\n        item_emb = self.item_embeddings(x[:, 0])\n        return torch.sum(user_emb * item_emb, dim=1, keepdim=True)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Begin training","metadata":{}},{"cell_type":"code","source":"from torch.utils.data import TensorDataset, DataLoader\ndevice = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n\n\ntrans_df1['article_id'] = trans_df1['article_id'].map(article_id_dict)\ntrans_df1['customer_id'] = trans_df1['customer_id'].map(customer_id_dict)\n\n\nX = torch.tensor(trans_df1[['article_id', 'customer_id']].values, dtype=torch.long).to(device)\ny = torch.tensor(trans_df1['val'].values, dtype=torch.long).to(device)\n\ndataset = TensorDataset(X, y)\ndataloader = DataLoader(dataset, 256, shuffle=True)\n\nloss = nn.MSELoss()\nmodel = FunkSVD(len(customer_df), len(article_df), 10).to(device)\noptimizer = torch.optim.Adam(model.parameters(), lr=0.001)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def train(model, loss, optimizer, dataloader, epoch):\n    model.train()\n    for i in range(epoch):\n        for X, y in tqdm.tqdm(dataloader):\n            y_hat = model(X)\n            l = loss(y_hat.squeeze(1), y.float())\n\n            model.zero_grad()\n            l.backward()\n            optimizer.step()","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train(model, loss, optimizer, dataloader, 10)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Compute final results","metadata":{}},{"cell_type":"code","source":"user_embeddings = model.user_embeddings.weight.to(torch.device('cpu')).detach().numpy()\nitem_embeddings = model.item_embeddings.weight.to(torch.device('cpu')).detach().numpy()","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"for i in tqdm.trange(len(sub_df)):\n    cur_customer = customer_id_dict[sub_df.loc[i, 'customer_id']]\n    score = item_embeddings@(user_embeddings[cur_customer])\n    res = ' '.join(['0' + str(id_article_dict[x]) for x in score.argsort()[::-1][:12].tolist()])\n    sub_df.loc[i, 'prediction'] = res","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sub_df.to_csv('my_sub.csv', index=False)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"reference notebook: [https://www.kaggle.com/code/hervind/h-m-faster-trending-products-weekly](https://www.kaggle.com/code/hervind/h-m-faster-trending-products-weekly)","metadata":{}}]}