"""
Machine Learning Post request
:githublink:`%|py|5`
"""
import os
import falcon
import numpy
from .data import get_wiki_img
from ..mlapp import MachineLearningPost, AuthMiddleware, MLStoragePost
from ..args import base642image, image2array, encrypt_password
[docs]def dummy_application(app=None, **params):
"""
Defines a dummy application using this API.
It returns a score produced by a model trained
on `Iris datasets <http://scikit-learn.org/stable/auto_examples/datasets/plot_iris_dataset.html>`_
and two features.
:param app: application, if None, creates one
:param params: parameters sent to :class:`MachineLearningPost <lightmlrestapi.mlapp.mlpost_rest.MachineLearningPost>`
:return: app
.. exref::
:title: Query a REST API with features
This example shows how to query a :epkg:`REST API`
by sending a vector of features.
You can start it by running:
::
start_mlrestapi --name=dummy
And then query it with:
::
import requests
import ujson
features = ujson.dumps({'X': [0.1, 0.2]})
r = requests.post('http://127.0.0.1:8081', data=features)
print(r)
print(r.json())
It should return:
::
{'Y': [[0.4994216179, 0.4514893599, 0.0490890222]]}
:githublink:`%|py|51`
"""
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)
if app is None:
app = falcon.API()
route = MachineLearningPost(
lambda clf=clf: clf, LogisticRegression.predict_proba, ccall='multi', **params)
# Let's check it works.
one = clf.predict_proba(X[:1])
two = route.check_single(X[0])
if type(one) != type(two): # pylint: disable=C0123
raise RuntimeError("Type mismath")
app.add_route('/', route)
return app
[docs]def _distance_img(img1, img2, arr1=None):
"""
Computes the distance between two images.
The function uses :epkg:`Pillow`.
:param img1: reference :epkg:`PIL:Image.Image`
:param img2: new image :epkg:`PIL:Image.Image`
:param arr1: img1 as an array if available (to avoid converting
the same image multiple times)
:return: distance (in [0, 1]) or list of distances
:githublink:`%|py|87`
"""
arr1 = image2array(img1) if arr1 is None else arr1
if isinstance(img2, list):
return [_distance_img(img1, im2, arr1) for im2 in img2]
else:
im2 = img2
if img1.size != im2.size:
im2 = im2.resize(img1.size)
if im2.mode != 'RGB':
im2 = im2.convert('RGB')
arr2 = image2array(im2)
# raise Exception("THIS {0}-{1}-{2}".format(im2.size, img1.size, arr1 is None))
diff = arr1.ravel() - arr2.ravel()
total = numpy.abs(diff)
return total.sum() / float(len(total)) / 255
[docs]def _distance_img_b64(img1, img2, arr1=None):
"""
Calls :func:`_distance_img <lightmlrestapi.testing.dummy_applications._distance_img>` on an image encoded with :epkg:`*pyf:base64`.
:param img1: reference :epkg:`PIL:Image.Image`
:param img2: new image or list of images encoded with :epkg:`*pyf:base64`
:param arr1: img1 as an array if available (to avoid converting
the same image multiple times)
:return: distance (in [0, 1]) or list of distances
:githublink:`%|py|115`
"""
if isinstance(img2, list):
img2 = [base642image(img) for img in img2]
else:
img2 = base642image(img2)
return _distance_img(img1, img2, arr1)
[docs]def dummy_application_image(app=None, options=None, **params):
"""
Defines a dummy application using this API
and processing one image. The API ingests
an image, resizes it to 224x224 and returns
a distance to an original image from
subfolder *data*.
:param app: application, if None, creates one
:param options: if not empty, path to an image
:param params: parameters sent to :class:`MachineLearningPost <lightmlrestapi.mlapp.mlpost_rest.MachineLearningPost>`
:return: app
.. exref::
:title: Query a REST API with an image
This example shows how to query a REST API
by sending an image.
You can start it by running:
::
start_mlrestapi --name=dummyimg
And then query it with:
::
import requests
import ujson
from lightmlrestapi.args import image2base64
img = "path_to_image"
b64 = image2base64(img)[1]
features = ujson.dumps({'X': b64}, reject_bytes=False)
r = requests.post('http://127.0.0.1:8081', data=features)
print(r)
print(r.json())
It should return something like:
::
{'distance': [0.21]}
:githublink:`%|py|166`
"""
if options is None or not isinstance(options, str) or len(options) == 0:
options = get_wiki_img()
if not os.path.exists(options):
raise FileNotFoundError("Unable to find image '{0}'.".format(options))
from PIL import Image
img_base = Image.open(get_wiki_img())
if img_base.size != (224, 224):
img_base = img_base.resize((224, 224))
if img_base.mode != 'RGB':
img_base = img_base.convert('RGB')
if app is None:
app = falcon.API()
app.add_route(
'/', MachineLearningPost(lambda: None, lambda model, X: _distance_img_b64(img_base, X), **params))
return app
[docs]def dummy_application_fct(restapi_load, restapi_predict, users=None, algo='sha224', app=None, **params):
"""
Defines an application as defined in the tutorial
:ref:`l-dummy-function-application`.
:param restapi_load: function to load a model
:param restapi_predict: predict function
:param params: parameters sent to :class:`MachineLearningPost <lightmlrestapi.mlapp.mlpost_rest.MachineLearningPost>`
:param users: restrict to authenticated users,
:func:`load_passwords <lightmlrestapi.args.encrypt_helper.load_passwords>`
:param algo: algorithm used to encrypt password
:param app: application, if None, creates one
:githublink:`%|py|197`
"""
if app is None:
if users:
middleware = [AuthMiddleware(users, algo=algo)]
app = falcon.API(middleware=middleware)
else:
app = falcon.API()
app.add_route(
'/', MachineLearningPost(restapi_load, restapi_predict, **params))
return app
[docs]def dummy_application_neighbors(app=None, **params):
"""
Defines a dummy application using this API.
It returns a list of neighbors with a score
on `Iris datasets <http://scikit-learn.org/stable/auto_examples/datasets/plot_iris_dataset.html>`_.
:param app: application, if None, creates one
:param params: parameters sent to :class:`MachineLearningPost <lightmlrestapi.mlapp.mlpost_rest.MachineLearningPost>`
:return: app
.. exref::
:title: Query a REST API with an image and get neighbors
A previous example shows how to send an image,
this one illustrates a result which is a classifier result
neither a regressor one but neighbors.
You can start it by running:
::
start_mlrestapi --name=dummyknn
And then query it with:
::
import requests
import ujson
features = ujson.dumps({'X': [0.1, 0.2]})
r = requests.post('http://127.0.0.1:8081', data=features)
print(r)
print(r.json())
It should return:
::
{'Y': [[[41, 4.8754486973], [13, 5.0477717856], [8, 5.0774009099], [38, 5.1312766443], [60, 5.2201532545]]]}
:githublink:`%|py|247`
"""
from sklearn import datasets
from sklearn.neighbors import NearestNeighbors
iris = datasets.load_iris()
X = iris.data[:, :2] # we only take the first two features.
knn = NearestNeighbors(n_neighbors=5)
knn.fit(X)
def to_serie(knn, x):
"converts into series"
dist, ind = knn.kneighbors(x)
res = []
for i in range(0, len(x)):
res.append([(int(j), float(s))
for j, s in zip(ind[i, :], dist[i, :])])
return res
if app is None:
app = falcon.API()
app.add_route(
'/', MachineLearningPost(lambda: knn, lambda knn, x: to_serie(knn, x),
ccall='multi', **params))
return app
[docs]def dummy_application_neighbors_image(app=None, options=None, **params):
"""
Defines a dummy application using this API.
It returns a list of one neighbor for an image
and metadata (random).
:param app: application, if None, creates one
:param options: if not empty, path to an image
:param params: parameters sent to :class:`MachineLearningPost <lightmlrestapi.mlapp.mlpost_rest.MachineLearningPost>`
:return: app
You can start it by running:
::
start_mlrestapi --name=dummyknnimg
And then query it with:
::
import requests
import ujson
from lightmlrestapi.args import image2base64
img = "path_to_image"
b64 = image2base64(img)[1]
features = ujson.dumps({'X': b64}, reject_bytes=False)
r = requests.post('http://127.0.0.1:8081', data=features)
print(r)
print(r.json())
It should return:
::
{'Y': [[[41, 4.8754486973, {'name': 'wiki.png', description='something'}]]]}
:githublink:`%|py|308`
"""
if options is None or not isinstance(options, str) or len(options) == 0:
options = get_wiki_img()
if not os.path.exists(options):
raise FileNotFoundError("Unable to find image '{0}'.".format(options))
from PIL import Image
img_base = Image.open(get_wiki_img())
if img_base.size != (224, 224):
img_base = img_base.resize((224, 224))
if img_base.mode != 'RGB':
img_base = img_base.convert('RGB')
def mypredict(img_base, X):
"overwrites predict"
res = _distance_img_b64(img_base, X)
final = []
for r, x in zip(res, X):
final.append([(0, r, dict(name=os.path.split(options)[1],
description="image from wikipedia: {0}".format(len(x))))])
return final
if app is None:
app = falcon.API()
app.add_route(
'/', MachineLearningPost(lambda: img_base, mypredict, ccall='multi', **params))
return app
[docs]def dummy_application_auth(app=None, algo="sha224", **params):
"""
Defines a dummy application using this API
including authentification.
It returns a score produced by a model trained
on `Iris datasets <http://scikit-learn.org/stable/auto_examples/datasets/plot_iris_dataset.html>`_
and two features.
:param app: application, if None, creates one
:param algo: algorithm used to encrypt the passwords
:param params: parameters sent to :class:`MachineLearningPost <lightmlrestapi.mlapp.mlpost_rest.MachineLearningPost>`
:return: app
It adds one users with the login 'me' and the password 'dummy'.
:githublink:`%|py|350`
"""
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)
if app is None:
zoo = encrypt_password("dummy", algo=algo)
data = "login,pwd\nme,{0}".format(zoo)
app = falcon.API(middleware=[AuthMiddleware(data, algo=algo)])
route = MachineLearningPost(
lambda clf=clf: clf, LogisticRegression.predict_proba, ccall='multi', **params)
# Let's check it works.
one = clf.predict_proba(X[:1])
two = route.check_single(X[0])
if type(one) != type(two): # pylint: disable=C0123
raise RuntimeError("Type mismath")
app.add_route('/', route)
return app
[docs]def dummy_mlstorage(app=None, **params):
"""
Defines a dummy application using this API.
It stores a model and it returns a score produced by a model trained
on `Iris datasets <http://scikit-learn.org/stable/auto_examples/datasets/plot_iris_dataset.html>`_
and two features.
:param app: application, if None, creates one
:param params: parameters sent to :class:`MLStoragePost <lightmlrestapi.mlapp.mlstorage_rest.MLStoragePost>`
:return: app
:githublink:`%|py|388`
"""
if app is None:
app = falcon.API()
route = MLStoragePost(**params)
app.add_route('/', route)
return app