From c52670884dab428ed79356a9d858f22cb3f41255 Mon Sep 17 00:00:00 2001 From: Kishan Savant <66986430+NeoKish@users.noreply.github.com> Date: Wed, 17 Aug 2022 06:54:50 +0530 Subject: [PATCH] Example of converting H&M Fashion Prediction Kaggle notebook to a Kubeflow pipeline (#976) * Added files via upload * Modified Kale NB * Updated the README.md and Kale NB * Updated README.md * Added a file size check for downloaded files in KaleNB * Fixed minor grammatical errors * Fixed a minor grammatical error * Updated the download link for house-prices kfp notebook * Fixed the error in Kale NB --- h-and-m-fash-rec-kaggle-competition/README.md | 179 +++++ .../h&m-fash-rec-kale.ipynb | 675 ++++++++++++++++++ .../h&m-fash-rec-kfp.ipynb | 614 ++++++++++++++++ .../h&m-fash-rec-orig.ipynb | 439 ++++++++++++ .../images/kaggle_api_token.PNG | Bin 0 -> 13345 bytes .../images/kale_cell_metadata.PNG | Bin 0 -> 18886 bytes .../images/kale_deployment_panel.PNG | Bin 0 -> 57775 bytes .../images/kale_pipeline_graph.PNG | Bin 0 -> 6504 bytes .../images/kfp_client.PNG | Bin 0 -> 6651 bytes .../images/kfp_pipeline_func.PNG | Bin 0 -> 25275 bytes .../images/kfp_pipeline_graph.PNG | Bin 0 -> 6426 bytes .../requirements.txt | 5 + .../resource.yaml | 16 + .../house-prices-kfp.ipynb | 2 +- 14 files changed, 1929 insertions(+), 1 deletion(-) create mode 100644 h-and-m-fash-rec-kaggle-competition/README.md create mode 100644 h-and-m-fash-rec-kaggle-competition/h&m-fash-rec-kale.ipynb create mode 100644 h-and-m-fash-rec-kaggle-competition/h&m-fash-rec-kfp.ipynb create mode 100644 h-and-m-fash-rec-kaggle-competition/h&m-fash-rec-orig.ipynb create mode 100644 h-and-m-fash-rec-kaggle-competition/images/kaggle_api_token.PNG create mode 100644 h-and-m-fash-rec-kaggle-competition/images/kale_cell_metadata.PNG create mode 100644 h-and-m-fash-rec-kaggle-competition/images/kale_deployment_panel.PNG create mode 100644 h-and-m-fash-rec-kaggle-competition/images/kale_pipeline_graph.PNG create mode 100644 h-and-m-fash-rec-kaggle-competition/images/kfp_client.PNG create mode 100644 h-and-m-fash-rec-kaggle-competition/images/kfp_pipeline_func.PNG create mode 100644 h-and-m-fash-rec-kaggle-competition/images/kfp_pipeline_graph.PNG create mode 100644 h-and-m-fash-rec-kaggle-competition/requirements.txt create mode 100644 h-and-m-fash-rec-kaggle-competition/resource.yaml diff --git a/h-and-m-fash-rec-kaggle-competition/README.md b/h-and-m-fash-rec-kaggle-competition/README.md new file mode 100644 index 000000000..82a62d1c7 --- /dev/null +++ b/h-and-m-fash-rec-kaggle-competition/README.md @@ -0,0 +1,179 @@ +# Kaggle Featured Prediction Competition: H&M Personalized Fashion Recommendations + +In this [competition](https://www.kaggle.com/competitions/h-and-m-personalized-fashion-recommendations), product recommendations have to be done based on previous purchases. There's a whole range of data available including customer meta data, product meta data, and meta data that spans from simple data, such as garment type and customer age, to text data from product descriptions, to image data from garment images. + +In this notebook we will be working with implicit's ALS library for our recommender systems. Please do check out the [docs](https://benfred.github.io/implicit/index.html) for more information. + +## Prerequisites for Building the Kubeflow Pipeline + +If you don’t already have Kubeflow up and running, we recommend signing up for a free trial of Arrikto's [Kubeflow as a Service](https://www.arrikto.com/kubeflow-as-a-service/). For the following example, we are using Kubeflow as a Service, but you should be able to run this example on any Kubeflow distribution. + +## Testing environment + +| Name | version | +| ------------- |:-------------:| +| Kubeflow | v1.4 | +| kfp | 1.8.11 | +| kubeflow-kale | 0.6.0 | + +## Initial Steps + +1. Please follow the Prerequisites section to get Kubeflow running. +2. Create a new Jupyter Notebook server with following resources + - CPU : 1 + - RAM : 32GB + - Workspace Volume : 50GB +3. Once you have the Jupyter Notebook server running, connect to it. +4. Clone this repo from the Terminal, so you have access to this directory. +5. Now before heading to Vanilla KFP steps, we need to save our Kaggle API credentials as a secret so that we can use the Kaggle Public [API](https://github.com/Kaggle/kaggle-api/blob/master/kaggle/api/kaggle_api_extended.py) to download the files from the Kaggle competition for our KFP/Kale pipeline. Following are the steps: + - If you are not a Kaggle user, you will first need to create a Kaggle account. After creation of the account, go to your Kaggle Account page and scroll down to API section. + +

+ +

+ + - Click on Create New API Token. A new API token in the form of kaggle.json file will be created which you can save locally. The kaggle.json file contains your Kaggle username and key. + - Once you have the API credentials, run the following command in the terminal with the username and key from the kaggle.json file that you just saved. + + ``` + kubectl create secret generic kaggle-secret --from-literal=KAGGLE_USERNAME= --from-literal=KAGGLE_KEY= + + ``` + This creates a secret for our credentials which can then be mounted on our pods. + + - Next create a yaml file with the following code in it. This would then be used to create a pod-default resource to mount the secret to any pod with a specific label(in our case kaggle-secret =true) + + ``` + apiVersion: "kubeflow.org/v1alpha1" + kind: PodDefault + metadata: + name: kaggle-access + spec: + selector: + matchLabels: + kaggle-secret: "true" + desc: "kaggle-access" + volumeMounts: + - name: secret-volume + mountPath: /secret/kaggle + volumes: + - name: secret-volume + secret: + secretName: kaggle-secret + + ``` + - To create a pod-default resource, run the following command, + + ``` + kubectl apply -f + + ``` + You can check out the following [link](https://support.arrikto.com/hc/en-us/articles/6335158153489-Acessing-External-System-with-User-Credentials-Kaggle-Example-) for more details about accessing external system with user credentials. +6. With the completion of 5th step, you are good to start with Vanilla KFP steps. + + + +## Vanilla KFP version + +To start building out a Kubeflow pipeline, you need to get yourself acquainted with the Kubeflow Pipelines [documentation](https://www.kubeflow.org/docs/components/pipelines/sdk/build-pipeline/) to understand what the pipelines are, its components, what goes into these components. There are different ways to build out a pipeline component as mentioned [here](https://www.kubeflow.org/docs/components/pipelines/sdk/build-pipeline/#building-pipeline-components). In the following example, we are going to use the [lightweight python functions](https://www.kubeflow.org/docs/components/pipelines/sdk/python-function-components/) based components for building up the pipeline. + +### Step 1: Install the Kubeflow Pipeline SDK and import the required kfp packages to run the pipeline + +From kfp, we will be using [func_to_container_op](https://kubeflow-pipelines.readthedocs.io/en/stable/source/kfp.components.html#kfp.components.func_to_container_op) which would help in building the factory function from the python function and we will use [InputPath](https://kubeflow-pipelines.readthedocs.io/en/stable/source/kfp.components.html#kfp.components.InputPath) and [OutputPath](https://kubeflow-pipelines.readthedocs.io/en/stable/source/kfp.components.html#kfp.components.OutputPath) from the components package to pass the paths of the files or models to these tasks. The [passing of data](https://www.kubeflow.org/docs/components/pipelines/sdk/python-function-components/#pass-data) is being implemented by kfp’s supported data passing mechanism. InputPath and OutputPath is how you pass on the data or model between the components. For [passing values](https://www.kubeflow.org/docs/components/pipelines/sdk/python-function-components/#passing-parameters-by-value), we are using NamedTuples which allows us to send multiple values between components. + +### Step 2: Next build out the pipeline components + +Our Kubeflow pipeline is broken down into five pipeline components: + +- Download the data from Kaggle +- Load and Preprocess the data +- Creating Sparse Matrix +- Train data +- Predictions + +We convert each python function to a factory function using the func_to_container_op which will then be converted to a pipeline task for our pipeline function. + +### Step 3 : Creating pipeline function + +After building all the pipeline components, we have to define a pipeline function connecting all the pipeline components with appropriate inputs and outputs. This when run would generate the pipeline graph. + +Pipeline function: + +

+ +

+ + +### Step 4 : Running the pipeline using the kfp.client instance + +There are different ways to run the pipeline function as mentioned in the [documentation](https://www.kubeflow.org/docs/components/pipelines/sdk/build-pipeline/#compile-and-run-your-pipeline). We would run the pipeline using the Kubeflow Pipelines SDK client. + +

+ +

+ +Once all the cells are executed successfully, you should see two hyperlinks ‘Experiment details’ and ‘Run details’. Click on ‘Run details’ link to observe the pipeline running. + +The final pipeline graph would look as follow: + +

+ +

+ +## Kale KFP version + +For the Kaggle notebook example, we are using [Kubeflow as a Service](https://www.arrikto.com/kubeflow-as-a-service/). If you are using Kubeflow as a Service then Kale comes preinstalled. For users with a different Kubeflow setup, you can refer to the [GitHub link](https://github.com/kubeflow-kale/kale#getting-started) for installing the Kale JupyterLab extension on your setup. + +### Step 1: Install all the required packages + +Run the first code cell to install all the required packages (not available under the standard python library) by using the requirements.txt file. Restart the kernel after installation. + +### Step 2: Download the data from Kaggle + +Run the second code cell to download the relevant data from Kaggle using the Kaggle Public API. You will require the API credentials from the kaggle.json file you got earlier in the Initial Steps. For the Kale notebook version, you don't have to create the secret, just need the API credentials to download the data. Once the code cell is run, you should see a new "data" directory being created with the zip files downloaded and unzipped. Please ensure that you run the cell only once so you don't create nested directories. Restart the kernel before running the code cell again. + +### Step 3: Annotate the notebook with Kale tags + +The Kale notebook in the directory is already annotated. To see the annotations, open up the Kale Deployment panel and click on the Enable switch button. Once you have it switched on, you should see the following: + +

+ +

+ +Please take time to understand how each cell is annotated by clicking on the cell and checking out the tag being used and what are is its dependencies. Kale provides us with six tags for annotations: + +- Imports +- Functions +- Pipeline Parameters +- Pipeline Metrics +- Pipeline Step +- Skip Cell + +You can also see the tags being created by checking out the Cell Metadata by clicking on the Property Inspector above the Kale Deployment Panel button. + +

+ +

+ +### Step 2: Run the Kubeflow Pipeline + +Once you’ve tagged your notebook, click on the “Compile and Run” button in the Kale widget. Kale will perform the following tasks for you: + +- Validate the notebook +- Take a snapshot +- Compile the notebook +- Upload the pipeline +- Run the pipeline + +In the “Running pipeline” output, click on the “View” hyperlink. This will take you directly to the runtime execution graph where you can watch your pipeline execute and update in real-time. + +

+ +

+ +## Note: +Both notebooks have been tested out and the whole pipeline run for both the Vanilla KFP and the Kale KFP versions take around 2hrs. Most of the time is being consumed in the predictions pipeline stage. In case of any error, please test out with the following docker image. + +Notebook server docker image used: gcr.io/arrikto/jupyter-kale-py36@sha256:dd3f92ca66b46d247e4b9b6a9d84ffbb368646263c2e3909473c3b851f3fe198 + +If the error persists, please raise an issue. diff --git a/h-and-m-fash-rec-kaggle-competition/h&m-fash-rec-kale.ipynb b/h-and-m-fash-rec-kaggle-competition/h&m-fash-rec-kale.ipynb new file mode 100644 index 000000000..007261ef6 --- /dev/null +++ b/h-and-m-fash-rec-kaggle-competition/h&m-fash-rec-kale.ipynb @@ -0,0 +1,675 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "tags": [] + }, + "source": [ + "# Kaggle Featured Prediction Competition: H&M Personalized Fashion Recommendations" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "tags": [] + }, + "source": [ + "In this [competition](https://www.kaggle.com/competitions/h-and-m-personalized-fashion-recommendations), product recommendations have to be done based on previous purchases. There's a whole range of data available including customer meta data, product meta data, and meta data that spans from simple data, such as garment type and customer age, to text data from product descriptions, to image data from garment images.\n", + "\n", + "In this notebook we will be working with implicit's ALS library for our recommender systems. Please do check out the [docs](https://benfred.github.io/implicit/index.html) for more information." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "tags": [] + }, + "source": [ + "## Install necessary packages\n", + "\n", + "We can install the necessary package by either running `pip install --user ` or include everything in a `requirements.txt` file and run `pip install --user -r requirements.txt`. We have put the dependencies in a `requirements.txt` file so we will use the former method.\n", + "\n", + "Restart the kernel after installation" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "tags": [ + "skip" + ] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Requirement already satisfied: numpy in /usr/local/lib/python3.6/dist-packages (from -r requirements.txt (line 1)) (1.19.5)\n", + "Requirement already satisfied: pandas in /usr/local/lib/python3.6/dist-packages (from -r requirements.txt (line 2)) (1.1.5)\n", + "Collecting implicit\n", + " Downloading implicit-0.5.2-cp36-cp36m-manylinux2014_x86_64.whl (18.6 MB)\n", + " |████████████████████████████████| 18.6 MB 8.6 MB/s \n", + "\u001b[?25hCollecting sklearn\n", + " Downloading sklearn-0.0.tar.gz (1.1 kB)\n", + " Preparing metadata (setup.py) ... \u001b[?25ldone\n", + "\u001b[?25hCollecting kaggle\n", + " Downloading kaggle-1.5.12.tar.gz (58 kB)\n", + " |████████████████████████████████| 58 kB 8.3 MB/s \n", + "\u001b[?25h Preparing metadata (setup.py) ... \u001b[?25ldone\n", + "\u001b[?25hRequirement already satisfied: pytz>=2017.2 in /usr/local/lib/python3.6/dist-packages (from pandas->-r requirements.txt (line 2)) (2021.3)\n", + "Requirement already satisfied: python-dateutil>=2.7.3 in /usr/local/lib/python3.6/dist-packages (from pandas->-r requirements.txt (line 2)) (2.8.2)\n", + "Collecting tqdm>=4.27\n", + " Downloading tqdm-4.64.0-py2.py3-none-any.whl (78 kB)\n", + " |████████████████████████████████| 78 kB 7.2 MB/s \n", + "\u001b[?25hRequirement already satisfied: scipy>=0.16 in /usr/local/lib/python3.6/dist-packages (from implicit->-r requirements.txt (line 3)) (1.5.4)\n", + "Requirement already satisfied: scikit-learn in /usr/local/lib/python3.6/dist-packages (from sklearn->-r requirements.txt (line 4)) (0.23.2)\n", + "Requirement already satisfied: six>=1.10 in /usr/local/lib/python3.6/dist-packages (from kaggle->-r requirements.txt (line 5)) (1.16.0)\n", + "Requirement already satisfied: certifi in /usr/local/lib/python3.6/dist-packages (from kaggle->-r requirements.txt (line 5)) (2021.10.8)\n", + "Requirement already satisfied: requests in /usr/local/lib/python3.6/dist-packages (from kaggle->-r requirements.txt (line 5)) (2.27.1)\n", + "Collecting python-slugify\n", + " Downloading python_slugify-6.1.2-py2.py3-none-any.whl (9.4 kB)\n", + "Requirement already satisfied: urllib3 in /usr/local/lib/python3.6/dist-packages (from kaggle->-r requirements.txt (line 5)) (1.26.8)\n", + "Requirement already satisfied: importlib-resources in /usr/local/lib/python3.6/dist-packages (from tqdm>=4.27->implicit->-r requirements.txt (line 3)) (5.4.0)\n", + "Collecting text-unidecode>=1.3\n", + " Downloading text_unidecode-1.3-py2.py3-none-any.whl (78 kB)\n", + " |████████████████████████████████| 78 kB 10.0 MB/s \n", + "\u001b[?25hRequirement already satisfied: charset-normalizer~=2.0.0 in /usr/local/lib/python3.6/dist-packages (from requests->kaggle->-r requirements.txt (line 5)) (2.0.10)\n", + "Requirement already satisfied: idna<4,>=2.5 in /usr/local/lib/python3.6/dist-packages (from requests->kaggle->-r requirements.txt (line 5)) (3.3)\n", + "Requirement already satisfied: threadpoolctl>=2.0.0 in /usr/local/lib/python3.6/dist-packages (from scikit-learn->sklearn->-r requirements.txt (line 4)) (3.0.0)\n", + "Requirement already satisfied: joblib>=0.11 in /usr/local/lib/python3.6/dist-packages (from scikit-learn->sklearn->-r requirements.txt (line 4)) (1.1.0)\n", + "Requirement already satisfied: zipp>=3.1.0 in /usr/local/lib/python3.6/dist-packages (from importlib-resources->tqdm>=4.27->implicit->-r requirements.txt (line 3)) (3.6.0)\n", + "Building wheels for collected packages: sklearn, kaggle\n", + " Building wheel for sklearn (setup.py) ... \u001b[?25ldone\n", + "\u001b[?25h Created wheel for sklearn: filename=sklearn-0.0-py2.py3-none-any.whl size=2381 sha256=b097d9ed6edd1c879d42180dffc20aa2659b2628f9711ae4204ce4b1cb636dc2\n", + " Stored in directory: /home/jovyan/.cache/pip/wheels/23/9d/42/5ec745cbbb17517000a53cecc49d6a865450d1f5cb16dc8a9c\n", + " Building wheel for kaggle (setup.py) ... \u001b[?25ldone\n", + "\u001b[?25h Created wheel for kaggle: filename=kaggle-1.5.12-py3-none-any.whl size=73994 sha256=6e228faf71a3c29b864d64cfe8fe5b59a1257cfc4afb24f7c4712943489a5710\n", + " Stored in directory: /home/jovyan/.cache/pip/wheels/77/47/e4/44a4ba1b7dfd53faaa35f59f1175e123b213ff401a8a56876b\n", + "Successfully built sklearn kaggle\n", + "Installing collected packages: text-unidecode, tqdm, python-slugify, sklearn, kaggle, implicit\n", + "Successfully installed implicit-0.5.2 kaggle-1.5.12 python-slugify-6.1.2 sklearn-0.0 text-unidecode-1.3 tqdm-4.64.0\n" + ] + } + ], + "source": [ + "!pip install --user -r requirements.txt" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "tags": [] + }, + "source": [ + "## Download Data from Kaggle" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "tags": [] + }, + "source": [ + "Download relevant data from kaggle by running the below code cell. Follow the initial steps information mentioned in Github README.md to get the Kaggle username and key for authentication of Kaggle Public API. There's no need of secret to be created for the following step. The credentials will be present in the kaggle.json file. This cell needs to be run before starting Kale pipeline from Kale deployment panel. Please ensure that you run the cell only once so you don't create nested directories. Restart the kernel before running the code cell again. " + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "tags": [ + "skip" + ] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "customers.csv.zip: Skipping, found more recently modified local copy (use --force to force download)\n", + "transactions_train.csv.zip: Skipping, found more recently modified local copy (use --force to force download)\n", + "articles.csv.zip: Skipping, found more recently modified local copy (use --force to force download)\n", + "sample_submission.csv.zip: Skipping, found more recently modified local copy (use --force to force download)\n", + "Unzipping the files ...\n", + "\n", + "\n", + "Checking the files are extracted properly ...\n", + "\n", + "\n", + "sample_submission.csv 258MB 258MB\n", + "customers.csv 198MB 198MB\n", + "transactions_train.csv 3GB 3GB\n", + "articles.csv 34MB 34MB\n", + "All files are downloaded and unzipped inside the data directory. Please move on to next step\n" + ] + } + ], + "source": [ + "import os\n", + "\n", + "\n", + "# Get the Kaggle Username and password from the kaggle.json file\n", + "# and paste it in place of KAGGLE_USERNAME AND KAGGLE_KEY on right hand side\n", + "\n", + "os.environ['KAGGLE_USERNAME'] = \"KAGGLE_USERNAME\"\n", + "os.environ['KAGGLE_KEY'] = \"KAGGLE_KEY\"\n", + "\n", + "path = \"data/\"\n", + "\n", + "os.chdir(os.getcwd())\n", + "os.system(\"mkdir \" + path)\n", + "os.chdir(path)\n", + "\n", + "import kaggle\n", + "from kaggle.api.kaggle_api_extended import KaggleApi\n", + "api = KaggleApi()\n", + "api.authenticate()\n", + "\n", + "# Getting the files list from Kaggle using Kaggle api\n", + "file_list = api.competition_list_files('h-and-m-personalized-fashion-recommendations')\n", + "\n", + "# Download the required files individually. You can also choose to download the entire dataset if you want to work with image data as well. The files will be in downloaded \n", + "api.competition_download_file('h-and-m-personalized-fashion-recommendations','customers.csv')\n", + "api.competition_download_file('h-and-m-personalized-fashion-recommendations','transactions_train.csv')\n", + "api.competition_download_file('h-and-m-personalized-fashion-recommendations','articles.csv')\n", + "api.competition_download_file('h-and-m-personalized-fashion-recommendations','sample_submission.csv') \n", + "\n", + "print(\"Unzipping the files ...\")\n", + "\n", + "# Get the path of the directory where the files are downloaded\n", + "path_dir = os.getcwd()\n", + "\n", + "from zipfile import ZipFile \n", + "\n", + "# Extracting all files from individual zip files\n", + "zipfile1 = ZipFile(path_dir + '/customers.csv.zip', 'r')\n", + "zipfile1.extract(\"customers.csv\")\n", + "zipfile1.close()\n", + " \n", + "zipfile2 = ZipFile(path_dir + '/transactions_train.csv.zip', 'r')\n", + "zipfile2.extract(\"transactions_train.csv\")\n", + "zipfile2.close()\n", + " \n", + "zipfile3 = ZipFile(path_dir + '/articles.csv.zip', 'r')\n", + "zipfile3.extract(\"articles.csv\")\n", + "zipfile3.close()\n", + " \n", + "zipfile4 = ZipFile(path_dir + '/sample_submission.csv.zip', 'r')\n", + "zipfile4.extract(\"sample_submission.csv\")\n", + "zipfile4.close()\n", + "\n", + "print(\"Checking the files are extracted properly ...\")\n", + "\n", + "for file in os.listdir(path_dir):\n", + " filename = os.fsdecode(file)\n", + " if filename.endswith(\".csv\"):\n", + " file_size = os.path.getsize(path_dir + \"/\" + filename)\n", + " if file_size< 1e9:\n", + " file_size = str(round(file_size/(1024*1024))) + \"MB\"\n", + " else:\n", + " file_size = str(round(file_size/(1024*1024*1024))) + \"GB\"\n", + " for file in file_list:\n", + " if file.name == filename and file.size == file_size:\n", + " print(file.name,file.size, file_size)\n", + "\n", + "print(\"All files are downloaded and unzipped inside the data directory. Please move on to next step\")\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "tags": [] + }, + "source": [ + "## Imports" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "imports" + ] + }, + "outputs": [], + "source": [ + "import numpy as np\n", + "import pandas as pd\n", + "import implicit \n", + "import scipy.sparse as sparse" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "tags": [] + }, + "source": [ + "## Load Data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "block:load_and_preprocess_data" + ] + }, + "outputs": [], + "source": [ + "path = \"data/\"\n", + "train_data_filepath = path + \"transactions_train.csv\"\n", + "article_metadata_filepath = path + \"articles.csv\"\n", + "customer_metadata_filepath = path + \"customers.csv\"\n", + "test_data_filepath = path + \"sample_submission.csv\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "train_data = pd.read_csv(train_data_filepath)\n", + "test_data = pd.read_csv(test_data_filepath)\n", + "customer_data = pd.read_csv(customer_metadata_filepath)\n", + "article_data = pd.read_csv(article_metadata_filepath)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "tags": [] + }, + "source": [ + "## Exploring the dataset" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "skip" + ] + }, + "outputs": [], + "source": [ + "train_data.head()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "skip" + ] + }, + "outputs": [], + "source": [ + "train_data.info()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "skip" + ] + }, + "outputs": [], + "source": [ + "train_data.describe()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "skip" + ] + }, + "outputs": [], + "source": [ + "test_data.tail()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "skip" + ] + }, + "outputs": [], + "source": [ + "customer_data.tail()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "skip" + ] + }, + "outputs": [], + "source": [ + "article_data.tail()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# We will be dropping t_dat, sales_channel and price as this won't be part of the recommendation system we will be building \n", + "train_data.drop(['t_dat','sales_channel_id','price'], axis= 1, inplace = True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "skip" + ] + }, + "outputs": [], + "source": [ + "train_data.head()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "tags": [] + }, + "source": [ + "## Preprocess Data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "block:" + ] + }, + "outputs": [], + "source": [ + "# create a new purchase count column that would gives us count of every article bought by the customers\n", + "X = train_data.groupby(['customer_id', 'article_id'])['article_id'].count().reset_index(name = \"purchase_count\") \n", + "\n", + "# Getting unique number of customers and articles using the customer and article metadata data files\n", + "unique_customers = customer_data['customer_id'].unique()\n", + "unique_articles = article_data['article_id'].unique()\n", + "\n", + "# length of the customers and articles\n", + "n_customers = len(unique_customers)\n", + "n_articles = len(unique_articles)\n", + "\n", + "# Create a mapping for customer_id to convert it from an object column to an int column for the sparse matrix creation\n", + "customer_id_dict = {unique_customers[i]:i for i in range(len(unique_customers))}\n", + "reverse_customer_id_dict = {i:unique_customers[i] for i in range(len(unique_customers))} \n", + "numeric_cus_id = []\n", + "for i in range(len(X['customer_id'])):\n", + " numeric_cus_id.append(customer_id_dict.get(X['customer_id'][i]))\n", + "X['customer_id'] = numeric_cus_id\n", + "\n", + "# Create a mapping for article_id so that the sparse matrix creation doesn't get large enough due to long int values of article_ids\n", + "article_id_dict = {unique_articles[i]:i for i in range(len(unique_articles))}\n", + "reverse_article_id_dict = {i:unique_articles[i] for i in range(len(unique_articles))}\n", + "numeric_art_id = []\n", + "for i in range(len(X['article_id'])):\n", + " numeric_art_id.append(article_id_dict.get(X['article_id'][i]))\n", + "X['article_id'] = numeric_art_id" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "skip" + ] + }, + "outputs": [], + "source": [ + "X.head()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "tags": [] + }, + "source": [ + "## Sparse Matrix Creation" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "block:sparse_matrix_creation", + "prev:load_and_preprocess_data" + ] + }, + "outputs": [], + "source": [ + "# Constructing sparse matrices for alternating least squares algorithm \n", + "sparse_user_item_coo = sparse.coo_matrix((X.purchase_count, (X.customer_id, X.article_id)), shape = (n_customers, n_articles))\n", + "sparse_user_item_csr = sparse.csr_matrix((X['purchase_count'], (X['customer_id'], X['article_id'])), shape = (n_customers, n_articles))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "skip" + ] + }, + "outputs": [], + "source": [ + "sparse_user_item_csr" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "tags": [] + }, + "source": [ + "## Model Training" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "block:train_model", + "prev:sparse_matrix_creation" + ] + }, + "outputs": [], + "source": [ + "# parameters for the model\n", + "als_params = dict(\n", + " factors = 200, # number of latent factors - try between 50 to 1000\n", + " regularization = 0.01, # regularization factor - try between 0.001 to 0.2\n", + " iterations = 5, # iterations - try between 2 to 100\n", + ")\n", + "\n", + "# initialize a model\n", + "model = implicit.als.AlternatingLeastSquares(**als_params)\n", + "\n", + "# train the model on a sparse matrix of user/item/confidence weights \n", + "model.fit(sparse_user_item_csr)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "skip" + ] + }, + "outputs": [], + "source": [ + "model" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "tags": [] + }, + "source": [ + "## Predictions" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "block:predictions", + "prev:sparse_matrix_creation", + "prev:train_model", + "prev:load_and_preprocess_data" + ] + }, + "outputs": [], + "source": [ + "predictions=[]\n", + "count = 0\n", + "for cust_id in test_data.customer_id:\n", + " cust_id = customer_id_dict.get(cust_id)\n", + " if(cust_id!=None): \n", + " recommendations = model.recommend(cust_id, sparse_user_item_csr[cust_id],10)\n", + " result=[]\n", + " for i in range(len(recommendations[0])):\n", + " val = reverse_article_id_dict.get(recommendations[0][i])\n", + " result.append(val) \n", + " predictions.append(result)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "test_data['prediction'] = predictions\n", + "test_data" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "tags": [] + }, + "source": [ + "### Final Submission" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "skip" + ] + }, + "outputs": [], + "source": [ + "test_data.to_csv('data/submission.csv', index=False)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "kubeflow_notebook": { + "autosnapshot": true, + "docker_image": "gcr.io/arrikto/jupyter-kale-py36@sha256:dd3f92ca66b46d247e4b9b6a9d84ffbb368646263c2e3909473c3b851f3fe198", + "experiment": { + "id": "new", + "name": "hm-fash-recomm" + }, + "experiment_name": "hm-fash-recomm", + "katib_metadata": { + "algorithm": { + "algorithmName": "grid" + }, + "maxFailedTrialCount": 3, + "maxTrialCount": 12, + "objective": { + "objectiveMetricName": "", + "type": "minimize" + }, + "parallelTrialCount": 3, + "parameters": [] + }, + "katib_run": false, + "pipeline_description": "", + "pipeline_name": "predict-hm-purchases-kale-1", + "snapshot_volumes": true, + "steps_defaults": [ + "label:access-ml-pipeline:true", + "label:access-rok:true" + ], + "volume_access_mode": "rwm", + "volumes": [ + { + "annotations": [], + "mount_point": "/home/jovyan", + "name": "hm-test2-workspace-6vjtz", + "size": 50, + "size_type": "Gi", + "snapshot": false, + "type": "clone" + } + ] + }, + "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.6.9" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/h-and-m-fash-rec-kaggle-competition/h&m-fash-rec-kfp.ipynb b/h-and-m-fash-rec-kaggle-competition/h&m-fash-rec-kfp.ipynb new file mode 100644 index 000000000..7c9c9fdcf --- /dev/null +++ b/h-and-m-fash-rec-kaggle-competition/h&m-fash-rec-kfp.ipynb @@ -0,0 +1,614 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "tags": [] + }, + "source": [ + "# Kaggle Featured Prediction Competition: H&M Personalized Fashion Recommendations" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "tags": [] + }, + "source": [ + "In this [competition](https://www.kaggle.com/competitions/h-and-m-personalized-fashion-recommendations), product recommendations have to be done based on previous purchases. There's a whole range of data available including customer meta data, product meta data, and meta data that spans from simple data, such as garment type and customer age, to text data from product descriptions, to image data from garment images.\n", + "\n", + "In this notebook we will be working with implicit's ALS library for our recommender systems. Please do check out the [docs](https://benfred.github.io/implicit/index.html) for more information" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "tags": [] + }, + "source": [ + "Install the kfp package by uncommenting the below line and restarting the kernel. Do comment it out once the kernel is restarted" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Install the kfp \n", + "# !pip install kfp --upgrade " + ] + }, + { + "cell_type": "markdown", + "metadata": { + "tags": [] + }, + "source": [ + "Following are the imports required to build the pipeline and pass the data between components for building up the kubeflow pipeline" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "import kfp\n", + "from kfp.components import func_to_container_op\n", + "import kfp.components as comp\n", + "from typing import NamedTuple" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "tags": [] + }, + "source": [ + "All the essential imports required in a pipeline component are put together in a list which then is passed on to each pipeline component. Though this might not be efficient when you are dealing with lot of packages, so in cases with many packages and dependencies you can go for docker image which then can be passed to each pipeline component" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "import_packages = ['pandas', 'sklearn', 'implicit', 'kaggle', 'numpy', 'pyarrow']" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "tags": [] + }, + "source": [ + "In the following implementation of kubeflow pipeline we are making use of [lightweight python function components](https://www.kubeflow.org/docs/components/pipelines/sdk/python-function-components/) to build up the pipeline. The data is passed between component instances(tasks) using InputPath and OutputPath. Different ways of storing and passing data between the pipelines have been explored in the following notebook." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "tags": [] + }, + "source": [ + "The pipeline is divided into five components\n", + "\n", + " 1. Download data from Kaggle\n", + " 2. Load and preprocess the data\n", + " 3. Creating sparse matrix\n", + " 4. Train model\n", + " 5. Predictions" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "tags": [] + }, + "source": [ + "### Download the data from Kaggle" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "tags": [] + }, + "source": [ + "Follow the prerequisites information in the Github README.md on how to create a secret for our credentials and mounting them to our pod using a pod-default resource. Once you have the secret mounted, you can use it to acccess the Username and key to download the files you need from kaggle. For the following competition, we have downloaded the files required instead of downloading the whole thing." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "def download_kaggle_dataset(path:str)->str:\n", + " \n", + " import os\n", + "\n", + " # Retrieve the credentials from the secret mounted and \n", + " # bring it onto our working environment\n", + " with open('/secret/kaggle/KAGGLE_KEY', 'r') as file:\n", + " kaggle_key = file.read().rstrip()\n", + " with open('/secret/kaggle/KAGGLE_USERNAME', 'r') as file:\n", + " kaggle_user = file.read().rstrip()\n", + " os.environ['KAGGLE_USERNAME'] = kaggle_user \n", + " os.environ['KAGGLE_KEY'] = kaggle_key\n", + "\n", + " os.chdir(os.getcwd())\n", + " os.system(\"mkdir \" + path)\n", + " os.chdir(path)\n", + " \n", + " # Using Kaggle Public API to download the datasets\n", + " import kaggle \n", + " from kaggle.api.kaggle_api_extended import KaggleApi\n", + " \n", + " api = KaggleApi()\n", + " api.authenticate()\n", + " \n", + " # Download the required files individually. You can also choose to download the entire dataset if you want to work with images as well. \n", + " api.competition_download_file('h-and-m-personalized-fashion-recommendations','customers.csv')\n", + " api.competition_download_file('h-and-m-personalized-fashion-recommendations','transactions_train.csv')\n", + " api.competition_download_file('h-and-m-personalized-fashion-recommendations','articles.csv')\n", + " api.competition_download_file('h-and-m-personalized-fashion-recommendations','sample_submission.csv') \n", + " \n", + " return path \n", + " \n" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "download_data_op = func_to_container_op(download_kaggle_dataset, packages_to_install = import_packages)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "tags": [] + }, + "source": [ + "### Load and Preprocess the data" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "def load_and_preprocess_data(path:str, preprocess_data_path: comp.OutputPath('ApacheParquet'))->NamedTuple('Outputs', [('data_path',str),('int_list', list)]):\n", + " \n", + " \n", + " import pandas as pd\n", + " import os\n", + " from zipfile import ZipFile \n", + " from pyarrow import parquet\n", + " import pyarrow as pa\n", + " \n", + " # Moving to current working directory and creating a new directory\n", + " os.chdir(os.getcwd())\n", + " print(os.listdir(path))\n", + " os.chdir(path)\n", + " \n", + " # Extracting all files from individual zip files\n", + " zipfile1 = ZipFile('customers.csv.zip', 'r')\n", + " zipfile1.extract(\"customers.csv\")\n", + " zipfile1.close()\n", + " \n", + " zipfile2 = ZipFile('transactions_train.csv.zip', 'r')\n", + " zipfile2.extract(\"transactions_train.csv\")\n", + " zipfile2.close()\n", + " \n", + " zipfile3 = ZipFile('articles.csv.zip', 'r')\n", + " zipfile3.extract(\"articles.csv\")\n", + " zipfile3.close()\n", + " \n", + " zipfile4 = ZipFile('sample_submission.csv.zip', 'r')\n", + " zipfile4.extract(\"sample_submission.csv\")\n", + " zipfile4.close()\n", + " \n", + " # Converting to pandas dataframe \n", + " customer_data = pd.read_csv(\"customers.csv\")\n", + " article_data = pd.read_csv(\"articles.csv\")\n", + " train_data = pd.read_csv(\"transactions_train.csv\") \n", + " \n", + " # create a new purchase count column that would gives us count of every article bought by the customers\n", + " X = train_data.groupby(['customer_id', 'article_id'])['article_id'].count().reset_index(name = \"purchase_count\") \n", + "\n", + " # Getting unique number of customers and articles using the customer and article metadata data files\n", + " unique_customers = customer_data['customer_id'].unique()\n", + " unique_articles = article_data['article_id'].unique()\n", + " \n", + " # length of the customers and articles\n", + " n_customers = len(unique_customers)\n", + " n_articles = len(unique_articles)\n", + "\n", + " # Create a mapping for customer_id to convert it from an object column to an int column for the sparse matrix creation\n", + " customer_id_dict = {unique_customers[i]:i for i in range(len(unique_customers))}\n", + " reverse_customer_id_dict = {i:unique_customers[i] for i in range(len(unique_customers))} \n", + " numeric_cus_id = []\n", + " for i in range(len(X['customer_id'])):\n", + " numeric_cus_id.append(customer_id_dict.get(X['customer_id'][i]))\n", + " X['customer_id'] = numeric_cus_id\n", + "\n", + " # Create a mapping for article_id so that the sparse matrix creation doesn't get large enough due to long int values of article_ids\n", + " article_id_dict = {unique_articles[i]:i for i in range(len(unique_articles))}\n", + " rev_art_id_dict = {i:int(unique_articles[i]) for i in range(len(unique_articles))}\n", + " numeric_art_id = []\n", + " for i in range(len(X['article_id'])):\n", + " numeric_art_id.append(article_id_dict.get(X['article_id'][i]))\n", + " X['article_id'] = numeric_art_id\n", + " \n", + " # Convert from pandas to Arrow\n", + " table = pa.Table.from_pandas(X)\n", + " parquet.write_table(table, preprocess_data_path)\n", + " \n", + " values=[n_customers, n_articles]\n", + " \n", + " return (path, values)\n", + " " + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "load_and_preprocess_data_op = func_to_container_op(load_and_preprocess_data,packages_to_install = import_packages)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "tags": [] + }, + "source": [ + "### Creating sparse matrix" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "def sparse_matrix_creation(data_path:str, list_val: list, file_path: comp.InputPath('ApacheParquet'), sparse_path: comp.OutputPath())->str:\n", + " \n", + " import pandas as pd\n", + " from pyarrow import parquet\n", + " import pyarrow as pa\n", + " import scipy.sparse as sparse\n", + " from scipy.sparse import coo_matrix\n", + " from pathlib import Path\n", + " import pickle\n", + " \n", + " X = parquet.read_pandas(file_path).to_pandas()\n", + " \n", + " n_customers = list_val[0]\n", + " n_articles = list_val[1]\n", + "\n", + " # Constructing sparse matrices for alternating least squares algorithm \n", + " sparse_user_item_coo = sparse.coo_matrix((X.purchase_count, (X.customer_id, X.article_id)), shape = (n_customers, n_articles))\n", + " sparse_user_item_csr = sparse.csr_matrix((X['purchase_count'], (X['customer_id'], X['article_id'])), shape = (n_customers, n_articles))\n", + "\n", + " pickle.dump(sparse_user_item_csr, open(sparse_path, 'wb'))\n", + " \n", + " return data_path " + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "sparse_matrix_creation_op = func_to_container_op(sparse_matrix_creation, packages_to_install = import_packages)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "tags": [] + }, + "source": [ + "### Train the Model" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "def train_model(path:str, sparse_matrix_path: comp.InputPath(), model_path: comp.OutputPath())->str:\n", + " \n", + " import implicit\n", + " import pandas as pd\n", + " from pyarrow import parquet\n", + " import pyarrow as pa\n", + " import scipy.sparse as sparse\n", + " import pickle\n", + " \n", + " # Loading the sparse user item matrix from pickle\n", + " sparse_user_item_csr = pickle.load(open(sparse_matrix_path, 'rb'))\n", + " \n", + " # parameters for the model\n", + " als_params = dict(\n", + " factors = 200, # number of latent factors - try between 50 to 1000\n", + " regularization = 0.01, # regularization factor - try between 0.001 to 0.2\n", + " iterations = 5, # iterations - try between 2 to 100\n", + " )\n", + "\n", + " # initialize a model\n", + " model = implicit.als.AlternatingLeastSquares(**als_params)\n", + "\n", + " # train the model on a sparse matrix of user/item/confidence weights \n", + " model.fit(sparse_user_item_csr)\n", + " \n", + " pickle.dump(model, open(model_path, 'wb'))\n", + " \n", + " return path" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "train_model_op = func_to_container_op(train_model, packages_to_install = import_packages)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "tags": [] + }, + "source": [ + "### Predictions" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [], + "source": [ + "def predictions(test_path:str, model_path : comp.InputPath(), sparse_path: comp.InputPath()):\n", + " \n", + " import pandas as pd\n", + " import os\n", + " from zipfile import ZipFile \n", + " import pickle\n", + " from pyarrow import parquet\n", + " import pyarrow as pa\n", + " import scipy.sparse as sparse\n", + " \n", + " sparse_user_item_csr = pickle.load(open(sparse_path, 'rb'))\n", + " model = pickle.load(open(model_path, 'rb'))\n", + " \n", + " os.chdir(os.getcwd())\n", + " print(os.listdir(test_path))\n", + " os.chdir(test_path)\n", + "\n", + " # Converting to pandas dataframe \n", + " customer_data = pd.read_csv(\"customers.csv\")\n", + " article_data = pd.read_csv(\"articles.csv\") \n", + " test_data = pd.read_csv(\"sample_submission.csv\")\n", + " \n", + " # Getting unique number of customers and articles using the customer and article metadata data files\n", + " unique_customers = customer_data['customer_id'].unique()\n", + " unique_articles = article_data['article_id'].unique()\n", + " \n", + " # length of the customers and articles\n", + " n_customers = len(unique_customers)\n", + " n_articles = len(unique_articles)\n", + " \n", + " # Create a mapping for customer_id\n", + " customer_id_dict = {unique_customers[i]:i for i in range(len(unique_customers))}\n", + "\n", + " # Create a reverse mapping for article_id\n", + " reverse_article_id_dict = {i:int(unique_articles[i]) for i in range(len(unique_articles))}\n", + "\n", + " predictions=[]\n", + " count = 0\n", + " for cust_id in test_data.customer_id:\n", + " cust_id = customer_id_dict.get(cust_id)\n", + " if(cust_id!=None): \n", + " recommendations = model.recommend(cust_id, sparse_user_item_csr[cust_id],10)\n", + " result=[]\n", + " for i in range(len(recommendations[0])):\n", + " val = reverse_article_id_dict.get(recommendations[0][i])\n", + " result.append(val) \n", + " predictions.append(result)\n", + " \n", + " test_data['prediction'] = predictions\n", + " test_data" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [], + "source": [ + "prediction_op = func_to_container_op(predictions, packages_to_install = import_packages)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "tags": [] + }, + "source": [ + "### Defining function that implements the pipeline" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [], + "source": [ + "def kfp_pipeline():\n", + " \n", + " vop = kfp.dsl.VolumeOp(\n", + " name=\"create-volume\", \n", + " resource_name=\"mypvc\",\n", + " size=\"10Gi\",\n", + " modes = kfp.dsl.VOLUME_MODE_RWM\n", + " )\n", + " \n", + " download_task = download_data_op(\"/mnt/data/\").add_pvolumes({\"/mnt\": vop.volume}).add_pod_label(\"kaggle-secret\", \"true\")\n", + " load_and_preprocess_data_task = load_and_preprocess_data_op(download_task.output).add_pvolumes({\"/mnt\": vop.volume})\n", + " sparse_matrix_task = sparse_matrix_creation_op(data_path =load_and_preprocess_data_task.outputs['data_path'], \n", + " file = load_and_preprocess_data_task.outputs['preprocess_data'], \n", + " list_val = load_and_preprocess_data_task.outputs['int_list']).add_pvolumes({\"/mnt\": vop.volume})\n", + " train_model_task = train_model_op(path = sparse_matrix_task.outputs['Output'], \n", + " sparse_matrix = sparse_matrix_task.outputs['sparse']).add_pvolumes({\"/mnt\": vop.volume})\n", + " prediction_task = prediction_op(test_path = train_model_task.outputs['Output'],\n", + " model = train_model_task.outputs['model'], \n", + " sparse = sparse_matrix_task.outputs['sparse']).add_pvolumes({\"/mnt\": vop.volume})\n", + " \n", + " " + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "Experiment details." + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "Run details." + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "RunPipelineResult(run_id=61cfeaf5-5caa-49f2-89c1-c3f3e9e8d5b5)" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Using kfp.Client() to run the pipeline from notebook itself\n", + "client = kfp.Client() # change arguments accordingly\n", + "\n", + "# Running the pipeline\n", + "client.create_run_from_pipeline_func(\n", + " kfp_pipeline,\n", + " arguments={\n", + " })" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "kubeflow_notebook": { + "autosnapshot": true, + "docker_image": "gcr.io/arrikto/jupyter-kale-py36@sha256:dd3f92ca66b46d247e4b9b6a9d84ffbb368646263c2e3909473c3b851f3fe198", + "experiment": { + "id": "", + "name": "" + }, + "experiment_name": "", + "katib_metadata": { + "algorithm": { + "algorithmName": "grid" + }, + "maxFailedTrialCount": 3, + "maxTrialCount": 12, + "objective": { + "objectiveMetricName": "", + "type": "minimize" + }, + "parallelTrialCount": 3, + "parameters": [] + }, + "katib_run": false, + "pipeline_description": "", + "pipeline_name": "", + "snapshot_volumes": true, + "steps_defaults": [ + "label:access-ml-pipeline:true", + "label:access-rok:true" + ], + "volume_access_mode": "rwm", + "volumes": [ + { + "annotations": [], + "mount_point": "/home/jovyan", + "name": "hm-fash-workspace-fhh9d", + "size": 50, + "size_type": "Gi", + "snapshot": false, + "type": "clone" + } + ] + }, + "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.6.9" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/h-and-m-fash-rec-kaggle-competition/h&m-fash-rec-orig.ipynb b/h-and-m-fash-rec-kaggle-competition/h&m-fash-rec-orig.ipynb new file mode 100644 index 000000000..b06da44ce --- /dev/null +++ b/h-and-m-fash-rec-kaggle-competition/h&m-fash-rec-orig.ipynb @@ -0,0 +1,439 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "tags": [] + }, + "source": [ + "# Kaggle Featured Prediction Competition: H&M Personalized Fashion Recommendations" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "tags": [] + }, + "source": [ + "In this [competition](https://www.kaggle.com/competitions/h-and-m-personalized-fashion-recommendations), product recommendations have to be done based on previous purchases. There's a whole range of data available including customer meta data, product meta data, and meta data that spans from simple data, such as garment type and customer age, to text data from product descriptions, to image data from garment images." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "tags": [] + }, + "source": [ + "## Install necessary packages\n", + "\n", + "We can install the necessary package by either running `pip install --user ` or include everything in a `requirements.txt` file and run `pip install --user -r requirements.txt`. We have put the dependencies in a `requirements.txt` file so we will use the former method.\n", + "\n", + "Restart the kernel after installation" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# !pip install --user -r requirements.txt" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "tags": [] + }, + "source": [ + "## Imports" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np # linear algebra\n", + "import pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n", + "import implicit" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "path = \"data/\"\n", + "train_data_filepath = path + \"transactions_train.csv\"\n", + "article_metadata_filepath = path + \"articles.csv\"\n", + "customer_metadata_filepath = path + \"customers.csv\"\n", + "test_data_filepath = path + \"sample_submission.csv\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "train_data = pd.read_csv(train_data_filepath,index_col='customer_id')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "train_data.head()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "train_data.drop(['t_dat','sales_channel_id','price'],axis= 1, inplace = True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "train_data.head()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "train_data=train_data.sort_values(by=['customer_id']).reset_index()\n", + "train_data.head()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(\"Unique customers\",train_data['customer_id'].nunique())\n", + "print(\"Unique articles\",train_data['article_id'].nunique())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "train_data.info()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "X = train_data.groupby(['customer_id', 'article_id'])['article_id'].count().reset_index(name = \"purchase_count\") " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "unique_customers = X['customer_id'].unique()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "unique_articles = X['article_id'].unique()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "customer_id_dict = {unique_customers[i]:i for i in range(len(unique_customers))}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "reverse_customer_id_dict = {i:unique_customers[i] for i in range(len(unique_customers))} " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "numeric_cus_id = []" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for i in range(len(X['customer_id'])):\n", + " numeric_cus_id.append(customer_id_dict.get(X['customer_id'][i]))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(X['customer_id'].nunique())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "len(numeric_cus_id)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "X['customer_id'] = numeric_cus_id" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "X.head()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "article_id_dict = {unique_articles[i]:i for i in range(len(unique_articles))}\n", + "reverse_article_id_dict = {i:unique_articles[i] for i in range(len(unique_articles))} " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "numeric_art_id = []" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for i in range(len(X['article_id'])):\n", + " numeric_art_id.append(article_id_dict.get(X['article_id'][i]))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "X['article_id'] = numeric_art_id\n", + "X.head()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Constructing sparse matrices for alternating least squares algorithm \n", + "import scipy.sparse as sparse\n", + "sparse_user_item_coo = sparse.coo_matrix((X.purchase_count, (X.customer_id, X.article_id)), shape = (n_customers, n_articles))\n", + "sparse_user_item_csr = sparse.csr_matrix((X['purchase_count'], (X['customer_id'], X['article_id'])), shape = (n_customers, n_articles))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# parameters for the model\n", + "als_params = dict(\n", + " factors = 200, # number of latent factors - try between 50 to 1000\n", + " regularization = 0.01, # regularization factor - try between 0.001 to 0.2\n", + " iterations = 5, # iterations - try between 2 to 100\n", + ")\n", + "\n", + "# initialize a model\n", + "model = implicit.als.AlternatingLeastSquares(**als_params)\n", + "\n", + "# train the model on a sparse matrix of user/item/confidence weights \n", + "model.fit(sparse_user_item_csr)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "test_data = pd.read_csv(test_data_filepath)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "test_data.head()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "predictions=[]\n", + "count = 0\n", + "for cust_id in test_data.customer_id:\n", + " cust_id = customer_id_dict.get(cust_id)\n", + "# if(cust_id!=None): \n", + " recommendations = model.recommend(cust_id, sparse_user_item_csr[cust_id],10)\n", + " result=[]\n", + " for i in range(len(recommendations[0])):\n", + " val = reverse_article_id_dict.get(recommendations[0][i])\n", + " result.append(val) \n", + " predictions.append(result)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "test_data['prediction'] = predictions\n", + "test_data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "test_data.to_csv('submission.csv', index=False)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "kubeflow_notebook": { + "autosnapshot": true, + "docker_image": "gcr.io/arrikto/jupyter-kale-py36@sha256:dd3f92ca66b46d247e4b9b6a9d84ffbb368646263c2e3909473c3b851f3fe198", + "experiment": { + "id": "", + "name": "" + }, + "experiment_name": "", + "katib_metadata": { + "algorithm": { + "algorithmName": "grid" + }, + "maxFailedTrialCount": 3, + "maxTrialCount": 12, + "objective": { + "objectiveMetricName": "", + "type": "minimize" + }, + "parallelTrialCount": 3, + "parameters": [] + }, + "katib_run": false, + "pipeline_description": "", + "pipeline_name": "", + "snapshot_volumes": true, + "steps_defaults": [ + "label:access-ml-pipeline:true", + "label:access-rok:true" + ], + "volume_access_mode": "rwm", + "volumes": [ + { + "annotations": [], + "mount_point": "/home/jovyan", + "name": "hm-fash-workspace-fhh9d", + "size": 50, + "size_type": "Gi", + "snapshot": false, + "type": "clone" + } + ] + }, + "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.6.9" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/h-and-m-fash-rec-kaggle-competition/images/kaggle_api_token.PNG b/h-and-m-fash-rec-kaggle-competition/images/kaggle_api_token.PNG new file mode 100644 index 0000000000000000000000000000000000000000..b842e8aee211eb2d409c06afefa8ef7c38fa1473 GIT binary patch literal 13345 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 S^;N&-Y<0!%a@D0f$^Qq8em`IU literal 0 HcmV?d00001 diff --git a/h-and-m-fash-rec-kaggle-competition/images/kale_cell_metadata.PNG b/h-and-m-fash-rec-kaggle-competition/images/kale_cell_metadata.PNG new file mode 100644 index 0000000000000000000000000000000000000000..b43addab22622ebd566d99a2128e24a6b3be3c0c GIT binary patch literal 18886 zcmcG$cOcdA|36B~Job!Z?`+w771=V9nU%dIn{qhzh>Xl+CnG|#*C8{qg>#5x6OP&M zb^3h1zwf>O-2d($j&^i3#Zmv9Pd+A84pO#KOXk1YgqlxZp27;@a5Y z8@BI5brr1YQRpW4<))*ujxrWjT{6+7%`NadftSW(Uo0#NKg<{Q(o^ODEUX9S57d;6 z0<3oO@Uo1JGaA1XI9>+^Jq>bmgqE2vNN6|BuS2_F(l#pk!-QNA$XtS<-RI3pRu6y`1~_tA6#($Kn^n5-TP#AKe>BYQGU|8Y{= z2w!G#7o&3XCXaH1clVuUsx7nRgZ%^6vmLeRJX#O>_fHl)H)54-IB}+l13jH)8+Kh~ ztwvLy>&C--mAKM4yDy)QiscLaJ~29S=j6qmZF#n(lFTIEU6N@F(R05efgI5sKX(sjZ`!{V%E7mmAsLMzTxi8=-ulm$kXx z@6*Vh&Z3^(a(8^=(%#O|hlOYlyE@Mn$N`aoyd)@#!s#@(V*w$g7^3lQzufiyc;HzOI9(UIHT@xQDuSlIUH7_`Mw#CA8?BW4@^dIgf915RAbZJ6Bb zu{kuiZV&l{yP5jHT zGQqohC>D_vx&ix-Y+16;pxjAypaGCwc+3rVXmdqMU=Px`9+T#8)pRI??04a?k6^R29vxDqrvLE~O*Mx^Jq9uahG|{Cp{}Y{ZbQFkOka`#t>d zh_v0xwl@CX4QdaEZ|ts}?XcrRj+J7c%+}g)-Gs42CSQg`dZy8BvtiE1Z+NsE#E*}I zN75h??xd8Ybu%ivmxcMVH_OMsVAgM)fOqs>{K3_~w^AN0%NjyD)MQ@@Ar??q4@a7` zAZ#ci{q%UGO`IYT4pf z=PgbnXyJr(`Y=X{iV<>Y3B(Mtp?=#9<}ri~p(rVzNX$y{CDz@i!}wrCkvy_|_WIcm zAG<&D)8d{>a(ZN#t6>h|Bjpm&z)8J`ZM#0-nid_)41BmVZSMIA`6#gNopL1ZTQEo^ z5)<7mzxaQ;?o?_$xOMXHQVXxaajGHMWH?JJtFJxUhgWHYj$TAFcrk-;&nM5#hWxEBSph02kd#rB;;u1UR0_^DzP zytrz+cv*KHlVY(wDenqY*D>1)yH4}RClk?dgXe~6+=!*Z(fz8IfTL5OAm4g2?|RaO zuSs}Myt{t6!FlqKV)*+iF4#BN2sj=>zEb?pPY-=}IURH{oljQ8w%V@E83{=RyU(kL zUKTB|BgJsAhz$Ja%^Hx895gunw6@t@8(E;K9^Vtm6*a^iQiFJHZ$iVAxkLtDkgF?R z{V~j;Rf&oFI{m@C2lULO%tg1hZ3+%2&XW4dDg}gf$m^phnED0~?j&S~*Y7*GsVHe9 z!3dj|B+xg~B#4OwZE4KW^ZbK(ldf$FK_wT33P-H_LSw}n9!iu8n>fpm{l_>LDTh)Q zrBO0LCKlY^W_Mua<;_BdGZX(G_pbJ@jVL1d=ca5|5RSjshp1nF_dPtbRCY1*e-;Ge z5xC5*-nS+DYdSO`MPAzKn370G2Jg9ElBY(wzs^Iy@k%5VwNzw{OZuy8BsxH$iQDRk zm7Nb=+h`^<9}8gPGpVXY3Kz1;D(T6RW+)1O`5kiE+#1t^GfH~v&fzgN8{@V1qX3cF z_Rud^Plo2bx}iCgLk;$wIx%@fB?)zQi1{doP~`(}x0PA2*XD#a-PSO_*EhDAAG}NO z_cZ*k<53v#r3`sLb9E}adi2360!~25H5=J4I+NVQ!^+@&(lDGaFMIh@D7-G9YlbM5 zih>=N112K=b)GIQ#!kqhS=du5PFr{MRM}#;2U(n^D=NEHRlmK6xg*J3pRc^gUP?U( ztyO?cmdvxtsF8KE0JHUx99k0jKl+r={bQAM39!5HcD(STfghZqRxXSJL|CwMl^!#E z)=RqjnQ8shSad%pw%n-3N`BF+hxqN&kvIO3eW|Pa^$BUK{m3kZi|zW_)1}}Xy4t)) zUky*Y@V5|$4ydlI)8o#MHSs-t7heK{P|cl9h>PIdigg<)Zq%2(_KR+Eb!aNBo}7cM z$}mn}Ogu4z1Vr5BJN|o<45#Y@<`FT5ui8L~>X6t}Xw7eD*E^lJ z-k7d>d^d+Xf#}Ac-&1_Kp(k@D0xH$$ou=hRnVq+)a5G+HuJ0(pJN4woeL)Fra{A=( zv%{ZnLGK|G?vCOczc=|bF@WlecW3bXHO?yL@_k@*C&S!>psXM)sNCekn;+UN3W_tB zUEacHkkiCL)xzi?z-+L>m#Hkz|4rYw)<9o#y!O8z9frEyn|@?u(o_!MXPW6y>CMe( zDLw!3>{0Xi@An_XIzKjLYlCIzd$r|4QoO-D>I)^Ate>V4t#ydE)Y0(W5s!{5f7dVG ztksTl&XH0tT6xk@8f=C+THtS*m!W9V6f$Xpc)6z3jW5gJ~Lnx55Er4VxIdu0bdNbd=y!#FlZRz|Bwl)GOqpJR zf`4ugiR^HtpA^Z((TdlSlu@JN>7p+(j<345fhl;qj)oLDq0B&(Vgi+)K}_DWJHNiz z^F#z4^wH`(@mjRVLH58G!uAK)lTDjtp8bY_%}ZSF8oi2yQR)i6n~!Z?AI_#EQ9!S> zzQQIw4!hWKRJ@<0I=D6}yEja^t2TT71_aV!v}GrEP+?hXm_u z{qUB+$m^*eQ}}XMO>oitWJ4X#f?-0=8>MD`AI+29*Q?m89iF06OrfZ?i?>fKcVvH` zhTrtbmTgDc;L*lKdL!MF7;H=AgS#CMN;j)mC^&H~LK60SU#Rw0AjaDci{b@826jKB zRu;^0p*cCSP}F?`m37C3t=dQfsN6Fo)ZsS7_o%wz3IiyHNDdMhBn1KJXP}X6^RWur zM`OP-^>Na=T3!f=P?WahT2{cuVLw|s5eDMpr&5<`D8(rW%NTVL^87m ztz(#zQBPe`b&2;N7VA5|{wpNWbCQFNOcPd7&amnLrWb&>6@+^nwF~BKjGY1$f}L$r zb1;zJ`x*j}QgH>%+sYf<8mqj9nuK^zFC{BW1!GmC;FWZ2ZanBhFeUcEtNKzVrk3Y> z9>yZY*{j~G`5}i7hJIj1GV*XpsA(TcgpCd5-@2m9rR|rQAZXykAaRMkq%2|WKo%z) zX)qc;e0Uj{r0mI?aOZY4GaGu|wPsEsYom5k$H)}NCyWzEQ(nq5Oqi+6dW%#6-Ps$l zsKBNsduwZznd&H6{v@j;@rXM=l0=55CcXFb!+Qv6*1~kEOH&V(bg^rTvwFe<-yGuh zaOZgJ`{DvqD8J!O^iJ{oD^4xI08{Biyx_T%H4=Nn z`=cV~3&6(aM0Lr$SFlm7B8s@2I8XpO+y(eO)$QLgk(Sku=pO4F6=UD0yow!Rn!JX5-WTZE~ z!35g#h2wZZ{bd+)U)L%RkqC?GeQE*A&59{6gBXF`;a5p&uOZFZi&*q9uOO?a)%Aro zUB+@0`f4xyZ#=GkVH{in$0I_uP5J`)ebZ9yN`Z`GOjc_>wC6p)BTyo7}DxmA^QwA zvURx{5Xg;vpc3+6IpOwQZ7SL4yRBd*LJRy1Ww15L540SuM18Houb%N`dylYo{2?^3 zI&f@k=<9qRTi${znsZ5WmRD+~%u#8-l0<~KRQ!9SNb@LR8ETY%?p$fyY#_N5V23^` z`0FO^SQ+loc5JeBOHDVcC48SqTQ7V0`lM;S^?{JLEnx0Mf3KGRx~ojSQzsp|wVHl9 zYe!0Xt1$iLlYqg9BR1X2YA4unGmm$Q0)08JvzLP855Ke>)ucDOrBV-DI?ntnGxWn( zuvuqx#<$%^E6 zl^lO708i_e5140CUFn(A*!WH(9zylmY_p~2@ZP?gCz=)dpKj#W#I3N!y8aUTfrY|Y z$7p=8f8j7kDB*i;yG5=P;k(-AIG?Il+N4xJA-%$$s7n_aAx)>AV|- zS#XVY)(m~>tnTbTt1$HG%wISTHHCYNNOK*9xh-cpXcAOxWq&L{AQ8!{8kvIpKhC8D zho_iA3cl|!F$bLgFg=m#>M22OO_mTmT-BkCc5e9t#Dbon4vDC~ts@m9s>^HvK9O-P zM-_Ynl2kf-X9^rvHHvaS+lZr+itscRiDf3Zz`#G&qR-ol1>Z?xgpaw7bG%X5xMrj3 z7u}lEZT=oQnT;V-Hz|a0>)0KkOcYv?%s+@WH*{xITf{|w>PSu3=TWkUv;^3uVR(>I zQC}yclp$1Bm04H}XZ2L>pqEl&fL=)Rz=zuK{Cj==&L2K*xr@Go-h&Gn42ddA(-7%Y zMv&y*VTQ^I*^9{C5E4B=QF<;Pa#b-1$=BuYw05c$Rmj9GP@tMa9|KlW{>v!t74~Q@ zIhQvY@~(5A7;+Pl-nFtAB7a|M$-*Hwzxj$KX?f-W1V%=kr3jN3 z>Bm{w-A3}H{d6k2FZlte08R{NOVtno%Mw(or}*KOY6TYeo?&IoVZ9V|5=bIT^x2kV zt;H-NWGWm{fNAQTZQE|Ii1fZV*t;z!FlD-jLcZ+0cAsyk6D;$-%=%ATnd_)^4~!U2 zMIyh8%-{GW((9C;PXDls?%lJ8^!uT+%rBmB=%6^mFr@MUN29%x5KFXEe`+vE!3owp z30eV5D7%jIzG;D@d=Dk%=mP0%K>%IpTre%>ksh$6+ID`y4vCHPC{DjA2T%Uyo?f#Z zAO+djJ*wh<3yVKxXKI&@Fb$B>{vVe6`{ZmKpV_9u^2>>kCaxeZ2fg~Pv4X-F!2z{! zcONGX3K`rRlb{J&qBzx5q|=!lckP#9Uj!^=|FSnC4Wp~xp^UBOLixV(7B?`^#H~7g z^B_?g$Sj+PH!8)-PhQdX0Hr_Eg)5 z$fsYs=*3;^_waj@+JHXt7Hl=hRCOi-AfBx)K!RD8IX|tN;Zl(RiLt_KO^ruunzqB8d>?i z`s>{cUTXahYA-D{9)5Xs-YVqCj=LB!JNq1w#2DEBahapwsR~pb>giUo5IWb?*Na>g z-t2y=^_WR3=4;&FdW68~= zTU<%gQy@@h4sD3Nt>5kZtte_FcrlDWclQ9n%*+&i>1|#bhby=E^)+A5W5+xP#I5Nv zrc4Kv-EZTE2SNNKdx#Tv*lM%e>b(wCQh70KJ>f#JG&qnl zMh5BY%i$h$ScP|A|A}@VmfUu(w6DtRJH{$CR&D$W37-6ZyNh#zUM*5>mQMhC{Ko?) z%k-KY=I^1)Nzue7SBijR?_2*pww{*SJ?J0xyF?!#B|FY^maeOgR(sJh^fM2hSC$u} zHd)%~Dv`P}n;aJ>YGDD3@w7rDK+CQ_Vb(|-O2x-F2)UC;m!1I{#v%mDcv0X#?`*Tu z`9ij-82(lm&2)E9u6dQ41S)rNAw%D>DSs<0GE|cng+XYq@VappLmGYW-VP^#FMM>+ zT39E^OuqJ7Y+4E$8oljgQ5G77Mo;6Qej*9tMMfz(1^2~RSbFa|jkDx%?_lf=cMDQY z0#Cpy0|4Qx`bxs1EZjO!hhSm=Jn$(g^-Q)T0*qDn<mmM=E2%rhFkVsZ(_8_FbW2nR;OqR?KresH`!f1@W#F$D_hfG&0#D`~ z1t3U0xdXNLA5{jVY56IOzeu|8OQS-S^w)|5NEH&cE7FHRd0W7L zpa1?T<9bnN`dd-2h|%6y${quCMu#c!qxF&c$~WKJ#4Z0018k7>gOn5e%4uxh)%SNrnc3*lTR*Q(Y&_H0D6!E3#bu^D zc&WjgC7Evdc6ov8*@4bT(_HnCHr|p1sPx48>aj}9OK)aY4E0oj&~e9#qnek82D|(np*JK!pyTZBF#{JKpes zN$mmFXevvcwufGrwUjHcB^s%d<81+v0J|~&6%L{(h`_=kiY#hXLB%%Z z?Q1o0g93%nVP_votg<-vqX5(XI??$%&rYR6OSLli>vsAoxY!`0IiAvKlU$j4a%=70 zE8{@M^g=K`pOv$dWmQ;PoXh+T4dVDVPEys=<$Lr%NHWWX2j#%<>~A`>Sz_&+*8ywe ztF+J6YyrB0%T7^~->yHQ?x@I-a!OYzq(O)*!_NDPoV&7-jIx{E4~SaTQDc8tDT_qe zLVJs~nw? zNEab%?9*+8^DV413Z*iUGDKv*mi3qmel>h|%#@T7%Aw03B}n`}jZ5dFgU=A~Pd)|e z#p@CHf{n*ah;h1Z55R9q;FcB8mf9hbOj^90)=IoHPHy`AFk&6albvZqW|6bO2;s?* z_n}?`Fm)sU1$Rl`wejKylze?&Nq?}4+);|xtBTs=bdZ#On|ZzR-)|G`!Swwqt5+?# z$?^f85T){I4hTms##-A=2&Q1u)|D9(d;=+<4uj=jKYkB+RdTmd{uj0Cv)D(suxV2% zZY{=%EGOUL*uz%KG^&}f zmDOeD)~&$$b^R3)Aw^36j$BX^pNV!)w>B}9hnU6d8dr2EmtpE&O~-YM@#Al2Vs!uVlo!k&QEHryeFXs?TCJ>0)Ss)o0O@183LDm> z%$$pY=EXEGT1S$xl&w_1NX66BPUMKw{b*1Ll?2# z9pD*f)3-bq2Ry`-2QXcz_5M&ZP%u& z^w#uFdl3a|3p9eVWZ1kKnL_^b9(*=#<*uYh(xfQF~ZlM%%dt zKI_9&y5g7@vU{`H8;)V990_+qtxQ2;21$?M__3x9M>in>k(KpX;;(1SEl-`$;d_5O zyp|GR+zj9E=gd{)AV`{ZS9Jx{Iu(5u=fM>kwmu{r|5K+%A(iFk33r1z#S6exyY*SG=D z!bRoTe)hB($VjSfLB}ffPe;1*o-f^YnXbBx5yEPHetxyaSE&Rp9(u&4l9dNQE@fM? zSZosd4INZFxaz;a!Tai#uh6g$W}j3N>BYN=B^RH>W{bO;lv{VS6JWeSD6qwCZj1wA zULRaV7-;Ub02S1NCEC(Nbl=&nT^FC#c2P(J;xo(;@n9CvGmoT#$^vtb&z#>sn9r0F z(~H%r0V#$n7nm~0w$mk|97)f{i5rG6>~8@;mF8!nrBjSHca8@`p9(KlnFBXJlG|I# z_h!WJg01Q=qm|(p0K-S1Pe>>0?+cVxsdMz?^@!2|4?H*dDN+Xs6WbSU3zY zbe1+&IgSoJ$by>=A;VF_-uPlaNUFC`jky?S> zKq{ga%zVzan503@gon@N0m2+GGrpUY0Zxl#dU{QY+9grUlywTgX6U&)CPxVrVzMOZ zb-MSvkvj`__dK>Pf~Jsz+yzj8m zBN7BYn&SJ~d*Gy`V5b`4yXUtvKb7I^6$W&3enFJZhdY`@bSo>E6GDRb+M14=QS$~5 zZ%o8K@#+Cer|Fkye#4h>x3D)JSh)3|+?mwfq=1y=$xX7nZY+t%9O4y#u`6&oLBNJ4 znwa6~1vwLb4!vW@r60PzfjKV#6>9?w94?q3MbwUvT3}~^VQiN!-VXQbl$w-3lzxu_ zeQ{HJQ$cgaup9NudsGT5OCdDK>3ezVC&t!`-7hr#w^$WS9`;4#o>~R(IB>qp@o3?- z2-+-ZR2XGt2Jsz}9~qi0z6V!?zLmG!PXkCnXk@(FQkF9E$MfZ@N5FsG{S&(dQbkP{ z#1z;DsVQ_+EVxF0^+lDayk>pZhwGjbXh<28G~jt*|5AzjXNu+F7e%lv0wJFZ7sl<8 zyqx@6OvI}!@!dT#)Ae;?I^oV~c9PKZKLo(vB?^`9_l3N+8=zUTad6IVU3fopGbK?5y z0*bf!IHrIH-%NTDky??2Y1FgyiJ-VmoTYcPmJbR3LS3O8;x{@;y zxyNK_qC5NI)#+%eE*bPEf!Vk~AD2ldf>LaNWJlh9Q`bp?^+LHqS%` zNIRmzm?G-9+VSkUM{Ak>Xd+%qY%``W;uZEOEys-_HV_+7f|OGfy}`#z{?_*gwamZ(fCXX*}+&9>l|)~{iTyTux7yFz+vG|h!J!#jPK-_EI3=aCL9<~*sL)@7WFbyCf%CjSb@F7Usy%-T?r*~@;rO3r{}mV5=UbWFQOyKwk4$_Jk`pliSP+hLl8Me)*x zHBZXUL&_aj&MTc};KhYB3$EW>-{vC>DfXLqfbk>iq?$b(^PZXfTKrsZ+0nEdwg7wS z+U1{n2>SJdB8~o4+bXg;H_ypFD8`vl@*%}BKTAwa=Nj~g2icg@K;!?lHVp{L~?OSncO&U6fSIl zvE(R({&pY3YhYlUE<&YAd4|&Q1B3(Wl3zva#S7OboiIS}68AO;d9_LVE6)r@z1%u4 z_OlH*ot0t)ga#<;K5&DRrtJ9 zW;(EFbj$uwh9EEDQ+w@CA9SpLOZ}L_9mirh5r{ z#3;Asx=69~3l|BzfWAj;BD~%w*sj?BhHkRL>J#5K`1g{luSxIvp${~NcoDYxpyzXF zi;7sm_Kk(Nlav!==Tp2UGk6?4SSYLt`L&@d_?nVK;WfnP-=kREB_);q_1#LoJ)buz zgLf-19JyUTCIQzv9=kvH!N()q@5FbMM}PvCx?4W2i}^qte~h zIP$Hm%%Noq=DRM-Q4xpFX4&S}n76t=A}@Q}TA~wEggHrJf(}qE zdG!7lmvZ2Qv+&X9dz5;#^Q%2ZjphD(kG{{q3&K=+09ciy;75pG#=YaL3Ff^x{mp8v z>A0=3EBk`@sutSWqJ0@eK{g*6VxtoBo&5BUe|GO@{s_>zD2yce_{}WvAhGM}GJ8Un zq^KNo#n=cu@_@N`1Kq%8I!Ll7{eJDoye$|H3t21YsFquVp#wwFicqzP!`!{zrd5l_ z8#bNckoFq$5bN3KKza>Q@4DfjXyi)pd~xOufBlbDF6c$TqMMy!}m@{gyv z+)D%Nj1ZOtvU^pEq9mtVI3D z36QlDJ2(ltbJmAM*bkQi833WM)!_XpUF(PRD_!|j;(9O<%{=h9*{uZ_L&{^IG{ zV48bKB}kOa@ial4QRv9wP;Q8wg*WMoZ;x-UrMP@J8dFqU1iQAx&95NudAKWpvnvij zWrN$?#EC=0qlv@=cKavyk`ScvHby-15r6fgcp%{>80J6``LT!Dq&;*Tq!TW-xvbTW z$aP-qF#+vqX}#Qas?y;I?4%K){%LrA-7o^=^7YR0_Ubi8`uR2fHZs)Iad+`Eml&Y8 zpS)k8q1=&FqMfYE=KI00&TSB1F_Dj|U)KtB+khk5Lvpfz3ryb)2?BE7_Ggxz;v*1| ztCa$jpPKR`$aOq!^*JdjPN4&U|Ak`8+C-b+k1aH-|C<<<%Wn*YR9!RQ(5x%t6&`HQ zM`os*rxPR_Kn~cvqUSRRYUof!kbCfwOC@N*d^ffN0BY;FI;mGajtG9fU57Ap?zZ{m z+n$_C$ZR|hbgB=8J<+`m~}%ALOWc5J%vc8??_x{&-RBq^=mToKm>twgfigf%ZX zDzys9dEUhqM$5l`7A;ZKb^V9`(96wH&4@mhyzY*1<=Pd?%m~D6TdJZE?9i8w+oAVTH=sIIaD(dKFw2uDCU>`4TeWgW8n z;LH;Ts7G97pYDkXa|E64{&1J?-%tV8vg^gKrTACf1wER4xKs%5VF&l?d*TXzc|>?D zWKs(vI^Ld}tbG29vlnMj?%DMsUl94#L2`Hlllv ztgA;;kF$|dQIW)PI7?*OYg-XoaN{)Dp%PApeNx7ngO2cY!32JSR8rT`v)*zl_K}KO zuT3E3?>WgG++)Tw1k9_-G`s~{;i+`#0)Nn~j>##q>G+rM&JY;=fjCM#B1So>VmrM`ERok?+6Z$oAT`fO>FN8*r3 zb!vLUR0rg(3O?#VpNCjWH8K;05FW^Mqpz>!0_JVCXp_n2R>mHdP~(J|UamYZIZJNm zQFQCT0u!xkk3e^R0Z&QSlp_&bothY9vx$}evF(ga$2)4Eita6Ri`~=l-g`|(O%Psc zELzTj@k2^p^cKD}$nWc9CM+!;6tEJ4@f4u<3~lw5_lXK$fm3j#(isVVt)~mn%K_A6 zR`M3z*S8>>H7!6H@`bGEl2xSqNxGxRKD_(l$k*<+)r%aW>(c7~{fE~j@(CSYKblz~ zq*>s-J&W4eJzM=Qo-qtE+qB8Yo<=i8^eV+1Cja*~4&Zh>^Phm^V4$1=E{t)GCX~Pt z7^y-Ntoye>P9DZX%z;Y{`S*aBH-WoDtCDv(7A~o&7=Z=dd1Wom#X%>KCekk#h^;_% zyMwhm1`dH8^5gDq1BIK><$F2#C`iqDjVt5LAyCSmEJOxjsdQ>y59_=^^nZQM@s7=6 zvp0sHnGknNkUq*l-{KjYQd#mJCJ`H|D_9|>Wy z^=|=zeG5D#B*^#*Oh?^uzRaR7d82`E0HzPPh=)=)CSK@uG$IMd=+J)}7d+~de;LZe z^kh)~d84S^g^T4nDA z$i8`ZM39eQw5$S;PT9t$;h++}!7D)NZQ%-GmuRnV_h_@}LI4YFOlsM?pGZGPcLkam zgo)nrYY|f7Ou(+tlgTz0dJ7{Vj9frc@%jsNsuyWK$}Bcebdu~YKE~?o#o$FGCI~0g z_UEA~098?{5W322qJh3AmTpX*4-B#}f)ZDA!7I9L80Ar9N@uA~PV^gbS2J^EK>w>m z=JJG7_5p#52I(mnaLMLdq*6Hr@R^>6+1BnL-2JP#Gv#iRKz*M`Ieg`93`6LBc87ut zFv%$?2$f$JXW4Sp0F9r{H9f_v@4C4sFF`~B##hrSYT-K~&IL`U`lUA( z8z={ENARXbEiA% zu3*Ee=asi>Y>6sIdwAJ14byUbKGf|$Nc4(RWhT*aeyVvuq} zbe}GVdtNxa**B8Q@7BasSaz{_ufH{EW^$*nVFja$^O4pX~RLn!hIFya5dZ zcHhni`z85b3r#h#*r*8oML_=jU+SCKV(|x`%L?w&oiI0bjNr5-fM=vWy-g9RvQKIC z*{9?aJIhV*h$PlwXC)u-bffdt-CTj?Mm)HZG_62kD{Z-G#P|EYrDWP29 zUD*EbuaDf@=1E{=70t=7=%`*62>%Z`pced6QNN8|+W zx@MccWhFSV;O}N>y#(INP+02)?Gp3o8~=B!Zz=e-ndH8N&BmK7!eSv9BRdwvs+Fsz1Ot@CH%=xuT8Fxrh+ZS(;ICeBD!ztQo-q`^nc` zaC_C{Ar%+yLijLaR`uAS0u;P_eOHA^AHrVjXR;r5@F1?$eY0{UC~LP|a3f?Glz6iX#a~}aR1u2DX z#Fs}PS={*iL!4d0`Eb~_VhDQQngF0+;_LjL*1#h-evY`O7~QYpKUD?;@x05=5xkKX zIWPBxr8Y59@Xg6GIKYC`L#4WTE|Z}AB?=?9fuOi1gs?{li&y}IG{?MsupL0PiFr=} zCfVrebd5@fl%rTlU@)8DI6Hj&Nf1da$CMyYr@o@W(Zr>QWLwz*)fwzSisy8%E8vB6 zM}m9=>m5)-1RmgjU!*bnEzsV<3(%{7$rv*YPqKm25MTliDje#NMvw<6s91cx&Rv4p zd5b`Z?@5Ck?xT6?e#x}kDb65;n~|Zd2h>o1>R9qfFq2EK4t1RwnEjqK7%1qk_Vm#> zz?4`4k>a=yL6_gK-r#iwbu4*|7x6;v_yu9-6(#pDviCi95!=p3{^&Kc#|$q!>u%pM z0ex5wxX?#2(6MX;|Ns49BqP!pcp;`3-$VTrhH)hB$h_8s*s2#4)F(Inq~((6#xxIg zo+dkPUF7K*aH<{Xtdn}sVv2TOK${wTtDv$alRbI1H4XRdxZtX?`Yf2R-NDkj5bBX7 z?i@%rAM8PQu}*sudPD7n*FEx^{~m|K3KGV9bIYye&(IX|igq2Y?wUl@mZMEHsc1oQ zE|$Z@^j>;ty2kY5(=wK|XuZU{6903UFzpTKWnAC;9#=*M;?sgF1E-*gnDKYt(uJR) zi^ks{d>{X_mWq5qbU&);%Ls|JfMs6&-q?*-f>!?x;n3hfKM{4ELjgbe_FK4lYkPmZ z@11JB;r%y9dx10Gh248m;}^Tm$b6``Q^+>n+aB@No?ce#=qbLL>(;-c%@d3mv{U|z z!wez}4fLpZ5}I{*jT%3hsN=dZ@;=OIlIgjwh{&(Njql^<>mMM? zYv~-VEj*Ts|IH=|;3{sc&%p2bnnZ8x<&c{hG2VWY_r}in{(AaNA1z$NC2OjsUw zojNyckdD?-1d$#1R|}yOxX*JaGXzcZ*a@W%)QG=UApQk|WMXL|TYu+4)fEI0T}>Yp z`YT`ZP>U%(a~v#0eisftJgl|vs@$K0l^{#-6i7%d<+#M8z10HN4(n|C;aXKNrM)6Q z&j0p~2}Jwmz~F||p|N!hv8R7N^{(s|yDMLz(Rk}uxWw{@rL|h8T}{f!joqAlL!SZi z*t{Kr52K9K(I?pqCN*EFvEzkgqJi*$8BDR%IM2T?yPGn%o3;11q%4JYd-%FVUMn1)caaTZ5HcX>E&eVGF{Q_eHFY-xMi7ucy-&{k7z!nI-Q1m~QUx(Rj)3+W5zL9@j^OHP_qTGj_cZo}3fs>C+bFe2v*C=Zc39s>zJbK4VLt%12h%tpDOf zCL$Dxuja#`PxZ_^_89$IJXD;#&*9Vi+BjkdnECgBvkrvKZb`Yg{g^+t*)A07R;4ZR z6Zmur@qxc9+$o&nWmh>pH|*xN%QX9BfMDQ?K8WlXi06A?sTF>{$(^$c$^qRL8U;M( z%H~w$m}n1tp)oU=k5FR$Gi7YWmND87sz7se2Ik<54NiqC_}=@q^hDDsgZ;({DqEav zBLfn~l*HEL@@wj(048mi0Z+CvCu`5WdWPODFkn#gOITdB7sTB)%|dxKdi-!1T3PVA zUzOa$mFPFadK?nk0uW-aH7TqAt$(9oy*JRtZKp+ecSoo@k~Wd#G&jp!MhQX}eM98e zRKObnNzlFU_sIllaKVpPGu4CP7Ho?5A7nt}@GLECH%!xW+nM2Fr+dqaZXnN|q4o7M zMKaQQI3f(qrg1G@K;e0nHe40AS+J4_ZwfHLFrF)Wt;HYJpte zY*$9-weYM39y3P&d6RY#eL%oo^)&{`Q_gkx;`gqCM}X7wpR>a5XjE>fkJ^)+u_@Kf zo2K8sT3r&UOq_rqaLDyHsFeVDUj(_Vo2M@SplPlCT6I2-L(45QNu zcmHltyYAeoFWb`o*l!l6${yc}aG>;urYI;bm+bRQFP7i0qF1A(6War%g=_gK`!5zb7DS4{%XWCL zao_Nmi+KTm7q|YBNDv=XOcIsl2f3JnTJxnwP~{ng2{PqA-rT@>pjoQ-~hu`Mr; zYr=^OKzYGvVD(5mp7+`B_cdA-4E6?eCADez?DO5^ie&iev! zhM+kY*cL*gn;7o|6AE`fs;otuH9PftSPTT7F|s~eh`D5^asRS8sgQc2LLB3$YkeoGy^)guRXu#QmZt^4nv|>lbbAj z06hF~Cvpc=Xh}HwVInV6dOup8351RQ1caIx&Pm@yu-_QtIxh+I2hJng;_x$EC zV6VY3bvb7%SywV%+DSUQ^ftxqOk}r4_b_A^W5HpJRcY7d6X54QZjssrb#vqx>0cKO ziq=Rl5_>l$4L_5zHPlullk^POI2n1grzBF$noliMt0$=+S&;vpsqwVk>?&%g=#C=h8bAYLNX0&?>|ptnY^F*(8CtWV-5*&rmvfTOKj$ekum z%7YNna0Sk0=QnbuER+SPSa2JPxZ4|ulZY@Hk^>?_BV?^uBzJD2M5_l>fVoQBN>&efl|SbJI}>8py};+s}}#ld}+u@`pDJf z15rWvhI%|L6jhi%H&KNvi{qq z3vl?$=Dn6JL8ACtvZq}yZ*gfrnYnkSc=*3Wmbq+!XQ@5I=p4>3A4bNTSWKU?In9rq#KP6*%m|6KMXp6ZXWr=MTy$UjF-EMmDEBXF__?u;sWe;+|9h1o zk%R*(>j}XG{xV`n!D5+H=cdlRos}^@r7<@qi5WVGm#^+3BtkCYkMB{#UQqz50&)fI(u`1or06?wfV56R zNmgc^q5WsILb`C?61?epA@^HGcm|2xqSDScZ#q60rB?4xXSmgHdnLlV8ipWa=*242 z8_fn=V*HNOSD-e}o*CX#rKVbGp3|*}`qKf#^2Yq%d4L7~@YzcKa#+TW;qm;uRp=x%Q<(o=MNpm6K)gIH|cKHrH5oL(fV!6|~%F&?Kkwa-{Wi4IEa zg~NNhxt_yvEu`&Qo*H)3QR8*Asxvk`ezGj60_2uuo7P0bE(5l%V1^rS-==qpdr1{w zswK6vgu61l*Vf)tY<29mY`Iw`SmRZ1fO=gI;y(PF-jlm8Rsx#Ldy{|@KstZxjijf? z(PlHq8;nG(k};#!^a9({neSF2ty6vjb zEIi0HX~cgbvL_I1^a7#9QBZzkv4ZgH&p)t!_f}9~qZR;asA`Eq$G|0GFlwCtXrcrp z=(INPs;2c&)3;lR7t2xe8Dv4E5;ov{arW(rjz_^!@p+8K5FVo1MPTwgudZn3o@FaswFW1l;4yvYd*Hf1W92T3s5?oVUn<=^g6ff z$0UzhpptD$$!oPiEbb^fRs^vW6Dn>K}f7pBUW<>6e9lHc82)(w?L`TU>V z&fN~&vDW@d#D(d(m-6!GYcx{>dYwc${rW3nHSRhGm1bv7B6A>tf|P zsoZ3Zoj>eKcB+{7RtV@<*yK2S+^c%68_ibr{m93O((2{?#^n_oH0CN)aIl8P^C=zJ z>S|DVpzz9*e=Dasr&xqvdVZnfeCdOj9ezj7e*9Bcur}lT!nKWM?AsqWQAgfzRljej4Mx>K&KpR zYqLMNUMF(Xkv~3#wUdr0FrNh)^&4xVh@L?0ngho{UaFcrhZ@{Xix^Y zFf#MzG@cjlJjLCBLl&MdX+(~U~uwOO&pA+Gr t5t$G<@G40JLFS1=gbbKEPvD>ZGcDm+`m literal 0 HcmV?d00001 diff --git a/h-and-m-fash-rec-kaggle-competition/images/kale_deployment_panel.PNG b/h-and-m-fash-rec-kaggle-competition/images/kale_deployment_panel.PNG new file mode 100644 index 0000000000000000000000000000000000000000..272b4f4c99bfb727f6e4171d1d3d7862e07a02d4 GIT binary patch literal 57775 zcmd43XIN8N_cx5=SjK`fiUDb(j0I2-5CLhU2&goXUL!;VX%Pa31W2riw5Wi9Gyw(a zBE3d|5TqnhBQ;8Z00CkM0TM{^o}e>-=lGL1_S$RjwbpN~_1k%T z#ms2;j)OadgoJiqx_IHLkkA%UA)#Lkw{HdR;BaoQfFGM+SB=gK6%!<9fM0&~IA?lJ zNT?LEbKP|d@cVDKF51I{gnkbc{M$5kLnc^A=<9_`7tUD)yUcOt%L45YW53OIgr@4f zgy`-S&Hl7a?uyujmv3(FZ4J1yKTG>pgYDZTDh{0u-nl;z^5pc9`%*DOF(FU3Y(iL|<9DcKm@!caQ9DJG;#nr*JRtZ4&Ko`{TEksFtX=uO*$?scU^f3se{H`Ltv$ zcsdM)ji70Ulq80XZrmyH@R)-82nikdq5x*Mk8vu%YibYnp5J~*@L$@?Q=*%$iO9#@ zD$YN4@}Jv9N+t$Eb`RSBOf8tbv~%N0!GE-!mLft2GF5(amRbL@ts}se_3Gi@Z^~pn z%-b#$btU;hW>x*<$rS7woWHOPwU)_GFaP_QL*

hP$#uU%5~9K*6z-Bwsy#?)J>h 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
t_V%?SyOV0TZW{s$kHmc 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 literal 0 HcmV?d00001 diff --git a/h-and-m-fash-rec-kaggle-competition/images/kfp_client.PNG b/h-and-m-fash-rec-kaggle-competition/images/kfp_client.PNG new file mode 100644 index 0000000000000000000000000000000000000000..d41bcee8f31ccfd76c726cb86a9dbefc80927513 GIT binary patch literal 6651 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 literal 0 HcmV?d00001 diff --git a/h-and-m-fash-rec-kaggle-competition/images/kfp_pipeline_func.PNG b/h-and-m-fash-rec-kaggle-competition/images/kfp_pipeline_func.PNG new file mode 100644 index 0000000000000000000000000000000000000000..1a9eeec1b999f75d30e6b680e021055521ea13ac GIT binary patch literal 25275 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 z

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 literal 0 HcmV?d00001 diff --git a/h-and-m-fash-rec-kaggle-competition/requirements.txt b/h-and-m-fash-rec-kaggle-competition/requirements.txt new file mode 100644 index 000000000..eb7ba5b32 --- /dev/null +++ b/h-and-m-fash-rec-kaggle-competition/requirements.txt @@ -0,0 +1,5 @@ +numpy +pandas +implicit +sklearn +kaggle diff --git a/h-and-m-fash-rec-kaggle-competition/resource.yaml b/h-and-m-fash-rec-kaggle-competition/resource.yaml new file mode 100644 index 000000000..29128335c --- /dev/null +++ b/h-and-m-fash-rec-kaggle-competition/resource.yaml @@ -0,0 +1,16 @@ +apiVersion: "kubeflow.org/v1alpha1" +kind: PodDefault +metadata: + name: kaggle-access +spec: + selector: + matchLabels: + kaggle-secret: "true" + desc: "kaggle-access" + volumeMounts: + - name: secret-volume + mountPath: /secret/kaggle + volumes: + - name: secret-volume + secret: + secretName: kaggle-secret \ No newline at end of file diff --git a/house-prices-kaggle-competition/house-prices-kfp.ipynb b/house-prices-kaggle-competition/house-prices-kfp.ipynb index 35933450f..584893274 100644 --- a/house-prices-kaggle-competition/house-prices-kfp.ipynb +++ b/house-prices-kaggle-competition/house-prices-kfp.ipynb @@ -728,7 +728,7 @@ " vanilla_pipeline,\n", " arguments={\n", " # Github url to fetch the data. This would change when you clone the repo. Please update the url as per that.\n", - " 'url': 'https://github.com/NeoKish/examples/raw/master/house-prices-kaggle-competition/data.zip'\n", + " 'url': 'https://github.com/kubeflow/examples/raw/master/house-prices-kaggle-competition/data.zip'\n", " })" ] },