{"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":"correct = False\nwhile not correct:\n    %pip install -U implicit\n    import pandas as pd\n    import numpy as np\n    from scipy.sparse import coo_matrix\n    from implicit.als import AlternatingLeastSquares\n    from implicit.evaluation import mean_average_precision_at_k\n    import matplotlib.image as mpimg\n    import matplotlib.pyplot as plt\n    correct = True","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2022-10-30T12:26:38.893258Z","iopub.execute_input":"2022-10-30T12:26:38.893583Z","iopub.status.idle":"2022-10-30T12:26:55.811508Z","shell.execute_reply.started":"2022-10-30T12:26:38.893505Z","shell.execute_reply":"2022-10-30T12:26:55.805842Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"PATH = \"../input/h-and-m-personalized-fashion-recommendations/\"\narticles = pd.read_csv(f\"{PATH}articles.csv\", encoding=\"utf-8\", dtype={'article_id': str})\ncustomers = pd.read_csv(f\"{PATH}customers.csv\", encoding=\"utf-8\")\ntx = pd.read_csv(f\"{PATH}transactions_train.csv\", encoding=\"utf-8\", dtype={'article_id': str}, parse_dates=['t_dat'])","metadata":{"execution":{"iopub.status.busy":"2022-10-30T12:26:55.813824Z","iopub.execute_input":"2022-10-30T12:26:55.814212Z","iopub.status.idle":"2022-10-30T12:28:17.957582Z","shell.execute_reply.started":"2022-10-30T12:26:55.814167Z","shell.execute_reply":"2022-10-30T12:28:17.956576Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"USERS = customers.customer_id.unique().tolist()\nITEMS = articles.article_id.unique().tolist()\n\nUSER_IDS = dict(list(enumerate(USERS)))\nITEM_IDS = dict(list(enumerate(ITEMS)))\n\nUSER_MAP = {u: uidx for uidx, u in USER_IDS.items()}\nITEM_MAP = {i: iidx for iidx, i in ITEM_IDS.items()}\n\ntx['user_id'] = tx['customer_id'].map(USER_MAP)\ntx['item_id'] = tx['article_id'].map(ITEM_MAP)","metadata":{"execution":{"iopub.status.busy":"2022-10-30T12:28:17.959189Z","iopub.execute_input":"2022-10-30T12:28:17.959576Z","iopub.status.idle":"2022-10-30T12:28:30.295990Z","shell.execute_reply.started":"2022-10-30T12:28:17.959540Z","shell.execute_reply":"2022-10-30T12:28:30.295015Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def pd_to_coo_matrix(df):\n    row = df['user_id'].values\n    col = df['item_id'].values\n    data = np.ones(df.shape[0])\n    coo = coo_matrix((data, (row, col)), shape=(len(USERS), len(ITEMS)))\n    return coo\n\ndef train_val_split(df, days=7):\n    validation_cut = df['t_dat'].max() - pd.Timedelta(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 matrix_maker(df, days=7):\n    df_train, df_val = train_val_split(df, days=days)\n    coo_train = pd_to_coo_matrix(df_train)\n    coo_val = pd_to_coo_matrix(df_val)\n\n    csr_train = coo_train.tocsr()\n    csr_val = coo_val.tocsr()\n    \n    return {'coo_train': coo_train,\n            'csr_train': csr_train,\n            'csr_val': csr_val}\n\ndef validate(matrices, factors=200, iterations=20, regularization=0.01, show_progress=True):\n    coo_train = matrices['coo_train']\n    csr_train = matrices['csr_train']\n    csr_val = matrices['csr_val']\n    \n    model = AlternatingLeastSquares(\n        factors=factors,\n        iterations=iterations, \n        regularization=regularization, \n        use_gpu=True,\n        random_state=12345)\n        \n    model.fit(coo_train, show_progress=show_progress)\n    \n    map12 = mean_average_precision_at_k(model, csr_train, csr_val, K=12, show_progress=show_progress)\n    print(f\"Factors: {factors:>3} - Iterations: {iterations:>2} - Regularization: {regularization:4.3f} ==> MAP@12: {map12:6.5f}\")\n    return map12","metadata":{"execution":{"iopub.status.busy":"2022-10-30T12:28:30.298746Z","iopub.execute_input":"2022-10-30T12:28:30.299152Z","iopub.status.idle":"2022-10-30T12:28:30.309882Z","shell.execute_reply.started":"2022-10-30T12:28:30.299115Z","shell.execute_reply":"2022-10-30T12:28:30.308839Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"matrices = matrix_maker(tx)","metadata":{"execution":{"iopub.status.busy":"2022-10-30T12:28:30.311223Z","iopub.execute_input":"2022-10-30T12:28:30.311577Z","iopub.status.idle":"2022-10-30T12:28:36.924160Z","shell.execute_reply.started":"2022-10-30T12:28:30.311543Z","shell.execute_reply":"2022-10-30T12:28:36.923190Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"\"\"\"\nBest parameters are:\n\n    factors: 60\n    iterations: 12\n    regularitazion: 0.01\n\nThe time needed in order to find the best parameters is:\n\n    CPU times: user 42min 22s, \n    sys: 11.4 s, total: 42min 34s\n    Wall time: 42min 43s\n\nSince ALS allows a random state parameter, this cell will be skipped\n\"\"\"\n\nbest_params = {'factors': 60, 'iterations': 12, 'regularization': 0.01}\n\nif not best_params:\n    %%time\n    best_map12 = 0\n    for factors in [40, 50, 60, 100, 200, 500, 1000]:\n        for iterations in [3, 12, 14, 15, 20]:\n            for regularization in [0.01]:\n                map12 = validate(matrices, factors, iterations, regularization, show_progress=False)\n                if map12 > best_map12:\n                    best_map12 = map12\n                    best_params = {'factors': factors, 'iterations': iterations, 'regularization': regularization}\n                    print(f\"Best MAP@12 found. Updating: {best_params}\")","metadata":{"execution":{"iopub.status.busy":"2022-10-30T12:28:36.925475Z","iopub.execute_input":"2022-10-30T12:28:36.925820Z","iopub.status.idle":"2022-10-30T12:28:36.935973Z","shell.execute_reply.started":"2022-10-30T12:28:36.925788Z","shell.execute_reply":"2022-10-30T12:28:36.934240Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"coo_train = pd_to_coo_matrix(tx)\ncsr_train = coo_train.tocsr()","metadata":{"execution":{"iopub.status.busy":"2022-10-30T12:28:36.937403Z","iopub.execute_input":"2022-10-30T12:28:36.937820Z","iopub.status.idle":"2022-10-30T12:28:39.415866Z","shell.execute_reply.started":"2022-10-30T12:28:36.937786Z","shell.execute_reply":"2022-10-30T12:28:39.414904Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def train(coo_train, factors, iterations, regularization, show_progress=True):\n    model = AlternatingLeastSquares(\n        factors=factors, \n        iterations=iterations, \n        regularization=regularization,\n        use_gpu=True,\n        random_state=12345)\n        \n    model.fit(coo_train, show_progress=show_progress)\n    return model","metadata":{"execution":{"iopub.status.busy":"2022-10-30T12:28:39.417101Z","iopub.execute_input":"2022-10-30T12:28:39.417661Z","iopub.status.idle":"2022-10-30T12:28:39.430197Z","shell.execute_reply.started":"2022-10-30T12:28:39.417627Z","shell.execute_reply":"2022-10-30T12:28:39.429218Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model = train(coo_train, **best_params)","metadata":{"execution":{"iopub.status.busy":"2022-10-30T12:28:39.434093Z","iopub.execute_input":"2022-10-30T12:28:39.435708Z","iopub.status.idle":"2022-10-30T12:28:49.334935Z","shell.execute_reply.started":"2022-10-30T12:28:39.435668Z","shell.execute_reply":"2022-10-30T12:28:49.333916Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def submit(model, csr_train, submission_name=\"submissions.csv\"):\n    preds = []\n    batch_size = 2000\n    to_generate = np.arange(len(USERS)/10)\n    for startidx in range(0, len(to_generate), batch_size):\n        batch = to_generate[startidx : startidx + batch_size]\n        ids, scores = model.recommend(batch, csr_train[batch], N=12, filter_already_liked_items=True)\n        for i, userid in enumerate(batch):\n            customer_id = USER_IDS[userid]\n            user_items = ids[i]\n            article_ids = [ITEM_IDS[item_id] for item_id in user_items]\n            preds.append((customer_id, ' '.join(article_ids), scores[i]))\n\n    df_preds = pd.DataFrame(preds, columns=['customer_id', 'prediction', 'scores'])\n    df_preds.to_csv(submission_name, index=False)\n    \n    display(df_preds.head())\n    print(df_preds.shape)\n    \n    return df_preds","metadata":{"execution":{"iopub.status.busy":"2022-10-30T12:28:49.338208Z","iopub.execute_input":"2022-10-30T12:28:49.338865Z","iopub.status.idle":"2022-10-30T12:28:49.348136Z","shell.execute_reply.started":"2022-10-30T12:28:49.338825Z","shell.execute_reply":"2022-10-30T12:28:49.346869Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"%%time\ndf_preds = submit(model, csr_train)","metadata":{"execution":{"iopub.status.busy":"2022-10-30T12:28:49.349544Z","iopub.execute_input":"2022-10-30T12:28:49.350261Z","iopub.status.idle":"2022-10-30T12:29:15.963181Z","shell.execute_reply.started":"2022-10-30T12:28:49.350225Z","shell.execute_reply":"2022-10-30T12:29:15.961853Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"\"\"\"\nLet's check what our recommendations are for first customer.\n\nFirst we will take the customer_id and then acquire what he has bought in the past\n\"\"\"\ndef customer_bought_suggested(ID: int):\n    customer_ID = df_preds.customer_id[ID]\n    items_bought = tx.loc[tx['customer_id'] == customer_ID].article_id.unique().tolist()\n    items_suggested = df_preds.prediction[ID].split()\n    scores = df_preds.scores[ID]\n    return customer_ID, items_bought, items_suggested, scores\n\ndef bought_suggest(ID, rows=2, columns=12, figsize=(20,10)):\n    customer_ID, items_bought, items_suggested, scores = customer_bought_suggested(ID)\n\n    f, ax = plt.subplots(rows, columns, figsize=figsize)\n    for i in range(rows):\n        index = 0\n        for j in range(columns):\n            if i==0:\n                try:\n                    img = mpimg.imread(f'{PATH}images/{str(items_bought[index])[:3]}/0{int(items_bought[index])}.jpg')\n                    ax[i,j].imshow(img)\n                    ax[i,j].set_xticks([], [])\n                    ax[i,j].set_yticks([], [])\n                    ax[i,j].grid(False)\n                    ax[i,j].set_title(\"Bought\")\n                    index += 1\n                except IndexError:\n                    continue\n            else:\n                try:\n                    img = mpimg.imread(f'{PATH}images/{str(items_suggested[index])[:3]}/0{int(items_suggested[index])}.jpg')\n                    ax[i,j].imshow(img)\n                    ax[i,j].set_xticks([], [])\n                    ax[i,j].set_yticks([], [])\n                    ax[i,j].grid(False)\n                    ax[i,j].set_title(\"Suggested\")\n                    ax[i,j].set(xlabel=f'Score: {scores[index]:.4f}')\n                    index += 1\n                except IndexError:\n                    continue\n                        \n    #plt.suptitle(\"Item bought in the past\")\n    plt.tight_layout()\n    plt.show()","metadata":{"execution":{"iopub.status.busy":"2022-10-30T12:29:15.964875Z","iopub.execute_input":"2022-10-30T12:29:15.965572Z","iopub.status.idle":"2022-10-30T12:29:15.978449Z","shell.execute_reply.started":"2022-10-30T12:29:15.965533Z","shell.execute_reply":"2022-10-30T12:29:15.977465Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"\"\"\"\nStarting from the products purchased in the past, the model suggests 10 items sorted by decreasing score (where the first is the most recommended)\n\"\"\"\nbought_suggest(ID=1024)","metadata":{"execution":{"iopub.status.busy":"2022-10-30T12:34:39.177256Z","iopub.execute_input":"2022-10-30T12:34:39.178324Z","iopub.status.idle":"2022-10-30T12:34:47.858872Z","shell.execute_reply.started":"2022-10-30T12:34:39.178275Z","shell.execute_reply":"2022-10-30T12:34:47.857961Z"},"trusted":true},"execution_count":null,"outputs":[]}]}