FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

Add a doc explaining how to convert an `Estimator` to run on a Cloud … · codeplaysoftware/tensorflow@3c2e6f8 · GitHub

Repository navigation

Commit 3c2e6f8

Browse files
authored andcommitted
Add a doc explaining how to convert an Estimator to run on a Cloud TPU.
PiperOrigin-RevId: 184075365
1 parent 5164442 commit 3c2e6f8

1 file changed

Lines changed: 396 additions & 0 deletions

File tree

Lines changed: 396 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,396 @@
1+
# Using TPUs
2+
3+
This document walks through the principal TensorFlow APIs necessary to make
4+
effective use of a [Cloud TPU](https://cloud.google.com/tpu/), and highlights
5+
the differences between regular TensorFlow usage, and usage on a TPU.
6+
7+
This doc is aimed at users who:
8+
9+
* Are familiar with TensorFlow's `Estimator` and `Dataset` APIs
10+
* Have maybe [tried out a Cloud TPU](https://cloud.google.com/tpu/docs/quickstart)
11+
using an existing model.
12+
* Have, perhaps, skimmed the code of an example TPU model
13+
[[1]](https://github.com/tensorflow/models/blob/master/official/mnist/mnist_tpu.py)
14+
[[2]](https://github.com/tensorflow/tpu-demos/tree/master/cloud_tpu/models).
15+
* Are interested in porting an existing `Estimator` model to
16+
run on Cloud TPUs
17+
18+
## TPUEstimator
19+
20+
@{tf.estimator.Estimator$Estimators} are TensorFlow's model-level abstraction.
21+
Standard `Estimators` can drive models on CPU and GPUs. You must use
22+
@{tf.contrib.tpu.TPUEstimator} to drive a model on TPUs.
23+
24+
Refer to TensorFlow's Getting Started section for an introduction to the basics
25+
of using a @{$get_started/premade_estimators$pre-made `Estimator`}, and
26+
@{$get_started/custom_estimators$custom `Estimator`s}.
27+
28+
The `TPUEstimator` class differs somewhat from the `Estimator` class.
29+
30+
The simplest way to maintain a model that can be run both on CPU/GPU or on a
31+
Cloud TPU is to define the model's inference phase (from inputs to predictions)
32+
outside of the `model_fn`. Then maintain separate implementations of the
33+
`Estimator` setup and `model_fn`, both wrapping this inference step. For an
34+
example of this pattern compare the `mnist.py` and `mnist_tpu.py` implementation in
35+
[tensorflow/models](https://github.com/tensorflow/models/tree/master/official/mnist).
36+
37+
### Running a `TPUEstimator` locally
38+
39+
To create a standard `Estimator` you call the constructor, and pass it a
40+
`model_fn`, for example:
41+
42+
```
43+
my_estimator = tf.estimator.Estimator(
44+
model_fn=my_model_fn)
45+
```
46+
47+
The changes required to use a @{tf.contrib.tpu.TPUEstimator} on your local
48+
machine are relatively minor. The constructor requires two additional arguments.
49+
You should set the `use_tpu` argument to `False`, and pass a
50+
@{tf.contrib.tpu.RunConfig} as the `config` argument, as shown below:
51+
52+
``` python
53+
my_tpu_estimator = tf.contrib.tpu.TPUEstimator(
54+
model_fn=my_model_fn,
55+
config=tf.contrib.tpu.RunConfig()
56+
use_tpu=False)
57+
```
58+
59+
Just this simple change will allow you to run a `TPUEstimator` locally.
60+
The majority of example TPU models can be run in this local mode,
61+
by setting the command line flags as follows:
62+
63+
64+
```
65+
$> python mnist_tpu.py --use_tpu=false --master=''
66+
```
67+
68+
Note: This `use_tpu=False` argument is useful for trying out the `TPUEstimator`
69+
API. It is not meant to be a complete TPU compatibility test. Successfully
70+
running a model locally in a `TPUEstimator` does not guarantee that it will
71+
work on a TPU.
72+
73+
74+
### Building a `tpu.RunConfig`
75+
76+
While the default `RunConfig` is sufficient for local training, these settings
77+
cannot be ignored in real usage.
78+
79+
A more typical setup for a `RunConfig`, that can be switched to use a Cloud
80+
TPU, might be as follows:
81+
82+
``` python
83+
import tempfile
84+
import subprocess
85+
86+
class FLAGS(object):
87+
use_tpu=False
88+
tpu_name=None
89+
# Use a local temporary path for the `model_dir`
90+
model_dir = tempfile.mkdtemp()
91+
# Number of training steps to run on the Cloud TPU before returning control.
92+
iterations = 50
93+
# A single Cloud TPU has 8 shards.
94+
num_shards = 8
95+
96+
if FLAGS.use_tpu:
97+
my_project_name = subprocess.check_output([
98+
'gcloud','config','get-value','project'])
99+
my_zone = subprocess.check_output([
100+
'gcloud','config','get-value','compute/zone'])
101+
cluster_resolver = tf.contrib.cluster_resolver.TPUClusterResolver(
102+
tpu_names=[FLAGS.tpu_name],
103+
zone=my_zone,
104+
project=my_project)
105+
master = tpu_cluster_resolver.get_master()
106+
else:
107+
master = ''
108+
109+
my_tpu_run_config = tf.contrib.tpu.RunConfig(
110+
master=master,
111+
evaluation_master=master,
112+
model_dir=FLAGS.model_dir,
113+
session_config=tf.ConfigProto(
114+
allow_soft_placement=True, log_device_placement=True),
115+
tpu_config=tf.contrib.tpu.TPUConfig(FLAGS.iterations,
116+
FLAGS.num_shards),
117+
)
118+
```
119+
120+
Then you must pass the @{tf.contrib.tpu.RunConfig} to the constructor:
121+
122+
``` python
123+
my_tpu_estimator = tf.contrib.tpu.TPUEstimator(
124+
model_fn=my_model_fn,
125+
config = my_tpu_run_config,
126+
use_tpu=FLAGS.use_tpu)
127+
```
128+
129+
Typically the `FLAGS` would be set by command line arguments. To switch from
130+
training locally to training on a cloud TPU you would need to:
131+
132+
1) Set `FLAGS.use_tpu` to `True`
133+
1) Set `FLAGS.tpu_name` so the
134+
`tf.contrib.cluster_resolver.TPUClusterResolver` can find it
135+
1) Set `FLAGS.model_dir` to a Google Cloud Storage bucket url (`gs://`).
136+
137+
138+
## Optimizer
139+
140+
When training on a cloud TPU you **must** wrap the optimizer in a
141+
@{tf.contrib.tpu.CrossShardOptimizer}, which uses an `allreduce` to aggregate
142+
gradients and broadcast the result to each shard (each TPU core).
143+
144+
The `CrossShardOptimizer` is not compatible with local training. So, to have
145+
the same code run both locally and on a Cloud TPU, add lines like the following:
146+
147+
``` python
148+
optimizer = tf.train.GradientDescentOptimizer(learning_rate=learning_rate)
149+
if FLAGS.use_tpu:
150+
optimizer = tf.contrib.tpu.CrossShardOptimizer(optimizer)
151+
```
152+
153+
If you prefer to avoid a global `FLAGS` variable in your model code, one
154+
approach is to set the optimizer as one of the `Estimator`'s params,
155+
as follows:
156+
157+
``` python
158+
my_tpu_estimator = tf.contrib.tpu.TPUEstimator(
159+
model_fn=my_model_fn,
160+
config = my_tpu_run_config,
161+
use_tpu=FLAGS.use_tpu,
162+
params={'optimizer':optimizer})
163+
```
164+
165+
## Model Function
166+
167+
This section details the changes you must make to the model function
168+
(`model_fn()`) to make it `TPUEstimator` compatible.
169+
170+
### Static shapes
171+
172+
During regular usage TensorFlow attempts to determine the shapes of each
173+
`tf.Tensor` during graph construction. During execution any unknown shape
174+
dimensions are determined dynamically,
175+
see @{$programmers_guide/tensors#shape$Tensor Shapes} for more details.
176+
177+
To run on Cloud TPUs TensorFlow models are compiled using @{$xla$XLA}.
178+
XLA uses a similar system for determining shapes at compile time. XLA requires
179+
that all tensor dimensions be statically defined at compile time. All shapes
180+
must evaluate to a constant, and not depend on external data, or stateful
181+
operations like variables or a random number generator.
182+
183+
184+
### Summaries
185+
186+
Remove any use of `tf.summary` from your model.
187+
188+
@{$summaries_and_tensorboard$TensorBoard summaries} are a great way see inside
189+
your model. A minimal set of basic summaries are automatically recorded by the
190+
`TPUEstimator`, to `event` files in the `model_dir`. Custom summaries, however,
191+
are currently unsupported when training on a Cloud TPU. So while the
192+
`TPUEstimator` will still run locally with summaries, it will fail if used on a
193+
TPU.
194+
195+
### Metrics
196+
197+
Build your evaluation metrics dictionary in a stand-alone `metric_fn`.
198+
199+
<!-- TODO(markdaoust) link to programmers_guide/metrics when it exists -->
200+
201+
Evaluation metrics are an essential part of training a model. These are fully
202+
supported on Cloud TPUs, but with a slightly different syntax.
203+
204+
A standard @{tf.metrics} returns two tensors. The first returns the running
205+
average of the metric value, while the second updates the running average and
206+
returns the value for this batch:
207+
208+
```
209+
running_average, current_batch = tf.metrics.accuracy(labels, predictions)
210+
```
211+
212+
In a standard `Estimator` you create a dictionary of these pairs, and return it
213+
as part of the `EstimatorSpec`.
214+
215+
```python
216+
my_metrics = {'accuracy': tf.metrics.accuracy(labels, predictions)}
217+
218+
return tf.estimator.EstimatorSpec(
219+
...
220+
eval_metric_ops=my_metrics
221+
)
222+
```
223+
224+
In a `TPUEstimator` you instead pass a function (which returns a metrics
225+
dictionary) and a list of argument tensors, as shown below:
226+
227+
```python
228+
def my_metric_fn(labels, predictions):
229+
return {'accuracy': tf.metrics.accuracy(labels, predictions)}
230+
231+
return tf.contrib.tpu.TPUEstimatorSpec(
232+
...
233+
eval_metrics=(my_metric_fn, [labels, predictions])
234+
)
235+
```
236+
237+
### Use `TPUEstimatorSpec`
238+
239+
`TPUEstimatorSpec` do not support hooks, and require function wrappers for
240+
some fields.
241+
242+
An `Estimator`'s `model_fn` must return an `EstimatorSpec`. An `EstimatorSpec`
243+
is a simple structure of named fields containing all the `tf.Tensors` of the
244+
model that the `Estimator` may need to interact with.
245+
246+
`TPUEstimators` use a @{tf.contrib.tpu.TPUEstimatorSpec}. There are a few
247+
differences between it and a standard @{tf.estimator.EstimatorSpec}:
248+
249+
250+
* The `eval_metric_ops` must be wrapped into a `metrics_fn`, this field is
251+
renamed `eval_metrics` ([see above](#metrics)).
252+
* The @{tf.train.SessionRunHook$hooks} are unsupported, so these fields are
253+
omitted.
254+
* The @{tf.train.Scaffold$`scaffold`}, if used, must also be wrapped in a
255+
function. This field is renamed to `scaffold_fn`.
256+
257+
`Scaffold` and `Hooks` are for advanced usage, and can typically be omitted.
258+
259+
## Input functions
260+
261+
Input functions work mainly unchanged as they run on the host computer, not the
262+
Cloud TPU itself. This section explains the two necessary adjustments.
263+
264+
### Params argument
265+
266+
<!-- TODO(markdaoust) link to input_fn doc when it exists -->
267+
268+
The `input_fn` for a standard `Estimator` _can_ include a
269+
`params` argument; the `input_fn` for a `TPUEstimator` *must* include a
270+
`params` argument. This is necessary to allow the estimator to set the batch
271+
size for each replica of the input stream. So the minimum signature for an
272+
`input_fn` for a `TPUEstimator` is:
273+
274+
```
275+
def my_input_fn(params):
276+
pass
277+
```
278+
279+
Where `params['batch-size']` will contain the batch size.
280+
281+
### Static shapes and batch size
282+
283+
The input pipeline generated by your `input_fn` is run on CPU. So it is mostly
284+
free strict static shape requirements imposed by the XLA/TPU environment. The
285+
one requirement is that the batches of data fed from your input pipeline to
286+
the TPU have a static shape, as determined by the standard TensorFlow shape
287+
inference algorithm. Intermediate tensors are free to have a dynamic shapes.
288+
If shape inference has failed, but the shape is known it is possible to
289+
impose the correct shape using `tf.set_shape()`.
290+
291+
In the example below the shape
292+
inference algorithm fails, but it is corrected using `set_shape`:
293+
294+
```
295+
>>> x = tf.zeros(tf.constant([1,2,3])+1)
296+
>>> x.shape
297+
298+
TensorShape([Dimension(None), Dimension(None), Dimension(None)])
299+
300+
>>> x.set_shape([2,3,4])
301+
```
302+
303+
In many cases the batch size is the only unknown dimension.
304+
305+
A typical input pipeline, using `tf.data`, will usually produce batches of a
306+
fixed size. The last batch of a finite `Dataset`, however, is typically smaller,
307+
containing just the remaining elements. Since a `Dataset` does not know its own
308+
length or finiteness, the standard @{tf.data.Dataset.batch$`batch`} method
309+
cannot determine if all batches will have a fixed size batch on its own:
310+
311+
```
312+
>>> params = {'batch_size':32}
313+
>>> ds = tf.data.Dataset.from_tensors([0, 1, 2])
314+
>>> ds = ds.repeat().batch(params['batch-size'])
315+
>>> ds
316+
317+
<BatchDataset shapes: (?, 3), types: tf.int32>
318+
```
319+
320+
The most straightforward fix is to
321+
@{tf.data.Dataset.apply$apply} @{tf.contrib.data.batch_and_drop_remainder}
322+
as follows:
323+
324+
```
325+
>>> params = {'batch_size':32}
326+
>>> ds = tf.data.Dataset.from_tensors([0, 1, 2])
327+
>>> ds = ds.repeat().apply(
328+
... tf.contrib.data.batch_and_drop_remainder(params['batch-size']))
329+
>>> ds
330+
331+
<_RestructuredDataset shapes: (32, 3), types: tf.int32>
332+
```
333+
334+
The one downside to this approach is that, as the name implies, this batching
335+
method throws out any fractional batch at the end of the dataset. This is fine
336+
for an infinitely repeating dataset being used for training, but could be a
337+
problem if you want to train for an exact number of epochs.
338+
339+
To do an exact 1-epoch of _evaluation_ you can work around this by manually
340+
padding the length of the batches, and setting the padding entries to have zero
341+
weight when creating your `tf.metrics`.
342+
343+
## Datasets
344+
345+
Efficient use of the `tf.data.Dataset` API is critical when using a Cloud
346+
TPU, as it is impossible to use the Cloud TPU's unless you can feed it data
347+
quickly enough. See @{$datasets_performance} for details on dataset performance.
348+
349+
For all but the simplest experimentation (using
350+
@{tf.data.Dataset.from_tensor_slices} or other in-graph data) you will need to
351+
store all data files read by the `TPUEstimator`'s `Dataset` in Google Cloud
352+
Storage Buckets.
353+
354+
<!--TODO(markdaoust): link to the `TFRecord` doc when it exists.-->
355+
356+
For most use-cases, we recommend converting your data into `TFRecord`
357+
format and using a @{tf.data.TFRecordDataset} to read it. This, however, is not
358+
a hard requirement and you can use other dataset readers
359+
(`FixedLengthRecordDataset` or `TextLineDataset`) if you prefer.
360+
361+
Small datasets can be loaded entirely into memory using
362+
@{tf.data.Dataset.cache}.
363+
364+
Regardless of the data format used, it is strongly recommended that you
365+
@{$performance_guide#use_large_files$use large files}, on the order of
366+
100MB. This is especially important in this networked setting as the overhead
367+
of opening a file is significantly higher.
368+
369+
It is also important, regardless of the type of reader used, to enable buffering
370+
using the `buffer_size` argument to the constructor. This argument is specified
371+
in bytes. A minimum of a few MB (`buffer_size=8*1024*1024`) is recommended so
372+
that data is available when needed.
373+
374+
The TPU-demos repo includes
375+
[a script](https://github.com/tensorflow/tpu-demos/blob/master/cloud_tpu/datasets/imagenet_to_gcs.py)
376+
for downloading the imagenet dataset and converting it to an appropriate format.
377+
This together with the imagenet
378+
[models](https://github.com/tensorflow/tpu-demos/tree/master/cloud_tpu/models)
379+
included in the repo demonstrate all of these best-practices.
380+
381+
382+
## What Next
383+
384+
For details on how to actually set up and run a Cloud TPU see:
385+
386+
* [Google Cloud TPU Documentation](https://cloud.google.com/tpu/docs/)
387+
388+
This document is by no means exhaustive. The best source of more detail on how
389+
to make a Cloud TPU compatible model are the example models published in:
390+
391+
* The [TPU Demos Repository.](https://github.com/tensorflow/tpu-demos/)
392+
393+
For more information about tuning TensorFlow code for performance see:
394+
395+
* The @{$performance$Performance Section.}
396+

0 commit comments

Comments
 (0)

Back | FazBrowse Home | New Git URL