|
15 | 15 | "cell_type": "markdown",
|
16 | 16 | "metadata": {},
|
17 | 17 | "source": [
|
18 |
| - "<a id=\"deploy\"></a>\n", |
19 |
| - "### Deploy our serving class using as a serverless function\n", |
20 |
| - "in the following section we create a new model serving function which wraps our class , and specify model and other resources." |
| 18 | + "in the following section we create a new model serving function which wraps our class , and specify model and other resources.\n", |
| 19 | + "Deploying the serving function will provide us an http endpoint that can handle requests in real time.\n", |
| 20 | + "This function is part of the [customer-churn-prediction demo](https://github.com/mlrun/demos/tree/master/customer-churn-prediction).<br>\n", |
| 21 | + "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." |
| 22 | + ] |
| 23 | + }, |
| 24 | + { |
| 25 | + "cell_type": "markdown", |
| 26 | + "metadata": {}, |
| 27 | + "source": [ |
| 28 | + "### **Steps**\n", |
| 29 | + "1. [Setup function parameters](#Setup-function-parameters)\n", |
| 30 | + "2. [Importing the function](#Importing-the-function)\n", |
| 31 | + "3. [Testing the function locally](#Testing-the-function-locally)\n", |
| 32 | + "4. [Testing the function remotely](#Testing-the-function-remotely)" |
21 | 33 | ]
|
22 | 34 | },
|
23 | 35 | {
|
|
26 | 38 | "metadata": {},
|
27 | 39 | "outputs": [],
|
28 | 40 | "source": [
|
29 |
| - "import mlrun\n", |
30 |
| - "import pandas as pd\n", |
31 |
| - "import os" |
| 41 | + "import warnings\n", |
| 42 | + "warnings.filterwarnings(\"ignore\")" |
32 | 43 | ]
|
33 | 44 | },
|
34 | 45 | {
|
35 | 46 | "cell_type": "code",
|
36 | 47 | "execution_count": 2,
|
37 | 48 | "metadata": {},
|
| 49 | + "outputs": [], |
| 50 | + "source": [ |
| 51 | + "# Following packages are required, make sure to install\n", |
| 52 | + "# !pip install xgboost==1.3.1" |
| 53 | + ] |
| 54 | + }, |
| 55 | + { |
| 56 | + "cell_type": "markdown", |
| 57 | + "metadata": {}, |
| 58 | + "source": [ |
| 59 | + "### **Setup function parameters**" |
| 60 | + ] |
| 61 | + }, |
| 62 | + { |
| 63 | + "cell_type": "code", |
| 64 | + "execution_count": 3, |
| 65 | + "metadata": {}, |
| 66 | + "outputs": [], |
| 67 | + "source": [ |
| 68 | + "# Setting up models path\n", |
| 69 | + "xgb_model_path = 'https://s3.wasabisys.com/iguazio/models/function-marketplace-models/churn_server/xgb_model.pkl'" |
| 70 | + ] |
| 71 | + }, |
| 72 | + { |
| 73 | + "cell_type": "markdown", |
| 74 | + "metadata": {}, |
| 75 | + "source": [ |
| 76 | + "### **Importing the function**" |
| 77 | + ] |
| 78 | + }, |
| 79 | + { |
| 80 | + "cell_type": "code", |
| 81 | + "execution_count": 4, |
| 82 | + "metadata": {}, |
38 | 83 | "outputs": [
|
39 | 84 | {
|
40 | 85 | "name": "stdout",
|
41 | 86 | "output_type": "stream",
|
42 | 87 | "text": [
|
43 |
| - "> 2021-10-12 07:29:57,497 [info] loaded project function-marketplace from MLRun DB\n" |
| 88 | + "> 2021-10-14 06:10:16,104 [info] loaded project function-marketplace from MLRun DB\n" |
44 | 89 | ]
|
45 | 90 | },
|
46 | 91 | {
|
47 | 92 | "data": {
|
48 | 93 | "text/plain": [
|
49 |
| - "('function-marketplace', 'v3io:///projects/{{run.project}}/artifacts')" |
| 94 | + "<mlrun.serving.states.TaskStep at 0x7f8f2306ca90>" |
50 | 95 | ]
|
51 | 96 | },
|
52 |
| - "execution_count": 2, |
| 97 | + "execution_count": 4, |
53 | 98 | "metadata": {},
|
54 | 99 | "output_type": "execute_result"
|
55 | 100 | }
|
56 | 101 | ],
|
57 | 102 | "source": [
|
58 |
| - "# setting environment \n", |
59 |
| - "mlrun.set_environment(project='function-marketplace')" |
| 103 | + "import mlrun\n", |
| 104 | + "mlrun.set_environment(project='function-marketplace')\n", |
| 105 | + "\n", |
| 106 | + "# Importing the function from the hub\n", |
| 107 | + "fn = mlrun.import_function(\"hub://churn_server:development\")\n", |
| 108 | + "fn.apply(mlrun.auto_mount())\n", |
| 109 | + "\n", |
| 110 | + "# Manually specifying needed packages \n", |
| 111 | + "fn.spec.build.commands = ['pip install lifelines==0.22.8', 'pip install xgboost==1.3.1']\n", |
| 112 | + "\n", |
| 113 | + "# Adding the model \n", |
| 114 | + "fn.add_model(key='xgb_model', model_path=xgb_model_path ,class_name='ChurnModel')" |
60 | 115 | ]
|
61 | 116 | },
|
62 | 117 | {
|
63 |
| - "cell_type": "code", |
64 |
| - "execution_count": 3, |
| 118 | + "cell_type": "markdown", |
65 | 119 | "metadata": {},
|
66 |
| - "outputs": [ |
67 |
| - { |
68 |
| - "name": "stdout", |
69 |
| - "output_type": "stream", |
70 |
| - "text": [ |
71 |
| - "File ‘/User/functions/churn_server/models/xgb_model.pkl’ already there; not retrieving.\n", |
72 |
| - "\n" |
73 |
| - ] |
74 |
| - } |
75 |
| - ], |
76 | 120 | "source": [
|
77 |
| - "# Stored artifacts path (S3)\n", |
78 |
| - "data_path = 'https://s3.wasabisys.com/iguazio/data/churn/test_set.csv'\n", |
79 |
| - "model_url = 'https://s3.wasabisys.com/iguazio/models/churn/xgb_model.pkl'\n", |
80 |
| - "model_path = os.getcwd() + '/models'\n", |
| 121 | + "### **Testing the function locally**" |
| 122 | + ] |
| 123 | + }, |
| 124 | + { |
| 125 | + "cell_type": "markdown", |
| 126 | + "metadata": {}, |
| 127 | + "source": [ |
| 128 | + "> Note that this function is a serving function, hence not needs to run, but deployed.<br>\n", |
81 | 129 | "\n",
|
82 |
| - "# Downloading the model\n", |
83 |
| - "! wget -nc -P {model_path} {model_url}" |
| 130 | + "in order to test locally without deploying to server, mlrun provides mocking api that simulate the action." |
84 | 131 | ]
|
85 | 132 | },
|
86 | 133 | {
|
87 | 134 | "cell_type": "code",
|
88 |
| - "execution_count": 4, |
| 135 | + "execution_count": 5, |
89 | 136 | "metadata": {},
|
90 | 137 | "outputs": [
|
91 | 138 | {
|
92 |
| - "data": { |
93 |
| - "text/plain": [ |
94 |
| - "<mlrun.serving.states.TaskStep at 0x7f559bdabb90>" |
95 |
| - ] |
96 |
| - }, |
97 |
| - "execution_count": 4, |
98 |
| - "metadata": {}, |
99 |
| - "output_type": "execute_result" |
| 139 | + "name": "stdout", |
| 140 | + "output_type": "stream", |
| 141 | + "text": [ |
| 142 | + "> 2021-10-14 06:10:19,145 [info] model xgb_model was loaded\n", |
| 143 | + "> 2021-10-14 06:10:19,145 [info] Initializing endpoint records\n", |
| 144 | + "> 2021-10-14 06:10:19,164 [info] Loaded ['xgb_model']\n" |
| 145 | + ] |
100 | 146 | }
|
101 | 147 | ],
|
102 | 148 | "source": [
|
103 |
| - "# importing the function from the function marketplace\n", |
104 |
| - "fn = mlrun.import_function(\"hub://churn_server\").apply(mlrun.auto_mount())\n", |
105 |
| - "fn.spec.build.commands = ['pip install lifelines==0.22.8', 'pip install xgboost==1.3.1']\n", |
| 149 | + "# When mocking, class has to be present\n", |
| 150 | + "from churn_server import *\n", |
106 | 151 | "\n",
|
107 |
| - "# Adding the model \n", |
108 |
| - "fn.add_model(key='xgb_model', model_path=model_path ,class_name='ChurnModel')" |
| 152 | + "# Mocking function\n", |
| 153 | + "server = fn.to_mock_server()" |
109 | 154 | ]
|
110 | 155 | },
|
111 | 156 | {
|
112 | 157 | "cell_type": "code",
|
113 |
| - "execution_count": 5, |
| 158 | + "execution_count": 6, |
114 | 159 | "metadata": {},
|
115 | 160 | "outputs": [
|
116 | 161 | {
|
|
315 | 360 | "[5 rows x 23 columns]"
|
316 | 361 | ]
|
317 | 362 | },
|
318 |
| - "execution_count": 5, |
| 363 | + "execution_count": 6, |
319 | 364 | "metadata": {},
|
320 | 365 | "output_type": "execute_result"
|
321 | 366 | }
|
322 | 367 | ],
|
323 | 368 | "source": [
|
| 369 | + "import pandas as pd\n", |
| 370 | + "\n", |
| 371 | + "#declaring test_set path\n", |
| 372 | + "test_set_path = \"https://s3.wasabisys.com/iguazio/data/function-marketplace-data/churn_server/test_set.csv\"\n", |
| 373 | + "\n", |
324 | 374 | "# Getting the data\n",
|
325 |
| - "x_test = pd.read_csv(data_path)\n", |
| 375 | + "x_test = pd.read_csv(test_set_path)\n", |
326 | 376 | "y_test = x_test['labels']\n",
|
327 | 377 | "x_test.drop(['labels'],axis=1,inplace=True)\n",
|
328 | 378 | "x_test.head()"
|
329 | 379 | ]
|
330 | 380 | },
|
331 | 381 | {
|
332 |
| - "cell_type": "markdown", |
| 382 | + "cell_type": "code", |
| 383 | + "execution_count": 12, |
333 | 384 | "metadata": {},
|
| 385 | + "outputs": [], |
334 | 386 | "source": [
|
335 |
| - "### **Testing the model locally**" |
| 387 | + "# KFServing protocol event\n", |
| 388 | + "event_data = {\"inputs\": x_test.values.tolist()}" |
336 | 389 | ]
|
337 | 390 | },
|
338 | 391 | {
|
339 | 392 | "cell_type": "code",
|
340 |
| - "execution_count": 6, |
| 393 | + "execution_count": 13, |
341 | 394 | "metadata": {},
|
342 | 395 | "outputs": [],
|
343 | 396 | "source": [
|
344 |
| - "# an old version of lifelines and xgboost is required when running locally, uncomment to install\n", |
345 |
| - "# !pip install lifelines==0.22.8\n", |
346 |
| - "# !pip install xgboost==1.3.1" |
| 397 | + "response = server.test(path='/v2/models/xgb_model/predict',body=event_data)" |
347 | 398 | ]
|
348 | 399 | },
|
349 | 400 | {
|
350 | 401 | "cell_type": "code",
|
351 |
| - "execution_count": 7, |
| 402 | + "execution_count": 14, |
352 | 403 | "metadata": {},
|
353 | 404 | "outputs": [
|
354 | 405 | {
|
355 | 406 | "name": "stdout",
|
356 | 407 | "output_type": "stream",
|
357 | 408 | "text": [
|
358 |
| - "> 2021-10-12 07:30:00,756 [info] model xgb_model was loaded\n", |
359 |
| - "> 2021-10-12 07:30:00,757 [info] Initializing endpoint records\n", |
360 |
| - "> 2021-10-12 07:30:00,768 [info] Loaded ['xgb_model']\n", |
361 |
| - "model's accuracy : 0.7913907284768212\n" |
| 409 | + "When mocking to server, returned dict has the following fields : id, model_name, outputs\n" |
362 | 410 | ]
|
363 | 411 | }
|
364 | 412 | ],
|
365 | 413 | "source": [
|
366 |
| - "# importing the class (a must when using mock_to_server)\n", |
367 |
| - "from churn_server import *\n", |
368 |
| - "\n", |
369 |
| - "server = fn.to_mock_server()\n", |
370 |
| - "response = server.test(\"/v2/models/xgb_model/predict\",body={'inputs' : x_test.values.tolist()})\n", |
371 |
| - "\n", |
372 |
| - "# Calculating model's accuracy\n", |
373 |
| - "accuracy = sum(1 for x,y in zip(response['outputs'],y_test) if x == y) / len(y_test)\n", |
374 |
| - "print(f\"model's accuracy : {accuracy}\")" |
| 414 | + "print(f'When mocking to server, returned dict has the following fields : {\", \".join([x for x in response.keys()])}')" |
375 | 415 | ]
|
376 | 416 | },
|
377 | 417 | {
|
378 | 418 | "cell_type": "markdown",
|
379 | 419 | "metadata": {},
|
380 | 420 | "source": [
|
381 |
| - "### **Deploying and testing our model server using HTTP request**" |
| 421 | + "### **Testing the function remotely**" |
382 | 422 | ]
|
383 | 423 | },
|
384 | 424 | {
|
385 | 425 | "cell_type": "code",
|
386 |
| - "execution_count": 9, |
| 426 | + "execution_count": 10, |
387 | 427 | "metadata": {},
|
388 | 428 | "outputs": [
|
389 | 429 | {
|
390 | 430 | "name": "stdout",
|
391 | 431 | "output_type": "stream",
|
392 | 432 | "text": [
|
393 |
| - "> 2021-10-12 07:33:32,143 [info] Starting remote function deploy\n", |
394 |
| - "2021-10-12 07:33:32 (info) Deploying function\n", |
395 |
| - "2021-10-12 07:33:32 (info) Building\n", |
396 |
| - "2021-10-12 07:33:32 (info) Staging files and preparing base images\n", |
397 |
| - "2021-10-12 07:33:32 (info) Building processor image\n", |
398 |
| - "2021-10-12 07:33:33 (info) Build complete\n", |
399 |
| - "2021-10-12 07:33:41 (info) Function deploy complete\n", |
400 |
| - "> 2021-10-12 07:33:42,050 [info] successfully deployed function: {'internal_invocation_urls': ['nuclio-default-churn-server.default-tenant.svc.cluster.local:8080'], 'external_invocation_urls': ['default-tenant.app.app-lab-eks-testing.iguazio-cd1.com:31334']}\n" |
| 433 | + "> 2021-10-14 06:10:20,163 [info] Starting remote function deploy\n", |
| 434 | + "2021-10-14 06:10:20 (info) Deploying function\n", |
| 435 | + "2021-10-14 06:10:20 (info) Building\n", |
| 436 | + "2021-10-14 06:10:20 (info) Staging files and preparing base images\n", |
| 437 | + "2021-10-14 06:10:20 (info) Building processor image\n", |
| 438 | + "2021-10-14 06:10:21 (info) Build complete\n", |
| 439 | + "2021-10-14 06:10:29 (info) Function deploy complete\n", |
| 440 | + "> 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" |
401 | 441 | ]
|
402 | 442 | }
|
403 | 443 | ],
|
404 | 444 | "source": [
|
405 | 445 | "address = fn.deploy()"
|
406 | 446 | ]
|
407 | 447 | },
|
408 |
| - { |
409 |
| - "cell_type": "markdown", |
410 |
| - "metadata": {}, |
411 |
| - "source": [ |
412 |
| - "We invoke our model serving function using test data, the data vector is specified in the `inputs` attribute." |
413 |
| - ] |
414 |
| - }, |
415 | 448 | {
|
416 | 449 | "cell_type": "code",
|
417 |
| - "execution_count": 19, |
| 450 | + "execution_count": 11, |
418 | 451 | "metadata": {},
|
419 | 452 | "outputs": [
|
420 | 453 | {
|
|
429 | 462 | "import json\n",
|
430 | 463 | "import requests\n",
|
431 | 464 | "\n",
|
432 |
| - "# KFServing protocol event\n", |
433 |
| - "event_data = {\"inputs\": x_test.values.tolist()}\n", |
434 |
| - "\n", |
435 |
| - "# sending data in a json format\n", |
| 465 | + "# using requests to predict\n", |
436 | 466 | "response = requests.put(address + \"/v2/models/xgb_model/predict\", json=json.dumps(event_data))\n",
|
437 | 467 | "\n",
|
438 | 468 | "# returned data is a string \n",
|
|
445 | 475 | "cell_type": "markdown",
|
446 | 476 | "metadata": {},
|
447 | 477 | "source": [
|
448 |
| - "**[back to top](#top)**" |
| 478 | + "[Back to the top](#Churn-Server)" |
449 | 479 | ]
|
450 | 480 | }
|
451 | 481 | ],
|
|
0 commit comments