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
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, e.g.

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

$ zip -r model.zip export/estimator
Expand Down
105 changes: 105 additions & 0 deletions examples/models/tf.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# 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 get_all_file_paths(directory):
file_paths = []
for root, directories, files in os.walk(directory):
for filename in files:
filepath = os.path.join(root, filename)
file_paths.append(filepath)

return file_paths


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()
images, labels = dataset_it.get_next()
return {"inputs": images}, labels


def json_serving_input_fn():
inputs = tf.placeholder(shape=[4], dtype=tf.float64)
features = {"inputs": 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["inputs"]
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)

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

# clean up
shutil.rmtree(EXPORT_DIR)
21 changes: 21 additions & 0 deletions pkg/workloads/tf_api/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,20 @@ def predict(deployment_name, api_name):

return jsonify(response)

def valid_model_dir(model_dir):
"""
validates that model_dir has the expected directory tree, e.g:

{TF serving version number}/
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")


def start(args):
ctx = Context(s3_path=args.context, cache_dir=args.cache_dir, workload_id=args.workload_id)
Expand Down Expand Up @@ -431,6 +445,13 @@ def start(args):
if not os.path.isdir(args.model_dir):
ctx.storage.download_and_unzip_external(api["model"], args.model_dir)


try:
valid_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