From 4ee61028fec1c42ea1ebfcc83b7205078dde77d3 Mon Sep 17 00:00:00 2001 From: Maxime Fournes Date: Wed, 14 Feb 2018 10:33:07 +0000 Subject: [PATCH 1/5] merge --- wrappers/python/seldon_requirements.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/wrappers/python/seldon_requirements.txt b/wrappers/python/seldon_requirements.txt index 18f8f76ba9..f4d24aaff5 100644 --- a/wrappers/python/seldon_requirements.txt +++ b/wrappers/python/seldon_requirements.txt @@ -1,6 +1,5 @@ numpy==1.11.2 pandas==0.18.1 -grpc==0.3.post19 grpcio==1.1.3 Flask==0.11.1 futures From d6d1d6984037379a8994c204467cac1bcf4705dc Mon Sep 17 00:00:00 2001 From: Maxime Fournes Date: Wed, 14 Feb 2018 14:06:44 +0000 Subject: [PATCH 2/5] Making wrapper compatible with python 3. --- .../models/mean_classifier/requirements.txt | 2 -- wrappers-docker/Makefile | 2 +- wrappers/Makefile | 2 +- wrappers/python/microservice.py | 2 +- wrappers/python/model_microservice.py | 4 ++-- .../python/outlier_detector_microservice.py | 6 +++--- wrappers/python/persistence.py | 7 ++++++- wrappers/python/transformer_microservice.py | 4 ++-- wrappers/python/wrap_model.py | 2 +- wrappers/tester.py | 20 +++++++++---------- 10 files changed, 27 insertions(+), 24 deletions(-) diff --git a/examples/models/mean_classifier/requirements.txt b/examples/models/mean_classifier/requirements.txt index 088485729f..a1b52aa895 100644 --- a/examples/models/mean_classifier/requirements.txt +++ b/examples/models/mean_classifier/requirements.txt @@ -1,4 +1,2 @@ numpy==1.11.2 -scikit-learn==0.17.1 pandas==0.18.1 -scipy==0.18.1 diff --git a/wrappers-docker/Makefile b/wrappers-docker/Makefile index 1d08179382..e6d6840f93 100644 --- a/wrappers-docker/Makefile +++ b/wrappers-docker/Makefile @@ -1,5 +1,5 @@ IMAGE_NAME=docker.io/seldonio/core-python-wrapper -IMAGE_VERSION=0.6 +IMAGE_VERSION=0.7 SELDON_CORE_DIR=.. diff --git a/wrappers/Makefile b/wrappers/Makefile index ed324b62dd..0995f3ad03 100644 --- a/wrappers/Makefile +++ b/wrappers/Makefile @@ -2,5 +2,5 @@ update_protos: cp ../proto/prediction.proto ./python/proto build_protos: - python -m grpc.tools.protoc -I./python/proto --python_out=./python/proto --grpc_python_out=./python/proto ./python/proto/prediction.proto + python -m grpc.tools.protoc -I./python --python_out=./python --grpc_python_out=./python ./python/proto/prediction.proto diff --git a/wrappers/python/microservice.py b/wrappers/python/microservice.py index be3e8c7338..14a1c1a77b 100644 --- a/wrappers/python/microservice.py +++ b/wrappers/python/microservice.py @@ -180,6 +180,6 @@ def parse_parameters(parameters): server.add_insecure_port("0.0.0.0:{}".format(port)) server.start() - print "GRPC Microservice Running on port {}".format(port) + print("GRPC Microservice Running on port {}".format(port)) while True: time.sleep(1000) diff --git a/wrappers/python/model_microservice.py b/wrappers/python/model_microservice.py index 0254fd6d93..be9b6566fb 100644 --- a/wrappers/python/model_microservice.py +++ b/wrappers/python/model_microservice.py @@ -36,8 +36,8 @@ def get_rest_microservice(user_model,debug=False): @app.errorhandler(SeldonMicroserviceException) def handle_invalid_usage(error): response = jsonify(error.to_dict()) - print "ERROR:" - print error.to_dict() + print("ERROR:") + print(error.to_dict()) response.status_code = 400 return response diff --git a/wrappers/python/outlier_detector_microservice.py b/wrappers/python/outlier_detector_microservice.py index ae658c79ff..fdf0042ce5 100644 --- a/wrappers/python/outlier_detector_microservice.py +++ b/wrappers/python/outlier_detector_microservice.py @@ -27,8 +27,8 @@ def get_rest_microservice(user_model,debug=False): @app.errorhandler(SeldonMicroserviceException) def handle_invalid_usage(error): response = jsonify(error.to_dict()) - print "ERROR:" - print error.to_dict() + print("ERROR:") + print(error.to_dict()) response.status_code = 400 return response @@ -67,7 +67,7 @@ def TransformInput(self,request,context): outlier_score = score(self.user_model,features,datadef.names) request.meta.tags["outlierScore"] = outlier_score - + return request def get_grpc_server(user_model,debug=False): diff --git a/wrappers/python/persistence.py b/wrappers/python/persistence.py index 28a4f824b8..eedd1ddf90 100644 --- a/wrappers/python/persistence.py +++ b/wrappers/python/persistence.py @@ -1,7 +1,12 @@ import threading import os import time -import cPickle as pickle +try: + # python 2 + import cPickle as pickle +except ImportError: + # python 3 + import pickle import redis diff --git a/wrappers/python/transformer_microservice.py b/wrappers/python/transformer_microservice.py index 8a2b0ed7e2..5a75ef1088 100644 --- a/wrappers/python/transformer_microservice.py +++ b/wrappers/python/transformer_microservice.py @@ -48,8 +48,8 @@ def get_rest_microservice(user_model,debug=False): @app.errorhandler(SeldonMicroserviceException) def handle_invalid_usage(error): response = jsonify(error.to_dict()) - print "ERROR:" - print error.to_dict() + print("ERROR:") + print(error.to_dict()) response.status_code = 400 return response diff --git a/wrappers/python/wrap_model.py b/wrappers/python/wrap_model.py index e464318352..50f047e023 100644 --- a/wrappers/python/wrap_model.py +++ b/wrappers/python/wrap_model.py @@ -15,7 +15,7 @@ def wrap_model( **wrapping_arguments): if os.path.isdir(build_folder): if not force_erase: - print "Build folder already exists. To force erase, use --force argument" + print("Build folder already exists. To force erase, use --force argument") exit(0) else: shutil.rmtree(build_folder) diff --git a/wrappers/tester.py b/wrappers/tester.py index 82a4695390..4e07bb6ef0 100644 --- a/wrappers/tester.py +++ b/wrappers/tester.py @@ -123,13 +123,13 @@ def run(args): for i in range(args.n_requests): batch = generate_batch(contract,args.batch_size) if args.prnt: - print '-'*40 - print "SENDING NEW REQUEST:" + print('-'*40) + print("SENDING NEW REQUEST:") if not args.grpc: REST_request = gen_REST_request(batch,features=feature_names,tensor=args.tensor) if args.prnt: - print REST_request + print(REST_request) response = requests.post( REST_url, @@ -137,22 +137,22 @@ def run(args): jresp = response.json() if args.prnt: - print "RECEIVED RESPONSE:" - print jresp - print + print("RECEIVED RESPONSE:") + print(jresp) + print() else: GRPC_request = gen_GRPC_request(batch,features=feature_names,tensor=args.tensor) if args.prnt: - print GRPC_request + print(GRPC_request) channel = grpc.insecure_channel('{}:{}'.format(args.host,args.port)) stub = prediction_pb2_grpc.ModelStub(channel) response = stub.Predict(GRPC_request) if args.prnt: - print "RECEIVED RESPONSE:" - print response - print + print("RECEIVED RESPONSE:") + print(response) + print() if __name__ == "__main__": From ed2907728c5411d86e2ed5c342d3d3cb5648b39f Mon Sep 17 00:00:00 2001 From: Clive Cox Date: Wed, 14 Feb 2018 18:35:28 +0000 Subject: [PATCH 3/5] fix typo in python docs for wrapper args --- docs/wrappers/python.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/wrappers/python.md b/docs/wrappers/python.md index a975bcc7dc..5fd4159d8f 100644 --- a/docs/wrappers/python.md +++ b/docs/wrappers/python.md @@ -97,10 +97,10 @@ Required: * docker_repo: The name of your dockerhub repository. In our example seldonio. Optional: -* out_folder: The folder that will be created to contain the output files. Defaults to ./build -* service_type: The type of Seldon Service API the model will use. Defaults to MODEL. Other options are ROUTER, COMBINER, TRANSFORMER, OUTPUT_TRANSFORMER -* base_image: The docker image your docker container will inherit from. Defaults to python:2. -* image_name: The name of your docker image. Defaults to model_name in lowercase +* out-folder: The folder that will be created to contain the output files. Defaults to ./build +* service-type: The type of Seldon Service API the model will use. Defaults to MODEL. Other options are ROUTER, COMBINER, TRANSFORMER, OUTPUT_TRANSFORMER +* base-image: The docker image your docker container will inherit from. Defaults to python:2. +* image-name: The name of your docker image. Defaults to model_name in lowercase * force: When this flag is present, the build folder will be overwritten if it already exists. The wrapping is aborted by default. * persistence: When this flag is present, the model will be made persistent, its state being saved at a regular interval on redis. * grpc: When this flag is present, the model will expose a GRPC API rather than the default REST API From b778da4953671f1e15d7de91199a6f73592318ca Mon Sep 17 00:00:00 2001 From: Clive Cox Date: Wed, 14 Feb 2018 18:35:53 +0000 Subject: [PATCH 4/5] make MeanClassifier python 3 --- examples/models/mean_classifier/MeanClassifier.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/examples/models/mean_classifier/MeanClassifier.py b/examples/models/mean_classifier/MeanClassifier.py index 42cd333589..f5cb2d379e 100644 --- a/examples/models/mean_classifier/MeanClassifier.py +++ b/examples/models/mean_classifier/MeanClassifier.py @@ -11,15 +11,16 @@ def __init__(self, intValue=0): assert type(intValue) == int, "intValue parameters must be an integer" self.int_value = intValue - print "Loading model here" - X = np.load(open("model.npy",'r')) + print("Loading model here") + + X = np.load(open("model.npy",'rb'), encoding='latin1') self.threshold_ = X.mean() + self.int_value def _meaning(self, x): return f(x.mean()-self.threshold_) def predict(self, X, feature_names): - print X + print(X) X = np.array(X) assert len(X.shape) == 2, "Incorrect shape" From a17f035c9f96c0a89d6e0a8dc3a1f52f3030ae53 Mon Sep 17 00:00:00 2001 From: Maxime Fournes Date: Thu, 15 Feb 2018 11:32:38 +0000 Subject: [PATCH 5/5] Updated documentation to refer to wrapper 0.7 --- docs/getting_started/minikube.md | 2 +- docs/wrappers/h2o.md | 4 ++-- docs/wrappers/python.md | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/getting_started/minikube.md b/docs/getting_started/minikube.md index 68fa7640e9..2a3bb21444 100644 --- a/docs/getting_started/minikube.md +++ b/docs/getting_started/minikube.md @@ -72,7 +72,7 @@ In this session, we show how to wrap the sklearn iris classifier in the [seldon- 3. Wrap your saved model using the core-python-wrapper docker image: ```bash - docker run -v $(pwd):/model seldonio/core-python-wrapper:0.6 /model IrisClassifier 0.1 seldonio --force + docker run -v $(pwd):/model seldonio/core-python-wrapper:0.7 /model IrisClassifier 0.1 seldonio --force ``` 4. Build the docker image locally diff --git a/docs/wrappers/h2o.md b/docs/wrappers/h2o.md index aeb9136494..f67c2e854d 100644 --- a/docs/wrappers/h2o.md +++ b/docs/wrappers/h2o.md @@ -44,7 +44,7 @@ Detailed steps: 3. Run the python wrapping scripts, with the additional ````--base-image``` argument: ```bash - docker run -v /path/to/your/model/folder:/model seldonio/core-python-wrapper:0.6 /model H2OModel 0.1 myrepo --base-image=H2OBase:1.0 + docker run -v /path/to/your/model/folder:/model seldonio/core-python-wrapper:0.7 /model H2OModel 0.1 myrepo --base-image=H2OBase:1.0 ``` "0.1" is the version of the docker image that will be created. "myrepo" is the name of your dockerhub repository. @@ -88,7 +88,7 @@ Here we give a step by step example in which we will train and save a [H2O model ```bash cd ../../ - docker run -v models/h2o_example:my_model seldonio/core-python-wrapper:0.6 my_model H2OModel 0.1 myrepo --base-image=H2OBase:1.0 + docker run -v models/h2o_example:my_model seldonio/core-python-wrapper:0.7 my_model H2OModel 0.1 myrepo --base-image=H2OBase:1.0 ``` This will create a docker image "seldonio/h2omodel:0.1", which is ready to be deployed in seldon-core. diff --git a/docs/wrappers/python.md b/docs/wrappers/python.md index 5fd4159d8f..d350c31a1d 100644 --- a/docs/wrappers/python.md +++ b/docs/wrappers/python.md @@ -61,13 +61,13 @@ After you have copied the required files in your model folder, you run the Seldo In order to make things even simpler (and because we love Docker!) we have dockerised the wrapper script so that you don't need to install anything on your machine to run it - except Docker. ``` -docker run -v /path/to/model/dir:/my_model seldonio/core-python-wrapper:0.6 /my_model MnistClassifier 0.1 seldonio +docker run -v /path/to/model/dir:/my_model seldonio/core-python-wrapper:0.7 /my_model MnistClassifier 0.1 seldonio ``` Let's explain each piece of this command in more details. -``` docker run seldonio/core-python-wrapper:0.6 ``` : run the core-python-wrapper container (version 0.6) +``` docker run seldonio/core-python-wrapper:0.7 ``` : run the core-python-wrapper container. ``` -v /path/to/model/dir:/my_model ``` : Tells docker to mount your local folder to /my_model in the container. This is used to access your files and generate the wrapped model files. @@ -76,7 +76,7 @@ Let's explain each piece of this command in more details. For reference, here is the complete list of arguments that can be passed to the script. ``` -docker run -v /path: seldonio/core-python-wrapper:0.6 +docker run -v /path: seldonio/core-python-wrapper:0.7 @@ -108,7 +108,7 @@ Optional: Note that you can access the command line help of the script by using the -h or --help argument as follows: ``` -docker run seldonio/core-python-wrapper:0.6 -h +docker run seldonio/core-python-wrapper:0.7 -h ``` Note also that you could use the python script directly if you feel so enclined, but you would have to check out seldon-core and install some python libraries on your local machine - by using the docker image you don't have to care about these dependencies.