Skip to content

Commit 818520b

Browse files
committed
Add print_pipeline_stage_assignment_summary()
Summary: Add an API which prints how pipeline stages are assigned to stages. TF2.4 Only Fix T43335 Test Plan: CI Reviewers: jackh, jakeh, #tensorflow, simonl, #framework_ip_review_-_any_oss_or_third-party_code_use_has_been_approved Reviewed By: jackh, #tensorflow, #framework_ip_review_-_any_oss_or_third-party_code_use_has_been_approved Maniphest Tasks: T43335 Differential Revision: https://phabricator.sourcevertex.net/D49022
1 parent 7bfbd0a commit 818520b

File tree

5 files changed

+210
-0
lines changed

5 files changed

+210
-0
lines changed

tensorflow/python/ipu/keras/extensions/functional_extensions.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
from tensorflow.python.ipu.keras.extensions import model_extensions
2424
from tensorflow.python.keras.engine import functional
2525
from tensorflow.python.training.tracking import base as trackable
26+
from tensorflow.python.util import nest
2627

2728

2829
class PipelineStage(object):
@@ -558,6 +559,53 @@ def reset_pipeline_stage_assignment(self):
558559
# Pipelining has changed therefore functions need to be recompiled.
559560
self._reset_ipu_extension()
560561

562+
def print_pipeline_stage_assignment_summary(self,
563+
line_length=None,
564+
print_fn=None):
565+
"""Prints a summary of the pipeline stage assignment of the model.
566+
567+
Arguments:
568+
line_length: Total length of printed lines (e.g. set this to adapt the
569+
display to different terminal window sizes).
570+
print_fn: Print function to use. It will be called on each line of the
571+
summary. You can set it to a custom function in order to capture the
572+
string summary. It defaults to `print` (prints to stdout).
573+
"""
574+
line_length = line_length or 89
575+
576+
def print_assignment_fn(assignment, print_row):
577+
layer = assignment.layer
578+
node_index = str(assignment.node_index)
579+
inbound_layers = nest.flatten(assignment.inbound_layers)
580+
pipeline_stage = str(assignment.pipeline_stage)
581+
582+
name = layer.name
583+
input_layer_names = [l.name for l in inbound_layers]
584+
585+
cls_name = layer.__class__.__name__
586+
if not input_layer_names:
587+
first_input = ''
588+
else:
589+
first_input = input_layer_names[0]
590+
591+
fields = [
592+
name + ' (' + cls_name + ') (' + node_index + ')', first_input,
593+
pipeline_stage
594+
]
595+
print_row(fields)
596+
597+
# Print other inputs on the new line.
598+
if len(input_layer_names) > 1:
599+
for i in range(1, len(input_layer_names)):
600+
fields = ['', input_layer_names[i], '']
601+
print_row(fields)
602+
603+
headers = ['Layer (type) (node index)', 'Input Layers', 'Pipeline Stage']
604+
column_widths = [.4, .8, 1.]
605+
self._print_pipeline_stage_assignment_summary_impl(print_assignment_fn,
606+
headers, column_widths,
607+
line_length, print_fn)
608+
561609
@trackable.no_automatic_dependency_tracking
562610
def _get_pipeline_maximum_pipeline_stage(self):
563611
assert self._is_pipelined()

tensorflow/python/ipu/keras/extensions/model_extensions.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1556,6 +1556,45 @@ def set_pipeline_stage_assignment(self, pipeline_stage_assignment):
15561556
def reset_pipeline_stage_assignment(self):
15571557
raise NotImplementedError
15581558

1559+
def print_pipeline_stage_assignment_summary(self,
1560+
line_length=None,
1561+
print_fn=None):
1562+
raise NotImplementedError
1563+
1564+
def _print_pipeline_stage_assignment_summary_impl(self, print_assignment_fn,
1565+
headers, column_widths,
1566+
line_length, print_fn):
1567+
"""Implementation function for the print_pipeline_stage_assignment_summary.
1568+
"""
1569+
print_fn = print_fn if print_fn else print
1570+
assignments = self.get_pipeline_stage_assignment()
1571+
1572+
positions = [int(line_length * p) for p in column_widths]
1573+
1574+
def print_row(fields):
1575+
assert len(fields) == len(positions)
1576+
line = ''
1577+
for i, field in enumerate(fields):
1578+
if i > 0:
1579+
line = line[:-1] + ' '
1580+
line += str(field)
1581+
line = line[:positions[i]]
1582+
line += ' ' * (positions[i] - len(line))
1583+
print_fn(line)
1584+
1585+
print_fn('Model: "{}"'.format(self.name))
1586+
print_fn('_' * line_length)
1587+
print_row(headers)
1588+
print_fn('=' * line_length)
1589+
1590+
for i, assignment in enumerate(assignments):
1591+
print_assignment_fn(assignment, print_row)
1592+
1593+
if i == len(assignments) - 1:
1594+
print_fn('=' * line_length)
1595+
else:
1596+
print_fn('_' * line_length)
1597+
15591598
def _get_pipeline_maximum_pipeline_stage(self):
15601599
"""Returns the maximum pipeline stage assignment"""
15611600
raise NotImplementedError

tensorflow/python/ipu/keras/extensions/sequential_extensions.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,36 @@ def reset_pipeline_stage_assignment(self):
459459
# Pipelining has changed therefore functions need to be recompiled.
460460
self._reset_ipu_extension()
461461

462+
def print_pipeline_stage_assignment_summary(self,
463+
line_length=None,
464+
print_fn=None):
465+
"""Prints a summary of the pipeline stage assignment of the model.
466+
467+
Arguments:
468+
line_length: Total length of printed lines (e.g. set this to adapt the
469+
display to different terminal window sizes).
470+
print_fn: Print function to use. It will be called on each line of the
471+
summary. You can set it to a custom function in order to capture the
472+
string summary. It defaults to `print` (prints to stdout).
473+
"""
474+
line_length = line_length or 60
475+
476+
def print_assignment_fn(assignment, print_row):
477+
layer = assignment.layer
478+
pipeline_stage = str(assignment.pipeline_stage)
479+
480+
name = layer.name
481+
cls_name = layer.__class__.__name__
482+
483+
fields = [name + ' (' + cls_name + ')', pipeline_stage]
484+
print_row(fields)
485+
486+
headers = ['Layer (type)', 'Pipeline Stage']
487+
column_widths = [.5, 1.]
488+
self._print_pipeline_stage_assignment_summary_impl(print_assignment_fn,
489+
headers, column_widths,
490+
line_length, print_fn)
491+
462492
@trackable.no_automatic_dependency_tracking
463493
def _get_pipeline_maximum_pipeline_stage(self):
464494
assert self._is_pipelined()

tensorflow/python/ipu/tests/keras/extensions/functional_pipeline_api_test.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,64 @@ def testSetPipeliningOptions(self):
333333
self.assertEqual(m._pipelining_accumulate_outfeed, True) # pylint: disable=protected-access
334334
self.assertEqual(m._experimental_pipelining_normalize_gradients, True) # pylint: disable=protected-access
335335

336+
def testPrintPipelineStageSummary(self):
337+
cfg = config.IPUConfig()
338+
339+
cfg.auto_select_ipus = 1
340+
cfg.configure_ipu_system()
341+
342+
strategy = ipu_strategy.IPUStrategyV1()
343+
with strategy.scope():
344+
d1 = layers.Input(32, name="input_a")
345+
d2 = layers.Input(32, name="input_b")
346+
f1 = layers.Flatten()(d1)
347+
f2 = layers.Flatten()(d2)
348+
c1 = layers.Concatenate()([f1, f2])
349+
x1 = layers.Dense(4)(c1)
350+
m = training_module.Model((d1, d2), x1)
351+
352+
strings = []
353+
354+
def print_fn(x):
355+
strings.append(x)
356+
357+
m.print_pipeline_stage_assignment_summary(line_length=85,
358+
print_fn=print_fn)
359+
360+
# pylint: disable=line-too-long
361+
self.assertEqual(strings[0], 'Model: "model"')
362+
self.assertEqual(strings[1], '_' * 85)
363+
self.assertEqual(
364+
strings[2],
365+
'Layer (type) (node index) Input Layers Pipeline Stage '
366+
)
367+
self.assertEqual(strings[3], '=' * 85)
368+
self.assertEqual(
369+
strings[4],
370+
'flatten (Flatten) (0) input_a None '
371+
)
372+
self.assertEqual(strings[5], '_' * 85)
373+
self.assertEqual(
374+
strings[6],
375+
'flatten_1 (Flatten) (0) input_b None '
376+
)
377+
self.assertEqual(strings[7], '_' * 85)
378+
self.assertEqual(
379+
strings[8],
380+
'concatenate (Concatenate) (0) flatten None '
381+
)
382+
self.assertEqual(
383+
strings[9],
384+
' flatten_1 '
385+
)
386+
self.assertEqual(strings[10], '_' * 85)
387+
self.assertEqual(
388+
strings[11],
389+
'dense (Dense) (0) concatenate None '
390+
)
391+
self.assertEqual(strings[12], '=' * 85)
392+
# pylint: enable=line-too-long
393+
336394

337395
if __name__ == '__main__':
338396
test.main()

tensorflow/python/ipu/tests/keras/extensions/sequential_pipeline_api_test.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,41 @@ def testSetPipeliningOptions(self):
246246
self.assertEqual(m._pipelining_accumulate_outfeed, True) # pylint: disable=protected-access
247247
self.assertEqual(m._experimental_pipelining_normalize_gradients, True) # pylint: disable=protected-access
248248

249+
def testPrintPipelineStageSummary(self):
250+
cfg = config.IPUConfig()
251+
252+
cfg.auto_select_ipus = 1
253+
cfg.configure_ipu_system()
254+
255+
strategy = ipu_strategy.IPUStrategyV1()
256+
with strategy.scope():
257+
m = sequential.Sequential([layers.Flatten(), layers.Dense(4)])
258+
259+
strings = []
260+
261+
def print_fn(x):
262+
strings.append(x)
263+
264+
m.print_pipeline_stage_assignment_summary(line_length=65,
265+
print_fn=print_fn)
266+
267+
# pylint: disable=line-too-long
268+
self.assertEqual(strings[0], 'Model: "sequential"')
269+
self.assertEqual(strings[1], '_' * 65)
270+
self.assertEqual(
271+
strings[2],
272+
'Layer (type) Pipeline Stage ')
273+
self.assertEqual(strings[3], '=' * 65)
274+
self.assertEqual(
275+
strings[4],
276+
'flatten (Flatten) None ')
277+
self.assertEqual(strings[5], '_' * 65)
278+
self.assertEqual(
279+
strings[6],
280+
'dense (Dense) None ')
281+
self.assertEqual(strings[7], '=' * 65)
282+
# pylint: enable=line-too-long
283+
249284

250285
if __name__ == '__main__':
251286
test.main()

0 commit comments

Comments
 (0)