From a79f11754a7dea4d8e4b1b81a9d4e507909ffec3 Mon Sep 17 00:00:00 2001
From: Kishan Savant <66986430+NeoKish@users.noreply.github.com>
Date: Wed, 12 Oct 2022 19:22:01 +0530
Subject: [PATCH] Example of converting Store Sales Forecast Kaggle notebook to
a Kubeflow pipeline (#994)
* Uploaded files
* Modified png extensions
* Modified the directory name
* Updated README.md
---
.../README.md | 183 +
.../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 -> 125352 bytes
.../images/kale_pipeline_graph.png | Bin 0 -> 8894 bytes
.../images/kfp_client.png | Bin 0 -> 6651 bytes
.../images/kfp_pipeline_func.png | Bin 0 -> 28904 bytes
.../images/kfp_pipeline_graph.png | Bin 0 -> 6197 bytes
.../requirements.txt | 4 +
.../resource.yaml | 16 +
.../store-sales-kale.ipynb | 508 +
.../store-sales-kfp.ipynb | 635 +
.../store-sales-orig.ipynb | 2259 ++
.../submission.csv | 28513 ++++++++++++++++
14 files changed, 32118 insertions(+)
create mode 100644 store-sales-forecasting-kaggle-competition/README.md
create mode 100644 store-sales-forecasting-kaggle-competition/images/kaggle_api_token.png
create mode 100644 store-sales-forecasting-kaggle-competition/images/kale_cell_metadata.png
create mode 100644 store-sales-forecasting-kaggle-competition/images/kale_deployment_panel.png
create mode 100644 store-sales-forecasting-kaggle-competition/images/kale_pipeline_graph.png
create mode 100644 store-sales-forecasting-kaggle-competition/images/kfp_client.png
create mode 100644 store-sales-forecasting-kaggle-competition/images/kfp_pipeline_func.png
create mode 100644 store-sales-forecasting-kaggle-competition/images/kfp_pipeline_graph.png
create mode 100644 store-sales-forecasting-kaggle-competition/requirements.txt
create mode 100644 store-sales-forecasting-kaggle-competition/resource.yaml
create mode 100644 store-sales-forecasting-kaggle-competition/store-sales-kale.ipynb
create mode 100644 store-sales-forecasting-kaggle-competition/store-sales-kfp.ipynb
create mode 100644 store-sales-forecasting-kaggle-competition/store-sales-orig.ipynb
create mode 100644 store-sales-forecasting-kaggle-competition/submission.csv
diff --git a/store-sales-forecasting-kaggle-competition/README.md b/store-sales-forecasting-kaggle-competition/README.md
new file mode 100644
index 000000000..c93efb0d8
--- /dev/null
+++ b/store-sales-forecasting-kaggle-competition/README.md
@@ -0,0 +1,183 @@
+# Kaggle Getting Started Prediction Competition: Store Sales - Time Series Forecasting
+
+In this [competition](https://www.kaggle.com/competitions/store-sales-time-series-forecasting), we will use time-series forecasting to forecast store sales on data from Corporación Favorita, a large Ecuadorian-based grocery retailer. The notebook is a buildup of hands-on-exercises presented in Kaggle Learn course of [Time Series Course](https://www.kaggle.com/learn/time-series) where you will learn to leverage periodic trends for forecasting as well as combine different models such as linear regression and XGBoost to perfect your forecasting. For the purpose of this tutorial we are looking at periodic trend for forecasting.
+
+## 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 : 8GB
+ - Workspace Volume : 10GB
+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 resource.yaml
+
+ ```
+ 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 the data
+- Create features
+- Train and evaluate the model
+- Forecast Sales
+
+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 this cell to download the data for this competition from Kaggle using the Kaggle Public API. You will need the username and API token from the kaggle.json file you generated previously for the the KFP SDK pipeline. For the Kale pipeline, you don't have to create a Kubernetes secret, just manually substitute your credentials in the following lines of code:
+
+```
+os.environ['KAGGLE_USERNAME'] = "KAGGLE_USERNAME"
+os.environ['KAGGLE_KEY'] = "KAGGLE_KEY"
+
+```
+
+Once the 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. 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/store-sales-forecasting-kaggle-competition/images/kaggle_api_token.png b/store-sales-forecasting-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/store-sales-forecasting-kaggle-competition/images/kale_cell_metadata.png b/store-sales-forecasting-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@xQD