From 0304c7f5cc87a935267867f2041ede67d0682d34 Mon Sep 17 00:00:00 2001 From: Eyal Danieli Date: Thu, 26 Sep 2024 10:05:03 +0300 Subject: [PATCH] merge from development #829, #830 (#831) * set `navigation_with_keys` to False (#829) * remove xgb and churn functions (#830) --- catalog.yaml | 180 --- churn_server/README.md | 15 - churn_server/churn_server.ipynb | 503 -------- churn_server/churn_server.py | 45 - churn_server/function.yaml | 51 - churn_server/item.yaml | 32 - churn_server/requirements.txt | 2 - churn_server/test_churn_server.py | 67 - cli/marketplace/conf.template | 1 + coxph_test/coxph_test.ipynb | 969 --------------- coxph_test/coxph_test.py | 75 -- coxph_test/function.yaml | 63 - coxph_test/item.yaml | 26 - coxph_trainer/coxph_trainer.ipynb | 1799 --------------------------- coxph_trainer/coxph_trainer.py | 201 --- coxph_trainer/function.yaml | 108 -- coxph_trainer/item.yaml | 26 - coxph_trainer/requirements.txt | 6 - coxph_trainer/test_coxph_trainer.py | 136 -- xgb_test/function.yaml | 63 - xgb_test/item.yaml | 25 - xgb_test/requirements.txt | 8 - xgb_test/test_xgb_test.py | 148 --- xgb_test/xgb_test.ipynb | 708 ----------- xgb_test/xgb_test.py | 61 - xgb_trainer/function.yaml | 102 -- xgb_trainer/item.yaml | 24 - xgb_trainer/requirements.txt | 8 - xgb_trainer/test_xgb_trainer.py | 50 - xgb_trainer/xgb_trainer.ipynb | 1013 --------------- xgb_trainer/xgb_trainer.py | 160 --- 31 files changed, 1 insertion(+), 6674 deletions(-) delete mode 100644 churn_server/README.md delete mode 100644 churn_server/churn_server.ipynb delete mode 100644 churn_server/churn_server.py delete mode 100644 churn_server/function.yaml delete mode 100644 churn_server/item.yaml delete mode 100644 churn_server/requirements.txt delete mode 100644 churn_server/test_churn_server.py delete mode 100644 coxph_test/coxph_test.ipynb delete mode 100644 coxph_test/coxph_test.py delete mode 100644 coxph_test/function.yaml delete mode 100644 coxph_test/item.yaml delete mode 100644 coxph_trainer/coxph_trainer.ipynb delete mode 100644 coxph_trainer/coxph_trainer.py delete mode 100644 coxph_trainer/function.yaml delete mode 100644 coxph_trainer/item.yaml delete mode 100644 coxph_trainer/requirements.txt delete mode 100644 coxph_trainer/test_coxph_trainer.py delete mode 100644 xgb_test/function.yaml delete mode 100644 xgb_test/item.yaml delete mode 100644 xgb_test/requirements.txt delete mode 100644 xgb_test/test_xgb_test.py delete mode 100644 xgb_test/xgb_test.ipynb delete mode 100644 xgb_test/xgb_test.py delete mode 100644 xgb_trainer/function.yaml delete mode 100644 xgb_trainer/item.yaml delete mode 100644 xgb_trainer/requirements.txt delete mode 100644 xgb_trainer/test_xgb_trainer.py delete mode 100644 xgb_trainer/xgb_trainer.ipynb delete mode 100644 xgb_trainer/xgb_trainer.py diff --git a/catalog.yaml b/catalog.yaml index c3364fefa..f603b1b9b 100644 --- a/catalog.yaml +++ b/catalog.yaml @@ -15,62 +15,6 @@ arc-to-parquet: kind: job versions: latest: arc_to_parquet/function.yaml -bert-embeddings: - categories: - - NLP - - BERT - - embeddings - description: Get BERT based embeddings for given text - docfile: bert_embeddings/bert_embeddings.ipynb - kind: remote - versions: - latest: bert_embeddings/function.yaml -churn-server: - categories: - - serving - - ml - description: churn classification and predictor - docfile: churn_server/churn_server.ipynb - kind: serving - versions: - latest: churn_server/function.yaml -concept-drift: - categories: - - ml - - serve - description: Deploy a streaming Concept Drift detector on a labeled stream - docfile: concept_drift/concept_drift.ipynb - kind: job - versions: - latest: concept_drift/function.yaml -concept-drift-streaming: - categories: - - ml - - serve - description: Deploy a streaming Concept Drift detector on a labeled stream. the - nuclio part of the concept_drift function - docfile: concept_drift_streaming/concept_drift_streaming.ipynb - kind: remote - versions: - latest: concept_drift_streaming/function.yaml -coxph-test: - categories: - - ml - - test - description: Test cox proportional hazards model - docfile: coxph_test/coxph_test.ipynb - kind: job - versions: - latest: coxph_test/function.yaml -coxph-trainer: - categories: - - training - - ml - description: cox proportional hazards, kaplan meier plots - docfile: coxph_trainer/coxph_trainer.ipynb - kind: job - versions: - latest: coxph_trainer/function.yaml describe: categories: - analysis @@ -94,14 +38,6 @@ describe-spark: kind: job versions: latest: describe_spark/function.yaml -feature-perms: - categories: - - analysis - description: estimate feature importances using permutations - docfile: feature_perms/feature_perms.ipynb - kind: job - versions: - latest: feature_perms/function.yaml feature-selection: categories: - data-prep @@ -144,13 +80,6 @@ model-monitoring-batch: kind: job versions: latest: model_monitoring_batch/function.yaml -model-monitoring-stream: - categories: [] - description: '' - docfile: model_monitoring_stream/model_monitoring_stream.ipynb - kind: remote - versions: - latest: model_monitoring_stream/function.yaml model-server: categories: - serving @@ -178,30 +107,6 @@ open-archive: kind: job versions: latest: open_archive/function.yaml -pandas-profiling-report: - categories: - - analysis - description: Create Pandas Profiling Report from Dataset - docfile: pandas_profiling_report/pandas_profiling_report.ipynb - kind: job - versions: - latest: pandas_profiling_report/function.yaml -project-runner: - categories: - - utils - description: Nuclio based - Cron scheduler for running your MLRun projects - docfile: project_runner/project_runner.ipynb - kind: remote - versions: - latest: project_runner/function.yaml -rnn-serving: - categories: - - model-serving - description: deploy an rnn based stock analysis model server. - docfile: rnn_serving/rnn_serving.ipynb - kind: serving - versions: - latest: rnn_serving/function.yaml send-email: categories: - notifications @@ -240,14 +145,6 @@ sklearn-classifier-dask: kind: job versions: latest: sklearn_classifier_dask/function.yaml -slack-notify: - categories: - - ops - description: Send Slack notification - docfile: slack_notify/slack_notify.ipynb - kind: job - versions: - latest: slack_notify/function.yaml spark-submit: categories: [] description: '' @@ -255,23 +152,6 @@ spark-submit: kind: job versions: latest: spark_submit/function.yaml -sql-to-file: - categories: - - data-prep - description: SQL To File - Ingest data using SQL query - docfile: sql_to_file/sql_to_file.ipynb - kind: job - versions: - latest: sql_to_file/function.yaml -stream-to-parquet: - categories: - - ml - - serve - description: Saves a stream to Parquet and can lunch drift detection task on it - docfile: stream_to_parquet/stream_to_parquet.ipynb - kind: remote - versions: - latest: stream_to_parquet/function.yaml test-classifier: categories: - ml @@ -281,15 +161,6 @@ test-classifier: kind: job versions: latest: test_classifier/function.yaml -tf1-serving: - categories: - - serving - - dl - description: tf1 image classification server - docfile: tf1_serving/tf1_serving.ipynb - kind: remote - versions: - latest: tf1_serving/function.yaml tf2-serving: categories: - serving @@ -299,15 +170,6 @@ tf2-serving: kind: remote versions: latest: tf2_serving/function.yaml -tf2-serving-v2: - categories: - - serving - - dl - description: tf2 image classification server v2 - docfile: tf2_serving_v2/tf2_serving_v2.ipynb - kind: serving - versions: - latest: tf2_serving_v2/function.yaml v2-model-server: categories: - serving @@ -326,45 +188,3 @@ v2-model-tester: kind: job versions: latest: v2_model_tester/function.yaml -virtual-drift: - categories: - - ml - - serve - - concept-drift - description: Compute drift magnitude between Time-Samples T and U - docfile: virtual_drift/virtual_drift.ipynb - kind: job - versions: - latest: virtual_drift/function.yaml -xgb-custom: - categories: - - model-testing - description: simulate data with outliers. - docfile: xgb_custom/xgb_custom.ipynb - kind: job - versions: - latest: xgb_custom/function.yaml -xgb-serving: - categories: - - model-serving - description: deploy an XGBoost model server. - docfile: xgb_serving/xgb_serving.ipynb - kind: remote - versions: - latest: xgb_serving/function.yaml -xgb-test: - categories: - - model-test - description: Test one or more classifier models against held-out dataset. - docfile: xgb_test/xgb_test.ipynb - kind: job - versions: - latest: xgb_test/function.yaml -xgb-trainer: - categories: - - model-prep - description: train multiple model types using xgboost. - docfile: xgb_trainer/xgb_trainer.ipynb - kind: job - versions: - latest: xgb_trainer/function.yaml diff --git a/churn_server/README.md b/churn_server/README.md deleted file mode 100644 index b6a517a5a..000000000 --- a/churn_server/README.md +++ /dev/null @@ -1,15 +0,0 @@ -# churn server - -the `churn-server` function was created as part of the **[churn demo](https://github.com/yjb-ds/demo-churn)**. A model server was needed that could combine the static model which answers the binary classification question "is this client churned or not-churned?" and the more dynamic model, which tries to add a time dimension to the prediction by providing an esdtimate of when and with what certainty churn events are likely to occur. - -the function `coxph_trainer` will output multiple models within a nested directory structire starting at `models_dest`: -* the coxph model is stored at `models_dest/cox` -* the [kaplan-meier](https://en.wikipedia.org/wiki/Kaplan%E2%80%93Meier_estimator) model at `models_dest/cox/km` - -each one of these pickled models stores all of the meta-data, vector and table estimates, including projections and scenarios - -with only slight modification, a more generic version of this server would enable its application in the domains of **[predictive maintenance](https://docs.microsoft.com/en-us/archive/msdn-magazine/2019/may/machine-learning-using-survival-analysis-for-predictive-maintenance)**, **[health](https://www.ncbi.nlm.nih.gov/pmc/articles/PMC3227332/)**, **finance** and **insurance** to name a few. - -**note** - -a small file `encode-data.csv` can be find in the root of this function folder, it is used to test the server. \ No newline at end of file diff --git a/churn_server/churn_server.ipynb b/churn_server/churn_server.ipynb deleted file mode 100644 index b8a962772..000000000 --- a/churn_server/churn_server.ipynb +++ /dev/null @@ -1,503 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "pycharm": { - "name": "#%% md\n" - } - }, - "source": [ - "# **Churn Server**\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "in the following section we create a new model serving function which wraps our class , and specify model and other resources.\n", - "Deploying the serving function will provide us an http endpoint that can handle requests in real time.\n", - "This function is part of the [customer-churn-prediction demo](https://github.com/mlrun/demos/tree/master/customer-churn-prediction).
\n", - "To see how the model is trained or how the data-set is generated, check out `coxph_trainer` and `xgb_trainer` functions from the function marketplace repository." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### **Steps**\n", - "1. [Setup function parameters](#Setup-function-parameters)\n", - "2. [Importing the function](#Importing-the-function)\n", - "3. [Testing the function locally](#Testing-the-function-locally)\n", - "4. [Testing the function remotely](#Testing-the-function-remotely)" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "import warnings\n", - "warnings.filterwarnings(\"ignore\")" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "# Following packages are required, make sure to install\n", - "# !pip install xgboost==1.3.1" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### **Setup function parameters**" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "# Setting up models path\n", - "xgb_model_path = 'https://s3.wasabisys.com/iguazio/models/function-marketplace-models/churn_server/xgb_model.pkl'" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### **Importing the function**" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "> 2021-10-14 06:10:16,104 [info] loaded project function-marketplace from MLRun DB\n" - ] - }, - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "import mlrun\n", - "mlrun.set_environment(project='function-marketplace')\n", - "\n", - "# Importing the function from the hub\n", - "fn = mlrun.import_function(\"hub://churn_server:development\")\n", - "fn.apply(mlrun.auto_mount())\n", - "\n", - "# Manually specifying needed packages \n", - "fn.spec.build.commands = ['pip install lifelines==0.22.8', 'pip install xgboost==1.3.1']\n", - "\n", - "# Adding the model \n", - "fn.add_model(key='xgb_model', model_path=xgb_model_path ,class_name='ChurnModel')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### **Testing the function locally**" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "> Note that this function is a serving function, hence not needs to run, but deployed.
\n", - "\n", - "in order to test locally without deploying to server, mlrun provides mocking api that simulate the action." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "> 2021-10-14 06:10:19,145 [info] model xgb_model was loaded\n", - "> 2021-10-14 06:10:19,145 [info] Initializing endpoint records\n", - "> 2021-10-14 06:10:19,164 [info] Loaded ['xgb_model']\n" - ] - } - ], - "source": [ - "# When mocking, class has to be present\n", - "from churn_server import *\n", - "\n", - "# Mocking function\n", - "server = fn.to_mock_server()" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
genderseniorpartnerdepstenurePhoneServiceMultipleLinesOnlineSecurityOnlineBackupDeviceProtection...PaperlessBillingMonthlyChargestenure_mapISP_1ISP_2Contract_1Contract_2Payment_1Payment_2Payment_3
000102710100...1101.902.01010100
10100111000...185.700.01000010
21000110000...169.550.01000010
300005311011...0105.554.01001010
400004311011...1104.603.01001010
\n", - "

5 rows × 23 columns

\n", - "
" - ], - "text/plain": [ - " gender senior partner deps tenure PhoneService MultipleLines \\\n", - "0 0 0 1 0 27 1 0 \n", - "1 0 1 0 0 1 1 1 \n", - "2 1 0 0 0 1 1 0 \n", - "3 0 0 0 0 53 1 1 \n", - "4 0 0 0 0 43 1 1 \n", - "\n", - " OnlineSecurity OnlineBackup DeviceProtection ... PaperlessBilling \\\n", - "0 1 0 0 ... 1 \n", - "1 0 0 0 ... 1 \n", - "2 0 0 0 ... 1 \n", - "3 0 1 1 ... 0 \n", - "4 0 1 1 ... 1 \n", - "\n", - " MonthlyCharges tenure_map ISP_1 ISP_2 Contract_1 Contract_2 \\\n", - "0 101.90 2.0 1 0 1 0 \n", - "1 85.70 0.0 1 0 0 0 \n", - "2 69.55 0.0 1 0 0 0 \n", - "3 105.55 4.0 1 0 0 1 \n", - "4 104.60 3.0 1 0 0 1 \n", - "\n", - " Payment_1 Payment_2 Payment_3 \n", - "0 1 0 0 \n", - "1 0 1 0 \n", - "2 0 1 0 \n", - "3 0 1 0 \n", - "4 0 1 0 \n", - "\n", - "[5 rows x 23 columns]" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "import pandas as pd\n", - "\n", - "#declaring test_set path\n", - "test_set_path = \"https://s3.wasabisys.com/iguazio/data/function-marketplace-data/churn_server/test_set.csv\"\n", - "\n", - "# Getting the data\n", - "x_test = pd.read_csv(test_set_path)\n", - "y_test = x_test['labels']\n", - "x_test.drop(['labels'],axis=1,inplace=True)\n", - "x_test.head()" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [], - "source": [ - "# KFServing protocol event\n", - "event_data = {\"inputs\": x_test.values.tolist()}" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [], - "source": [ - "response = server.test(path='/v2/models/xgb_model/predict',body=event_data)" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "When mocking to server, returned dict has the following fields : id, model_name, outputs\n" - ] - } - ], - "source": [ - "print(f'When mocking to server, returned dict has the following fields : {\", \".join([x for x in response.keys()])}')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### **Testing the function remotely**" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "> 2021-10-14 06:10:20,163 [info] Starting remote function deploy\n", - "2021-10-14 06:10:20 (info) Deploying function\n", - "2021-10-14 06:10:20 (info) Building\n", - "2021-10-14 06:10:20 (info) Staging files and preparing base images\n", - "2021-10-14 06:10:20 (info) Building processor image\n", - "2021-10-14 06:10:21 (info) Build complete\n", - "2021-10-14 06:10:29 (info) Function deploy complete\n", - "> 2021-10-14 06:10:30,408 [info] successfully deployed function: {'internal_invocation_urls': ['nuclio-function-marketplace-churn-server.default-tenant.svc.cluster.local:8080'], 'external_invocation_urls': ['default-tenant.app.dev39.lab.iguazeng.com:31984']}\n" - ] - } - ], - "source": [ - "address = fn.deploy()" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "model's accuracy : 0.7913907284768212\n" - ] - } - ], - "source": [ - "import json\n", - "import requests\n", - "\n", - "# using requests to predict\n", - "response = requests.put(address + \"/v2/models/xgb_model/predict\", json=json.dumps(event_data))\n", - "\n", - "# returned data is a string \n", - "y_predict = json.loads(response.text)['outputs']\n", - "accuracy = sum(1 for x,y in zip(y_predict,y_test) if x == y) / len(y_test)\n", - "print(f\"model's accuracy : {accuracy}\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "[Back to the top](#Churn-Server)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.6" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/churn_server/churn_server.py b/churn_server/churn_server.py deleted file mode 100644 index def2850da..000000000 --- a/churn_server/churn_server.py +++ /dev/null @@ -1,45 +0,0 @@ -# Copyright 2019 Iguazio -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# Generated by nuclio.export.NuclioExporter - -import numpy as np -from cloudpickle import load - - -import mlrun - - -class ChurnModel(mlrun.serving.V2ModelServer): - def load(self): - """ - load multiple models in nested folders, churn model only - """ - clf_model_file, extra_data = self.get_model(".pkl") - self.model = load(open(str(clf_model_file), "rb")) - if "cox" in extra_data.keys(): - cox_model_file = extra_data["cox"] - self.cox_model = load(open(str(cox_model_file), "rb")) - if "cox/km" in extra_data.keys(): - km_model_file = extra_data["cox/km"] - self.km_model = load(open(str(km_model_file), "rb")) - - def predict(self, body): - try: - feats = np.asarray(body["inputs"], dtype=np.float32).reshape(-1, 23) - result = self.model.predict(feats, validate_features=False) - return result.tolist() - except Exception as e: - raise Exception("Failed to predict %s" % e) - diff --git a/churn_server/function.yaml b/churn_server/function.yaml deleted file mode 100644 index 14f6c8cef..000000000 --- a/churn_server/function.yaml +++ /dev/null @@ -1,51 +0,0 @@ -kind: serving -metadata: - name: churn-server - tag: '' - hash: 805b4583ab8fa8df90c71d97eef54bbccf8729e8 - project: '' - labels: - author: Iguazio - framework: churn - categories: - - model-serving - - machine-learning -spec: - command: '' - args: [] - image: mlrun/ml-models - description: churn classification and predictor - min_replicas: 1 - max_replicas: 4 - env: - - name: ENABLE_EXPLAINER - value: 'False' - base_spec: - apiVersion: nuclio.io/v1 - kind: Function - metadata: - name: churn-server - labels: {} - annotations: - nuclio.io/generated_by: function generated from /User/functions/churn_server/churn_server.py - spec: - runtime: python:3.9 - handler: churn_server:handler - env: [] - volumes: [] - build: - commands: [] - noBaseImagesPull: true - functionSourceCode: IyBDb3B5cmlnaHQgMjAxOSBJZ3VhemlvCiMKIyBMaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgIkxpY2Vuc2UiKTsKIyB5b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuCiMgWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0CiMKIyAgICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wCiMKIyBVbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlCiMgZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gIkFTIElTIiBCQVNJUywKIyBXSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC4KIyBTZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kCiMgbGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuCiMKIyBHZW5lcmF0ZWQgYnkgbnVjbGlvLmV4cG9ydC5OdWNsaW9FeHBvcnRlcgoKaW1wb3J0IG51bXB5IGFzIG5wCmZyb20gY2xvdWRwaWNrbGUgaW1wb3J0IGxvYWQKCgppbXBvcnQgbWxydW4KCgpjbGFzcyBDaHVybk1vZGVsKG1scnVuLnNlcnZpbmcuVjJNb2RlbFNlcnZlcik6CiAgICBkZWYgbG9hZChzZWxmKToKICAgICAgICAiIiIKICAgICAgICBsb2FkIG11bHRpcGxlIG1vZGVscyBpbiBuZXN0ZWQgZm9sZGVycywgY2h1cm4gbW9kZWwgb25seQogICAgICAgICIiIgogICAgICAgIGNsZl9tb2RlbF9maWxlLCBleHRyYV9kYXRhID0gc2VsZi5nZXRfbW9kZWwoIi5wa2wiKQogICAgICAgIHNlbGYubW9kZWwgPSBsb2FkKG9wZW4oc3RyKGNsZl9tb2RlbF9maWxlKSwgInJiIikpCiAgICAgICAgaWYgImNveCIgaW4gZXh0cmFfZGF0YS5rZXlzKCk6CiAgICAgICAgICAgIGNveF9tb2RlbF9maWxlID0gZXh0cmFfZGF0YVsiY294Il0KICAgICAgICAgICAgc2VsZi5jb3hfbW9kZWwgPSBsb2FkKG9wZW4oc3RyKGNveF9tb2RlbF9maWxlKSwgInJiIikpCiAgICAgICAgICAgIGlmICJjb3gva20iIGluIGV4dHJhX2RhdGEua2V5cygpOgogICAgICAgICAgICAgICAga21fbW9kZWxfZmlsZSA9IGV4dHJhX2RhdGFbImNveC9rbSJdCiAgICAgICAgICAgICAgICBzZWxmLmttX21vZGVsID0gbG9hZChvcGVuKHN0cihrbV9tb2RlbF9maWxlKSwgInJiIikpCgogICAgZGVmIHByZWRpY3Qoc2VsZiwgYm9keSk6CiAgICAgICAgdHJ5OgogICAgICAgICAgICBmZWF0cyA9IG5wLmFzYXJyYXkoYm9keVsiaW5wdXRzIl0sIGR0eXBlPW5wLmZsb2F0MzIpLnJlc2hhcGUoLTEsIDIzKQogICAgICAgICAgICByZXN1bHQgPSBzZWxmLm1vZGVsLnByZWRpY3QoZmVhdHMsIHZhbGlkYXRlX2ZlYXR1cmVzPUZhbHNlKQogICAgICAgICAgICByZXR1cm4gcmVzdWx0LnRvbGlzdCgpCiAgICAgICAgZXhjZXB0IEV4Y2VwdGlvbiBhcyBlOgogICAgICAgICAgICByYWlzZSBFeGNlcHRpb24oIkZhaWxlZCB0byBwcmVkaWN0ICVzIiAlIGUpCgoKZnJvbSBtbHJ1bi5ydW50aW1lcyBpbXBvcnQgbnVjbGlvX2luaXRfaG9vawpkZWYgaW5pdF9jb250ZXh0KGNvbnRleHQpOgogICAgbnVjbGlvX2luaXRfaG9vayhjb250ZXh0LCBnbG9iYWxzKCksICdzZXJ2aW5nX3YyJykKCmRlZiBoYW5kbGVyKGNvbnRleHQsIGV2ZW50KToKICAgIHJldHVybiBjb250ZXh0Lm1scnVuX2hhbmRsZXIoY29udGV4dCwgZXZlbnQpCg== - source: '' - function_kind: serving_v2 - default_class: ChurnModel - build: - commands: - - python -m pip install xgboost==1.3.1 lifelines==0.22.8 - code_origin: https://github.com/daniels290813/functions.git#34d1b0d7e26924d931c2df2869425d01df21a23c:/User/functions/churn_server/churn_server.py - origin_filename: /User/functions/churn_server/churn_server.py - secret_sources: [] - disable_auto_mount: false - affinity: null -verbose: false diff --git a/churn_server/item.yaml b/churn_server/item.yaml deleted file mode 100644 index 09ba9b713..000000000 --- a/churn_server/item.yaml +++ /dev/null @@ -1,32 +0,0 @@ -apiVersion: v1 -categories: -- model-serving -- machine-learning -description: churn classification and predictor -doc: '' -example: churn_server.ipynb -generationDate: 2022-08-28:17-25 -hidden: false -icon: '' -labels: - author: Iguazio - framework: churn -maintainers: [] -marketplaceType: '' -mlrunVersion: 1.1.0 -name: churn-server -platformVersion: 3.5.0 -spec: - customFields: - default_class: ChurnModel - env: - ENABLE_EXPLAINER: 'False' - filename: churn_server.py - handler: handler - image: mlrun/ml-models - kind: serving - requirements: - - xgboost==1.3.1 - - lifelines==0.22.8 -url: '' -version: 1.2.0 diff --git a/churn_server/requirements.txt b/churn_server/requirements.txt deleted file mode 100644 index eb8827c5c..000000000 --- a/churn_server/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -wget -pygit2 \ No newline at end of file diff --git a/churn_server/test_churn_server.py b/churn_server/test_churn_server.py deleted file mode 100644 index 64d1b8490..000000000 --- a/churn_server/test_churn_server.py +++ /dev/null @@ -1,67 +0,0 @@ -# Copyright 2019 Iguazio -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -import os -import wget -from mlrun import import_function -import os.path -from os import path -import mlrun -from pygit2 import Repository - - -MODEL_PATH = os.path.join(os.path.abspath("./"), "models") -MODEL = MODEL_PATH + "model.pt" - - -def set_mlrun_hub_url(): - branch = Repository(".").head.shorthand - hub_url = "https://raw.githubusercontent.com/mlrun/functions/{}/churn_server/function.yaml".format( - branch - ) - mlrun.mlconf.hub_url = hub_url - - -def download_pretrained_model(model_path): - # Run this to download the pre-trained model to your `models` directory - import os - - model_location = None - saved_models_directory = model_path - # Create paths - os.makedirs(saved_models_directory, exist_ok=1) - model_filepath = os.path.join( - saved_models_directory, os.path.basename(model_location) - ) - wget.download(model_location, model_filepath) - - -def test_local_churn_server(): - # set_mlrun_hub_url() - # model_path = os.path.join(os.path.abspath("./"), "models") - # model = model_path + "/model.pt" - # if not path.exists(model): - # download_pretrained_model(model_path) - # fn = import_function("hub://churn_server") - # fn.add_model("mymodel", model_path=model, class_name="ChurnModel") - # # create an emulator (mock server) from the function configuration) - # server = fn.to_mock_server() - # - # instances = [ - # "I had a pleasure to work with such dedicated team. Looking forward to \ - # cooperate with each and every one of them again." - # ] - # result = server.test("/v2/models/mymodel/infer", {"instances": instances}) - # assert result[0] == 2 - print("we need to download churn model") diff --git a/cli/marketplace/conf.template b/cli/marketplace/conf.template index 8c6e9f344..f78fde1e6 100644 --- a/cli/marketplace/conf.template +++ b/cli/marketplace/conf.template @@ -93,6 +93,7 @@ html_theme_options = { "path_to_docs": "docs", "repository_branch": "{{repository_branch}}", "single_page": True, + "navigation_with_keys": False, } html_title = "{{html_title}}" diff --git a/coxph_test/coxph_test.ipynb b/coxph_test/coxph_test.ipynb deleted file mode 100644 index 0ee0b29c9..000000000 --- a/coxph_test/coxph_test.ipynb +++ /dev/null @@ -1,969 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# **CoxPH test**" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This function handles evaluating Cox proportional hazards model performance, test one or more classifier models against held-out dataset Using held-out test features,
and evaluates the peformance of the estimated model.
\n", - "Can be part of a kubeflow pipeline as a test step that is run post EDA and training/validation cycles.
\n", - "This function is part of the [customer-churn-prediction](https://github.com/mlrun/demos/tree/master/customer-churn-prediction) demo.
\n", - "To see how the model is trained or how the data-set is generated, check out `coxph_trainer` function from the function marketplace repository" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### **Steps**\n", - "1. [Setup function parameters](#Setup-function-parameters)\n", - "2. [Importing the function](#Importing-the-function)\n", - "3. [Running the function locally](#Running-the-function-locally)\n", - "4. [Running the function remotely](#Running-the-function-remotely)" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "import warnings\n", - "warnings.filterwarnings(\"ignore\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### **Setup function parameters**" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "test_set = \"https://s3.wasabisys.com/iguazio/data/function-marketplace-data/xgb_test/test_set.csv\"\n", - "models_path = \"https://s3.wasabisys.com/iguazio/models/function-marketplace-models/coxph_test/cx-model.pkl\"" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### **Importing the function**" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "> 2021-10-17 13:38:44,758 [info] loaded project function-marketplace from MLRun DB\n" - ] - } - ], - "source": [ - "import mlrun\n", - "mlrun.set_environment(project='function-marketplace')\n", - "\n", - "fn = mlrun.import_function(\"hub://coxph_test\")\n", - "fn.apply(mlrun.auto_mount())\n", - "\n", - "fn.spec.build.image=\"mlrun/ml-models\"" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### **Running the function locally**" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "> 2021-10-17 13:38:45,149 [info] starting run tasks_coxph_test uid=be4bd195e5c146a69ecdee3b6a631569 DB=http://mlrun-api:8080\n", - "> 2021-10-17 13:38:49,428 [info] cox tester not implemented\n" - ] - }, - { - "data": { - "text/html": [ - "\n", - "
\n", - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
projectuiditerstartstatenamelabelsinputsparametersresultsartifacts
function-marketplace0Oct 17 13:38:45completedtasks_coxph_test
v3io_user=dani
kind=
owner=dani
host=jupyter-dani-6bfbd76d96-zxx6f
test_set
models_path
label_column=labels
plots_dest=plots/xgb_test
cox-test-summary
\n", - "
\n", - "
\n", - "
\n", - " Title\n", - " ×\n", - "
\n", - " \n", - "
\n", - "
\n" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n" - ] - }, - { - "data": { - "text/html": [ - " > to track results use the .show() or .logs() methods or click here to open in UI" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "> 2021-10-17 13:38:49,497 [info] run executed, status=completed\n" - ] - } - ], - "source": [ - "coxph_run = fn.run(name='tasks_coxph_test',\n", - " params = {\"label_column\" : \"labels\",\n", - " \"plots_dest\" : \"plots/xgb_test\"},\n", - " inputs = {\"test_set\" : test_set,\n", - " \"models_path\" : models_path},\n", - " local=True)" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
covariatecoefexp(coef)se(coef)coef lower 95%coef upper 95%exp(coef) lower 95%exp(coef) upper 95%zp-log2(p)
0gender0.7129862.040073e+000.3434710.0397951.3861761.0405983.9995282.0758260.0379104.721274
1senior-0.3301377.188252e-010.444705-1.2017430.5414680.3006701.718528-0.7423740.4578611.127018
2partner-0.3944496.740516e-010.432243-1.2416300.4527320.2889131.572603-0.9125620.3614731.468041
3deps0.6163731.852199e+000.499075-0.3617971.5945430.6964244.9260801.2350310.2168192.205436
4MultipleLines-0.7878854.548059e-011.087536-2.9194171.3436480.0539653.832999-0.7244670.4687791.093020
5OnlineSecurity-0.7666834.645512e-011.299746-3.3141391.7807720.0363655.934435-0.5898720.5552770.848721
6OnlineBackup-0.4666916.270740e-010.949068-2.3268291.3934480.0976054.028715-0.4917360.6229060.682914
7DeviceProtection-0.4126206.619136e-011.083731-2.5366941.7114530.0791285.537002-0.3807410.7033960.507591
8TechSupport0.5097561.664885e+001.168080-1.7796382.7991500.16869916.4306750.4364050.6625430.593915
9PaperlessBilling0.3499701.419025e+000.408827-0.4513171.1512570.6367893.1621650.8560330.3919801.351150
10MonthlyCharges-0.0783999.245958e-010.194463-0.4595390.3027420.6315741.353566-0.4031540.6868350.541965
11Contract_1-2.1882791.121096e-010.712197-3.584159-0.7923980.0277600.452758-3.0725750.0021228.880219
12Contract_2-19.9407672.186930e-093478.684973-6838.0380276798.1564930.000000inf-0.0057320.9954260.006614
13Payment_1-0.8654244.208732e-010.615020-2.0708400.3399930.1260801.404937-1.4071480.1593832.649426
14Payment_20.4583631.581483e+000.446978-0.4176971.3344230.6585623.7978051.0254720.3051411.712453
15Payment_30.2325191.261774e+000.641176-1.0241621.4892000.3590974.4335470.3626440.7168700.480216
\n", - "
" - ], - "text/plain": [ - " covariate coef exp(coef) se(coef) coef lower 95% \\\n", - "0 gender 0.712986 2.040073e+00 0.343471 0.039795 \n", - "1 senior -0.330137 7.188252e-01 0.444705 -1.201743 \n", - "2 partner -0.394449 6.740516e-01 0.432243 -1.241630 \n", - "3 deps 0.616373 1.852199e+00 0.499075 -0.361797 \n", - "4 MultipleLines -0.787885 4.548059e-01 1.087536 -2.919417 \n", - "5 OnlineSecurity -0.766683 4.645512e-01 1.299746 -3.314139 \n", - "6 OnlineBackup -0.466691 6.270740e-01 0.949068 -2.326829 \n", - "7 DeviceProtection -0.412620 6.619136e-01 1.083731 -2.536694 \n", - "8 TechSupport 0.509756 1.664885e+00 1.168080 -1.779638 \n", - "9 PaperlessBilling 0.349970 1.419025e+00 0.408827 -0.451317 \n", - "10 MonthlyCharges -0.078399 9.245958e-01 0.194463 -0.459539 \n", - "11 Contract_1 -2.188279 1.121096e-01 0.712197 -3.584159 \n", - "12 Contract_2 -19.940767 2.186930e-09 3478.684973 -6838.038027 \n", - "13 Payment_1 -0.865424 4.208732e-01 0.615020 -2.070840 \n", - "14 Payment_2 0.458363 1.581483e+00 0.446978 -0.417697 \n", - "15 Payment_3 0.232519 1.261774e+00 0.641176 -1.024162 \n", - "\n", - " coef upper 95% exp(coef) lower 95% exp(coef) upper 95% z \\\n", - "0 1.386176 1.040598 3.999528 2.075826 \n", - "1 0.541468 0.300670 1.718528 -0.742374 \n", - "2 0.452732 0.288913 1.572603 -0.912562 \n", - "3 1.594543 0.696424 4.926080 1.235031 \n", - "4 1.343648 0.053965 3.832999 -0.724467 \n", - "5 1.780772 0.036365 5.934435 -0.589872 \n", - "6 1.393448 0.097605 4.028715 -0.491736 \n", - "7 1.711453 0.079128 5.537002 -0.380741 \n", - "8 2.799150 0.168699 16.430675 0.436405 \n", - "9 1.151257 0.636789 3.162165 0.856033 \n", - "10 0.302742 0.631574 1.353566 -0.403154 \n", - "11 -0.792398 0.027760 0.452758 -3.072575 \n", - "12 6798.156493 0.000000 inf -0.005732 \n", - "13 0.339993 0.126080 1.404937 -1.407148 \n", - "14 1.334423 0.658562 3.797805 1.025472 \n", - "15 1.489200 0.359097 4.433547 0.362644 \n", - "\n", - " p -log2(p) \n", - "0 0.037910 4.721274 \n", - "1 0.457861 1.127018 \n", - "2 0.361473 1.468041 \n", - "3 0.216819 2.205436 \n", - "4 0.468779 1.093020 \n", - "5 0.555277 0.848721 \n", - "6 0.622906 0.682914 \n", - "7 0.703396 0.507591 \n", - "8 0.662543 0.593915 \n", - "9 0.391980 1.351150 \n", - "10 0.686835 0.541965 \n", - "11 0.002122 8.880219 \n", - "12 0.995426 0.006614 \n", - "13 0.159383 2.649426 \n", - "14 0.305141 1.712453 \n", - "15 0.716870 0.480216 " - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "coxph_run.artifact('cox-test-summary').show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### **Running the function remotely**" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "> 2021-10-17 13:38:49,644 [info] starting run tasks_coxph_test uid=c28d05f0261b4c60956eee528bf68e96 DB=http://mlrun-api:8080\n", - "> 2021-10-17 13:38:49,776 [info] Job is running in the background, pod: tasks-coxph-test-hfj9b\n", - "> 2021-10-17 13:38:59,015 [info] cox tester not implemented\n", - "> 2021-10-17 13:38:59,049 [info] run executed, status=completed\n", - "final state: completed\n" - ] - }, - { - "data": { - "text/html": [ - "\n", - "
\n", - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
projectuiditerstartstatenamelabelsinputsparametersresultsartifacts
function-marketplace0Oct 17 13:38:56completedtasks_coxph_test
v3io_user=dani
kind=job
owner=dani
host=tasks-coxph-test-hfj9b
test_set
models_path
label_column=labels
plots_dest=plots/xgb_test
cox-test-summary
\n", - "
\n", - "
\n", - "
\n", - " Title\n", - " ×\n", - "
\n", - " \n", - "
\n", - "
\n" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n" - ] - }, - { - "data": { - "text/html": [ - " > to track results use the .show() or .logs() methods or click here to open in UI" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "> 2021-10-17 13:39:08,990 [info] run executed, status=completed\n" - ] - } - ], - "source": [ - "fn.deploy(with_mlrun=False, # mlrun is included in our image (mlrun/ml-models) therefore no mlrun installation is needed.\n", - " skip_deployed=True) # because no new packages or upgrade is required, we can use the original image and not build another one.\n", - "\n", - "coxph_run = fn.run(name='tasks_coxph_test',\n", - " params = {\"label_column\" : \"labels\",\n", - " \"plots_dest\" : \"plots/xgb_test\"},\n", - " inputs = {\"test_set\" : test_set,\n", - " \"models_path\" : models_path},\n", - " local=False)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "[Back to the top](#CoxPH-test)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.6" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/coxph_test/coxph_test.py b/coxph_test/coxph_test.py deleted file mode 100644 index f635fbdf3..000000000 --- a/coxph_test/coxph_test.py +++ /dev/null @@ -1,75 +0,0 @@ -# Copyright 2019 Iguazio -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# Generated by nuclio.export.NuclioExporter - -import warnings - -warnings.simplefilter(action="ignore", category=FutureWarning) - -import os -import pandas as pd -from mlrun.datastore import DataItem -from mlrun.artifacts import get_model -from cloudpickle import load -from mlrun.mlutils.models import eval_class_model - - -def cox_test( - context, - models_path: DataItem, - test_set: DataItem, - label_column: str, - plots_dest: str = "plots", - model_evaluator=None, -) -> None: - """Test one or more classifier models against held-out dataset - - Using held-out test features, evaluates the peformance of the estimated model - - Can be part of a kubeflow pipeline as a test step that is run post EDA and - training/validation cycles - - :param context: the function context - :param model_file: model artifact to be tested - :param test_set: test features and labels - :param label_column: column name for ground truth labels - :param score_method: for multiclass classification - :param plots_dest: dir for test plots - :param model_evaluator: WIP: specific method to generate eval, passed in as string - or available in this folder - """ - xtest = test_set.as_df() - ytest = xtest.pop(label_column) - - model_file, model_obj, _ = get_model(models_path.url, suffix=".pkl") - model_obj = load(open(str(model_file), "rb")) - - try: - if not model_evaluator: - eval_metrics = eval_class_model(context, xtest, ytest, model_obj) - - model_plots = eval_metrics.pop("plots") - model_tables = eval_metrics.pop("tables") - for plot in model_plots: - context.log_artifact(plot, local_path=f"{plots_dest}/{plot.key}.html") - for tbl in model_tables: - context.log_artifact(tbl, local_path=f"{plots_dest}/{plot.key}.csv") - - context.log_results(eval_metrics) - except: - context.log_dataset( - "cox-test-summary", df=model_obj.summary, index=True, format="csv" - ) - context.logger.info("cox tester not implemented") diff --git a/coxph_test/function.yaml b/coxph_test/function.yaml deleted file mode 100644 index e09fb90a0..000000000 --- a/coxph_test/function.yaml +++ /dev/null @@ -1,63 +0,0 @@ -kind: job -metadata: - name: coxph-test - tag: '' - hash: 1edbfe55668a7dcfaa59a6aeb5b3b1bd3f594aab - project: '' - labels: - author: Iguazio - framework: survival - categories: - - machine-learning - - model-testing -spec: - command: '' - args: [] - image: mlrun/ml-models - env: [] - default_handler: cox_test - entry_points: - cox_test: - name: cox_test - doc: 'Test one or more classifier models against held-out dataset - - - Using held-out test features, evaluates the peformance of the estimated model - - - Can be part of a kubeflow pipeline as a test step that is run post EDA and - - training/validation cycles' - parameters: - - name: context - doc: the function context - default: '' - - name: models_path - type: DataItem - default: '' - - name: test_set - type: DataItem - doc: test features and labels - default: '' - - name: label_column - type: str - doc: column name for ground truth labels - default: '' - - name: plots_dest - type: str - doc: dir for test plots - default: plots - - name: model_evaluator - doc: 'WIP: specific method to generate eval, passed in as string or available - in this folder' - default: null - outputs: - - default: '' - lineno: 15 - description: Test cox proportional hazards model - build: - functionSourceCode: IyBHZW5lcmF0ZWQgYnkgbnVjbGlvLmV4cG9ydC5OdWNsaW9FeHBvcnRlcgoKaW1wb3J0IHdhcm5pbmdzCgp3YXJuaW5ncy5zaW1wbGVmaWx0ZXIoYWN0aW9uPSJpZ25vcmUiLCBjYXRlZ29yeT1GdXR1cmVXYXJuaW5nKQoKaW1wb3J0IG9zCmltcG9ydCBwYW5kYXMgYXMgcGQKZnJvbSBtbHJ1bi5kYXRhc3RvcmUgaW1wb3J0IERhdGFJdGVtCmZyb20gbWxydW4uYXJ0aWZhY3RzIGltcG9ydCBnZXRfbW9kZWwKZnJvbSBjbG91ZHBpY2tsZSBpbXBvcnQgbG9hZApmcm9tIG1scnVuLm1sdXRpbHMubW9kZWxzIGltcG9ydCBldmFsX2NsYXNzX21vZGVsCgoKZGVmIGNveF90ZXN0KAogICAgY29udGV4dCwKICAgIG1vZGVsc19wYXRoOiBEYXRhSXRlbSwKICAgIHRlc3Rfc2V0OiBEYXRhSXRlbSwKICAgIGxhYmVsX2NvbHVtbjogc3RyLAogICAgcGxvdHNfZGVzdDogc3RyID0gInBsb3RzIiwKICAgIG1vZGVsX2V2YWx1YXRvcj1Ob25lLAopIC0+IE5vbmU6CiAgICAiIiJUZXN0IG9uZSBvciBtb3JlIGNsYXNzaWZpZXIgbW9kZWxzIGFnYWluc3QgaGVsZC1vdXQgZGF0YXNldAoKICAgIFVzaW5nIGhlbGQtb3V0IHRlc3QgZmVhdHVyZXMsIGV2YWx1YXRlcyB0aGUgcGVmb3JtYW5jZSBvZiB0aGUgZXN0aW1hdGVkIG1vZGVsCgogICAgQ2FuIGJlIHBhcnQgb2YgYSBrdWJlZmxvdyBwaXBlbGluZSBhcyBhIHRlc3Qgc3RlcCB0aGF0IGlzIHJ1biBwb3N0IEVEQSBhbmQKICAgIHRyYWluaW5nL3ZhbGlkYXRpb24gY3ljbGVzCgogICAgOnBhcmFtIGNvbnRleHQ6ICAgICAgICAgdGhlIGZ1bmN0aW9uIGNvbnRleHQKICAgIDpwYXJhbSBtb2RlbF9maWxlOiAgICAgIG1vZGVsIGFydGlmYWN0IHRvIGJlIHRlc3RlZAogICAgOnBhcmFtIHRlc3Rfc2V0OiAgICAgICAgdGVzdCBmZWF0dXJlcyBhbmQgbGFiZWxzCiAgICA6cGFyYW0gbGFiZWxfY29sdW1uOiAgICBjb2x1bW4gbmFtZSBmb3IgZ3JvdW5kIHRydXRoIGxhYmVscwogICAgOnBhcmFtIHNjb3JlX21ldGhvZDogICAgZm9yIG11bHRpY2xhc3MgY2xhc3NpZmljYXRpb24KICAgIDpwYXJhbSBwbG90c19kZXN0OiAgICAgIGRpciBmb3IgdGVzdCBwbG90cwogICAgOnBhcmFtIG1vZGVsX2V2YWx1YXRvcjogV0lQOiBzcGVjaWZpYyBtZXRob2QgdG8gZ2VuZXJhdGUgZXZhbCwgcGFzc2VkIGluIGFzIHN0cmluZwogICAgICAgICAgICAgICAgICAgICAgICAgICAgb3IgYXZhaWxhYmxlIGluIHRoaXMgZm9sZGVyCiAgICAiIiIKICAgIHh0ZXN0ID0gdGVzdF9zZXQuYXNfZGYoKQogICAgeXRlc3QgPSB4dGVzdC5wb3AobGFiZWxfY29sdW1uKQoKICAgIG1vZGVsX2ZpbGUsIG1vZGVsX29iaiwgXyA9IGdldF9tb2RlbChtb2RlbHNfcGF0aC51cmwsIHN1ZmZpeD0iLnBrbCIpCiAgICBtb2RlbF9vYmogPSBsb2FkKG9wZW4oc3RyKG1vZGVsX2ZpbGUpLCAicmIiKSkKCiAgICB0cnk6CiAgICAgICAgaWYgbm90IG1vZGVsX2V2YWx1YXRvcjoKICAgICAgICAgICAgZXZhbF9tZXRyaWNzID0gZXZhbF9jbGFzc19tb2RlbChjb250ZXh0LCB4dGVzdCwgeXRlc3QsIG1vZGVsX29iaikKCiAgICAgICAgbW9kZWxfcGxvdHMgPSBldmFsX21ldHJpY3MucG9wKCJwbG90cyIpCiAgICAgICAgbW9kZWxfdGFibGVzID0gZXZhbF9tZXRyaWNzLnBvcCgidGFibGVzIikKICAgICAgICBmb3IgcGxvdCBpbiBtb2RlbF9wbG90czoKICAgICAgICAgICAgY29udGV4dC5sb2dfYXJ0aWZhY3QocGxvdCwgbG9jYWxfcGF0aD1mIntwbG90c19kZXN0fS97cGxvdC5rZXl9Lmh0bWwiKQogICAgICAgIGZvciB0YmwgaW4gbW9kZWxfdGFibGVzOgogICAgICAgICAgICBjb250ZXh0LmxvZ19hcnRpZmFjdCh0YmwsIGxvY2FsX3BhdGg9ZiJ7cGxvdHNfZGVzdH0ve3Bsb3Qua2V5fS5jc3YiKQoKICAgICAgICBjb250ZXh0LmxvZ19yZXN1bHRzKGV2YWxfbWV0cmljcykKICAgIGV4Y2VwdDoKICAgICAgICBjb250ZXh0LmxvZ19kYXRhc2V0KAogICAgICAgICAgICAiY294LXRlc3Qtc3VtbWFyeSIsIGRmPW1vZGVsX29iai5zdW1tYXJ5LCBpbmRleD1UcnVlLCBmb3JtYXQ9ImNzdiIKICAgICAgICApCiAgICAgICAgY29udGV4dC5sb2dnZXIuaW5mbygiY294IHRlc3RlciBub3QgaW1wbGVtZW50ZWQiKQo= - commands: [] - code_origin: https://github.com/daniels290813/functions.git#55a79c32be5d233cc11efcf40cd3edbe309bfdef:/home/kali/functions/coxph_test/coxph_test.py - affinity: null -verbose: false diff --git a/coxph_test/item.yaml b/coxph_test/item.yaml deleted file mode 100644 index 241e6d560..000000000 --- a/coxph_test/item.yaml +++ /dev/null @@ -1,26 +0,0 @@ -apiVersion: v1 -categories: -- machine-learning -- model-testing -description: Test cox proportional hazards model -doc: '' -example: coxph_test.ipynb -generationDate: 2022-08-28:17-25 -hidden: false -icon: '' -labels: - author: Iguazio - framework: survival -maintainers: [] -marketplaceType: '' -mlrunVersion: 1.1.0 -name: coxph-test -platformVersion: 3.5.0 -spec: - filename: coxph_test.py - handler: cox_test - image: mlrun/ml-models - kind: job - requirements: [] -url: '' -version: 1.1.0 diff --git a/coxph_trainer/coxph_trainer.ipynb b/coxph_trainer/coxph_trainer.ipynb deleted file mode 100644 index d49d6ccf8..000000000 --- a/coxph_trainer/coxph_trainer.ipynb +++ /dev/null @@ -1,1799 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# **Coxph trainer - Survival analysis**" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The following function provides both [Cox proprotional hazards modelling](https://en.wikipedia.org/wiki/Proportional_hazards_model)\n", - "and [Kaplan-Meier](https://en.wikipedia.org/wiki/Kaplan%E2%80%93Meier_estimator) plots." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### **Basics of the Cox proportional hazards model**\n", - "The purpose of the model is to evaluate simultaneously the effect of several factors on survival.
In other words, it allows us to examine how specified factors influence the rate of a particular event happening (e.g., infection, death) at a particular point in time.
This rate is commonly referred as the hazard rate ([link](http://www.sthda.com/english/wiki/cox-proportional-hazards-model)).\n", - "\n", - "### **Kaplan-Meier survival estimate**\n", - "The Kaplan-Meier (KM) method is a non-parametric method used to estimate the survival probability from observed survival times (Kaplan and Meier, 1958)([link](http://www.sthda.com/english/wiki/survival-analysis-basics))." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### **The function**\n", - "train models to predict the timing of events.
\n", - "Although identical in structure to other training functions, this one\n", - "requires generating a 'Y' that represents the age/duration/tenure of\n", - "the obervation, designated 'tenure' here, and a binary labels columns that\n", - "represents the event of interest, churned/not-churned.
\n", - "In addition, there is a strata_cols parameter, representing a list of\n", - "stratification (aka grouping) variables." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The following example covers:\n", - "\n", - "- [Data exploration](#Data-exploration)\n", - "- [Training Cox proprotional hazards and Kaplan-Meier model](#Training-Cox-proprotional-hazards-and-Kaplan-Meier-model)\n", - "- - [Importing the function](#Importing-the-function)\n", - "- - [Setup function parameters](#Setup-function-parameters)\n", - "- - [Running the function locally](#Running-the-function-locally)\n", - "- [A peek at a pickled kaplan-meier model](#A-peek-at-a-pickled-kaplan-meier-model)\n", - "- [A peek at a pickeld cox hazards default model](#A-peek-at-a-pickeld-cox-hazards-default-model)\n", - "- [Some potential default analyses of coxph](#Some-potential-default-analyses-of-coxph)\n", - "- - [Running the function remotely](#Running-the-function-remotely)\n", - "\n", - "We will train on [Telco Customer Churn dataset](https://www.kaggle.com/blastchar/telco-customer-churn) from kaggle, click the link for context.
\n", - "The dataset is transformed using [one-hot-encoding](https://en.wikipedia.org/wiki/One-hot), check out [customer-churn-prediction demo](https://github.com/mlrun/demos/tree/master/customer-churn-prediction) in the clean_data section for further information." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "import warnings\n", - "warnings.filterwarnings(\"ignore\")" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "# Make sure the following libraries are installed \n", - "# !pip install lifelines" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### **Data exploration**" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "raw dataset\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
customerIDgenderSeniorCitizenPartnerDependentstenurePhoneServiceMultipleLinesInternetServiceOnlineSecurity...DeviceProtectionTechSupportStreamingTVStreamingMoviesContractPaperlessBillingPaymentMethodMonthlyChargesTotalChargesChurn
07590-VHVEGFemale0YesNo1NoNo phone serviceDSLNo...NoNoNoNoMonth-to-monthYesElectronic check29.8529.85No
15575-GNVDEMale0NoNo34YesNoDSLYes...YesNoNoNoOne yearNoMailed check56.951889.5No
23668-QPYBKMale0NoNo2YesNoDSLYes...NoNoNoNoMonth-to-monthYesMailed check53.85108.15Yes
37795-CFOCWMale0NoNo45NoNo phone serviceDSLYes...YesYesNoNoOne yearNoBank transfer (automatic)42.301840.75No
49237-HQITUFemale0NoNo2YesNoFiber opticNo...NoNoNoNoMonth-to-monthYesElectronic check70.70151.65Yes
\n", - "

5 rows × 21 columns

\n", - "
" - ], - "text/plain": [ - " customerID gender SeniorCitizen Partner Dependents tenure PhoneService \\\n", - "0 7590-VHVEG Female 0 Yes No 1 No \n", - "1 5575-GNVDE Male 0 No No 34 Yes \n", - "2 3668-QPYBK Male 0 No No 2 Yes \n", - "3 7795-CFOCW Male 0 No No 45 No \n", - "4 9237-HQITU Female 0 No No 2 Yes \n", - "\n", - " MultipleLines InternetService OnlineSecurity ... DeviceProtection \\\n", - "0 No phone service DSL No ... No \n", - "1 No DSL Yes ... Yes \n", - "2 No DSL Yes ... No \n", - "3 No phone service DSL Yes ... Yes \n", - "4 No Fiber optic No ... No \n", - "\n", - " TechSupport StreamingTV StreamingMovies Contract PaperlessBilling \\\n", - "0 No No No Month-to-month Yes \n", - "1 No No No One year No \n", - "2 No No No Month-to-month Yes \n", - "3 Yes No No One year No \n", - "4 No No No Month-to-month Yes \n", - "\n", - " PaymentMethod MonthlyCharges TotalCharges Churn \n", - "0 Electronic check 29.85 29.85 No \n", - "1 Mailed check 56.95 1889.5 No \n", - "2 Mailed check 53.85 108.15 Yes \n", - "3 Bank transfer (automatic) 42.30 1840.75 No \n", - "4 Electronic check 70.70 151.65 Yes \n", - "\n", - "[5 rows x 21 columns]" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Getting raw data as - downloaded from kaggle\n", - "import pandas as pd\n", - "\n", - "df = pd.read_csv(\"https://s3.wasabisys.com/iguazio/data/function-marketplace-data/coxph_trainer/WA_Fn-UseC_-Telco-Customer-Churn.csv\")\n", - "print('raw dataset')\n", - "df.head()" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "encoded dataset\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
genderseniorpartnerdepstenurePhoneServiceMultipleLinesInternetServiceOnlineSecurityOnlineBackupDeviceProtectionTechSupportStreamingTVStreamingMoviesContractPaperlessBillingPaymentMethodMonthlyChargeslabelstenure_map
00010100001000001229.8500.0
110003410010100010356.9502.0
21000210011000001353.8510.0
310004500010110010042.3003.0
40000210100000001270.7010.0
\n", - "
" - ], - "text/plain": [ - " gender senior partner deps tenure PhoneService MultipleLines \\\n", - "0 0 0 1 0 1 0 0 \n", - "1 1 0 0 0 34 1 0 \n", - "2 1 0 0 0 2 1 0 \n", - "3 1 0 0 0 45 0 0 \n", - "4 0 0 0 0 2 1 0 \n", - "\n", - " InternetService OnlineSecurity OnlineBackup DeviceProtection \\\n", - "0 0 0 1 0 \n", - "1 0 1 0 1 \n", - "2 0 1 1 0 \n", - "3 0 1 0 1 \n", - "4 1 0 0 0 \n", - "\n", - " TechSupport StreamingTV StreamingMovies Contract PaperlessBilling \\\n", - "0 0 0 0 0 1 \n", - "1 0 0 0 1 0 \n", - "2 0 0 0 0 1 \n", - "3 1 0 0 1 0 \n", - "4 0 0 0 0 1 \n", - "\n", - " PaymentMethod MonthlyCharges labels tenure_map \n", - "0 2 29.85 0 0.0 \n", - "1 3 56.95 0 2.0 \n", - "2 3 53.85 1 0.0 \n", - "3 0 42.30 0 3.0 \n", - "4 2 70.70 1 0.0 " - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "df = pd.read_csv(\"https://s3.wasabisys.com/iguazio/data/function-marketplace-data/coxph_trainer/encoded-data.csv\")\n", - "print('encoded dataset')\n", - "df.head()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## **Training Cox proprotional hazards and Kaplan-Meier model**" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### **Importing the function**" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "> 2021-10-13 13:22:07,678 [info] loaded project function-marketplace from MLRun DB\n" - ] - }, - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 16, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "import mlrun\n", - "mlrun.set_environment(project='function-marketplace')\n", - "\n", - "fn = mlrun.import_function(\"hub://coxph_trainer\")\n", - "fn.image='mlrun/mlrun'\n", - "fn.apply(mlrun.auto_mount())" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### **Setup function parameters**" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "task = mlrun.new_task(name = \"tasks-survive-trainer\",\n", - " params = {\"event_column\" : \"labels\", \n", - " \"strata_cols\" : ['InternetService', 'StreamingMovies', 'StreamingTV', 'PhoneService'],\n", - " \"p_value\" : 0.005,\n", - " \"encode_cols\" : {\"Contract\" : \"Contract\",\n", - " \"PaymentMethod\" : \"Payment\"},\n", - " \"models_dest\" : 'models/cox',\n", - " \"file_ext\" : \"csv\"})" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### **Running the function locally**" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "> 2021-10-13 13:18:36,975 [info] starting run tasks-survive-trainer uid=c525c7402c7e4188b0e22996e8fc682c DB=http://mlrun-api:8080\n" - ] - }, - { - "data": { - "text/html": [ - "\n", - "
\n", - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
projectuiditerstartstatenamelabelsinputsparametersresultsartifacts
default0Oct 13 13:18:37completedtasks-survive-trainer
v3io_user=dani
kind=
owner=dani
host=jupyter-dani-5bbd9959b7-tsgh8
dataset
event_column=labels
strata_cols=['InternetService', 'StreamingMovies', 'StreamingTV', 'PhoneService']
p_value=0.005
encode_cols={'Contract': 'Contract', 'PaymentMethod': 'Payment'}
models_dest=models/cox
file_ext=csv
tenured-test-set
km-timelines
km-survival
km-model
coxhazard-summary
cx-model
\n", - "
\n", - "
\n", - "
\n", - " Title\n", - " ×\n", - "
\n", - " \n", - "
\n", - "
\n" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n" - ] - }, - { - "data": { - "text/html": [ - " > to track results use the .show() or .logs() methods or click here to open in UI" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "> 2021-10-13 13:18:41,277 [info] run executed, status=completed\n" - ] - }, - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXQAAAEGCAYAAAB1iW6ZAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjEsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+j8jraAAAZZ0lEQVR4nO3de5BU5ZnH8e8jjo7AAApoKZcMiyReE0xmBcPGTVQUjTDRzZYoW7msSqgVK8lu3AU3iTc2hUVK11Q0LOWFSrSkDEaZWCwQiQbX0ggkJHLROF7QlqwCCSphB0Ge/aPPjE3T0Kd7+nLO279P1dT0ucyZp2bgN6ef8573mLsjIiLpd1i9CxARkcpQoIuIBEKBLiISCAW6iEggFOgiIoE4vF7feMiQId7a2lqvby8ikkpr167d5u5DC22rW6C3trayZs2aen17EZFUMrPNB9umlouISCAU6CIigVCgi4gEom49dBFJnj179pDJZOjq6qp3KQ2vubmZ4cOH09TUFPtrFOgi0iOTydDS0kJraytmVu9yGpa7s337djKZDKNGjYr9dUVbLmZ2r5m9bWbrD7LdzOwHZtZpZr83s0+WULeIJEhXVxeDBw9WmNeZmTF48OCS3ynF6aEvBCYdYvuFwJjoYzrwo5IqEJFEUZgnQzm/h6ItF3dfZWath9ilHfixZ+fhfdbMBpnZ8e7+x5KrieGmn29g45Z3Dyxi7DCuGDeyGt9SRCQVKtFDHwa8kbOcidYdEOhmNp3sWTwjR5Yfvu927dlvefP2XbzbtUeBLiINrRLDFgu9Lyj41Ax3X+Dube7eNnRowTtXi7ph8qn899fP3u/j9GEDyzqWiCRP//79e14vXbqUMWPG8Prrr3PjjTdiZnR2dvZsv/322zGzit11/uijj7Jx48ae5e9+97s8/vjjvT7ujh07uOuuu3p9nGIqEegZYETO8nBgSwWOKyINbOXKlVx77bUsW7as5x396aefzqJFi3r2Wbx4MaecckrFvmd+oN98882cd955vT5urQK9Ei2XDmCmmS0CxgHvVKt/LiK1c7DrVb1xygkDuGHyqUX3e+qpp7j66qtZunQpo0eP7ln/hS98gSVLlvDtb3+bV155hYEDBxYdp71ixQpuuOEGdu/ezejRo7nvvvvo378/s2bNoqOjg8MPP5zzzz+fSy+9lI6ODn71q18xZ84cHn74YW655RYuvvhivvjFL9La2soVV1zBE088wZ49e1iwYAGzZ8+ms7OT6667jhkzZrBz507a29v585//zJ49e5gzZw7t7e3MmjWLl19+mbFjxzJx4kTmzZvHvHnzeOihh9i9ezeXXHIJN910U69/vkUD3cweBD4LDDGzDHAD0ATg7vOBpcBFQCewC/hqr6sSkYa1e/du2tvbefLJJznppJP22zZgwABGjBjB+vXrWbJkCZdddhn33XffQY+1bds25syZw+OPP06/fv249dZbue2225g5cyaPPPIIL7zwAmbGjh07GDRoEFOmTOkJ8EJGjBjBM888wze/+U2+8pWv8PTTT9PV1cWpp57KjBkzaG5u5pFHHmHAgAFs27aN8ePHM2XKFObOncv69etZt24dkP0j89JLL/Hcc8/h7kyZMoVVq1Zx9tln9+pnF2eUy+VFtjtwTa+qEJHEiXMmXQ1NTU18+tOf5p577uGOO+44YPvUqVNZtGgRy5cvZ+XKlYcM9GeffZaNGzcyYcIEAN5//33OOussBgwYQHNzM1dddRWf//znufjii2PVNmXKFCDb+tm5cyctLS20tLTQ3NzMjh076NevH9dffz2rVq3isMMO48033+Stt9464DgrVqxgxYoVnHHGGQDs3LmTl156qfqBLiJSS4cddhgPPfQQ5513Ht/73ve4/vrr99s+efJkrrvuOtra2hgwYMAhj+XuTJw4kQcffPCAbc899xwrV65k0aJF/PCHP+SXv/xl0dqOPPLInhq7X3cv7927lwceeICtW7eydu1ampqaaG1tLXhzkLsze/Zsvva1rxX9nqUIJtA/2Od0rHuzZ7nlqCY+97Fj61iRiJSrb9++PPbYY3zmM5/huOOO48orr+zZdtRRR3Hrrbfy0Y9+tOhxxo8fzzXXXENnZycnnngiu3btIpPJcMIJJ7Br1y4uuugixo8fz4knnghAS0sL7733Xtl1v/POOxx77LE0NTXxxBNPsHnz5oLHveCCC/jOd77DtGnT6N+/P2+++SZNTU0ce2zvMiuYQN+3zxna0tyzvPU9TS4kkmbHHHMMy5Yt4+yzz2bIkCH7bZs6dWqsYwwdOpSFCxdy+eWXs3v3bgDmzJlDS0sL7e3tdHV14e7cfvvtPce9+uqr+cEPfsDixYtLrnnatGlMnjyZ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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "# running the function with the task provided\n", - "coxph_run = fn.run(task,\n", - " local=True,\n", - " inputs={\"dataset\" : \"https://s3.wasabisys.com/iguazio/data/function-marketplace-data/coxph_trainer/encoded-data.csv\"})" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### **A peek at a pickled kaplan-meier model**" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [], - "source": [ - "# loading the model trained\n", - "from mlrun.artifacts import get_model\n", - "import pickle\n", - "model_file, model_obj, _ = get_model(coxph_run.artifact('km-model'))\n", - "model = pickle.load(open(model_file,'rb'))" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "1 0.969027\n", - "10 0.869452\n", - "30 0.781377\n", - "100 0.668167\n", - "200 0.668167\n", - "Name: KM_estimate, dtype: float64" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "model.predict([1,10,30,100,200])" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAosAAAFzCAYAAABWw0P+AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjEsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+j8jraAAAfcUlEQVR4nO3df7DdZX0n8PdHSA1CECXgtAQNzVAFfzS2WX80O67WX1glUdeOgJ1Wp9UyK07b3bqLtlt/0a4OO1o7almmIm1lZShWRYcFKv7AYVSEllZ+aI3YaKArSVqQNBsk8Owf94DXkO89595z7j333PN6zWRyvj/u9z48ucZ3vs/zfJ5qrQUAAA7mEeNuAAAAy5ewCABAJ2ERAIBOwiIAAJ2ERQAAOgmLAAB0OnRc33jt2rVt/fr14/r2AAD03HDDDbtaa8cc7NrYwuL69etz/fXXj+vbAwDQU1Xbu64ZhgYAoJOwCABAJ2ERAIBOY5uzCABMh/vuuy87duzIvn37xt2Uqbd69eqsW7cuq1atGvhrhEUAYFHt2LEja9asyfr161NV427O1GqtZffu3dmxY0dOOOGEgb/OMDQAsKj27duXo48+WlAcs6rK0UcfPe83vMIiALDoBMXlYSF/DsIiAACdhEUAYMU74ogjHvp8+eWX58QTT8x3v/vdvP3tb09VZdu2bQ9df9/73peqGtnmIZ/85Cdzyy23PHT8B3/wB/nsZz879HPvuuuufOhDHxr6Of30DYtVdUFV3VlVN3Vcr6r6k6raVlX/UFU/N/pmAgAM7+qrr86b3vSmXHHFFXn84x+fJHnqU5+aiy+++KF7Lr300px88skj+54HhsV3vvOdecELXjD0c5cqLA6yGvrCJB9I8hcd11+S5MTer2cm+dPe7wAAP+Ydn745t9zxg5E+8+SfOjJvO/XJfe/70pe+lNe//vW5/PLLs2HDhofOv/zlL8+nPvWp/P7v/35uu+22PPrRj+5bWuaqq67K2972ttx7773ZsGFDPvKRj+SII47I2WefncsuuyyHHnpoXvSiF+WVr3xlLrvssnzxi1/MOeeck49//ON517velZe97GV51atelfXr1+eMM87I5z//+dx33305//zz85a3vCXbtm3Lm9/85px55pnZs2dPtm7dmn/913/Nfffdl3POOSdbt27N2WefnW9/+9vZuHFjXvjCF+bcc8/Nueeem0suuST33ntvXvGKV+Qd73jH0P3bNyy21q6pqvVz3LI1yV+01lqSr1TVUVX1k621fx66dSMyyA/m1o3H5YxnPn6JWgQALKV77703W7duzRe+8IU86UlP+rFrRx55ZI4//vjcdNNN+dSnPpVXv/rV+chHPtL5rF27duWcc87JZz/72Rx++OF5z3vek/e+970566yz8olPfCLf+MY3UlW56667ctRRR2XLli0PhcODOf744/PlL385v/M7v5PXvva1ufbaa7Nv3748+clPzplnnpnVq1fnE5/4RI488sjs2rUrz3rWs7Jly5a8+93vzk033ZQbb7wxyUyA/da3vpXrrrsurbVs2bIl11xzTZ7znOcM1XejqLN4XJLvzTre0Tv3sLBYVW9I8oYkD736XSo/2Hdf57Xtu/fmB/vuExYBYJEN8gZwMaxatSq/8Au/kA9/+MN5//vf/7Drp512Wi6++OJceeWVufrqq+cMi1/5yldyyy23ZPPmzUmSH/7wh3n2s5+dI488MqtXr85v/MZv5KUvfWle9rKXDdS2LVu2JJkZDt+zZ0/WrFmTNWvWZPXq1bnrrrty+OGH561vfWuuueaaPOIRj8jtt9+e73//+w97zlVXXZWrrroqT3/605Mke/bsybe+9a1lERYPtga7HezG1tr5Sc5Pkk2bNh30nsXQ7wfz1f/ry3OGSQBgsj3iEY/IJZdckhe84AX5oz/6o7z1rW/9seunnnpq3vzmN2fTpk058sgj53xWay0vfOEL87GPfexh16677rpcffXVufjii/OBD3wgn/vc5/q27ZGPfORDbXzw84PH+/fvz0UXXZSdO3fmhhtuyKpVq7J+/fqD1kpsreUtb3lLfvM3f7Pv95yPUayG3pHk+FnH65LcMYLnAgCMzKMe9ah85jOfyUUXXZQPf/jDP3btsMMOy3ve85783u/9Xt/nPOtZz8q111770ArqvXv35h//8R+zZ8+e3H333fmlX/ql/PEf//FDw8Nr1qzJPffcs+B233333Tn22GOzatWqfP7zn8/27dsP+twXv/jFueCCC7Jnz54kye23354777xzwd/3QaN4s3hZkrOq6uLMLGy5eznNVwQAeNBjH/vYXHHFFXnOc56TtWvX/ti10047baBnHHPMMbnwwgtz+umn5957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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "m = model.plot(figsize=(11,6))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### **A peek at a pickeld cox hazards default model**" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [], - "source": [ - "# loading the model trained\n", - "from mlrun.artifacts import get_model\n", - "import pickle\n", - "model_file, model_obj, _ = get_model(coxph_run.artifact('cx-model'))\n", - "model = pickle.load(open(model_file,'rb'))" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
modellifelines.CoxPHFitter
duration col'tenure'
event col'labels'
strata[InternetService, StreamingMovies, StreamingTV...
baseline estimationbreslow
number of observations226
number of events observed55
partial log-likelihood-102.57
time fit was run2021-10-13 13:18:38 UTC
\n", - "
\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
coefexp(coef)se(coef)coef lower 95%coef upper 95%exp(coef) lower 95%exp(coef) upper 95%zp-log2(p)
gender0.712.040.340.041.391.044.002.080.044.72
senior-0.330.720.44-1.200.540.301.72-0.740.461.13
partner-0.390.670.43-1.240.450.291.57-0.910.361.47
deps0.621.850.50-0.361.590.704.931.240.222.21
MultipleLines-0.790.451.09-2.921.340.053.83-0.720.471.09
OnlineSecurity-0.770.461.30-3.311.780.045.93-0.590.560.85
OnlineBackup-0.470.630.95-2.331.390.104.03-0.490.620.68
DeviceProtection-0.410.661.08-2.541.710.085.54-0.380.700.51
TechSupport0.511.661.17-1.782.800.1716.430.440.660.59
PaperlessBilling0.351.420.41-0.451.150.643.160.860.391.35
MonthlyCharges-0.080.920.19-0.460.300.631.35-0.400.690.54
Contract_1-2.190.110.71-3.58-0.790.030.45-3.07<0.0058.88
Contract_2-19.940.003478.68-6838.046798.160.00inf-0.011.000.01
Payment_1-0.870.420.62-2.070.340.131.40-1.410.162.65
Payment_20.461.580.45-0.421.330.663.801.030.311.71
Payment_30.231.260.64-1.021.490.364.430.360.720.48

\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
Concordance0.88
Partial AIC237.14
log-likelihood ratio test106.72 on 16 df
-log2(p) of ll-ratio test48.92
\n", - "
" - ], - "text/latex": [ - "\\begin{tabular}{lrrrrrrrrrr}\n", - "\\toprule\n", - "{} & coef & exp(coef) & se(coef) & coef lower 95\\% & coef upper 95\\% & exp(coef) lower 95\\% & exp(coef) upper 95\\% & z & p & -log2(p) \\\\\n", - "covariate & & & & & & & & & & \\\\\n", - "\\midrule\n", - "gender & 0.71 & 2.04 & 0.34 & 0.04 & 1.39 & 1.04 & 4.00 & 2.08 & 0.04 & 4.72 \\\\\n", - "senior & -0.33 & 0.72 & 0.44 & -1.20 & 0.54 & 0.30 & 1.72 & -0.74 & 0.46 & 1.13 \\\\\n", - "partner & -0.39 & 0.67 & 0.43 & -1.24 & 0.45 & 0.29 & 1.57 & -0.91 & 0.36 & 1.47 \\\\\n", - "deps & 0.62 & 1.85 & 0.50 & -0.36 & 1.59 & 0.70 & 4.93 & 1.24 & 0.22 & 2.21 \\\\\n", - "MultipleLines & -0.79 & 0.45 & 1.09 & -2.92 & 1.34 & 0.05 & 3.83 & -0.72 & 0.47 & 1.09 \\\\\n", - "OnlineSecurity & -0.77 & 0.46 & 1.30 & -3.31 & 1.78 & 0.04 & 5.93 & -0.59 & 0.56 & 0.85 \\\\\n", - "OnlineBackup & -0.47 & 0.63 & 0.95 & -2.33 & 1.39 & 0.10 & 4.03 & -0.49 & 0.62 & 0.68 \\\\\n", - "DeviceProtection & -0.41 & 0.66 & 1.08 & -2.54 & 1.71 & 0.08 & 5.54 & -0.38 & 0.70 & 0.51 \\\\\n", - "TechSupport & 0.51 & 1.66 & 1.17 & -1.78 & 2.80 & 0.17 & 16.43 & 0.44 & 0.66 & 0.59 \\\\\n", - "PaperlessBilling & 0.35 & 1.42 & 0.41 & -0.45 & 1.15 & 0.64 & 3.16 & 0.86 & 0.39 & 1.35 \\\\\n", - "MonthlyCharges & -0.08 & 0.92 & 0.19 & -0.46 & 0.30 & 0.63 & 1.35 & -0.40 & 0.69 & 0.54 \\\\\n", - "Contract\\_1 & -2.19 & 0.11 & 0.71 & -3.58 & -0.79 & 0.03 & 0.45 & -3.07 & 0.00 & 8.88 \\\\\n", - "Contract\\_2 & -19.94 & 0.00 & 3478.68 & -6838.04 & 6798.16 & 0.00 & inf & -0.01 & 1.00 & 0.01 \\\\\n", - "Payment\\_1 & -0.87 & 0.42 & 0.62 & -2.07 & 0.34 & 0.13 & 1.40 & -1.41 & 0.16 & 2.65 \\\\\n", - "Payment\\_2 & 0.46 & 1.58 & 0.45 & -0.42 & 1.33 & 0.66 & 3.80 & 1.03 & 0.31 & 1.71 \\\\\n", - "Payment\\_3 & 0.23 & 1.26 & 0.64 & -1.02 & 1.49 & 0.36 & 4.43 & 0.36 & 0.72 & 0.48 \\\\\n", - "\\bottomrule\n", - "\\end{tabular}\n" - ], - "text/plain": [ - "\n", - " duration col = 'tenure'\n", - " event col = 'labels'\n", - " strata = ['InternetService', 'StreamingMovies', 'StreamingTV', 'PhoneService']\n", - " baseline estimation = breslow\n", - " number of observations = 226\n", - "number of events observed = 55\n", - " partial log-likelihood = -102.57\n", - " time fit was run = 2021-10-13 13:18:38 UTC\n", - "\n", - "---\n", - " coef exp(coef) se(coef) coef lower 95% coef upper 95% exp(coef) lower 95% exp(coef) upper 95%\n", - "covariate \n", - "gender 0.71 2.04 0.34 0.04 1.39 1.04 4.00\n", - "senior -0.33 0.72 0.44 -1.20 0.54 0.30 1.72\n", - "partner -0.39 0.67 0.43 -1.24 0.45 0.29 1.57\n", - "deps 0.62 1.85 0.50 -0.36 1.59 0.70 4.93\n", - "MultipleLines -0.79 0.45 1.09 -2.92 1.34 0.05 3.83\n", - "OnlineSecurity -0.77 0.46 1.30 -3.31 1.78 0.04 5.93\n", - "OnlineBackup -0.47 0.63 0.95 -2.33 1.39 0.10 4.03\n", - "DeviceProtection -0.41 0.66 1.08 -2.54 1.71 0.08 5.54\n", - "TechSupport 0.51 1.66 1.17 -1.78 2.80 0.17 16.43\n", - "PaperlessBilling 0.35 1.42 0.41 -0.45 1.15 0.64 3.16\n", - "MonthlyCharges -0.08 0.92 0.19 -0.46 0.30 0.63 1.35\n", - "Contract_1 -2.19 0.11 0.71 -3.58 -0.79 0.03 0.45\n", - "Contract_2 -19.94 0.00 3478.68 -6838.04 6798.16 0.00 inf\n", - "Payment_1 -0.87 0.42 0.62 -2.07 0.34 0.13 1.40\n", - "Payment_2 0.46 1.58 0.45 -0.42 1.33 0.66 3.80\n", - "Payment_3 0.23 1.26 0.64 -1.02 1.49 0.36 4.43\n", - "\n", - " z p -log2(p)\n", - "covariate \n", - "gender 2.08 0.04 4.72\n", - "senior -0.74 0.46 1.13\n", - "partner -0.91 0.36 1.47\n", - "deps 1.24 0.22 2.21\n", - "MultipleLines -0.72 0.47 1.09\n", - "OnlineSecurity -0.59 0.56 0.85\n", - "OnlineBackup -0.49 0.62 0.68\n", - "DeviceProtection -0.38 0.70 0.51\n", - "TechSupport 0.44 0.66 0.59\n", - "PaperlessBilling 0.86 0.39 1.35\n", - "MonthlyCharges -0.40 0.69 0.54\n", - "Contract_1 -3.07 <0.005 8.88\n", - "Contract_2 -0.01 1.00 0.01\n", - "Payment_1 -1.41 0.16 2.65\n", - "Payment_2 1.03 0.31 1.71\n", - "Payment_3 0.36 0.72 0.48\n", - "---\n", - "Concordance = 0.88\n", - "Partial AIC = 237.14\n", - "log-likelihood ratio test = 106.72 on 16 df\n", - "-log2(p) of ll-ratio test = 48.92" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "model.print_summary()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### **Some potential default analyses of coxph**" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 13, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "model.baseline_survival_.plot()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "* run the following for each of the lines that passes some test (p < 0.005,for example):
\n", - " `model.plot_covariate_groups('Contract_1', values=[0, 1]);`
\n", - " the plot needs to have the strata decoded\n", - " \n", - " In the train_model above, set param `plot_cov_groups=True` and produce the following set of artifacts by selecting only those covariates whose p-values\n", - " are below some threshold `p_value`" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### **Running the function remotely**\n" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "> 2021-10-13 13:18:42,095 [info] Started building image: .mlrun/func-default-coxph-trainer:latest\n", - "\u001b[36mINFO\u001b[0m[0000] Retrieving image manifest mlrun/mlrun:0.7.1 \n", - "\u001b[36mINFO\u001b[0m[0000] Retrieving image manifest mlrun/mlrun:0.7.1 \n", - "\u001b[36mINFO\u001b[0m[0000] Built cross stage deps: map[] \n", - "\u001b[36mINFO\u001b[0m[0000] Retrieving image manifest mlrun/mlrun:0.7.1 \n", - "\u001b[36mINFO\u001b[0m[0000] Retrieving image manifest mlrun/mlrun:0.7.1 \n", - "\u001b[36mINFO\u001b[0m[0001] Executing 0 build triggers \n", - "\u001b[36mINFO\u001b[0m[0001] Unpacking rootfs as cmd RUN pip install lifelines requires it. \n", - "\u001b[36mINFO\u001b[0m[0015] RUN pip install lifelines \n", - "\u001b[36mINFO\u001b[0m[0015] Taking snapshot of full filesystem... \n", - "\u001b[36mINFO\u001b[0m[0026] cmd: /bin/sh \n", - "\u001b[36mINFO\u001b[0m[0026] args: [-c pip install lifelines] \n", - "\u001b[36mINFO\u001b[0m[0026] Running: [/bin/sh -c pip install lifelines] \n", - "Collecting lifelines\n", - " Downloading lifelines-0.26.3-py3-none-any.whl (348 kB)\n", - "Requirement already satisfied: numpy>=1.14.0 in /usr/local/lib/python3.7/site-packages (from lifelines) (1.19.5)\n", - "Collecting autograd-gamma>=0.3\n", - " Downloading autograd-gamma-0.5.0.tar.gz (4.0 kB)\n", - "Collecting autograd>=1.3\n", - " Downloading autograd-1.3.tar.gz (38 kB)\n", - "Requirement already satisfied: matplotlib>=3.0 in /usr/local/lib/python3.7/site-packages (from lifelines) (3.4.3)\n", - "Collecting formulaic<0.3,>=0.2.2\n", - " Downloading formulaic-0.2.4-py3-none-any.whl (55 kB)\n", - "Requirement already satisfied: pandas>=0.23.0 in /usr/local/lib/python3.7/site-packages (from lifelines) (1.3.2)\n", - "Requirement already satisfied: scipy>=1.2.0 in /usr/local/lib/python3.7/site-packages (from lifelines) (1.7.1)\n", - "Requirement already satisfied: future>=0.15.2 in /usr/local/lib/python3.7/site-packages (from autograd>=1.3->lifelines) (0.18.2)\n", - "Requirement already satisfied: cycler>=0.10 in /usr/local/lib/python3.7/site-packages (from matplotlib>=3.0->lifelines) (0.10.0)\n", - "Requirement already satisfied: pillow>=6.2.0 in /usr/local/lib/python3.7/site-packages (from matplotlib>=3.0->lifelines) (8.3.2)\n", - "Requirement already satisfied: pyparsing>=2.2.1 in /usr/local/lib/python3.7/site-packages (from matplotlib>=3.0->lifelines) (2.4.7)\n", - "Requirement already satisfied: kiwisolver>=1.0.1 in /usr/local/lib/python3.7/site-packages (from matplotlib>=3.0->lifelines) (1.3.2)\n", - "Requirement already satisfied: python-dateutil>=2.7 in /usr/local/lib/python3.7/site-packages (from matplotlib>=3.0->lifelines) (2.8.2)\n", - "Collecting interface-meta>=1.2\n", - " Downloading interface_meta-1.2.4-py2.py3-none-any.whl (14 kB)\n", - "Requirement already satisfied: wrapt in /usr/local/lib/python3.7/site-packages (from formulaic<0.3,>=0.2.2->lifelines) (1.12.1)\n", - "Collecting astor\n", - " Downloading astor-0.8.1-py2.py3-none-any.whl (27 kB)\n", - "Requirement already satisfied: pytz>=2017.3 in /usr/local/lib/python3.7/site-packages (from pandas>=0.23.0->lifelines) (2021.1)\n", - "Requirement already satisfied: six in /usr/local/lib/python3.7/site-packages (from cycler>=0.10->matplotlib>=3.0->lifelines) (1.16.0)\n", - "Building wheels for collected packages: autograd-gamma, autograd\n", - " Building wheel for autograd-gamma (setup.py): started\n", - " Building wheel for autograd-gamma (setup.py): finished with status 'done'\n", - " Created wheel for autograd-gamma: filename=autograd_gamma-0.5.0-py3-none-any.whl size=4034 sha256=5211d5dddff0a9102583375dc2c468962b66b424d3fc0c75437b2d2f0d7e2576\n", - " Stored in directory: /tmp/pip-ephem-wheel-cache-vbqa_7jp/wheels/9f/01/ee/1331593abb5725ff7d8c1333aee93a50a1c29d6ddda9665c9f\n", - " Building wheel for autograd (setup.py): started\n", - " Building wheel for autograd (setup.py): finished with status 'done'\n", - " Created wheel for autograd: filename=autograd-1.3-py3-none-any.whl size=47989 sha256=f7fbf41d442f597b0969c5314cbdbda143f3bd2da827efc46f58d03ca6373e55\n", - " Stored in directory: /tmp/pip-ephem-wheel-cache-vbqa_7jp/wheels/ef/32/31/0e87227cd0ca1d99ad51fbe4b54c6fa02afccf7e483d045e04\n", - "Successfully built autograd-gamma autograd\n", - "Installing collected packages: autograd, autograd-gamma, interface-meta, astor, formulaic, lifelines\n", - "Successfully installed astor-0.8.1 autograd-1.3 autograd-gamma-0.5.0 formulaic-0.2.4 interface-meta-1.2.4 lifelines-0.26.3\n", - "WARNING: You are using pip version 20.2.4; however, version 21.3 is available.\n", - "You should consider upgrading via the '/usr/local/bin/python -m pip install --upgrade pip' command.\n", - "\u001b[36mINFO\u001b[0m[0029] Taking snapshot of full filesystem... \n" - ] - }, - { - "data": { - "text/plain": [ - "True" - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "fn.spec.build.commands=['pip install lifelines']\n", - "fn.deploy(with_mlrun=False)" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "> 2021-10-13 13:19:41,275 [info] starting run tasks-survive-trainer uid=8b4ee122120645be9eb29a646ef6e562 DB=http://mlrun-api:8080\n", - "> 2021-10-13 13:19:41,455 [info] Job is running in the background, pod: tasks-survive-trainer-swjkk\n", - "> 2021-10-13 13:19:50,461 [info] run executed, status=completed\n", - "\n", - "A value is trying to be set on a copy of a slice from a DataFrame\n", - "\n", - "See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n", - "Column Contract_2 have very low variance when conditioned on death event present or not. This may harm convergence. This could be a form of 'complete separation'. For example, try the following code:\n", - "\n", - ">>> events = df['labels'].astype(bool)\n", - ">>> print(df.loc[events, 'Contract_2'].var())\n", - ">>> print(df.loc[~events, 'Contract_2'].var())\n", - "\n", - "A very low variance means that the column Contract_2 completely determines whether a subject dies or not. See https://stats.stackexchange.com/questions/11109/how-to-deal-with-perfect-separation-in-logistic-regression.\n", - "\n", - "Newton-Rhaphson convergence completed successfully but norm(delta) is still high, 0.443. This may imply non-unique solutions to the maximum likelihood. Perhaps there is collinearity or complete separation in the dataset?\n", - "\n", - "final state: completed\n" - ] - }, - { - "data": { - "text/html": [ - "\n", - "
\n", - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
projectuiditerstartstatenamelabelsinputsparametersresultsartifacts
default0Oct 13 13:19:47completedtasks-survive-trainer
v3io_user=dani
kind=job
owner=dani
host=tasks-survive-trainer-swjkk
dataset
event_column=labels
strata_cols=['InternetService', 'StreamingMovies', 'StreamingTV', 'PhoneService']
p_value=0.005
encode_cols={'Contract': 'Contract', 'PaymentMethod': 'Payment'}
models_dest=models/cox
file_ext=csv
tenured-test-set
km-timelines
km-survival
km-model
coxhazard-summary
cx-model
\n", - "
\n", - "
\n", - "
\n", - " Title\n", - " ×\n", - "
\n", - " \n", - "
\n", - "
\n" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n" - ] - }, - { - "data": { - "text/html": [ - " > to track results use the .show() or .logs() methods or click here to open in UI" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "> 2021-10-13 13:19:50,645 [info] run executed, status=completed\n" - ] - } - ], - "source": [ - "coxph_run = fn.run(task,\n", - " local=False,\n", - " inputs={\"dataset\" : \"https://s3.wasabisys.com/iguazio/data/function-marketplace-data/coxph_trainer/encoded-data.csv\"})" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "[Back to the top](#Coxph-trainer---Survival-analysis)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.6" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/coxph_trainer/coxph_trainer.py b/coxph_trainer/coxph_trainer.py deleted file mode 100644 index 42c443ad3..000000000 --- a/coxph_trainer/coxph_trainer.py +++ /dev/null @@ -1,201 +0,0 @@ -# Copyright 2019 Iguazio -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# Generated by nuclio.export.NuclioExporter - -import warnings - -warnings.simplefilter(action="ignore", category=FutureWarning) - -from mlrun.mlutils.data import get_sample, get_splits -from mlrun.mlutils.plots import gcf_clear - -from mlrun.execution import MLClientCtx -from mlrun.datastore import DataItem -from mlrun.artifacts import PlotArtifact, TableArtifact - -from cloudpickle import dumps -import pandas as pd -import os - -from lifelines import CoxPHFitter, KaplanMeierFitter - - -def _coxph_log_model( - context, - model, - dataset_key: str = "coxhazard-summary", - models_dest: str = "models", - plot_cov_groups: bool = False, - p_value: float = 0.005, - plot_key: str = "km-cx", - plots_dest: str = "plots", - file_ext="csv", - extra_data: dict = {}, -): - """log a coxph model (and submodel locations) - - :param model: estimated coxph model - :param extra_data: if this model wants to store the locations of submodels - use this - """ - import matplotlib.pyplot as plt - - sumtbl = model.summary - - context.log_dataset(dataset_key, df=sumtbl, index=True, format=file_ext) - - model_bin = dumps(model) - context.log_model( - "cx-model", - body=model_bin, - artifact_path=os.path.join(context.artifact_path, models_dest), - model_file="model.pkl", - ) - if plot_cov_groups: - select_covars = summary[summary.p <= p_value].index.values - for group in select_covars: - axs = model.plot_covariate_groups(group, values=[0, 1]) - for ix, ax in enumerate(axs): - f = ax.get_figure() - context.log_artifact( - PlotArtifact(f"cx-{group}-{ix}", body=plt.gcf()), - local_path=f"{plots_dest}/cx-{group}-{ix}.html", - ) - gcf_clear(plt) - - -def _kaplan_meier_log_model( - context, - model, - time_column: str = "tenure", - dataset_key: str = "km-timelines", - plot_key: str = "km-survival", - plots_dest: str = "plots", - models_dest: str = "models", - file_ext: str = "csv", -): - import matplotlib.pyplot as plt - - o = [] - for obj in model.__dict__.keys(): - if isinstance(model.__dict__[obj], pd.DataFrame): - o.append(model.__dict__[obj]) - df = pd.concat(o, axis=1) - df.index.name = time_column - context.log_dataset(dataset_key, df=df, index=True, format=file_ext) - model.plot() - context.log_artifact( - PlotArtifact(plot_key, body=plt.gcf()), - local_path=f"{plots_dest}/{plot_key}.html", - ) - context.log_model( - "km-model", - body=dumps(model), - model_dir=f"{models_dest}/km", - model_file="model.pkl", - ) - - -def train_model( - context: MLClientCtx, - dataset: DataItem, - event_column: str = "labels", - time_column: str = "tenure", - encode_cols: dict = {}, - strata_cols: list = [], - plot_cov_groups: bool = False, - p_value: float = 0.005, - sample: int = -1, - test_size: float = 0.25, - valid_size: float = 0.75, # (after test removed) - random_state: int = 1, - models_dest: str = "", - plots_dest: str = "", - file_ext: str = "csv", -) -> None: - """train models to predict the timing of events - - Although identical in structure to other training functions, this one - requires generating a 'Y' that represents the age/duration/tenure of - the obervation, designated 'tenure' here, and a binary labels columns that - represents the event of interest, churned/not-churned. - - In addition, there is a strata_cols parameter, representing a list of - stratification (aka grouping) variables. - - :param context: the function context - :param dataset: ("data") name of raw data file - :param event_column: ground-truth (y) labels (considered as events in this model) - :param time_column: age or tenure column - :param encode_cols: dictionary of names and prefixes for columns that are - to hot be encoded. - :param strata_cols: columns used to stratify predictors - :param plot_cov_groups: - :param p_value: (0.005) max p value for coeffcients selected - :param sample: Selects the first n rows, or select a sample - starting from the first. If negative <-1, select - a random sample - :param test_size: (0.25) test set size - :param valid_size: (0.75) Once the test set has been removed the - training set gets this proportion. - :param random_state: (1) sklearn rng seed - :param models_dest: destination subfolder for model artifacts - :param plots_dest: destination subfolder for plot artifacts - :param file_ext: format for test_set_key hold out data - """ - from lifelines.plotting import plot_lifetimes - import matplotlib.pyplot as plt - - models_dest = models_dest or "models" - plots_dest = plots_dest or f"plots/{context.name}" - - raw, tenure, header = get_sample(dataset, sample, time_column) - - if encode_cols: - raw = pd.get_dummies( - raw, - columns=list(encode_cols.keys()), - prefix=list(encode_cols.values()), - drop_first=True, - ) - - (xtrain, ytrain), (xvalid, yvalid), (xtest, ytest) = get_splits( - raw, tenure, 3, test_size, valid_size, random_state - ) - for X in [xtrain, xvalid, xtest]: - drop_cols = X.columns.str.startswith(time_column) - X.drop(X.columns[drop_cols], axis=1, inplace=True) - for Y in [ytrain, yvalid, ytest]: - Y.name = time_column - - context.log_dataset( - "tenured-test-set", - df=pd.concat([xtest, ytest.to_frame()], axis=1), - format=file_ext, - index=False, - ) - - km_model = KaplanMeierFitter().fit(ytrain, xtrain.labels) - _kaplan_meier_log_model(context, km_model, models_dest=models_dest) - - coxdata = pd.concat([xtrain, ytrain.to_frame()], axis=1) - cx_model = CoxPHFitter().fit(coxdata, time_column, event_column, strata=strata_cols) - _coxph_log_model( - context, - cx_model, - models_dest=models_dest, - plot_cov_groups=plot_cov_groups, - extra_data={"km": f"{models_dest}/km"}, - ) diff --git a/coxph_trainer/function.yaml b/coxph_trainer/function.yaml deleted file mode 100644 index 5033b87ba..000000000 --- a/coxph_trainer/function.yaml +++ /dev/null @@ -1,108 +0,0 @@ -kind: job -metadata: - name: coxph-trainer - tag: '' - hash: 65292d47d13eba9327a2b402066d9d76408a7985 - project: '' - labels: - author: yjb - framework: survival - categories: - - model-training - - machine-learning -spec: - command: '' - args: [] - image: mlrun/ml-models - env: [] - default_handler: train_model - entry_points: - train_model: - name: train_model - doc: 'train models to predict the timing of events - - - Although identical in structure to other training functions, this one - - requires generating a ''Y'' that represents the age/duration/tenure of - - the obervation, designated ''tenure'' here, and a binary labels columns that - - represents the event of interest, churned/not-churned. - - - In addition, there is a strata_cols parameter, representing a list of - - stratification (aka grouping) variables.' - parameters: - - name: context - type: MLClientCtx - doc: the function context - default: '' - - name: dataset - type: DataItem - doc: ("data") name of raw data file - default: '' - - name: event_column - type: str - doc: ground-truth (y) labels (considered as events in this model) - default: labels - - name: time_column - type: str - doc: age or tenure column - default: tenure - - name: encode_cols - type: dict - doc: dictionary of names and prefixes for columns that are to hot be encoded. - default: {} - - name: strata_cols - type: list - doc: columns used to stratify predictors - default: [] - - name: plot_cov_groups - type: bool - default: false - - name: p_value - type: float - doc: (0.005) max p value for coeffcients selected - default: 0.005 - - name: sample - type: int - doc: Selects the first n rows, or select a sample starting from the first. - If negative <-1, select a random sample - default: <_ast.USub object at 0x7f3b619b97b8> - - name: test_size - type: float - doc: (0.25) test set size - default: 0.25 - - name: valid_size - type: float - doc: (0.75) Once the test set has been removed the training set gets this - proportion. - default: 0.75 - - name: random_state - type: int - doc: (1) sklearn rng seed - default: 1 - - name: models_dest - type: str - doc: destination subfolder for model artifacts - default: '' - - name: plots_dest - type: str - doc: destination subfolder for plot artifacts - default: '' - - name: file_ext - type: str - doc: format for test_set_key hold out data - default: csv - outputs: - - default: '' - lineno: 97 - description: cox proportional hazards, kaplan meier plots - build: - functionSourceCode: IyBHZW5lcmF0ZWQgYnkgbnVjbGlvLmV4cG9ydC5OdWNsaW9FeHBvcnRlcgoKaW1wb3J0IHdhcm5pbmdzCgp3YXJuaW5ncy5zaW1wbGVmaWx0ZXIoYWN0aW9uPSJpZ25vcmUiLCBjYXRlZ29yeT1GdXR1cmVXYXJuaW5nKQoKZnJvbSBtbHJ1bi5tbHV0aWxzLmRhdGEgaW1wb3J0IGdldF9zYW1wbGUsIGdldF9zcGxpdHMKZnJvbSBtbHJ1bi5tbHV0aWxzLnBsb3RzIGltcG9ydCBnY2ZfY2xlYXIKCmZyb20gbWxydW4uZXhlY3V0aW9uIGltcG9ydCBNTENsaWVudEN0eApmcm9tIG1scnVuLmRhdGFzdG9yZSBpbXBvcnQgRGF0YUl0ZW0KZnJvbSBtbHJ1bi5hcnRpZmFjdHMgaW1wb3J0IFBsb3RBcnRpZmFjdCwgVGFibGVBcnRpZmFjdAoKZnJvbSBjbG91ZHBpY2tsZSBpbXBvcnQgZHVtcHMKaW1wb3J0IHBhbmRhcyBhcyBwZAppbXBvcnQgb3MKCmZyb20gbGlmZWxpbmVzIGltcG9ydCBDb3hQSEZpdHRlciwgS2FwbGFuTWVpZXJGaXR0ZXIKCgpkZWYgX2NveHBoX2xvZ19tb2RlbCgKICAgIGNvbnRleHQsCiAgICBtb2RlbCwKICAgIGRhdGFzZXRfa2V5OiBzdHIgPSAiY294aGF6YXJkLXN1bW1hcnkiLAogICAgbW9kZWxzX2Rlc3Q6IHN0ciA9ICJtb2RlbHMiLAogICAgcGxvdF9jb3ZfZ3JvdXBzOiBib29sID0gRmFsc2UsCiAgICBwX3ZhbHVlOiBmbG9hdCA9IDAuMDA1LAogICAgcGxvdF9rZXk6IHN0ciA9ICJrbS1jeCIsCiAgICBwbG90c19kZXN0OiBzdHIgPSAicGxvdHMiLAogICAgZmlsZV9leHQ9ImNzdiIsCiAgICBleHRyYV9kYXRhOiBkaWN0ID0ge30sCik6CiAgICAiIiJsb2cgYSBjb3hwaCBtb2RlbCAoYW5kIHN1Ym1vZGVsIGxvY2F0aW9ucykKCiAgICA6cGFyYW0gbW9kZWw6ICAgICAgICBlc3RpbWF0ZWQgY294cGggbW9kZWwKICAgIDpwYXJhbSBleHRyYV9kYXRhOiAgIGlmIHRoaXMgbW9kZWwgd2FudHMgdG8gc3RvcmUgdGhlIGxvY2F0aW9ucyBvZiBzdWJtb2RlbHMKICAgICAgICAgICAgICAgICAgICAgICAgIHVzZSB0aGlzCiAgICAiIiIKICAgIGltcG9ydCBtYXRwbG90bGliLnB5cGxvdCBhcyBwbHQKCiAgICBzdW10YmwgPSBtb2RlbC5zdW1tYXJ5CgogICAgY29udGV4dC5sb2dfZGF0YXNldChkYXRhc2V0X2tleSwgZGY9c3VtdGJsLCBpbmRleD1UcnVlLCBmb3JtYXQ9ZmlsZV9leHQpCgogICAgbW9kZWxfYmluID0gZHVtcHMobW9kZWwpCiAgICBjb250ZXh0LmxvZ19tb2RlbCgKICAgICAgICAiY3gtbW9kZWwiLAogICAgICAgIGJvZHk9bW9kZWxfYmluLAogICAgICAgIGFydGlmYWN0X3BhdGg9b3MucGF0aC5qb2luKGNvbnRleHQuYXJ0aWZhY3RfcGF0aCwgbW9kZWxzX2Rlc3QpLAogICAgICAgIG1vZGVsX2ZpbGU9Im1vZGVsLnBrbCIsCiAgICApCiAgICBpZiBwbG90X2Nvdl9ncm91cHM6CiAgICAgICAgc2VsZWN0X2NvdmFycyA9IHN1bW1hcnlbc3VtbWFyeS5wIDw9IHBfdmFsdWVdLmluZGV4LnZhbHVlcwogICAgICAgIGZvciBncm91cCBpbiBzZWxlY3RfY292YXJzOgogICAgICAgICAgICBheHMgPSBtb2RlbC5wbG90X2NvdmFyaWF0ZV9ncm91cHMoZ3JvdXAsIHZhbHVlcz1bMCwgMV0pCiAgICAgICAgICAgIGZvciBpeCwgYXggaW4gZW51bWVyYXRlKGF4cyk6CiAgICAgICAgICAgICAgICBmID0gYXguZ2V0X2ZpZ3VyZSgpCiAgICAgICAgICAgICAgICBjb250ZXh0LmxvZ19hcnRpZmFjdCgKICAgICAgICAgICAgICAgICAgICBQbG90QXJ0aWZhY3QoZiJjeC17Z3JvdXB9LXtpeH0iLCBib2R5PXBsdC5nY2YoKSksCiAgICAgICAgICAgICAgICAgICAgbG9jYWxfcGF0aD1mIntwbG90c19kZXN0fS9jeC17Z3JvdXB9LXtpeH0uaHRtbCIsCiAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICBnY2ZfY2xlYXIocGx0KQoKCmRlZiBfa2FwbGFuX21laWVyX2xvZ19tb2RlbCgKICAgIGNvbnRleHQsCiAgICBtb2RlbCwKICAgIHRpbWVfY29sdW1uOiBzdHIgPSAidGVudXJlIiwKICAgIGRhdGFzZXRfa2V5OiBzdHIgPSAia20tdGltZWxpbmVzIiwKICAgIHBsb3Rfa2V5OiBzdHIgPSAia20tc3Vydml2YWwiLAogICAgcGxvdHNfZGVzdDogc3RyID0gInBsb3RzIiwKICAgIG1vZGVsc19kZXN0OiBzdHIgPSAibW9kZWxzIiwKICAgIGZpbGVfZXh0OiBzdHIgPSAiY3N2IiwKKToKICAgIGltcG9ydCBtYXRwbG90bGliLnB5cGxvdCBhcyBwbHQKCiAgICBvID0gW10KICAgIGZvciBvYmogaW4gbW9kZWwuX19kaWN0X18ua2V5cygpOgogICAgICAgIGlmIGlzaW5zdGFuY2UobW9kZWwuX19kaWN0X19bb2JqXSwgcGQuRGF0YUZyYW1lKToKICAgICAgICAgICAgby5hcHBlbmQobW9kZWwuX19kaWN0X19bb2JqXSkKICAgIGRmID0gcGQuY29uY2F0KG8sIGF4aXM9MSkKICAgIGRmLmluZGV4Lm5hbWUgPSB0aW1lX2NvbHVtbgogICAgY29udGV4dC5sb2dfZGF0YXNldChkYXRhc2V0X2tleSwgZGY9ZGYsIGluZGV4PVRydWUsIGZvcm1hdD1maWxlX2V4dCkKICAgIG1vZGVsLnBsb3QoKQogICAgY29udGV4dC5sb2dfYXJ0aWZhY3QoCiAgICAgICAgUGxvdEFydGlmYWN0KHBsb3Rfa2V5LCBib2R5PXBsdC5nY2YoKSksCiAgICAgICAgbG9jYWxfcGF0aD1mIntwbG90c19kZXN0fS97cGxvdF9rZXl9Lmh0bWwiLAogICAgKQogICAgY29udGV4dC5sb2dfbW9kZWwoCiAgICAgICAgImttLW1vZGVsIiwKICAgICAgICBib2R5PWR1bXBzKG1vZGVsKSwKICAgICAgICBtb2RlbF9kaXI9ZiJ7bW9kZWxzX2Rlc3R9L2ttIiwKICAgICAgICBtb2RlbF9maWxlPSJtb2RlbC5wa2wiLAogICAgKQoKCmRlZiB0cmFpbl9tb2RlbCgKICAgIGNvbnRleHQ6IE1MQ2xpZW50Q3R4LAogICAgZGF0YXNldDogRGF0YUl0ZW0sCiAgICBldmVudF9jb2x1bW46IHN0ciA9ICJsYWJlbHMiLAogICAgdGltZV9jb2x1bW46IHN0ciA9ICJ0ZW51cmUiLAogICAgZW5jb2RlX2NvbHM6IGRpY3QgPSB7fSwKICAgIHN0cmF0YV9jb2xzOiBsaXN0ID0gW10sCiAgICBwbG90X2Nvdl9ncm91cHM6IGJvb2wgPSBGYWxzZSwKICAgIHBfdmFsdWU6IGZsb2F0ID0gMC4wMDUsCiAgICBzYW1wbGU6IGludCA9IC0xLAogICAgdGVzdF9zaXplOiBmbG9hdCA9IDAuMjUsCiAgICB2YWxpZF9zaXplOiBmbG9hdCA9IDAuNzUsICAjIChhZnRlciB0ZXN0IHJlbW92ZWQpCiAgICByYW5kb21fc3RhdGU6IGludCA9IDEsCiAgICBtb2RlbHNfZGVzdDogc3RyID0gIiIsCiAgICBwbG90c19kZXN0OiBzdHIgPSAiIiwKICAgIGZpbGVfZXh0OiBzdHIgPSAiY3N2IiwKKSAtPiBOb25lOgogICAgIiIidHJhaW4gbW9kZWxzIHRvIHByZWRpY3QgdGhlIHRpbWluZyBvZiBldmVudHMKCiAgICBBbHRob3VnaCBpZGVudGljYWwgaW4gc3RydWN0dXJlIHRvIG90aGVyIHRyYWluaW5nIGZ1bmN0aW9ucywgdGhpcyBvbmUKICAgIHJlcXVpcmVzIGdlbmVyYXRpbmcgYSAnWScgdGhhdCByZXByZXNlbnRzIHRoZSBhZ2UvZHVyYXRpb24vdGVudXJlIG9mCiAgICB0aGUgb2JlcnZhdGlvbiwgZGVzaWduYXRlZCAndGVudXJlJyBoZXJlLCBhbmQgYSBiaW5hcnkgbGFiZWxzIGNvbHVtbnMgdGhhdAogICAgcmVwcmVzZW50cyB0aGUgZXZlbnQgb2YgaW50ZXJlc3QsIGNodXJuZWQvbm90LWNodXJuZWQuCgogICAgSW4gYWRkaXRpb24sIHRoZXJlIGlzIGEgc3RyYXRhX2NvbHMgcGFyYW1ldGVyLCByZXByZXNlbnRpbmcgYSBsaXN0IG9mCiAgICBzdHJhdGlmaWNhdGlvbiAoYWthIGdyb3VwaW5nKSB2YXJpYWJsZXMuCgogICAgOnBhcmFtIGNvbnRleHQ6ICAgICAgICAgICB0aGUgZnVuY3Rpb24gY29udGV4dAogICAgOnBhcmFtIGRhdGFzZXQ6ICAgICAgICAgICAoImRhdGEiKSBuYW1lIG9mIHJhdyBkYXRhIGZpbGUKICAgIDpwYXJhbSBldmVudF9jb2x1bW46ICAgICAgZ3JvdW5kLXRydXRoICh5KSBsYWJlbHMgKGNvbnNpZGVyZWQgYXMgZXZlbnRzIGluIHRoaXMgbW9kZWwpCiAgICA6cGFyYW0gdGltZV9jb2x1bW46ICAgICAgIGFnZSBvciB0ZW51cmUgY29sdW1uCiAgICA6cGFyYW0gZW5jb2RlX2NvbHM6ICAgICAgIGRpY3Rpb25hcnkgb2YgbmFtZXMgYW5kIHByZWZpeGVzIGZvciBjb2x1bW5zIHRoYXQgYXJlCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRvIGhvdCBiZSBlbmNvZGVkLgogICAgOnBhcmFtIHN0cmF0YV9jb2xzOiAgICAgICBjb2x1bW5zIHVzZWQgdG8gc3RyYXRpZnkgcHJlZGljdG9ycwogICAgOnBhcmFtIHBsb3RfY292X2dyb3VwczoKICAgIDpwYXJhbSBwX3ZhbHVlOiAgICAgICAgICAgKDAuMDA1KSBtYXggcCB2YWx1ZSBmb3IgY29lZmZjaWVudHMgc2VsZWN0ZWQKICAgIDpwYXJhbSBzYW1wbGU6ICAgICAgICAgICAgU2VsZWN0cyB0aGUgZmlyc3QgbiByb3dzLCBvciBzZWxlY3QgYSBzYW1wbGUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RhcnRpbmcgZnJvbSB0aGUgZmlyc3QuIElmIG5lZ2F0aXZlIDwtMSwgc2VsZWN0CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGEgcmFuZG9tIHNhbXBsZQogICAgOnBhcmFtIHRlc3Rfc2l6ZTogICAgICAgICAoMC4yNSkgdGVzdCBzZXQgc2l6ZQogICAgOnBhcmFtIHZhbGlkX3NpemU6ICAgICAgICAoMC43NSkgT25jZSB0aGUgdGVzdCBzZXQgaGFzIGJlZW4gcmVtb3ZlZCB0aGUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHJhaW5pbmcgc2V0IGdldHMgdGhpcyBwcm9wb3J0aW9uLgogICAgOnBhcmFtIHJhbmRvbV9zdGF0ZTogICAgICAoMSkgc2tsZWFybiBybmcgc2VlZAogICAgOnBhcmFtIG1vZGVsc19kZXN0OiAgICAgICBkZXN0aW5hdGlvbiBzdWJmb2xkZXIgZm9yIG1vZGVsIGFydGlmYWN0cwogICAgOnBhcmFtIHBsb3RzX2Rlc3Q6ICAgICAgICBkZXN0aW5hdGlvbiBzdWJmb2xkZXIgZm9yIHBsb3QgYXJ0aWZhY3RzCiAgICA6cGFyYW0gZmlsZV9leHQ6ICAgICAgICAgIGZvcm1hdCBmb3IgdGVzdF9zZXRfa2V5IGhvbGQgb3V0IGRhdGEKICAgICIiIgogICAgZnJvbSBsaWZlbGluZXMucGxvdHRpbmcgaW1wb3J0IHBsb3RfbGlmZXRpbWVzCiAgICBpbXBvcnQgbWF0cGxvdGxpYi5weXBsb3QgYXMgcGx0CgogICAgbW9kZWxzX2Rlc3QgPSBtb2RlbHNfZGVzdCBvciAibW9kZWxzIgogICAgcGxvdHNfZGVzdCA9IHBsb3RzX2Rlc3Qgb3IgZiJwbG90cy97Y29udGV4dC5uYW1lfSIKCiAgICByYXcsIHRlbnVyZSwgaGVhZGVyID0gZ2V0X3NhbXBsZShkYXRhc2V0LCBzYW1wbGUsIHRpbWVfY29sdW1uKQoKICAgIGlmIGVuY29kZV9jb2xzOgogICAgICAgIHJhdyA9IHBkLmdldF9kdW1taWVzKAogICAgICAgICAgICByYXcsCiAgICAgICAgICAgIGNvbHVtbnM9bGlzdChlbmNvZGVfY29scy5rZXlzKCkpLAogICAgICAgICAgICBwcmVmaXg9bGlzdChlbmNvZGVfY29scy52YWx1ZXMoKSksCiAgICAgICAgICAgIGRyb3BfZmlyc3Q9VHJ1ZSwKICAgICAgICApCgogICAgKHh0cmFpbiwgeXRyYWluKSwgKHh2YWxpZCwgeXZhbGlkKSwgKHh0ZXN0LCB5dGVzdCkgPSBnZXRfc3BsaXRzKAogICAgICAgIHJhdywgdGVudXJlLCAzLCB0ZXN0X3NpemUsIHZhbGlkX3NpemUsIHJhbmRvbV9zdGF0ZQogICAgKQogICAgZm9yIFggaW4gW3h0cmFpbiwgeHZhbGlkLCB4dGVzdF06CiAgICAgICAgZHJvcF9jb2xzID0gWC5jb2x1bW5zLnN0ci5zdGFydHN3aXRoKHRpbWVfY29sdW1uKQogICAgICAgIFguZHJvcChYLmNvbHVtbnNbZHJvcF9jb2xzXSwgYXhpcz0xLCBpbnBsYWNlPVRydWUpCiAgICBmb3IgWSBpbiBbeXRyYWluLCB5dmFsaWQsIHl0ZXN0XToKICAgICAgICBZLm5hbWUgPSB0aW1lX2NvbHVtbgoKICAgIGNvbnRleHQubG9nX2RhdGFzZXQoCiAgICAgICAgInRlbnVyZWQtdGVzdC1zZXQiLAogICAgICAgIGRmPXBkLmNvbmNhdChbeHRlc3QsIHl0ZXN0LnRvX2ZyYW1lKCldLCBheGlzPTEpLAogICAgICAgIGZvcm1hdD1maWxlX2V4dCwKICAgICAgICBpbmRleD1GYWxzZSwKICAgICkKCiAgICBrbV9tb2RlbCA9IEthcGxhbk1laWVyRml0dGVyKCkuZml0KHl0cmFpbiwgeHRyYWluLmxhYmVscykKICAgIF9rYXBsYW5fbWVpZXJfbG9nX21vZGVsKGNvbnRleHQsIGttX21vZGVsLCBtb2RlbHNfZGVzdD1tb2RlbHNfZGVzdCkKCiAgICBjb3hkYXRhID0gcGQuY29uY2F0KFt4dHJhaW4sIHl0cmFpbi50b19mcmFtZSgpXSwgYXhpcz0xKQogICAgY3hfbW9kZWwgPSBDb3hQSEZpdHRlcigpLmZpdChjb3hkYXRhLCB0aW1lX2NvbHVtbiwgZXZlbnRfY29sdW1uLCBzdHJhdGE9c3RyYXRhX2NvbHMpCiAgICBfY294cGhfbG9nX21vZGVsKAogICAgICAgIGNvbnRleHQsCiAgICAgICAgY3hfbW9kZWwsCiAgICAgICAgbW9kZWxzX2Rlc3Q9bW9kZWxzX2Rlc3QsCiAgICAgICAgcGxvdF9jb3ZfZ3JvdXBzPXBsb3RfY292X2dyb3VwcywKICAgICAgICBleHRyYV9kYXRhPXsia20iOiBmInttb2RlbHNfZGVzdH0va20ifSwKICAgICkK - commands: [] - code_origin: https://github.com/daniels290813/functions.git#55a79c32be5d233cc11efcf40cd3edbe309bfdef:/home/kali/functions/coxph_trainer/coxph_trainer.py - affinity: null -verbose: false diff --git a/coxph_trainer/item.yaml b/coxph_trainer/item.yaml deleted file mode 100644 index 2b4cca63d..000000000 --- a/coxph_trainer/item.yaml +++ /dev/null @@ -1,26 +0,0 @@ -apiVersion: v1 -categories: -- model-training -- machine-learning -description: cox proportional hazards, kaplan meier plots -doc: '' -example: coxph_trainer.ipynb -generationDate: 2022-08-28:17-25 -hidden: true -icon: '' -labels: - author: yjb - framework: survival -maintainers: [] -marketplaceType: '' -mlrunVersion: 1.1.0 -name: coxph-trainer -platformVersion: 3.5.0 -spec: - filename: coxph_trainer.py - handler: train_model - image: mlrun/ml-models - kind: job - requirements: [] -url: '' -version: 1.1.0 diff --git a/coxph_trainer/requirements.txt b/coxph_trainer/requirements.txt deleted file mode 100644 index ca8c96f68..000000000 --- a/coxph_trainer/requirements.txt +++ /dev/null @@ -1,6 +0,0 @@ -scikit-learn -seaborn -scikit-plot -pandas -lifelines -matplotlib \ No newline at end of file diff --git a/coxph_trainer/test_coxph_trainer.py b/coxph_trainer/test_coxph_trainer.py deleted file mode 100644 index 8d1344668..000000000 --- a/coxph_trainer/test_coxph_trainer.py +++ /dev/null @@ -1,136 +0,0 @@ -# Copyright 2019 Iguazio -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -from mlrun import get_or_create_ctx, import_function -import os -import json -import pandas as pd -import numpy as np -from collections import defaultdict -from cloudpickle import dumps, load -from sklearn.preprocessing import LabelEncoder -from mlrun.execution import MLClientCtx -from mlrun.datastore import DataItem -import mlrun - -ARTIFACT_PATH = "artifacts" -FUNCTION_PATH = "functions" -MODELS_PATH = "models" -PLOTS_PATH = "plots" -RUNS_PATH = "runs" -SCHEDULES_PATH = "schedules" -DATA_URL = "https://raw.githubusercontent.com/mlrun/demos/0.6.x/customer-churn-prediction/WA_Fn-UseC_-Telco-Customer-Churn.csv" - - -def data_clean( - context: MLClientCtx, - src: DataItem, - file_ext: str = "csv", - models_dest: str = "models/encoders", - cleaned_key: str = "cleaned-data", - encoded_key: str = "encoded-data" -): - df = src.as_df() - - # drop columns - drop_cols_list = ["customerID", "TotalCharges"] - df.drop(drop_cols_list, axis=1, inplace=True) - - # header transformations - old_cols = df.columns - rename_cols_map = { - "SeniorCitizen": "senior", - "Partner": "partner", - "Dependents": "deps", - "Churn": "labels" - } - df.rename(rename_cols_map, axis=1, inplace=True) - - # add drop column to logs: - for col in drop_cols_list: - rename_cols_map.update({col: "_DROPPED_"}) - - # log the op - tp = os.path.join(models_dest, "preproc-column_map.json") - context.log_artifact("preproc-column_map.json", - body=json.dumps(rename_cols_map), - local_path=tp) - df = df.applymap(lambda x: "No" if str(x).startswith("No ") else x) - - # encode numerical type as category bins (ordinal) - bins = [0, 12, 24, 36, 48, 60, np.inf] - labels = [0, 1, 2, 3, 4, 5] - tenure = df.tenure.copy(deep=True) - df["tenure_map"] = pd.cut(df.tenure, bins, labels=False) - tenure_map = dict(zip(bins, labels)) - # save this transformation - tp = os.path.join(models_dest, "preproc-numcat_map.json") - context.log_artifact("preproc-numcat_map.json", - body=bytes(json.dumps(tenure_map).encode("utf-8")), - local_path=tp) - - context.log_dataset(cleaned_key, df=df, format=file_ext, index=False) - fix_cols = ["gender", "partner", "deps", "OnlineSecurity", - "OnlineBackup", "DeviceProtection", "TechSupport", - "StreamingTV", "StreamingMovies", "PhoneService", - "MultipleLines", "PaperlessBilling", "InternetService", - "Contract", "PaymentMethod", "labels"] - - d = defaultdict(LabelEncoder) - df[fix_cols] = df[fix_cols].apply(lambda x: d[x.name].fit_transform(x.astype(str))) - context.log_dataset(encoded_key, df=df, format=file_ext, index=False) - - model_bin = dumps(d) - context.log_model("model", - body=model_bin, - artifact_path=os.path.join(context.artifact_path, - models_dest), - model_file="model.pkl") - - -def test_local_coxph_train(): - # ctx = get_or_create_ctx(name="tasks survive trainer") - # src = mlrun.get_dataitem(DATA_URL) - data_clean_function = mlrun.code_to_function( - filename="test_coxph_trainer.py", - name="data_clean", - kind="job", - image="mlrun/mlrun", - ) - data_clean_run = data_clean_function.run( - handler="data_clean", - inputs={"src": DATA_URL}, - params={ - "cleaned_key": "cleaned-data", - "encoded_key": "encoded-data", - }, - local=True, - artifact_path='./' - ) - - trainer_fn = import_function("function.yaml") - trainer_run = trainer_fn.run( - params={ - "strata_cols": ['InternetService', 'StreamingMovies', 'StreamingTV', 'PhoneService'], - "encode_cols": {"Contract": "Contract", "PaymentMethod": "Payment"}, - "models_dest": 'models/cox' - }, - inputs={"dataset": data_clean_run.artifact("encoded-data").url}, - local=True, - artifact_path='./' - ) - - model = load(open(f"{trainer_run.artifact('km-model').url}model.pkl", "rb")) - ans = model.predict([1, 10, 30, 100, 200]) - assert(sum([abs(x-y) for x, y in zip(list(np.around(ans, 2)), [0.95, 0.85, 0.77, 0.58, 0.58])]) < 0.5) \ No newline at end of file diff --git a/xgb_test/function.yaml b/xgb_test/function.yaml deleted file mode 100644 index 1ba562a9e..000000000 --- a/xgb_test/function.yaml +++ /dev/null @@ -1,63 +0,0 @@ -kind: job -metadata: - name: xgb-test - tag: '' - hash: 3f3368b15f934eba5f6f6b23972da804b6eb88d4 - project: '' - labels: - author: Daniel - framework: xgboost - categories: - - model-testing -spec: - command: '' - args: [] - image: mlrun/mlrun - env: [] - default_handler: xgb_test - entry_points: - xgb_test: - name: xgb_test - doc: 'Test one or more classifier models against held-out dataset - - - Using held-out test features, evaluates the peformance of the estimated model - - - Can be part of a kubeflow pipeline as a test step that is run post EDA and - - training/validation cycles' - parameters: - - name: context - doc: the function context - default: '' - - name: models_path - type: DataItem - doc: model artifact to be tested - default: '' - - name: test_set - type: DataItem - doc: test features and labels - default: '' - - name: label_column - type: str - doc: column name for ground truth labels - default: '' - - name: plots_dest - type: str - doc: dir for test plots - default: plots - - name: default_model - type: str - doc: '''model.pkl'', default model artifact file name' - default: model.pkl - outputs: - - default: '' - lineno: 16 - description: Test one or more classifier models against held-out dataset. - build: - functionSourceCode: IyBHZW5lcmF0ZWQgYnkgbnVjbGlvLmV4cG9ydC5OdWNsaW9FeHBvcnRlcgoKaW1wb3J0IHdhcm5pbmdzCgp3YXJuaW5ncy5zaW1wbGVmaWx0ZXIoYWN0aW9uPSJpZ25vcmUiLCBjYXRlZ29yeT1GdXR1cmVXYXJuaW5nKQoKaW1wb3J0IG9zCmltcG9ydCBwYW5kYXMgYXMgcGQKZnJvbSBtbHJ1bi5kYXRhc3RvcmUgaW1wb3J0IERhdGFJdGVtCmZyb20gbWxydW4uYXJ0aWZhY3RzIGltcG9ydCBnZXRfbW9kZWwKZnJvbSBjbG91ZHBpY2tsZSBpbXBvcnQgbG9hZAoKZnJvbSBtbHJ1bi5tbHV0aWxzLm1vZGVscyBpbXBvcnQgZXZhbF9tb2RlbF92MgoKCmRlZiB4Z2JfdGVzdCgKICAgIGNvbnRleHQsCiAgICBtb2RlbHNfcGF0aDogRGF0YUl0ZW0sCiAgICB0ZXN0X3NldDogRGF0YUl0ZW0sCiAgICBsYWJlbF9jb2x1bW46IHN0ciwKICAgIHBsb3RzX2Rlc3Q6IHN0ciA9ICJwbG90cyIsCiAgICBkZWZhdWx0X21vZGVsOiBzdHIgPSAibW9kZWwucGtsIiwKKSAtPiBOb25lOgogICAgIiIiVGVzdCBvbmUgb3IgbW9yZSBjbGFzc2lmaWVyIG1vZGVscyBhZ2FpbnN0IGhlbGQtb3V0IGRhdGFzZXQKCiAgICBVc2luZyBoZWxkLW91dCB0ZXN0IGZlYXR1cmVzLCBldmFsdWF0ZXMgdGhlIHBlZm9ybWFuY2Ugb2YgdGhlIGVzdGltYXRlZCBtb2RlbAoKICAgIENhbiBiZSBwYXJ0IG9mIGEga3ViZWZsb3cgcGlwZWxpbmUgYXMgYSB0ZXN0IHN0ZXAgdGhhdCBpcyBydW4gcG9zdCBFREEgYW5kCiAgICB0cmFpbmluZy92YWxpZGF0aW9uIGN5Y2xlcwoKICAgIDpwYXJhbSBjb250ZXh0OiAgICAgICAgIHRoZSBmdW5jdGlvbiBjb250ZXh0CiAgICA6cGFyYW0gbW9kZWxzX3BhdGg6ICAgICBtb2RlbCBhcnRpZmFjdCB0byBiZSB0ZXN0ZWQKICAgIDpwYXJhbSB0ZXN0X3NldDogICAgICAgIHRlc3QgZmVhdHVyZXMgYW5kIGxhYmVscwogICAgOnBhcmFtIGxhYmVsX2NvbHVtbjogICAgY29sdW1uIG5hbWUgZm9yIGdyb3VuZCB0cnV0aCBsYWJlbHMKICAgIDpwYXJhbSBwbG90c19kZXN0OiAgICAgIGRpciBmb3IgdGVzdCBwbG90cwogICAgOnBhcmFtIGRlZmF1bHRfbW9kZWw6ICAgJ21vZGVsLnBrbCcsIGRlZmF1bHQgbW9kZWwgYXJ0aWZhY3QgZmlsZSBuYW1lCiAgICAiIiIKICAgIHh0ZXN0ID0gdGVzdF9zZXQuYXNfZGYoKQogICAgeXRlc3QgPSB4dGVzdC5wb3AobGFiZWxfY29sdW1uKQoKICAgIHRyeToKICAgICAgICBtb2RlbF9maWxlLCBtb2RlbF9vYmosIF8gPSBnZXRfbW9kZWwobW9kZWxzX3BhdGgudXJsLCBzdWZmaXg9Ii5wa2wiKQogICAgICAgIG1vZGVsX29iaiA9IGxvYWQob3Blbihtb2RlbF9maWxlLCAicmIiKSkKICAgIGV4Y2VwdCBFeGNlcHRpb24gYXMgYToKICAgICAgICByYWlzZSBFeGNlcHRpb24oIm1vZGVsIGxvY2F0aW9uIGxpa2VseSBtaXNzcGVjaWZpZWQiKQoKICAgIGV2YWxfbWV0cmljcyA9IGV2YWxfbW9kZWxfdjIoY29udGV4dCwgeHRlc3QsIHl0ZXN0LnZhbHVlcywgbW9kZWxfb2JqKQo= - commands: [] - code_origin: https://github.com/daniels290813/functions.git#55a79c32be5d233cc11efcf40cd3edbe309bfdef:/home/kali/functions/xgb_test/xgb_test.py - affinity: null -verbose: false diff --git a/xgb_test/item.yaml b/xgb_test/item.yaml deleted file mode 100644 index cc376e9f7..000000000 --- a/xgb_test/item.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: v1 -categories: -- model-testing -description: Test one or more classifier models against held-out dataset. -doc: '' -example: xgb_test.ipynb -generationDate: 2022-08-28:17-25 -hidden: true -icon: '' -labels: - author: Daniel - framework: xgboost -maintainers: [] -marketplaceType: '' -mlrunVersion: 1.4.1 -name: xgb_test -platformVersion: 3.5.3 -spec: - filename: xgb_test.py - handler: xgb_test - image: mlrun/mlrun - kind: job - requirements: [] -url: '' -version: 1.1.1 diff --git a/xgb_test/requirements.txt b/xgb_test/requirements.txt deleted file mode 100644 index fc5c36f78..000000000 --- a/xgb_test/requirements.txt +++ /dev/null @@ -1,8 +0,0 @@ -pandas -xgboost -cloudpickle -pygit2 -matplotlib -seaborn -scikit-plot -scikit-learn==1.0.2 diff --git a/xgb_test/test_xgb_test.py b/xgb_test/test_xgb_test.py deleted file mode 100644 index a2f92746a..000000000 --- a/xgb_test/test_xgb_test.py +++ /dev/null @@ -1,148 +0,0 @@ -# Copyright 2019 Iguazio -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -from mlrun import code_to_function, import_function -import os -import pandas as pd - - -def get_class_data(): - fn = import_function("hub://gen_class_data") - run = fn.run( - params={ - "n_samples": 10_000, - "m_features": 5, - "k_classes": 2, - "header": None, - "weight": [0.5, 0.5], - "sk_params": {"n_informative": 2}, - "file_ext": "csv", - }, - local=True, - artifact_path="./artifacts/inputs", - ) - return run.status.artifacts[0]['spec']['target_path'] - - -def xgb_trainer(): - data = get_class_data() - fn = code_to_function( - name='xgb_trainer', - filename="../xgb_trainer/xgb_trainer.py", - handler="train_model", - kind="job", - ) - run = fn.run( - params={ - "model_type": "classifier", - "CLASS_tree_method": "hist", - "CLASS_objective": "binary:logistic", - "CLASS_booster": "gbtree", - "FIT_verbose": 0, - "label_column": "labels", - }, - local=True, - inputs={"dataset": data}, - ) - - for artifact in run.status.artifacts: - if artifact['kind'] == 'model': - assert os.path.exists(artifact['spec']['target_path']), "Failed locating model file" # validating model exists - break - return data, artifact['spec']['target_path'] + artifact['spec']['model_file'] - - -def test_xgb_test_code_to_function(): - data, model = xgb_trainer() - fn = code_to_function( - name='test_xgb_test', - filename="../xgb_test/xgb_test.py", - handler="xgb_test", - kind="job", - ) - run = fn.run( - params={ - "label_column": "labels", - "plots_dest": "plots/xgb_test", - }, - local=True, - inputs={ - "test_set": data, - "models_path": model, - } - ) - - assert run.outputs['accuracy'] and run.state() == 'completed' - - -def test_local_xgb_test_import_local_function(): - # importing data preparation function (gen_class_data) locally - fn = import_function("../gen_class_data/function.yaml") - run = fn.run( - params={ - "n_samples": 10_000, - "m_features": 5, - "k_classes": 2, - "header": None, - "weight": [0.5, 0.5], - "sk_params": {"n_informative": 2}, - "file_ext": "csv", - }, - local=True, - artifact_path="./artifacts/inputs", - ) - data = run.status.artifacts[0]['spec']['target_path'] - - # importing model training function (xgb_trainer) locally - fn = import_function("../xgb_trainer/function.yaml") - run = fn.run( - params={ - "model_type": "classifier", - "CLASS_tree_method": "hist", - "CLASS_objective": "binary:logistic", - "CLASS_booster": "gbtree", - "FIT_verbose": 0, - "label_column": "labels", - }, - local=True, - inputs={"dataset": data}, - ) - for artifact in run.status.artifacts: - if artifact['kind'] == 'model': - assert os.path.exists(artifact['spec']['target_path']), "Failed locating model file" # validating model exists - break - - model = artifact['spec']['target_path'] + artifact['spec']['model_file'] - - # importing xgb_test function.yaml and running tests - fn = import_function("function.yaml") - run = fn.run( - params={ - "label_column": "labels", - "plots_dest": "plots/xgb_test", - }, - local=True, - inputs={ - "test_set": data, - "models_path": model, - } - ) - - # tests for gen_class_data - assert data - df = pd.read_csv(data) - assert (True if df["labels"].sum() == 5008 else False) - # tests for xgb_trainer - assert model - # no tests for xgb_test (it is a test already) diff --git a/xgb_test/xgb_test.ipynb b/xgb_test/xgb_test.ipynb deleted file mode 100644 index f404cab3a..000000000 --- a/xgb_test/xgb_test.ipynb +++ /dev/null @@ -1,708 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# **XGBoost test**" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This function handles evaluating XGBoost model performance, test one or more classifier models against held-out dataset
\n", - "Using held-out test features, evaluates the peformance of the estimated model.
\n", - "Can be part of a kubeflow pipeline as a test step that is run post EDA and training/validation cycles.
\n", - "This function is part of the [customer-churn-prediction](https://github.com/mlrun/demos/tree/master/customer-churn-prediction) demo.
\n", - "To see how the model is trained or how the data-set is generated, check out `xgb_trainer` function from the function marketplace repository" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Steps\n", - "1. [Setup function parameters](#Setup-function-parameters)\n", - "2. [Importing the function](#Importing-the-function)\n", - "3. [Running the function locally](#Running-the-function-locally)\n", - "4. [Running the function remotely](#Running-the-function-remotely)" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "import warnings\n", - "warnings.filterwarnings(\"ignore\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### **Setup function parameters** " - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "collapsed": false, - "jupyter": { - "outputs_hidden": false - }, - "pycharm": { - "name": "#%%\n" - } - }, - "outputs": [], - "source": [ - "test_set = \"https://s3.wasabisys.com/iguazio/data/function-marketplace-data/xgb_test/test_set.csv\"\n", - "models_path = \"https://s3.wasabisys.com/iguazio/models/function-marketplace-models/xgb_test/xgb_model.pkl\"" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### **Importing the function**" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "> 2021-10-17 13:24:42,721 [info] loaded project function-marketplace from MLRun DB\n" - ] - } - ], - "source": [ - "import mlrun\n", - "mlrun.set_environment(project='function-marketplace')\n", - "\n", - "fn = mlrun.import_function('hub://xgb_test')\n", - "fn.apply(mlrun.auto_mount())\n", - "fn.image = \"mlrun/ml-models\"" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### **Running the function locally**" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "> 2021-10-17 13:24:43,112 [info] starting run tasks_xgb_test uid=1259c7c9bd0e4b0895be4f5e2fbb65c4 DB=http://mlrun-api:8080\n" - ] - }, - { - "data": { - "text/html": [ - "\n", - "
\n", - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
projectuiditerstartstatenamelabelsinputsparametersresultsartifacts
function-marketplace0Oct 17 13:24:43completedtasks_xgb_test
v3io_user=dani
kind=
owner=dani
host=jupyter-dani-6bfbd76d96-zxx6f
test_set
models_path
label_column=labels
plots_dest=plots/xgb_test
accuracy=0.9632
test-error=0.0368
rocauc=0.984364949478981
brier_score=0.03287091841943238
f1-score=0.9624796084828712
precision_score=0.9744013212221305
recall_score=0.9508460918614021
probability-calibration
confusion-matrix
feature-importances
precision-recall-binary
roc-binary
\n", - "
\n", - "
\n", - "
\n", - " Title\n", - " ×\n", - "
\n", - " \n", - "
\n", - "
\n" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n" - ] - }, - { - "data": { - "text/html": [ - " > to track results use the .show() or .logs() methods or click here to open in UI" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "> 2021-10-17 13:24:48,490 [info] run executed, status=completed\n" - ] - }, - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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y0Jx9e3bFzc3t9gBZl9//TVZsmTh/fffd3JkhtOoqt1uQFPgFHAW+Cie17sCh623P4BKSR2zatWqaiRfYGCgTp8+XaOjo1VV9datWw6P4datW/r++++ri4uL5suXT6dPn67u7u6KpXvyAzcXFxfNkSOH/vTTTw6PMznat2+vBQoU0Hv37jk7lGRxc3OL9313d3d3dmiGnQD7NKHv6oReSOkNcAH+xjIYLRtwCCj30Da1gCet918D9iR1XJMIHs93332nWbJk0f379zu87MjISJ05c6bmz59fs2TJov369dMbN24kus+NGzf05ZdfVkC//PLL2ASWlty8eVOzZcumAwcOdHYoyRZfEoi5GRmTsxJBTeDnOI8/Bj5OZPsngctJHdckAttdv3499os/MjJSDx8+7PAYtm3bpp6engpo3bp19eDBgzbvGx4erl27dlVAe/bsmebOumfOnKmAU5JrSplEkPkklgjseXG4CHAxzuNL1ucS8jawKb4XROQdEdknIvtu3LiRiiFmbO3ataNTp06xk8Q5cnqI8+fP06FDB+rXr8+dO3dYtmwZ27ZtS9ZEddmzZ8fHx4cxY8awYMECGjduzK1bt+wYdfJ4e3tToUIFqlSp4uxQDCNlEsoQKb0BHYDv4zzuBnyTwLb1gRNA/qSOa2oEibt06ZLev39fVVX379+vR48edWj5ISEhOmrUKM2RI4e6ubnp2LFj9e7duyk+7qJFizRbtmxaunRpPXPmTCpEmjInT55UQCdNmuTsUB4LpkaQ6eCkGsEloFicx0WBKw9vJCKewPdAK1W9acd4MryYSeImTZoEwIsvvkj58uUdUraq8uOPP1KmTBk+//xz2rRpw6lTpxg1ahRubm4pPn7Xrl35v//7P27dukWNGjXYsWNHKkT9+Ly9vcmSJQtdu3Z1ahyGkSoSyhApvQGuwD9ACf7XWFz+oW2ewdKjqJatxzU1gkfFPeMeN26c/v333w4tf//+/Vq7dm0FtEqVKrpjxw67lXX27Fl94YUXNGvWrOrt7W23chITFRWlxYoV06ZNmzql/NSQUG8t02so48IZjcWWcmkGnMbSe+g/1uf6An2t978HbgMHrbcEA425mUTwoJ9++kk9PDz00qVLDi/b399fe/furSKiBQoU0Dlz5mhkZKTdy71165bWr19fAR01apTDexT5+fkpoEuXLnVo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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - }, - { - "data": { - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "fn.run(name='tasks_xgb_test',\n", - " params = {\"label_column\" : \"labels\",\n", - " \"plots_dest\" : \"plots/xgb_test\"},\n", - " inputs = {\"test_set\" : test_set,\n", - " \"models_path\" : models_path},\n", - " local=True)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### **Running the function remotely**" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "> 2021-10-17 13:24:48,946 [info] starting run tasks_xgb_test uid=550a773aeb7e4754b4652772d205365a DB=http://mlrun-api:8080\n", - "> 2021-10-17 13:24:49,084 [info] Job is running in the background, pod: tasks-xgb-test-gj7q4\n", - "> 2021-10-17 13:24:59,214 [info] run executed, status=completed\n", - "final state: completed\n" - ] - }, - { - "data": { - "text/html": [ - "\n", - "
\n", - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
projectuiditerstartstatenamelabelsinputsparametersresultsartifacts
function-marketplace0Oct 17 13:24:55completedtasks_xgb_test
v3io_user=dani
kind=job
owner=dani
host=tasks-xgb-test-gj7q4
test_set
models_path
label_column=labels
plots_dest=plots/xgb_test
accuracy=0.9632
test-error=0.0368
rocauc=0.984364949478981
brier_score=0.03287091841943238
f1-score=0.9624796084828712
precision_score=0.9744013212221305
recall_score=0.9508460918614021
probability-calibration
confusion-matrix
feature-importances
precision-recall-binary
roc-binary
\n", - "
\n", - "
\n", - "
\n", - " Title\n", - " ×\n", - "
\n", - " \n", - "
\n", - "
\n" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n" - ] - }, - { - "data": { - "text/html": [ - " > to track results use the .show() or .logs() methods or click here to open in UI" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "> 2021-10-17 13:25:08,339 [info] run executed, status=completed\n" - ] - }, - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "fn.deploy(with_mlrun=False, # mlrun is included in our image (mlrun/ml-models) therefore no mlrun installation is needed.\n", - " skip_deployed=True) # because no new packages or upgrade is required, we can use the original image and not build another one.\n", - "\n", - "fn.run(name='tasks_xgb_test',\n", - " params = {\"label_column\" : \"labels\",\n", - " \"plots_dest\" : \"plots/xgb_test\"},\n", - " inputs = {\"test_set\" : test_set,\n", - " \"models_path\" : models_path},\n", - " local=False)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "[Back to the top](#XGBoost-test)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.6" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/xgb_test/xgb_test.py b/xgb_test/xgb_test.py deleted file mode 100644 index 8ad3a6a1c..000000000 --- a/xgb_test/xgb_test.py +++ /dev/null @@ -1,61 +0,0 @@ -# Copyright 2019 Iguazio -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# Generated by nuclio.export.NuclioExporter - -import warnings - -warnings.simplefilter(action="ignore", category=FutureWarning) - -import os -import pandas as pd -from mlrun.datastore import DataItem -from mlrun.artifacts import get_model -from cloudpickle import load - -from mlrun.mlutils.models import eval_model_v2 - - -def xgb_test( - context, - models_path: DataItem, - test_set: DataItem, - label_column: str, - plots_dest: str = "plots", - default_model: str = "model.pkl", -) -> None: - """Test one or more classifier models against held-out dataset - - Using held-out test features, evaluates the peformance of the estimated model - - Can be part of a kubeflow pipeline as a test step that is run post EDA and - training/validation cycles - - :param context: the function context - :param models_path: model artifact to be tested - :param test_set: test features and labels - :param label_column: column name for ground truth labels - :param plots_dest: dir for test plots - :param default_model: 'model.pkl', default model artifact file name - """ - xtest = test_set.as_df() - ytest = xtest.pop(label_column) - - try: - model_file, model_obj, _ = get_model(models_path.url, suffix=".pkl") - model_obj = load(open(model_file, "rb")) - except Exception as a: - raise Exception("model location likely misspecified") - - eval_metrics = eval_model_v2(context, xtest, ytest.values, model_obj) diff --git a/xgb_trainer/function.yaml b/xgb_trainer/function.yaml deleted file mode 100644 index 425e61c54..000000000 --- a/xgb_trainer/function.yaml +++ /dev/null @@ -1,102 +0,0 @@ -kind: job -metadata: - name: xgb-trainer - tag: '' - hash: 74f26135df3322a88554136c4c5dbe8d95a5fadc - project: '' - labels: - author: Daniel - categories: - - model-training -spec: - command: '' - args: [] - image: mlrun/mlrun - env: [] - default_handler: train_model - entry_points: - train_model: - name: train_model - doc: 'train an xgboost model. - - - Note on imabalanced data: the `imbal_vec` parameter represents the measured - - class representations in the sample and can be used as a first step in tuning - - an XGBoost model. This isn''t a hyperparamter, merely an estimate that should - - be set as ''constant'' throughout tuning process.' - parameters: - - name: context - type: MLClientCtx - doc: the function context - default: '' - - name: model_type - type: str - doc: the model type to train, "classifier", "regressor"... - default: '' - - name: dataset - type: Union[DataItem, DataFrame] - doc: ("data") name of raw data file - default: '' - - name: label_column - type: str - doc: ground-truth (y) labels - default: labels - - name: encode_cols - type: dict - doc: dictionary of names and prefixes for columns that are to hot be encoded. - default: {} - - name: sample - type: int - doc: Selects the first n rows, or select a sample starting from the first. - If negative <-1, select a random sample - default: <_ast.USub object at 0x7f66a8fbc7b8> - - name: imbal_vec - doc: ([]) vector of class weights seen in sample - default: [] - - name: test_size - type: float - doc: (0.05) test set size - default: 0.25 - - name: valid_size - type: float - doc: (0.75) Once the test set has been removed the training set gets this - proportion. - default: 0.75 - - name: random_state - type: int - doc: (1) sklearn rng seed - default: 1 - - name: models_dest - type: str - doc: destination subfolder for model artifacts - default: models - - name: plots_dest - type: str - doc: destination subfolder for plot artifacts - default: plots - - name: eval_metrics - type: list - doc: (["error", "auc"]) learning curve metrics - default: - - error - - auc - - name: file_ext - type: str - doc: format for test_set_key hold out data - default: parquet - - name: test_set - type: str - default: test_set - outputs: - - default: '' - lineno: 57 - description: train multiple model types using xgboost. - build: - functionSourceCode: IyBHZW5lcmF0ZWQgYnkgbnVjbGlvLmV4cG9ydC5OdWNsaW9FeHBvcnRlcgoKaW1wb3J0IHdhcm5pbmdzCgp3YXJuaW5ncy5zaW1wbGVmaWx0ZXIoYWN0aW9uPSJpZ25vcmUiLCBjYXRlZ29yeT1GdXR1cmVXYXJuaW5nKQoKZnJvbSBtbHJ1bi5tbHV0aWxzLmRhdGEgaW1wb3J0IGdldF9zYW1wbGUsIGdldF9zcGxpdHMKZnJvbSBtbHJ1bi5tbHV0aWxzLm1vZGVscyBpbXBvcnQgZ2VuX3NrbGVhcm5fbW9kZWwsIGV2YWxfbW9kZWxfdjIKZnJvbSBtbHJ1bi51dGlscy5oZWxwZXJzIGltcG9ydCBjcmVhdGVfY2xhc3MKCmZyb20gbWxydW4uZXhlY3V0aW9uIGltcG9ydCBNTENsaWVudEN0eApmcm9tIG1scnVuLmRhdGFzdG9yZSBpbXBvcnQgRGF0YUl0ZW0KCmZyb20gY2xvdWRwaWNrbGUgaW1wb3J0IGR1bXBzCmltcG9ydCBwYW5kYXMgYXMgcGQKaW1wb3J0IG9zCmZyb20gdHlwaW5nIGltcG9ydCBVbmlvbgoKCmRlZiBfZ2VuX3hnYl9tb2RlbChtb2RlbF90eXBlOiBzdHIsIHhnYl9wYXJhbXM6IGRpY3QpOgogICAgIiIiZ2VuZXJhdGUgYW4geGdib29zdCBtb2RlbAoKICAgIE11bHRpcGxlIG1vZGVsIHR5cGVzIHRoYXQgY2FuIGJlIGVzdGltYXRlZCB1c2luZwogICAgdGhlIFhHQm9vc3QgU2Npa2l0LUxlYXJuIEFQSS4KCiAgICBJbnB1dCBjYW4gZWl0aGVyIGJlIGEgcHJlZGVmaW5lZCBqc29uIG1vZGVsIGNvbmZpZ3VyYXRpb24gb3Igb25lCiAgICBvZiB0aGUgZml2ZSB4Z2Jvb3N0IG1vZGVsIHR5cGVzOiAiY2xhc3NpZmllciIsICJyZWdyZXNzb3IiLCAicmFua2VyIiwKICAgICJyZl9jbGFzc2lmaWVyIiwgb3IgInJmX3JlZ3Jlc3NvciIuCgogICAgSW4gZWl0aGVyIGNhc2Ugb25lIGNhbiBwYXNzIGluIGEgcGFyYW1zIGRpY3QgdG8gbW9kaWZ5IGRlZmF1bHRzIHZhbHVlcy4KCiAgICBCYXNlZCBvbiBgbWx1dGlscy5tb2RlbHMuZ2VuX3NrbGVhcm5fbW9kZWxgLCBzZWUgdGhlIGZ1bmN0aW9uCiAgICBgc2tsZWFybl9jbGFzc2lmaWVyYCBpbiB0aGlzIHJlcG9zaXRvcnkuCgogICAgOnBhcmFtIG1vZGVsX3R5cGU6IG9uZSBvZiAiY2xhc3NpZmllciIsICJyZWdyZXNzb3IiLAogICAgICAgICAgICAgICAgICAgICAgICJyYW5rZXIiLCAicmZfY2xhc3NpZmllciIsIG9yCiAgICAgICAgICAgICAgICAgICAgICAicmZfcmVncmVzc29yIgogICAgOnBhcmFtIHhnYl9wYXJhbXM6IGNsYXNzIGluaXQgcGFyYW1ldGVycwogICAgIiIiCiAgICBtdHlwZXMgPSB7CiAgICAgICAgImNsYXNzaWZpZXIiOiAieGdib29zdC5YR0JDbGFzc2lmaWVyIiwKICAgICAgICAicmVncmVzc29yIjogInhnYm9vc3QuWEdCUmVncmVzc29yIiwKICAgICAgICAicmFua2VyIjogInhnYm9vc3QuWEdCUmFua2VyIiwKICAgICAgICAicmZfY2xhc3NpZmllciI6ICJ4Z2Jvb3N0LlhHQlJGQ2xhc3NpZmllciIsCiAgICAgICAgInJmX3JlZ3Jlc3NvciI6ICJ4Z2Jvb3N0LlhHQlJGUmVncmVzc29yIiwKICAgIH0KICAgIGlmIG1vZGVsX3R5cGUuZW5kc3dpdGgoImpzb24iKToKICAgICAgICBtb2RlbF9jb25maWcgPSBtb2RlbF90eXBlCiAgICBlbGlmIG1vZGVsX3R5cGUgaW4gbXR5cGVzLmtleXMoKToKICAgICAgICBtb2RlbF9jb25maWcgPSBtdHlwZXNbbW9kZWxfdHlwZV0KICAgIGVsc2U6CiAgICAgICAgcmFpc2UgRXhjZXB0aW9uKCJ1bnJlY29nbml6ZWQgbW9kZWwgdHlwZSwgc2VlIGhlbHAgZG9jdW1lbnRhdGlvbiIpCgogICAgcmV0dXJuIGdlbl9za2xlYXJuX21vZGVsKG1vZGVsX2NvbmZpZywgeGdiX3BhcmFtcykKCgpkZWYgdHJhaW5fbW9kZWwoCiAgICBjb250ZXh0OiBNTENsaWVudEN0eCwKICAgIG1vZGVsX3R5cGU6IHN0ciwKICAgIGRhdGFzZXQ6IFVuaW9uW0RhdGFJdGVtLCBwZC5jb3JlLmZyYW1lLkRhdGFGcmFtZV0sCiAgICBsYWJlbF9jb2x1bW46IHN0ciA9ICJsYWJlbHMiLAogICAgZW5jb2RlX2NvbHM6IGRpY3QgPSB7fSwKICAgIHNhbXBsZTogaW50ID0gLTEsCiAgICBpbWJhbF92ZWM9W10sCiAgICB0ZXN0X3NpemU6IGZsb2F0ID0gMC4yNSwKICAgIHZhbGlkX3NpemU6IGZsb2F0ID0gMC43NSwKICAgIHJhbmRvbV9zdGF0ZTogaW50ID0gMSwKICAgIG1vZGVsc19kZXN0OiBzdHIgPSAibW9kZWxzIiwKICAgIHBsb3RzX2Rlc3Q6IHN0ciA9ICJwbG90cyIsCiAgICBldmFsX21ldHJpY3M6IGxpc3QgPSBbImVycm9yIiwgImF1YyJdLAogICAgZmlsZV9leHQ6IHN0ciA9ICJwYXJxdWV0IiwKICAgIHRlc3Rfc2V0OiBzdHIgPSAidGVzdF9zZXQiLAopIC0+IE5vbmU6CiAgICAiIiJ0cmFpbiBhbiB4Z2Jvb3N0IG1vZGVsLgoKICAgIE5vdGUgb24gaW1hYmFsYW5jZWQgZGF0YTogIHRoZSBgaW1iYWxfdmVjYCBwYXJhbWV0ZXIgcmVwcmVzZW50cyB0aGUgbWVhc3VyZWQKICAgIGNsYXNzIHJlcHJlc2VudGF0aW9ucyBpbiB0aGUgc2FtcGxlIGFuZCBjYW4gYmUgdXNlZCBhcyBhIGZpcnN0IHN0ZXAgaW4gdHVuaW5nCiAgICBhbiBYR0Jvb3N0IG1vZGVsLiAgVGhpcyBpc24ndCBhIGh5cGVycGFyYW10ZXIsIG1lcmVseSBhbiBlc3RpbWF0ZSB0aGF0IHNob3VsZAogICAgYmUgc2V0IGFzICdjb25zdGFudCcgdGhyb3VnaG91dCB0dW5pbmcgcHJvY2Vzcy4KCiAgICA6cGFyYW0gY29udGV4dDogICAgICAgICAgIHRoZSBmdW5jdGlvbiBjb250ZXh0CiAgICA6cGFyYW0gbW9kZWxfdHlwZTogICAgICAgIHRoZSBtb2RlbCB0eXBlIHRvIHRyYWluLCAiY2xhc3NpZmllciIsICJyZWdyZXNzb3IiLi4uCiAgICA6cGFyYW0gZGF0YXNldDogICAgICAgICAgICgiZGF0YSIpIG5hbWUgb2YgcmF3IGRhdGEgZmlsZQogICAgOnBhcmFtIGxhYmVsX2NvbHVtbjogICAgICBncm91bmQtdHJ1dGggKHkpIGxhYmVscwogICAgOnBhcmFtIGVuY29kZV9jb2xzOiAgICAgICBkaWN0aW9uYXJ5IG9mIG5hbWVzIGFuZCBwcmVmaXhlcyBmb3IgY29sdW1ucyB0aGF0IGFyZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0byBob3QgYmUgZW5jb2RlZC4KICAgIDpwYXJhbSBzYW1wbGU6ICAgICAgICAgICAgU2VsZWN0cyB0aGUgZmlyc3QgbiByb3dzLCBvciBzZWxlY3QgYSBzYW1wbGUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RhcnRpbmcgZnJvbSB0aGUgZmlyc3QuIElmIG5lZ2F0aXZlIDwtMSwgc2VsZWN0CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGEgcmFuZG9tIHNhbXBsZQogICAgOnBhcmFtIGltYmFsX3ZlYzogICAgICAgICAoW10pIHZlY3RvciBvZiBjbGFzcyB3ZWlnaHRzIHNlZW4gaW4gc2FtcGxlCiAgICA6cGFyYW0gdGVzdF9zaXplOiAgICAgICAgICgwLjA1KSB0ZXN0IHNldCBzaXplCiAgICA6cGFyYW0gdmFsaWRfc2l6ZTogICAgICAgICgwLjc1KSBPbmNlIHRoZSB0ZXN0IHNldCBoYXMgYmVlbiByZW1vdmVkIHRoZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0cmFpbmluZyBzZXQgZ2V0cyB0aGlzIHByb3BvcnRpb24uCiAgICA6cGFyYW0gcmFuZG9tX3N0YXRlOiAgICAgICgxKSBza2xlYXJuIHJuZyBzZWVkCiAgICA6cGFyYW0gbW9kZWxzX2Rlc3Q6ICAgICAgIGRlc3RpbmF0aW9uIHN1YmZvbGRlciBmb3IgbW9kZWwgYXJ0aWZhY3RzCiAgICA6cGFyYW0gcGxvdHNfZGVzdDogICAgICAgIGRlc3RpbmF0aW9uIHN1YmZvbGRlciBmb3IgcGxvdCBhcnRpZmFjdHMKICAgIDpwYXJhbSBldmFsX21ldHJpY3M6ICAgICAgKFsiZXJyb3IiLCAiYXVjIl0pIGxlYXJuaW5nIGN1cnZlIG1ldHJpY3MKICAgIDpwYXJhbSBmaWxlX2V4dDogICAgICAgICAgZm9ybWF0IGZvciB0ZXN0X3NldF9rZXkgaG9sZCBvdXQgZGF0YQogICAgOnBhcmFtIHRlc3Qtc2V0OiAgICAgICAgICAodGVzdF9zZXQpIGtleSBvZiBoZWxkIG91dCBkYXRhIGluIGFydGlmYWN0IHN0b3JlCiAgICAiIiIKICAgIG1vZGVsc19kZXN0ID0gbW9kZWxzX2Rlc3Qgb3IgIm1vZGVscyIKICAgIHBsb3RzX2Rlc3QgPSBwbG90c19kZXN0IG9yIGYicGxvdHMve2NvbnRleHQubmFtZX0iCgogICAgcmF3LCBsYWJlbHMsIGhlYWRlciA9IGdldF9zYW1wbGUoZGF0YXNldCwgc2FtcGxlLCBsYWJlbF9jb2x1bW4pCgogICAgaWYgZW5jb2RlX2NvbHM6CiAgICAgICAgcmF3ID0gcGQuZ2V0X2R1bW1pZXMoCiAgICAgICAgICAgIHJhdywKICAgICAgICAgICAgY29sdW1ucz1saXN0KGVuY29kZV9jb2xzLmtleXMoKSksCiAgICAgICAgICAgIHByZWZpeD1saXN0KGVuY29kZV9jb2xzLnZhbHVlcygpKSwKICAgICAgICAgICAgZHJvcF9maXJzdD1UcnVlLAogICAgICAgICkKCiAgICAoeHRyYWluLCB5dHJhaW4pLCAoeHZhbGlkLCB5dmFsaWQpLCAoeHRlc3QsIHl0ZXN0KSA9IGdldF9zcGxpdHMoCiAgICAgICAgcmF3LCBsYWJlbHMsIDMsIHRlc3Rfc2l6ZSwgdmFsaWRfc2l6ZSwgcmFuZG9tX3N0YXRlCiAgICApCgogICAgY29udGV4dC5sb2dfZGF0YXNldCgKICAgICAgICB0ZXN0X3NldCwgZGY9cGQuY29uY2F0KFt4dGVzdCwgeXRlc3RdLCBheGlzPTEpLCBmb3JtYXQ9ZmlsZV9leHQsIGluZGV4PUZhbHNlCiAgICApCgogICAgbW9kZWxfY29uZmlnID0gX2dlbl94Z2JfbW9kZWwobW9kZWxfdHlwZSwgY29udGV4dC5wYXJhbWV0ZXJzLml0ZW1zKCkpCgogICAgWEdCQm9vc3RDbGFzcyA9IGNyZWF0ZV9jbGFzcyhtb2RlbF9jb25maWdbIk1FVEEiXVsiY2xhc3MiXSkKICAgIG1vZGVsID0gWEdCQm9vc3RDbGFzcygqKm1vZGVsX2NvbmZpZ1siQ0xBU1MiXSkKCiAgICBtb2RlbF9jb25maWdbIkZJVCJdLnVwZGF0ZSgKICAgICAgICB7CiAgICAgICAgICAgICJYIjogeHRyYWluLAogICAgICAgICAgICAieSI6IHl0cmFpbi52YWx1ZXMsCiAgICAgICAgICAgICJldmFsX3NldCI6IFsoeHRyYWluLCB5dHJhaW4pLCAoeHZhbGlkLCB5dmFsaWQpXSwKICAgICAgICAgICAgImV2YWxfbWV0cmljIjogZXZhbF9tZXRyaWNzLAogICAgICAgIH0KICAgICkKCiAgICBtb2RlbC5maXQoKiptb2RlbF9jb25maWdbIkZJVCJdKQoKICAgIGV2YWxfbWV0cmljcyA9IGV2YWxfbW9kZWxfdjIoY29udGV4dCwgeHZhbGlkLCB5dmFsaWQsIG1vZGVsKQoKICAgIG1vZGVsX2JpbiA9IGR1bXBzKG1vZGVsKQogICAgY29udGV4dC5sb2dfbW9kZWwoCiAgICAgICAgIm1vZGVsIiwKICAgICAgICBib2R5PW1vZGVsX2JpbiwKICAgICAgICBhcnRpZmFjdF9wYXRoPW9zLnBhdGguam9pbihjb250ZXh0LmFydGlmYWN0X3BhdGgsIG1vZGVsc19kZXN0KSwKICAgICAgICBtb2RlbF9maWxlPSJtb2RlbC5wa2wiLAogICAgKQo= - commands: [] - code_origin: https://github.com/daniels290813/functions.git#55a79c32be5d233cc11efcf40cd3edbe309bfdef:/home/kali/functions/xgb_trainer/xgb_trainer.py - affinity: null -verbose: false diff --git a/xgb_trainer/item.yaml b/xgb_trainer/item.yaml deleted file mode 100644 index 5c910a0c8..000000000 --- a/xgb_trainer/item.yaml +++ /dev/null @@ -1,24 +0,0 @@ -apiVersion: v1 -categories: -- model-training -description: train multiple model types using xgboost. -doc: '' -example: xgb_trainer.ipynb -generationDate: 2022-08-28:17-25 -hidden: true -icon: '' -labels: - author: Daniel -maintainers: [] -marketplaceType: '' -mlrunVersion: 1.4.1 -name: xgb_trainer -platformVersion: 3.5.4 -spec: - filename: xgb_trainer.py - handler: train_model - image: mlrun/mlrun - kind: job - requirements: [] -url: '' -version: 1.1.1 diff --git a/xgb_trainer/requirements.txt b/xgb_trainer/requirements.txt deleted file mode 100644 index 644bcc710..000000000 --- a/xgb_trainer/requirements.txt +++ /dev/null @@ -1,8 +0,0 @@ -pandas -xgboost -cloudpickle -pygit2 -scikit-learn==1.0.2 -matplotlib -seaborn -scikit-plot diff --git a/xgb_trainer/test_xgb_trainer.py b/xgb_trainer/test_xgb_trainer.py deleted file mode 100644 index e9119e307..000000000 --- a/xgb_trainer/test_xgb_trainer.py +++ /dev/null @@ -1,50 +0,0 @@ -# Copyright 2019 Iguazio -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -import mlrun -import os - - -def get_class_data(): - fn = mlrun.import_function('../gen_class_data/function.yaml') - run = fn.run(params={'key': 'classifier-data', - 'n_samples': 10_000, - 'm_features': 5, - 'k_classes': 2, - 'header': None, - 'weight': [0.5, 0.5], - 'sk_params': {'n_informative': 2}, - 'file_ext': 'csv'}, local=True, artifact_path="./artifacts") - - return run - - -def test_local_xgb_trainer_import_function(): - # running data preparation function locally - gen_data_run = get_class_data() - - fn = mlrun.import_function('function.yaml') - run = fn.run(params={'model_type': 'classifier', - 'CLASS_tree_method': 'hist', - 'CLASS_objective': 'binary:logistic', - 'CLASS_booster': 'gbtree', - 'FIT_verbose': 0, - 'label_column': 'labels'}, - local=True, inputs={'dataset': gen_data_run.status.artifacts[0]['spec']['target_path']}) # only one dataset artifact created - - for artifact in run.status.artifacts: - if artifact['kind'] == 'model': - assert os.path.exists(artifact['spec']['target_path']) # validating model exists - return - assert False, "Model artifact is unavailable or miss-predicted" diff --git a/xgb_trainer/xgb_trainer.ipynb b/xgb_trainer/xgb_trainer.ipynb deleted file mode 100644 index 444d40400..000000000 --- a/xgb_trainer/xgb_trainer.ipynb +++ /dev/null @@ -1,1013 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# XGBoost trainer" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This notebook function handles training and logging of xgboost models **only**, exposing both the sklearn and low level api\"s.
\n", - "More information about XGBoost - [here](https://en.wikipedia.org/wiki/XGBoost)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Multiple model types that can be estimated using the XGBoost Scikit-Learn API.
\n", - "Input can either be a predefined json model configuration or one\n", - "of the five xgboost model types.
\n", - "In either case one can pass in a params dict to modify defaults values.
\n", - "Based on `mlutils.models.gen_sklearn_model`, see the function\n", - "`sklearn_classifier` in the function-marketplace repository.
\n", - "> **param model_type:**\n", - " one of \"classifier\", \"regressor\",\n", - " \"ranker\", \"rf_classifier\", or\n", - " \"rf_regressor\"
\n", - "> **param xgb_params:** class init parameters" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Steps\n", - "1. [Data Exploration](#Data-Exploration)\n", - "2. [Importing the function](#Importing-the-function)\n", - "3. [Setup XGBoost parameters](#Setup-XGBoost-parameters)\n", - "4. [Running the function locally](#Running-the-function-locally)\n", - "5. [Getting the model](#Getting-the-model)\n", - "6. [Some plotting](#Some-plotting)\n", - "7. [Running the function remotely](#Running-the-function-remotely)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### **Data Exploration**" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "To generate the dataset we used the \"gen_class_data\" function from the hub, \n", - "which wraps scikit-learn's [make_classification](https://scikit-learn.org/stable/modules/generated/sklearn.datasets.make_classification.html#sklearn-datasets-make-classification).
\n", - "See the link for a description of all parameters." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "import warnings\n", - "warnings.filterwarnings(\"ignore\")" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "# make sure proper xgboost version installed, uncomment to install\n", - "# !pip install xgboost==1.3.1" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Data set containing 10000 instances, with 2 labels.\n", - "Number of instances labeled 1 : 5008\n", - "Number of instances labeled 0 : 4992\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
feat_0feat_1feat_2feat_3feat_4labels
0-0.265115-1.9322600.303992-1.863833-1.0456351
1-3.135479-2.8355481.338381-1.385303-2.2764560
2-1.519005-1.8075490.697304-1.1188601.1049000
3-0.632087-0.3456590.244329-0.0460660.4472800
4-1.405883-1.7460450.653617-1.110985-1.6754660
\n", - "
" - ], - "text/plain": [ - " feat_0 feat_1 feat_2 feat_3 feat_4 labels\n", - "0 -0.265115 -1.932260 0.303992 -1.863833 -1.045635 1\n", - "1 -3.135479 -2.835548 1.338381 -1.385303 -2.276456 0\n", - "2 -1.519005 -1.807549 0.697304 -1.118860 1.104900 0\n", - "3 -0.632087 -0.345659 0.244329 -0.046066 0.447280 0\n", - "4 -1.405883 -1.746045 0.653617 -1.110985 -1.675466 0" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Getting the data from wasabi\n", - "import pandas as pd\n", - "\n", - "df = pd.read_csv('https://s3.wasabisys.com/iguazio/data/function-marketplace-data/xgb_trainer/classifier-data.csv')\n", - "print(f'Data set containing {df.shape[0]} instances, with {len(df[\"labels\"].unique())} labels.')\n", - "\n", - "print(f\"Number of instances labeled {df['labels'].unique()[0]} : {df.groupby('labels').count()[df.columns[0]][df['labels'].unique()[0]]}\")\n", - "print(f\"Number of instances labeled {df['labels'].unique()[1]} : {df.groupby('labels').count()[df.columns[0]][df['labels'].unique()[1]]}\")\n", - "\n", - "df.head()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### **Importing the function**" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "> 2021-10-13 10:10:21,588 [info] loaded project function-marketplace from MLRun DB\n" - ] - }, - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "import mlrun\n", - "mlrun.set_environment(project='function-marketplace')\n", - "# If GPU is available - set to True\n", - "GPU = False\n", - "\n", - "\n", - "fn = mlrun.import_function(\"hub://xgb_trainer\")\n", - "fn.image = \"mlrun/ml-models\" if not GPU else \"mlrun/ml-models-gpu\"\n", - "fn.apply(mlrun.auto_mount())" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### **Setup XGBoost parameters**" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "task_params = {\"model_type\": \"classifier\",\n", - " \"CLASS_tree_method\": \"hist\",\n", - " \"CLASS_objective\": \"binary:logistic\",\n", - " \"CLASS_booster\": \"gbtree\",\n", - " \"FIT_verbose\": 0,\n", - " \"label_column\": \"labels\"}" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### **Running the function locally**" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": { - "pycharm": { - "name": "#%%\n" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "> 2021-10-13 10:10:21,807 [info] starting run xgb-trainer-train_model uid=5ec8a83eb65b46dc9b7f9dd654cd1b31 DB=http://mlrun-api:8080\n" - ] - }, - { - "data": { - "text/html": [ - "\n", - "
\n", - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
projectuiditerstartstatenamelabelsinputsparametersresultsartifacts
function-marketplace0Oct 13 10:10:22completedxgb-trainer-train_model
v3io_user=dani
kind=
owner=dani
host=jupyter-dani-5bbd9959b7-tsgh8
dataset
model_type=classifier
CLASS_tree_method=hist
CLASS_objective=binary:logistic
CLASS_booster=gbtree
FIT_verbose=0
label_column=labels
accuracy=0.9552
test-error=0.0448
rocauc=0.9799618829687036
brier_score=0.038984999293145965
f1-score=0.954983922829582
precision_score=0.965679190751445
recall_score=0.9445229681978798
test_set
probability-calibration
confusion-matrix
feature-importances
precision-recall-binary
roc-binary
model
\n", - "
\n", - "
\n", - "
\n", - " Title\n", - " ×\n", - "
\n", - " \n", - "
\n", - "
\n" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n" - ] - }, - { - "data": { - "text/html": [ - " > to track results use the .show() or .logs() methods or click here to open in UI" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "> 2021-10-13 10:10:24,178 [info] run executed, status=completed\n" - ] - }, - { - "data": { - "image/png": "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e+65Jz1DNsYYj8p2LYLkxD9H+caFJvXq1aNFixa2KpgxJsvKdi0CV4wcOZIXX3wRgPvvv5+5c+dSvHhxL0dlzO0rWbIkInLLzdWpJLzldqd5jm/58uWMHz8egDNnztCgQQNq165NYGAgTzzxBBcvXnRLPZmJtQgSERUVRVRUFDExMXZGkMlSkuoaTerxrKh169a0bt0agB9//JGqVavGTUbXqFGjVJWVVb4jrEUQz41L2ceNG8dXX32VJT5gk70MHDiQJk2aJHlLTlL7DBw4MNn9jhw5QtWqVenduzc1atSgS5curFu3jocffphKlSqxdetWYmNjqVSpUtxaurGxsVSsWPGWo/zEpppOqG3bttSpUwdfX1+mT58OOL6QX3jhBWrUqEHNmjXjruydPHky1atXx8/PL26511mzZvHyyy+za9cuBg8ezMqVK6lVqxZXr169qeXx9ddfU79+fWrVqsW//vWvuCuBCxYsyKhRo2jQoAGbNm1K4RPJHLJdiyCpqZ5z5MjBhg0bqFWrlk0LYUwqHTp0iG+//TZuapVvvvmGX375heXLl/Pee++xdOlSunbtSkBAAAMHDmTdunX4+/tTrFixm8pJbKrphGbMmMFdd93F1atXqVevHh06dODIkSOcOHGCffv2AcR174wfP56///6bPHny3NLlU6tWrVumf74hKCiIBQsWsHHjRnLlysVLL71EQEAA3bt3JyIigho1ajBmzBi3vX/elu0SwY2pns+dO8eMGTN44403EBHCw8MpVKiQl6Mz5vbcmGAtKckd5Lgyw2ZSKlSoQM2aNQHH5G6PP/44IkLNmjU5cuQIAD179qRNmzYMHDiQGTNmJDr/jytTTU+ePDludt/jx4/z559/UqVKFf766y9eeeUVnnzySZo3bw4QNwdR27Ztadu2rcuv58cff2THjh3Uq1cPcEyzfWOc0MfHhw4dOrhcVmbg0a4hEWkpIn+IyCERGZrI8yIik53P7xER96yy4ILFixczfPjwuO4gSwLGpF2ePHnifs+RI0fc/Rw5csQt0ViuXDlKlCjBf//7X7Zs2UKrVq1uKSelqabXr1/PunXr2LRpE7t376Z27dpERkZy5513snv3bpo0acKnn35K7969AVixYgX9+/dnx44d1KlTJ9nlIhPG8fzzz7Nr1y527drFH3/8wejRowHHNUdZrdvYY4lA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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - }, - { - "data": { - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - }, - { - "data": { - "image/png": "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VcYY//JPInBfK/q4m5OTw2233cbAgQNRVY444gheeOEFDjvssIoNxBhjSuGbRLBPxWWCJUuW0KVLF+69915SU1OtSJwxplLyTSLQChyjcs+ePVx33XWccMIJ7N27l4ULFzJz5kwrEmeMqZT8kwjc14poGsrLy+PVV19l5MiRfPfdd5x++uneb9QYY8rINw+U4XFn8Y4dO5g0aRK33347derUYdWqVdSsWdOjrRljTPnx0RVB4cA05Z8KXnvtNdLS0pgwYUJRkThLAsaYaOGbRFCoPNPA5s2bGTBgAAMHDqRx48YsX77cisQZY6KOb5qGvOgrvvjii1m2bBn3338///jHP4iL882v0xhThfjmyLWv1tChrWfdunXUqVOH1NRUJk+eTFJSEm3btj30AI0xZZKfn8/GjRvJycmJdCiVQmJiIk2bNj2o6sX+SQTua1kHpiksEnfrrbcyZMgQHnvsMY4++ujyC9AYUyYbN24kNTWVli1b+v5JfVVl+/btbNy4kVatWoX9Pf/1EZTh7+T777+nT58+XHfddfTu3Zsbbrih/AMzxpRJTk4OdevW9X0SAOdmmLp16x701ZFvEkFZHyibNWsWnTt3ZtWqVTz77LPMnz+fFi1alHN0xphDYUlgn7L8LvyTCA5y+WAwCED37t256KKLSE9P57LLLrM/OGNMleOfRBBmZ3F2djZjxoxhwIABRUXinnvuORo2bOh9kMaYKqlv374sX758v8u0bNmSbdu2hb3OmTNnMmrUqEMNDfBRIii0v87ijz/+mKOPPpoHHniAunXrkp+fX4GRGWNMZPjmrqH9NQ5lZmYyZswYpk6dSqtWrXj33Xc55ZRTKjA2Y0x5GP/mStI3ZZTrOtMa1+DOczuU+nn//v3ZsGEDOTk5XH/99QwdOnS/67vmmmtYtmwZ2dnZDBw4kPHjxxd99tBDD/H+++8D8MILL3DkkUeydetWhg8fzvr16wF47LHHOP7448thz/bxTSLYX9NQfn4+r7/+OqNHj2bChAmkpKRUbHDGmKj1zDPPUKdOHbKzs+nevTsDBgygbt26pS5/zz33UKdOHQKBAP369WPFihV06tQJgBo1avDFF1/w7LPPMnr0aObNm8f111/PDTfcwAknnMD69es5/fTTWbVqVbnug38SgftamAi2b9/O448/ztixY6lTpw7ff/89qampEYvP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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "train_run = fn.run(params = task_params, \n", - " inputs={\"dataset\" : 'https://s3.wasabisys.com/iguazio/data/function-marketplace-data/xgb_trainer/classifier-data.csv'},\n", - " local=True)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### **Getting the model**" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "from mlrun.artifacts import get_model\n", - "import pickle\n", - "\n", - "model_file, model_obj, _ = get_model(train_run.artifact('model'))\n", - "model = pickle.load(open(model_file,'rb'))" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "model score : 0.9632\n" - ] - } - ], - "source": [ - "print(f\"model score : {model.score(train_run.artifact('test_set').as_df().drop(['labels'],axis=1),train_run.artifact('test_set').as_df()['labels'])}\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### **Some plotting**" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Display the probability calibration" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "\n", - "

probability calibration plot

\n", - "\n" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "train_run.artifact('probability-calibration').show()" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "\n", - "

Feature Importances

\n", - "\n" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "train_run.artifact('feature-importances').show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### **Running the function remotely**" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "> 2021-10-13 10:10:24,882 [info] Started building image: .mlrun/func-function-marketplace-xgb-trainer:latest\n", - "\u001b[36mINFO\u001b[0m[0000] Retrieving image manifest mlrun/mlrun:0.7.1 \n", - "\u001b[36mINFO\u001b[0m[0000] Retrieving image manifest mlrun/mlrun:0.7.1 \n", - "\u001b[36mINFO\u001b[0m[0000] Built cross stage deps: map[] \n", - "\u001b[36mINFO\u001b[0m[0000] Retrieving image manifest mlrun/mlrun:0.7.1 \n", - "\u001b[36mINFO\u001b[0m[0001] Retrieving image manifest mlrun/mlrun:0.7.1 \n", - "\u001b[36mINFO\u001b[0m[0002] Executing 0 build triggers \n", - "\u001b[36mINFO\u001b[0m[0002] Unpacking rootfs as cmd RUN pip install xgboost==1.3.1 requires it. \n", - "\u001b[36mINFO\u001b[0m[0024] RUN pip install xgboost==1.3.1 \n", - "\u001b[36mINFO\u001b[0m[0024] Taking snapshot of full filesystem... \n", - "\u001b[36mINFO\u001b[0m[0035] cmd: /bin/sh \n", - "\u001b[36mINFO\u001b[0m[0035] args: [-c pip install xgboost==1.3.1] \n", - "\u001b[36mINFO\u001b[0m[0035] Running: [/bin/sh -c pip install xgboost==1.3.1] \n", - "Collecting xgboost==1.3.1\n", - " Downloading xgboost-1.3.1-py3-none-manylinux2010_x86_64.whl (157.5 MB)\n", - "Requirement already satisfied: scipy in /usr/local/lib/python3.7/site-packages (from xgboost==1.3.1) (1.7.1)\n", - "Requirement already satisfied: numpy in /usr/local/lib/python3.7/site-packages (from xgboost==1.3.1) (1.19.5)\n", - "Installing collected packages: xgboost\n", - "Successfully installed xgboost-1.3.1\n", - "WARNING: You are using pip version 20.2.4; however, version 21.3 is available.\n", - "You should consider upgrading via the '/usr/local/bin/python -m pip install --upgrade pip' command.\n", - "\u001b[36mINFO\u001b[0m[0042] Taking snapshot of full filesystem... \n" - ] - }, - { - "data": { - "text/plain": [ - "True" - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "fn.spec.build.commands=['pip install xgboost==1.3.1']\n", - "fn.deploy(with_mlrun=False)" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "> 2021-10-13 10:11:39,577 [info] starting run xgb-trainer-train_model uid=7332ff5d727948c89221d4645b84d028 DB=http://mlrun-api:8080\n", - "> 2021-10-13 10:11:39,764 [info] Job is running in the background, pod: xgb-trainer-train-model-4scfq\n", - "> 2021-10-13 10:11:55,207 [info] run executed, status=completed\n", - "The use of label encoder in XGBClassifier is deprecated and will be removed in a future release. To remove this warning, do the following: 1) Pass option use_label_encoder=False when constructing XGBClassifier object; and 2) Encode your labels (y) as integers starting with 0, i.e. 0, 1, 2, ..., [num_class - 1].\n", - "final state: completed\n" - ] - }, - { - "data": { - "text/html": [ - "\n", - "
\n", - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
projectuiditerstartstatenamelabelsinputsparametersresultsartifacts
function-marketplace0Oct 13 10:11:51completedxgb-trainer-train_model
v3io_user=dani
kind=job
owner=dani
host=xgb-trainer-train-model-4scfq
dataset
model_type=classifier
CLASS_tree_method=hist
CLASS_objective=binary:logistic
CLASS_booster=gbtree
FIT_verbose=0
label_column=labels
accuracy=0.9552
test-error=0.0448
rocauc=0.9799618829687036
brier_score=0.038984999293145965
f1-score=0.954983922829582
precision_score=0.965679190751445
recall_score=0.9445229681978798
test_set
probability-calibration
confusion-matrix
feature-importances
precision-recall-binary
roc-binary
model
\n", - "
\n", - "
\n", - "
\n", - " Title\n", - " ×\n", - "
\n", - " \n", - "
\n", - "
\n" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n" - ] - }, - { - "data": { - "text/html": [ - " > to track results use the .show() or .logs() methods or click here to open in UI" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "> 2021-10-13 10:11:58,969 [info] run executed, status=completed\n" - ] - }, - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 12, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "fn.run(inputs={\"dataset\" : 'https://s3.wasabisys.com/iguazio/data/function-marketplace-data/xgb_trainer/classifier-data.csv'},\n", - " params=task_params)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "[Back to the top](#XGBoost-trainer)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.6" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/xgb_trainer/xgb_trainer.py b/xgb_trainer/xgb_trainer.py deleted file mode 100644 index 4754aae26..000000000 --- a/xgb_trainer/xgb_trainer.py +++ /dev/null @@ -1,160 +0,0 @@ -# Copyright 2019 Iguazio -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# Generated by nuclio.export.NuclioExporter - -import warnings - -warnings.simplefilter(action="ignore", category=FutureWarning) - -from mlrun.mlutils.data import get_sample, get_splits -from mlrun.mlutils.models import gen_sklearn_model, eval_model_v2 -from mlrun.utils.helpers import create_class - -from mlrun.execution import MLClientCtx -from mlrun.datastore import DataItem - -from cloudpickle import dumps -import pandas as pd -import os -from typing import Union - - -def _gen_xgb_model(model_type: str, xgb_params: dict): - """generate an xgboost model - - Multiple model types that can be estimated using - the XGBoost Scikit-Learn API. - - Input can either be a predefined json model configuration or one - of the five xgboost model types: "classifier", "regressor", "ranker", - "rf_classifier", or "rf_regressor". - - In either case one can pass in a params dict to modify defaults values. - - Based on `mlutils.models.gen_sklearn_model`, see the function - `sklearn_classifier` in this repository. - - :param model_type: one of "classifier", "regressor", - "ranker", "rf_classifier", or - "rf_regressor" - :param xgb_params: class init parameters - """ - mtypes = { - "classifier": "xgboost.XGBClassifier", - "regressor": "xgboost.XGBRegressor", - "ranker": "xgboost.XGBRanker", - "rf_classifier": "xgboost.XGBRFClassifier", - "rf_regressor": "xgboost.XGBRFRegressor", - } - if model_type.endswith("json"): - model_config = model_type - elif model_type in mtypes.keys(): - model_config = mtypes[model_type] - else: - raise Exception("unrecognized model type, see help documentation") - - return gen_sklearn_model(model_config, xgb_params) - - -def train_model( - context: MLClientCtx, - model_type: str, - dataset: Union[DataItem, pd.core.frame.DataFrame], - label_column: str = "labels", - encode_cols: dict = {}, - sample: int = -1, - imbal_vec=[], - test_size: float = 0.25, - valid_size: float = 0.75, - random_state: int = 1, - models_dest: str = "models", - plots_dest: str = "plots", - eval_metrics: list = ["error", "auc"], - file_ext: str = "parquet", - test_set: str = "test_set", -) -> None: - """train an xgboost model. - - Note on imabalanced data: the `imbal_vec` parameter represents the measured - class representations in the sample and can be used as a first step in tuning - an XGBoost model. This isn't a hyperparamter, merely an estimate that should - be set as 'constant' throughout tuning process. - - :param context: the function context - :param model_type: the model type to train, "classifier", "regressor"... - :param dataset: ("data") name of raw data file - :param label_column: ground-truth (y) labels - :param encode_cols: dictionary of names and prefixes for columns that are - to hot be encoded. - :param sample: Selects the first n rows, or select a sample - starting from the first. If negative <-1, select - a random sample - :param imbal_vec: ([]) vector of class weights seen in sample - :param test_size: (0.05) test set size - :param valid_size: (0.75) Once the test set has been removed the - training set gets this proportion. - :param random_state: (1) sklearn rng seed - :param models_dest: destination subfolder for model artifacts - :param plots_dest: destination subfolder for plot artifacts - :param eval_metrics: (["error", "auc"]) learning curve metrics - :param file_ext: format for test_set_key hold out data - :param test-set: (test_set) key of held out data in artifact store - """ - models_dest = models_dest or "models" - plots_dest = plots_dest or f"plots/{context.name}" - - raw, labels, header = get_sample(dataset, sample, label_column) - - if encode_cols: - raw = pd.get_dummies( - raw, - columns=list(encode_cols.keys()), - prefix=list(encode_cols.values()), - drop_first=True, - ) - - (xtrain, ytrain), (xvalid, yvalid), (xtest, ytest) = get_splits( - raw, labels, 3, test_size, valid_size, random_state - ) - - context.log_dataset( - test_set, df=pd.concat([xtest, ytest], axis=1), format=file_ext, index=False - ) - - model_config = _gen_xgb_model(model_type, context.parameters.items()) - - XGBBoostClass = create_class(model_config["META"]["class"]) - model = XGBBoostClass(**model_config["CLASS"]) - - model_config["FIT"].update( - { - "X": xtrain, - "y": ytrain.values, - "eval_set": [(xtrain, ytrain), (xvalid, yvalid)], - "eval_metric": eval_metrics, - } - ) - - model.fit(**model_config["FIT"]) - - eval_metrics = eval_model_v2(context, xvalid, yvalid, model) - - model_bin = dumps(model) - context.log_model( - "model", - body=model_bin, - artifact_path=os.path.join(context.artifact_path, models_dest), - model_file="model.pkl", - )