{"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":"# H&M image embeddings\n#### preface:\nI entered the competition about 2 to 3 weeks ago but had no time committing to a point where I could make good submissions. Now that there are just 10 days to go, I'm probably not going to get anywhere so I publish the few things I did till now so maybe someone else can profit from them. Like the title already states I mainly worked with the image data till now and experimented a bit with embeddings. In this notebook, I'm going to roughly go over how I created the 2 versions of embeddings you can find in the dataset linked to the notebook as well as potential use cases. <br>\n\n**if you don't want to read through all this: the files \"article_embedings.parquet\" and \"combined_article_embedings.parquet\" contain embeddings that map articles bought together closer together in space, they can be used with a similarity metric like cosine similarity or as a feature to add to an existing ml model**\n\n## Table of contents:\n* [why embeddings?](#why-embeddings?)\n* [how they were created:](#how-they-were-created:)\n    * [data preparation](#data-preparation)\n    * [the models](#the-models)\n    * [the embedding files](#the-embedding-files)\n* [potential use cases](#potential-use-cases)\n\n<br>\n\n****\n\n## why embeddings?\nOriginally I considered it a fun experiment not going with conventional ranking algorithms but with embeddings and image similarity to judge which products would be how likely to be bought by a customer in future. The rough concepts were:\n\n* creating embeddings that map visually similar images closer together in n-dimensional space\n* utilising embeddings as an additional feature for ranking algorithms\n* maybe even \"ranking\" candidates with some sort of knn algorithm + embeddings\n\nMore detailed code and description of potential systems can be found in the [potential use cases](#potential-use-cases) section. <br>\n\nApart from that idea for a potential system I'm just really obsessed with embeddings and since I entered a bit late in this competition I thought I might as well just have some fun playing around with the data ^^ <br>\n\n****\n\n## how they were created:\nSince I made good experiences with using the triplet loss the kinda famous FaceNet paper introduced quite a while ago, I just used it even though I think that newer embedding losses like InfoNCE do have larger potential. Quick overview of embedding model training with triplet loss:\n\n* prepare samples with 3 images: anchor, positive (similar to anchor) and negative (un-similar to anchor)\n* usage of some sort of deep learning model, to encode the images and turn them into an n-dimensional output vector, it is used as a siamese network -> features get passed through the same neural network, the output vectors get compared to update the weights\n* reduce the distance between the output vectors for anchor and positive image\n* maximise the distance between the output vectors for anchor and negative image\n\n### data preparation\nFor the system to be able to work the already mentioned triplets consisting of an anchor, a positive and a negative image have to be created in a way where the differences/similarities are as clear differentiable as possible. This is not as easy as it might sound at the first glance due to manually labelling being nearly impossible. So the method that was used doesn't exactly reflect pure visual similarity:\n\n* for each customer take the most frequent bought article as an anchor\n* take some other recently bought article as positive\n* take an article the customer isn't likely to buy as negative\n\n**-> similarity as whether customers would buy an article or not**\n\n#### **first simple implementation:**\n* for each customer take the most frequent bought article as an anchor\n* take some other articles from the last 12 buys as positive\n* aggregate the least frequent article group a customer buys from, pick an article that wasn't bought and comes from this article group as negative\n\nThis way 10000 triplets were created(due to limited computing power and working memory):","metadata":{}},{"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nfrom keras.preprocessing import image\nimport gc\n\nimport matplotlib.pyplot as plt\nimport seaborn as sns","metadata":{"execution":{"iopub.status.busy":"2022-04-30T16:21:14.065015Z","iopub.execute_input":"2022-04-30T16:21:14.065608Z","iopub.status.idle":"2022-04-30T16:21:14.072265Z","shell.execute_reply.started":"2022-04-30T16:21:14.065560Z","shell.execute_reply":"2022-04-30T16:21:14.071134Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"first_gen_triplets = pd.read_csv(\"../input/aggregated-article-feature-per-month-and-customer/10k simple triplets cleaned.csv\").drop(columns=\"Unnamed: 0\")\nfirst_gen_triplets.head()","metadata":{"execution":{"iopub.status.busy":"2022-04-30T14:01:07.735534Z","iopub.execute_input":"2022-04-30T14:01:07.735917Z","iopub.status.idle":"2022-04-30T14:01:07.787829Z","shell.execute_reply.started":"2022-04-30T14:01:07.735881Z","shell.execute_reply":"2022-04-30T14:01:07.786560Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**let's take a look at some of the visuals:**","metadata":{}},{"cell_type":"code","source":"#  function to load and plot the images from the h&m competition data images folder\ndef show_tripplet(triplet):\n    fig, (ax1, ax2, ax3) = plt.subplots(1, 3, figsize=(12, 4))\n    axis = [ax1, ax2, ax3]\n    titles = [\"positive\", \"anchor\", \"negative\"]\n    for i in range(len(triplet)):\n        article_id = triplet[i]\n        try:\n            img = image.load_img(\"../input/h-and-m-personalized-fashion-recommendations/images/0\" + str(article_id)[:2] + \"/0\" + str(article_id) + \".jpg\", target_size=(300, 200))\n            array = image.img_to_array(img)\n            axis[i].imshow(img)\n            axis[i].set_title(titles[i])\n        except FileNotFoundError:\n            pass\n    plt.show()","metadata":{"execution":{"iopub.status.busy":"2022-04-30T14:01:07.789481Z","iopub.execute_input":"2022-04-30T14:01:07.789975Z","iopub.status.idle":"2022-04-30T14:01:07.796452Z","shell.execute_reply.started":"2022-04-30T14:01:07.789941Z","shell.execute_reply":"2022-04-30T14:01:07.795602Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"show_tripplet(first_gen_triplets.iloc[0, :])","metadata":{"execution":{"iopub.status.busy":"2022-04-30T14:01:07.799005Z","iopub.execute_input":"2022-04-30T14:01:07.800870Z","iopub.status.idle":"2022-04-30T14:01:08.371967Z","shell.execute_reply.started":"2022-04-30T14:01:07.800811Z","shell.execute_reply":"2022-04-30T14:01:08.371048Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"show_tripplet(first_gen_triplets.iloc[4000, :])","metadata":{"execution":{"iopub.status.busy":"2022-04-30T14:01:08.373604Z","iopub.execute_input":"2022-04-30T14:01:08.374316Z","iopub.status.idle":"2022-04-30T14:01:08.887338Z","shell.execute_reply.started":"2022-04-30T14:01:08.374231Z","shell.execute_reply":"2022-04-30T14:01:08.886286Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"show_tripplet(first_gen_triplets.iloc[6000, :])","metadata":{"execution":{"iopub.status.busy":"2022-04-30T14:01:08.889178Z","iopub.execute_input":"2022-04-30T14:01:08.889498Z","iopub.status.idle":"2022-04-30T14:01:09.385628Z","shell.execute_reply.started":"2022-04-30T14:01:08.889461Z","shell.execute_reply":"2022-04-30T14:01:09.384802Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"show_tripplet(first_gen_triplets.iloc[7000, :])","metadata":{"execution":{"iopub.status.busy":"2022-04-30T14:01:09.386922Z","iopub.execute_input":"2022-04-30T14:01:09.387699Z","iopub.status.idle":"2022-04-30T14:01:09.874635Z","shell.execute_reply.started":"2022-04-30T14:01:09.387653Z","shell.execute_reply":"2022-04-30T14:01:09.873921Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**flaws and potential improvement for this method:** <br>\n* over-representation of female clothing -> might cause difficulties for creating embeddings from male articles\n* often children category is used as a negative sample due to it being the category least bought from -> contains a wide variety of styles, might be too similar\n* 10000 training triplets is not much","metadata":{}},{"cell_type":"markdown","source":"### the models\nThe previously described triplets were used in a first version model to create the file \"article_embedings.parquet\", where every article with an image was passed through the model to create an embedding. After that, I thought that due to already not going for pure visual similarity I might as well just pass the tabular article data in \"articles.csv\" into the model which resulted in \"combined_article_embedings.parquet\". <br>\nThe main model was created by utilising transfer learning (using the pre-trained weights of resnet50 on imagenet and building a structure on top of it that was trained to interpret the encoded output):","metadata":{}},{"cell_type":"code","source":"def build_model():\n    \n    input_img = Input(shape=(300, 200, 3))\n    input_tab = Input(shape=(11,))\n\n    #  image encoder\n    resnet_feature_encoder = resnet.ResNet50(weights=\"imagenet\", include_top=False)\n    encoded_img = layers.Flatten()(resnet_feature_encoder(input_img))\n\n    #  tabular data encoder\n    a = layers.Dense(1100, activation=\"relu\")(input_tab)\n    b_a = layers.BatchNormalization()(a)\n    a = layers.Dense(1100, activation=\"relu\")(b_a)\n    encoded_tab = layers.BatchNormalization()(a)\n\n    #  decoder\n    concat_encoded = Concatenate(axis=1)([encoded_img, encoded_tab])\n    a = layers.Dense(1100, activation=\"relu\")(concat_encoded)\n    b_a = layers.BatchNormalization()(a)\n    out = layers.Dense(550, activation=\"relu\")(b_a)\n\n    embedding_model = Model(inputs = [input_img, input_tab], outputs=out)\n\n    #  freeze weights of immage encoder\n    for layer in resnet_feature_encoder.layers:\n        layer.trainable = False\n    \n    return embedding_model","metadata":{"execution":{"iopub.status.busy":"2022-04-30T14:01:09.875678Z","iopub.execute_input":"2022-04-30T14:01:09.876607Z","iopub.status.idle":"2022-04-30T14:01:09.885120Z","shell.execute_reply.started":"2022-04-30T14:01:09.876537Z","shell.execute_reply":"2022-04-30T14:01:09.884212Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"since this should be no guide on siamese networks etc. I leave it at that, the only things may be important left to note are:\n* **AdamW** was used as an optimizer to prevent overfitting\n* since I was limited to 16 GB of ram not the entire dataset of images could be loaded simultaneously so the model was trained on **separate batches of images**","metadata":{}},{"cell_type":"markdown","source":"### the embedding files\nIn the dataset 2 files can be found containing generated embeddings for each article that has a corresponding image. Every file without image has a [nan] stored in the embedding column (in list due to parquet files just accepting same datatype in one row)","metadata":{}},{"cell_type":"code","source":"embeddings_v1 = pd.read_parquet(\"../input/aggregated-article-feature-per-month-and-customer/article_embedings.parquet\")\nembeddings_v2 = pd.read_parquet(\"../input/aggregated-article-feature-per-month-and-customer/combined_article_embedings.parquet\")","metadata":{"execution":{"iopub.status.busy":"2022-04-30T14:01:09.886353Z","iopub.execute_input":"2022-04-30T14:01:09.886576Z","iopub.status.idle":"2022-04-30T14:01:14.753625Z","shell.execute_reply.started":"2022-04-30T14:01:09.886550Z","shell.execute_reply":"2022-04-30T14:01:14.752627Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"embeddings_v1.info()","metadata":{"execution":{"iopub.status.busy":"2022-04-30T14:01:14.756568Z","iopub.execute_input":"2022-04-30T14:01:14.757178Z","iopub.status.idle":"2022-04-30T14:01:14.786266Z","shell.execute_reply.started":"2022-04-30T14:01:14.757131Z","shell.execute_reply":"2022-04-30T14:01:14.785315Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"embeddings_v2.info()","metadata":{"execution":{"iopub.status.busy":"2022-04-30T14:01:14.787568Z","iopub.execute_input":"2022-04-30T14:01:14.788308Z","iopub.status.idle":"2022-04-30T14:01:14.877402Z","shell.execute_reply.started":"2022-04-30T14:01:14.788264Z","shell.execute_reply":"2022-04-30T14:01:14.876432Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"In the second file, the embeddings were just added to the article.csv file from the H&M competition data, because I prefer working with parquets when dealing with large data so with that I also had a file to use instead of article.csv. <br>\nAlso, you might notice there being many zeros in the embedding vectors, this is due to the high dimensionality - it helped slightly reduce the loss but could be smaller and packed with a higher \"information density\"","metadata":{}},{"cell_type":"markdown","source":"## potential use cases\nbut how could these embeddings now be used? - there are many systems you could brew with them but in this section, I will show 2 things: <br>\n* pair the triplet visuals with similarity scores to see how they work\n* a sample algorithm to rank articles from a candidate for each customer\n\nthe downside of embedding usage for this competition: <br>\n* let's face it: it's a good method for recommendation systems but not to make predictions that are judged by a ranking loss\n* obviously they still can be used to achieve quite good results but being limited in computing power and time, getting them optimal is hard\n\n**realistic way to still draw advantage from them:** <br>\n* still they can be used as a pseudo dimensionality reduced version of the images to pass into conventional ranking algorithms as additional features","metadata":{}},{"cell_type":"markdown","source":"### using cosine similarity to judge how similar articles are to each other based on embeddings:","metadata":{}},{"cell_type":"code","source":"from tensorflow.keras import metrics \n\n#  update the visualisation function for the tripplets\ndef show_tripplet(triplet):\n    fig, (ax1, ax2, ax3) = plt.subplots(1, 3, figsize=(12, 4))\n    axis = [ax1, ax2, ax3]\n    \n    #  cosine similarity\n    cosine_similarity = metrics.CosineSimilarity()\n    pos_similarity = cosine_similarity(embeddings_v2[\"embeding\"][embeddings_v2[\"article_id\"]==triplet[1]].iloc[0], \n                                       embeddings_v2[\"embeding\"][embeddings_v2[\"article_id\"]==triplet[0]].iloc[0])\n    neg_similarity = cosine_similarity(embeddings_v2[\"embeding\"][embeddings_v2[\"article_id\"]==triplet[1]].iloc[0], \n                                       embeddings_v2[\"embeding\"][embeddings_v2[\"article_id\"]==triplet[2]].iloc[0])\n    titles = [f\"positive, similarity: {pos_similarity}\", \"anchor\", f\"negative, similarity: {neg_similarity}\"]\n    for i in range(len(triplet)):\n        article_id = triplet[i]\n        try:\n            img = image.load_img(\"../input/h-and-m-personalized-fashion-recommendations/images/0\" + str(article_id)[:2] + \"/0\" + str(article_id) + \".jpg\", target_size=(300, 200))\n            array = image.img_to_array(img)\n            axis[i].imshow(img)\n            axis[i].set_title(titles[i])\n        except FileNotFoundError:\n            pass\n    plt.show()","metadata":{"execution":{"iopub.status.busy":"2022-04-30T14:01:14.878909Z","iopub.execute_input":"2022-04-30T14:01:14.879189Z","iopub.status.idle":"2022-04-30T14:01:15.401074Z","shell.execute_reply.started":"2022-04-30T14:01:14.879159Z","shell.execute_reply":"2022-04-30T14:01:15.400070Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"show_tripplet(first_gen_triplets.iloc[0, :])","metadata":{"execution":{"iopub.status.busy":"2022-04-30T14:01:15.402668Z","iopub.execute_input":"2022-04-30T14:01:15.402970Z","iopub.status.idle":"2022-04-30T14:01:15.990686Z","shell.execute_reply.started":"2022-04-30T14:01:15.402930Z","shell.execute_reply":"2022-04-30T14:01:15.988317Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"show_tripplet(first_gen_triplets.iloc[4000, :])","metadata":{"execution":{"iopub.status.busy":"2022-04-30T14:01:15.992159Z","iopub.execute_input":"2022-04-30T14:01:15.992518Z","iopub.status.idle":"2022-04-30T14:01:16.681100Z","shell.execute_reply.started":"2022-04-30T14:01:15.992475Z","shell.execute_reply":"2022-04-30T14:01:16.680060Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"show_tripplet(first_gen_triplets.iloc[6000, :])","metadata":{"execution":{"iopub.status.busy":"2022-04-30T14:01:16.682905Z","iopub.execute_input":"2022-04-30T14:01:16.683237Z","iopub.status.idle":"2022-04-30T14:01:17.151290Z","shell.execute_reply.started":"2022-04-30T14:01:16.683195Z","shell.execute_reply":"2022-04-30T14:01:17.150433Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"show_tripplet(first_gen_triplets.iloc[7000, :])","metadata":{"execution":{"iopub.status.busy":"2022-04-30T14:01:17.152582Z","iopub.execute_input":"2022-04-30T14:01:17.153014Z","iopub.status.idle":"2022-04-30T14:01:17.628976Z","shell.execute_reply.started":"2022-04-30T14:01:17.152975Z","shell.execute_reply":"2022-04-30T14:01:17.628142Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"as you can see, it's not perfect but kinda fun","metadata":{}},{"cell_type":"markdown","source":"### using similarity to pseudo rank candidates\n* **reminder:** this is more playing around than an actual system, it could work with better embeddings, but they would require more time and computing power\n* to keep it simple I will just pick to top hundred most frequent bought articles of the most recent week as sample candidat\n* also the candidat's will just be compared to the most frequent bought article of a period (of the individual customers)\n* I won't go far into testing due to it being just an example and not a final system","metadata":{}},{"cell_type":"code","source":"#  sample candidat:\ncandidat = pd.read_parquet(\"../input/aggregated-article-feature-per-month-and-customer/sample candidat.parquet\")\ncandidat[\"embedded_candidat\"] = candidat[\"article_id\"].map(lambda x: embeddings_v2[\"embeding\"][embeddings_v2[\"article_id\"] == x].iloc[0])\ncandidat.head()","metadata":{"execution":{"iopub.status.busy":"2022-04-30T14:01:17.630482Z","iopub.execute_input":"2022-04-30T14:01:17.630909Z","iopub.status.idle":"2022-04-30T14:01:17.694030Z","shell.execute_reply.started":"2022-04-30T14:01:17.630869Z","shell.execute_reply":"2022-04-30T14:01:17.692952Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"* usage of sklearn cosine_similarity due to it enabeling comoputing similarity of every article in the candidat to an other article simultaniously:","metadata":{}},{"cell_type":"code","source":"from sklearn.metrics.pairwise import cosine_similarity\n\n# example\ncosine_similarity([[1, 1, 2]], [[1, 1, 2], [1, 2, 2], [1, 7, 7]])","metadata":{"execution":{"iopub.status.busy":"2022-04-30T14:01:17.695304Z","iopub.execute_input":"2022-04-30T14:01:17.695537Z","iopub.status.idle":"2022-04-30T14:01:17.901875Z","shell.execute_reply.started":"2022-04-30T14:01:17.695509Z","shell.execute_reply":"2022-04-30T14:01:17.900977Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#  function to return cosine similarity\ncandidat_array = np.stack(candidat[\"embedded_candidat\"].to_numpy())\n\ndef cosine_sim(anchor):\n    return cosine_similarity([embeddings_v2[\"embeding\"][embeddings_v2[\"article_id\"] == anchor].iloc[0]], candidat_array)","metadata":{"execution":{"iopub.status.busy":"2022-04-30T14:01:17.903279Z","iopub.execute_input":"2022-04-30T14:01:17.903596Z","iopub.status.idle":"2022-04-30T14:01:17.911097Z","shell.execute_reply.started":"2022-04-30T14:01:17.903555Z","shell.execute_reply":"2022-04-30T14:01:17.909885Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#  function to return top 12 ranked customers\ndef get_top_12(anchor, customer_id):\n    \n    predictions = candidat.iloc[:, :1]\n    \n    try:\n        predictions[\"score\"] = cosine_sim(anchor)[0]\n\n        predictions = predictions.sort_values(\"score\", ascending=False)\n\n        return predictions[\"article_id\"].to_numpy()[:12]\n    \n    except ValueError:\n        return predictions[\"article_id\"].to_numpy()[:12]","metadata":{"execution":{"iopub.status.busy":"2022-04-30T14:01:17.912613Z","iopub.execute_input":"2022-04-30T14:01:17.913062Z","iopub.status.idle":"2022-04-30T14:01:17.928055Z","shell.execute_reply.started":"2022-04-30T14:01:17.912997Z","shell.execute_reply":"2022-04-30T14:01:17.926818Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"submission = pd.read_csv(\"../input/h-and-m-personalized-fashion-recommendations/customers.csv\", usecols=[\"customer_id\"])\ntransactions = pd.read_csv(\"../input/h-and-m-personalized-fashion-recommendations/transactions_train.csv\")","metadata":{"execution":{"iopub.status.busy":"2022-04-30T15:26:16.822836Z","iopub.execute_input":"2022-04-30T15:26:16.823376Z","iopub.status.idle":"2022-04-30T15:27:19.855307Z","shell.execute_reply.started":"2022-04-30T15:26:16.823324Z","shell.execute_reply":"2022-04-30T15:27:19.854530Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"* just use transaction from the current year","metadata":{}},{"cell_type":"code","source":"recent_transactions = transactions.loc[transactions['t_dat'] >= \"2020-01-01\"]","metadata":{"execution":{"iopub.status.busy":"2022-04-30T14:02:25.246487Z","iopub.execute_input":"2022-04-30T14:02:25.246881Z","iopub.status.idle":"2022-04-30T14:02:27.467073Z","shell.execute_reply.started":"2022-04-30T14:02:25.246835Z","shell.execute_reply":"2022-04-30T14:02:27.466149Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from tqdm.notebook import tqdm\ntqdm.pandas()","metadata":{"execution":{"iopub.status.busy":"2022-04-30T14:02:27.468524Z","iopub.execute_input":"2022-04-30T14:02:27.468928Z","iopub.status.idle":"2022-04-30T14:02:27.477102Z","shell.execute_reply.started":"2022-04-30T14:02:27.468884Z","shell.execute_reply":"2022-04-30T14:02:27.476275Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"* calculating modes/anchors before passing into function to use groupby apply -> way faster","metadata":{}},{"cell_type":"code","source":"anchors = recent_transactions.groupby(\"customer_id\")[\"article_id\"].progress_apply(lambda x:x.value_counts().index[0]).reset_index().rename(columns={\"article_id\":\"anchor\"})","metadata":{"execution":{"iopub.status.busy":"2022-04-30T14:02:27.478634Z","iopub.execute_input":"2022-04-30T14:02:27.479267Z","iopub.status.idle":"2022-04-30T14:06:45.936795Z","shell.execute_reply.started":"2022-04-30T14:02:27.479203Z","shell.execute_reply":"2022-04-30T14:06:45.935440Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"customers_with_buy = submission[submission[\"customer_id\"].isin(recent_transactions[\"customer_id\"])].merge(anchors, on=\"customer_id\")","metadata":{"execution":{"iopub.status.busy":"2022-04-30T14:06:45.938819Z","iopub.execute_input":"2022-04-30T14:06:45.939110Z","iopub.status.idle":"2022-04-30T14:06:49.539639Z","shell.execute_reply.started":"2022-04-30T14:06:45.939073Z","shell.execute_reply":"2022-04-30T14:06:49.538868Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"customers_with_buy[\"prediction\"] = customers_with_buy.progress_apply(lambda x: get_top_12(x.anchor, x.customer_id), axis=1)","metadata":{"execution":{"iopub.status.busy":"2022-04-30T14:06:49.554688Z","iopub.execute_input":"2022-04-30T14:06:49.554988Z","iopub.status.idle":"2022-04-30T15:07:28.110508Z","shell.execute_reply.started":"2022-04-30T14:06:49.554949Z","shell.execute_reply":"2022-04-30T15:07:28.109764Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"customers_with_buy.to_parquet(\"save.parquet\")","metadata":{"execution":{"iopub.status.busy":"2022-04-30T15:08:22.756995Z","iopub.execute_input":"2022-04-30T15:08:22.757293Z","iopub.status.idle":"2022-04-30T15:08:24.476222Z","shell.execute_reply.started":"2022-04-30T15:08:22.757261Z","shell.execute_reply":"2022-04-30T15:08:24.475239Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"predictions = pd.Series([candidat[\"article_id\"].to_numpy()[:12] for i in range(len(customers_without_buy))])\ncustomers_without_buy[\"prediction\"] = predictions","metadata":{"execution":{"iopub.status.busy":"2022-04-30T15:32:48.022880Z","iopub.execute_input":"2022-04-30T15:32:48.023487Z","iopub.status.idle":"2022-04-30T15:32:50.530075Z","shell.execute_reply.started":"2022-04-30T15:32:48.023450Z","shell.execute_reply":"2022-04-30T15:32:50.529357Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sample_submission[\"prediction\"] = sample_submission[\"prediction\"].progress_map(lambda x: ' '.join([\"0\"+str(item) for item in x]))","metadata":{"execution":{"iopub.status.busy":"2022-04-30T15:59:45.005170Z","iopub.execute_input":"2022-04-30T15:59:45.006104Z","iopub.status.idle":"2022-04-30T16:00:03.506427Z","shell.execute_reply.started":"2022-04-30T15:59:45.006052Z","shell.execute_reply":"2022-04-30T16:00:03.505365Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sample_submission.to_csv(\"sample_submission_v0.csv\", index=False)","metadata":{"execution":{"iopub.status.busy":"2022-04-30T16:00:21.630781Z","iopub.execute_input":"2022-04-30T16:00:21.631078Z","iopub.status.idle":"2022-04-30T16:00:28.703214Z","shell.execute_reply.started":"2022-04-30T16:00:21.631048Z","shell.execute_reply":"2022-04-30T16:00:28.702135Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"del transactions","metadata":{"execution":{"iopub.status.busy":"2022-04-30T16:45:49.814498Z","iopub.execute_input":"2022-04-30T16:45:49.815350Z","iopub.status.idle":"2022-04-30T16:45:53.525707Z","shell.execute_reply.started":"2022-04-30T16:45:49.815302Z","shell.execute_reply":"2022-04-30T16:45:53.524292Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### using embeddings as some kind of ensembling method? - why not\nEmbedding similarity works surprisingly well for ensembling, but it's extremely slow. I might come up with a way that's fast enough to test larger amounts of data. For now, I just wanted to mention it as a possibility. If you come up with one please let me know. <br>\nMy approach was:\n* comparing the predictions of many systems index wise \n* computing cosine similarity for each one to some type of anchor\n* picking the predicted ideas with the highest score","metadata":{"execution":{"iopub.status.busy":"2022-04-30T16:45:56.193683Z","iopub.execute_input":"2022-04-30T16:45:56.194079Z","iopub.status.idle":"2022-04-30T16:45:56.527687Z","shell.execute_reply.started":"2022-04-30T16:45:56.194039Z","shell.execute_reply":"2022-04-30T16:45:56.526759Z"}}}]}