From ded0b225e2f150803ba9ba10995da638caa56f97 Mon Sep 17 00:00:00 2001 From: Gurminder Sunner Date: Mon, 4 Jun 2018 10:59:19 +0100 Subject: [PATCH 01/18] Updated CHANGELOG for 0.1.7 --- CHANGELOG.md | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ed2ec3848..db5e9f6c85 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,34 @@ # Change Log +## [v0.1.7](https://github.com/SeldonIO/seldon-core/tree/v0.1.7) (2018-06-04) +[Full Changelog](https://github.com/SeldonIO/seldon-core/compare/v0.1.6...v0.1.7) + +**Closed issues:** + +- Quickstart problem [\#153](https://github.com/SeldonIO/seldon-core/issues/153) +- NameError: global name 'ListValue' is not defined [\#148](https://github.com/SeldonIO/seldon-core/issues/148) +- bad credentials error with get\_token function [\#144](https://github.com/SeldonIO/seldon-core/issues/144) +- Make CRD Namespaced scoped [\#141](https://github.com/SeldonIO/seldon-core/issues/141) +- Create wrappers for Java based models [\#137](https://github.com/SeldonIO/seldon-core/issues/137) +- Update ksonnet prototypes for latest image version [\#130](https://github.com/SeldonIO/seldon-core/issues/130) +- Create demo notebook for Azure [\#129](https://github.com/SeldonIO/seldon-core/issues/129) +- Grafana Dashboard [\#109](https://github.com/SeldonIO/seldon-core/issues/109) +- Multiple helm seldon-core installs on separate namespaces fails [\#106](https://github.com/SeldonIO/seldon-core/issues/106) + +**Merged pull requests:** + +- Add install guide [\#156](https://github.com/SeldonIO/seldon-core/pull/156) ([cliveseldon](https://github.com/cliveseldon)) +- WIP : PySpark and PMML example [\#155](https://github.com/SeldonIO/seldon-core/pull/155) ([cliveseldon](https://github.com/cliveseldon)) +- Fix gRPC tests for wrappers and update sklearn iris example to show use [\#150](https://github.com/SeldonIO/seldon-core/pull/150) ([cliveseldon](https://github.com/cliveseldon)) +- Minikube RBAC updates and Notebooks for Model examples [\#147](https://github.com/SeldonIO/seldon-core/pull/147) ([cliveseldon](https://github.com/cliveseldon)) +- change ClusterRoleBinding to RoleBinding [\#146](https://github.com/SeldonIO/seldon-core/pull/146) ([gsunner](https://github.com/gsunner)) +- MNIST loadtest [\#143](https://github.com/SeldonIO/seldon-core/pull/143) ([cliveseldon](https://github.com/cliveseldon)) +- Openshift article on using s2i in seldon-core [\#140](https://github.com/SeldonIO/seldon-core/pull/140) ([cliveseldon](https://github.com/cliveseldon)) +- Java wrappers [\#138](https://github.com/SeldonIO/seldon-core/pull/138) ([cliveseldon](https://github.com/cliveseldon)) +- add notebook for azure demo [\#135](https://github.com/SeldonIO/seldon-core/pull/135) ([gsunner](https://github.com/gsunner)) +- update ksonnet defaults to 0.1.6 [\#131](https://github.com/SeldonIO/seldon-core/pull/131) ([cliveseldon](https://github.com/cliveseldon)) +- Typos fix [\#128](https://github.com/SeldonIO/seldon-core/pull/128) ([LevineHuang](https://github.com/LevineHuang)) + ## [v0.1.6](https://github.com/SeldonIO/seldon-core/tree/v0.1.6) (2018-03-29) [Full Changelog](https://github.com/SeldonIO/seldon-core/compare/v0.1.5...v0.1.6) From fa6666b5e3c60920c63346ff1f37dd4069b03495 Mon Sep 17 00:00:00 2001 From: Gurminder Sunner Date: Mon, 4 Jun 2018 11:07:06 +0100 Subject: [PATCH 02/18] version 0.1.8-SNAPSHOT set --- api-frontend/pom.xml | 2 +- cluster-manager/pom.xml | 2 +- engine/pom.xml | 2 +- helm-charts/seldon-core-crd/Chart.yaml | 2 +- helm-charts/seldon-core/Chart.yaml | 2 +- helm-charts/seldon-core/values.yaml | 6 +++--- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/api-frontend/pom.xml b/api-frontend/pom.xml index a5a920d50d..8da42d37ff 100644 --- a/api-frontend/pom.xml +++ b/api-frontend/pom.xml @@ -10,7 +10,7 @@ io.seldon.apife seldon-apife - 0.1.7 + 0.1.8-SNAPSHOT jar api-frontend diff --git a/cluster-manager/pom.xml b/cluster-manager/pom.xml index bb194e576a..03d2466e37 100644 --- a/cluster-manager/pom.xml +++ b/cluster-manager/pom.xml @@ -4,7 +4,7 @@ io.seldon.clustermanager seldon-cluster-manager jar - 0.1.7 + 0.1.8-SNAPSHOT seldon-cluster-manager http://maven.apache.org diff --git a/engine/pom.xml b/engine/pom.xml index 57a3a61fb6..644c03b18e 100644 --- a/engine/pom.xml +++ b/engine/pom.xml @@ -10,7 +10,7 @@ io.seldon.engine seldon-engine - 0.1.7 + 0.1.8-SNAPSHOT jar engine diff --git a/helm-charts/seldon-core-crd/Chart.yaml b/helm-charts/seldon-core-crd/Chart.yaml index 2b91823622..0874dff1a7 100644 --- a/helm-charts/seldon-core-crd/Chart.yaml +++ b/helm-charts/seldon-core-crd/Chart.yaml @@ -6,4 +6,4 @@ keywords: name: seldon-core-crd sources: - https://github.com/SeldonIO/seldon-core -version: 0.1.7 +version: 0.1.8-SNAPSHOT diff --git a/helm-charts/seldon-core/Chart.yaml b/helm-charts/seldon-core/Chart.yaml index 6c379d6556..22b5a32e85 100644 --- a/helm-charts/seldon-core/Chart.yaml +++ b/helm-charts/seldon-core/Chart.yaml @@ -6,4 +6,4 @@ keywords: name: seldon-core sources: - https://github.com/SeldonIO/seldon-core -version: 0.1.7 +version: 0.1.8-SNAPSHOT diff --git a/helm-charts/seldon-core/values.yaml b/helm-charts/seldon-core/values.yaml index 6c0df0a674..90dadeebfe 100644 --- a/helm-charts/seldon-core/values.yaml +++ b/helm-charts/seldon-core/values.yaml @@ -2,17 +2,17 @@ apife: enabled: true image: pull_policy: IfNotPresent - tag: 0.1.7 + tag: 0.1.8-SNAPSHOT apife_service_type: NodePort cluster_manager: image: pull_policy: IfNotPresent - tag: 0.1.7 + tag: 0.1.8-SNAPSHOT java_opts: '' spring_opts: '' engine: image: - tag: 0.1.7 + tag: 0.1.8-SNAPSHOT rbac: enabled: true redis: From f10d4519519e637dbc9392c7f8dcf69ead279d94 Mon Sep 17 00:00:00 2001 From: Gurminder Sunner Date: Wed, 6 Jun 2018 14:27:54 +0100 Subject: [PATCH 03/18] add update to core.jsonnet when setting version --- release.py | 41 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/release.py b/release.py index 1f5f2b0c2b..783fce6bad 100644 --- a/release.py +++ b/release.py @@ -9,6 +9,8 @@ import os import sys import argparse +import re +import shutil def pp(o): pprinter = pprint.PrettyPrinter(indent=4) @@ -97,11 +99,42 @@ def update_values_yaml_file(fpath, seldon_core_version, debug=False): print "updated {fpath}".format(**locals()) -def set_version(seldon_core_version, pom_files, chart_yaml_files, values_yaml_file, debug=False): +def update_core_jsonnet(fpath, seldon_core_version, debug=False): + # eg. + # raw_line = // @optionalParam apifeImage string seldonio/apife:0.1.6 Default image for API Front End + # srch_str = seldonio/apife + # seldon_core_version = 1.2.3 + # return = // @optionalParam apifeImage string seldonio/apife:1.2.3 Default image for API Front End + def get_output_line(raw_line, srch_str): + if raw_line.find('%s:' % srch_str) > 0: + return re.sub( r" (%s):.*? " % srch_str, r" \1:%s " % seldon_core_version, raw_line) + else: + return raw_line + + fpath = os.path.realpath(fpath) + if debug: + print "processing [{}]".format(fpath) + + tmpfpath = fpath+'.tmp' + with open(fpath, 'r') as f: + with open(tmpfpath, 'w') as ftmp: + for raw_line in f: + output_line = raw_line + for srch_str in ['seldonio/apife','seldonio/cluster-manager', 'seldonio/engine']: + if raw_line.find(srch_str + ':') > 0: + output_line = get_output_line(raw_line, srch_str) + ftmp.write(output_line) + if debug: + print "created {tmpfpath}".format(**locals()) + shutil.move(tmpfpath, fpath) # move created tmp file to original file + print "updated {fpath}".format(**locals()) + +def set_version(seldon_core_version, pom_files, chart_yaml_files, values_yaml_file, core_jsonnet_file, debug=False): # Normalize file paths pom_files_realpaths = [os.path.realpath(x) for x in pom_files] chart_yaml_file_realpaths = [os.path.realpath(x) for x in chart_yaml_files] values_yaml_file_realpath = os.path.realpath(values_yaml_file) if values_yaml_file != None else None + core_jsonnet_file_realpath = os.path.realpath(core_jsonnet_file) if core_jsonnet_file != None else None # update the pom files for fpath in pom_files_realpaths: @@ -115,15 +148,19 @@ def set_version(seldon_core_version, pom_files, chart_yaml_files, values_yaml_fi if values_yaml_file != None: update_values_yaml_file(values_yaml_file_realpath, seldon_core_version, debug) + # update the jsonnet file + update_core_jsonnet(core_jsonnet_file_realpath, seldon_core_version, debug) + def main(argv): POM_FILES = ['engine/pom.xml', 'api-frontend/pom.xml', 'cluster-manager/pom.xml'] CHART_YAML_FILES = ['helm-charts/seldon-core/Chart.yaml', 'helm-charts/seldon-core-crd/Chart.yaml'] VALUES_YAML_FILE = 'helm-charts/seldon-core/values.yaml' + CORE_JSONNET_FILE = 'seldon-core/seldon-core/prototypes/core.jsonnet' opts = getOpts(argv[1:]) if opts.debug: pp(opts) - set_version(opts.seldon_core_version, POM_FILES, CHART_YAML_FILES, VALUES_YAML_FILE, opts.debug) + set_version(opts.seldon_core_version, POM_FILES, CHART_YAML_FILES, VALUES_YAML_FILE, CORE_JSONNET_FILE, opts.debug) print "done" if __name__ == "__main__": From 9e515612f9ede5d8d26cb7cead6be4a74f14961a Mon Sep 17 00:00:00 2001 From: Gurminder Sunner Date: Wed, 6 Jun 2018 17:03:14 +0100 Subject: [PATCH 04/18] release script updated for python3 --- core-builder/Dockerfile | 5 +++-- core-builder/Makefile | 2 +- release | 2 +- release.py | 32 ++++++++++++++++---------------- 4 files changed, 21 insertions(+), 20 deletions(-) diff --git a/core-builder/Dockerfile b/core-builder/Dockerfile index 128f1739a4..812b2c4987 100644 --- a/core-builder/Dockerfile +++ b/core-builder/Dockerfile @@ -27,9 +27,10 @@ RUN \ # dependencies for release script RUN \ + # install env for python3 apt-get update -y && \ - apt-get install -y python-pip && \ - pip install pyyaml && \ + apt-get install -y python3-pip && \ + pip3 install pyyaml && \ apt-get remove -y --auto-remove && apt-get clean -y && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* WORKDIR /work diff --git a/core-builder/Makefile b/core-builder/Makefile index 678ee03bb3..2af9fad769 100644 --- a/core-builder/Makefile +++ b/core-builder/Makefile @@ -1,5 +1,5 @@ DOCKER_IMAGE_NAME=seldonio/core-builder -DOCKER_IMAGE_VERSION=0.2 +DOCKER_IMAGE_VERSION=0.3 build_docker_image: docker build --force-rm=true -t $(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_VERSION) . diff --git a/release b/release index c356c89fc1..9874e82f68 100755 --- a/release +++ b/release @@ -9,5 +9,5 @@ STARTUP_DIR="$( cd "$( dirname "$0" )" && pwd )" docker run --rm -it \ -v ${HOME}/.m2:/root/.m2 \ -v "${STARTUP_DIR}":/work \ - seldonio/core-builder:0.2 python release.py "$@" + seldonio/core-builder:0.3 python3 release.py "$@" diff --git a/release.py b/release.py index 783fce6bad..5512b8e19c 100644 --- a/release.py +++ b/release.py @@ -3,7 +3,7 @@ # import yaml -import StringIO +from io import StringIO import pprint from subprocess import Popen, PIPE import os @@ -27,13 +27,13 @@ def dict_to_yaml(d): return yaml.dump(d, default_flow_style=False) def yaml_to_dict(yaml_data): - return yaml.load(StringIO.StringIO(yaml_data)) + return yaml.load(StringIO(yaml_data)) def run_command(args, debug=False): err, out = None, None if debug: - print "cwd[{}]".format(os.getcwd()) - print "Executing: " + repr(args) + print("cwd[{}]".format(os.getcwd())) + print("Executing: " + repr(args)) p = Popen(args, stdout=PIPE, stderr=PIPE) if p.wait() == 0: out = p.stdout.read() @@ -51,7 +51,7 @@ def run_command(args, debug=False): def update_pom_file(fpath, seldon_core_version, debug=False): fpath = os.path.realpath(fpath) if debug: - print "processing [{}]".format(fpath) + print("processing [{}]".format(fpath)) comp_dir_path = os.path.dirname(fpath) os.chdir(comp_dir_path) args = ["mvn", "versions:set", "-DnewVersion={seldon_core_version}".format(**locals())] @@ -59,15 +59,15 @@ def update_pom_file(fpath, seldon_core_version, debug=False): ##pp(out) ##pp(err) if err == None: - print "updated {fpath}".format(**locals()) + print("updated {fpath}".format(**locals())) else: - print "error {fpath}".format(**locals()) - print err + print("error {fpath}".format(**locals())) + print(err) def update_chart_yaml_file(fpath, seldon_core_version, debug=False): fpath = os.path.realpath(fpath) if debug: - print "processing [{}]".format(fpath) + print("processing [{}]".format(fpath)) f = open(fpath) yaml_data = f.read() f.close() @@ -78,13 +78,13 @@ def update_chart_yaml_file(fpath, seldon_core_version, debug=False): with open(fpath, 'w') as f: f.write(dict_to_yaml(d)) - print "updated {fpath}".format(**locals()) + print("updated {fpath}".format(**locals())) def update_values_yaml_file(fpath, seldon_core_version, debug=False): fpath = os.path.realpath(fpath) if debug: - print "processing [{}]".format(fpath) + print("processing [{}]".format(fpath)) f = open(fpath) yaml_data = f.read() f.close() @@ -97,7 +97,7 @@ def update_values_yaml_file(fpath, seldon_core_version, debug=False): with open(fpath, 'w') as f: f.write(dict_to_yaml(d)) - print "updated {fpath}".format(**locals()) + print("updated {fpath}".format(**locals())) def update_core_jsonnet(fpath, seldon_core_version, debug=False): # eg. @@ -113,7 +113,7 @@ def get_output_line(raw_line, srch_str): fpath = os.path.realpath(fpath) if debug: - print "processing [{}]".format(fpath) + print("processing [{}]".format(fpath)) tmpfpath = fpath+'.tmp' with open(fpath, 'r') as f: @@ -125,9 +125,9 @@ def get_output_line(raw_line, srch_str): output_line = get_output_line(raw_line, srch_str) ftmp.write(output_line) if debug: - print "created {tmpfpath}".format(**locals()) + print("created {tmpfpath}".format(**locals())) shutil.move(tmpfpath, fpath) # move created tmp file to original file - print "updated {fpath}".format(**locals()) + print("updated {fpath}".format(**locals())) def set_version(seldon_core_version, pom_files, chart_yaml_files, values_yaml_file, core_jsonnet_file, debug=False): # Normalize file paths @@ -161,7 +161,7 @@ def main(argv): if opts.debug: pp(opts) set_version(opts.seldon_core_version, POM_FILES, CHART_YAML_FILES, VALUES_YAML_FILE, CORE_JSONNET_FILE, opts.debug) - print "done" + print("done") if __name__ == "__main__": main(sys.argv) From bb65ff107467ffc2a28774a03c1b1d60f252f5b4 Mon Sep 17 00:00:00 2001 From: Clive Cox Date: Wed, 6 Jun 2018 17:06:39 +0100 Subject: [PATCH 05/18] handle string and number values in h2o wrapper utils. Update wrapper to 0.1.1 release and change examples to use this release --- examples/models/h2o_mojo/contract.json | 6 ++--- examples/models/h2o_mojo/pom.xml | 7 ++--- examples/models/pyspark_pmml/pom.xml | 12 ++------- wrappers/s2i/java/wrapper/pom.xml | 2 +- .../io/seldon/wrapper/utils/H2OUtils.java | 27 ++++++++++++++++++- wrappers/testing/tester.py | 10 +++++-- 6 files changed, 44 insertions(+), 20 deletions(-) diff --git a/examples/models/h2o_mojo/contract.json b/examples/models/h2o_mojo/contract.json index 85e2407be3..831a63fa25 100644 --- a/examples/models/h2o_mojo/contract.json +++ b/examples/models/h2o_mojo/contract.json @@ -9,17 +9,17 @@ { "name":"RACE", "ftype":"categorical", - "values":[0,1,2] + "values":["0","1","2"] }, { "name":"DCAPS", "ftype":"categorical", - "values":[0,1,2] + "values":["0","1","2"] }, { "name":"VOL", "ftype":"categorical", - "values":[0,1,2] + "values":["0","1","2"] }, { "name":"GLEASON", diff --git a/examples/models/h2o_mojo/pom.xml b/examples/models/h2o_mojo/pom.xml index f4784c4803..51c87b28e0 100644 --- a/examples/models/h2o_mojo/pom.xml +++ b/examples/models/h2o_mojo/pom.xml @@ -1,4 +1,5 @@ - 4.0.0 io.seldon.example.h2o @@ -48,7 +49,7 @@ - + @@ -73,7 +74,7 @@ io.seldon.wrapper seldon-core-wrapper - 0.1.0 + 0.1.1 diff --git a/examples/models/pyspark_pmml/pom.xml b/examples/models/pyspark_pmml/pom.xml index a1e373f1de..972a4b7a67 100644 --- a/examples/models/pyspark_pmml/pom.xml +++ b/examples/models/pyspark_pmml/pom.xml @@ -53,15 +53,7 @@ - - - snapshots-repo - https://oss.sonatype.org/content/repositories/snapshots - false - true - - - + @@ -86,7 +78,7 @@ io.seldon.wrapper seldon-core-wrapper - 0.1.1-SNAPSHOT + 0.1.1 org.jpmml diff --git a/wrappers/s2i/java/wrapper/pom.xml b/wrappers/s2i/java/wrapper/pom.xml index 11b191d920..157e92f74a 100644 --- a/wrappers/s2i/java/wrapper/pom.xml +++ b/wrappers/s2i/java/wrapper/pom.xml @@ -5,7 +5,7 @@ io.seldon.wrapper seldon-core-wrapper jar - 0.1.1-SNAPSHOT + 0.1.1 Seldon Core Java Wrapper http://maven.apache.org Wrapper for seldon-core Java prediction models. Allows easy creation of a Spring Boot app with Tomcat and gRPC servers for handling the microservice APIs for seldon-core. diff --git a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/utils/H2OUtils.java b/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/utils/H2OUtils.java index 71b3ee1ca7..9c8a956d3f 100644 --- a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/utils/H2OUtils.java +++ b/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/utils/H2OUtils.java @@ -3,6 +3,8 @@ import java.util.ArrayList; import java.util.List; +import org.nd4j.linalg.dataset.api.iterator.CachingDataSetIterator; + import com.google.protobuf.ListValue; import com.google.protobuf.Value; @@ -68,7 +70,30 @@ public static List convertSeldonMessage(DefaultData data) { RowData row = new RowData(); for (int j = 0; j < cols; j++) { String name = data.getNamesCount() > 0 ? data.getNames(j % cols) : "" + (j % cols); - Double value = list.getValues(i).getListValue().getValues(j).getNumberValue(); + Object value; + Value listValue = list.getValues(i).getListValue().getValues(j); + switch(listValue.getKindCase()) { + case NUMBER_VALUE: + value = listValue.getNumberValue(); + break; + case STRING_VALUE: + value = listValue.getStringValue(); + break; + case BOOL_VALUE: + //Get value as String + value = listValue.getStringValue(); + break; + case NULL_VALUE: + // Treat Nulls as 0 + value = 0.0; + break; + case LIST_VALUE: + throw new UnsupportedOperationException("Only 2-D arrays unsupported for H2O conversion"); + case STRUCT_VALUE: + throw new UnsupportedOperationException("Struct in NDArray unsupported for H2O conversion"); + default: + throw new UnsupportedOperationException("Unknown kind in NDArray"); + } row.put(name, value); } out.add(row); diff --git a/wrappers/testing/tester.py b/wrappers/testing/tester.py index 80c9c55a0e..c5a3b5a43b 100644 --- a/wrappers/testing/tester.py +++ b/wrappers/testing/tester.py @@ -37,11 +37,13 @@ def reconciliate_cont_type(feature,dtype): def gen_categorical(values,n): vals = np.random.randint(len(values),size=n) - return np.array(values)[vals].astype(float) + return np.array(values)[vals] def generate_batch(contract,n): feature_batches = [] + ty_set = set() for feature_def in contract['features']: + ty_set.add(feature_def["ftype"]) if feature_def["ftype"] == "continuous": if "range" in feature_def: range = feature_def["range"] @@ -53,7 +55,11 @@ def generate_batch(contract,n): elif feature_def["ftype"] == "categorical": batch = gen_categorical(feature_def["values"],n) feature_batches.append(batch[:,None]) - return np.concatenate(feature_batches,axis=1) + if len(ty_set) == 1: + return np.concatenate(feature_batches,axis=1) + else: + out = np.empty((n,len(contract['features'])),dtype=object) + return np.concatenate(feature_batches,axis=1,out=out) def gen_REST_request(batch,features,tensor=True): if tensor: From 784c1e4a9db6b3af5140b870d550321d898be66d Mon Sep 17 00:00:00 2001 From: Clive Cox Date: Thu, 7 Jun 2018 11:34:04 +0100 Subject: [PATCH 06/18] Add recent Kubecon and Openshift videos to readme --- readme.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/readme.md b/readme.md index d228725466..4c4a34e247 100644 --- a/readme.md +++ b/readme.md @@ -106,8 +106,10 @@ Three steps: - [Prediction API](./docs/reference/prediction.md) - [Seldon Deployment Custom Resource](./docs/reference/seldon-deployment.md) -## Articles +## Articles/Blogs/Videos + - [Kubecon Europe 2018 - Serving Machine Learning Models at Scale with Kubeflow and Seldon](https://www.youtube.com/watch?v=pDlapGtecbY) + - [Openshift Commons ML SIG - Openshift S2I Helping ML Deployment with Seldon-Core](https://www.youtube.com/watch?v=1uZPBcfYxlM) - [Overview of Openshift source-to-image use in Seldon-Core](./docs/articles/openshift_s2i.md) ## Testing From 0ae6bef6e5dc1dc768903d8591383e0b79dd825f Mon Sep 17 00:00:00 2001 From: Clive Cox Date: Tue, 12 Jun 2018 22:58:28 +0100 Subject: [PATCH 07/18] Add IBM FfDL article to readme --- readme.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 4c4a34e247..78d0f42fb9 100644 --- a/readme.md +++ b/readme.md @@ -111,7 +111,8 @@ Three steps: - [Kubecon Europe 2018 - Serving Machine Learning Models at Scale with Kubeflow and Seldon](https://www.youtube.com/watch?v=pDlapGtecbY) - [Openshift Commons ML SIG - Openshift S2I Helping ML Deployment with Seldon-Core](https://www.youtube.com/watch?v=1uZPBcfYxlM) - [Overview of Openshift source-to-image use in Seldon-Core](./docs/articles/openshift_s2i.md) - + - [IBM Framework for Deep Learning and Seldon-Core](https://developer.ibm.com/code/2018/06/12/serve-it-hot-deploy-your-ffdl-trained-models-using-seldon/) + ## Testing - [Benchmarking seldon-core](docs/benchmarking.md) From d5169d223109b947962d899818a3b99296445140 Mon Sep 17 00:00:00 2001 From: Clive Cox Date: Wed, 13 Jun 2018 08:14:55 +0100 Subject: [PATCH 08/18] Add pyTorch FfDL link to readme --- readme.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/readme.md b/readme.md index 78d0f42fb9..12b5de8378 100644 --- a/readme.md +++ b/readme.md @@ -10,6 +10,8 @@ Seldon Core is an open source platform for deploying machine learning models on - [Goals](#goals) - [Prerequisites](#prerequisites) - [Quick Start](#quick-start) +- [Example Components](#example-components) +- [Integrations](#integrations) - [Install](#install) - [Deployment guide](#deployment-guide) - [Reference](#reference) @@ -85,6 +87,9 @@ Seldon-core allows various types of components to be built and plugged into the * Seldon-core can be installed as part of the kubeflow project. A detailed [end-to-end example](https://github.com/kubeflow/example-seldon) provides a complete workflow for training various models and deploying them using seldon-core. * [IBM's Fabric for Deep Learning](https://github.com/IBM/FfDL) * Seldon-core can be used to [serve deep learning models trained using FfDL](https://github.com/IBM/FfDL/blob/master/community/FfDL-Seldon/README.md). + * [Train and deploy a Tensorflow MNIST classififer using FfDL and Seldon.](https://github.com/IBM/FfDL/blob/master/community/FfDL-Seldon/tf-model/README.md) + * [Train and deploy a PyTorch MNIST classififer using FfDL and Seldon.](https://github.com/IBM/FfDL/blob/master/community/FfDL-Seldon/pytorch-model/README.md) + ## Install From 40c7e19b235318a1ac4c9c28f836ea5da062a9ca Mon Sep 17 00:00:00 2001 From: Clive Cox Date: Wed, 13 Jun 2018 18:17:41 +0100 Subject: [PATCH 09/18] update notebooks for minikube and ambassador --- docs/getting_started/minikube.md | 8 - docs/readme.md | 5 - notebooks/ksonnet_ambassador_gcp.ipynb | 118 +--- notebooks/ksonnet_ambassador_minikube.ipynb | 241 +++----- notebooks/kubectl_demo_minikube.ipynb | 598 -------------------- notebooks/kubectl_demo_minikube_rbac.ipynb | 71 ++- notebooks/resources/ambassador-rbac.yaml | 84 +-- notebooks/resources/ambassador-service.yaml | 15 - readme.md | 2 +- 9 files changed, 184 insertions(+), 958 deletions(-) delete mode 100644 notebooks/kubectl_demo_minikube.ipynb delete mode 100644 notebooks/resources/ambassador-service.yaml diff --git a/docs/getting_started/minikube.md b/docs/getting_started/minikube.md index abbcf1f909..52940ef7f2 100644 --- a/docs/getting_started/minikube.md +++ b/docs/getting_started/minikube.md @@ -213,12 +213,4 @@ The response contains: * "names": The names of your classes. * "ndarray": The predicted probabilities for each class. -## Next Steps - - * You can run several notebooks that show various examples on minikube and Google cloud platform - * [Jupyter Notebook showing deployment of prebuilt model using Minikube](https://github.com/SeldonIO/seldon-core/blob/master/notebooks/kubectl_demo_minikube.ipynb) - * [Jupyter Notebook showing deployment of prebuilt model using GCP cluster](https://github.com/SeldonIO/seldon-core/blob/master/notebooks/kubectl_demo_gcp.ipynb) - * [Epsilon-greedy multi-armed bandits for real time optimization of models](https://github.com/SeldonIO/seldon-core/blob/master/notebooks/epsilon_greedy_gcp.ipynb) - * [Advanced graphs showing the various types of runtime prediction graphs that can be built](https://github.com/cliveseldon/seldon-core/blob/master/notebooks/advanced_graphs.ipynb) - * [Jupyter notebook to create seldon-core with ksonnet and expose APIs using Ambassador.](https://github.com/SeldonIO/seldon-core/blob/master/notebooks/ksonnet_ambassador_minikube.ipynb) diff --git a/docs/readme.md b/docs/readme.md index c8e24febea..68fbf430c6 100644 --- a/docs/readme.md +++ b/docs/readme.md @@ -1,10 +1,5 @@ # Seldon Documentation -## Quick Start - - - [Quick Start using Minikube](./getting_started/minikube.md) - - [Jupyter Notebook showing deployment of prebuilt model](https://github.com/SeldonIO/seldon-core/blob/master/notebooks/kubectl_demo_minikube.ipynb) - ## Deployment Guide ![API](./deploy.png) diff --git a/notebooks/ksonnet_ambassador_gcp.ipynb b/notebooks/ksonnet_ambassador_gcp.ipynb index 7f9efd3067..cd495315c1 100644 --- a/notebooks/ksonnet_ambassador_gcp.ipynb +++ b/notebooks/ksonnet_ambassador_gcp.ipynb @@ -30,9 +30,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "!kubectl create namespace seldon" @@ -53,9 +51,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "!kubectl create clusterrolebinding my-cluster-admin-binding --clusterrole=cluster-admin --user=$(gcloud info --format=\"value(config.account)\")" @@ -64,20 +60,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "!kubectl apply -f resources/ambassador-service.yaml -n seldon" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "!kubectl apply -f resources/ambassador-rbac.yaml -n seldon" @@ -94,9 +77,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "!ks init my-ml-deployment --api-spec=version:v1.8.0" @@ -105,9 +86,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "!cd my-ml-deployment && \\\n", @@ -119,9 +98,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "!cd my-ml-deployment && \\\n", @@ -145,9 +122,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "!cp ../proto/prediction.proto ./proto\n", @@ -164,9 +139,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "import requests\n", @@ -228,9 +201,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "!kubectl get pods -n seldon" @@ -239,9 +210,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "!kubectl apply -f resources/model.json -n seldon" @@ -257,9 +226,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "!kubectl get seldondeployments seldon-deployment-example -o jsonpath='{.status}' -n seldon" @@ -288,9 +255,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "rest_request(\"seldon-deployment-example\")" @@ -306,9 +271,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "grpc_request(\"seldon-deployment-example\")" @@ -325,9 +288,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "!kubectl apply -f resources/ambassador-auth-service-setup.yaml -n seldon" @@ -343,9 +304,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "!kubectl get pods -n seldon" @@ -354,9 +313,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "!kubectl apply -f resources/ambassador-auth-service-config.yaml -n seldon" @@ -372,9 +329,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "rest_request(\"seldon-deployment-example\")" @@ -390,9 +345,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "rest_request_auth(\"seldon-deployment-example\",\"username\",\"password\")" @@ -408,9 +361,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "!kubectl delete -f resources/ambassador-auth-service-setup.yaml -n seldon" @@ -419,9 +370,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "!kubectl delete -f resources/ambassador-rbac.yaml -n seldon" @@ -430,20 +379,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "!kubectl delete -f resources/ambassador-service.yaml -n seldon" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "!kubectl delete -f resources/model.json -n seldon" @@ -452,9 +388,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "!cd my-ml-deployment && ks delete default" @@ -463,9 +397,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "!rm -rf my-ml-deployment" @@ -474,9 +406,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [] } diff --git a/notebooks/ksonnet_ambassador_minikube.ipynb b/notebooks/ksonnet_ambassador_minikube.ipynb index 36ea4a232c..cb3d605f77 100644 --- a/notebooks/ksonnet_ambassador_minikube.ipynb +++ b/notebooks/ksonnet_ambassador_minikube.ipynb @@ -4,8 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Deploying Machine Learning Models using ksonnet and Ambassador\n", - "## Experimental\n" + "# Deploying Machine Learning Models using ksonnet and Ambassador\n" ] }, { @@ -24,55 +23,39 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Start minikube and ensure custom resource validation is activated and ther is 5G of memory." + "Start minikube and ensure custom resource validation is activated and there is 5G of memory. \n", + "\n", + "**2018-06-13** : At present we find the most stable version of minikube across platforms is 0.25.2 as there are issues with 0.26 and 0.27 on some systems. We also find the default VirtualBox driver can be problematic on some systems so we suggest using the [KVM2 driver](https://github.com/kubernetes/minikube/blob/master/docs/drivers.md#kvm2-driver).\n", + "\n", + "Your start command would then look like:\n", + "```\n", + "minikube start --vm-driver kvm2 --memory 4096 --feature-gates=CustomResourceValidation=true --extra-config=apiserver.Authorization.Mode=RBAC\n", + "```" ] }, { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Starting local Kubernetes v1.9.4 cluster...\n", - "Starting VM...\n", - "Getting VM IP address...\n", - "Moving files into cluster...\n", - "Setting up certs...\n", - "Connecting to cluster...\n", - "Setting up kubeconfig...\n", - "Starting cluster components...\n", - "Kubectl is now configured to use the cluster.\n", - "Loading cached images from config file.\n" - ] - } - ], - "source": [ - "!minikube start --memory 4096 --feature-gates=CustomResourceValidation=true --extra-config=apiserver.Authorization.Mode=AlwaysAllow" + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Setup\n", + "\n", + "When you have a running minikube cluster run:\n" ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": null, "metadata": {}, + "outputs": [], "source": [ - "## Create Namespace" + "!kubectl create clusterrolebinding kube-system-cluster-admin --clusterrole=cluster-admin --serviceaccount=kube-system:default" ] }, { "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "namespace \"seldon\" created\r\n" - ] - } - ], + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "!kubectl create namespace seldon" ] @@ -87,37 +70,11 @@ }, { "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "service \"ambassador\" created\r\n" - ] - } - ], - "source": [ - "!kubectl apply -f resources/ambassador-service.yaml -n seldon" - ] - }, - { - "cell_type": "code", - "execution_count": 4, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "service \"ambassador-admin\" created\r\n", - "deployment \"ambassador\" created\r\n" - ] - } - ], + "outputs": [], "source": [ - "!kubectl apply -f https://getambassador.io/yaml/ambassador/ambassador-no-rbac.yaml -n seldon" + "!kubectl apply -f resources/ambassador-rbac.yaml -n seldon" ] }, { @@ -130,65 +87,30 @@ }, { "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\u001b[34mINFO \u001b[0mUsing context 'minikube' from the kubeconfig file specified at the environment variable $KUBECONFIG\n", - "\u001b[34mINFO \u001b[0mCreating environment \"default\" with namespace \"default\", pointing to cluster at address \"https://192.168.99.100:8443\"\n", - "\u001b[34mINFO \u001b[0mGenerating ksonnet-lib data at path '/home/clive/work/seldon-core/fork-seldon-core/notebooks/my-ml-deployment/lib/v1.8.0'\n", - "\u001b[34mINFO \u001b[0mksonnet app successfully created! Next, try creating a component with `ks generate`.\n" - ] - } - ], + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "!ks init my-ml-deployment --api-spec=version:v1.8.0" ] }, { "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\u001b[34mINFO \u001b[0mRetrieved 12 files\n", - "\u001b[34mINFO \u001b[0mWriting component at '/home/clive/work/seldon-core/fork-seldon-core/notebooks/my-ml-deployment/components/seldon-core.jsonnet'\n" - ] - } - ], + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "!cd my-ml-deployment && \\\n", " ks registry add seldon-core github.com/SeldonIO/seldon-core/tree/master/seldon-core && \\\n", " ks pkg install seldon-core/seldon-core@master && \\\n", - " ks generate seldon-core seldon-core --withApife=false --namespace=seldon" + " ks generate seldon-core seldon-core --withApife=false --namespace=seldon --withRbac=true" ] }, { "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\u001b[34mINFO \u001b[0mUpdating deployments seldon.seldon-cluster-manager\n", - "\u001b[34mINFO \u001b[0mCreating non-existent deployments seldon.seldon-cluster-manager\n", - "\u001b[34mINFO \u001b[0mUpdating deployments seldon.redis\n", - "\u001b[34mINFO \u001b[0mCreating non-existent deployments seldon.redis\n", - "\u001b[34mINFO \u001b[0mUpdating services seldon.redis\n", - "\u001b[34mINFO \u001b[0mCreating non-existent services seldon.redis\n", - "\u001b[34mINFO \u001b[0mUpdating customresourcedefinitions seldondeployments.machinelearning.seldon.io\n", - "\u001b[34mINFO \u001b[0mCreating non-existent customresourcedefinitions seldondeployments.machinelearning.seldon.io\n" - ] - } - ], + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "!cd my-ml-deployment && \\\n", " ks apply default" @@ -198,7 +120,13 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Set up REST and gRPC methods" + "## Set up REST and gRPC methods\n", + "\n", + "**Ensure you port forward ambassador**:\n", + "\n", + "```\n", + "kubectl port-forward $(kubectl get pods -n seldon -l service=ambassador -o jsonpath='{.items[0].metadata.name}') -n seldon 8002:80\n", + "```" ] }, { @@ -210,7 +138,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -227,7 +155,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -235,19 +163,14 @@ "from requests.auth import HTTPBasicAuth\n", "from proto import prediction_pb2\n", "from proto import prediction_pb2_grpc\n", - "try:\n", - " from commands import getoutput # python 2\n", - "except ImportError:\n", - " from subprocess import getoutput # python 3\n", + "import grpc\n", "\n", - "NAMESPACE='seldon'\n", - "MINIKUBE_IP=getoutput('minikube ip')\n", - "AMBASSADOR_PORT=getoutput(\"kubectl get svc -n \"+NAMESPACE+\" -l service=ambassador -o jsonpath='{.items[0].spec.ports[0].nodePort}'\")\n", + "AMBASSADOR_API=\"localhost:8002\"\n", "\n", "def rest_request(deploymentName):\n", " payload = {\"data\":{\"names\":[\"a\",\"b\"],\"tensor\":{\"shape\":[2,2],\"values\":[0,0,1,1]}}}\n", " response = requests.post(\n", - " \"http://\"+MINIKUBE_IP+\":\"+AMBASSADOR_PORT+\"/seldon/\"+deploymentName+\"/api/v0.1/predictions\",\n", + " \"http://\"+AMBASSADOR_API+\"/seldon/\"+deploymentName+\"/api/v0.1/predictions\",\n", " json=payload)\n", " print(response.status_code)\n", " print(response.text) \n", @@ -255,7 +178,7 @@ "def rest_request_auth(deploymentName,username,password):\n", " payload = {\"data\":{\"names\":[\"a\",\"b\"],\"tensor\":{\"shape\":[2,2],\"values\":[0,0,1,1]}}}\n", " response = requests.post(\n", - " \"http://\"+MINIKUBE_IP+\":\"+AMBASSADOR_PORT+\"/seldon/\"+deploymentName+\"/api/v0.1/predictions\",\n", + " \"http://\"+AMBASSADOR_API+\"/seldon/\"+deploymentName+\"/api/v0.1/predictions\",\n", " json=payload,\n", " auth=HTTPBasicAuth(username, password))\n", " print(response.status_code)\n", @@ -270,7 +193,7 @@ " )\n", " )\n", " request = prediction_pb2.SeldonMessage(data = datadef)\n", - " channel = grpc.insecure_channel(MINIKUBE_IP+\":\"+AMBASSADOR_PORT)\n", + " channel = grpc.insecure_channel(AMBASSADOR_API)\n", " stub = prediction_pb2_grpc.SeldonStub(channel)\n", " metadata = [('seldon',deploymentName)]\n", " response = stub.Predict(request=request,metadata=metadata)\n", @@ -294,9 +217,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "!kubectl get pods -n seldon" @@ -305,9 +226,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "!kubectl apply -f resources/model.json -n seldon" @@ -323,9 +242,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "!kubectl get seldondeployments seldon-deployment-example -o jsonpath='{.status}' -n seldon" @@ -348,9 +265,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "rest_request(\"seldon-deployment-example\")" @@ -366,9 +281,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "grpc_request(\"seldon-deployment-example\")" @@ -385,9 +298,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "!kubectl apply -f resources/ambassador-auth-service-setup.yaml -n seldon" @@ -403,9 +314,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "!kubectl get pods -n seldon" @@ -414,9 +323,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "!kubectl apply -f resources/ambassador-auth-service-config.yaml -n seldon" @@ -432,9 +339,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "rest_request(\"seldon-deployment-example\")" @@ -450,9 +355,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "rest_request_auth(\"seldon-deployment-example\",\"username\",\"password\")" @@ -468,9 +371,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "!kubectl delete -f resources/ambassador-auth-service-setup.yaml -n seldon" @@ -479,20 +380,16 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ - "!kubectl delete -f https://getambassador.io/yaml/ambassador/ambassador-no-rbac.yaml -n seldon" + "!kubectl delete -f resources/ambassador-rbac.yaml -n seldon" ] }, { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "!kubectl delete -f resources/model.json" @@ -501,9 +398,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "!cd my-ml-deployment && ks delete default" @@ -512,9 +407,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "!rm -rf my-ml-deployment" @@ -523,9 +416,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [] } diff --git a/notebooks/kubectl_demo_minikube.ipynb b/notebooks/kubectl_demo_minikube.ipynb deleted file mode 100644 index 899c1690bf..0000000000 --- a/notebooks/kubectl_demo_minikube.ipynb +++ /dev/null @@ -1,598 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Deploying Machine Learning Models using kubectl on Minikube (no RBAC)\n", - "This demo shows how you can interact directly with kubernetes using kubectl to create and manage runtime machine learning models. It uses Minikube as the target Kubernetes cluster.\n", - "\"predictor" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Prerequistes\n", - "You will need\n", - " - [Git clone of Seldon Core](https://github.com/SeldonIO/seldon-core)\n", - " - [Helm](https://github.com/kubernetes/helm)\n", - " - [Minikube](https://github.com/kubernetes/minikube) version v0.24.0 or greater\n", - " - [python grpc tools](https://grpc.io/docs/quickstart/python.html)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Start minikube and ensure custom resource validation is activated and ther is 5G of memory." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "!minikube start --memory=5000 --feature-gates=CustomResourceValidation=true --extra-config=apiserver.Authorization.Mode=AlwaysAllow" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Install Helm" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "!helm init" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Label the node to allow load testing to run on it" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "!kubectl label nodes `kubectl get nodes -o jsonpath='{.items[0].metadata.name}'` role=locust --overwrite" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Start seldon-core" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Install the custom resource definition" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "!helm install ../helm-charts/seldon-core-crd --name seldon-core-crd \\\n", - " --set usage_metrics.enabled=true \\\n", - " --set rbac.enabled=false" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "!kubectl create namespace seldon" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "!helm install ../helm-charts/seldon-core --name seldon-core --namespace seldon \\\n", - " --set rbac.enabled=false" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Install prometheus and grafana for analytics" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "!helm install ../helm-charts/seldon-core-analytics --name seldon-core-analytics \\\n", - " --set grafana_prom_admin_password=password \\\n", - " --set persistence.enabled=false \\\n", - " --set rbac.enabled=false \\\n", - " --namespace seldon" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Check all services are running before proceeding." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "!kubectl get pods -n seldon" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Set up REST and gRPC methods" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Install gRPC modules for the prediction protos." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "!cp ../proto/prediction.proto ./proto\n", - "!python -m grpc.tools.protoc -I. --python_out=. --grpc_python_out=. ./proto/prediction.proto" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Illustration of both REST and gRPC requests. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import requests\n", - "from requests.auth import HTTPBasicAuth\n", - "from proto import prediction_pb2\n", - "from proto import prediction_pb2_grpc\n", - "import grpc\n", - "try:\n", - " from commands import getoutput # python 2\n", - "except ImportError:\n", - " from subprocess import getoutput # python 3\n", - "\n", - "\n", - "NAMESPACE='seldon'\n", - "MINIKUBE_IP=getoutput('minikube ip')\n", - "MINIKUBE_HTTP_PORT=getoutput(\"kubectl get svc -n \"+NAMESPACE+\" -l app=seldon-apiserver-container-app -o jsonpath='{.items[0].spec.ports[0].nodePort}'\")\n", - "MINIKUBE_GRPC_PORT=getoutput(\"kubectl get svc -n \"+NAMESPACE+\" -l app=seldon-apiserver-container-app -o jsonpath='{.items[0].spec.ports[1].nodePort}'\")\n", - "\n", - "def get_token():\n", - " payload = {'grant_type': 'client_credentials'}\n", - " response = requests.post(\n", - " \"http://\"+MINIKUBE_IP+\":\"+MINIKUBE_HTTP_PORT+\"/oauth/token\",\n", - " auth=HTTPBasicAuth('oauth-key', 'oauth-secret'),\n", - " data=payload)\n", - " print(response.text)\n", - " token = response.json()[\"access_token\"]\n", - " return token\n", - "\n", - "def rest_request():\n", - " token = get_token()\n", - " headers = {'Authorization': 'Bearer '+token}\n", - " payload = {\"data\":{\"names\":[\"a\",\"b\"],\"tensor\":{\"shape\":[2,2],\"values\":[0,0,1,1]}}}\n", - " response = requests.post(\n", - " \"http://\"+MINIKUBE_IP+\":\"+MINIKUBE_HTTP_PORT+\"/api/v0.1/predictions\",\n", - " headers=headers,\n", - " json=payload)\n", - " print(response.text)\n", - " \n", - "def grpc_request():\n", - " token = get_token()\n", - " datadef = prediction_pb2.DefaultData(\n", - " names = [\"a\",\"b\"],\n", - " tensor = prediction_pb2.Tensor(\n", - " shape = [3,2],\n", - " values = [1.0,1.0,2.0,3.0,4.0,5.0]\n", - " )\n", - " )\n", - " request = prediction_pb2.SeldonMessage(data = datadef)\n", - " channel = grpc.insecure_channel(MINIKUBE_IP+\":\"+MINIKUBE_GRPC_PORT)\n", - " stub = prediction_pb2_grpc.SeldonStub(channel)\n", - " metadata = [('oauth_token', token)]\n", - " response = stub.Predict(request=request,metadata=metadata)\n", - " print(response)\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Integrating with Kubernetes API" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Validation" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Using OpenAPI Schema certain basic validation can be done before the custom resource is accepted." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "!kubectl create -f resources/model_invalid1.json -n seldon" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Normal Operation\n", - "A simple example is shown below we use a single prepacked model for illustration. The spec contains a set of predictors each of which contains a ***componentSpec*** which is a Kubernetes [PodTemplateSpec](https://kubernetes.io/docs/api-reference/v1.9/#podtemplatespec-v1-core) alongside a ***graph*** which describes how components fit together." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "!pygmentize resources/model.json" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Create Seldon Deployment" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Deploy the runtime graph to kubernetes." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "!kubectl apply -f resources/model.json -n seldon" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "!kubectl get seldondeployments -n seldon" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "!kubectl describe seldondeployments seldon-deployment-example -n seldon" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Get the status of the SeldonDeployment. **When ready the replicasAvailable should be 1**." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "!kubectl get seldondeployments seldon-deployment-example -o jsonpath='{.status}' -n seldon" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Get predictions" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### REST Request" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "rest_request()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### gRPC Request" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "grpc_request()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Update deployment with canary" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We will change the deployment to add a \"canary\" deployment. This illustrates:\n", - " - Updating a deployment with no downtime\n", - " - Adding an extra predictor to run alongside th exsting predictor.\n", - " \n", - " You could manage different traffic levels by controlling the number of replicas of each." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "!pygmentize resources/model_with_canary.json" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "!kubectl apply -f resources/model_with_canary.json -n seldon" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Check the status of the deployments. Note: **Might need to run several times until replicasAvailable is 1 for both predictors**." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "!kubectl get seldondeployments seldon-deployment-example -o jsonpath='{.status}' -n seldon" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### REST Request" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "rest_request()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### gRPC request" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "grpc_request()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Load test" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Start a load test which will post REST requests at 10 requests per second." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "!helm install seldon-core-loadtesting --name loadtest \\\n", - " --set oauth.key=oauth-key \\\n", - " --set oauth.secret=oauth-secret \\\n", - " --namespace seldon \\\n", - " --repo https://storage.googleapis.com/seldon-charts" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You should port-foward the grafana dashboard\n", - "\n", - "```bash\n", - "kubectl port-forward $(kubectl get pods -n seldon -l app=grafana-prom-server -o jsonpath='{.items[0].metadata.name}') -n seldon 3000:3000\n", - "```\n", - "\n", - "You can then iew an analytics dashboard inside the cluster at http://localhost:3000/dashboard/db/prediction-analytics?refresh=5s&orgId=1. Your IP address may be different. get it via minikube ip. Login with:\n", - " - Username : admin\n", - " - password : password (as set when starting seldon-core above)\n", - " \n", - " The dashboard should look like below:\n", - " \n", - " \n", - " \"predictor" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Tear down" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "!helm delete loadtest --purge" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "!kubectl delete -f resources/model_with_canary.json -n seldon" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "!helm delete seldon-core-analytics --purge" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "!helm delete seldon-core --purge" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "!helm delete seldon-core-crd --purge" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "anaconda-cloud": {}, - "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.6.4" - } - }, - "nbformat": 4, - "nbformat_minor": 1 -} diff --git a/notebooks/kubectl_demo_minikube_rbac.ipynb b/notebooks/kubectl_demo_minikube_rbac.ipynb index 40bd736fbc..9ca1d63361 100644 --- a/notebooks/kubectl_demo_minikube_rbac.ipynb +++ b/notebooks/kubectl_demo_minikube_rbac.ipynb @@ -18,14 +18,30 @@ " - [Git clone of Seldon Core](https://github.com/SeldonIO/seldon-core)\n", " - [Helm](https://github.com/kubernetes/helm)\n", " - [Minikube](https://github.com/kubernetes/minikube) version v0.24.0 or greater\n", - " - [python grpc tools](https://grpc.io/docs/quickstart/python.html)" + " - [python grpc tools](https://grpc.io/docs/quickstart/python.html)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Start minikube with RBAC and ensure custom resource validation is activated and there is 5G of memory." + "# Create Cluster\n", + "\n", + "Start minikube and ensure custom resource validation is activated and there is 5G of memory. \n", + "\n", + "**2018-06-13** : At present we find the most stable version of minikube across platforms is 0.25.2 as there are issues with 0.26 and 0.27 on some systems. We also find the default VirtualBox driver can be problematic on some systems to we suggest using the [KVM2 driver](https://github.com/kubernetes/minikube/blob/master/docs/drivers.md#kvm2-driver).\n", + "\n", + "Your start command would then look like:\n", + "```\n", + "minikube start --vm-driver kvm2 --memory 4096 --feature-gates=CustomResourceValidation=true --extra-config=apiserver.Authorization.Mode=RBAC\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Setup" ] }, { @@ -34,7 +50,7 @@ "metadata": {}, "outputs": [], "source": [ - "!minikube start --memory=5000 --feature-gates=CustomResourceValidation=true --extra-config=apiserver.Authorization.Mode=RBAC" + "!kubectl create namespace seldon" ] }, { @@ -46,11 +62,20 @@ "!kubectl create clusterrolebinding kube-system-cluster-admin --clusterrole=cluster-admin --serviceaccount=kube-system:default" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!kubectl create clusterrolebinding seldon-cluster-admin --clusterrole=cluster-admin --serviceaccount=seldon:seldon" + ] + }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Install Helm" + "# Install Helm" ] }, { @@ -103,15 +128,6 @@ "!helm install ../helm-charts/seldon-core-crd --name seldon-core-crd --set usage_metrics.enabled=true" ] }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "!kubectl create namespace seldon" - ] - }, { "cell_type": "code", "execution_count": null, @@ -160,7 +176,19 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Set up REST and gRPC methods" + "## Set up REST and gRPC methods\n", + "\n", + "**Ensure you port forward the seldon api-server REST and GRPC ports**:\n", + "\n", + "REST:\n", + "```\n", + "kubectl port-forward $(kubectl get pods -n seldon -l app=seldon-apiserver-container-app -o jsonpath='{.items[0].metadata.name}') -n seldon 8002:8080\n", + "```\n", + "\n", + "GRPC:\n", + "```\n", + "kubectl port-forward $(kubectl get pods -n seldon -l app=seldon-apiserver-container-app -o jsonpath='{.items[0].metadata.name}') -n seldon 8003:5000\n", + "```" ] }, { @@ -203,16 +231,13 @@ "except ImportError:\n", " from subprocess import getoutput # python 3\n", "\n", - "\n", - "NAMESPACE='seldon'\n", - "MINIKUBE_IP=getoutput('minikube ip')\n", - "MINIKUBE_HTTP_PORT=getoutput(\"kubectl get svc -n \"+NAMESPACE+\" -l app=seldon-apiserver-container-app -o jsonpath='{.items[0].spec.ports[0].nodePort}'\")\n", - "MINIKUBE_GRPC_PORT=getoutput(\"kubectl get svc -n \"+NAMESPACE+\" -l app=seldon-apiserver-container-app -o jsonpath='{.items[0].spec.ports[1].nodePort}'\")\n", + "API_HTTP=\"localhost:8002\"\n", + "API_GRPC=\"localhost:8003\"\n", "\n", "def get_token():\n", " payload = {'grant_type': 'client_credentials'}\n", " response = requests.post(\n", - " \"http://\"+MINIKUBE_IP+\":\"+MINIKUBE_HTTP_PORT+\"/oauth/token\",\n", + " \"http://\"+API_HTTP+\"/oauth/token\",\n", " auth=HTTPBasicAuth('oauth-key', 'oauth-secret'),\n", " data=payload)\n", " print(response.text)\n", @@ -224,7 +249,7 @@ " headers = {'Authorization': 'Bearer '+token}\n", " payload = {\"data\":{\"names\":[\"a\",\"b\"],\"tensor\":{\"shape\":[2,2],\"values\":[0,0,1,1]}}}\n", " response = requests.post(\n", - " \"http://\"+MINIKUBE_IP+\":\"+MINIKUBE_HTTP_PORT+\"/api/v0.1/predictions\",\n", + " \"http://\"+API_HTTP+\"/api/v0.1/predictions\",\n", " headers=headers,\n", " json=payload)\n", " print(response.text)\n", @@ -239,7 +264,7 @@ " )\n", " )\n", " request = prediction_pb2.SeldonMessage(data = datadef)\n", - " channel = grpc.insecure_channel(MINIKUBE_IP+\":\"+MINIKUBE_GRPC_PORT)\n", + " channel = grpc.insecure_channel(API_GRPC)\n", " stub = prediction_pb2_grpc.SeldonStub(channel)\n", " metadata = [('oauth_token', token)]\n", " response = stub.Predict(request=request,metadata=metadata)\n", @@ -512,7 +537,7 @@ "\n", "You can then iew an analytics dashboard inside the cluster at http://localhost:3000/dashboard/db/prediction-analytics?refresh=5s&orgId=1. Your IP address may be different. get it via minikube ip. Login with:\n", " - Username : admin\n", - " - password : password (as set when starting seldon-core above)\n", + " - password : password (as set when starting seldon-core-analytics above)\n", " \n", " The dashboard should look like below:\n", " \n", diff --git a/notebooks/resources/ambassador-rbac.yaml b/notebooks/resources/ambassador-rbac.yaml index 1afc897bb4..1f16cc1100 100644 --- a/notebooks/resources/ambassador-rbac.yaml +++ b/notebooks/resources/ambassador-rbac.yaml @@ -1,19 +1,4 @@ --- -apiVersion: v1 -kind: Service -metadata: - labels: - service: ambassador-admin - name: ambassador-admin -spec: - type: NodePort - ports: - - name: ambassador-admin - port: 8877 - targetPort: 8877 - selector: - service: ambassador ---- apiVersion: rbac.authorization.k8s.io/v1beta1 kind: ClusterRole metadata: @@ -48,23 +33,60 @@ roleRef: subjects: - kind: ServiceAccount name: ambassador - namespace: seldon + namespace: seldon +--- +apiVersion: v1 +kind: Service +metadata: + labels: + service: ambassador + name: ambassador +spec: + selector: + service: ambassador + ports: + - name: http + protocol: TCP + port: 80 + targetPort: 80 + type: NodePort +--- +apiVersion: v1 +kind: Service +metadata: + labels: + service: ambassador-admin + name: ambassador-admin +spec: + ports: + - name: ambassador-admin + port: 8877 + targetPort: 8877 + selector: + service: ambassador + type: NodePort --- apiVersion: extensions/v1beta1 kind: Deployment metadata: name: ambassador spec: - replicas: 3 + replicas: 1 template: metadata: + annotations: + sidecar.istio.io/inject: 'false' labels: service: ambassador spec: - serviceAccountName: ambassador containers: - - name: ambassador - image: quay.io/datawire/ambassador:0.26.0 + - image: quay.io/datawire/ambassador:0.34.1 + name: ambassador + env: + - name: AMBASSADOR_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace resources: limits: cpu: 1 @@ -72,23 +94,7 @@ spec: requests: cpu: 200m memory: 100Mi - env: - - name: AMBASSADOR_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - livenessProbe: - httpGet: - path: /ambassador/v0/check_alive - port: 8877 - initialDelaySeconds: 3 - periodSeconds: 3 - readinessProbe: - httpGet: - path: /ambassador/v0/check_ready - port: 8877 - initialDelaySeconds: 3 - periodSeconds: 3 - - name: statsd - image: quay.io/datawire/statsd:0.26.0 + - image: quay.io/datawire/statsd:0.34.1 + name: statsd restartPolicy: Always + serviceAccountName: ambassador diff --git a/notebooks/resources/ambassador-service.yaml b/notebooks/resources/ambassador-service.yaml deleted file mode 100644 index 94f1332777..0000000000 --- a/notebooks/resources/ambassador-service.yaml +++ /dev/null @@ -1,15 +0,0 @@ ---- -apiVersion: v1 -kind: Service -metadata: - labels: - service: ambassador - name: ambassador -spec: - type: NodePort - ports: - - name: ambassador - port: 80 - targetPort: 80 - selector: - service: ambassador diff --git a/readme.md b/readme.md index 12b5de8378..197aa5c6e1 100644 --- a/readme.md +++ b/readme.md @@ -50,7 +50,7 @@ Machine learning deployment has many [challenges](./docs/challenges.md). Seldon - Jupyter notebooks showing worked examples: * Minikube: * [Jupyter Notebook showing deployment of prebuilt model using Minikube - with RBAC](https://github.com/SeldonIO/seldon-core/blob/master/notebooks/kubectl_demo_minikube_rbac.ipynb) - * [Jupyter notebook to create seldon-core with ksonnet and expose APIs using Ambassador on Minikube.](https://github.com/SeldonIO/seldon-core/blob/master/notebooks/ksonnet_ambassador_minikube.ipynb) + * [Jupyter notebook to create seldon-core with ksonnet and expose APIs using Ambassador on Minikube with RBAC.](https://github.com/SeldonIO/seldon-core/blob/master/notebooks/ksonnet_ambassador_minikube.ipynb) * GCP: * [Jupyter Notebook showing deployment of prebuilt model using GCP cluster](https://github.com/SeldonIO/seldon-core/blob/master/notebooks/kubectl_demo_gcp.ipynb) * [Jupyter notebook to create seldon-core with ksonnet and expose APIs using Ambassador on GCP.](https://github.com/SeldonIO/seldon-core/blob/master/notebooks/ksonnet_ambassador_gcp.ipynb) From f522d5c4502a8c8eff2c0088e2219fec04fbf4cc Mon Sep 17 00:00:00 2001 From: Clive Cox Date: Sat, 16 Jun 2018 18:21:20 +0100 Subject: [PATCH 10/18] fix service account creation for prometheus in RBAC --- .../templates/prometheus-deployment.json | 2 +- .../seldon-core-analytics/templates/prometheus-rbac.yaml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/helm-charts/seldon-core-analytics/templates/prometheus-deployment.json b/helm-charts/seldon-core-analytics/templates/prometheus-deployment.json index 98badfacfa..a7b5b09507 100644 --- a/helm-charts/seldon-core-analytics/templates/prometheus-deployment.json +++ b/helm-charts/seldon-core-analytics/templates/prometheus-deployment.json @@ -22,7 +22,7 @@ }, "spec": { {{- if .Values.rbac.enabled }} - "serviceAccountName": "seldon", + "serviceAccountName": "prometheus", {{- end }} "containers": [ { diff --git a/helm-charts/seldon-core-analytics/templates/prometheus-rbac.yaml b/helm-charts/seldon-core-analytics/templates/prometheus-rbac.yaml index d279931a22..3093bb34a2 100644 --- a/helm-charts/seldon-core-analytics/templates/prometheus-rbac.yaml +++ b/helm-charts/seldon-core-analytics/templates/prometheus-rbac.yaml @@ -24,7 +24,7 @@ apiVersion: v1 kind: ServiceAccount metadata: name: prometheus - namespace: default + namespace: {{ .Release.Namespace }} --- apiVersion: rbac.authorization.k8s.io/v1beta1 kind: ClusterRoleBinding @@ -37,5 +37,5 @@ roleRef: subjects: - kind: ServiceAccount name: prometheus - namespace: default + namespace: {{ .Release.Namespace }} {{- end }} From 34c88a298414f177f699a5767fff9b6aef110c25 Mon Sep 17 00:00:00 2001 From: Clive Cox Date: Sat, 16 Jun 2018 18:58:50 +0100 Subject: [PATCH 11/18] update minikube rbac notebook --- notebooks/kubectl_demo_minikube_rbac.ipynb | 9 --------- 1 file changed, 9 deletions(-) diff --git a/notebooks/kubectl_demo_minikube_rbac.ipynb b/notebooks/kubectl_demo_minikube_rbac.ipynb index 9ca1d63361..b36c72e96b 100644 --- a/notebooks/kubectl_demo_minikube_rbac.ipynb +++ b/notebooks/kubectl_demo_minikube_rbac.ipynb @@ -62,15 +62,6 @@ "!kubectl create clusterrolebinding kube-system-cluster-admin --clusterrole=cluster-admin --serviceaccount=kube-system:default" ] }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "!kubectl create clusterrolebinding seldon-cluster-admin --clusterrole=cluster-admin --serviceaccount=seldon:seldon" - ] - }, { "cell_type": "markdown", "metadata": {}, From e6cf44b217e63bbcebe6b69b20f9d9316481d2a2 Mon Sep 17 00:00:00 2001 From: Clive Cox Date: Sat, 23 Jun 2018 15:12:22 +0100 Subject: [PATCH 12/18] remove nd4j and replace with oj matrix library --- engine/pom.xml | 6 +- .../predictors/AverageCombinerUnit.java | 14 +-- .../engine/predictors/PredictiveUnitBean.java | 8 +- .../engine/predictors/PredictorUtils.java | 112 ++++++------------ .../io/seldon/engine/pb/TestMatrixOps.java | 41 +++++++ 5 files changed, 92 insertions(+), 89 deletions(-) create mode 100644 engine/src/test/java/io/seldon/engine/pb/TestMatrixOps.java diff --git a/engine/pom.xml b/engine/pom.xml index 644c03b18e..d7482f839b 100644 --- a/engine/pom.xml +++ b/engine/pom.xml @@ -76,9 +76,9 @@ - org.nd4j - nd4j-native-platform - 0.9.1 + org.ojalgo + ojalgo + 45.1.1 org.springframework.boot diff --git a/engine/src/main/java/io/seldon/engine/predictors/AverageCombinerUnit.java b/engine/src/main/java/io/seldon/engine/predictors/AverageCombinerUnit.java index 0215526e5f..1208bb356e 100644 --- a/engine/src/main/java/io/seldon/engine/predictors/AverageCombinerUnit.java +++ b/engine/src/main/java/io/seldon/engine/predictors/AverageCombinerUnit.java @@ -18,16 +18,14 @@ import java.util.Iterator; import java.util.List; -import org.nd4j.linalg.api.ndarray.INDArray; -import org.nd4j.linalg.factory.Nd4j; +import org.ojalgo.matrix.BasicMatrix; +import org.ojalgo.matrix.PrimitiveMatrix; import org.springframework.stereotype.Component; import io.seldon.engine.exception.APIException; import io.seldon.protos.PredictionProtos.DefaultData; import io.seldon.protos.PredictionProtos.SeldonMessage; -import io.seldon.engine.predictors.PredictorUtils; - @Component public class AverageCombinerUnit extends PredictiveUnitImpl { @@ -49,8 +47,8 @@ public SeldonMessage aggregate(List outputs, PredictiveUnitState if (shape.length!=2){ throw new APIException(APIException.ApiExceptionType.ENGINE_INVALID_COMBINER_RESPONSE, String.format("Combiner received data that is not 2 dimensional")); } - - INDArray currentSum = Nd4j.zeros(shape[0],shape[1]); + BasicMatrix.Factory matrixFactory = PrimitiveMatrix.FACTORY; + PrimitiveMatrix currentSum = matrixFactory.makeZero(shape[0], shape[1]); SeldonMessage.Builder respBuilder = SeldonMessage.newBuilder(); for (Iterator i = outputs.iterator(); i.hasNext();) @@ -69,10 +67,10 @@ public SeldonMessage aggregate(List outputs, PredictiveUnitState if (inputShape[1] != shape[1]){ throw new APIException(APIException.ApiExceptionType.ENGINE_INVALID_COMBINER_RESPONSE, String.format("Expected batch length %d but found %d",shape[1],inputShape[1])); } - INDArray inputArr = PredictorUtils.getINDArray(inputData); + PrimitiveMatrix inputArr = PredictorUtils.getOJMatrix(inputData); currentSum = currentSum.add(inputArr); } - currentSum = currentSum.div((float)outputs.size()); + currentSum = currentSum.divide((float)outputs.size()); DefaultData newData = PredictorUtils.updateData(outputs.get(0).getData(), currentSum); respBuilder.setData(newData); diff --git a/engine/src/main/java/io/seldon/engine/predictors/PredictiveUnitBean.java b/engine/src/main/java/io/seldon/engine/predictors/PredictiveUnitBean.java index e1a4512dd0..aaa0c1d4b7 100644 --- a/engine/src/main/java/io/seldon/engine/predictors/PredictiveUnitBean.java +++ b/engine/src/main/java/io/seldon/engine/predictors/PredictiveUnitBean.java @@ -22,7 +22,7 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; -import org.nd4j.linalg.api.ndarray.INDArray; +import org.ojalgo.matrix.PrimitiveMatrix; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.AsyncResult; @@ -37,8 +37,8 @@ import io.seldon.engine.service.InternalPredictionService; import io.seldon.protos.DeploymentProtos.PredictiveUnit.PredictiveUnitMethod; import io.seldon.protos.PredictionProtos.Feedback; -import io.seldon.protos.PredictionProtos.SeldonMessage; import io.seldon.protos.PredictionProtos.Meta; +import io.seldon.protos.PredictionProtos.SeldonMessage; @Component public class PredictiveUnitBean extends PredictiveUnitImpl { @@ -227,8 +227,8 @@ public void doSendFeedback(Feedback feedback, PredictiveUnitState state) throws private int getBranchIndex(SeldonMessage routerReturn, PredictiveUnitState state){ int branchIndex = 0; try { - INDArray dataArray = PredictorUtils.getINDArray(routerReturn.getData()); - branchIndex = (int) dataArray.getInt(0); + PrimitiveMatrix dataArray = PredictorUtils.getOJMatrix(routerReturn.getData()); + branchIndex = dataArray.get(0).intValue(); } catch (IndexOutOfBoundsException e) { throw new APIException(APIException.ApiExceptionType.ENGINE_INVALID_ROUTING,"Router that caused the exception: id="+state.name+" name="+state.name); diff --git a/engine/src/main/java/io/seldon/engine/predictors/PredictorUtils.java b/engine/src/main/java/io/seldon/engine/predictors/PredictorUtils.java index 7ab7acf9fb..3d2ed60598 100644 --- a/engine/src/main/java/io/seldon/engine/predictors/PredictorUtils.java +++ b/engine/src/main/java/io/seldon/engine/predictors/PredictorUtils.java @@ -15,56 +15,22 @@ *******************************************************************************/ package io.seldon.engine.predictors; -import io.seldon.protos.PredictionProtos.Tensor; -import io.seldon.protos.PredictionProtos.DefaultData.DataOneofCase; -import io.seldon.protos.PredictionProtos.DefaultData; - -import java.util.Arrays; import java.util.Iterator; import java.util.List; -import java.util.stream.Collectors; -import org.nd4j.linalg.api.ndarray.INDArray; -import org.nd4j.linalg.factory.Nd4j; +import org.ojalgo.matrix.BasicMatrix; +import org.ojalgo.matrix.PrimitiveMatrix; import com.google.protobuf.ListValue; import com.google.protobuf.Value; +import io.seldon.protos.PredictionProtos.DefaultData; +import io.seldon.protos.PredictionProtos.DefaultData.DataOneofCase; +import io.seldon.protos.PredictionProtos.Tensor; + public class PredictorUtils { - public static DefaultData tensorToNDArray(DefaultData data){ - if (data.getDataOneofCase() == DataOneofCase.TENSOR) - { - List valuesList = data.getTensor().getValuesList(); - List shapeList = data.getTensor().getShapeList(); - - DefaultData.Builder dataBuilder = DefaultData.newBuilder(); - - int index=0; - for (Iterator i = data.getNamesList().iterator(); i.hasNext();){ - dataBuilder.setNames(index, i.next()); - index++; - } - - ListValue.Builder b1 = ListValue.newBuilder(); - for (int i = 0; i < shapeList.get(0); ++i) { - ListValue.Builder b2 = ListValue.newBuilder(); - for (int j = 0; j < shapeList.get(1); j++){ - b2.addValues(Value.newBuilder().setNumberValue(valuesList.get(i*shapeList.get(1))+j)); - } - b1.addValues(Value.newBuilder().setListValue(b2.build())); - } - dataBuilder.setNdarray(b1.build()); - - return dataBuilder.build(); - - } - else if (data.getDataOneofCase() == DataOneofCase.NDARRAY) - { - return data; - } - return null; - } + public static DefaultData nDArrayToTensor(DefaultData data){ if (data.getDataOneofCase() == DataOneofCase.TENSOR) @@ -98,49 +64,45 @@ else if (data.getDataOneofCase() == DataOneofCase.NDARRAY) } return null; } - - public static INDArray getINDArray(DefaultData data){ - + + public static PrimitiveMatrix getOJMatrix(DefaultData data){ + BasicMatrix.Factory matrixFactory = PrimitiveMatrix.FACTORY; if (data.getDataOneofCase() == DataOneofCase.TENSOR) { List valuesList = data.getTensor().getValuesList(); List shapeList = data.getTensor().getShapeList(); - double[] values = new double[valuesList.size()]; - int[] shape = new int[shapeList.size()]; - for (int i = 0; i < values.length; i++) { - values[i] = valuesList.get(i); - } - for (int i = 0; i < shape.length; i++) { - shape[i] = shapeList.get(i); + int rows = shapeList.get(0); + int columns = shapeList.get(1); + + double[][] values = new double[rows][columns]; + for (int i = 0; i < rows*columns; i++) { + values[i/columns][i%columns] = valuesList.get(i); } - - INDArray newArr = Nd4j.create(values,shape,'c'); - return newArr; + return matrixFactory.rows(values); } else if (data.getDataOneofCase() == DataOneofCase.NDARRAY) { ListValue list = data.getNdarray(); - int bLength = list.getValuesCount(); - int vLength = list.getValues(0).getListValue().getValuesCount(); - - double[] values = new double[bLength*vLength]; - int[] shape = {bLength,vLength}; + int rows = list.getValuesCount(); + int cols = list.getValues(0).getListValue().getValuesCount(); - for (int i = 0; i < bLength; ++i) { - for (int j = 0; j < vLength; j++){ - values[i*bLength+j] = list.getValues(i).getListValue().getValues(j).getNumberValue(); + double[][] values = new double[rows][cols]; + for (int i = 0; i < rows; ++i) { + for (int j = 0; j < cols; j++){ + values[i][j] = list.getValues(i).getListValue().getValues(j).getNumberValue(); } } - INDArray newArr = Nd4j.create(values,shape,'c'); - - return newArr; + return matrixFactory.rows(values); } return null; } + + + public static int[] getShape(DefaultData data){ if (data.getDataOneofCase() == DataOneofCase.TENSOR){ @@ -162,7 +124,7 @@ else if(data.getDataOneofCase() == DataOneofCase.NDARRAY){ return null; } - public static DefaultData updateData(DefaultData oldData, INDArray newData){ + public static DefaultData updateData(DefaultData oldData, PrimitiveMatrix newData){ DefaultData.Builder dataBuilder = DefaultData.newBuilder(); dataBuilder.addAllNames(oldData.getNamesList()); @@ -175,12 +137,13 @@ public static DefaultData updateData(DefaultData oldData, INDArray newData){ if (oldData.getDataOneofCase() == DataOneofCase.TENSOR){ Tensor.Builder tBuilder = Tensor.newBuilder(); - List shapeList = Arrays.stream(newData.shape()).boxed().collect(Collectors.toList()); - tBuilder.addAllShape(shapeList); + + tBuilder.addShape((int)newData.countRows()); + tBuilder.addShape((int)newData.countColumns()); - for (int i=0; i Date: Sat, 23 Jun 2018 15:38:52 +0100 Subject: [PATCH 13/18] remove java wrapper library from code base --- wrappers/s2i/java/wrapper/.gitignore | 1 - wrappers/s2i/java/wrapper/Makefile | 12 - wrappers/s2i/java/wrapper/pom.xml | 288 - .../src/main/config/application.properties | 7 - .../src/main/java/io/seldon/wrapper/App.java | 20 - .../wrapper/api/CombinerRestController.java | 58 - .../wrapper/api/ModelRestController.java | 59 - .../wrapper/api/RouterRestController.java | 85 - .../wrapper/api/SeldonPredictionService.java | 26 - .../api/TransformerRestController.java | 84 - .../io/seldon/wrapper/config/AppConfig.java | 100 - .../wrapper/config/CustomizationBean.java | 47 - .../wrapper/exception/APIException.java | 87 - .../seldon/wrapper/grpc/CombinerService.java | 26 - .../seldon/wrapper/grpc/GenericService.java | 60 - .../io/seldon/wrapper/grpc/ModelService.java | 48 - .../grpc/OutputTransformerService.java | 26 - .../io/seldon/wrapper/grpc/RouterService.java | 38 - .../seldon/wrapper/grpc/SeldonGrpcServer.java | 109 - .../grpc/SeldonGrpcServerInitializer.java | 47 - .../wrapper/grpc/TransformerService.java | 26 - .../io/seldon/wrapper/pb/ProtoBufUtils.java | 69 - .../io/seldon/wrapper/utils/DL4JUtils.java | 115 - .../io/seldon/wrapper/utils/H2OUtils.java | 183 - .../io/seldon/wrapper/utils/PMMLUtils.java | 193 - .../s2i/java/wrapper/src/main/proto/.keep | 0 .../src/main/resources/application.properties | 8 - .../seldon/wrapper/utils/TestPMMMLUtils.java | 79 - .../wrapper/src/test/resources/mnist.pmml | 8710 ----------------- 29 files changed, 10611 deletions(-) delete mode 100644 wrappers/s2i/java/wrapper/.gitignore delete mode 100644 wrappers/s2i/java/wrapper/Makefile delete mode 100644 wrappers/s2i/java/wrapper/pom.xml delete mode 100644 wrappers/s2i/java/wrapper/src/main/config/application.properties delete mode 100644 wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/App.java delete mode 100644 wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/api/CombinerRestController.java delete mode 100644 wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/api/ModelRestController.java delete mode 100644 wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/api/RouterRestController.java delete mode 100644 wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/api/SeldonPredictionService.java delete mode 100644 wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/api/TransformerRestController.java delete mode 100644 wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/config/AppConfig.java delete mode 100644 wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/config/CustomizationBean.java delete mode 100644 wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/exception/APIException.java delete mode 100644 wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/grpc/CombinerService.java delete mode 100644 wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/grpc/GenericService.java delete mode 100644 wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/grpc/ModelService.java delete mode 100644 wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/grpc/OutputTransformerService.java delete mode 100644 wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/grpc/RouterService.java delete mode 100644 wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/grpc/SeldonGrpcServer.java delete mode 100644 wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/grpc/SeldonGrpcServerInitializer.java delete mode 100644 wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/grpc/TransformerService.java delete mode 100644 wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/pb/ProtoBufUtils.java delete mode 100644 wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/utils/DL4JUtils.java delete mode 100644 wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/utils/H2OUtils.java delete mode 100644 wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/utils/PMMLUtils.java delete mode 100644 wrappers/s2i/java/wrapper/src/main/proto/.keep delete mode 100644 wrappers/s2i/java/wrapper/src/main/resources/application.properties delete mode 100644 wrappers/s2i/java/wrapper/src/test/java/io/seldon/wrapper/utils/TestPMMMLUtils.java delete mode 100644 wrappers/s2i/java/wrapper/src/test/resources/mnist.pmml diff --git a/wrappers/s2i/java/wrapper/.gitignore b/wrappers/s2i/java/wrapper/.gitignore deleted file mode 100644 index b83d22266a..0000000000 --- a/wrappers/s2i/java/wrapper/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/target/ diff --git a/wrappers/s2i/java/wrapper/Makefile b/wrappers/s2i/java/wrapper/Makefile deleted file mode 100644 index b7c19da8e3..0000000000 --- a/wrappers/s2i/java/wrapper/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -IMAGE_NAME=seldon-java-wrapper -VERSION_FILE=target/version.txt - -build_jar: update_proto - mvn clean package -B - -update_proto: - @cp -v ../../../../proto/prediction.proto src/main/proto/ - -clean: - rm -rf src/main/proto/* - mvn clean diff --git a/wrappers/s2i/java/wrapper/pom.xml b/wrappers/s2i/java/wrapper/pom.xml deleted file mode 100644 index 157e92f74a..0000000000 --- a/wrappers/s2i/java/wrapper/pom.xml +++ /dev/null @@ -1,288 +0,0 @@ - - 4.0.0 - io.seldon.wrapper - seldon-core-wrapper - jar - 0.1.1 - Seldon Core Java Wrapper - http://maven.apache.org - Wrapper for seldon-core Java prediction models. Allows easy creation of a Spring Boot app with Tomcat and gRPC servers for handling the microservice APIs for seldon-core. - - Seldon - https://www.seldon.io/ - - - - Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0 - - - - - cc - Clive Cox - cc at seldon.io - Seldon - http://www.seldon.io - - Project lead - - - - - scm:git:git://github.com/SeldonIO/seldon-core.git - https://github.com/SeldonIO/seldon-core/tree/master - scm:git:git@github.com:SeldonIO/seldon-core.git - - - - org.springframework.boot - spring-boot-starter-parent - 1.5.1.RELEASE - - - - 1.8 - UTF-8 - UTF-8 - 1.0.0 - 1.0.0-rc.1 - io.seldon.wrapper.App - - - - ossrh - https://oss.sonatype.org/content/repositories/snapshots - - - ossrh - https://oss.sonatype.org/service/local/staging/deploy/maven2/ - - - - ${project.artifactId}-${project.version} - - - kr.motd.maven - os-maven-plugin - 1.4.1.Final - - - - - org.springframework.boot - spring-boot-maven-plugin - 1.4.1.RELEASE - - - - repackage - - - exec - - - - - - maven-compiler-plugin - 3.5.1 - - 1.8 - 1.8 - UTF-8 - - - - org.springframework.boot - spring-boot-maven-plugin - - - org.xolstice.maven.plugins - protobuf-maven-plugin - 0.5.0 - - com.google.protobuf:protoc:3.1.0:exe:${os.detected.classifier} - grpc-java - io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier} - false - true - - k8s.io/**/*.proto - **/v1.proto - - - - - - compile - compile-custom - - - - - - org.apache.maven.plugins - maven-source-plugin - 2.2.1 - - - attach-sources - - jar-no-fork - - - - - - org.apache.maven.plugins - maven-javadoc-plugin - 2.9.1 - - - attach-javadocs - - jar - - - - - - org.sonatype.plugins - nexus-staging-maven-plugin - 1.6.7 - true - - ossrh - https://oss.sonatype.org/ - true - - - - org.apache.maven.plugins - maven-gpg-plugin - 1.5 - - Seldon Dev Team - - - - sign-artifacts - verify - - sign - - - - - - - - - - - org.springframework.boot - spring-boot-starter-test - test - - - log4j - log4j - 1.2.17 - - - com.fasterxml.jackson.core - jackson-core - 2.8.6 - - - com.fasterxml.jackson.core - jackson-databind - 2.8.6 - - - commons-lang - commons-lang - 2.6 - - - - org.apache.commons - commons-lang3 - 3.5 - - - org.springframework.boot - spring-boot-starter-web - - - org.apache.httpcomponents - httpclient - 4.3.4 - - - - - io.grpc - grpc-netty - ${grpc.version} - - - io.grpc - grpc-stub - ${grpc.version} - - - io.grpc - grpc-protobuf - ${grpc.version} - - - - com.google.protobuf - protobuf-java - 3.2.0 - - - com.google.protobuf - protobuf-java-util - 3.2.0rc2 - - - com.google.guava - guava - 22.0 - - - - org.springframework.boot - spring-boot-starter-actuator - - - - ai.h2o - h2o-genmodel - 3.18.0.5 - - - org.nd4j - nd4j-native-platform - 0.9.1 - - - - org.jpmml - pmml-evaluator - 1.4.1 - - - org.jpmml - pmml-evaluator-extension - 1.4.1 - - - - - diff --git a/wrappers/s2i/java/wrapper/src/main/config/application.properties b/wrappers/s2i/java/wrapper/src/main/config/application.properties deleted file mode 100644 index 4a35865320..0000000000 --- a/wrappers/s2i/java/wrapper/src/main/config/application.properties +++ /dev/null @@ -1,7 +0,0 @@ -server.port = 9000 - -seldon.api.model.enabled=true -seldon.api.route.enabled=false - -spring.jmx.enabled = false - diff --git a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/App.java b/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/App.java deleted file mode 100644 index 239d613eb7..0000000000 --- a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/App.java +++ /dev/null @@ -1,20 +0,0 @@ -package io.seldon.wrapper; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; -import org.springframework.context.annotation.Import; -import org.springframework.scheduling.annotation.EnableAsync; - -import io.seldon.wrapper.config.AppConfig; - -@SpringBootApplication -@EnableAsync -@Import({ AppConfig.class }) -public class App { - public static void main(String[] args) throws Exception { - SpringApplication.run(App.class, args); - } - - -} diff --git a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/api/CombinerRestController.java b/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/api/CombinerRestController.java deleted file mode 100644 index e1ad4db692..0000000000 --- a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/api/CombinerRestController.java +++ /dev/null @@ -1,58 +0,0 @@ -package io.seldon.wrapper.api; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; - -import com.google.protobuf.InvalidProtocolBufferException; - -import io.seldon.protos.PredictionProtos.SeldonMessage; -import io.seldon.protos.PredictionProtos.SeldonMessageList; -import io.seldon.wrapper.exception.APIException; -import io.seldon.wrapper.exception.APIException.ApiExceptionType; -import io.seldon.wrapper.pb.ProtoBufUtils; - -@RestController -@ConditionalOnExpression("${seldon.api.combiner.enabled:false}") -public class CombinerRestController { - - private static Logger logger = LoggerFactory.getLogger(RouterRestController.class.getName()); - - @Autowired - SeldonPredictionService predictionService; - - @RequestMapping(value = "/aggregate", method = {RequestMethod.GET, RequestMethod.POST}, produces = "application/json; charset=utf-8") - public ResponseEntity route( @RequestParam("json") String json) - { - SeldonMessageList request; - try - { - SeldonMessageList.Builder builder = SeldonMessageList.newBuilder(); - ProtoBufUtils.updateMessageBuilderFromJson(builder, json ); - request = builder.build(); - } - catch (InvalidProtocolBufferException e) - { - logger.error("Bad request",e); - throw new APIException(ApiExceptionType.WRAPPER_INVALID_MESSAGE,json); - } - - try - { - SeldonMessage response = predictionService.aggregate(request); - String res = ProtoBufUtils.toJson(response); - return new ResponseEntity(res,HttpStatus.OK); - } - catch (InvalidProtocolBufferException e) { - throw new APIException(ApiExceptionType.WRAPPER_INVALID_MESSAGE,""); - } - } -} - \ No newline at end of file diff --git a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/api/ModelRestController.java b/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/api/ModelRestController.java deleted file mode 100644 index 7ef1b857e8..0000000000 --- a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/api/ModelRestController.java +++ /dev/null @@ -1,59 +0,0 @@ -package io.seldon.wrapper.api; - - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; - -import com.google.protobuf.InvalidProtocolBufferException; - -import io.seldon.protos.PredictionProtos.SeldonMessage; -import io.seldon.wrapper.exception.APIException; -import io.seldon.wrapper.exception.APIException.ApiExceptionType; -import io.seldon.wrapper.pb.ProtoBufUtils; - -@RestController -@ConditionalOnExpression("${seldon.api.model.enabled:false}") -public class ModelRestController { - private static Logger logger = LoggerFactory.getLogger(ModelRestController.class.getName()); - - - @Autowired - SeldonPredictionService predictionService; - - @RequestMapping(value = "/predict", method = {RequestMethod.GET, RequestMethod.POST}, produces = "application/json; charset=utf-8") - public ResponseEntity predictions( @RequestParam("json") String json) - { - SeldonMessage request; - try - { - SeldonMessage.Builder builder = SeldonMessage.newBuilder(); - ProtoBufUtils.updateMessageBuilderFromJson(builder, json ); - request = builder.build(); - } - catch (InvalidProtocolBufferException e) - { - logger.error("Bad request",e); - throw new APIException(ApiExceptionType.WRAPPER_INVALID_MESSAGE,json); - } - - try - { - SeldonMessage response = predictionService.predict(request); - String res = ProtoBufUtils.toJson(response); - return new ResponseEntity(res,HttpStatus.OK); - } - catch (InvalidProtocolBufferException e) { - throw new APIException(ApiExceptionType.WRAPPER_INVALID_MESSAGE,""); - } - - - } -} diff --git a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/api/RouterRestController.java b/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/api/RouterRestController.java deleted file mode 100644 index bc0a74a0fb..0000000000 --- a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/api/RouterRestController.java +++ /dev/null @@ -1,85 +0,0 @@ -package io.seldon.wrapper.api; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; - -import com.google.protobuf.InvalidProtocolBufferException; - -import io.seldon.protos.PredictionProtos.Feedback; -import io.seldon.protos.PredictionProtos.SeldonMessage; -import io.seldon.wrapper.exception.APIException; -import io.seldon.wrapper.exception.APIException.ApiExceptionType; -import io.seldon.wrapper.pb.ProtoBufUtils; - -@RestController -@ConditionalOnExpression("${seldon.api.router.enabled:false}") -public class RouterRestController { - - private static Logger logger = LoggerFactory.getLogger(RouterRestController.class.getName()); - - @Autowired - SeldonPredictionService predictionService; - - @RequestMapping(value = "/route", method = {RequestMethod.GET, RequestMethod.POST}, produces = "application/json; charset=utf-8") - public ResponseEntity route( @RequestParam("json") String json) - { - SeldonMessage request; - try - { - SeldonMessage.Builder builder = SeldonMessage.newBuilder(); - ProtoBufUtils.updateMessageBuilderFromJson(builder, json ); - request = builder.build(); - } - catch (InvalidProtocolBufferException e) - { - logger.error("Bad request",e); - throw new APIException(ApiExceptionType.WRAPPER_INVALID_MESSAGE,json); - } - - try - { - SeldonMessage response = predictionService.route(request); - String res = ProtoBufUtils.toJson(response); - return new ResponseEntity(res,HttpStatus.OK); - } - catch (InvalidProtocolBufferException e) { - throw new APIException(ApiExceptionType.WRAPPER_INVALID_MESSAGE,""); - } - } - - @RequestMapping(value = "/send-feedback", method = {RequestMethod.GET, RequestMethod.POST}, produces = "application/json; charset=utf-8") - public ResponseEntity sendFeedback( @RequestParam("json") String json) - { - Feedback request; - try - { - Feedback.Builder builder = Feedback.newBuilder(); - ProtoBufUtils.updateMessageBuilderFromJson(builder, json ); - request = builder.build(); - } - catch (InvalidProtocolBufferException e) - { - logger.error("Bad request",e); - throw new APIException(ApiExceptionType.WRAPPER_INVALID_MESSAGE,json); - } - - try - { - SeldonMessage response = predictionService.sendFeedback(request); - String res = ProtoBufUtils.toJson(response); - return new ResponseEntity(res,HttpStatus.OK); - } - catch (InvalidProtocolBufferException e) { - throw new APIException(ApiExceptionType.WRAPPER_INVALID_MESSAGE,""); - } - } - -} \ No newline at end of file diff --git a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/api/SeldonPredictionService.java b/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/api/SeldonPredictionService.java deleted file mode 100644 index 70f575689d..0000000000 --- a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/api/SeldonPredictionService.java +++ /dev/null @@ -1,26 +0,0 @@ -package io.seldon.wrapper.api; - -import io.seldon.protos.PredictionProtos.Feedback; -import io.seldon.protos.PredictionProtos.SeldonMessage; -import io.seldon.protos.PredictionProtos.SeldonMessageList; - -public interface SeldonPredictionService { - default public SeldonMessage predict(SeldonMessage request) { - return null; - } - default public SeldonMessage route(SeldonMessage request) { - return null; - } - default public SeldonMessage sendFeedback(Feedback request) { - return null; - } - default public SeldonMessage transformInput(SeldonMessage request) { - return null; - } - default public SeldonMessage transformOutput(SeldonMessage request) { - return null; - } - default public SeldonMessage aggregate(SeldonMessageList request) { - return null; - } -} diff --git a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/api/TransformerRestController.java b/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/api/TransformerRestController.java deleted file mode 100644 index 474ec8c706..0000000000 --- a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/api/TransformerRestController.java +++ /dev/null @@ -1,84 +0,0 @@ -package io.seldon.wrapper.api; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; - -import com.google.protobuf.InvalidProtocolBufferException; - -import io.seldon.protos.PredictionProtos.SeldonMessage; -import io.seldon.wrapper.exception.APIException; -import io.seldon.wrapper.exception.APIException.ApiExceptionType; -import io.seldon.wrapper.pb.ProtoBufUtils; - -@RestController -@ConditionalOnExpression("${seldon.api.transformer.enabled:false}") -public class TransformerRestController { - - private static Logger logger = LoggerFactory.getLogger(TransformerRestController.class.getName()); - - @Autowired - SeldonPredictionService predictionService; - - @RequestMapping(value = "/transform-input", method = {RequestMethod.GET, RequestMethod.POST}, produces = "application/json; charset=utf-8") - public ResponseEntity transformInput( @RequestParam("json") String json) - { - SeldonMessage request; - try - { - SeldonMessage.Builder builder = SeldonMessage.newBuilder(); - ProtoBufUtils.updateMessageBuilderFromJson(builder, json ); - request = builder.build(); - } - catch (InvalidProtocolBufferException e) - { - logger.error("Bad request",e); - throw new APIException(ApiExceptionType.WRAPPER_INVALID_MESSAGE,json); - } - - try - { - SeldonMessage response = predictionService.transformInput(request); - String res = ProtoBufUtils.toJson(response); - return new ResponseEntity(res,HttpStatus.OK); - } - catch (InvalidProtocolBufferException e) { - throw new APIException(ApiExceptionType.WRAPPER_INVALID_MESSAGE,""); - } - } - - @RequestMapping(value = "/transform-output", method = {RequestMethod.GET, RequestMethod.POST}, produces = "application/json; charset=utf-8") - public ResponseEntity transformOutput( @RequestParam("json") String json) - { - SeldonMessage request; - try - { - SeldonMessage.Builder builder = SeldonMessage.newBuilder(); - ProtoBufUtils.updateMessageBuilderFromJson(builder, json ); - request = builder.build(); - } - catch (InvalidProtocolBufferException e) - { - logger.error("Bad request",e); - throw new APIException(ApiExceptionType.WRAPPER_INVALID_MESSAGE,json); - } - - try - { - SeldonMessage response = predictionService.transformOutput(request); - String res = ProtoBufUtils.toJson(response); - return new ResponseEntity(res,HttpStatus.OK); - } - catch (InvalidProtocolBufferException e) { - throw new APIException(ApiExceptionType.WRAPPER_INVALID_MESSAGE,""); - } - } -} - \ No newline at end of file diff --git a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/config/AppConfig.java b/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/config/AppConfig.java deleted file mode 100644 index d55b9e53ce..0000000000 --- a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/config/AppConfig.java +++ /dev/null @@ -1,100 +0,0 @@ -package io.seldon.wrapper.config; - -import java.util.concurrent.Executor; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; - -import org.apache.catalina.connector.Connector; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.config.ConfigurableBeanFactory; -import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer; -import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer; -import org.springframework.boot.context.embedded.tomcat.TomcatConnectorCustomizer; -import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory; -import org.springframework.context.ApplicationListener; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Scope; -import org.springframework.context.event.ContextClosedEvent; - - -/** - * Spring REST config - * @author clive - * - */ -public class AppConfig { - - @Bean - @Scope(ConfigurableBeanFactory.SCOPE_SINGLETON) - public EmbeddedServletContainerCustomizer containerCustomizer() { - return new CustomizationBean(); - } - - @Bean - public GracefulShutdown gracefulShutdown() { - return new GracefulShutdown(); - } - - @Bean - public EmbeddedServletContainerCustomizer tomcatCustomizer() { - return new EmbeddedServletContainerCustomizer() { - - @Override - public void customize(ConfigurableEmbeddedServletContainer container) { - if (container instanceof TomcatEmbeddedServletContainerFactory) { - ((TomcatEmbeddedServletContainerFactory) container) - .addConnectorCustomizers(gracefulShutdown()); - } - - } - }; - } - - /** - * Ensure a graceful shutdown of Tomcat to allow requests in process to complete. - * @author clive - * - */ - private static class GracefulShutdown implements TomcatConnectorCustomizer, - ApplicationListener { - - private static final Logger log = LoggerFactory.getLogger(GracefulShutdown.class); - - private volatile Connector connector; - - @Override - public void customize(Connector connector) { - this.connector = connector; - } - - @Override - public void onApplicationEvent(ContextClosedEvent event) { - log.info("Starting graceful shutdown of Tomcat"); - if (this.connector != null) - { - this.connector.pause(); - Executor executor = this.connector.getProtocolHandler().getExecutor(); - if (executor instanceof ThreadPoolExecutor) { - try { - ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executor; - threadPoolExecutor.shutdown(); - if (!threadPoolExecutor.awaitTermination(20, TimeUnit.SECONDS)) { - log.warn("Tomcat thread pool did not shut down gracefully within " - + "20 seconds. Proceeding with forceful shutdown"); - } - else - { - log.info("Thread pool has closed"); - } - } - catch (InterruptedException ex) { - Thread.currentThread().interrupt(); - } - } - } - } - - } -} - diff --git a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/config/CustomizationBean.java b/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/config/CustomizationBean.java deleted file mode 100644 index bb9839d6ca..0000000000 --- a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/config/CustomizationBean.java +++ /dev/null @@ -1,47 +0,0 @@ -/******************************************************************************* - * Copyright 2017 Seldon Technologies Ltd (http://www.seldon.io/) - * - * 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. - *******************************************************************************/ -package io.seldon.wrapper.config; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer; -import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer; - -/** - * Customization of the Tomcat embedded servlet engne - * @author clive - * - */ -public class CustomizationBean implements EmbeddedServletContainerCustomizer { - - private final static Logger logger = LoggerFactory.getLogger(CustomizationBean.class); - - @Value("${server.port}") - private Integer defaultServerPort; - - @Override - public void customize(ConfigurableEmbeddedServletContainer container) { - logger.info("Customizing EmbeddedServlet"); - - Integer serverPort; - serverPort = defaultServerPort; - - logger.info("setting serverPort[{}]", serverPort); - container.setPort(serverPort); - } - -} diff --git a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/exception/APIException.java b/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/exception/APIException.java deleted file mode 100644 index 33603cb49b..0000000000 --- a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/exception/APIException.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Seldon -- open source prediction engine - * ======================================= - * - * Copyright 2011-2017 Seldon Technologies Ltd and Rummble Ltd (http://www.seldon.io/) - * - * ******************************************************************************************** - * - * 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. - * - * ******************************************************************************************** - */ - -package io.seldon.wrapper.exception; - -/** - * API Exceptions - * @author clive - * - */ -public class APIException extends RuntimeException { - - public enum ApiExceptionType { - - WRAPPER_INVALID_MESSAGE(201,"Invalid prediction message",500); - - int id; - String message; - int httpCode; - - ApiExceptionType(int id,String message,int httpCode) { - this.id = id; - this.message = message; - this.httpCode = httpCode; - } - - public int getId() { - return id; - } - - public String getMessage() { - return message; - } - - public int getHttpCode() { - return httpCode; - } - - - }; - - ApiExceptionType apiExceptionType; - String info; - - public APIException(ApiExceptionType apiExceptionType,String info) { - super(); - this.apiExceptionType = apiExceptionType; - this.info = info; - } - - public ApiExceptionType getApiExceptionType() { - return apiExceptionType; - } - - public void setApiExceptionType(ApiExceptionType apiExceptionType) { - this.apiExceptionType = apiExceptionType; - } - - public String getInfo() { - return info; - } - - public void setInfo(String info) { - this.info = info; - } - -} diff --git a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/grpc/CombinerService.java b/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/grpc/CombinerService.java deleted file mode 100644 index 90d74c735c..0000000000 --- a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/grpc/CombinerService.java +++ /dev/null @@ -1,26 +0,0 @@ -package io.seldon.wrapper.grpc; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.seldon.protos.CombinerGrpc; - -public class CombinerService extends CombinerGrpc.CombinerImplBase { - - protected static Logger logger = LoggerFactory.getLogger(CombinerService.class.getName()); - - private SeldonGrpcServer server; - - public CombinerService(SeldonGrpcServer server) { - super(); - this.server = server; - } - - @Override - public void aggregate(io.seldon.protos.PredictionProtos.SeldonMessageList request, - io.grpc.stub.StreamObserver responseObserver) { - logger.debug("Received aggregate request"); - responseObserver.onNext(server.getPredictionService().aggregate(request)); - responseObserver.onCompleted(); - } -} \ No newline at end of file diff --git a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/grpc/GenericService.java b/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/grpc/GenericService.java deleted file mode 100644 index b4047324a7..0000000000 --- a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/grpc/GenericService.java +++ /dev/null @@ -1,60 +0,0 @@ -package io.seldon.wrapper.grpc; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.seldon.protos.GenericGrpc; - -public class GenericService extends GenericGrpc.GenericImplBase { - - protected static Logger logger = LoggerFactory.getLogger(GenericService.class.getName()); - - private SeldonGrpcServer server; - - public GenericService(SeldonGrpcServer server) { - super(); - this.server = server; - } - - @Override - public void transformInput(io.seldon.protos.PredictionProtos.SeldonMessage request, - io.grpc.stub.StreamObserver responseObserver) { - logger.debug("Received transformInput request"); - responseObserver.onNext(server.getPredictionService().transformInput(request)); - responseObserver.onCompleted(); - } - - @Override - public void route(io.seldon.protos.PredictionProtos.SeldonMessage request, - io.grpc.stub.StreamObserver responseObserver) { - logger.debug("Received route request"); - responseObserver.onNext(server.getPredictionService().route(request)); - responseObserver.onCompleted(); - } - - @Override - public void sendFeedback(io.seldon.protos.PredictionProtos.Feedback request, - io.grpc.stub.StreamObserver responseObserver) { - logger.debug("Received sendFeedback request"); - responseObserver.onNext(server.getPredictionService().sendFeedback(request)); - responseObserver.onCompleted(); - } - - - - @Override - public void transformOutput(io.seldon.protos.PredictionProtos.SeldonMessage request, - io.grpc.stub.StreamObserver responseObserver) { - logger.debug("Received transformOutput request"); - responseObserver.onNext(server.getPredictionService().transformOutput(request)); - responseObserver.onCompleted(); - } - - @Override - public void aggregate(io.seldon.protos.PredictionProtos.SeldonMessageList request, - io.grpc.stub.StreamObserver responseObserver) { - logger.debug("Received aggregate request"); - responseObserver.onNext(server.getPredictionService().aggregate(request)); - responseObserver.onCompleted(); - } -} diff --git a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/grpc/ModelService.java b/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/grpc/ModelService.java deleted file mode 100644 index 25f558bef2..0000000000 --- a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/grpc/ModelService.java +++ /dev/null @@ -1,48 +0,0 @@ -/******************************************************************************* - * Copyright 2017 Seldon Technologies Ltd (http://www.seldon.io/) - * - * 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. - *******************************************************************************/ -package io.seldon.wrapper.grpc; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.seldon.protos.ModelGrpc; - -/** - * Passes gRPC requests on to the engine. - * @author clive - * - */ -public class ModelService extends ModelGrpc.ModelImplBase { - - protected static Logger logger = LoggerFactory.getLogger(ModelService.class.getName()); - - private SeldonGrpcServer server; - - public ModelService(SeldonGrpcServer server) { - super(); - this.server = server; - } - - @Override - public void predict(io.seldon.protos.PredictionProtos.SeldonMessage request, - io.grpc.stub.StreamObserver responseObserver) { - logger.debug("Received predict request"); - - responseObserver.onNext(server.getPredictionService().predict(request)); - responseObserver.onCompleted(); - } - -} diff --git a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/grpc/OutputTransformerService.java b/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/grpc/OutputTransformerService.java deleted file mode 100644 index 6ccedcc99c..0000000000 --- a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/grpc/OutputTransformerService.java +++ /dev/null @@ -1,26 +0,0 @@ -package io.seldon.wrapper.grpc; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.seldon.protos.OutputTransformerGrpc; - -public class OutputTransformerService extends OutputTransformerGrpc.OutputTransformerImplBase { - - protected static Logger logger = LoggerFactory.getLogger(OutputTransformerService.class.getName()); - - private SeldonGrpcServer server; - - public OutputTransformerService(SeldonGrpcServer server) { - super(); - this.server = server; - } - - @Override - public void transformOutput(io.seldon.protos.PredictionProtos.SeldonMessage request, - io.grpc.stub.StreamObserver responseObserver) { - logger.debug("Received transformOutput request"); - responseObserver.onNext(server.getPredictionService().transformOutput(request)); - responseObserver.onCompleted(); - } -} \ No newline at end of file diff --git a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/grpc/RouterService.java b/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/grpc/RouterService.java deleted file mode 100644 index 6589451723..0000000000 --- a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/grpc/RouterService.java +++ /dev/null @@ -1,38 +0,0 @@ -package io.seldon.wrapper.grpc; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.seldon.protos.RouterGrpc; - -public class RouterService extends RouterGrpc.RouterImplBase { - - protected static Logger logger = LoggerFactory.getLogger(RouterService.class.getName()); - - private SeldonGrpcServer server; - - public RouterService(SeldonGrpcServer server) { - super(); - this.server = server; - } - - @Override - public void route(io.seldon.protos.PredictionProtos.SeldonMessage request, - io.grpc.stub.StreamObserver responseObserver) { - logger.debug("Received route request"); - responseObserver.onNext(server.getPredictionService().route(request)); - responseObserver.onCompleted(); - } - - @Override - public void sendFeedback(io.seldon.protos.PredictionProtos.Feedback request, - io.grpc.stub.StreamObserver responseObserver) { - logger.debug("Received sendFeedback request"); - responseObserver.onNext(server.getPredictionService().sendFeedback(request)); - responseObserver.onCompleted(); - } - - - -} - diff --git a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/grpc/SeldonGrpcServer.java b/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/grpc/SeldonGrpcServer.java deleted file mode 100644 index 24e08640eb..0000000000 --- a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/grpc/SeldonGrpcServer.java +++ /dev/null @@ -1,109 +0,0 @@ -/******************************************************************************* - * Copyright 2017 Seldon Technologies Ltd (http://www.seldon.io/) - * - * 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. - *******************************************************************************/ -package io.seldon.wrapper.grpc; - -import java.io.IOException; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.scheduling.annotation.Async; -import org.springframework.stereotype.Component; - -import io.grpc.Server; -import io.grpc.ServerBuilder; -import io.seldon.wrapper.api.SeldonPredictionService; - -@Component -public class SeldonGrpcServer { - protected static Logger logger = LoggerFactory.getLogger(SeldonGrpcServer.class.getName()); - - public static final int SERVER_PORT = 5001; - - private final int port; - private final Server server; - - private final SeldonPredictionService predictionService; - - @Autowired - public SeldonGrpcServer(SeldonPredictionService predictionService,@Value("${grpc.port}") Integer grpcPort) - { - logger.info("grpc port {}",grpcPort); - - port = grpcPort; - - this.predictionService = predictionService; - server = ServerBuilder - .forPort(port) - .addService(new ModelService(this)) - .addService(new RouterService(this)) - .addService(new TransformerService(this)) - .addService(new OutputTransformerService(this)) - .addService(new CombinerService(this)) - .addService(new GenericService(this)) - .build(); - } - - - - public SeldonPredictionService getPredictionService() { - return predictionService; - } - - @Async - public void runServer() throws InterruptedException, IOException - { - logger.info("Starting grpc server"); - start(); - blockUntilShutdown(); - } - - /** - * Start serving requests. - */ - public void start() throws IOException { - server.start(); - logger.info("Server started, listening on " + port); - Runtime.getRuntime().addShutdownHook(new Thread() { - @Override - public void run() { - // Use stderr here since the logger may has been reset by its JVM shutdown hook. - System.err.println("*** shutting down gRPC server since JVM is shutting down"); - SeldonGrpcServer.this.stop(); - System.err.println("*** server shut down"); - } - }); - } - - /** Stop serving requests and shutdown resources. */ - public void stop() { - if (server != null) { - server.shutdown(); - } - } - - /** - * Await termination on the main thread since the grpc library uses daemon threads. - */ - private void blockUntilShutdown() throws InterruptedException { - if (server != null) { - server.awaitTermination(); - } - } - - -} diff --git a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/grpc/SeldonGrpcServerInitializer.java b/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/grpc/SeldonGrpcServerInitializer.java deleted file mode 100644 index 0555cbdef0..0000000000 --- a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/grpc/SeldonGrpcServerInitializer.java +++ /dev/null @@ -1,47 +0,0 @@ -/******************************************************************************* - * Copyright 2017 Seldon Technologies Ltd (http://www.seldon.io/) - * - * 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. - *******************************************************************************/ -package io.seldon.wrapper.grpc; - -import java.io.IOException; - -import javax.annotation.PostConstruct; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -@Component -public class SeldonGrpcServerInitializer { - - protected static Logger logger = LoggerFactory.getLogger(SeldonGrpcServerInitializer.class.getName()); - - @Autowired - SeldonGrpcServer server; - - @PostConstruct - public void initialise() { - try - { - server.runServer(); - } catch (InterruptedException e) { - logger.error("Failed to start grc server",e); - } catch (IOException e) { - logger.error("Failed to start grc server",e); - } - } - -} diff --git a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/grpc/TransformerService.java b/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/grpc/TransformerService.java deleted file mode 100644 index d9c60a05cc..0000000000 --- a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/grpc/TransformerService.java +++ /dev/null @@ -1,26 +0,0 @@ -package io.seldon.wrapper.grpc; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.seldon.protos.TransformerGrpc; - -public class TransformerService extends TransformerGrpc.TransformerImplBase { - - protected static Logger logger = LoggerFactory.getLogger(TransformerService.class.getName()); - - private SeldonGrpcServer server; - - public TransformerService(SeldonGrpcServer server) { - super(); - this.server = server; - } - - @Override - public void transformInput(io.seldon.protos.PredictionProtos.SeldonMessage request, - io.grpc.stub.StreamObserver responseObserver) { - logger.debug("Received transformInput request"); - responseObserver.onNext(server.getPredictionService().transformInput(request)); - responseObserver.onCompleted(); - } -} diff --git a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/pb/ProtoBufUtils.java b/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/pb/ProtoBufUtils.java deleted file mode 100644 index 7794ec8bb2..0000000000 --- a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/pb/ProtoBufUtils.java +++ /dev/null @@ -1,69 +0,0 @@ -/******************************************************************************* - * Copyright 2017 Seldon Technologies Ltd (http://www.seldon.io/) - * - * 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. - *******************************************************************************/ -package io.seldon.wrapper.pb; - -import com.google.protobuf.InvalidProtocolBufferException; -import com.google.protobuf.Message; -import com.google.protobuf.util.JsonFormat; -import com.google.protobuf.util.JsonFormat.Printer; - -public class ProtoBufUtils { - - private ProtoBufUtils() { - } - - /** - * Serialize a protobuf message to JSON, indicating if whitespace needs to be stripped. - * - * @param message - * The protobuf message to serialize. - * @param omittingInsignificantWhitespace - * True if needs to be stripped of whitespace. - * @return json string - * @throws InvalidProtocolBufferException - */ - public static String toJson(Message message, boolean omittingInsignificantWhitespace) throws InvalidProtocolBufferException { - String json = null; - // json = JsonFormat.printer().includingDefaultValueFields().preservingProtoFieldNames().print(message); - // json = - // JsonFormat.printer().includingDefaultValueFields().preservingProtoFieldNames().omittingInsignificantWhitespace().print(message); - - Printer jsonPrinter = JsonFormat.printer().includingDefaultValueFields().preservingProtoFieldNames(); - if (omittingInsignificantWhitespace) { - jsonPrinter = jsonPrinter.omittingInsignificantWhitespace(); - } - return jsonPrinter.print(message); - } - - /** - * Serialize a protobuf message to JSON, allowing the inclusion of whitespace. - * - * @param message - * The protobuf message to serialize. - * @return json string - * @throws InvalidProtocolBufferException - */ - public static String toJson(Message message) throws InvalidProtocolBufferException { - boolean omittingInsignificantWhitespace = false; - return toJson(message, omittingInsignificantWhitespace); - } - - public static void updateMessageBuilderFromJson(T messageBuilder, String json) throws InvalidProtocolBufferException { - JsonFormat.parser().ignoringUnknownFields() - .merge(json, messageBuilder); - } - -} diff --git a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/utils/DL4JUtils.java b/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/utils/DL4JUtils.java deleted file mode 100644 index db6ae5f4e2..0000000000 --- a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/utils/DL4JUtils.java +++ /dev/null @@ -1,115 +0,0 @@ -package io.seldon.wrapper.utils; - -import java.util.Arrays; -import java.util.List; -import java.util.stream.Collectors; - -import org.nd4j.linalg.api.ndarray.INDArray; -import org.nd4j.linalg.factory.Nd4j; - -import com.google.protobuf.ListValue; -import com.google.protobuf.Value; - -import io.seldon.protos.PredictionProtos.DefaultData; -import io.seldon.protos.PredictionProtos.Tensor; -import io.seldon.protos.PredictionProtos.DefaultData.DataOneofCase; - -/** - * Utilities for working with Deep Learning4J Models - * @author clive - * - */ -public class DL4JUtils { - - /** - * Convert seldon protobuf DefaultData to nd4j Array - * @param data Seldon protobuf message - * @return nd4j Array - */ - public static INDArray getINDArray(DefaultData data) { - - if (data.getDataOneofCase() == DataOneofCase.TENSOR) { - - List valuesList = data.getTensor().getValuesList(); - List shapeList = data.getTensor().getShapeList(); - - double[] values = new double[valuesList.size()]; - int[] shape = new int[shapeList.size()]; - for (int i = 0; i < values.length; i++) { - values[i] = valuesList.get(i); - } - for (int i = 0; i < shape.length; i++) { - shape[i] = shapeList.get(i); - } - - INDArray newArr = Nd4j.create(values, shape, 'c'); - - return newArr; - } else if (data.getDataOneofCase() == DataOneofCase.NDARRAY) { - ListValue list = data.getNdarray(); - int bLength = list.getValuesCount(); - int vLength = list.getValues(0).getListValue().getValuesCount(); - - double[] values = new double[bLength * vLength]; - int[] shape = { bLength, vLength }; - - for (int i = 0; i < bLength; ++i) { - for (int j = 0; j < vLength; j++) { - values[i * bLength + j] = list.getValues(i).getListValue().getValues(j).getNumberValue(); - } - } - - INDArray newArr = Nd4j.create(values, shape, 'c'); - - return newArr; - } - return null; - } - - /** - * Convert a nd4j array into a seldon protobuf DefaultData following same type as oldData - * @param oldData original data - * @param newData nd4j array - * @return seldon DefaultData protobuf message - */ - public static DefaultData updateData(DefaultData oldData, INDArray newData) { - DefaultData.Builder dataBuilder = DefaultData.newBuilder(); - - dataBuilder.addAllNames(oldData.getNamesList()); - - // int index=0; - // for (Iterator i = oldData.getFeaturesList().iterator(); - // i.hasNext();){ - // dataBuilder.setFeatures(index, i.next()); - // index++; - // } - - if (oldData == null || oldData.getDataOneofCase() == DataOneofCase.TENSOR) { - Tensor.Builder tBuilder = Tensor.newBuilder(); - List shapeList = Arrays.stream(newData.shape()).boxed().collect(Collectors.toList()); - tBuilder.addAllShape(shapeList); - - for (int i = 0; i < shapeList.get(0); ++i) { - for (int j = 0; j < shapeList.get(1); ++j) { - tBuilder.addValues(newData.getDouble(i, j)); - } - } - dataBuilder.setTensor(tBuilder); - return dataBuilder.build(); - } else if (oldData.getDataOneofCase() == DataOneofCase.NDARRAY) { - ListValue.Builder b1 = ListValue.newBuilder(); - for (int i = 0; i < newData.shape()[0]; ++i) { - ListValue.Builder b2 = ListValue.newBuilder(); - for (int j = 0; j < newData.shape()[1]; j++) { - b2.addValues(Value.newBuilder().setNumberValue(newData.getDouble(i, j))); - } - b1.addValues(Value.newBuilder().setListValue(b2.build())); - } - dataBuilder.setNdarray(b1.build()); - return dataBuilder.build(); - } - return null; - - } - -} diff --git a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/utils/H2OUtils.java b/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/utils/H2OUtils.java deleted file mode 100644 index 9c8a956d3f..0000000000 --- a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/utils/H2OUtils.java +++ /dev/null @@ -1,183 +0,0 @@ -package io.seldon.wrapper.utils; - -import java.util.ArrayList; -import java.util.List; - -import org.nd4j.linalg.dataset.api.iterator.CachingDataSetIterator; - -import com.google.protobuf.ListValue; -import com.google.protobuf.Value; - -import hex.genmodel.easy.RowData; -import hex.genmodel.easy.prediction.AbstractPrediction; -import hex.genmodel.easy.prediction.BinomialModelPrediction; -import hex.genmodel.easy.prediction.ClusteringModelPrediction; -import hex.genmodel.easy.prediction.DimReductionModelPrediction; -import hex.genmodel.easy.prediction.MultinomialModelPrediction; -import hex.genmodel.easy.prediction.OrdinalModelPrediction; -import hex.genmodel.easy.prediction.RegressionModelPrediction; -import io.seldon.protos.PredictionProtos.DefaultData; -import io.seldon.protos.PredictionProtos.Tensor; -import io.seldon.protos.PredictionProtos.DefaultData.DataOneofCase; - -/** - * Utilities for working with H2O models - * - * @author clive - * - */ -public class H2OUtils { - - /** - * Convert a Seldon Default Data into H2O RowData - * @param data Seldon protobuf data - * @return List of H2O RowData - */ - public static List convertSeldonMessage(DefaultData data) { - List out = new ArrayList<>(); - if (data.getDataOneofCase() == DataOneofCase.TENSOR) { - - List valuesList = data.getTensor().getValuesList(); - List shapeList = data.getTensor().getShapeList(); - - if (shapeList.size() > 2) { - return null; - } - - int cols = 0; - if (shapeList.size() == 1) - cols = shapeList.get(0); - else - cols = shapeList.get(1); - RowData row = new RowData(); - for (int i = 0; i < valuesList.size(); i++) { - if (i > 0 && i % cols == 0) { - out.add(row); - row = new RowData(); - } - String name = data.getNamesCount() > 0 ? data.getNames(i % cols) : "" + (i % cols); - row.put(name, valuesList.get(i)); - } - out.add(row); - - return out; - } else if (data.getDataOneofCase() == DataOneofCase.NDARRAY) { - ListValue list = data.getNdarray(); - int rows = list.getValuesCount(); - int cols = list.getValues(0).getListValue().getValuesCount(); - - for (int i = 0; i < rows; ++i) { - RowData row = new RowData(); - for (int j = 0; j < cols; j++) { - String name = data.getNamesCount() > 0 ? data.getNames(j % cols) : "" + (j % cols); - Object value; - Value listValue = list.getValues(i).getListValue().getValues(j); - switch(listValue.getKindCase()) { - case NUMBER_VALUE: - value = listValue.getNumberValue(); - break; - case STRING_VALUE: - value = listValue.getStringValue(); - break; - case BOOL_VALUE: - //Get value as String - value = listValue.getStringValue(); - break; - case NULL_VALUE: - // Treat Nulls as 0 - value = 0.0; - break; - case LIST_VALUE: - throw new UnsupportedOperationException("Only 2-D arrays unsupported for H2O conversion"); - case STRUCT_VALUE: - throw new UnsupportedOperationException("Struct in NDArray unsupported for H2O conversion"); - default: - throw new UnsupportedOperationException("Unknown kind in NDArray"); - } - row.put(name, value); - } - out.add(row); - } - return out; - } else - return null; - } - - /** - * Convert a prediction result from H2O to Seldon protobuf DefaultData with same type as input - * @param predictions The H2O predictions - * @param input The original input - * @return A seldon DefaultData protobuf message - */ - public static DefaultData convertH2OPrediction(List predictions, DefaultData input) { - if (input == null || input.getDataOneofCase() == DataOneofCase.TENSOR) { - int rows = predictions.size(); - Tensor.Builder tBuilder = Tensor.newBuilder(); - for (AbstractPrediction p : predictions) { - if (p instanceof BinomialModelPrediction) { - BinomialModelPrediction bp = (BinomialModelPrediction) p; - for (int i = 0; i < bp.classProbabilities.length; i++) - tBuilder.addValues(bp.classProbabilities[i]); - } else if (p instanceof MultinomialModelPrediction) { - MultinomialModelPrediction mp = (MultinomialModelPrediction) p; - for (int i = 0; i < mp.classProbabilities.length; i++) - tBuilder.addValues(mp.classProbabilities[i]); - } else if (p instanceof OrdinalModelPrediction) { - OrdinalModelPrediction op = (OrdinalModelPrediction) p; - for (int i = 0; i < op.classProbabilities.length; i++) - tBuilder.addValues(op.classProbabilities[i]); - } else if (p instanceof ClusteringModelPrediction) { - ClusteringModelPrediction cp = (ClusteringModelPrediction) p; - for (int i = 0; i < cp.distances.length; i++) - tBuilder.addValues(cp.distances[i]); - } else if (p instanceof RegressionModelPrediction) { - RegressionModelPrediction r = (RegressionModelPrediction) p; - tBuilder.addValues(r.value); - } else if (p instanceof DimReductionModelPrediction) { - DimReductionModelPrediction cp = (DimReductionModelPrediction) p; - for (int i = 0; i < cp.dimensions.length; i++) - tBuilder.addValues(cp.dimensions[i]); - } else - return null; - } - DefaultData.Builder dataBuilder = DefaultData.newBuilder(); - dataBuilder.setTensor(tBuilder); - return dataBuilder.build(); - } else if (input.getDataOneofCase() == DataOneofCase.NDARRAY) { - ListValue.Builder rows = ListValue.newBuilder(); - for (AbstractPrediction p : predictions) { - ListValue.Builder row = ListValue.newBuilder(); - if (p instanceof BinomialModelPrediction) { - BinomialModelPrediction bp = (BinomialModelPrediction) p; - for (int i = 0; i < bp.classProbabilities.length; i++) - row.addValues(Value.newBuilder().setNumberValue(bp.classProbabilities[i])); - } else if (p instanceof MultinomialModelPrediction) { - MultinomialModelPrediction mp = (MultinomialModelPrediction) p; - for (int i = 0; i < mp.classProbabilities.length; i++) - row.addValues(Value.newBuilder().setNumberValue(mp.classProbabilities[i])); - } else if (p instanceof OrdinalModelPrediction) { - OrdinalModelPrediction op = (OrdinalModelPrediction) p; - for (int i = 0; i < op.classProbabilities.length; i++) - row.addValues(Value.newBuilder().setNumberValue(op.classProbabilities[i])); - } else if (p instanceof ClusteringModelPrediction) { - ClusteringModelPrediction cp = (ClusteringModelPrediction) p; - for (int i = 0; i < cp.distances.length; i++) - row.addValues(Value.newBuilder().setNumberValue(cp.distances[i])); - } else if (p instanceof RegressionModelPrediction) { - RegressionModelPrediction r = (RegressionModelPrediction) p; - row.addValues(Value.newBuilder().setNumberValue(r.value)); - } else if (p instanceof DimReductionModelPrediction) { - DimReductionModelPrediction cp = (DimReductionModelPrediction) p; - for (int i = 0; i < cp.dimensions.length; i++) - row.addValues(Value.newBuilder().setNumberValue(cp.dimensions[i])); - } else - return null; - rows.addValues(Value.newBuilder().setListValue(row.build())); - } - DefaultData.Builder dataBuilder = DefaultData.newBuilder(); - dataBuilder.setNdarray(rows.build()); - return dataBuilder.build(); - } else - return null; - } -} diff --git a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/utils/PMMLUtils.java b/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/utils/PMMLUtils.java deleted file mode 100644 index 266b9ebcd0..0000000000 --- a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/utils/PMMLUtils.java +++ /dev/null @@ -1,193 +0,0 @@ -package io.seldon.wrapper.utils; - -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.dmg.pmml.FieldName; -import org.dmg.pmml.MiningFunction; -import org.dmg.pmml.PMML; -import org.jpmml.evaluator.Evaluator; -import org.jpmml.evaluator.FieldValue; -import org.jpmml.evaluator.InputField; -import org.jpmml.evaluator.ModelEvaluatorFactory; -import org.jpmml.evaluator.ProbabilityDistribution; -import org.jpmml.evaluator.ReportingValueFactoryFactory; -import org.jpmml.evaluator.TargetField; -import org.jpmml.evaluator.ValueFactoryFactory; - -import com.google.protobuf.ListValue; -import com.google.protobuf.Value; - -import hex.genmodel.easy.RowData; -import io.seldon.protos.PredictionProtos.DefaultData; -import io.seldon.protos.PredictionProtos.DefaultData.DataOneofCase; -import io.seldon.protos.PredictionProtos.Tensor; -import io.seldon.wrapper.exception.APIException; -import io.seldon.wrapper.exception.APIException.ApiExceptionType; - -public class PMMLUtils { - - - public static abstract class ValuesBuilder { - public abstract void addValues(double v); - } - - public static class TensorBuilder extends ValuesBuilder { - Tensor.Builder b; - - public TensorBuilder(Tensor.Builder b) { - super(); - this.b = b; - } - - @Override - public void addValues(double v) { - b.addValues(v); - } - } - - public static class ListBuilder extends ValuesBuilder { - ListValue.Builder b; - - public ListBuilder(ListValue.Builder b) { - super(); - this.b = b; - } - - @Override - public void addValues(double v) { - b.addValues(Value.newBuilder().setNumberValue(v)); - } - } - - private Map getInputFieldMap(Evaluator evaluator) - { - Map fieldMap = new HashMap<>(); - List inputFields = evaluator.getInputFields(); - for(InputField inputField : inputFields){ - FieldName inputFieldName = inputField.getName(); - fieldMap.put(inputFieldName.getValue(), inputField); - } - return fieldMap; - } - - - private void evaluateForTensor(Evaluator evaluator,Map arguments,ValuesBuilder tBuilder) - { - Map result = evaluator.evaluate(arguments); - List targetFields = evaluator.getTargetFields(); - MiningFunction miningFunction = evaluator.getMiningFunction(); - TargetField targetField = targetFields.get(0); - switch(miningFunction){ - case CLASSIFICATION: - FieldName targetFieldName = targetField.getName(); - Object targetFieldValue = result.get(targetFieldName); - if (targetFieldValue instanceof ProbabilityDistribution) - { - ProbabilityDistribution prob = (ProbabilityDistribution) targetFieldValue; - Set categories = prob.getCategories(); - for (String k : categories) - { - Double v = prob.getValue(k); - tBuilder.addValues(v); - } - arguments.clear(); - return; - } - else - throw new IllegalArgumentException("Expected probability distribution"); - case REGRESSION: - default: - throw new IllegalArgumentException("Expected a classification model, got " + miningFunction); - } - } - - public DefaultData evaluate(PMML model,DefaultData data) { - - if (data.getNamesCount() == 0) - { - throw new APIException(APIException.ApiExceptionType.WRAPPER_INVALID_MESSAGE, "Data for PMML models must contain names for the fields in the prediction message"); - } - - ModelEvaluatorFactory modelEvaluatorFactory = ModelEvaluatorFactory.newInstance(); - ValueFactoryFactory valueFactoryFactory = ReportingValueFactoryFactory.newInstance(); - modelEvaluatorFactory.setValueFactoryFactory(valueFactoryFactory); - Evaluator evaluator = (Evaluator)modelEvaluatorFactory.newModelEvaluator(model); - Map arguments = new LinkedHashMap<>(); - Map fieldMap = getInputFieldMap(evaluator); - - if (data.getDataOneofCase() == DataOneofCase.TENSOR) { - Tensor.Builder tBuilder = Tensor.newBuilder(); - TensorBuilder tb = new TensorBuilder(tBuilder); - List valuesList = data.getTensor().getValuesList(); - List shapeList = data.getTensor().getShapeList(); - - if (shapeList.size() > 2) { - return null; - } - - int cols = 0; - if (shapeList.size() == 1) - cols = shapeList.get(0); - else - cols = shapeList.get(1); - - for (int i = 0; i < valuesList.size(); i++) { - if (i > 0 && i % cols == 0) { - evaluateForTensor(evaluator, arguments, tb); - } - String name = data.getNames(i % cols); - InputField field = fieldMap.get(name); - if (field != null) - { - Object rawValue = valuesList.get(i); - FieldValue inputFieldValue = field.prepare(rawValue); - arguments.put(field.getName(), inputFieldValue); - } - } - evaluateForTensor(evaluator, arguments, tb); - if (shapeList.size() == 1) - tBuilder.addShape(tBuilder.getValuesCount()); - else - { - int outCols = tBuilder.getValuesCount() / shapeList.get(0); - tBuilder.addShape(tBuilder.getValuesCount() / outCols).addShape(outCols); - } - DefaultData.Builder dataBuilder = DefaultData.newBuilder(); - dataBuilder.setTensor(tBuilder); - return dataBuilder.build(); - }else if (data.getDataOneofCase() == DataOneofCase.NDARRAY) { - ListValue list = data.getNdarray(); - int rows = list.getValuesCount(); - int cols = list.getValues(0).getListValue().getValuesCount(); - - ListValue.Builder rowsBuilder = ListValue.newBuilder(); - for (int i = 0; i < rows; ++i) { - - ListValue.Builder row = ListValue.newBuilder(); - ListBuilder lb = new ListBuilder(row); - for (int j = 0; j < cols; j++) { - String name = data.getNames(j % cols); - InputField field = fieldMap.get(name); - if (field != null) - { - Double rawValue = list.getValues(i).getListValue().getValues(j).getNumberValue(); - FieldValue inputFieldValue = field.prepare(rawValue); - arguments.put(field.getName(), inputFieldValue); - } - } - evaluateForTensor(evaluator, arguments, lb); - rowsBuilder.addValues(Value.newBuilder().setListValue(row.build())); - } - DefaultData.Builder dataBuilder = DefaultData.newBuilder(); - dataBuilder.setNdarray(rowsBuilder.build()); - return dataBuilder.build(); - } - else - throw new UnsupportedOperationException("Only Tensor or NDArray is supported"); - } - -} diff --git a/wrappers/s2i/java/wrapper/src/main/proto/.keep b/wrappers/s2i/java/wrapper/src/main/proto/.keep deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/wrappers/s2i/java/wrapper/src/main/resources/application.properties b/wrappers/s2i/java/wrapper/src/main/resources/application.properties deleted file mode 100644 index 650ed26ff9..0000000000 --- a/wrappers/s2i/java/wrapper/src/main/resources/application.properties +++ /dev/null @@ -1,8 +0,0 @@ -server.port = 9000 -grpc.port = 9001 - -seldon.api.model.enabled=true -seldon.api.route.enabled=false - -spring.jmx.enabled = false - diff --git a/wrappers/s2i/java/wrapper/src/test/java/io/seldon/wrapper/utils/TestPMMMLUtils.java b/wrappers/s2i/java/wrapper/src/test/java/io/seldon/wrapper/utils/TestPMMMLUtils.java deleted file mode 100644 index 9c46cafa1b..0000000000 --- a/wrappers/s2i/java/wrapper/src/test/java/io/seldon/wrapper/utils/TestPMMMLUtils.java +++ /dev/null @@ -1,79 +0,0 @@ -package io.seldon.wrapper.utils; - -import java.util.ArrayList; -import java.util.List; -import java.util.Random; - -import javax.xml.bind.JAXBException; - -import org.dmg.pmml.PMML; -import org.jpmml.model.PMMLUtil; -import org.junit.Test; -import org.xml.sax.SAXException; - -import com.google.protobuf.ListValue; -import com.google.protobuf.Value; - -import io.seldon.protos.PredictionProtos.DefaultData; -import io.seldon.protos.PredictionProtos.Tensor; - -public class TestPMMMLUtils { - - @Test - public void testTensorMnist() throws SAXException, JAXBException - { - PMML model = PMMLUtil.unmarshal(getClass().getClassLoader().getResourceAsStream( - "mnist.pmml")); - - Tensor.Builder tBuilder = Tensor.newBuilder(); - Random r = new Random(); - List names = new ArrayList<>(); - int numRows = 2; - for(int rows = 0;rows < numRows;rows++) - for(int i=0;i<784;i++) - { - tBuilder.addValues(r.nextInt() % 255); - names.add("_c"+i); - } - if (numRows == 1) - tBuilder.addShape(784); - else - tBuilder.addShape(numRows).addShape(784); - DefaultData.Builder dataBuilder = DefaultData.newBuilder(); - dataBuilder.setTensor(tBuilder).addAllNames(names); - - PMMLUtils util = new PMMLUtils(); - DefaultData resp = util.evaluate(model, dataBuilder.build()); - System.out.println(resp); - } - @Test - - public void testNDArrayMnist() throws SAXException, JAXBException - { - PMML model = PMMLUtil.unmarshal(getClass().getClassLoader().getResourceAsStream( - "mnist.pmml")); - - ListValue.Builder rowsBuilder = ListValue.newBuilder(); - Random r = new Random(); - List names = new ArrayList<>(); - int numRows = 2; - for(int rows = 0;rows < numRows;rows++) - { - ListValue.Builder row = ListValue.newBuilder(); - for(int i=0;i<784;i++) - { - - row.addValues(Value.newBuilder().setNumberValue(r.nextInt() % 255)); - names.add("_c"+i); - } - rowsBuilder.addValues(Value.newBuilder().setListValue(row.build())); - } - DefaultData.Builder dataBuilder = DefaultData.newBuilder(); - dataBuilder.setNdarray(rowsBuilder).addAllNames(names); - - PMMLUtils util = new PMMLUtils(); - DefaultData resp = util.evaluate(model, dataBuilder.build()); - System.out.println(resp); - } - -} diff --git a/wrappers/s2i/java/wrapper/src/test/resources/mnist.pmml b/wrappers/s2i/java/wrapper/src/test/resources/mnist.pmml deleted file mode 100644 index 352a2d287b..0000000000 --- a/wrappers/s2i/java/wrapper/src/test/resources/mnist.pmml +++ /dev/null @@ -1,8710 +0,0 @@ - - -
- - 2018-05-19T15:57:10Z -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 0 - 0 - - - 1 - 1 - - - 2 - 2 - - - 3 - 3 - - - 4 - 4 - - - 5 - 5 - - - 6 - 6 - - - 7 - 7 - - - 8 - 8 - - - 9 - 9 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
From d4a96aad9743a95e3b6be102a7da995dcb4d4f4f Mon Sep 17 00:00:00 2001 From: Clive Cox Date: Sat, 23 Jun 2018 15:41:35 +0100 Subject: [PATCH 14/18] Change latest version for java wrapper to 0.1.1 in docs --- docs/wrappers/java.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/wrappers/java.md b/docs/wrappers/java.md index 88953e0199..dc466096a7 100644 --- a/docs/wrappers/java.md +++ b/docs/wrappers/java.md @@ -38,7 +38,7 @@ Create a Spring Boot Maven project and include the dependency: io.seldon.wrapper seldon-core-wrapper - 0.1.0 + 0.1.1 ``` From d61291d511adb6a1d269bac9a3f72324ac026761 Mon Sep 17 00:00:00 2001 From: Clive Cox Date: Sun, 24 Jun 2018 12:01:40 +0100 Subject: [PATCH 15/18] remove PMML example --- examples/models/pyspark_pmml/.s2i/environment | 2 - examples/models/pyspark_pmml/contract.json | 22 -- examples/models/pyspark_pmml/mnist.ipynb | 282 ------------------ .../models/pyspark_pmml/mnist_deployment.json | 53 ---- examples/models/pyspark_pmml/pom.xml | 100 ------- .../java/io/seldon/example/mnist/App.java | 16 - .../example/mnist/model/PMMLModelHandler.java | 31 -- .../src/main/resources/application.properties | 6 - .../java/io/seldon/example/mnist/AppTest.java | 38 --- readme.md | 3 +- 10 files changed, 1 insertion(+), 552 deletions(-) delete mode 100644 examples/models/pyspark_pmml/.s2i/environment delete mode 100644 examples/models/pyspark_pmml/contract.json delete mode 100644 examples/models/pyspark_pmml/mnist.ipynb delete mode 100644 examples/models/pyspark_pmml/mnist_deployment.json delete mode 100644 examples/models/pyspark_pmml/pom.xml delete mode 100644 examples/models/pyspark_pmml/src/main/java/io/seldon/example/mnist/App.java delete mode 100644 examples/models/pyspark_pmml/src/main/java/io/seldon/example/mnist/model/PMMLModelHandler.java delete mode 100644 examples/models/pyspark_pmml/src/main/resources/application.properties delete mode 100644 examples/models/pyspark_pmml/src/test/java/io/seldon/example/mnist/AppTest.java diff --git a/examples/models/pyspark_pmml/.s2i/environment b/examples/models/pyspark_pmml/.s2i/environment deleted file mode 100644 index eb7b3132d1..0000000000 --- a/examples/models/pyspark_pmml/.s2i/environment +++ /dev/null @@ -1,2 +0,0 @@ -API_TYPE=REST -SERVICE_TYPE=MODEL diff --git a/examples/models/pyspark_pmml/contract.json b/examples/models/pyspark_pmml/contract.json deleted file mode 100644 index b45d85c296..0000000000 --- a/examples/models/pyspark_pmml/contract.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "features":[ - { - "name":"_c", - "dtype":"INT", - "ftype":"continuous", - "range":[0,255], - "repeat":784 - } - ], - "targets":[ - { - "name":"class", - "dtype":"FLOAT", - "ftype":"continuous", - "range":[0,1], - "repeat":10 - } - ] -} - - diff --git a/examples/models/pyspark_pmml/mnist.ipynb b/examples/models/pyspark_pmml/mnist.ipynb deleted file mode 100644 index 87c37b159b..0000000000 --- a/examples/models/pyspark_pmml/mnist.ipynb +++ /dev/null @@ -1,282 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# PySpark and PMML Example\n", - "In this example we train an MNIST model using pySpark, export the model to PMML and then wrap it using Seldon's S2I interface so we can run predictions against it using seldon-core.\n", - "\n", - "## Dependencies\n", - "\n", - "To run this notebook you will need to set up pySpark and JPMML's spark export.\n", - "\n", - " * [Install pySpark along with Spark](http://spark.apache.org/downloads.html)\n", - " * [Install JPMML Spark Package](https://github.com/jpmml/jpmml-sparkml-package)\n", - " \n", - " Following the above instruction you should add a set of environment variables of the form shown below to your shell:\n", - "\n", - "```\n", - "export SPARK_HOME=/spark-2.3.0-bin-hadoop2.7\n", - "export PATH=$SPARK_HOME/bin:$PATH\n", - "export PYSPARK_DRIVER_PYTHON=jupyter\n", - "export PYSPARK_DRIVER_PYTHON_OPTS='notebook'\n", - "export PYTHONPATH=/jpmml-sparkml-package/target/jpmml_sparkml-1.4rc0-py3.6.egg\n", - "```\n", - "\n", - "Then when you run pyspark from the folder of this notebook it should start Jupyter running with a Spark context and the JPMML libraries availble, e.g.\n", - "\n", - "```\n", - "pyspark --jars /jpmml-sparkml-package/target/jpmml-sparkml-package-1.4-SNAPSHOT.jar\n", - "```" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Train MNIST Model using pySpark" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from tensorflow.examples.tutorials.mnist import input_data\n", - "import numpy as np\n", - "\n", - "mnist = input_data.read_data_sets('data/MNIST_data', one_hot=False)\n", - "X = (mnist.train.images * 225).astype(int)\n", - "X_y = np.concatenate((X,np.expand_dims(mnist.train.labels,1)),axis=1)\n", - "np.savetxt(\"mnist_train.csv\", X_y, fmt='%i', delimiter=\",\")\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from pyspark.ml import Pipeline\n", - "from pyspark.ml.classification import LogisticRegression, LogisticRegressionModel\n", - "from pyspark.ml.feature import VectorAssembler\n", - "\n", - "df = sqlContext.read.csv(\"./mnist_train.csv\",inferSchema=True)\n", - "\n", - "df = df.withColumnRenamed(\"_c784\",\"label\")\n", - "\n", - "assembler = (VectorAssembler()\n", - " .setInputCols(df.columns[0:784])\n", - " .setOutputCol(\"features\"))\n", - "\n", - "lr = LogisticRegression(maxIter=10, regParam=0.01)\n", - "\n", - "pipeline = Pipeline(stages=[assembler, lr])\n", - "model = pipeline.fit(df)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from jpmml_sparkml import toPMMLBytes\n", - "\n", - "pmmlBytes = toPMMLBytes(sc, df, model)\n", - "f = open('model.pmml', 'wb')\n", - "f.write(pmmlBytes)\n", - "f.close()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "!mv model.pmml src/main/resources" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Build Image with S2I" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "!s2i build . seldonio/seldon-core-s2i-java-build pyspark-test:0.1 --runtime-image seldonio/seldon-core-s2i-java-runtime" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Test with Docker" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "!docker run --name \"pyspark_predictor\" -d --rm -p 5000:5000 pyspark-test:0.1" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "!cd ../../../wrappers/testing && make build_protos" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "!python ../../../wrappers/testing/tester.py contract.json 0.0.0.0 5000 -p -t" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "!docker rm pyspark_predictor --force" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Test in Minikube" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "!minikube start --memory 4096 --feature-gates=CustomResourceValidation=true --extra-config=apiserver.Authorization.Mode=RBAC" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "!kubectl create clusterrolebinding kube-system-cluster-admin --clusterrole=cluster-admin --serviceaccount=kube-system:default" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "!helm init" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "!helm install ../../../helm-charts/seldon-core-crd --name seldon-core-crd --set usage_metrics.enabled=true\n", - "!helm install ../../../helm-charts/seldon-core --name seldon-core" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "!eval $(minikube docker-env) && s2i build . seldonio/seldon-core-s2i-java-build pyspark-test:0.1 --runtime-image seldonio/seldon-core-s2i-java-runtime" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "!kubectl create -f mnist_deployment.json" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Wait until ready (replicas == replicasAvailable)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "!kubectl get seldondeployments seldon-deployment-example -o jsonpath='{.status}'" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "!python ../../../util/api_tester/api-tester.py contract.json \\\n", - " `minikube ip` `kubectl get svc -l app=seldon-apiserver-container-app -o jsonpath='{.items[0].spec.ports[0].nodePort}'` \\\n", - " --oauth-key oauth-key --oauth-secret oauth-secret -p" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "!minikube delete" - ] - } - ], - "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.6.4" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/examples/models/pyspark_pmml/mnist_deployment.json b/examples/models/pyspark_pmml/mnist_deployment.json deleted file mode 100644 index 79586c4482..0000000000 --- a/examples/models/pyspark_pmml/mnist_deployment.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "apiVersion": "machinelearning.seldon.io/v1alpha1", - "kind": "SeldonDeployment", - "metadata": { - "labels": { - "app": "seldon" - }, - "name": "seldon-deployment-example" - }, - "spec": { - "annotations": { - "project_name": "Pyspark PMML Example", - "deployment_version": "0.1" - }, - "name": "h2o-deployment", - "oauth_key": "oauth-key", - "oauth_secret": "oauth-secret", - "predictors": [ - { - "componentSpec": { - "spec": { - "containers": [ - { - "image": "pyspark-test:0.1", - "imagePullPolicy": "IfNotPresent", - "name": "mnist-classifier", - "resources": { - "requests": { - "memory": "1Mi" - } - } - } - ], - "terminationGracePeriodSeconds": 20 - } - }, - "graph": { - "children": [], - "name": "mnist-classifier", - "endpoint": { - "type" : "REST" - }, - "type": "MODEL" - }, - "name": "pmml-predictor", - "replicas": 1, - "annotations": { - "predictor_version" : "0.1" - } - } - ] - } -} diff --git a/examples/models/pyspark_pmml/pom.xml b/examples/models/pyspark_pmml/pom.xml deleted file mode 100644 index 972a4b7a67..0000000000 --- a/examples/models/pyspark_pmml/pom.xml +++ /dev/null @@ -1,100 +0,0 @@ - - 4.0.0 - - io.seldon.example.pmml.mnist - pyspark-pmml-evaluator - 0.0.1-SNAPSHOT - jar - - pyspark-pmml-evaluator - http://maven.apache.org - - - - org.springframework.boot - spring-boot-starter-parent - 1.5.1.RELEASE - - - - 1.8 - UTF-8 - UTF-8 - 1.0.0 - 1.0.0-rc.1 - io.seldon.example.mnist.App - - - - ${project.artifactId}-${project.version} - - - kr.motd.maven - os-maven-plugin - 1.4.1.Final - - - - - maven-compiler-plugin - 3.5.1 - - 1.8 - 1.8 - UTF-8 - - - - org.springframework.boot - spring-boot-maven-plugin - - - - - - - - - org.springframework.boot - spring-boot-starter-test - test - - - log4j - log4j - 1.2.17 - - - org.springframework.boot - spring-boot-starter-web - - - - org.springframework.boot - spring-boot-starter-actuator - - - io.seldon.wrapper - seldon-core-wrapper - 0.1.1 - - - org.jpmml - pmml-evaluator - 1.4.1 - - - org.jpmml - pmml-evaluator-extension - 1.4.1 - - - junit - junit - 3.8.1 - test - - - diff --git a/examples/models/pyspark_pmml/src/main/java/io/seldon/example/mnist/App.java b/examples/models/pyspark_pmml/src/main/java/io/seldon/example/mnist/App.java deleted file mode 100644 index 81a75868e0..0000000000 --- a/examples/models/pyspark_pmml/src/main/java/io/seldon/example/mnist/App.java +++ /dev/null @@ -1,16 +0,0 @@ -package io.seldon.example.mnist; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.context.annotation.Import; -import org.springframework.scheduling.annotation.EnableAsync; - -@EnableAsync -@SpringBootApplication(scanBasePackages = {"io.seldon.wrapper","io.seldon.example.mnist"}) -@Import({ io.seldon.wrapper.config.AppConfig.class }) -public class App { - public static void main(String[] args) throws Exception { - SpringApplication.run(App.class, args); - } -} - diff --git a/examples/models/pyspark_pmml/src/main/java/io/seldon/example/mnist/model/PMMLModelHandler.java b/examples/models/pyspark_pmml/src/main/java/io/seldon/example/mnist/model/PMMLModelHandler.java deleted file mode 100644 index 89492b4c6c..0000000000 --- a/examples/models/pyspark_pmml/src/main/java/io/seldon/example/mnist/model/PMMLModelHandler.java +++ /dev/null @@ -1,31 +0,0 @@ -package io.seldon.example.mnist.model; - -import javax.xml.bind.JAXBException; - -import org.dmg.pmml.PMML; -import org.jpmml.model.PMMLUtil; -import org.springframework.stereotype.Component; -import org.xml.sax.SAXException; - -import io.seldon.protos.PredictionProtos.DefaultData; -import io.seldon.protos.PredictionProtos.SeldonMessage; -import io.seldon.wrapper.api.SeldonPredictionService; -import io.seldon.wrapper.utils.PMMLUtils; - -@Component -public class PMMLModelHandler implements SeldonPredictionService { - - private final PMML model; - - public PMMLModelHandler() throws SAXException, JAXBException { - model = PMMLUtil.unmarshal(getClass().getClassLoader().getResourceAsStream( - "model.pmml")); - } - - @Override - public SeldonMessage predict(SeldonMessage payload) { - PMMLUtils pmmlUtils = new PMMLUtils(); - DefaultData res = pmmlUtils.evaluate(model, payload.getData()); - return SeldonMessage.newBuilder().setData(res).build(); - } -} diff --git a/examples/models/pyspark_pmml/src/main/resources/application.properties b/examples/models/pyspark_pmml/src/main/resources/application.properties deleted file mode 100644 index b142a92a5c..0000000000 --- a/examples/models/pyspark_pmml/src/main/resources/application.properties +++ /dev/null @@ -1,6 +0,0 @@ -grpc.port=5001 - -seldon.api.model.enabled=true -seldon.api.route.enabled=false - -spring.jmx.enabled = false diff --git a/examples/models/pyspark_pmml/src/test/java/io/seldon/example/mnist/AppTest.java b/examples/models/pyspark_pmml/src/test/java/io/seldon/example/mnist/AppTest.java deleted file mode 100644 index 19e0da2e8b..0000000000 --- a/examples/models/pyspark_pmml/src/test/java/io/seldon/example/mnist/AppTest.java +++ /dev/null @@ -1,38 +0,0 @@ -package io.seldon.example.mnist; - -import junit.framework.Test; -import junit.framework.TestCase; -import junit.framework.TestSuite; - -/** - * Unit test for simple App. - */ -public class AppTest - extends TestCase -{ - /** - * Create the test case - * - * @param testName name of the test case - */ - public AppTest( String testName ) - { - super( testName ); - } - - /** - * @return the suite of tests being tested - */ - public static Test suite() - { - return new TestSuite( AppTest.class ); - } - - /** - * Rigourous Test :-) - */ - public void testApp() - { - assertTrue( true ); - } -} diff --git a/readme.md b/readme.md index 197aa5c6e1..2f8e89fbc4 100644 --- a/readme.md +++ b/readme.md @@ -74,8 +74,7 @@ Seldon-core allows various types of components to be built and plugged into the * [R Iris Classifier](./examples/models/r_iris/r_iris.ipynb) * Java * [H2O Classifier](./examples/models/h2o_mojo/h2o_model.ipynb) - * PMML - * [pySpark MNIST Classifier](./examples/models/pyspark_pmml/mnist.ipynb) + * **routers** * [Epsilon-greedy multi-armed bandits for real time optimization of models](https://github.com/SeldonIO/seldon-core/blob/master/notebooks/epsilon_greedy_gcp.ipynb) * **transformers** From 842cb8e010dfcd42903c7fd19681f6c36459bd65 Mon Sep 17 00:00:00 2001 From: Clive Cox Date: Sun, 24 Jun 2018 17:02:05 +0100 Subject: [PATCH 16/18] update java wrapper version in tests --- wrappers/s2i/java/test/model-template-app/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wrappers/s2i/java/test/model-template-app/pom.xml b/wrappers/s2i/java/test/model-template-app/pom.xml index 67f10c62b7..d870d2da2e 100644 --- a/wrappers/s2i/java/test/model-template-app/pom.xml +++ b/wrappers/s2i/java/test/model-template-app/pom.xml @@ -73,7 +73,7 @@ io.seldon.wrapper seldon-core-wrapper - 0.1.0 + 0.1.2
From 170c1d07257f30e2385c6c5c1e34cc76e14ee75b Mon Sep 17 00:00:00 2001 From: Clive Cox Date: Mon, 25 Jun 2018 09:07:56 +0100 Subject: [PATCH 17/18] add PMML example link --- readme.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/readme.md b/readme.md index 2f8e89fbc4..f78818afad 100644 --- a/readme.md +++ b/readme.md @@ -74,6 +74,8 @@ Seldon-core allows various types of components to be built and plugged into the * [R Iris Classifier](./examples/models/r_iris/r_iris.ipynb) * Java * [H2O Classifier](./examples/models/h2o_mojo/h2o_model.ipynb) + * PMML + * [PySpark MNIST Classifier](https://github.com/SeldonIO/JPMML-utils/blob/master/examples/pyspark_pmml/mnist.ipynb) * **routers** * [Epsilon-greedy multi-armed bandits for real time optimization of models](https://github.com/SeldonIO/seldon-core/blob/master/notebooks/epsilon_greedy_gcp.ipynb) From 957fcdace16389e2dc88037f0d3859597c730162 Mon Sep 17 00:00:00 2001 From: Gurminder Sunner Date: Wed, 27 Jun 2018 13:35:12 +0100 Subject: [PATCH 18/18] release 0.1.8 --- api-frontend/pom.xml | 2 +- cluster-manager/pom.xml | 2 +- engine/pom.xml | 2 +- helm-charts/seldon-core-crd/Chart.yaml | 2 +- helm-charts/seldon-core/Chart.yaml | 2 +- helm-charts/seldon-core/values.yaml | 6 +++--- seldon-core/seldon-core/prototypes/core.jsonnet | 6 +++--- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/api-frontend/pom.xml b/api-frontend/pom.xml index 8da42d37ff..cb656ad0b7 100644 --- a/api-frontend/pom.xml +++ b/api-frontend/pom.xml @@ -10,7 +10,7 @@ io.seldon.apife seldon-apife - 0.1.8-SNAPSHOT + 0.1.8 jar api-frontend diff --git a/cluster-manager/pom.xml b/cluster-manager/pom.xml index 03d2466e37..57d5f66533 100644 --- a/cluster-manager/pom.xml +++ b/cluster-manager/pom.xml @@ -4,7 +4,7 @@ io.seldon.clustermanager seldon-cluster-manager jar - 0.1.8-SNAPSHOT + 0.1.8 seldon-cluster-manager http://maven.apache.org diff --git a/engine/pom.xml b/engine/pom.xml index d7482f839b..e47dbff84c 100644 --- a/engine/pom.xml +++ b/engine/pom.xml @@ -10,7 +10,7 @@ io.seldon.engine seldon-engine - 0.1.8-SNAPSHOT + 0.1.8 jar engine diff --git a/helm-charts/seldon-core-crd/Chart.yaml b/helm-charts/seldon-core-crd/Chart.yaml index 0874dff1a7..e93dfaa0f3 100644 --- a/helm-charts/seldon-core-crd/Chart.yaml +++ b/helm-charts/seldon-core-crd/Chart.yaml @@ -6,4 +6,4 @@ keywords: name: seldon-core-crd sources: - https://github.com/SeldonIO/seldon-core -version: 0.1.8-SNAPSHOT +version: 0.1.8 diff --git a/helm-charts/seldon-core/Chart.yaml b/helm-charts/seldon-core/Chart.yaml index 22b5a32e85..cd29941ef1 100644 --- a/helm-charts/seldon-core/Chart.yaml +++ b/helm-charts/seldon-core/Chart.yaml @@ -6,4 +6,4 @@ keywords: name: seldon-core sources: - https://github.com/SeldonIO/seldon-core -version: 0.1.8-SNAPSHOT +version: 0.1.8 diff --git a/helm-charts/seldon-core/values.yaml b/helm-charts/seldon-core/values.yaml index 90dadeebfe..ccd9fc8696 100644 --- a/helm-charts/seldon-core/values.yaml +++ b/helm-charts/seldon-core/values.yaml @@ -2,17 +2,17 @@ apife: enabled: true image: pull_policy: IfNotPresent - tag: 0.1.8-SNAPSHOT + tag: 0.1.8 apife_service_type: NodePort cluster_manager: image: pull_policy: IfNotPresent - tag: 0.1.8-SNAPSHOT + tag: 0.1.8 java_opts: '' spring_opts: '' engine: image: - tag: 0.1.8-SNAPSHOT + tag: 0.1.8 rbac: enabled: true redis: diff --git a/seldon-core/seldon-core/prototypes/core.jsonnet b/seldon-core/seldon-core/prototypes/core.jsonnet index fd25b5853e..8e826869eb 100644 --- a/seldon-core/seldon-core/prototypes/core.jsonnet +++ b/seldon-core/seldon-core/prototypes/core.jsonnet @@ -6,12 +6,12 @@ // @optionalParam namespace string default Namespace // @optionalParam withRbac string false Whether to include RBAC setup // @optionalParam withApife string true Whether to include builtin API Oauth fornt end server for ingress -// @optionalParam apifeImage string seldonio/apife:0.1.6 Default image for API Front End +// @optionalParam apifeImage string seldonio/apife:0.1.8 Default image for API Front End // @optionalParam apifeServiceType string NodePort API Front End Service Type -// @optionalParam operatorImage string seldonio/cluster-manager:0.1.6 Seldon cluster manager image version +// @optionalParam operatorImage string seldonio/cluster-manager:0.1.8 Seldon cluster manager image version // @optionalParam operatorSpringOpts string null cluster manager spring opts // @optionalParam operatorJavaOpts string null cluster manager java opts -// @optionalParam engineImage string seldonio/engine:0.1.6 Seldon engine image version +// @optionalParam engineImage string seldonio/engine:0.1.8 Seldon engine image version // TODO(https://github.com/ksonnet/ksonnet/issues/222): We have to add namespace as an explicit parameter // because ksonnet doesn't support inheriting it from the environment yet.