Skip to content

Iris Tensorflow example #208

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 17 commits into from
Jul 5, 2019
Merged
2 changes: 1 addition & 1 deletion docs/apis/packaging-models.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
Zip the exported estimator output in your checkpoint directory:

```text
$ ls export/estimator
$ ls export/estimator/1560263597/
saved_model.pb variables/

$ zip -r model.zip export/estimator
Expand Down
94 changes: 94 additions & 0 deletions examples/iris/tensorflow/model.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# sources copied/modified from https://github.com/tensorflow/models/blob/master/samples/core/get_started/

import tensorflow as tf
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
import shutil
import os

EXPORT_DIR = "iris_tf_export"


def input_fn(features, labels, batch_size, mode):
"""An input function for training"""
dataset = tf.data.Dataset.from_tensor_slices((features, labels))
if mode == tf.estimator.ModeKeys.TRAIN:
dataset = dataset.shuffle(1000).repeat()
dataset = dataset.batch(batch_size)
dataset_it = dataset.make_one_shot_iterator()
irises, labels = dataset_it.get_next()
return {"irises": irises}, labels


def json_serving_input_fn():
inputs = tf.placeholder(shape=[4], dtype=tf.float64)
features = {"irises": tf.expand_dims(inputs, 0)}
return tf.estimator.export.ServingInputReceiver(features=features, receiver_tensors=inputs)


def my_model(features, labels, mode, params):
"""DNN with three hidden layers and learning_rate=0.1."""
net = features["irises"]
for units in params["hidden_units"]:
net = tf.layers.dense(net, units=units, activation=tf.nn.relu)

logits = tf.layers.dense(net, params["n_classes"], activation=None)

predicted_classes = tf.argmax(logits, 1)
if mode == tf.estimator.ModeKeys.PREDICT:
predictions = {
"class_ids": predicted_classes[:, tf.newaxis],
"probabilities": tf.nn.softmax(logits),
"logits": logits,
}
return tf.estimator.EstimatorSpec(
mode=mode,
predictions=predictions,
export_outputs={
"predict": tf.estimator.export.PredictOutput(
{
"class_ids": predicted_classes[:, tf.newaxis],
"probabilities": tf.nn.softmax(logits),
}
)
},
)

loss = tf.losses.sparse_softmax_cross_entropy(labels=labels, logits=logits)

accuracy = tf.metrics.accuracy(labels=labels, predictions=predicted_classes, name="acc_op")
metrics = {"accuracy": accuracy}
tf.summary.scalar("accuracy", accuracy[1])

if mode == tf.estimator.ModeKeys.EVAL:
return tf.estimator.EstimatorSpec(mode, loss=loss, eval_metric_ops=metrics)

optimizer = tf.train.AdagradOptimizer(learning_rate=0.1)
train_op = optimizer.minimize(loss, global_step=tf.train.get_global_step())
return tf.estimator.EstimatorSpec(mode, loss=loss, train_op=train_op)


iris = load_iris()
X, y = iris.data, iris.target
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.8, random_state=42)

classifier = tf.estimator.Estimator(
model_fn=my_model, model_dir=EXPORT_DIR, params={"hidden_units": [10, 10], "n_classes": 3}
)


train_input_fn = lambda: input_fn(X_train, y_train, 100, tf.estimator.ModeKeys.TRAIN)
eval_input_fn = lambda: input_fn(X_test, y_test, 100, tf.estimator.ModeKeys.EVAL)
serving_input_fn = lambda: json_serving_input_fn()
exporter = tf.estimator.FinalExporter("estimator", serving_input_fn, as_text=False)
train_spec = tf.estimator.TrainSpec(train_input_fn, max_steps=1000)
eval_spec = tf.estimator.EvalSpec(eval_input_fn, exporters=[exporter], name="estimator-eval")

tf.estimator.train_and_evaluate(classifier, train_spec, eval_spec)

# zip the estimator export dir (the exported path looks like iris_tf_export/export/estimator/1562353043/)
estimator_dir = EXPORT_DIR + "/export/estimator"
shutil.make_archive("tensorflow", "zip", os.path.join(estimator_dir))

# clean up
shutil.rmtree(EXPORT_DIR)
2 changes: 2 additions & 0 deletions examples/iris/tensorflow/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
tensorflow
sklearn
33 changes: 31 additions & 2 deletions pkg/workloads/tf_api/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,30 @@ def predict(deployment_name, api_name):
return jsonify(response)


def validate_model_dir(model_dir):
"""
validates that model_dir has the expected directory tree.

For example (your TF serving version number may be different):

1562353043/
saved_model.pb
variables/
variables.data-00000-of-00001
variables.index
"""
version = os.listdir(model_dir)[0]
if not version.isdigit():
raise UserException(
"No versions of servable default found under base path in model_dir. See docs.cortex.dev for how to properly package your TensorFlow model"
)

if "saved_model.pb" not in os.listdir(os.path.join(model_dir, version)):
raise UserException(
'Expected packaged model to have a "saved_model.pb" file. See docs.cortex.dev for how to properly package your TensorFlow model'
)


def start(args):
ctx = Context(s3_path=args.context, cache_dir=args.cache_dir, workload_id=args.workload_id)

Expand All @@ -406,8 +430,7 @@ def start(args):
package.install_packages(ctx.python_packages, ctx.storage)
if not os.path.isdir(args.model_dir):
ctx.storage.download_and_unzip_external(api["model"], args.model_dir)

if util.is_resource_ref(api["model"]):
else:
package.install_packages(ctx.python_packages, ctx.storage)
model_name = util.get_resource_ref(api["model"])
model = ctx.models[model_name]
Expand Down Expand Up @@ -446,6 +469,12 @@ def start(args):
model["input"]["target_vocab"], None, False
)

try:
validate_model_dir(args.model_dir)
except Exception as e:
logger.exception(e)
sys.exit(1)

channel = grpc.insecure_channel("localhost:" + str(args.tf_serve_port))
local_cache["stub"] = prediction_service_pb2_grpc.PredictionServiceStub(channel)

Expand Down