{ "cells": [ { "cell_type": "markdown", "metadata": { "id": "J0v8-m3JYFQo" }, "source": [ "# Session-based recommendation using word2vec\n", "> How to build a session-based product recommender using word2vec model on retail dataset\n", "\n", "- toc: true\n", "- badges: true\n", "- comments: true\n", "- categories: [session, retail]\n", "- image: " ] }, { "cell_type": "markdown", "metadata": { "id": "XIY8wbf-VFyT" }, "source": [ "### Install/import libraries" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "5pz-XFyoj5is" }, "outputs": [], "source": [ "# !sudo apt-get install -y ray\n", "# !pip install ray\n", "# !pip install ray[default]\n", "# !pip install ray[tune]" ] }, { "cell_type": "code", "execution_count": 80, "metadata": { "id": "zV-QhI_1UkjA" }, "outputs": [], "source": [ "import pandas as pd\n", "import numpy as np\n", "import matplotlib.pyplot as plt\n", "import seaborn as sns\n", "import os\n", "import pickle\n", "from numpy.random import default_rng\n", "import collections\n", "import itertools\n", "from copy import deepcopy \n", "\n", "from gensim.models.word2vec import Word2Vec\n", "from gensim.models.callbacks import CallbackAny2Vec\n", "from ray import tune\n", "\n", "import os\n", "import argparse\n", "import ray\n", "import time\n", "\n", "MODEL_DIR = \"/content\"" ] }, { "cell_type": "code", "execution_count": 74, "metadata": { "id": "XDOxi0wabyQs" }, "outputs": [], "source": [ "plt.style.use(\"seaborn-white\")\n", "cldr_colors = ['#00b6b5', '#f7955b','#6c8cc7', '#828282']#\n", "cldr_green = '#a4d65d'\n", "color_palette = \"viridis\"\n", "\n", "rng = default_rng(123)\n", "\n", "ECOMM_PATH = \"/content\"\n", "ECOMM_FILENAME = \"OnlineRetail.csv\"\n", "\n", "%load_ext tensorboard" ] }, { "cell_type": "markdown", "metadata": { "id": "4UPgoIvlU9z2" }, "source": [ "### Load data" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "t5DGOggiTryP" }, "outputs": [], "source": [ "!wget -O data.zip https://github.com/sparsh-ai/reco-data/blob/master/onlineretail.zip?raw=true\n", "!unzip data.zip" ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "id": "Hgc7vOvTV7y9" }, "outputs": [], "source": [ "def load_original_ecomm(pathname=ECOMM_PATH):\n", " df = pd.read_csv(os.path.join(pathname, ECOMM_FILENAME),\n", " encoding=\"ISO-8859-1\",\n", " parse_dates=[\"InvoiceDate\"],\n", " )\n", " return df" ] }, { "cell_type": "code", "execution_count": 46, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 289 }, "id": "TmsQtNH4dEwb", "outputId": "f6fb36b1-69ac-4d1a-f014-50a407304374" }, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
InvoiceNoStockCodeDescriptionQuantityInvoiceDateUnitPriceCustomerIDCountry
053636585123AWHITE HANGING HEART T-LIGHT HOLDER62010-12-01 08:26:002.5517850.0United Kingdom
153636571053WHITE METAL LANTERN62010-12-01 08:26:003.3917850.0United Kingdom
253636584406BCREAM CUPID HEARTS COAT HANGER82010-12-01 08:26:002.7517850.0United Kingdom
353636584029GKNITTED UNION FLAG HOT WATER BOTTLE62010-12-01 08:26:003.3917850.0United Kingdom
453636584029ERED WOOLLY HOTTIE WHITE HEART.62010-12-01 08:26:003.3917850.0United Kingdom
\n", "
" ], "text/plain": [ " InvoiceNo StockCode ... CustomerID Country\n", "0 536365 85123A ... 17850.0 United Kingdom\n", "1 536365 71053 ... 17850.0 United Kingdom\n", "2 536365 84406B ... 17850.0 United Kingdom\n", "3 536365 84029G ... 17850.0 United Kingdom\n", "4 536365 84029E ... 17850.0 United Kingdom\n", "\n", "[5 rows x 8 columns]" ] }, "execution_count": 46, "metadata": { "tags": [] }, "output_type": "execute_result" } ], "source": [ "df = load_original_ecomm()\n", "df.head()" ] }, { "cell_type": "code", "execution_count": 47, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "QnlEr3cngRbq", "outputId": "d50991db-b741-453a-f264-97ba5f51430f" }, "outputs": [ { "data": { "text/plain": [ "InvoiceNo 0\n", "StockCode 0\n", "Description 1454\n", "Quantity 0\n", "InvoiceDate 0\n", "UnitPrice 0\n", "CustomerID 135080\n", "Country 0\n", "dtype: int64" ] }, "execution_count": 47, "metadata": { "tags": [] }, "output_type": "execute_result" } ], "source": [ "df.isnull().sum()" ] }, { "cell_type": "markdown", "metadata": { "id": "XedYV2JkWigc" }, "source": [ "### Preprocess" ] }, { "cell_type": "markdown", "metadata": { "id": "474PEoLOgFI_" }, "source": [ "There are some rows with missing information, so we'll filter those out. Since we want to define customer sessions, we'll use group by CustomerID field and filter out any customer entries that have fewer than three purchased items." ] }, { "cell_type": "code", "execution_count": 48, "metadata": { "id": "3KVsMP58gp5h" }, "outputs": [], "source": [ "def preprocess_ecomm(df, min_session_count=3):\n", "\n", " df.dropna(inplace=True)\n", " item_counts = df.groupby([\"CustomerID\"]).count()[\"StockCode\"]\n", " df = df[df[\"CustomerID\"].isin(item_counts[item_counts >= min_session_count].index)].reset_index(drop=True)\n", " \n", " return df" ] }, { "cell_type": "code", "execution_count": 57, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 289 }, "id": "5138zkW_g1g0", "outputId": "18135a1a-f3df-4007-c987-c7a02bf27355" }, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
InvoiceNoStockCodeDescriptionQuantityInvoiceDateUnitPriceCustomerIDCountry
053636585123AWHITE HANGING HEART T-LIGHT HOLDER62010-12-01 08:26:002.5517850.0United Kingdom
153636571053WHITE METAL LANTERN62010-12-01 08:26:003.3917850.0United Kingdom
253636584406BCREAM CUPID HEARTS COAT HANGER82010-12-01 08:26:002.7517850.0United Kingdom
353636584029GKNITTED UNION FLAG HOT WATER BOTTLE62010-12-01 08:26:003.3917850.0United Kingdom
453636584029ERED WOOLLY HOTTIE WHITE HEART.62010-12-01 08:26:003.3917850.0United Kingdom
\n", "
" ], "text/plain": [ " InvoiceNo StockCode ... CustomerID Country\n", "0 536365 85123A ... 17850.0 United Kingdom\n", "1 536365 71053 ... 17850.0 United Kingdom\n", "2 536365 84406B ... 17850.0 United Kingdom\n", "3 536365 84029G ... 17850.0 United Kingdom\n", "4 536365 84029E ... 17850.0 United Kingdom\n", "\n", "[5 rows x 8 columns]" ] }, "execution_count": 57, "metadata": { "tags": [] }, "output_type": "execute_result" } ], "source": [ "df = preprocess_ecomm(df)\n", "df.head()" ] }, { "cell_type": "code", "execution_count": 58, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "0P1BcYYYhDqI", "outputId": "377b0962-8019-4c93-9812-932b7ff020cd" }, "outputs": [ { "data": { "text/plain": [ "4234" ] }, "execution_count": 58, "metadata": { "tags": [] }, "output_type": "execute_result" } ], "source": [ "# Number of unique customers after preprocessing\n", "df.CustomerID.nunique()" ] }, { "cell_type": "code", "execution_count": 59, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "wq63Y-D-hWKR", "outputId": "5e39d429-418b-4d08-d4f3-c529cfcf3252" }, "outputs": [ { "data": { "text/plain": [ "3684" ] }, "execution_count": 59, "metadata": { "tags": [] }, "output_type": "execute_result" } ], "source": [ "# Number of unique stock codes (products)\n", "df.StockCode.nunique()" ] }, { "cell_type": "markdown", "metadata": { "id": "lKpBJdaOiExk" }, "source": [ "### Product popularity\n", "Here we plot the frequency by which each product is purchased (occurs in a transaction)" ] }, { "cell_type": "code", "execution_count": 60, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 441 }, "id": "TZLOGjaPiHPY", "outputId": "12ef2092-5394-41c3-b158-6b0f7b8809f0" }, "outputs": [ { "data": { "image/png": "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yu9jNXr0aIuTAACAmpKZmamWLVtWGPMzxhiL8lS74uJibdq0STExMQoICLA6DgAAcCO73a7MzEwlJSUpNDS0wjafKjgAAAASi4wBAIAPouAAAACfQ8EBAAA+h4LjgsOHD2vs2LHq1auX+vfvr0cffVQ2m83qWNWiffv2SkpKUnJysvO/hx9+WJL0ww8/6Nprr1XXrl112WWX6YMPPqjwue+9954uv/xyde3aVddee61+/PFHK6ZwRtLS0jR06FANHDiwwvi5zNVms2nq1KkaMGCAevXqpbFjx3rkpQoqm/vq1avVvn37Cj//5ORkffHFF87HePvcDx48qD/96U/q3bu3evfurXvuuUcZGRmSTnxPbr75ZnXv3l2DBg3Siy++qN8uS1y8eLGuvPJKdenSRcOHD9dXX33l3GaM0cyZM3XxxRere/fuuvnmm7V9+/Yan9+pVDX3AwcOVPraf+2115yf6+1zX79+vW688UZ17dpVffr00YQJE5xn2vry672qedeG1/pJDE7r6quvNpMnTzY5OTnmwIED5qqrrjLTp0+3Ola1iI+PN6tWrTpp/MiRI6ZLly7mvffeM0VFReann34yXbt2Nd99950xxphvvvnGdO3a1axZs8YUFxebDz74wHTt2tVkZmbW9BRc9uWXX5q+ffuau+66y1x00UXO8XOd61NPPWWuvPJKs2/fPpObm2smT55srrnmGkvmWJWq5r5q1SoTHx9f5ef5wtyHDh1qJk6caPLy8szRo0fNzTffbO644w5TVFRk+vfvb5599lmTn59vtm3bZvr372/ef/99Y4wxW7ZsMUlJSWbp0qWmuLjYfP311yY5OdmkpaUZY4x59913Tf/+/c3WrVtNQUGBee6558xFF11kiouLrZxuBVXNff/+/SY+Pt7s37+/0s/z9rlnZ2ebLl26mLfeesvYbDZz9OhRc+ONN5px48b59Ov9VPOuDa/136PgnMaGDRtMQkKCycrKco4tWrTI9OjRw9jtdguTVY+qCs7cuXPN0KFDK4xNnTrVjBs3zhhjzB133GEee+yxCtuHDBli3nzzTbdlPVcfffSROXjwoHnnnXcqvMmfy1xLS0tNt27dzJIlS5zbsrKyTPv27c3mzZvdOJszU9XcT/dHz9vnnpOTYyZPnmzS09OdY1988YXp0qWLWbRokenZs6cpLS11bps7d64ZPny4MebE78Cdd95Z4evdcccd5vHHHzfGnPg+vPHGG85tNpvNdO/e3SxdutSdU3LZqeZ+uoLj7XM/cuSI+fjjjyuMzZs3z1x00UU+/Xo/1bx9/bVeGQ5RnUZqaqpiY2MVFRXlHEtMTFROTo727dtnYbLqM2/ePA0aNEjdunXTX//6V+Xm5io1NVWJiYkVHtexY0dt3LhR0onvS8eOHavc7olGjhyppk2bnjR+LnPdt2+f8vLyKmyPiopSkyZNPOp7UdXcy02aNEkXXHCB+vTpo9mzZ8vhcEjy/rlHRERo2rRpaty4sXPs8OHDaty4sVJTUxUfH6/AwF+vd9qxY0dt27ZNJSUlp/y9KC4u1o4dOyrMPSgoSPHx8V4x93LTp0/XhRdeqJ49e+rJJ590Hnr39rnHxMToD3/4g6QTh9N27typzz77TEOGDPHp1/up5l3OV1/rlaHgnEZ2drYiIiIqjEVGRkqSjh8/bkWkatW5c2d1795dX375pT799FOlpaXpoYceqnTe9evXd865qu9LdnZ2jWWvLucy1/L5lv9O/Ha7N/x+hIWFqUuXLho6dKi+++47zZgxQ6+//rrmz58vyffmvmvXLs2ePVt33XVXlT93h8OhnJycKud+/Phx5eTkyBjjtXMPDg5WSkqKBgwYoK+//lpvvfWWli5dqhdeeEFS1T93b5v71q1blZSUpKFDhyo5OVl/+ctfasXrvbJ517bXukTBcYnx4Wshfvjhh7rlllsUGhqqli1basKECVq8eLHMicOXVserMec6V2/9XiUmJmr+/PkaMGCAgoKC1Lt3b40aNUqff/65y1/DW+a+ceNG3Xjjjfq///s/DRs2TNLps5/rdk/x+7k3atRICxYs0IgRIxQcHKyOHTvq9ttvr/Bz94W5JyQkaNOmTfriiy+0e/duTZgwQZLvv94rm3dteq2Xo+CcRlRU1El7Jco/jo6OtiKSWzVv3lzGmErnffz4ceecGzRocFJzz87OrnAoz1s0aNDgrOdaPt/Kfke88XshSc2aNdORI0ck+c7cly9frjFjxmj8+PEaP368pKpf2wEBAapfv36lvxfZ2dmKjo5W/fr15e/v77Vzr0yzZs2UlZUlu93uM3OXJD8/P7Vt29b5jze73V4rXu+/n3f5GWS/5Yuv9d+i4JxGUlKSMjIyKvxybNiwQdHR0YqLi7Mw2bnbvHmznnrqqQpjO3fuVFBQkDp06KBNmzZV2LZx40Z17txZ0onvy++3b9iwQSkpKe4N7QbJyclnPde4uDhFRkZW2J6RkaH09HSv+F4sWrRI77//foWxXbt2qXnz5pJ8Y+4///yz7r33Xj399NO64YYbnONJSUlKS0urcMmHDRs2qEOHDgoODq507uW/FyEhITrvvPMqrD+w2WzaunWrV8x95cqVmj17doXH7tq1S7GxsQoICPD6uS9atEhXX311hTF//xNvd/379/fZ1/up5r1mzRqff62fpIYXNXulUaNGmfvuu8/k5uaaffv2mSuuuMLMmjXL6ljnLD093aSkpJhXX33VlJSUmF27dpkrrrjCTJ061WRlZZlu3bqZd9991xQXF5tVq1aZlJQU88MPPxhjjFm+fLlJSUlxnlL45ptvml69epns7GyLZ3V6vz+T6FznOmPGDDN06FCzf/9+k5OTYyZMmGBuvvlmS+Z2Or+f+9KlS02nTp3M8uXLjc1mMytWrDApKSlm0aJFxhjvn3tpaam54oorzFtvvXXStpKSEjNw4EDzzDPPmIKCArNlyxbTp08f89lnnxljjNm+fbtJSkoyX331lSkpKTELFy40nTp1Mnv27DHGGDN//nzTt29fk5aWZgoKCsxTTz1lBg8ebGw2W43OsSqnmvvGjRtNYmKi+ec//2lsNpvZsGGD6dOnj5k7d64xxvvnnp6ebrp27WpmzZplioqKzNGjR82tt95qrrvuOp9+vZ9q3r7+Wq8MBccF6enp5s477zSdO3c2vXr1Mk899ZQpKyuzOla1+OGHH8yoUaNMSkqK6dmzp5k2bZrzWhY//vijGTFihElKSjKDBg1y/uEvt2DBAnPRRReZpKQkM3LkSPPzzz9bMQWXXXrppSYpKcl07NjRxMfHm6SkJJOUlGQOHDhwTnO12WzmscceMz179jQpKSnm7rvvrnBZAU9wqrnPnz/fXHrppSY5OdlcdNFF5sMPP6zwud489zVr1lSY72//O3DggNmxY4cZPXq0SU5ONn379jVz5syp8PlLly41l112mUlMTDRDhgxxXiul3KxZs0yfPn1McnKy+eMf/+gsAJ7gdHP/6quvzPDhw02nTp1Mnz59zCuvvFLh0hfePHdjjFm/fr0ZNWqUSU5ONueff7659957nafM+/Lr/VTz9uXXemW4mzgAAPA5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\n", "text/plain": [ "
" ] }, "metadata": { "tags": [] }, "output_type": "display_data" } ], "source": [ "plt.style.use(\"seaborn-white\")\n", "\n", "# Number of unique customer IDs\n", "product_counts = df.groupby(['StockCode']).count()['InvoiceNo'].values\n", "\n", "fig = plt.figure(figsize=(8,6))\n", "plt.yticks(fontsize=14)\n", "plt.xticks(fontsize=14)\n", "\n", "plt.semilogy(sorted(product_counts))\n", "plt.ylabel(\"Product counts\", fontsize=16);\n", "plt.xlabel(\"Product index\", fontsize=16);\n", "\n", "plt.tight_layout()" ] }, { "cell_type": "markdown", "metadata": { "id": "I4evk-MwiT-f" }, "source": [ "The left side of the figure corresponds to products that are not very popular (because they aren't purchased very often), while the far right side indicates that some products are extremely popular and have been purchased hundreds of times.\n", "\n", "### Customer session lengths\n", "We define a customer's \"session\" as all the products they purchased in each transaction, in the order in which they were purchased (ordered InvoiceDate). We can then examine statistics regarding the length of these sessions. Below is a boxplot of all customer session lengths." ] }, { "cell_type": "code", "execution_count": 61, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 441 }, "id": "0nlaj3ieiILl", "outputId": "36a93a1c-2047-44f6-9880-164a37107b14" }, "outputs": [ { "data": { "image/png": "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c9m68cYb1aZNG23YsEHvvPOODhw4oMcee4x5LkOer2BYv369Fi5cqPfff1/nzp3ThAkTyu21uVRvMjb8LcByxfyWj2+++UYDBw7U/fffr549e0oqea7ZF6XXokULxcXFaf369fr+++81YcIEScxhWZo9e7b69u2rX//610XKmOey88Ybb2jYsGGqVKmSmjZtqgkTJuiDDz6QOf82jqvdPSt45nH48OFq0KCB6tSpowkTJmjbtm3Ky8srl9fmEgNOrVq1iqRRz+PatWtfdIPwde211xaZ31OnTjlze+211xZJqWlpaT63DFHU5s2bNXToUI0ZM0ZjxoyRVPyxHBAQoJo1a/rdF2lpaRznF+ByudS8eXPnhJCfn8/xXEa2bNmib775RqNGjSpSxutG+WrUqJGMMX5fM5jnS1OnTh1J56/MeDRs2FDS+e+dKo/X5hIDTlhYmFJSUpyPgErS7t27Vbt2bTVu3LikzVGC8PBwxcXF+Sz75ptvdOONN0o6P/+Fy3fv3q3IyMgr1sefm127dmn8+PF65plndN999znLw8LCdODAAZ8/cbB7927dcMMNCgoK8jvX3vsC573//vu66667fJZVqHD+pSQ2NpbjuYy8++67SklJUYcOHXTTTTc5c37TTTfJ7XYzz2Vk7969mjNnjs+yb7/9VhUrVtQNN9zAPJeR+vXr65prrtHevXudZUlJSZKku+66q3xem0vzzud+/fqZiRMnmoyMDJOYmGjuuOMOM3/+/NJsikIKf4rq5MmTpnXr1mbFihUmOzvbfPHFFyYyMtJ89dVXxhhjNm/ebCIjI52PIS5evNjcdNNNJi0t7WoN4Sft3Llz5o477jBLliwpUpaTk2M6depknnvuOZOVlWX27dtnYmJizNq1a40xxhw8eNCEhYWZDz/80OTk5Jh//vOfJiIiwhw+fPhKD+MnLTk52URHR5v58+ebs2fPmhMnTpjhw4eb/v37czyXobS0NHPs2DHn344dO4zb7TbHjh0zSUlJzHMZSU5ONpGRkebvf/+7ycnJMd9995254447zJNPPsnxXMaee+45Exsbaw4dOmTS0tLMsGHDzAMPPFBur82lCjjJyclmxIgR5sYbbzQ33XSTmTNnjsnLy7v80f6CdOvWzYSFhZmWLVsat9ttwsLCTFhYmElKSjJbt241vXv3NmFhYaZz587OTvV4/fXXTceOHU1YWJjp06eP2bVr11UaxU/f119/7TO/3v+SkpLMoUOHzIABA0x4eLhp3769WbRokc/2H330kbnttttMq1atzJ133un8vQv42rlzp+nXr58JDw83N998sxk/frzz0XyO5/Jx5MgR52PixjDPZemrr74y/fr1M5GRkaZt27Zm9uzZJjs72xjDPJel3Nxc89RTT5m2bduaG2+80YwbN86cOnXKGGPK5bWZbxMHAADW4buoAACAdQg4AADAOgQcAABgHQIOAACwDgEHAABYh4ADAACsQ8DBBW3fvl0PPvigOnTooLCwMLVt21bB52UAABUuSURBVFYjR47U1q1br2g/vvzyS4WGhpZ7u1eqndJ45513FBoaqh9//PGStp85c6b69u3r89dB//rXv+qf//yn87hnz57Kycm5rH4uXLhQ0dHRP5m/+JyUlKTQ0FD97//+79Xuyk/GlClTfL6N/Kdm0KBBGjp0qHJzc9W7d28988wzV7tLsAABB8XaunWrhgwZonr16mnhwoX66KOP9PLLLysvL0/3339/iV9VX5aioqL0+eef/2ROouVh4cKFmjJlSpnUtWHDBq1du1bPP/+8goKCnOXx8fFyu92SpNzcXBUUFCg4OPiS28nOzta8efN02223acOGDZfd75+qnTt3qlOnTle7G9YLCgrSvHnztGbNGn388cdXuzv4mSPgoFjLly9Xs2bN9Nhjj6lFixZq0KCBfvvb32rBggVq1arVFQ04QUFBCgkJUcWKFa9Ym1fazp07y6SenJwczZkzRwMHDlSjRo18yr7//ns1bdpUknT48GE1a9bsstrKzMxUfn6+2rRpU6Qtm5TVvkHJmjZtqv79++vpp5/WuXPnrnZ38DNGwEGxcnJynBOYt6CgIK1Zs8bniyxTUlI0fvx4dejQQTfeeKP69++vHTt2OOUFBQV68cUX1blzZ4WHh6t9+/b685//rMzMzFKV+7t1tHLlSt12220KCwtTu3btNHHiRJ04ccIpHzRokB555BGtW7dO3bp104033qg+ffpcdDB766231KtXL0VGRqp9+/aaO3euz22f0rSTlpamcePGKSoqSu3atdMLL7ygxYsXq2XLlk4d//73v7V27VqFhobqyy+/dLY9duyYhgwZooiICMXExOj111+/YH/ffvttnTp1SkOHDvVZnpWVpaCgICckHjx4UL/5zW8uWNe+ffs0fPhwRUVFKSIiQvfcc482b94s6fw+iYmJkSRNnTpVoaGhfut46aWX1K5dO3399dfq0aOHwsLC1L17d5/f0KdMmaJ7771Xf/vb3xQVFaU333yzxPY9lixZoltuuUUREREaNGiQEhISfMqnTJmirl27+izbsGGDQkNDnS/7k6RVq1ape/fuioiIUM+ePZ1bXC+99JJmz56to0ePKjQ0VC+99JIkacWKFbr99tsVERGhdu3aady4cTp+/Hixczlo0CD96U9/0sqVK3XLLbcoPDxc/fv317fffuusExoaqgULFvhsN3z4cA0aNMhnncWLF+u+++5TeHi4cywW139vW7ZscfbB7bff7vMczcjI0PTp03XzzTcrLCxMnTt31vz58+X9x+63bNmi/v37Kzo6WtHR0RowYIC2b9/ulOfm5mru3Lnq3r27wsPDddttt+mtt97y6cP+/fvVt29fhYeHq3Pnznr77beL9HP48OFKTk7Wu+++W+x8AiUq+2+bgC2WL19u3G63GTRokPnss8/M2bNn/a6Xk5Njunfvbnr06GG+/PJLEx8fbyZNmmQiIyNNYmKiMcaYNWvWmMjISPPJJ5+Yo0ePmq+++sp069bNTJs2rVTlX3zxhXG73ebrr782xhizatUqc8MNN5gVK1aYw4cPmy1btphu3bqZu+66yxQUFBhjjBk4cKDp2rWrGTt2rNm/f7+Ji4szt99+u7nzzjuLHXPhdt555x3jdrvNSy+9ZL7//nvz4Ycfmptuusk8/vjjzjalaWfcuHGmTZs25qOPPjIHDx40jzzyiOnatau54YYbjDHGnDp1ynTt2tWMGzfOHD9+3OTk5Ji3337buN1uM3ToULNx40bz3XffmYcffti0aNHCJCUlFTuGP/zhD+a+++5zHn/55ZemY8eOJiYmxkRGRpqOHTuajh07mtatW5t27dqZjh07+v1ywJSUFNOmTRszevRos3fvXnPo0CEzbdo007JlS7N3716Tk5Nj9u/fb9xut1myZIk5fvy43/68+OKLplWrVmbw4MFm69at5sCBA2bUqFEmPDzcHDt2zBhjzOTJk03Hjh3N6NGjTWJiojl9+nSJ7RtjzMaNG43b7TZ//etfzffff28+/vhj07NnT+N2u826deucurt06eLTp/Xr1xu3222OHDlijDHmrbfeMuHh4eadd94xCQkJZvny5SY0NNR8+umnJjMz00yaNMl06NDBHD9+3GRmZprNmzebFi1amLVr15qkpCSza9cu069fPzNkyJBi98vAgQNN+/btzUMPPWTi4+PNtm3bTLdu3UyPHj2cY9btdpuXX37ZZ7thw4aZgQMHOo/dbrfp0qWLef31101SUpIpKCi4YP89cxATE2NGjhxp4uLizJ49e0yPHj185uWRRx4xsbGxZtu2bebo0aPmgw8+MOHh4WbVqlXGmPNf/hkZGWmefvppc/jwYXPo0CEzffp007p1a5OVlWWMMWbKlCmmTZs25t133zXff/+9ee2110yLFi3Mhg0bjDHnXytuvfVWc9ddd5m4uDizb98+M2LECNOuXbsic3f33Xeb0aNHFzufQEkIOChWfn6+ee6550xYWJhxu92mVatW5t577zWvvvqqzwlxw4YNxu12OycdY86/kMXExJg5c+YYY4x5/PHHiwSLxMRE8+2335aqvHDw6Natm5kwYYLP+ps3bzZut9vs2LHDGHP+hNKmTRvnxdcYYxYuXGjcbrc5c+aM3zEXbue2224zo0aN8lln+fLlpmXLliY9Pb1U7WRlZZlWrVqZBQsWOOV5eXk+AccYY7p3724mT57sPPYEnPfee89Ztm/fPuN2u82//vUvv/03xpioqCjzl7/8pcjyZcuWmYULFzqPhw0bZhISEoqt55VXXjHh4eHm9OnTzrL8/HzToUMH8+ijjxpjjDl+/Lhxu93m7bffLraeF1980bjdbvP55587y5KTk01oaKhZsWKFMeb8CTg0NNQJPKVtf8KECaZ79+4+7a1evfqiA84dd9xhpk6d6rPO3LlzzRtvvGGMMebRRx81HTt2dMoWLlxooqOjfb50OCUlxezbt6/YeRg4cGCR8bz77rvG7Xab+Ph4Y0zpA87QoUN91imp/5MnTzYtWrRwvhDVGGOWLl1q3G6382WHycnJRYLzwIEDzYMPPmiMMWbXrl3G7Xb7fJlkTk6O2bZtm8nJyTHJycmmRYsWZvHixT51jB071vTu3dsYU/Q5aowxGRkZJiwsrEjAefrpp03btm0NcKm4RYViVahQQQ8//LA2bdqkOXPm6M4771RiYqJzCTouLk6StGvXLtWoUUM33HCDs21QUJCio6O1b98+SVJsbKwOHTqkP/zhD3rvvfd04sQJNW7cWL/+9a9LVe4tMzNThw8fVlRUlM/yiIgISdKePXucZc2bN1eVKlWcx7Vq1ZJ0/nJ8STIzM/Xdd9+pXbt2Psvbtm2rvLw8xcfHl6qdY8eO6dy5c86beyUpICBAv/vd70rsgySFhYUVqTcrK8vvumfOnFFWVpbq1KlTpGzv3r0+dSUlJalx48bFthsXF6ff/OY3qlatmrOsQoUKatWqlfbu3VuqvnvzfoN4vXr1VLt2bR09etRZVqtWLdWvX/+i2j906JDPcSdJkZGRF9Wv7OxsHTp0SK1atfJZPnHiRPXt29fvNr/73e+Um5urAQMG6M0339TRo0dVt25dtWjR4oJtFR6P5xblDz/8cFF99mx3Mf2vU6eO6tWr5zwufCy5XC699tpr6tatm1q3bq2oqCht27ZN6enpTt8bNmyohx56SIsWLdK+fftUsWJFRUdHKygoSHFxcSooKPD7fImPj5cxRocOHZIkn312zTXXqHnz5kXGGBISorS0NJ/bwcDFCLzaHcBP37XXXqvevXurd+/eKigo0CeffKIpU6Zo1qxZWr16tTIzM5WRkVEkcOTm5upXv/qVJKljx4567bXXtGzZMk2fPl05OTmKiYnRjBkz1LBhwxLLvXnel+N9opCkqlWrSvI9+VeqVMlnHZfLJUk+7ysojqedZ599Vs8//7yz3LOt9/t9LtROWlqapPMv5N5q1KhRYh8K111S/0+fPi2p6NxI5wPO1KlTJZ1/X0/dunWd+vzJzMz0W0/VqlWduSktl8tVpK4qVao4/fXUe7HtZ2VlFZl776BZGp4TeOXKlUu9TatWrbRy5Ur94x//0Jw5c5SZmamoqCg98cQTFww5xR2zpQnc/raTSt//Cx2jxhgNHz5caWlpmjp1qtxutypWrKhp06Y561epUkWrV6/WokWLtGLFCj333HNq2LChJk6cqNtvv93ZJ/379/c5rvLy8nTu3DmdOnVKWVlZcrlcRT6552+fVa9eXdL5Y7p27dolzglQGAEHxcrJyZHL5fL5mHGFChXUpUsX3X333c4bQa+55hrVrFnT75tfAwP/e4jFxMQoJiZGOTk52rRpk2bPnq3x48frjTfeKFW5h+ck4X1y9H7s76R4KTz1jBw50u/fECnti67nxbzw35vxBJ+y5AlR3gFk4sSJ+vjjj3X27FnFxsZKOv+m7ry8PEVFRalLly569tln/dblfYXF4/Tp00XCWkmMMcrOzvY5yWZlZTknseLGUlL7lStXVnZ2tk954bDgcrmKBMIzZ844P1977bVyuVwXHdoiIiI0b948nTt3Tl999ZXmzp2rP/7xj9q4caMqVPB/cfzs2bM+jz1h3Hse/PXV+3lU2KX231t8fLzi4+P13HPP6Y477nCWnz592ieI16tXT9OnT9f06dO1f/9+vfLKK5owYYKuv/56Z5/Mnz/f75XB6tWrq0qVKjLGKCcnxyfk+Asxnv14scca4MEtKvh14sQJtWnTRkuWLPFbnpiY6FzujoiIUHp6uipWrKimTZs6/6Tzl5kl6fPPP3cuTwcHB6tr164aPHiwDh48WKpyb9WqVVOzZs18Pr0h/fejvOHh4Zc5+v+28+tf/1rHjh3zGVdISIgCAgJKHaSaNGkil8vlc+ssNzdXn332WZF1S3Nl6UKqVKmiqlWr6uTJk86ySZMmae7cubr11lu1bt06rVu3Tn369NGECRO0bt06TZo0yW9dYWFhOnjwoE9gyMvLU1xc3CXNsfcn4I4dO6aTJ086V/gutf1f/epXzq1Sj//85z8+j6tWrVok9Ozatcv5OSgoSNdff32R42nmzJmaN2+e89h732zfvl27d++WJFWsWFExMTEaM2aMjh8/7lxR8Sc+Pt6nL56+e27FVqtWzaf8zJkzzvOiOKXt/4V4Po597bXXOsv279/v3FqSpISEBH366adOeYsWLTRjxgwVFBTo22+/VVhYmCpUqKAff/zR5/lSqVIl1axZU4GBgc7+9v6EYUpKis8nyTxSU1NVo0YNn1+wgItBwIFfderU0b333qsXXnhBzz//vL755hv98MMP2r17t2bMmKFPPvlEo0ePliR17txZTZo00YQJE7R9+3YlJSXp7bffVq9evZyPqr7zzjsaN26cvvjiCx07dky7d+/We++9p7Zt25aqvLA//OEP+te//qUlS5YoMTFRn3/+uWbOnKm2bduWWcCRzn9cdd26dVq6dKkSEhL0zTffaPz48RoyZEip3xtQvXp1xcTEaNmyZdq8ebMOHTqkqVOnFrklU6NGDe3du1f79u3zuf11sVq3bq1t27Y5j0NCQpSSkqKbb77ZOel8//336tKlixPY/OnTp4+qVKmihx9+2DnZTZ06VRkZGRowYMBF9SkgIEB///vftXXrVh08eFAzZsxQ5cqV1a1bt2K3KU37PXr0UGJiol588UUdPnxYH374odavX+9TT1hYmNLT07Vs2TIdOXJEa9as8Qk4knT//ffrX//6l9asWaOkpCStWbNGq1atct6zVKNGDaWmpmrr1q06cuSIPv30Uz344IP69NNP9cMPP2j//v164403dP311/uEhMKqVaum6dOnKz4+Xtu3b9fLL7+sqKgo54pHq1at9MEHH2jnzp06ePCgpk6d6rxX5kJK6n9JfvWrX+maa67RqlWrlJiYqM2bN2vatGnq1KmTEhMTlZCQoMTERI0ZM0YrVqzQkSNHlJiYqEWLFik4OFgRERGqW7euevbsqblz5+rjjz9WUlKS/u///k+DBw/WU089JUlq166d6tSpo7lz52rv3r3au3evpk2b5neM27dvV5s2bUrVf8AfblGhWNOmTdMNN9ygt99+W2+++abS09NVrVo1RURE6LXXXlP79u0lnb/ismTJEj3zzDMaMWKEzpw5oyZNmmjy5MnOmxxnzJihOXPm6JFHHlFaWppq1aqlmJgYPfLII6UqL6xv3746d+6cli5dqmeffVY1atRQ586dNXHixDKdgz59+sgYo8WLF+vZZ59VpUqVFBMToyVLllzUb5azZs3StGnT9OCDD+raa6/V0KFD1bhxYy1dutRZZ9iwYXrsscd07733avbs2Zfc51tvvVWzZ89WWlqaatasKen8ycLzd3Hy8/P1ww8/OFfZilO7dm0tXbpUzzzzjPr37y9jjMLDw7V48WK/bwotybhx4zRz5kwdOnRIDRs21AsvvHDBW1Slab9bt26aMGGCli1bpldffVURERHOV1R49OjRwwkT8+bNU6dOnTRhwgSNHDnSWeeuu+5SRkaGFi1apFmzZqlJkyaaOXOmunTpIknq3bu3PvzwQw0dOlT33nuvJk2apPz8fD355JM6ceKEatSoodatWxf5GzaFXX/99br55ps1YsQInThxQhERET77+rHHHtOf//xnDRkyRLVr19aoUaNUuXJlv7fqvJXU/5JUrVpVc+fO1Zw5c9SzZ0+1aNFCTz31lM6ePasHH3xQ/fv315YtW/Tkk086z7mKFSsqNDRUf/vb39SgQQNJ568aPf/885oxY4ZOnDihWrVqqWfPnho3bpyk8+8DWrBggZ588kndc889qlu3rkaPHq3g4GCf24Y//vij9uzZ4wQj4FK4zOVeEwdQopycHJ09e9YJHJI0fvx4HTp0SO+9916ZtpWdna0uXbqob9++zonlanrppZf0yiuvXNInr2wyaNAgBQQEFHvbF//17LPP6v3339cHH3zALSpcMm5RAVfApEmT1KtXL23ZskVJSUl655139OGHH+ruu+8u87YqVaqkyZMna/ny5Rf98WPgajty5IhWrVqlyZMnE25wWbhFBVwBM2fO1DPPPKOJEycqIyND1113ncaPH+/zJ/jLUs+ePbVr1y499NBDWrlypdXf4QV75Obmaty4cerfv7+6d+9+tbuDnzluUQEAAOtwiwoAAFiHgAMAAKxDwAEAANYh4AAAAOsQcAAAgHX+H4PoEHJndZDKAAAAAElFTkSuQmCC\n", "text/plain": [ "
" ] }, "metadata": { "tags": [] }, "output_type": "display_data" } ], "source": [ "session_lengths = df.groupby(\"CustomerID\").count()['InvoiceNo'].values\n", "\n", "fig = plt.figure(figsize=(8,6))\n", "plt.xticks(fontsize=14)\n", "\n", "ax = sns.boxplot(x=session_lengths, color=cldr_colors[2])\n", "\n", "for patch in ax.artists:\n", " r, g, b, a = patch.get_facecolor()\n", " patch.set_facecolor((r, g, b, .7))\n", " \n", "plt.xlim(0,600)\n", "plt.xlabel(\"Session length (# of products purchased)\", fontsize=16);\n", "\n", "plt.tight_layout()\n", "plt.savefig(\"session_lengths.png\", transparent=True, dpi=150)" ] }, { "cell_type": "code", "execution_count": 62, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "a6aVEx4uiIIi", "outputId": "ecf53307-79cf-444e-9b90-e0e049bfee97" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Minimum session length: \t 3\n", "Maximum session length: \t 7983\n", "Mean session length: \t \t 96.03967879074162\n", "Median session length: \t \t 44.0\n", "Total number of purchases: \t 406632\n" ] } ], "source": [ "print(\"Minimum session length: \\t\", min(session_lengths))\n", "print(\"Maximum session length: \\t\", max(session_lengths))\n", "print(\"Mean session length: \\t \\t\", np.mean(session_lengths))\n", "print(\"Median session length: \\t \\t\", np.median(session_lengths))\n", "print(\"Total number of purchases: \\t\", np.sum(session_lengths))" ] }, { "cell_type": "markdown", "metadata": { "id": "pJ7qK-MCXBwx" }, "source": [ "### Sessionization" ] }, { "cell_type": "code", "execution_count": 56, "metadata": { "id": "V_tNkOldV7t6" }, "outputs": [], "source": [ "def construct_session_sequences(df, sessionID, itemID, save_filename):\n", " \"\"\"\n", " Given a dataset in pandas df format, construct a list of lists where each sublist\n", " represents the interactions relevant to a specific session, for each sessionID. \n", " These sublists are composed of a series of itemIDs (str) and are the core training \n", " data used in the Word2Vec algorithm. \n", " This is performed by first grouping over the SessionID column, then casting to list\n", " each group's series of values in the ItemID column. \n", " INPUTS\n", " ------------\n", " df: pandas dataframe\n", " sessionID: str column name in the df that represents invididual sessions\n", " itemID: str column name in the df that represents the items within a session\n", " save_filename: str output filename \n", " \n", " Example:\n", " Given a df that looks like \n", " SessionID | ItemID \n", " ----------------------\n", " 1 | 111\n", " 1 | 123\n", " 1 | 345\n", " 2 | 045 \n", " 2 | 334\n", " 2 | 342\n", " 2 | 8970\n", " 2 | 345\n", " \n", " Retrun a list of lists like this: \n", " sessions = [\n", " ['111', '123', '345'],\n", " ['045', '334', '342', '8970', '345'],\n", " ]\n", " \"\"\"\n", " grp_by_session = df.groupby([sessionID])\n", "\n", " session_sequences = []\n", " for name, group in grp_by_session:\n", " session_sequences.append(list(group[itemID].values))\n", "\n", " pickle.dump(session_sequences, open(save_filename, \"wb\"))\n", " return session_sequences" ] }, { "cell_type": "code", "execution_count": 63, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 171 }, "id": "jiswHnavV7wt", "outputId": "a4078ea5-9b1c-4253-aa38-97ab6628ced3" }, "outputs": [ { "data": { "application/vnd.google.colaboratory.intrinsic+json": { "type": "string" }, "text/plain": [ "'85116 --> 22375 --> 71477 --> 22492 --> 22771 --> 22772 --> 22773 --> 22774 --> 22775 --> 22805 --> 22725 --> 22726 --> 22727 --> 22728 --> 22729 --> 22212 --> 85167B --> 21171 --> 22195 --> 84969 --> 84997C --> 84997B --> 84997D --> 22494 --> 22497 --> 85232D --> 21064 --> 21731 --> 84558A --> 20780 --> 20782 --> 84625A --> 84625C --> 85116 --> 20719 --> 22375 --> 22376 --> 20966 --> 22725 --> 22726 --> 22727 --> 22728 --> 22729 --> 22196 --> 84992 --> 84991 --> 21976 --> 22417 --> 47559B --> 21154 --> 21041 --> 21035 --> 22423 --> 84969 --> 22134 --> 21832 --> 22422 --> 22497 --> 21731 --> 84558A --> 22376 --> 22374 --> 22371 --> 22375 --> 20665 --> 23076 --> 21791 --> 22550 --> 23177 --> 22432 --> 22774 --> 22195 --> 22196 --> 21975 --> 21041 --> 22423 --> 22699 --> 21731 --> 22492 --> 84559A --> 84559B --> 16008 --> 22821 --> 22497 --> 23084 --> 23162 --> 23171 --> 23172 --> 23170 --> 23173 --> 23174 --> 23175 --> 22371 --> 22375 --> 85178 --> 17021 --> 23146 --> 22196 --> 84558A --> 51014C --> 22727 --> 22725 --> 23308 --> 23297 --> 22375 --> 22374 --> 22376 --> 22371 --> 22372 --> 21578 --> 20719 --> 22727 --> 23146 --> 23147 --> 47559B --> 84992 --> 84991 --> 21975 --> 22423 --> 23175 --> 84558A --> 22992 --> 21791 --> 23316 --> 23480 --> 21265 --> 21636 --> 22372 --> 22375 --> 22371 --> 22374 --> 22252 --> 22945 --> 22423 --> 23173 --> 47580 --> 47567B --> 47559B --> 22698 --> 22697 --> 84558A --> 23084 --> 21731 --> 23177 --> 21791 --> 23508 --> 23506 --> 23503 --> 22992 --> 22561 --> 22492 --> 22621 --> 23146 --> 23421 --> 23422 --> 23420 --> 22699 --> 22725 --> 22728 --> 22726 --> 22727 --> 21976 --> 22417 --> 23308 --> 84991 --> 84992 --> 22196 --> 22195 --> 20719 --> 23162 --> 22131 --> 23497 --> 23552 --> 21064 --> 84625A --> 21731 --> 23084 --> 20719 --> 21265 --> 23271 --> 23506 --> 23508'" ] }, "execution_count": 63, "metadata": { "tags": [] }, "output_type": "execute_result" } ], "source": [ "filename = os.path.join(ECOMM_PATH, ECOMM_FILENAME.replace(\".csv\", \"_sessions.pkl\"))\n", "sessions = construct_session_sequences(df, \"CustomerID\", \"StockCode\", save_filename=filename)\n", "' --> '.join(sessions[0])" ] }, { "cell_type": "code", "execution_count": 75, "metadata": { "id": "7nvizM2Tl4_n" }, "outputs": [], "source": [ "def load_ecomm(filename=None):\n", " \"\"\"\n", " Checks to see if the processed Online Retail ecommerce session sequence file exists\n", " If True: loads and returns the session sequences\n", " If False: creates and returns the session sequences constructed from the original data file\n", " \"\"\"\n", " original_filename = os.path.join(ECOMM_PATH, ECOMM_FILENAME)\n", " if filename is None:\n", " processed_filename = original_filename.replace(\".csv\", \"_sessions.pkl\")\n", " if os.path.exists(processed_filename):\n", " return pickle.load(open(processed_filename,'rb'))\n", "\n", " df = load_original_ecomm(original_filename)\n", " df = preprocess_ecomm(df)\n", " session_sequences = construct_session_sequences(df, \"CustomerID\", \"StockCode\",\n", " save_filename=original_filename)\n", " return session_sequences" ] }, { "cell_type": "markdown", "metadata": { "id": "4IgJEMr7XHB2" }, "source": [ "### Splitting" ] }, { "cell_type": "code", "execution_count": 40, "metadata": { "id": "GpIVmG6QV7rG" }, "outputs": [], "source": [ "def train_test_split(session_sequences, test_size: int = 10000, rng=rng):\n", " \"\"\"\n", " Next Event Prediction (NEP) does not necessarily follow the traditional train/test split. \n", " Instead training is perform on the first n-1 items in a session sequence of n items. \n", " The test set is constructed of (n-1, n) \"query\" pairs where the n-1 item is used to generate \n", " recommendation predictions and it is checked whether the nth item is included in those recommendations. \n", " Example:\n", " Given a session sequence ['045', '334', '342', '8970', '128']\n", " Training is done on ['045', '334', '342', '8970']\n", " Testing (and validation) is done on ['8970', '128']\n", " \n", " Test and Validation sets are constructed to be disjoint. \n", " \"\"\"\n", "\n", " ## Construct training set\n", " # use (1 st, ..., n-1 th) items from each session sequence to form the train set (drop last item)\n", " train = [sess[:-1] for sess in session_sequences]\n", "\n", " if test_size > len(train):\n", " print(\n", " f\"Test set cannot be larger than train set. Train set contains {len(train)} sessions.\"\n", " )\n", " return\n", "\n", " ### Construct test and validation sets\n", " # sub-sample 10k sessions, and use (n-1 th, n th) pairs of items from session_squences to form the\n", " # disjoint validaton and test sets\n", " test_validation = [sess[-2:] for sess in session_sequences]\n", " index = np.random.choice(range(len(test_validation)), test_size * 2, replace=False)\n", " test = np.array(test_validation)[index[:test_size]].tolist()\n", " validation = np.array(test_validation)[index[test_size:]].tolist()\n", "\n", " return train, test, validation" ] }, { "cell_type": "code", "execution_count": 42, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "dR2iVWuscfvt", "outputId": "218a61ca-53c1-4381-8af0-b00651e97fe5" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "4234\n", "4234 1000 1000\n" ] } ], "source": [ "print(len(sessions))\n", "train, test, valid = train_test_split(sessions, test_size=1000)\n", "print(len(train), len(valid), len(test))" ] }, { "cell_type": "markdown", "metadata": { "id": "nrfW72QYXL3Z" }, "source": [ "### Metrics" ] }, { "cell_type": "code", "execution_count": 64, "metadata": { "id": "9gZKiSz4fALx" }, "outputs": [], "source": [ "def recall_at_k(test, embeddings, k: int = 10) -> float:\n", " \"\"\"\n", " test must be a list of (query, ground truth) pairs\n", " embeddings must be a gensim.word2vec.wv thingy\n", " \"\"\"\n", " ratk_score = 0\n", " for query_item, ground_truth in test:\n", " # get the k most similar items to the query item (computes cosine similarity)\n", " neighbors = embeddings.similar_by_vector(query_item, topn=k)\n", " # clean up the list\n", " recommendations = [item for item, score in neighbors]\n", " # check if ground truth is in the recommedations\n", " if ground_truth in recommendations:\n", " ratk_score += 1\n", " ratk_score /= len(test)\n", " return ratk_score\n", "\n", "\n", "def recall_at_k_baseline(test, comatrix, k: int = 10) -> float:\n", " \"\"\"\n", " test must be a list of (query, ground truth) pairs\n", " embeddings must be a gensim.word2vec.wv thingy\n", " \"\"\"\n", " ratk_score = 0\n", " for query_item, ground_truth in test:\n", " # get the k most similar items to the query item (computes cosine similarity)\n", " try:\n", " co_occ = collections.Counter(comatrix[query_item])\n", " items_and_counts = co_occ.most_common(k)\n", " recommendations = [item for (item, counts) in items_and_counts]\n", " if ground_truth in recommendations: \n", " ratk_score +=1\n", " except:\n", " pass\n", " ratk_score /= len(test)\n", " return ratk_score\n", "\n", "\n", "def hitratio_at_k(test, embeddings, k: int = 10) -> float:\n", " \"\"\"\n", " Implemented EXACTLY as was done in the Hyperparameters Matter paper. \n", " In the paper this metric is described as \n", " • Hit ratio at K (HR@K). It is equal to 1 if the test item appears\n", " in the list of k predicted items and 0 otherwise [13]. \n", " \n", " But this is not what they implement, where they instead divide by k. \n", " What they have actually implemented is more like Precision@k.\n", " However, Precision@k doesn't make a lot of sense in this context because\n", " there is only ONE possible correct answer in the list of generated \n", " recommendations. I don't think this is the best metric to use but \n", " I'll keep it here for posterity. \n", " test must be a list of (query, ground truth) pairs\n", " embeddings must be a gensim.word2vec.wv thingy\n", " \"\"\"\n", " hratk_score = 0\n", " for query_item, ground_truth in test:\n", " # If the query item and next item are the same, prediction is automatically correct\n", " if query_item == ground_truth:\n", " hratk_score += 1 / k\n", " else:\n", " # get the k most similar items to the query item (computes cosine similarity)\n", " neighbors = embeddings.similar_by_vector(query_item, topn=k)\n", " # clean up the list\n", " recommendations = [item for item, score in neighbors]\n", " # check if ground truth is in the recommedations\n", " if ground_truth in recommendations:\n", " hratk_score += 1 / k\n", " hratk_score /= len(test)\n", " return hratk_score*1000\n", "\n", "\n", "def mrr_at_k(test, embeddings, k: int) -> float:\n", " \"\"\"\n", " Mean Reciprocal Rank. \n", " test must be a list of (query, ground truth) pairs\n", " embeddings must be a gensim.word2vec.wv thingy\n", " \"\"\"\n", " mrratk_score = 0\n", " for query_item, ground_truth in test:\n", " # get the k most similar items to the query item (computes cosine similarity)\n", " neighbors = embeddings.similar_by_vector(query_item, topn=k)\n", " # clean up the list\n", " recommendations = [item for item, score in neighbors]\n", " # check if ground truth is in the recommedations\n", " if ground_truth in recommendations:\n", " # identify where the item is in the list\n", " rank_idx = (\n", " np.argwhere(np.array(recommendations) == ground_truth)[0][0] + 1\n", " )\n", " # score higher-ranked ground truth higher than lower-ranked ground truth\n", " mrratk_score += 1 / rank_idx\n", " mrratk_score /= len(test)\n", " return mrratk_score\n", "\n", "\n", "def mrr_at_k_baseline(test, comatrix, k: int = 10) -> float:\n", " \"\"\"\n", " Mean Reciprocal Rank. \n", " test must be a list of (query, ground truth) pairs\n", " embeddings must be a gensim.word2vec.wv thingy\n", " \"\"\"\n", " mrratk_score = 0\n", " for query_item, ground_truth in test:\n", " # get the k most similar items to the query item (computes cosine similarity)\n", " try:\n", " co_occ = collections.Counter(comatrix[query_item])\n", " items_and_counts = co_occ.most_common(k)\n", " recommendations = [item for (item, counts) in items_and_counts]\n", " if ground_truth in recommendations: \n", " rank_idx = (\n", " np.argwhere(np.array(recommendations) == ground_truth)[0][0] + 1\n", " )\n", " mrratk_score += 1 / rank_idx\n", " except:\n", " pass\n", " mrratk_score /= len(test)\n", " return mrratk_score" ] }, { "cell_type": "markdown", "metadata": { "id": "rxY8Dbfzshsv" }, "source": [ "### Baseline analysis" ] }, { "cell_type": "code", "execution_count": 77, "metadata": { "id": "ZPmpUqm_smzm" }, "outputs": [], "source": [ "def association_rules_baseline(train_sessions):\n", " \"\"\"\n", " Constructs a co-occurence matrix that counts how frequently each item \n", " co-occurs with any other item in a given session. This matrix can \n", " then be used to generate a list of recommendations according to the most\n", " frequently co-occurring items for the item in question. \n", "\n", " These recommendations must be evaluated using the \"_baseline\" recall/mrr functions in metrics.py\n", " \"\"\"\n", " comatrix = collections.defaultdict(list)\n", " for session in train_sessions:\n", " for (x, y) in itertools.permutations(session, 2):\n", " comatrix[x].append(y)\n", " return comatrix" ] }, { "cell_type": "code", "execution_count": 78, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "dCUFeOw4smwI", "outputId": "bb1aaf8c-b440-4e45-f69c-a6f473e193b6" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Recall@10: 0.143\n", "MRR@10: 0.06450158730158734\n" ] } ], "source": [ "# Construct a co-occurrence matrix containing how frequently \n", "# each item is found in the same session as any other item\n", "comatrix = association_rules_baseline(train)\n", "\n", "# Recommendations are generated as the top K most frequently co-occurring items\n", "# Compute metrics on these recommendations for each (query item, ground truth item)\n", "# pair in the test set\n", "recall_at_10 = recall_at_k_baseline(test, comatrix, k=10)\n", "mrr_at_10 = mrr_at_k_baseline(test, comatrix, k=10)\n", "\n", "print(\"Recall@10:\", recall_at_10)\n", "print(\"MRR@10:\", mrr_at_10)" ] }, { "cell_type": "markdown", "metadata": { "id": "sfCnGNBUXT-V" }, "source": [ "### Initializing Ray" ] }, { "cell_type": "code", "execution_count": 82, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "9uT3Woact705", "outputId": "36390597-5e7d-482b-ba7d-702bff389666" }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "2021-06-11 06:21:17,485\tINFO services.py:1274 -- View the Ray dashboard at \u001b[1m\u001b[32mhttp://127.0.0.1:8265\u001b[39m\u001b[22m\n" ] }, { "data": { "text/plain": [ "{'metrics_export_port': 42187,\n", " 'node_id': '36950d392a77ab525dbcfe5ffdb2a3629ba8446f2aed261213be1f1f',\n", " 'node_ip_address': '172.28.0.2',\n", " 'object_store_address': '/tmp/ray/session_2021-06-11_06-21-14_124261_62/sockets/plasma_store',\n", " 'raylet_ip_address': '172.28.0.2',\n", " 'raylet_socket_name': '/tmp/ray/session_2021-06-11_06-21-14_124261_62/sockets/raylet',\n", " 'redis_address': '172.28.0.2:6379',\n", " 'session_dir': '/tmp/ray/session_2021-06-11_06-21-14_124261_62',\n", " 'webui_url': '127.0.0.1:8265'}" ] }, "execution_count": 82, "metadata": { "tags": [] }, "output_type": "execute_result" } ], "source": [ "ray.init(num_cpus=4, ignore_reinit_error=True)" ] }, { "cell_type": "markdown", "metadata": { "id": "nEn7YBp94I3K" }, "source": [ "### Train word2vec with logging" ] }, { "cell_type": "code", "execution_count": 88, "metadata": { "id": "C_6624rRjSdW" }, "outputs": [], "source": [ "def train_w2v(train_data, params:dict, callbacks=None, model_name=None):\n", " if model_name: \n", " # Load a model for additional training. \n", " model = Word2Vec.load(model_name)\n", " else: \n", " # train model\n", " if callbacks:\n", " model = Word2Vec(callbacks=callbacks, **params)\n", " else:\n", " model = Word2Vec(**params)\n", " model.build_vocab(train_data)\n", "\n", " model.train(train_data, total_examples=model.corpus_count, epochs=model.epochs, compute_loss=True)\n", " vectors = model.wv\n", " return vectors\n", " \n", "\n", "def tune_w2v(config):\n", " ratk_logger = RecallAtKLogger(valid, k=config['k'], ray_tune=True)\n", "\n", " # remove keys from config that aren't hyperparameters of word2vec\n", " config.pop('dataset')\n", " config.pop('k')\n", " train_w2v(train, params=config, callbacks=[ratk_logger])\n", "\n", "\n", "class RecallAtKLogger(CallbackAny2Vec):\n", " '''Report Recall@K at each epoch'''\n", " def __init__(self, validation_set, k, ray_tune=False, save_model=False):\n", " self.epoch = 0\n", " self.recall_scores = []\n", " self.validation = validation_set\n", " self.k = k\n", " self.tune = ray_tune\n", " self.save = save_model\n", "\n", " def on_epoch_begin(self, model):\n", " if not self.tune:\n", " print(f'Epoch: {self.epoch}', end='\\t')\n", "\n", " def on_epoch_end(self, model):\n", " # method 1: deepcopy the model and set the model copy's wv to None\n", " mod = deepcopy(model)\n", " mod.wv.norms = None # will cause it recalculate norms? \n", " \n", " # Every 10 epochs, save the model \n", " if self.epoch%10 == 0 and self.save: \n", " # method 2: save and reload the model\n", " model.save(f\"{MODEL_DIR}w2v_{self.epoch}.model\")\n", " #mod = Word2Vec.load(f\"w2v_{self.epoch}.model\")\n", " \n", " ratk_score = recall_at_k(self.validation, mod.wv, self.k) \n", "\n", " if self.tune: \n", " tune.report(recall_at_k = ratk_score) \n", " else:\n", " self.recall_scores.append(ratk_score)\n", " print(f' Recall@10: {ratk_score}')\n", " self.epoch += 1\n", "\n", "\n", "class LossLogger(CallbackAny2Vec):\n", " '''Report training loss at each epoch'''\n", " def __init__(self):\n", " self.epoch = 0\n", " self.previous_loss = 0\n", " self.training_loss = []\n", "\n", " def on_epoch_end(self, model):\n", " # the loss output by Word2Vec is more akin to a cumulative loss and increases each epoch\n", " # to get a value closer to loss per epoch, we subtract\n", " cumulative_loss = model.get_latest_training_loss()\n", " loss = cumulative_loss - self.previous_loss\n", " self.previous_loss = cumulative_loss\n", " self.training_loss.append(loss)\n", " print(f' Loss: {loss}')\n", " self.epoch += 1" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "1TeCHqd30rcy" }, "outputs": [], "source": [ "expt_dir = '/content/big_HPO_no_distributed'" ] }, { "cell_type": "code", "execution_count": 87, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 466 }, "id": "xEJnbuOgsmLQ", "outputId": "29669c60-14ec-444c-831e-3c05cc101d3c" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Epoch: 0\t Recall@10: 0.181\n", " Loss: 731531.3125\n", "Epoch: 1\t Recall@10: 0.209\n", " Loss: 619873.5625\n", "Epoch: 2\t Recall@10: 0.213\n", " Loss: 511931.75\n", "Epoch: 3\t Recall@10: 0.218\n", " Loss: 568087.625\n", "Epoch: 4\t Recall@10: 0.216\n", " Loss: 587301.25\n" ] }, { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXQAAAD1CAYAAABA+A6aAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjIsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+WH4yJAAAgAElEQVR4nO3deVxU9f7H8dewCwPIKIMKuGGl4ZK4S+6oSZuZC14XTNzSbt2bdjVa7F6v3uxqt6630rxalpZoLqn1U4u0W0miZSpoGVQILsAIosMi2/z+ODBKKoMywxmGz/Px8GHDbB9O8uY733M+36/GZDKZEEIIUe85qV2AEEII65BAF0IIByGBLoQQDkICXQghHIQEuhBCOAgXNd60qKiIpKQk/P39cXZ2VqMEIYSod8rKysjOzqZjx454eHhcd78qgZ6UlMSECRPUeGshhKj3NmzYQPfu3a/7uiqB7u/vDyhFNWvWTI0ShBCi3jl//jwTJkwwZ+jvqRLoldMszZo1IygoSI0ShBCi3rrZVLWcFBVCCAchgS6EEA5CAl0IIRxEjQL91KlTREREsH79+uvuO3DgAKNHj2bcuHG88cYb5q8vWbKEcePGERUVxbFjx6xXsRBCiBuyeFK0oKCARYsW0adPnxve//e//501a9YQEBDAxIkTGT58ODk5OaSlpREXF0dqaiqxsbHExcVZvXghhBBXWRyhu7m5sXr1avR6/XX3paen4+vrS/PmzXFycmLAgAEkJCSQkJBAREQEACEhIeTl5WE0Gq1fvRBCdbICt/2wOEJ3cXHBxeXGD8vOzkan05lv63Q60tPTyc3NJTQ0tMrXs7Oz0Wq1ta+4IAdWD4L7lsJd99X+9YQQNVZWbiI128jxjDyOn8kj6UweJ85dwmQCnZcbOi83/Lzc0Hm6ovNyR+flip+XG0283PDzdDM/prGnG85OGrW/HYdTJ9ehW/U3uLsPePjCthkw8yvwa2W91xZCmJWWlZNSEd5JZ5QAP3nuMoUlZQA0cnXm7hY+jOkWhKuzEzkFxeTmF5OTX8yvBiO5+SUYr5Te8LU1GvBt5KoEvKfb1dCvuH3tL4fKr3u5OaPRyC+B6tQq0PV6PQaDwXw7MzMTvV6Pq6trla9nZWXdtLPpljm7wJh1sGoAbJ4CU3eDi7t1XluIBqqkrJyfM43m4FbC+xJXSssB8HRzJrSFD1E9g+kU6EvHQF9C/LUWR9lFJWVcLCghpyLoK0P/Qn5F+FfcTs8p4Gj6RXILiikpu/EA0M3FyRz+Oq+KTwCertf/MtAqfzf2dMPNpWFdyFerQA8KCsJoNJKRkUGzZs3Yt28fy5YtIzc3lxUrVhAVFUVycjJ6vd460y2VdG1g5BsQNxH2vgCRr1jvtYVwcMWl5ZzKvGwO76Szlzh57hLFFeGtdXfh7hY+TOzdqiK8fWjT1HJ434iHqzPNfJ1p5nv9QlI3YjKZuHyl1DzSr/yTW3DNL4H8EnLyr5B0MY+c/GLyCktu+nreHi7KSN/zmtCv/ON5/W2fRi71+lOAxUBPSkpi6dKlnDlzBhcXF/bs2cPgwYMJCgpi6NChvPTSS8ydOxeAyMhI2rRpQ5s2bQgNDSUqKgqNRsPChQutX3mHB6H3HPj2DWjVB0Ifsf57CFHPXSkt49R5Y0VwK1MnP567THGZEt7e7i6EBvoQ3acVHStG3m2aeOGk0vy2RqPBx8MVHw9XWjXxqtFzSsrKuVhQooS+UQn/3/8yyMkv5vylIk6eu8SF/GLzJ4/fc3bSVMz1u16d9rnBL4Nrzwd4uNrPirEaNTaJzsjIYMiQIcTHx9duLZfSYng3ErJ+hJlfQpMQ6xUpRD1TVFLGT+cvm4P7+Jk8fjp/2TyF4ePhQsdAXzoF+hJa8Xcrnadq4a2mguJSJezzS7iQf6Ui9JWRf05+ydVPCBVTQrkFxZTfJCk93ZyV0NdWDfrfB3/lNJFvI9fbPiFsKTtVWZzLalzcYPQ7sKofbI6GmM/AtZHaVQlhc0UlZZw8d4mks5dIqrji5FTmZUorUse3kSudAn2JubctHQN96BToS0udZ72eTrAmTzcXPN1cCPKr2ePLyk1cKiwh55rRf+U5gBxjcZUTwr/U4ITwSw+GEt23tfW+oQr1O9ABGgfDI6vgg7GwewE8+LraFQlhVYXFZZw4d4nks3nmywV/zjJSVhHefp6udAz0ZcZdbc0j8CC/RhLeVuTspMGvYtolpIbXd9zshHBuQTE92+gsv8BtqP+BDnDncLj3z/D1v6BlX+gyTu2KhLgtBcWlnDx3qSK4L5F0Jo+U7Kvh3cTLjY6BvkR0CKBjoA8dA30JbCzhbY9u9YSwNThGoAMMeh5OH4Rdf4LmXUDfXu2KhKhW/pVSTlSEd+Wcd2q20TxX21TrTqdAH4aFBphH3s19PSS8xU05TqA7u8DotbDyXmU+ffoX4Fazs+RC2JrxSinJZ652Vx4/k8cvhnwqL0nQe7vTKdCXEZ2a06kivAN83CW8xS1xnEAH8GkOj/4X3n8EPpkLI99SzkAIUYcuFZWQXDFdUhngv164Gt7NfDzoGOjDg11amMNb71N3H8uF43KsQAcIGQQD5sOXL0OrcAibpHZFwoHlFZaYR96V4f3bhQLz/S18PQgN9GVk10Bzh6W/t3Q2C9twvEAHGPAXOJ0An86DFl2hWUe1KxIO4GJBMUlnLpmDO+lsHmnXhHdg40Z0DPRhdLcgc5NOU62Et6g7jhnoTs7K1MvKfrBpMszYDx4+alcl6pHc/OIqo+6ks3mk5xSa7w/ya0SnQF/Gdr+6tonOy03FioVw1EAH0OqVk6TrHoCdTyn/LfPp4homk4kL+cWkZBlJzTZW/J1PSuZlzuYVmR/XUudJ58DG/KHn1bVNGntKeAv747iBDtA6HAY/D/F/U/67xzS1KxIqKCs3cSa3kJTsy6Rm5ZOSZSQlWwnxiwVXF3Zq5OpMiN6Lnm10dGiudFeGtvDF19NVxeqFqDnHDnSA8D9DWgLsfhYCuylz6sIhFZWU8ashv8qIOyXLyK+G/CqLMTXVutHWX0tkp+a089cSotfSTq+luY9Hg1zXRDgOxw90JycY9XbFfHo0zPwfNGqsdlWiFi4WFFcJ7NRsJcTTcwvMlwZqNBDs50mIvxf97mhKO72WEH/lj5/MdQsH5fiBDuCpgzHvwDsj4OM5MG69zKfbOZPJxNm8IlIrQjsl20hqxcjbYCw2P87NxYm2Tb3oFOTLI10DzcHd1t/LrpY1FaIuNIxABwjuCUP/Bnti4ds3oc8ctSsSKJstpF343TRJtpFfsvMpKC4zP863kSvt9FqGtA8gRO9lDu4gP0/Zm1KICg0n0AF6z4a0A/DZixDUE4J7qF1Rg3G5qMQ8NWK+oiTLSFpOgXnhKVAacUL0Wsb10BHir8xtt9NraeLlJm3wQljQsAJdo4GH/3N1P9JZXynTMcIqTCYTWZevKNMk5ssAlb8zL10xP87FSUPrpl7cGeBNZKfmyojb35u2/l54uTesf5JCWFPD++lp5Adj3oW1w2HbTBgfp5w4FTVWWlbO6ZwC84i7MrhTs41cLrq6qL/W3YUQfy/C2109KdlOr6WlzhNXZznmQlhbwwt0gMAwGL5EWRrgm9eg39NqV2SXCopL+SX7+ssAf7uQX2Vndr23O+30WkbeE2ieIgnx18pqgULUsYYZ6KA0GaUdgC8WQXAvpfGoATKZTORUdEsqV5Lkm68oOXPxaqu7kwZaNfEixF/L4A562lWMttv6a/FtJI03QtiDhhvoGo2yXd25o/DRVJj1NWhruLdUPVRebiIjt/B3128rIX5tt6SHqxMh/lq6t/ZjnH+wecTdqokn7i5yGaAQ9qzhBjooC3aNXQf/jYCt02DiVmVhLwdyLq+Qpz78gaMZF6t0SzbxciPEX8uIjs0rpkiUSwFb+DaSbkkh6qmGHegAzTpB5D9hxx/hf/+EgQvUrshq8gpKiF6byNmLRUzq3UoJbr2WdtItKYRDkkAH6DpJmU/f/7Iynx4ySO2Kaq2opIzp7x3mV0M+6x7rSd92TdUuSQhhYzW6dmzJkiWMGzeOqKgojh07VuW+zz//nEcffZTx48ezfv16AA4ePEjv3r2ZNGkSkyZNYtGiRdav3Jo0Grh/OfjfBVumwaVzaldUK2XlJp7aeIRDaTm8OvYeCXMhGgiLI/TExETS0tKIi4sjNTWV2NhY4uLiACgvL2fRokVs27aNxo0bM336dCIiIgDo2bMn//73v21bvTW5ecGYdbB6EGyJgck7lI2n6xmTycQLHyexJzmThQ/ezYNdWqhdkhCijlgcoSckJJhDOiQkhLy8PIxGIwC5ubn4+Pig0+lwcnKid+/eHDhwwLYV25K+PTzwGqR9A/sWq13NbVnxRQofHDzNrAEhPBbeRu1yhBB1yGKgGwwG/Pz8zLd1Oh3Z2dnm/87Pz+e3336jpKSEgwcPYjAYAEhJSWHWrFmMHz+eb775xkbl20CXcRAWDV+/Cqf2ql3NLfkw8TSvfnaKUWGBzL/vLrXLEULUsVueUzCZrnYIajQaXn75ZWJjY/H29iYoKAiA1q1b88QTTzBixAjS09OZPHkye/fuxc2tnlxZMWIpnPkets2AmV9B42C1K7LosxOZPLftOAPu9Gfpo52lQ1OIBsjiCF2v15tH3QBZWVn4+19twOnZsycffPABq1atwtvbm8DAQAICAoiMjESj0dCyZUuaNm1KZmambb4DW3BtpFyfXlYKHz0GpcWWn6Oi79JyeOKD7+kU6MubE8JknRQhGiiLP/nh4eHs2bMHgOTkZPR6PVqt1nz/tGnTuHDhAgUFBezbt48+ffqwY8cO1qxZA0B2djYXLlwgICDARt+CjTQJgYdXQMYhiP+r2tXc1M+Zl5n67mFaNG7E2ik9ZLVCIRowiz/9YWFhhIaGEhUVhUajYeHChWzduhVvb2+GDh3K2LFjmTp1KhqNhhkzZqDT6Rg8eDDz5s0jPj6ekpISXnrppfoz3XKt0EeU69MT/gMte0OHB9WuqIpzeYVEr03EzcWJ96b2pInWXe2ShBAq0piunRSvIxkZGQwZMoT4+HjzvLvdKr2iLLV74ReY+SXo7OPKkbyCEsauSuDMxUI2zuhNx0BftUsSQtiYpeyUyVZLXNyV9dM1wOZoKClSuyJzF+gvBiOrJnWTMBdCABLoNePXGkauVFZm3PucqqVUdoEm/pbD8rH3EC5doEKIChLoNdU+Evo8AYf+C0lbVCnBZDLxYkUX6IsP3M1D0gUqhLiGBPqtiHhJ2Vx6x5NgSKnzt1/xRQobDp5m5oC2TL3XPubyhRD2QwL9Vji7wph3wNkNNk2GkkLLz7GSjZVdoF0DmT+8fZ29rxCi/pBAv1W+QTBqNWQlw6fP1MlbfnYik9htx+l/pz9LR3eWDSiEEDckgX477oiAfvPgyPvww4c2favKLtCOgb68JV2gQohqSDrcroHPQqt74ZOnIeukTd4iJUvpAm3u6yFdoEIIiyTQb5ezC4xeo6yjvikarhit+vLn84qYvCYRV2cn3pvai6bSBSqEsEACvTa8m8Gja8BwCnb9GazUdJtXqOwFeqmolHcf60HLJp5WeV0hhGOTQK+ttgNgUCwc3wTfr6v1y13bBbpyonSBCiFqTgLdGvrNg5DB8Olf4Nwxy4+/ibJyE3/a+AOJvypdoPfeIV2gQoiak0C3Bicn5VJGT52y3kvRpVt+CZPJxEs7ktmdfJ4XpAtUCHEbJNCtxaspjF4LuWmw44+3PJ/+ny9SeP/bNGb2b0uMdIEKIW6DBLo1teoLQ16EE9shcXWNn7Yx8TTLK7tA75MuUCHE7ZFAt7a+T8Kd98GeWDjzncWHfy5doEIIK5FAtzYnJxj5lnJJ46YpUJh704d+l5bLEx9KF6gQwjokQWzBU6dsinH5HGyffcP59JSsy8SsO0QzH+kCFUJYhwS6rQR1h2GL4KdPlT1Jr3E+r4jotYdwcdJIF6gQwmok0G2p1yxlY+nPFsLpb4GrXaAXC4p597Ge0gUqhLAaCXRb0mjg4TegcTBsfoyii5nX7AXaXbpAhRBWJYFuax6+MGYdpoILpL49gUO/Glg2pot0gQohrE4CvQ6YmndhZ/M/ElpwiE2h3/LwPYFqlySEcEAS6HXgjX0pPJnSlWTdMHr88ib8+pXaJQkhHFCNAn3JkiWMGzeOqKgojh2ruvjU559/zqOPPsr48eNZv359jZ7TkGw6lM6yvad4pGsQHaavAV0IbImBy5lqlyaEcDAWAz0xMZG0tDTi4uJYvHgxixcvNt9XXl7OokWLWL16NRs2bGDfvn2cP3++2uc0JPEnM3l223H63dGUpY92xqmRD4xdpyzetSUGysvULlEI4UAsBnpCQgIREREAhISEkJeXh9Go7M6Tm5uLj48POp0OJycnevfuzYEDB6p9TkPxXVoucz74nrub+/DWxG64uVQc6oBQuH85/PYV7H9Z3SKFEA7FYqAbDAb8/PzMt3U6HdnZ2eb/zs/P57fffqOkpISDBw9iMBiqfU5DkJJlJGbdIQJ8PHjnsR5of98F2nUC3DMB/vdPSIlXp0ghhMO55X5z0zVt7BqNhpdffpnY2Fi8vb0JCgqy+BxHp3SBJlZ0gfa8eRdo5DI4ewS2TodZX4OPrH8uhKgdiyN0vV6PwWAw387KysLf3998u2fPnnzwwQesWrUKb29vAgMDLT7HUeUVljDlnatdoK2aeN38wW6eMGYdlBTBR1OhrKTuChVCOCSLgR4eHs6ePXsASE5ORq/Xo9VqzfdPmzaNCxcuUFBQwL59++jTp4/F5ziiopIyZrx3mNRsIysn1XAvUP874cHX4XQCfLHI9kUKIRyaxSmXsLAwQkNDiYqKQqPRsHDhQrZu3Yq3tzdDhw5l7NixTJ06FY1Gw4wZM9DpdOh0uuue48jKyk08vekHDv6aw+tR99Dvjlv4NNJ5DJw+AN+8Di37wF0jbFeoEMKhaUwqTHBnZGQwZMgQ4uPjbzrvXl+YTCYW7kjmvYQ0nr+/A9P6tb31FykpgjVD4eJpmPUVNG5p/UKFEPWepeyUTtFaenN/Ku8lpDGjf9vbC3MAVw9l/XRTOWyeAqXF1ixRCNFASKDXwqbD6fxzz0+MvKcFC2q7F2iTEHj4P8q2dZ+9aJ0ChRANigT6bYo/mcmzW5Uu0FdGd7HOXqB3Pwy9HoeDb8GJj2v/ekKIBkUC/TZ8f/omXaDWMPRvENgNPn4CLqRa73WFEA5PAv0WpWQZmfqu0gW6dsoNukBry8VNmU/XOMHmaOWEqRBC1IAE+i3IvFS1C9Tf20Z7gTZuCY+sgvPHYc+ztnkPIYTDkUCvoWv3An1nioUuUGu46z4IfwoOr4Vjm237XkIIhyCBXgOVXaApWUoXaKegOtoLdPALSrPRzqcg+1TdvKcQot6SQLfg2i7QZWO63FoXaG05u8Lotcp16psmQ3FB3b23EKLekUCvhslk4q87k/n0+Hmev78DI7uqsBeoTwsYtRqyf4RP59X9+wsh6g0J9GpUdoFO79fm9rtAraHdEOj/DPywAY6st/x4IUSDJIF+E5VdoA/f04JnR3RQuxwYuABa94NP5kFmstrVCCHskAT6DXzx49Uu0H9aqwu0tpyc4dE14OEDm6LhymW1KxJC2BkJ9N85cjqX2Ru+p0Nzb+t3gdaWd4AS6jmpypUvDWgnKCGEZXaUVupLzVa6QPXeHrwzpaf1u0CtoU0/GPQcJG1RrlEXQogKEugVMi8VMXlNIs627gK1hnufhnYRsHsBnP1B7WqEEHZCAh24VKR0geZWdIG2bmrjLtDacnKCR94Gz6bKei9FeWpXJISwAw0+0Kt0gU6swy7Q2vJqoizilZcBH8+R+XQhRMMO9Mou0G9/UbpA+99Zh12g1tCyF0S8BCd3wsGValcjhFBZgw10k8nE3yq6QJ+LVKkL1Br6PAF33Q97n4eMw2pXI4RQUYMN9Df3p7IuIY1p97Zhen8Vu0BrS6OBkW8oSwRsngIFOWpXJIRQSYMM9M3XdIHGRtpBF2htNfJT5tMvn4ftj0N5udoVCSFU0OACfd+PWSzYepx729lRF6g1BHaD4Uvg1G448G+1qxFCqKBBBfq1XaArJ9lZF6g19JwOd4+E+L9B2gG1qxFC1LEatUIuWbKEo0ePotFoiI2NpXPnzub7NmzYwI4dO3BycqJjx44899xzbN26lddff52WLVsC0LdvXx5//HHbfAc1VNkF6u/tbr9doLWl0cBDK+D8MfhoKsz8CrT17ModIcRts5hqiYmJpKWlERcXR2pqKrGxscTFxQFgNBpZs2YNe/fuxcXFhalTp/LDD0rnYmRkJPPnz7dt9TVU2QXqpKkHXaC15eEDY9bBfyNg2wyY8JGysJcQwuFZnHNISEggIiICgJCQEPLy8jAajQC4urri6upKQUEBpaWlFBYW4utrX405l4pKmPLOIaUL9LEe9t8Fag3NO0PkK5D6BXy1XO1qhBB1xGKgGwwG/Pz8zLd1Oh3Z2dkAuLu7M2fOHCIiIhg0aBBdunShTZs2gDKyj4mJITo6mhMnTtio/OpdKVW6QH/OvMzKid3oHNRYlTpUERYNncfBviXwy361qxFC1IFbnkg2XdNibjQaWbVqFbt370ar1RIdHc2PP/5Ily5d0Ol0DBw4kCNHjjB//nx27txp1cItKS838XTcUb79JYd/jauHXaC1pdHA/a8qi3dtmQazvgbvZmpXJYSwIYsjdL1ej8FgMN/OysrC318Jx9TUVIKDg9HpdLi5udG9e3eSkpIICQlh4MCBAHTt2pWcnBzKysps8x3cgMlk4m+7TvDJ8XPERrbnka5BdfbedsVdC2Pfg+J8pemorETtioQQNmQx0MPDw9mzZw8AycnJ6PV6tFotAIGBgaSmplJUVARAUlISrVu3ZvXq1ezatQuAU6dOodPpcHauuxNzb32ZyrsHfiPm3jZMV3MvUHugb69c+XI6Afa+oHY1QggbsjjlEhYWRmhoKFFRUWg0GhYuXMjWrVvx9vZm6NChxMTEMHnyZJydnenatSvdu3cnKCiIZ555ho0bN1JaWsrixYvr4nsBlC7QV3b/xENdWvBcZAc0GgdpHKqNTqOVdV4OvgVB3ZXbQgiHozGZ6n7d1YyMDIYMGUJ8fDxBQdabDtn3UxbT1h2mT9smrJ3Sw/Eah2qjrATWPQjnjsK0zyEgVO2KhBC3yFJ2OkziHTmdy+z139O+mTdvTQyTMP89Z1dlvRd3b4ibKJtiCOGAHCL1fqnoAm3q7cY7j/XA28NV7ZLsk3czpeno4mnYNksW8RLCwdT7QM+6VMTktZVdoL3Qe3uoXZJ9a9UHhi2Gnz6Fr19VuxohhBXV60C/VFRC9DuHyMkvZu2UHrRpCF2g1tBrJnQaA1/8HVLi1a5GCGEl9TbQr5SWMfO97/g58zJvTexGl+AG1AVaWxoNPPg66DvAlhjITVO7IiGEFdTLQC8vN/H0pqMk/HKBV0Z3ZkBD6wK1BjcvGLceystg02QoKVK7IiFELdW7QDd3gR47x7Mj2jMqrIF2gVpDkxB4ZBWc+wE+nad2NUKIWqp3gZ6TX8y7B35jangbZtTnvUDtRftI6DcPjrwP372rdjVCiFqod7s8NNG6882CwbTw9ZAuUGsZFAtnv4dPn4FmnZTt7IQQ9U69G6EDBDZuJGFuTU7O8Oga0DaDuMmQb7D8HCGE3amXgS5swFMH496D/Gxl+7ryulsdUwhhHRLo4qoWXeH+5fDrl/DFIrWrEULcIgl0UVXYJOg2Bb7+F5ys201JhBC1I4EurjfiFWgRBtseB8PPalcjhKghCXRxPRd3ZacjFzdlZcYrRrUrEkLUgAS6uLHGwTB6LRhOwY4noO6XzRdC3CIJdHFzbQfCkBcheRt8+6ba1QghLJBAF9UL/xO0f0DZj/S3r9WuRghRDQl0UT2NBka+Bbo2sHkKXDqrdkVCiJuQQBeWefgoKzMWF8CmaCgtVrsiIcQNSKCLmtF3gIf/AxmJsPc5tasRQtyABLqouY6joM8TkPg2HI1TuxohxO9IoItbE/EStAqHnU/B+eNqVyOEuIYEurg1zq4w5l1o1FhpOirMVbsiIUSFGgX6kiVLGDduHFFRURw7dqzKfRs2bGDcuHGMHz+exYsXA1BSUsLcuXMZP348EydOJD093fqVC/Vo9TBmHeSdgW2zoLxc7YqEqD+MWTZbzdRioCcmJpKWlkZcXByLFy82hzaA0WhkzZo1bNiwgQ8//JDU1FR++OEHdu3ahY+PDx9++CGzZs1i+fLlNileqKhlL7jvH3BqN3y1TO1qhLBPZaVw9ggcXKUsS/2vjrDsDvji7zZ5O4s7FiUkJBAREQFASEgIeXl5GI1GtFotrq6uuLq6UlBQgKenJ4WFhfj6+pKQkMDIkSMB6Nu3L7GxsTYpXqisxzTIOAT7lihL794xVO2KhFBXQQ5kHIb0g8qfM99BSYFyn3cLZSDUZw50HmeTt7cY6AaDgdDQUPNtnU5HdnY2Wq0Wd3d35syZQ0REBO7u7tx///20adMGg8GATqcDwMnJCY1GQ3FxMW5ubjb5JoRKNBp44DXITIYt02Dml+DXWu2qhKgbJhNcSLka3qcPguEn5T6Ns7KdY9dJENwTWvYGX9tvaH/Le4qarlmkyWg0smrVKnbv3o1WqyU6Opoff/yx2ucIB+PmCePeh7cHKidJYz4D10ZqVyWE9RUXKNMn6d9CeqLypzBHuc+jsRLcncdAcG8IDAM3rzov0WKg6/V6DIare0xmZWXh7+8PQGpqKsHBwebRePfu3UlKSkKv15OdnU379u0pKSnBZDLJ6NyR6drCqNXwwVjY9TSMfFMZvQtRn106C6crw/sgnD8G5aXKfU3ugLsilSmU4F7KbSf1Lxq0GOjh4eGsWLGCqKgokpOT0ev1aLVaAAIDA0lNTaWoqAgPDw+SkpIYMGAA7u7u7N69m379+rFv3z569epl829EqOzO4TBgPny5FIK6Q48YtSsSoubKSiHz+NXwTk+EvIqr81w8ILAb9H1SCe+gHuDVRN16b8JioIeFhREaGkpUVBQajWV6odMAABIxSURBVIaFCxeydetWvL29GTp0KDExMUyePBlnZ2e6du1K9+7dKSsr48CBA4wfPx43NzdefvnluvhehNoGLIAz38P/zYfmXZRgF8Ie1fTkZXBPCOikbPZSD2hMKkxwZ2RkMGTIEOLj4wkKsv2JAlGHCnKU+fSyEpj5P9D6q12RaOh+f/IyPRGyK871VZ68DO6lhHdwL2VzFztlKTtv+aSoENXy1CknSdcMg48eg0nbwVn+mYk6ZD55eU2Am09e+iqh3Wm08ndgN1VOXtqK/KQJ62veBR74F2x/HOL/CsMWqV2RcGSXzl69bPBmJy8rLx20k5OXtiKBLmzjnj8oc5QH/q3Mpd/9sNoVCUdQVgqZSVVH39edvPyjcumgHZ+8tBUJdGE79/0Dzh2F7bPBvz3436V2RaK+KcyF9EMOd/LSViTQhe24uMPY92BVf6XpaPoX4O6tdlXCXtXk5GVl52VwL6XzUvodqpBAF7blGwhj3oH3HlZG6mPfkx9Coahy8rLi+u+bnbxsEQbuWnXrrQck0IXttekPEX+Fz16AAysg/Em1KxJqqDx5mZ6odGDe7ORlcC9oeqdDn7y0FQl0UTf6/lFZmfHzhdDiHiXkheMyn7xMvLr2iZy8tDkJdFE3NBpljZfVP8Lmx5SmI99AtasS1lKYq1zVdPrbG5y8bK6MuuXkpc1JoIu64+4N49bD6sGwaTI89qly4lTUX+ePwydzlRCHipOXHeXkpUok0EXd8r8LHn4DNkfD7mfhgVfVrkjcjtJi+Gq5sltVIx0Mek5p3JGTl6qSQBd1L3QknHnyatPRPX9QuyJxK84dU65YyjwOncbAiFeUJR+E6iTQhTqGLFQuWdv1ZwgIVZYLEPattFgZkX+1HDybQNQH0P5+tasS15DrgoQ6nF1g9DvKx/W4ScoqjcJ+nf1BWUXzy6XQ8VGY/a2EuR2SQBfq0forjUaXzsLWGVBernZF4vdKryg71K8eDAUXYPxGGPW2TLHYKQl0oa7gHjBiKaR8poz+hP04870yKv/fP6HzWJjzLdw1Qu2qRDVkDl2or/tU5RrmL19WNte9c7jaFTVspVeUX65fvwZaPYyPg7vuU7sqUQMyQhfq02iUyxebdYKt0yHnF7UrarjOfAerBignPrtEwewECfN6RAJd2AfXRkrTERqIm6ws3CTqTkkRfP4S/DcCivLgD5uVzt5GfmpXJm6BBLqwH36t4dH/KmuA7PqTspyqsL2M7+DtAfD1v5SegNkJcOcwtasSt0ECXdiXO4bCwGfhWBwc+q/a1Ti2kiL47EVYEwFXLsOELUoXb6PGalcmbpOcFBX2p/8zylzu7gXQrLOyK42wrvRD8PFsMJyCsMkw7O/KGuSiXpMRurA/Tk4wapWyqNPmaLicqXZFjqOkEPa+AGuHKecpJm6Bh1ZImDsICXRhnxr5KSdJCy/CR49BWYnaFdV/6Ymwsp+yhk7YZGWuvF2E2lUJK6rRlMuSJUs4evQoGo2G2NhYOnfuDEBmZibz5s0zPy49PZ25c+dSUlLC66+/TsuWLQHo27cvjz/+uA3KFw6tWSd48HXYNkO5AmP4YrUrqp9KCpVuz4Q3lE89k7ZByGC1qxI2YDHQExMTSUtLIy4ujtTUVGJjY4mLiwMgICCA999/H4DS0lImTZrE4MGD2bNnD5GRkcyfP9+21QvH12UcnDkMCf9RdrnpOErtiuqX09/Cx3OUzZe7PQZD/wYePmpXJWzEYqAnJCQQEaF8LAsJCSEvLw+j0YhWW3XN423btjF8+HC8vLxsU6louIYthnNH4eMnQN9B+SOqV1ygjMq/fRN8g2HSdggZpHZVwsYszqEbDAb8/K42F+h0OrKzs6973ObNmxk9erT5dmJiIjExMURHR3PixAkrlSsaJBc3GLMO3LwgbiIUXVK7IvuWlgArw+HbN5RlFWYfkDBvIG75skXTDZo9jhw5Qtu2bc2j9i5duqDT6Rg4cCBHjhxh/vz57Ny5s/bViobLpzmMeQfWPQTbH1dOmMq2ZlUV50P8Iji4EhoHw+Qd0HaA2lWJOmRxhK7X6zEYDObbWVlZ+Pv7V3nM/v376dOnj/l2SEgIAwcOBKBr167k5ORQVlZmpZJFg9X6XmUO+Mdd8M1raldjX377Bt4Kh4NvQY9p8HiChHkDZDHQw8PD2bNnDwDJycno9frr5s+PHz9O+/btzbdXr17Nrl27ADh16hQ6nQ5nZ2dr1i0aqj5zIPQRiP8b/LJf7WrUV5wPn/4F3o0EUzlE74L7l8m+ng2UxSmXsLAwQkNDiYqKQqPRsHDhQrZu3Yq3tzdDhw4FIDs7myZNmpif8+CDD/LMM8+wceNGSktLWbxYLjcTVqLRwEP/gayT8NFUmPGlMr3QEP32tXIFS+5v0HMmDHlRgryB05huNCluYxkZGQwZMoT4+HiCgoLq+u2FIzD8DG8PgqZ3wGP/B64ealdUd64YlevyD61WFjR7+A1lOko4PEvZKZ2ion5qegc88hac/R52N6B+h1//B2/1VcK81yx4/ICEuTCTQBf1V4cH4d4/w3fvwvfvq12NbV0xwq6nYd2D4OSsfCoZsVS5lFOICrLaoqjfBj2v7H35yVxo1hFadFW7Iuv75UvY8QRcTIfes2HwC+DmqXZVwg7JCF3Ub84uMHotePkrOx0V5KhdkfVcuQy7/gzvPQROrsqo/L5/SJiLm5JAF/WfV1MY9x4Yz8OWGCh3gJ6H1H3wZh84/A70eQJmfQ2t+lh+nmjQJNCFYwjsBpH/hNQvYP8/1K7m9hVdgp1PwfsjwcUdpu5RVpmUUbmoAZlDF44jLBoyDsH//gktwqB9pNoV3ZqUeNjxJFw6A33/CIOeUzbPFqKGZIQuHIdGA5HLofk9sG0mXEhVu6KaKcqDHX+E9aOUAI/Zq2wJJ2EubpEEunAsrh4w9j3l0r64iUprvD37+XNlrvzIegh/CmZ9BcE91a5K1FMS6MLx+LWCR9coywPsfArqvhnassKLStv+hkfBTQsxnykLj8moXNSCBLpwTO2GwODn4PhmOLhK7Wqq+vkzZVT+wwdKY9TM/0FQd7WrEg5ATooKx3XvXKXpaO9z0LyL+pf9FV6EPc/BD+vBv72ypntQN3VrEg5FRujCcTk5wci3oHFL2BwNl8+rV8upPfBmbzj6Idz7tLJKpIS5sDIJdOHYGjVWRsJXLsPmKVBWUrfvX5gL2x6HD8aCR2OY9jlELGxYq0OKOiOBLhxfQCg8tAJOJ8DeF+rufX/6P3ijNxyLg37zYOaXEBhWd+8vGhyZQxcNQ6fRkHFY2aItqLty21YKcmD3s3BsI+jvhj9sdMxFw4TdkUAXDcewRXDuB6WJR99BGblb24+fwq4/Qb4B+v8F+j8DLm7Wfx8hbkCmXETD4ewKY94Fd2+l6agoz3qvXZADW6bDxvHKyo/Tv1Aum5QwF3VIAl00LN7NYMw6uHgats2C8vLav+bJXfBGL0jeCgMWwPR90OKe2r+uELdIAl00PK36KGul/PQpfP3q7b9O/gX4KAbiJoA2QAnyQc/KqFyoRubQRcPUa5ZykvSLvysnLNsNubXnn9gBnzytXJY48Fnl2nIJcqEyGaGLhkmjgYf+rZwc3RIDuWk1e17+Bdj8GGyapEzfzNgPAxdImAu7IIEuGi43L6XpqLwMNk2GkqLqH5+8Hd7oCSd3KmuVT98HzTrVTa1C1IAEumjYmoTAI6uUyxk/nXfjx+QbYFO0snyAb6AyKh/wF+WqGSHsSI3m0JcsWcLRo0fRaDTExsbSuXNnADIzM5k37+oPQXp6OnPnzuW+++5jwYIFnD17FmdnZ/7xj38QHBxsm+9AiNpqH6l0cn61TGk66jbl6n3J2+CTucrWcIOfh/A/SZALu2Ux0BMTE0lLSyMuLo7U1FRiY2OJi4sDICAggPfffx+A0tJSJk2axODBg9m1axc+Pj4sX76cr7/+muXLl/Paa6/Z9jsRojYGxcLZ7+HTZ5RpFN+W8OlcOPGxsgNS9FsQcLfaVQpRLYtTLgkJCURERAAQEhJCXl4eRqPxusdt27aN4cOH4+XlRUJCAkOHDgWgb9++fP/991YuWwgrc3JWNsXQNoONE5S58p/+D4a8CNPiJcxFvWAx0A0GA35+fubbOp2O7Ozs6x63efNmRo8ebX6OTqdT3sDJCY1GQ3FxsbVqFsI2PHUw7j1l3XK/1srGE/3mgrNc3Svqh1v+l2q6wXZeR44coW3btmi12ho/Rwi71KIrzD0J7j7KqF2IesTiCF2v12MwGMy3s7Ky8Pf3r/KY/fv306dPnyrPqRzFl5SUYDKZcHOT63RFPdHIT8Jc1EsWAz08PJw9e/YAkJycjF6vv24kfvz4cdq3b1/lObt37wZg37599OrVy5o1CyGEuAGLUy5hYWGEhoYSFRWFRqNh4cKFbN26FW9vb/OJz+zsbJo0aWJ+TmRkJAcOHGD8+PG4ubnx8ssv2+47EEIIAdRwDv3aa82BKqNxgJ07d1a5XXntuRBCiLojnaJCCOEgJNCFEMJBSKALIYSDUKVjoqysDIDz58+r8fZCCFEvVWZmZYb+niqBXnmN+oQJE9R4eyGEqNeys7Np1arVdV/XmFRo4ywqKiIpKQl/f3+cnaWBQwghaqKsrIzs7Gw6duyIh4fHdferEuhCCCGsT06KCiGEg7D7ZeRutrkGwIEDB3j11Vdxdnamf//+zJkzxy7qGjx4MM2aNTNPJy1btoyAgIA6qevUqVPMnj2bKVOmMHHixCr3qXm8qqtLzeP1yiuv8N1331FaWsrMmTMZNmyY+T41j1d1dal1vAoLC1mwYAEXLlzgypUrzJ49m0GDBpnvV+t4WapLzX9foEwxP/DAA8yePZtRo0aZv26T42WyYwcPHjTNmDHDZDKZTCkpKaaxY8dWuX/EiBGms2fPmsrKykzjx483/fzzz3ZR16BBg0xGo7FOarlWfn6+aeLEiabnn3/e9P777193v1rHy1Jdah2vhIQE07Rp00wmk8mUk5NjGjBgQJX71TpelupS63h98sknprfffttkMplMGRkZpmHDhlW5X63jZakutY5XpVdffdU0atQo05YtW6p83RbHy66nXKrbXCM9PR1fX1+aN2+Ok5MTAwYMICEhQfW61OTm5sbq1avR6/XX3afm8aquLjX16NGD119/HQAfHx8KCwvNl4Opebyqq0tNkZGRTJ8+HYBz585VGeWqebyqq0ttqamppKSkMHDgwCpft9XxsuspF4PBQGhoqPl25eYaWq2W7Oxs8yYalfelp6erXlelhQsXcubMGbp168bcuXPRaDQ2r8vFxQUXlxv/L1XzeFVXVyU1jpezszOenp4AfPTRR/Tv39/8sVzN41VdXZXUOF6VoqKiOH/+PCtXrjR/Tc3jVV1dldQ6XkuXLuWFF15g+/btVb5uq+Nl14H+eyY7vSDn93U9+eST9OvXD19fX+bMmcOePXu47777VKrO/ql9vD7//HM++ugj1q5dW2fvWRM3q0vt47Vx40ZOnjzJM888w44dO+r0l0l1blaXWsdr+/bt3HPPPQQHB9v8vSrZ9ZRLdZtr/P6+zMzMOvtIb2nTj5EjR9KkSRNcXFzo378/p06dqpO6qqPm8bJEzeP11VdfsXLlSlavXo23t7f562ofr5vVBeodr6SkJM6dOwdAhw4dKCsrIycnB1D3eFVXF6h3vPbv3098fDxjx45l8+bNvPnmmxw4cACw3fGy60CvbnONoKAgjEYjGRkZlJaWsm/fPsLDw1Wv6/Lly8TExJj3UD106BB33HFHndRVHTWPV3XUPF6XL1/mlVdeYdWqVTRu3LjKfWoer+rqUvN4HT582PxpwWAwUFBQYN5vWM3jVV1dah6v1157jS1btrBp0ybGjBnD7Nmz6du3L2C742X3jUXLli3j8OHD5s01Tpw4Yd5c49ChQyxbtgyAYcOGERMTYxd1rVu3ju3bt+Pu7s7dd9/NCy+8UCcfS5OSkli6dClnzpzBxcWFgIAABg8eTFBQkKrHy1Jdah2vuLg4VqxYQZs2bcxf69WrF3fddZeqx8tSXWodr6KiIp577jnOnTtHUVERTzzxBBcvXlT959FSXWodr2utWLGCwMBAAJseL7sPdCGEEDVj11MuQgghak4CXQghHIQEuhBCOAgJdCGEcBAS6EII4SAk0IUQwkFIoAshhIOQQBdCCAfx/8I2Xz21+MObAAAAAElFTkSuQmCC\n", "text/plain": [ "
" ] }, "metadata": { "tags": [] }, "output_type": "display_data" }, { "name": "stdout", "output_type": "stream", "text": [ "0.235\n", "0.13457301587301593\n" ] } ], "source": [ "use_saved_expt = False\n", "if use_saved_expt:\n", " analysis = Analysis(expt_dir, default_metric=\"recall_at_k\", default_mode=\"max\")\n", " w2v_params = analysis.get_best_config()\n", "else:\n", " w2v_params = {\n", " \"min_count\": 1,\n", " \"iter\": 5,\n", " \"workers\": 10,\n", " \"sg\": 1,\n", " }\n", "\n", "# Instantiate callback to measurs Recall@K on the validation set after each epoch of training\n", "ratk_logger = RecallAtKLogger(valid, k=10, save_model=True)\n", "# Instantiate callback to compute Word2Vec's training loss on the training set after each epoch of training\n", "loss_logger = LossLogger()\n", "# Train Word2Vec model and retrieve trained embeddings\n", "embeddings = train_w2v(train, w2v_params, [ratk_logger, loss_logger])\n", "\n", "# Save results\n", "pickle.dump(ratk_logger.recall_scores, open(os.path.join(\"/content\", f\"recall@k_per_epoch.pkl\"), \"wb\"))\n", "pickle.dump(loss_logger.training_loss, open(os.path.join(\"/content\", f\"trainloss_per_epoch.pkl\"), \"wb\"))\n", "\n", "# Save trained embeddings\n", "embeddings.save(os.path.join(\"/content\", f\"embeddings.wv\"))\n", "\n", "# Visualize metrics as a function of epoch\n", "plt.plot(np.array(ratk_logger.recall_scores)/np.max(ratk_logger.recall_scores))\n", "plt.plot(np.array(loss_logger.training_loss)/np.max(loss_logger.training_loss))\n", "plt.show()\n", "\n", "# Print results on the test set\n", "print(recall_at_k(test, embeddings, k=10))\n", "print(mrr_at_k(test, embeddings, k=10))" ] }, { "cell_type": "markdown", "metadata": { "id": "6F0OaTwC4NJK" }, "source": [ "### Tune word2vec with ray" ] }, { "cell_type": "code", "execution_count": 111, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 258 }, "id": "W-rvETTgsepP", "outputId": "93a8ae6c-cc79-45df-8b08-899c017f8135" }, "outputs": [ { "data": { "text/html": [ "== Status ==
Memory usage on this node: 2.6/12.7 GiB
Using AsyncHyperBand: num_stopped=308\n", "Bracket: Iter 40.000: None | Iter 10.000: 0.206
Resources requested: 4.0/4 CPUs, 0/0 GPUs, 0.0/7.36 GiB heap, 0.0/3.68 GiB objects
Current best trial: a0670_00440 with recall_at_k=0.244 and parameters={'size': 16, 'window': 1, 'ns_exponent': 0.7999999999999996, 'alpha': 0.1, 'negative': 19, 'iter': 10, 'min_count': 1, 'workers': 6, 'sg': 1}
Result logdir: /content/big_HPO_no_distributed
Number of trials: 512/25872 (16 PENDING, 4 RUNNING, 492 TERMINATED)

" ], "text/plain": [ "" ] }, "metadata": { "tags": [] }, "output_type": "display_data" }, { "name": "stderr", "output_type": "stream", "text": [ "2021-06-11 09:06:44,670\tERROR tune.py:545 -- Trials did not complete: [tune_w2v_a0670_00492, tune_w2v_a0670_00493, tune_w2v_a0670_00494, tune_w2v_a0670_00495, tune_w2v_a0670_00496, tune_w2v_a0670_00497, tune_w2v_a0670_00498, tune_w2v_a0670_00499, tune_w2v_a0670_00500, tune_w2v_a0670_00501, tune_w2v_a0670_00502, tune_w2v_a0670_00503, tune_w2v_a0670_00504, tune_w2v_a0670_00505, tune_w2v_a0670_00506, tune_w2v_a0670_00507, tune_w2v_a0670_00508, tune_w2v_a0670_00509, tune_w2v_a0670_00510, tune_w2v_a0670_00511]\n", "2021-06-11 09:06:44,678\tINFO tune.py:549 -- Total run time: 6750.68 seconds (6746.07 seconds for the tuning loop).\n", "2021-06-11 09:06:44,680\tWARNING tune.py:554 -- Experiment has been interrupted, but the most recent state was saved. You can continue running this experiment by passing `resume=True` to `tune.run()`\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Best hyperparameters found were: {'dataset': 'ecomm', 'k': 10, 'size': 16, 'window': 1, 'ns_exponent': 0.7999999999999996, 'alpha': 0.1, 'negative': 19, 'iter': 10, 'min_count': 1, 'workers': 6, 'sg': 1}\n" ] } ], "source": [ "from ray.tune import Analysis\n", "from ray.tune.schedulers import ASHAScheduler\n", "\n", "# Define the hyperparameter search space for Word2Vec algorithm\n", "search_space = {\n", " \"dataset\": \"ecomm\",\n", " \"k\": 10,\n", " \"size\": tune.grid_search(list(np.arange(10,106, 6))),\n", " \"window\": tune.grid_search(list(np.arange(1,22, 3))),\n", " \"ns_exponent\": tune.grid_search(list(np.arange(-1, 1.2, .2))),\n", " \"alpha\": tune.grid_search([0.001, 0.01, 0.1]),\n", " \"negative\": tune.grid_search(list(np.arange(1,22, 3))),\n", " \"iter\": 10,\n", " \"min_count\": 1,\n", " \"workers\": 6,\n", " \"sg\": 1,\n", "}\n", "\n", "use_asha = True\n", "smoke_test = False\n", "\n", "# The ASHA Scheduler will stop underperforming trials in a principled fashion\n", "asha_scheduler = ASHAScheduler(max_t=100, grace_period=10) if use_asha else None\n", "\n", "# Set the stopping critera -- use the smoke-test arg to test the system \n", "stopping_criteria = {\"training_iteration\": 1 if smoke_test else 9999}\n", "\n", "# Perform hyperparamter sweep with Ray Tune\n", "analysis = tune.run(\n", " tune_w2v,\n", " name=expt_dir,\n", " local_dir=\"ray_results\",\n", " metric=\"recall_at_k\",\n", " mode=\"max\",\n", " scheduler=asha_scheduler,\n", " stop=stopping_criteria,\n", " num_samples=1,\n", " verbose=1,\n", " resources_per_trial={\n", " \"cpu\": 1,\n", " \"gpu\": 0\n", " },\n", " config=search_space,\n", ")\n", "print(\"Best hyperparameters found were: \", analysis.best_config)" ] }, { "cell_type": "markdown", "metadata": { "id": "jjdH1oSHrt2p" }, "source": [ "Ray Tune saves the results of each trial in the ray_results directory. Each time Ray Tune performs an HPO sweep, the results for that run are saved under a unique subdirectory. In this case, we named that subdirectory big_HPO_no_distributed. Ray Tune provides methods for interacting with these results, starting with the Analysis class that loads the results from each trial, including performance metrics as a function of training time and tons of metadata.\n", "\n", "These results are stored as JSON but the Analysis class provides a nice wrapper for converting those results in a pandas dataframe." ] }, { "cell_type": "markdown", "metadata": { "id": "RexEQbOd3TNR" }, "source": [ "### Explore the results of the full hyperparameter sweep\n", "Next, we're going to look at how the Recall@10 score changes as a function of various hyperparameter configurations that we tuned over. We tuned over three hyperparameters: the context window size, negative sampling exponent, and the number of negative samples.\n", "\n", "We want to look at the Recall@10 scores for all of these configurations but this is a 3-dimensional space and, as such, will be difficult to visualize. Instead, we'll \"collapse\" one dimension, while examining the other two. To do this, we aggregate the Recall@10 scores (taking the mean) along the \"collapsed\" dimension." ] }, { "cell_type": "code", "execution_count": 112, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 949 }, "id": "O7lbr2-5sebq", "outputId": "a2704370-f4b4-464a-87ee-6b4b6f7ed347" }, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
recall_at_ktime_this_iter_sdonetimesteps_totalepisodes_totaltraining_iterationexperiment_iddatetimestamptime_total_spidhostnamenode_iptime_since_restoretimesteps_since_restoreiterations_since_restoretrial_idconfig/alphaconfig/datasetconfig/iterconfig/kconfig/min_countconfig/negativeconfig/ns_exponentconfig/sgconfig/sizeconfig/windowconfig/workerslogdir
00.2375.543207FalseNaNNaN101faab4240d3746cbb4c51569db0690512021-06-11_08-49-15162340135571.839448456843b5d652723e6172.28.0.271.839448010a0670_004190.100ecomm10101190.6116.01.06big_HPO_no_distributed/tune_w2v_a0670_00419_41...
10.2034.436352FalseNaNNaN10fe00d91e28cf49beb45a0f47b900d2ae2021-06-11_07-54-42162339808275.372504222523b5d652723e6172.28.0.275.372504010a0670_001870.010ecomm10101190.6110.01.06big_HPO_no_distributed/tune_w2v_a0670_00187_18...
20.03610.416867FalseNaNNaN9965436df76d64eb1badb38eceda079982021-06-11_08-42-17162340093755.470819431083b5d652723e6172.28.0.255.47081909a0670_003930.001ecomm10101160.4116.01.06big_HPO_no_distributed/tune_w2v_a0670_00393_39...
30.2365.960799FalseNaNNaN98a3090a631b447bd9fb9ee249ef02a302021-06-11_08-52-44162340156454.629899472863b5d652723e6172.28.0.254.62989909a0670_004340.100ecomm10101130.8116.01.06big_HPO_no_distributed/tune_w2v_a0670_00434_43...
40.0411.409388TrueNaNNaN10cce93e60ce3a4ff4a0db54a8e47fa3d92021-06-11_08-06-00162339876038.842165278103b5d652723e6172.28.0.238.842165010a0670_002410.010ecomm1010110-1.0116.01.06big_HPO_no_distributed/tune_w2v_a0670_00241_24...
..........................................................................................
5030.0235.446685FalseNaNNaN39f3c574035ac47ac850519bf18e371842021-06-11_09-01-30162340209022.201976510823b5d652723e6172.28.0.222.20197603a0670_004710.001ecomm1010110-1.0122.01.06big_HPO_no_distributed/tune_w2v_a0670_00471_47...
5040.0152.176694TrueNaNNaN10e927f2c1c10e42908c4c5b26d8ba5b862021-06-11_08-17-32162339945241.176507330623b5d652723e6172.28.0.241.176507010a0670_002940.001ecomm101011-0.4116.01.06big_HPO_no_distributed/tune_w2v_a0670_00294_29...
5050.2111.697303FalseNaNNaN9de953f60dd084976b0a01b62918adea52021-06-11_08-33-35162340041519.104989397823b5d652723e6172.28.0.219.10498909a0670_003590.100ecomm1010110.2116.01.06big_HPO_no_distributed/tune_w2v_a0670_00359_35...
5060.0241.774843FalseNaNNaN4d60beb2097b64164acbcb80fa85800192021-06-11_07-14-48162339568813.84004034543b5d652723e6172.28.0.213.84004004a0670_000030.001ecomm101014-1.0110.01.06big_HPO_no_distributed/tune_w2v_a0670_00003_3_...
5070.1982.985146FalseNaNNaN83ad21430e1004d108a4683096833aeb22021-06-11_07-46-29162339758925.868437189243b5d652723e6172.28.0.225.86843708a0670_001540.010ecomm1010170.4110.01.06big_HPO_no_distributed/tune_w2v_a0670_00154_15...
\n", "

508 rows × 29 columns

\n", "
" ], "text/plain": [ " recall_at_k ... logdir\n", "0 0.237 ... big_HPO_no_distributed/tune_w2v_a0670_00419_41...\n", "1 0.203 ... big_HPO_no_distributed/tune_w2v_a0670_00187_18...\n", "2 0.036 ... big_HPO_no_distributed/tune_w2v_a0670_00393_39...\n", "3 0.236 ... big_HPO_no_distributed/tune_w2v_a0670_00434_43...\n", "4 0.041 ... big_HPO_no_distributed/tune_w2v_a0670_00241_24...\n", ".. ... ... ...\n", "503 0.023 ... big_HPO_no_distributed/tune_w2v_a0670_00471_47...\n", "504 0.015 ... big_HPO_no_distributed/tune_w2v_a0670_00294_29...\n", "505 0.211 ... big_HPO_no_distributed/tune_w2v_a0670_00359_35...\n", "506 0.024 ... big_HPO_no_distributed/tune_w2v_a0670_00003_3_...\n", "507 0.198 ... big_HPO_no_distributed/tune_w2v_a0670_00154_15...\n", "\n", "[508 rows x 29 columns]" ] }, "execution_count": 112, "metadata": { "tags": [] }, "output_type": "execute_result" } ], "source": [ "analysis = Analysis(\"big_HPO_no_distributed/\", \n", " default_metric=\"recall_at_k\",\n", " default_mode=\"max\")\n", "\n", "results = analysis.dataframe()\n", "results" ] }, { "cell_type": "markdown", "metadata": { "id": "FHKEwe9u3LDv" }, "source": [ "The Analysis objects also has methods to quickly retrieve the best configuration found during the HPO sweep.\n", "\n" ] }, { "cell_type": "code", "execution_count": 113, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "WnKAoaER2vkp", "outputId": "1f50520a-3267-4d0c-fb99-d7a566fbc95b" }, "outputs": [ { "data": { "text/plain": [ "{'alpha': 0.1,\n", " 'dataset': 'ecomm',\n", " 'iter': 10,\n", " 'k': 10,\n", " 'min_count': 1,\n", " 'negative': 19,\n", " 'ns_exponent': 0.7999999999999996,\n", " 'sg': 1,\n", " 'size': 16,\n", " 'window': 1,\n", " 'workers': 6}" ] }, "execution_count": 113, "metadata": { "tags": [] }, "output_type": "execute_result" } ], "source": [ "best_config = analysis.get_best_config()\n", "best_config" ] }, { "cell_type": "markdown", "metadata": { "id": "tGk8zNkp3Pkr" }, "source": [ "While the results dataframe contains the final Recall@10 scores for each of the 539 trials, it's also nice to explore how those scores evolved as a function of training for any given trial. Again, the Analysis class delivers, providing the ability to access the full training results for any of the trials. Below we plot the Recall@10 score as a function of training epochs for the best configuration." ] }, { "cell_type": "code", "execution_count": 114, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 276 }, "id": "y8QF0YZY3GXZ", "outputId": "01f09f26-14ee-4003-d621-7135cf1f0861" }, "outputs": [ { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "tags": [] }, "output_type": "display_data" } ], "source": [ "best_path = analysis.get_best_logdir()\n", "dfs = analysis.fetch_trial_dataframes()\n", "\n", "plt.plot(dfs[best_path]['recall_at_k']);\n", "plt.xlabel(\"Epoch\")\n", "plt.ylabel(\"Recall@10\");" ] }, { "cell_type": "code", "execution_count": 116, "metadata": { "id": "z6sCIiAK3Rmt" }, "outputs": [], "source": [ "def aggregate_z(x_name, y_name):\n", " grouped = results.groupby([f\"config/{x_name}\", f\"config/{y_name}\"])\n", " x_values = []\n", " y_values = []\n", " mean_recall_values = []\n", " \n", " for name, grp in grouped:\n", " x_values.append(name[0])\n", " y_values.append(name[1])\n", " mean_recall_values.append(grp['recall_at_k'].mean())\n", " return x_values, y_values, mean_recall_values" ] }, { "cell_type": "code", "execution_count": 117, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 327 }, "id": "6MDZY3uQ3xDA", "outputId": "11d3cdd2-949c-4bc7-ee82-24ee63c5ba46" }, "outputs": [ { "data": { "image/png": "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i4XRTZrwXzk5fFFczOpDxZR4vDRy2pnX5SzOTUzUNboiIiInoPOpiNRnKmCISINiGAbnJSVxXlJSXaciIiJy2tL5VETqI837EhEREREREZF6TwUMEREREREREan3VMAQERERERERkXpPBQwRERERERERqfdUwBARERERERGRek8FDBERERERERGp91TAEBEREREREZF6TwUMEREREREREan3VMAQERERERERkXpPBQwRERERERERqfeskQ7odruZNm0ar7/+OitXriQjI4Onn36a5cuXB/aprKwkOTmZhQsXVnvuwoULefzxx0lLSwu0DR8+nOHDh0csfxERERGRUNMYWUTkxCJewMjNzaVz587V2saPH8/48eMDjx9++GFatmxZ6/MHDBjAk08+GdYcRUREREQiSWNkEZETi/glJLm5udxxxx3H3L5582ZWr17NtddeG8GsRERERETqjsbIIiInFvECRteuXY+7/cUXX2TUqFFYrbVPDvnhhx8YMWIEl1xyCRMmTKCkpCQcaYqIiIiIRIzGyCIiJ1avFvHcsWMH//rXvxgyZEit25s1a0a/fv2YOXMmH3zwAaWlpTzxxBMRzlJEREREJHI0RhYROSLia2Acz+LFixkwYAA2m63W7d26daNbt26Bx2PGjGHUqFGRSk9EREREJOI0RhYROaJezcBYsWIFffv2Peb2PXv2UFhYGHjs9XqPOY1ORERERKQh0BhZROSIelXA2LRp0zFXVgZ48803mThxIm63G6/Xy/z58/ntb38buQRFRERERCJMY2QRkSMiWsAoKChg4MCBDBw4EIARI0YwcOBA9u3bR3FxMRUVFdXuXw3wxhtv8PzzzwMwduxY4uPjGTx4MIMGDcJqtVa7tZSIiIiIyOlGY2QRkeBEdG5ZamoqS5YsOeb2TZs21WgbPnx44Ofo6Gjd31pEREREGhSNkUVEglOvLiEREREREREREamNChgiInJMK1asoG3btuTl5VVrz8vLo2PHjoEpzwMHDtR0ZREREREJKy1PLCIitaqoqGDatGkkJibWuj09Pf24U55FREREREJJMzBERKRW06dP5/LLLycmJqauUxERERERUQFDRERq2rRpE59//jl/+MMfjrlPaWkpubm5DBw4kJEjR7Jly5bIJSgiIiIiZxwVMEREpBq/38/kyZOZOHEiNput1n1iYmIYMmQIEyZMYPHixfTu3Zvc3Fw8Hk+EsxURERGRM4UKGCIiUs1bb71Fq1at6NGjxzH3SUpKYtKkSWRnZ2OxWLjpppsoKChg+/btkUtURERERM4oKmCIiEg1y5YtY9myZfTu3ZvevXuzZ88ehg0bxpdffhnY59ChQ+zatava83w+H1ar1oYWERERkfDQSFNERKqZNWtWtccXXXQR8+bNIzs7O9C2bt06Jk2axLvvvktycjJvv/02mZmZ5OTkRDpdERERETlDBD0D46OPPqKgoAAAl8vFs88+y9ixY5kzZw5+vz9sCYqISP3w/fffM3LkSAD69OnDddddx7XXXsvAgQNZvHgx06dPxzTNOs5SRERERBqqoGZgzJo1ixkzZvDmm2+SmprK1KlTWbhwIb1792bmzJmUl5eTm5sb7lxFRKQOLF++HIDs7Gxmz54daB81ahSjRo2qq7RERERE5AwT1AyMd955h8cff5x27dpRUVHB+++/z7hx45gxYwZPPvkkH374YbjzFBEREREREZEzWFAFjL1799KtWzcAVq9ejdvt5rLLLgOgffv27NmzJ3wZioiIiIiIiMgZL6hLSOLj4ykoKCAjI4Nly5bRuXNnEhMTASgsLCQqKiqsSYbblvwCfswrwDQttM1Oo0l6Uthi5ZUV8++ivZR73bRJSKN9QjqGYYQlVqnnELvLt1LqKSbZnkFWdHPsZnjeK7e3kgNVP3LIvRunmUxaVBuc1sSwxPL7/RRW/USRayumEUWqoy1x9oywxAIod2+n1PUDPr+XWHtbYu2twxbL49mLy/09Xl8xNmsrHPZOGIY9LLG83kO43OvxePMwzSwcts6YZkJYYvn9btzuDXjcP2KxJGC1dcZqzQxLLACXezNVrg0Yhond1hG7rWXYYnk8ebjd6/D7yrDa2mKzdcQwdIMnERE5vUX63C0iEoygChjnnXceEydOpHv37rz33ntMmjQJgNLSUl5++WW6du0a1iTDacP2vYx59l3Kq9wApCXEMOOPv6Nl49SQx9pZWsjoz95iS8lBAOwWkzl9r+fstCYhj1XuKWVR/ly+Lf400DY0azQ9kweEvGDi9/vZdPjvrNj3fKCtTfwA+qbfRpQZF9JYAHsr/sX/7L4bn//Ie5Zga8rFWU+RYM8KeaxS1498t+8mXN4DAJhGDF0z5hLv6BzyWB7PPg4U3U5V1Wf/12KQlvwqMc5BIY/l81dyqPQvFJc8G2hLiL2dpPi7sVhCX+SqqvwnxYU3Az4AbPaeJCTNDMtAqMr1PfkHhuH3lwBgsaSQmfoODnv7kMfyeHZSePAmPJ4f/q/FRkrqf+Nw9A55LBERkUiqrPwnRUedu+32niSG6dwtIhKsoL4mfOCBB2jZsiVffvklN954I1dffTUAn376KevWrWP8+PFhTTJcvD4fb//zu0DxAuDAoTL+d922sMRbW7A7ULwAcPm8PLd+BeUeV8hj7avcWa14AbBozzwOuvaGPNZhdz6f7X+5Wtvmw0s5WBX6f0e3t5w1B18NFC8ADrl3sL9yfchjARysWBEoXgB4/WXkHV6A3+8LeSyXe/1RxQsAP4XFE/B494c8ltu9leKS56u1HSp9Ebfnx5DH8noLOHzoQX4eAAG4XV/jca8LeSyAw6ULAsULAJ/vIGUV/xOWWG7Xt0cVLwDcHD70BF7v4bDEExERiYTazt2uMJ67RUSCFdQMjKSkJKZNm1ajvV+/flxyySWn7W3zPF4fW/YU1mjftb84LPH2V5bUaNtVVkS5x4XTGtrLBCp95TXaXL5KXL6qkMYBcPsq8Pgra8bzloY8lsdfxWH37hrtFZ6Dtex96spd22u0lbl/wud3YxqOkMby+Q7VaPP69uP3lUOIf8V8/hKOHpQc4cfnq/l/9FT5/RX4vDULZz5f6D/k+/1eXJ6NNdrdnp9CHgvA6yuo2ebdCVQC8WGJKSIiEm5+fwXeCJ27RURORtAXavv9flauXMnLL7/MY489RmFhIQ6Hg+3bt4cxvfBy2KwM7dOpRvsFXVqEJd5ZyTUvcRjW7DekOGJCHivFnontFx+wc6JbkWgL/aUxsbZ0GjnaVmuzGlEk2rNDHivKTKRt/JAa7WlR7UIeCyDV2a9GW+PYqzEtoS1eANhsrfhlpcIZdRmmGfr1PaxmE0yz+hRQ05KGzdo05LFMsxFR0b/7RasFq61VyGMZhkl8zHU12mOia/6fCQWbrWb/Ee28Bosl9L9nIiIikWKajYiO0LlbRORkBFXA2L9/P1dccQVjxoxhzpw5/Pd//zdlZWVs2bKFoUOH8s0334Q7z7C54KyWjBlyLlF2K/FOB/dfeyFdW4V+LQWALsmNebbnFaQ6YrBbTG5sdTZXNf9NWBbxbBSVxc3NHyTD0QQDg7Zx3bgyJxenNTbksaLMOPo1Hk/TmHMAgxR7cy7LeZIkR+jX9jAMgzYJg+mYeBUWw0a0mcxvMyaRFtUh5LEAEqN60CZ5ElZLAhYjmuYJt5PqvDAssey2DjRKmYfVbApYiIn+HUkJ94VlTQqbNZOMlDlE2c8FwGHrQUbKPKzW0P/fNwwHsXF/JCp6GGBimjkkJr+OzdYx5LEAoqMuIin+fgzDicWIJyVhCtGO88ISy2brQmLSy1gs6YANp/MmYmJu0CKeIiJyWvv53B191Lk7OYznbhGRYAV1CckTTzyB1Wrlo48+onXr1oFFO1u2bMkNN9zACy+8wPz588OaaLikJsQwesi5/L/eHbFYLDRKDP0H/J9FW21c1rQT5zZqhsvnJT06DqslfB90msd2YHTLR6n0lRNrJmA3Qz9r4GcpjuYMbDyZCm8xdouTKGv4ps/H2TI4Jy2XTklXYxo2nNaUsMWymfFkx19PmrM/frw4zMyw3TXGMKw4oy/Cbl+M31+GaTbCEuLLVI7msHchPWUePn8RFiMR0wzfe2a1NSch6U/Ext+LYURjmuGboWA100iMu4M45zDAEtbFxiyWaJzOy3E4euH3V2Ga6RiGLWzxREREIsVma05i0p+Ii8C5W0QkWEEVMFatWsWrr75K69Y1bx955ZVX8sYbb4Q8sUgyDIOM5Mhdr54WHb4iyS85rbE4iUw8mxmFLQyXO9TGYliJs0UmFoDDmh6xWFYzGUiOSCzTjMMk9HeKqY1h2LFacyIUywjLbJJjMc20iMUSERGJlEieu0VEghHU1/8Wi4WYmNrXaXC73WH7RlpEREREREREBIIsYLRr147p06fj8XhqbHvjjTfo1KnmQnYiIiIiIiIiIqES1CUkd9xxByNHjuS3v/0t3bp1w+128/jjj7Njxw7y8/N5/fXXw52niIiIiIiIiJzBgpqB0b17d9577z369etHXl4ejRs3Zv/+/ZxzzjksXLiQbt26hTtPERERERERETmDBTUDA47cceSRRx4JZy4iIiIiIiIiIrU6ZgHj5ZdfDvoghmEwZsyYkCQkIiIiIiIiIvJLxyxgPP/880EfRAUMEREREREREQmnYxYwNm7cGJaAbrebadOm8frrr7Ny5UoyMjJYuHAhjz/+OGlpaYH9hg8fzvDhw2s8f9GiRcycORO3202bNm144okniIuLC0uuIiIiIiKRoDGyiMiJBb0GBkBZWRl79+6lpKSEhIQEMjMziYqKOqmAubm5dO7cuUb7gAEDePLJJ4/73Pz8fKZMmcLChQtp3LgxTz75JM899xyTJk06qRxEREREROoTjZFFRE4sqLuQHDhwgFtuuYWePXsyZMgQrrnmGgYNGkSvXr0YP348RUVFQQfMzc3ljjvu+FXJLlu2jF69etG4cWMAhg0bxpIlS37VsURERERE6guNkUVETiyoGRgPPvgg69ev54477qBDhw44nU7KyspYv3498+fPp7S0lBkzZgQVsGvXrrW2//DDD4wYMYL9+/fTvXt3HnjggRrT3rZv306TJk0Cj5s0acLBgwc5dOgQCQkJQcUXEREREalvNEYWETmxoGZgfPXVV/zpT39izJgxnH/++XTv3p2+ffuSm5vLM888w+eff35KSTRr1ox+/foxc+ZMPvjgA0pLS3niiSdq7FdRUYHdbg88ttvtGIZBRUXFKcUXEREREalvNEYWEakuqBkYTqczMCXtlxo3bkxsbOwpJdGtWze6desWeDxmzBhGjRpVax4ulyvwuKqqCr/fj9PpPKX4IiIiIiL1jcbIIiLVBTUD45prrmHBggU12v1+PwsWLODaa689pST27NlDYWFh4LHX68VqrVlbad68OTt27Ag83r59O2lpacTHx59SfBERERGR+kZjZBGR6oKagVFVVcXSpUtZtmwZnTp1Ii4ujoqKCr799lu8Xi8XXHABDz30EACGYfDoo4+eVBJvvvkmP/30Ey+88AIWi4X58+fz29/+tsZ+/fv3589//jNbt26lRYsWzJkzhyFDhpxULBERERGR04HGyCIi1QVVwFiyZAkWy5HJGuvXrw+0G4aB1Wpl1apV1dqOpaCgoNp9q0eMGIFpmsydO5fnnnuOwYMHYxgG3bp1Y/z48QAsXbqU5cuXM3XqVNLT05k8eTK33norXq+XDh06MHHixJN7xSIiErQVK1YwZswYli1bRnZ2drVtX3zxBU8//TTl5eU0btyYqVOnkpGRUUeZioicvjRGFhEJTlAFjOXLl4ckWGpq6jFv6XSs+1sPGDCAAQMGBB4PGjSIQYMGhSQfEZGGqLS0lM2bN1NQUECfPn1wOp14PJ5apx0fT0VFBdOmTSMxMbHGtvLycu6++25effVVOnbsyLx585g8eTKvvPJKqF6GiMhp41T7XY2RRUSCE9QaGD8rLS1lz5495Ofn1/gjIiJ1y+128+ijj3Luuedy3XXXceedd3Lw4EF2797NpZdeyu7du0/qeNOnT+fyyy8nJiamxrYvv/ySnJwcOnbsCMCVV17JqlWrKC0tDclrERE5HYS63xURkeMLqoDxxRdf0K9fP84++2wuuugi+vXrF/jz82MREalbzz//PP/zP//DAw88wIcffkhUVBQAycnJtGjRgmnTpgV9rE2bNvH555/zhz/8odbt27dvJycnJ/A4JiaGxMREdu7ceUqvQUTkdBLKfldERE4sqHltjz76KE2bNuW+++4jISHhuLF7FVAAACAASURBVOtciIhI3fjwww+ZMmUK/fv3r9YeHR3NbbfdxsiRI4M6jt/vZ/LkyUycOBGbzVbrPhUVFTgcjmptDoeD8vLyX5e8iMhpKFT9roiIBCeoAsbevXt55ZVXaNKkSbjzERGRX6m0tJQ2bdrUui0hIYGKioqgjvPWW2/RqlUrevToccx9nE4nVVVV1doqKytrvdxERKShClW/KyIiwQnqEpJu3bqxffv2MKciIiKnolmzZixatKjWbStWrKBp06ZBHWfZsmUsW7aM3r1707t3b/bs2cOwYcP48ssvA/u0aNGi2uUiJSUlHDp0KOgYIiINQaj6XRERCU5QMzCeeOIJJkyYwMaNG2nXrh3R0dE19jn77LNDnpyIiATv+uuvZ+LEifz73//mnHPOwefzsWjRIvLz83n//fd5+OGHgzrOrFmzqj2+6KKLmDdvXrXbqJ5zzjlMmDCBb775hh49ejBnzhwuvPBCnE5nKF+SiEi9Fqp+V0REghNUAWPFihWsXr2aVatW1brdMAx++OGHkCYmIiInZ9iwYZimyaxZs1i6dClwZIG5Fi1a8PDDD3PllVee0vG///57XnjhBWbPnk1UVBTPPvssjz76KBUVFTRp0uSYt/oTEWmowt3viohIdUEVMKZPn86gQYMYMWKEFvEUEanHhg4dytChQyktLaWsrIzY2NhTXpdi+fLlAGRnZzN79uxA+znnnMOHH354SscWETndhaPfFRGR2gW1BkZFRQW33norHTt2JDs7m6ysrBp/RESkbn3wwQfs2bMHgNjYWNLT0wOD6NLSUh544IG6TE9EpMFRvysiEllBFTAuvfTSY14+IiIi9cP999/P0KFD+d///d8a2yorK/nggw/q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3ngj3HmKiMgJ7Nu3jzVr1rB27VrWrl3L5s2biY6OpmfPntx8882cd955QR/r+uuv5/rrr6/R/vHHHwd+vuKKK7jiiitCkruIyOkolP2uiIicWFAFjFatWrF48WLeeecd1q1bR0JCAnFxcVx66aVceeWVJCcnhztPERE5gQsuuAC73c5vfvMbBgwYwOTJk+ncuTMWS9B3zBYRkZOgfldEJLKCKmC8/fbbXHrppYwePTrc+YiIyK80e/Zsunfvftw7hYiISOio3xURiaygChgPP/wwU6ZMoU+fPlx22WX069cPh8MR7txEROQk9O7dmxUrVrBgwQL+/e9/U1paSlxcHJ06deLmm2+mZ8+edZ2iiEiDon5XRCSygprf9umnn3L//fdTXl7OvffeS69evRg3bhwrV67E6/WGO0cREQnCRx99xC233EJJSQmDBw/m5ptvZuDAgRQUFPCHP/xBi8mJiISY+l0RkcgKagZGampqYEG3oqIili5dyt///nduvfVW4uLiGDhwIJMnTw53riIichyzZs1i7Nix3HnnnTW2PfXUU0yfPp0LLrigDjITEWmY1O+KiETWSa8wlJSUxNVXX82sWbOYOXMm6enp/PWvfw1HbiIichK2bdvG0KFDa9129dVX8+OPP0Y4IxGRhk39rohIZAU1A+Nnbrebzz//nKVLl7J8+XKKi4vp2rUrEydODFd+IiISpOjoaIqLi2nSpEmNbSUlJdjt9jrISkSk4VK/KyISWUEVMBYvXsw//vEPPv30U0pLSznrrLMYPXo0l156Kenp6eHOUUREgnDOOefw1FNP8eSTT5KTkxNo37ZtG4899hi9evWqw+xERBoe9bsiIpEVVAHj7rvvpkOHDtxyyy1ceumlZGVlhTsvERE5Sffffz833HADF198MYmJicTGxlJSUsKhQ4do0qQJL7zwQl2nKCLSoKjfFRGJrKAKGH//+99rnRonIiL1R1ZWFosWLWLp0qVs2LCh2u38BgwYoKnMIiIhpn5XRCSyjlnAeOihh7j//vuJiYlh1qxZxz2IYRg8+uijIU9OREROTlRUFJdddhmXXXZZXaciInJGUL8rIhI5xyxgrFq1CpfLRUxMDKtWrYpkTiIiEqSbb775pPZ/7bXXwpSJiMiZQf2uiEjdOWYBY/ny5bX+LCIi9Yfb7a72eMuWLZSWltKyZUucTieHDx9m69atpKSk0KVLlzrKUkSk4VC/KyJSd4JaA+OGG27gxRdfJD4+vsa2zZs388ADD/Dee++FPDkRETm++fPnB35euHAhS5cu5ZlnniE2NjbQvm/fPiZMmMBFF11UFymKiDQo6ndFROqO5Xgb8/Pzyc/P5+uvv2bXrl2Bxz//2b17N6tXr2bz5s2RyldERI5h5syZ/PGPf6w2iAZIT0/nnnvuYcaMGXWUmYhIw6R+V0Qkso47A2Pw4MFUVlZiGAbDhg2rdR+/38+5554bluRERCR4+/btw+/317rNMAz27dsX4YxERBo29bsiIpF13ALGmjVr2LRpE0OHDuWRRx4hLi6uxj7x8fH07NkzbAmKiEhw2rZty0MPPcRDDz1Eu3btsNvtuFwu1q1bxzPPPEObNm3qOkURkQZF/a6ISGQdt4BhsVho37498+bNo2vXrthsthr7lJaW8vbbbzN8+PCwJSkiIic2ZcoUxo4dy+9//3vgyLd/fr8fv99PamqqpjKLiISY+l0RkcgKahHPnj174vF42Lx5M4cOHQq0+/1+vvvuO1588UUVMERE6li7du34+9//zldffcVPP/1EeXk5TqeT5s2bc+655+JwOOo6RRGRBkX9rohIZAVVwNi4cSO5ubns2bOn1u0DBgwIaVIiIvLr2Gw2+vTpQ58+feo6FRGRM4L6XRGRyAmqgPHUU0/Rvn17pk6dypgxY3jqqaewWCx89NFHJCQkMGXKlHDnKSIiJ1BUVMRLL73E999/X2223NE++eSTCGclItJwqd8VEYmsoAoYGzZs4M0336Rly5YYhkGHDh3IyclhwIABPPbYYzz33HPcdddd4c5VRESOY+LEiaxatYqePXvSokULDMOo65RERBo09bsiIpEVVAGjsrKS6OhoAJxOJ0VFReTk5AAwfPhwhg8frgKGiEgd++qrr5g+fTrnn39+XaciInJGUL8rIhJZlmB2atOmDX/9619xuVw0b96cd999N7Btx44dVFVVhS1BEREJjsPhoGnTpnWdhojIGUP9rohIZAVVwBgzZgyzZ88mLy+P6667jrfffptLL72Ua6+9lttuu42+ffuGO08RETmBoUOH8vHHH9d1GiIiZwz1uyIikRXUJSQDBgzg448/JisrixYtWmCxWFi8eDEul4vc3FxuvPHGoAO63W6mTZvG66+/zsqVK8nIyABg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ty+vvvoqaWlpYeckJibidrvbIDsRkfZH/a6ISNuIqICxefNmq/MQEZFWMAyD1atXk5OTs99zPvnkE4YMGRLFrERE2i/1uyIi0RfRJp4iInL4O++883j++efD2uvr65k5cyYTJ05sg6xERNov9bsiItGlAoaISDtx1VVXcd999zFx4kSKiooA+PDDDxkzZgzvv/8+jz76aBtnKCLSvqjfFRGJLhUwRETaiauvvprXX3+d5ORkzjvvPK655hquueYaRo8ezfLlyznzzDPbOkURkXZF/a6ISHSpgCEi0o5kZ2dzww03kJWVxTvvvMOJJ57I5MmTiYmJaevURETaJfW7IiLRs98Cxuuvv059fT3Q9KzrxsbGqCUlIiKHzuv1MnfuXMaOHcvAgQN59tlnqa2t5ZxzzuGVV15p6/RERNod9bsiItG136eQ3Hzzzbz66qt069aN6dOnM3To0H0+IkpERA4P55xzDgDz589vnrb8/PPPs2TJEu6++25eeuklnnnmmbZMUUSkXVG/KyISXfstYHTq1InLLruMrl27EgqFuPbaa3E6nfs81zAMlixZYlmSIiJycEOHDuWmm24iISGhuc0wDC677DKGDx/OnXfeGfG1Vq1axbx582hsbCQlJYW7776bXr16tTind+/e5OfnN7/OysrS7wIROaqY2e+KiMjB7beA8dBDD7Fo0SKqqqowDAOXy7XfAoaIiLS9u+++e7/HcnNzWbx4cUTX2bNnD7feeivPPfccPXr04Nlnn+XOO+/c56MC33jjjZ+dr4jIkc6sfldERCKz3wJG3759efDBBwEYNmwYDz30EKmpqVFLTEREDt1nn33G4sWL2bx5M6Wlpbzyyiukp6fzxBNPcP3110d0DYfDwZw5c+jRowcAgwcPZu7cuVamLSJyxDKj3xURkchE9BSS1atXk5qaSiAQYOvWrWzcuJGCggKCwaDV+YmISITefvttJkyYQFFREcOGDcPn8wFQWVnJ0qVLefzxxyO6Tnp6OkOHDm1+/d577zFw4MB9njtt2jRGjRrFhAkTWLduXeu/CRGRI4hZ/a6IiERmvzMwfiwYDPLggw+ydOlS6urqmtsTExOZOHEi1157basTeeONN3jooYdatG3bto21a9c2ryssKipi5MiR5ObmNp8zYMAA7r///lbHFxE50s2bN4/LL7+cadOmATQv+cjJyeGuu+7i/vvv56qrrjqka65Zs4YlS5bsc2+Liy66iAkTJtCnTx9ef/11pkyZwptvvklSUlLrvxkRkSOAFf3uj2l8LCLSUkQFjHnz5vH888/z29/+lv79+xMfH09tbS3r1q3jiSeeICYmhkmTJrUqkXPOOad5J2doeozrihUrWmyKBE2bxGnNtYhIuIKCgv0u9TjmmGPYvXv3IV3vrbfeYtasWTz22GPNy0l+bNasWc1/HjVqFI8++ijr169v3olfRKS9M7vf/SmNj0VEWoqogPHqq68yc+ZMfvnLX7ZoP+uss+jWrRtPPPFEqwsYP+b1enn44Yd54oknTLumiEh7l5GRQWFhIV27dg07tmPHDpKTkyO+1kcffcS9997LU089Rffu3cOO19XVsWfPHrp169bcFggEcDgi+rUiItIumNnvHozGxyIiEe6BsXfvXgYNGrTPYyeffDLFxcWmJvXiiy8yaNAgunTpEnastraWqVOncs455zBp0iS2bt1qamwRkSPVqaeeysyZM1m9ejV1dXUYhkFjYyPr16/nnnvuYfjw4RFdx+PxMH36dObPn7/P4gXA7t27GTduHIWFhQB88MEHVFRU7HevDBGR9sisfjcSGh+LiEQ4AyMtLY2CggI6d+4cdmzLli2mPp0kGAzy1FNP8dhjj4Udi4+PZ8yYMVxxxRXk5OTw9NNPM3XqVJYvX65P/UTkqHfLLbdQWFjI1KlTMQyDUCjEmDFjADjhhBO4+eabI7rOqlWrKC8vb17T/b1FixZx9dVXs2zZMrp3785tt93GlClTCAaDJCcns3DhwrBpzSIi7ZlZ/e7BaHwsItIkol5t5MiR3H777Vx//fUcd9xxJCQkUFNTw7p165g/fz6jR482LaH169cTFxdHz549w46lpqZy5513Nr++/PLLWbBgAdu3b9/n+mwRkaNJQkICf/3rX9m4cSMbNmygtraWxMREBgwYwIABAyK+zpgxY5oH4D+1bNmy5j+ff/75nH/++a3OW0TkSGVWv3swGh+LiDSJqIAxbdo0SktLmTFjRot2wzAYM2YMN954o2kJvfPOO/vdAK6qqorq6uoWuywHg0FVl0VEgOnTp3P77bfvc+C8bds25s6dy7x589ooOxGR9ida/a7GxyIiTSLaA8PtdjN37lzeeecdFixYwH333cfChQt5++23uf/++3G5XKYl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\n", "text/plain": [ "
" ] }, "metadata": { "tags": [] }, "output_type": "display_data" } ], "source": [ "fig = plt.figure(figsize=(15,5))\n", "\n", "ax = fig.add_subplot(131)\n", "negative, ns_exp, recall = aggregate_z(\"negative\", \"ns_exponent\")\n", "cm = sns.scatterplot(x=ns_exp, y=negative, hue=recall, palette=color_palette, legend=None)\n", "ax.set_xlabel(\"Negative sampling exponent\", fontsize=16)\n", "ax.set_ylabel(\"Number of negative samples\", fontsize=16)\n", "plt.xticks(fontsize=12)\n", "plt.yticks(fontsize=12)\n", "ax.plot(0.75, 5, \n", " marker='*', \n", " color=cldr_colors[1],\n", " markersize=10)\n", "ax.plot(best_config['ns_exponent'], \n", " best_config['negative'], \n", " marker=\"o\", \n", " fillstyle='none', \n", " color=cldr_colors[0],\n", " markersize=15)\n", "ax = fig.add_subplot(132)\n", "\n", "window, ns_exp, recall = aggregate_z(\"window\", \"ns_exponent\")\n", "cm = sns.scatterplot(x=ns_exp, y=window, hue=recall, palette=color_palette, legend=None)\n", "ax.set_xlabel(\"Negative sampling exponent\", fontsize=16)\n", "ax.set_ylabel(\"Context window size\", fontsize=16)\n", "plt.xticks(fontsize=12)\n", "plt.yticks(fontsize=12)\n", "ax.plot(0.75, 5, \n", " marker='*', \n", " color=cldr_colors[1],\n", " markersize=10)\n", "ax.plot(best_config['ns_exponent'], \n", " best_config['window'], \n", " marker=\"o\", \n", " fillstyle='none', \n", " color=cldr_colors[0],\n", " markersize=15)\n", "\n", "ax = fig.add_subplot(133)\n", "window, negative, recall = aggregate_z(\"window\", \"negative\")\n", "cm = sns.scatterplot(x=window, y=negative, hue=recall, palette=color_palette, legend=None)\n", "ax.set_xlabel(\"Number of negative examples\", fontsize=16)\n", "ax.set_ylabel(\"Context window size\", fontsize=16)\n", "plt.xticks(fontsize=12)\n", "plt.yticks(fontsize=12)\n", "ax.plot(5, 5, \n", " marker='*',\n", " color=cldr_colors[1],\n", " markersize=10)\n", "ax.plot(best_config['window'], \n", " best_config['negative'], \n", " marker=\"o\", \n", " fillstyle='none', \n", " color=cldr_colors[0],\n", " markersize=15);\n", "\n", "plt.tight_layout()\n", "plt.savefig(\"hpsweep_results.png\", transparent=True, dpi=150)" ] }, { "cell_type": "markdown", "metadata": { "id": "-5KwIKIl36gA" }, "source": [ "And there we have it! Each panel shows the Recall@10 scores (where yellow is a high score and purple is a low score) associated with a unique configuration of hyperparameters. The best hyperparameter values for the Online Retail Data Set are denoted by the light blue circle. Word2vec’s default values are shown by the orange star. In all cases, the orange star is nowhere near the light blue circle, indicating that the default values are not optimal for this dataset." ] } ], "metadata": { "colab": { "authorship_tag": "ABX9TyNmIBT1S3jeboIbbIKzkpye", "collapsed_sections": [], "name": "2021-06-11-recostep-session-based-recommender-using-word2vec.ipynb", "provenance": [], "toc_visible": true }, "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.7.3" } }, "nbformat": 4, "nbformat_minor": 4 }