Publishes and queries a mchaine learned model through a REST API

This example starts a waitress server, creates a WSGI application based on falcon and queries the REST API. This application returns the prediction from a model trained on Iris dataset.

Settings.

import os
host = '127.0.0.1'
port = 8093
location = os.path.abspath('temp_scripts_ml').replace("\\", "/")

We need users and to encrypt passwords.

users = """xavier,passWrd!
clémence,notmybirthday"""
with open("users.txt", "w", encoding="utf-8") as f:
    f.write(users)

from lightmlrestapi.cli import encrypt_pwd
if not os.path.exists(location):
    os.mkdir(location)
encrypt_pwd("users.txt", os.path.join(location, "encrypted_users.txt"))

Out:

[encrypt_pwd] encrypt 'users.txt'
[encrypt_pwd] to      'somewhere/workspace/lightmlrestapi/lightmlrestapi_UT_39_std/_doc/examples/temp_scripts_ml/encrypted_users.txt'
[encrypt_pwd] done.

Let’s train a machine learned model and pickle the model.

from sklearn import datasets
from sklearn.linear_model import LogisticRegression

iris = datasets.load_iris()
X = iris.data[:, :2]  # we only take the first two features.
y = iris.target
clf = LogisticRegression()
clf.fit(X, y)

# save with pickle
import pickle
model_data = pickle.dumps(clf)
model_file = "model_iris2.pkl"
with open(model_file, "wb") as f:
    f.write(model_data)

We create a file which follows the API defined in tutoral REST API to a storage for machine learned model.

from lightmlrestapi.testing import template_ml
with open(template_ml.__file__, "r", encoding="utf-8") as f:
    code = f.read()
code = code.replace("iris2.pkl", model_file)
with open("model_iris.py", "w", encoding="utf-8") as f:
    f.write(code)
print(code)

Out:

"""
Template application for a machine learning model
available through a REST API.


:githublink:`%|py|6`
"""
import pickle
import os


# Declare an id for the REST API.
def restapi_version():
    """
    Displays a version.


    :githublink:`%|py|14`
    """
    return "0.1.1234"


# Declare a loading function.
def restapi_load(files={"model": "model_iris2.pkl"}):  # pylint: disable=W0102
    """
    Loads the model.
    The model name is relative to this file.
    When call by a REST API, the default value is always used.


    :githublink:`%|py|24`
    """
    model = files["model"]
    here = os.path.dirname(__file__)
    model = os.path.join(here, model)
    if not os.path.exists(model):
        raise FileNotFoundError("Cannot find model '{0}' (full path is '{1}')".format(
            model, os.path.abspath(model)))
    with open(model, "rb") as f:
        loaded_model = pickle.load(f)
    return loaded_model


# Declare a predict function.
def restapi_predict(model, X):
    """
    Computes the prediction for model *clf*.

    :param model: pipeline following :epkg:`scikit-learn` API
    :param X: inputs
    :return: output of *predict_proba*


    :githublink:`%|py|44`
    """
    return model.predict_proba(X)

Creates a dummy application and starts a server in a different process. See dummy_application.

def process_server(host, port, location):
    from lightmlrestapi.cli import start_mlreststor
    import os
    with open("log.log", "a", encoding="utf-8") as f:
        def flog(*li):
            f.write(" ".join(str(_) for _ in li) + "\n")
            f.flush()
        flog("create app in ", os.getcwd())
        app = start_mlreststor(location=location, name='ml',
                               nostart=True, wsgi=None,
                               secret='', users='encrypted_users.txt',
                               fLOG=flog)
        from waitress import serve
        serve(app, host=host, port=port, url_scheme='https')

Saves this code into a file and we start it from a different process.

import lightmlrestapi

header = """
import sys
sys.path.append(r'{0}')
""".format(os.path.join(os.path.dirname(lightmlrestapi.__file__), '..'))

import inspect
code = "".join(inspect.getsourcelines(process_server)[0])
code = header + code + \
    "\nprocess_server('{0}', {1}, '{2}')\n".format(host, port, location)
if not os.path.exists(location):
    os.mkdir(location)
code_file = os.path.join(location, "_start_server_store.py")
print("Write file '{0}'.".format(code_file))
with open(code_file, "w") as f:
    f.write(code)

import sys
import os
from subprocess import Popen
if sys.platform.startswith('win'):
    cmd = '{0} -u "{1}"'.format(sys.executable, code_file)
    print("Running '{0}'".format(cmd))
    proc = Popen(cmd, cwd=location)
else:
    cmd = [sys.executable, '-u', code_file]
    print("Running '{0}'".format(cmd))
    proc = Popen(cmd, cwd=location)
print('Start server, process id', proc.pid)

Out:

Write file 'somewhere/workspace/lightmlrestapi/lightmlrestapi_UT_39_std/_doc/examples/temp_scripts_ml/_start_server_store.py'.
Running '['somewhere/workspace/lightmlrestapi/lightmlrestapi_UT_39_std/_venv/bin/python3.9', '-u', 'somewhere/workspace/lightmlrestapi/lightmlrestapi_UT_39_std/_doc/examples/temp_scripts_ml/_start_server_store.py']'
Start server, process id 32414

Let’s wait.

from time import sleep
sleep(5)

Let’s publish the machine learned model.

import random
url = 'http://{0}:{1}/'.format(host, port)
model_name = "xavier/iris%d" % random.randint(0, 1000)

from lightmlrestapi.netrest import submit_rest_request, json_upload_model
req = json_upload_model(name=model_name,
                        pyfile="model_iris.py", data=model_file)
r = submit_rest_request(req, login="xavier", timeout=600,
                        pwd="passWrd!", url=url, fLOG=print)
print(r)

Out:

[submit_rest_request] submit authentified request as 'xavier' to 'http://127.0.0.1:8093/'
{'name': 'xavier/iris665'}

Let’s query the machine learned model.

from lightmlrestapi.netrest import json_predict_model, submit_rest_request
from sklearn import datasets

iris = datasets.load_iris()
X = iris.data[:, :2]

req = json_predict_model(model_name, X)
res = submit_rest_request(
    req, login="xavier", pwd="passWrd!", url=url, fLOG=print)
print(res)

Out:

[submit_rest_request] submit authentified request as 'xavier' to 'http://127.0.0.1:8093/'
{'output': [[0.9234731532336989, 0.058508096672784914, 0.018018750093516302], [0.7915650006370775, 0.1809126483492763, 0.027522351013646258], [0.9423640406095638, 0.050863449789936226, 0.00677250960049992], [0.9405535399387285, 0.053759434605640345, 0.005687025455631219], [0.961853129580408, 0.02961439317208289, 0.008532477247509003], [0.943195774514809, 0.03408792070603857, 0.02271630477915267], [0.9802285396200284, 0.017417105712913755, 0.002354354667057888], [0.92292733657345, 0.061921756735515775, 0.015150906691034194], [0.9359989835328045, 0.0599949469680326, 0.004006069499162854], [0.8471817123793164, 0.1311650346519944, 0.021653252968689248], [0.8902141684182246, 0.07010917396182785, 0.03967665761994747], [0.9607933684121736, 0.03317356907082062, 0.006033062517005778], [0.8435549382773381, 0.1383053376890674, 0.018139724033594463], [0.9679654971023505, 0.030150988395632114, 0.0018835145020176232], [0.8240950931361675, 0.0761837358088091, 0.09972117105502343], [0.9654194827486003, 0.013482823524878264, 0.021097693726521476], [0.943195774514809, 0.03408792070603857, 0.02271630477915267], [0.9234731532336989, 0.058508096672784914, 0.018018750093516302], [0.7794179611269384, 0.11263678648844676, 0.10794525238461489], [0.9732868385358185, 0.019169466270612678, 0.007543695193568866], [0.7323106376629455, 0.18552319559107847, 0.08216616674597596], [0.9618916670151066, 0.027966374868260785, 0.010141958116632698], [0.9906366751031744, 0.00807758177752486, 0.0012857431193008758], [0.8516655098151943, 0.11758243220651414, 0.030752057978291527], [0.9607933684121736, 0.03317356907082062, 0.006033062517005778], [0.7273712276484463, 0.23173692731756346, 0.04089184503399024], [0.92292733657345, 0.061921756735515775, 0.015150906691034194], [0.8929635107185602, 0.07886461493099485, 0.028171874350445075], [0.8523034810503894, 0.11111778573736782, 0.03657873321224272], [0.9423640406095638, 0.050863449789936226, 0.00677250960049992], [0.8874102794254259, 0.09856192623951608, 0.014027794335058009], [0.7323106376629455, 0.18552319559107847, 0.08216616674597596], [0.9866670242870762, 0.008421201079673046, 0.004911774633250869], [0.9698094272524459, 0.01518865505636525, 0.015001917691188906], [0.8471817123793164, 0.1311650346519944, 0.021653252968689248], [0.849919110756144, 0.12426105524519258, 0.02581983399866329], [0.7285485258948998, 0.17429193603749915, 0.09715953806760096], [0.973166496530196, 0.021494356411811164, 0.005339147057992672], [0.9555049116971855, 0.04148884824486507, 0.003006240057949361], [0.8928019755039266, 0.08350023164615653, 0.023697792849916893], [0.9456106970478181, 0.04297810809081938, 0.011411194861362363], [0.5080191245826822, 0.4697006788289132, 0.022280196588404702], [0.9788317922607497, 0.019504051632642794, 0.0016641561066075017], [0.9456106970478181, 0.04297810809081938, 0.011411194861362363], [0.9732868385358185, 0.019169466270612678, 0.007543695193568866], [0.8435549382773381, 0.1383053376890674, 0.018139724033594463], [0.9732868385358185, 0.019169466270612678, 0.007543695193568866], [0.958621742155442, 0.037117404797182986, 0.0042608530473747805], [0.9224566152078492, 0.05211581324910247, 0.02542757154304847], [0.8917638176608806, 0.08832150245357649, 0.019914679885542786], [0.0017657201805898522, 0.19815949876974642, 0.8000747810496637], [0.024007686984029275, 0.3671999919271465, 0.6087923210888243], [0.0019957401264977842, 0.23718163963477878, 0.7608226202387234], [0.031277327615531335, 0.8011955916653569, 0.16752708071911168], [0.004121016118773013, 0.4172312240040552, 0.5786477598771718], [0.09003528230891716, 0.6393386956840372, 0.2706260220070456], [0.05009603833859246, 0.3723557847060536, 0.577548176955354], [0.27891438095679155, 0.6596268206062768, 0.061458798436931694], [0.003795064252706369, 0.36283365080457675, 0.633371284942717], [0.2974883678069201, 0.5924411248351082, 0.11007050735797158], [0.055944288776728836, 0.8757966938453309, 0.06825901737794031], [0.08636711900405467, 0.5468882171054812, 0.36674466389046406], [0.003564310349150445, 0.7094293604210975, 0.28700632922975206], [0.029183797016525195, 0.5300859929960172, 0.44073020998745754], [0.17189962901689151, 0.5931924012159495, 0.23490796976715897], [0.004808174899990312, 0.2940652980443155, 0.7011265270556941], [0.23029805385010305, 0.5383566299248574, 0.23134531622503943], [0.044997271718438626, 0.6575095464859422, 0.29749318179561923], [0.0016714632308045725, 0.6464635326217157, 0.3518650041474796], [0.04531803317634923, 0.7426001966387058, 0.21208177018494498], [0.16127894138075502, 0.4686472944264535, 0.3700737641927916], [0.02067956286310204, 0.5544810758920162, 0.42483936124488186], [0.003291516734841676, 0.551667658802517, 0.44504082446264137], [0.02067956286310204, 0.5544810758920162, 0.42483936124488186], [0.00876895928375104, 0.4314424980848815, 0.5597885426313675], [0.005306453624878623, 0.34367817762231, 0.6510153687528114], [0.0011510204601492826, 0.31566459060996704, 0.6831843889298836], [0.003452451595993037, 0.3116965251311246, 0.6848510232728824], [0.04278961953130301, 0.5575530282124554, 0.39965735225624155], [0.0453796747261111, 0.7022008038046239, 0.25241952146926516], [0.044863003774492094, 0.7784960587441299, 0.17664093748137788], [0.044863003774492094, 0.7784960587441299, 0.17664093748137788], [0.044997271718438626, 0.6575095464859422, 0.29749318179561923], [0.02142310267564516, 0.6082915826423594, 0.3702853146819955], [0.3865114080968157, 0.4649738806286233, 0.14851471127456103], [0.2056038280560513, 0.38218597108499114, 0.4122102008589576], [0.004808174899990312, 0.2940652980443155, 0.7011265270556941], [0.0016222193556176686, 0.5924786695428816, 0.40589911110150073], [0.23029805385010305, 0.5383566299248574, 0.23134531622503943], [0.06389992072991345, 0.7511515153076571, 0.18494856396242956], [0.09016467534990207, 0.7179972978181405, 0.19183802683195747], [0.04097367347592081, 0.5041602404419407, 0.45486608608213847], [0.03168153598751881, 0.6833810805760488, 0.28493738343643227], [0.15744897247753584, 0.7662400529741181, 0.0763109745483461], [0.09050914416631224, 0.6806044457298722, 0.22888641010381552], [0.16998452406739825, 0.5539186147097488, 0.27609686122285293], [0.12468965722997379, 0.5998019168145986, 0.2755084259554277], [0.01970909161346736, 0.49903145317871334, 0.4812594552078193], [0.22042833372116796, 0.6862265734417736, 0.09334509283705841], [0.09003528230891716, 0.6393386956840372, 0.2706260220070456], [0.05009603833859246, 0.3723557847060536, 0.577548176955354], [0.044997271718438626, 0.6575095464859422, 0.29749318179561923], [0.0005890315959255634, 0.20080294030386836, 0.798608028100206], [0.013196623532142212, 0.46578008654966196, 0.5210232899181959], [0.008107842063144513, 0.376700403477002, 0.6151917544598535], [5.9599942653727486e-05, 0.10694517892107538, 0.8929952211362708], [0.3617855382031336, 0.5796125219990548, 0.058601939797811704], [0.00017239305194130568, 0.16857983863471676, 0.8312477683133419], [0.0006418544686363522, 0.406204883615126, 0.5931532619162377], [0.0025456052384480394, 0.11690531606605384, 0.8805490786954981], [0.015792138487953175, 0.33670538744822515, 0.6475024740638217], [0.004417920827248897, 0.4736684743464957, 0.5219136048262555], [0.0022341870355005655, 0.28117756669080207, 0.7165882462736974], [0.03183782640158381, 0.727250780208637, 0.24091139338977913], [0.06345719876676807, 0.628139255542753, 0.30840354569047906], [0.024007686984029275, 0.3671999919271465, 0.6087923210888243], [0.008107842063144513, 0.376700403477002, 0.6151917544598535], [0.0004592162956232197, 0.05094044149398049, 0.9486003422103964], [1.0543664049199335e-05, 0.12523539068513884, 0.8747540656508119], [0.003564310349150445, 0.7094293604210975, 0.28700632922975206], [0.002764374385319656, 0.22255281709084718, 0.7746828085238331], [0.12573852861910728, 0.6405158121133395, 0.2337456592675531], [1.9887088895065128e-05, 0.10839894283368977, 0.8915811700774151], [0.006623557727093663, 0.5094377212245702, 0.4839387210483362], [0.00927098556345095, 0.26020026928226764, 0.7305287451542815], [0.000712415091037477, 0.15536016796990493, 0.8439274169390575], [0.013967620256190902, 0.5220649924066246, 0.4639673873371844], [0.04097367347592081, 0.5041602404419407, 0.45486608608213847], [0.006231630519296217, 0.4526029765051552, 0.5411653929755487], [0.0003747570374804811, 0.17808939244503885, 0.8215358505174806], [7.924502144870908e-05, 0.159461367162131, 0.8404593878164203], [0.0001780310726467265, 0.038375564712726684, 0.9614464042146267], [0.006231630519296217, 0.4526029765051552, 0.5411653929755487], [0.00936228809534162, 0.4877990302653535, 0.5028386816393048], [0.010262018812111355, 0.5995975704668249, 0.39014041072106365], [3.741322504955522e-05, 0.09358307135596618, 0.9063795154189843], [0.06891786544444248, 0.3470129629845428, 0.5840691715710147], [0.017219232480345223, 0.3887827383062576, 0.5939980292133973], [0.059906187819246795, 0.5287843533655983, 0.41130945881515485], [0.0019957401264977842, 0.23718163963477878, 0.7608226202387234], [0.004808174899990312, 0.2940652980443155, 0.7011265270556941], [0.0019957401264977842, 0.23718163963477878, 0.7608226202387234], [0.044997271718438626, 0.6575095464859422, 0.29749318179561923], [0.004309001052706455, 0.248860550478636, 0.7468304484686574], [0.00927098556345095, 0.26020026928226764, 0.7305287451542815], [0.003452451595993037, 0.3116965251311246, 0.6848510232728824], [0.003291516734841676, 0.551667658802517, 0.44504082446264137], [0.008107842063144513, 0.376700403477002, 0.6151917544598535], [0.1014865720176284, 0.3665773990269557, 0.5319360289554159], [0.08636711900405467, 0.5468882171054812, 0.36674466389046406]], 'version': '0.1.1234'}

Let’s measure the processing time.

def query_model(X):
    req = json_predict_model(model_name, X)
    return submit_rest_request(req, login="xavier", pwd="passWrd!", url=url, fLOG=None)


import timeit
N = 100
print(timeit.timeit('query_model(X)', number=N, globals=globals()) / N)

Out:

0.009650388909794855

Let’s stop the server.

from pyquickhelper.loghelper import reap_children
reap_children(subset={proc.pid}, fLOG=print)

Out:

process psutil.Process(pid=32414, status='terminated', exitcode=<Negsignal.SIGTERM: -15>, started='07:17:23') terminated with exit code -15

{32414}

Total running time of the script: ( 0 minutes 9.256 seconds)

Gallery generated by Sphinx-Gallery