Unverified Commit 72f5834c authored by Asim Shankar's avatar Asim Shankar Committed by GitHub
Browse files

Merge pull request #3024 from asimshankar/mnist

[mnist]: Updates
parents 0f5803bd 4d79fee3
...@@ -21,12 +21,19 @@ python mnist.py ...@@ -21,12 +21,19 @@ python mnist.py
The model will begin training and will automatically evaluate itself on the The model will begin training and will automatically evaluate itself on the
validation data. validation data.
Illustrative unit tests and benchmarks can be run with:
```
python mnist_test.py
python mnist_test.py --benchmarks=.
```
## Exporting the model ## Exporting the model
You can export the model into Tensorflow [SavedModel](https://www.tensorflow.org/programmers_guide/saved_model) format by using the argument `--export_dir`: You can export the model into Tensorflow [SavedModel](https://www.tensorflow.org/programmers_guide/saved_model) format by using the argument `--export_dir`:
``` ```
python mnist.py --export_dir /tmp/mnist_saved_model python mnist.py --export_dir /tmp/mnist_saved_model
``` ```
The SavedModel will be saved in a timestamped directory under `/tmp/mnist_saved_model/` (e.g. `/tmp/mnist_saved_model/1513630966/`). The SavedModel will be saved in a timestamped directory under `/tmp/mnist_saved_model/` (e.g. `/tmp/mnist_saved_model/1513630966/`).
...@@ -35,7 +42,7 @@ The SavedModel will be saved in a timestamped directory under `/tmp/mnist_saved_ ...@@ -35,7 +42,7 @@ The SavedModel will be saved in a timestamped directory under `/tmp/mnist_saved_
Use [`saved_model_cli`](https://www.tensorflow.org/programmers_guide/saved_model#cli_to_inspect_and_execute_savedmodel) to inspect and execute the SavedModel. Use [`saved_model_cli`](https://www.tensorflow.org/programmers_guide/saved_model#cli_to_inspect_and_execute_savedmodel) to inspect and execute the SavedModel.
``` ```
saved_model_cli run --dir /tmp/mnist_saved_model/TIMESTAMP --tag_set serve --signature_def classify --inputs image_raw=examples.npy saved_model_cli run --dir /tmp/mnist_saved_model/TIMESTAMP --tag_set serve --signature_def classify --inputs image=examples.npy
``` ```
`examples.npy` contains the data from `example5.png` and `example3.png` in a numpy array, in that order. The array values are normalized to values between 0 and 1. `examples.npy` contains the data from `example5.png` and `example3.png` in a numpy array, in that order. The array values are normalized to values between 0 and 1.
......
...@@ -63,6 +63,7 @@ parser.add_argument( ...@@ -63,6 +63,7 @@ parser.add_argument(
type=str, type=str,
help='The directory where the exported SavedModel will be stored.') help='The directory where the exported SavedModel will be stored.')
def train_dataset(data_dir): def train_dataset(data_dir):
"""Returns a tf.data.Dataset yielding (image, label) pairs for training.""" """Returns a tf.data.Dataset yielding (image, label) pairs for training."""
data = input_data.read_data_sets(data_dir, one_hot=True).train data = input_data.read_data_sets(data_dir, one_hot=True).train
...@@ -75,141 +76,115 @@ def eval_dataset(data_dir): ...@@ -75,141 +76,115 @@ def eval_dataset(data_dir):
return tf.data.Dataset.from_tensors((data.images, data.labels)) return tf.data.Dataset.from_tensors((data.images, data.labels))
def mnist_model(inputs, mode, data_format): class Model(object):
"""Takes the MNIST inputs and mode and outputs a tensor of logits.""" """Class that defines a graph to recognize digits in the MNIST dataset."""
# Input Layer
# Reshape X to 4-D tensor: [batch_size, width, height, channels] def __init__(self, data_format):
# MNIST images are 28x28 pixels, and have one color channel """Creates a model for classifying a hand-written digit.
inputs = tf.reshape(inputs, [-1, 28, 28, 1])
Args:
if data_format is None: data_format: Either 'channels_first' or 'channels_last'.
# When running on GPU, transpose the data from channels_last (NHWC) to 'channels_first' is typically faster on GPUs while 'channels_last' is
# channels_first (NCHW) to improve performance. typically faster on CPUs. See
# See https://www.tensorflow.org/performance/performance_guide#data_formats https://www.tensorflow.org/performance/performance_guide#data_formats
data_format = ('channels_first' """
if tf.test.is_built_with_cuda() else 'channels_last') if data_format == 'channels_first':
self._input_shape = [-1, 1, 28, 28]
if data_format == 'channels_first': else:
inputs = tf.transpose(inputs, [0, 3, 1, 2]) assert data_format == 'channels_last'
self._input_shape = [-1, 28, 28, 1]
# Convolutional Layer #1
# Computes 32 features using a 5x5 filter with ReLU activation. self.conv1 = tf.layers.Conv2D(
# Padding is added to preserve width and height. 32, 5, padding='same', data_format=data_format, activation=tf.nn.relu)
# Input Tensor Shape: [batch_size, 28, 28, 1] self.conv2 = tf.layers.Conv2D(
# Output Tensor Shape: [batch_size, 28, 28, 32] 64, 5, padding='same', data_format=data_format, activation=tf.nn.relu)
conv1 = tf.layers.conv2d( self.fc1 = tf.layers.Dense(1024, activation=tf.nn.relu)
inputs=inputs, self.fc2 = tf.layers.Dense(10)
filters=32, self.dropout = tf.layers.Dropout(0.4)
kernel_size=[5, 5], self.max_pool2d = tf.layers.MaxPooling2D(
padding='same', (2, 2), (2, 2), padding='same', data_format=data_format)
activation=tf.nn.relu,
data_format=data_format) def __call__(self, inputs, training):
"""Add operations to classify a batch of input images.
# Pooling Layer #1
# First max pooling layer with a 2x2 filter and stride of 2 Args:
# Input Tensor Shape: [batch_size, 28, 28, 32] inputs: A Tensor representing a batch of input images.
# Output Tensor Shape: [batch_size, 14, 14, 32] training: A boolean. Set to True to add operations required only when
pool1 = tf.layers.max_pooling2d( training the classifier.
inputs=conv1, pool_size=[2, 2], strides=2, data_format=data_format)
Returns:
# Convolutional Layer #2 A logits Tensor with shape [<batch_size>, 10].
# Computes 64 features using a 5x5 filter. """
# Padding is added to preserve width and height. y = tf.reshape(inputs, self._input_shape)
# Input Tensor Shape: [batch_size, 14, 14, 32] y = self.conv1(y)
# Output Tensor Shape: [batch_size, 14, 14, 64] y = self.max_pool2d(y)
conv2 = tf.layers.conv2d( y = self.conv2(y)
inputs=pool1, y = self.max_pool2d(y)
filters=64, y = tf.layers.flatten(y)
kernel_size=[5, 5], y = self.fc1(y)
padding='same', y = self.dropout(y, training=training)
activation=tf.nn.relu, return self.fc2(y)
data_format=data_format)
# Pooling Layer #2 def model_fn(features, labels, mode, params):
# Second max pooling layer with a 2x2 filter and stride of 2 """The model_fn argument for creating an Estimator."""
# Input Tensor Shape: [batch_size, 14, 14, 64] model = Model(params['data_format'])
# Output Tensor Shape: [batch_size, 7, 7, 64] image = features
pool2 = tf.layers.max_pooling2d( if isinstance(image, dict):
inputs=conv2, pool_size=[2, 2], strides=2, data_format=data_format) image = features['image']
# Flatten tensor into a batch of vectors
# Input Tensor Shape: [batch_size, 7, 7, 64]
# Output Tensor Shape: [batch_size, 7 * 7 * 64]
pool2_flat = tf.reshape(pool2, [-1, 7 * 7 * 64])
# Dense Layer
# Densely connected layer with 1024 neurons
# Input Tensor Shape: [batch_size, 7 * 7 * 64]
# Output Tensor Shape: [batch_size, 1024]
dense = tf.layers.dense(inputs=pool2_flat, units=1024, activation=tf.nn.relu)
# Add dropout operation; 0.6 probability that element will be kept
dropout = tf.layers.dropout(
inputs=dense, rate=0.4, training=(mode == tf.estimator.ModeKeys.TRAIN))
# Logits layer
# Input Tensor Shape: [batch_size, 1024]
# Output Tensor Shape: [batch_size, 10]
logits = tf.layers.dense(inputs=dropout, units=10)
return logits
def mnist_model_fn(features, labels, mode, params):
"""Model function for MNIST."""
if mode == tf.estimator.ModeKeys.PREDICT and isinstance(features,dict):
features = features['image_raw']
logits = mnist_model(features, mode, params['data_format'])
predictions = {
'classes': tf.argmax(input=logits, axis=1),
'probabilities': tf.nn.softmax(logits, name='softmax_tensor')
}
if mode == tf.estimator.ModeKeys.PREDICT: if mode == tf.estimator.ModeKeys.PREDICT:
export_outputs={'classify': tf.estimator.export.PredictOutput(predictions)} logits = model(image, training=False)
return tf.estimator.EstimatorSpec(mode=mode, predictions=predictions, predictions = {
export_outputs=export_outputs) 'classes': tf.argmax(logits, axis=1),
'probabilities': tf.nn.softmax(logits),
loss = tf.losses.softmax_cross_entropy(onehot_labels=labels, logits=logits) }
return tf.estimator.EstimatorSpec(
# Configure the training op mode=tf.estimator.ModeKeys.PREDICT,
predictions=predictions,
export_outputs={
'classify': tf.estimator.export.PredictOutput(predictions)
})
if mode == tf.estimator.ModeKeys.TRAIN: if mode == tf.estimator.ModeKeys.TRAIN:
optimizer = tf.train.AdamOptimizer(learning_rate=1e-4) optimizer = tf.train.AdamOptimizer(learning_rate=1e-4)
train_op = optimizer.minimize(loss, tf.train.get_or_create_global_step()) logits = model(image, training=True)
else: loss = tf.losses.softmax_cross_entropy(onehot_labels=labels, logits=logits)
train_op = None accuracy = tf.metrics.accuracy(
labels=tf.argmax(labels, axis=1), predictions=tf.argmax(logits, axis=1))
accuracy = tf.metrics.accuracy( # Name the accuracy tensor 'train_accuracy' to demonstrate the
tf.argmax(labels, axis=1), predictions['classes']) # LoggingTensorHook.
metrics = {'accuracy': accuracy} tf.identity(accuracy[1], name='train_accuracy')
tf.summary.scalar('train_accuracy', accuracy[1])
# Create a tensor named train_accuracy for logging purposes return tf.estimator.EstimatorSpec(
tf.identity(accuracy[1], name='train_accuracy') mode=tf.estimator.ModeKeys.TRAIN,
tf.summary.scalar('train_accuracy', accuracy[1]) loss=loss,
train_op=optimizer.minimize(loss, tf.train.get_or_create_global_step()))
return tf.estimator.EstimatorSpec( if mode == tf.estimator.ModeKeys.EVAL:
mode=mode, logits = model(image, training=False)
predictions=predictions, loss = tf.losses.softmax_cross_entropy(onehot_labels=labels, logits=logits)
loss=loss, return tf.estimator.EstimatorSpec(
train_op=train_op, mode=tf.estimator.ModeKeys.EVAL,
eval_metric_ops=metrics) loss=loss,
eval_metric_ops={
'accuracy':
tf.metrics.accuracy(
labels=tf.argmax(labels, axis=1),
predictions=tf.argmax(logits, axis=1)),
})
def main(unused_argv): def main(unused_argv):
# Create the Estimator data_format = FLAGS.data_format
if data_format is None:
data_format = ('channels_first'
if tf.test.is_built_with_cuda() else 'channels_last')
mnist_classifier = tf.estimator.Estimator( mnist_classifier = tf.estimator.Estimator(
model_fn=mnist_model_fn, model_fn=model_fn,
model_dir=FLAGS.model_dir, model_dir=FLAGS.model_dir,
params={ params={
'data_format': FLAGS.data_format 'data_format': data_format
}) })
# Set up training hook that logs the training accuracy every 100 steps.
tensors_to_log = {'train_accuracy': 'train_accuracy'}
logging_hook = tf.train.LoggingTensorHook(
tensors=tensors_to_log, every_n_iter=100)
# Train the model # Train the model
def train_input_fn(): def train_input_fn():
# When choosing shuffle buffer sizes, larger sizes result in better # When choosing shuffle buffer sizes, larger sizes result in better
...@@ -221,6 +196,10 @@ def main(unused_argv): ...@@ -221,6 +196,10 @@ def main(unused_argv):
(images, labels) = dataset.make_one_shot_iterator().get_next() (images, labels) = dataset.make_one_shot_iterator().get_next()
return (images, labels) return (images, labels)
# Set up training hook that logs the training accuracy every 100 steps.
tensors_to_log = {'train_accuracy': 'train_accuracy'}
logging_hook = tf.train.LoggingTensorHook(
tensors=tensors_to_log, every_n_iter=100)
mnist_classifier.train(input_fn=train_input_fn, hooks=[logging_hook]) mnist_classifier.train(input_fn=train_input_fn, hooks=[logging_hook])
# Evaluate the model and print results # Evaluate the model and print results
...@@ -233,10 +212,11 @@ def main(unused_argv): ...@@ -233,10 +212,11 @@ def main(unused_argv):
# Export the model # Export the model
if FLAGS.export_dir is not None: if FLAGS.export_dir is not None:
image = tf.placeholder(tf.float32,[None, 28, 28]) image = tf.placeholder(tf.float32, [None, 28, 28])
serving_input_fn = tf.estimator.export.build_raw_serving_input_receiver_fn( input_fn = tf.estimator.export.build_raw_serving_input_receiver_fn({
{"image_raw":image}) 'image': image,
mnist_classifier.export_savedmodel(FLAGS.export_dir, serving_input_fn) })
mnist_classifier.export_savedmodel(FLAGS.export_dir, input_fn)
if __name__ == '__main__': if __name__ == '__main__':
......
...@@ -18,30 +18,63 @@ from __future__ import division ...@@ -18,30 +18,63 @@ from __future__ import division
from __future__ import print_function from __future__ import print_function
import tensorflow as tf import tensorflow as tf
import time
import mnist import mnist
tf.logging.set_verbosity(tf.logging.ERROR) BATCH_SIZE = 100
class BaseTest(tf.test.TestCase): def dummy_input_fn():
image = tf.random_uniform([BATCH_SIZE, 784])
labels = tf.random_uniform([BATCH_SIZE], maxval=9, dtype=tf.int32)
return image, tf.one_hot(labels, 10)
def input_fn(self):
features = tf.random_uniform([55000, 784]) def make_estimator():
labels = tf.random_uniform([55000], maxval=9, dtype=tf.int32) data_format = 'channels_last'
return features, tf.one_hot(labels, 10) if tf.test.is_built_with_cuda():
data_format = 'channels_first'
return tf.estimator.Estimator(
model_fn=mnist.model_fn, params={
'data_format': data_format
})
class Tests(tf.test.TestCase):
def test_mnist(self):
classifier = make_estimator()
classifier.train(input_fn=dummy_input_fn, steps=2)
eval_results = classifier.evaluate(input_fn=dummy_input_fn, steps=1)
loss = eval_results['loss']
global_step = eval_results['global_step']
accuracy = eval_results['accuracy']
self.assertEqual(loss.shape, ())
self.assertEqual(2, global_step)
self.assertEqual(accuracy.shape, ())
input_fn = lambda: tf.random_uniform([3, 784])
predictions_generator = classifier.predict(input_fn)
for i in range(3):
predictions = next(predictions_generator)
self.assertEqual(predictions['probabilities'].shape, (10,))
self.assertEqual(predictions['classes'].shape, ())
def mnist_model_fn_helper(self, mode): def mnist_model_fn_helper(self, mode):
features, labels = self.input_fn() features, labels = dummy_input_fn()
image_count = features.shape[0] image_count = features.shape[0]
spec = mnist.mnist_model_fn( spec = mnist.model_fn(features, labels, mode, {
features, labels, mode, {'data_format': 'channels_last'}) 'data_format': 'channels_last'
})
predictions = spec.predictions if mode == tf.estimator.ModeKeys.PREDICT:
self.assertAllEqual(predictions['probabilities'].shape, (image_count, 10)) predictions = spec.predictions
self.assertEqual(predictions['probabilities'].dtype, tf.float32) self.assertAllEqual(predictions['probabilities'].shape, (image_count, 10))
self.assertAllEqual(predictions['classes'].shape, (image_count,)) self.assertEqual(predictions['probabilities'].dtype, tf.float32)
self.assertEqual(predictions['classes'].dtype, tf.int64) self.assertAllEqual(predictions['classes'].shape, (image_count,))
self.assertEqual(predictions['classes'].dtype, tf.int64)
if mode != tf.estimator.ModeKeys.PREDICT: if mode != tf.estimator.ModeKeys.PREDICT:
loss = spec.loss loss = spec.loss
...@@ -65,5 +98,31 @@ class BaseTest(tf.test.TestCase): ...@@ -65,5 +98,31 @@ class BaseTest(tf.test.TestCase):
self.mnist_model_fn_helper(tf.estimator.ModeKeys.PREDICT) self.mnist_model_fn_helper(tf.estimator.ModeKeys.PREDICT)
class Benchmarks(tf.test.Benchmark):
def benchmark_train_step_time(self):
classifier = make_estimator()
# Run one step to warmup any use of the GPU.
classifier.train(input_fn=dummy_input_fn, steps=1)
have_gpu = tf.test.is_gpu_available()
num_steps = 1000 if have_gpu else 100
name = 'train_step_time_%s' % ('gpu' if have_gpu else 'cpu')
start = time.time()
classifier.train(input_fn=dummy_input_fn, steps=num_steps)
end = time.time()
wall_time = (end - start) / num_steps
self.report_benchmark(
iters=num_steps,
wall_time=wall_time,
name=name,
extras={
'examples_per_sec': BATCH_SIZE / wall_time
})
if __name__ == '__main__': if __name__ == '__main__':
tf.logging.set_verbosity(tf.logging.ERROR)
tf.test.main() tf.test.main()
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment