1# Copyright 2017 The TensorFlow Authors. All Rights Reserved. 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14#============================================================================== 15"""Contains utility functions used by summary ops.""" 16 17from __future__ import absolute_import 18from __future__ import division 19from __future__ import print_function 20 21import contextlib 22import re 23 24from tensorflow.python.framework import ops 25from tensorflow.python.platform import tf_logging 26 27 28def collect(val, collections, default_collections): 29 """Adds keys to a collection. 30 31 Args: 32 val: The value to add per each key. 33 collections: A collection of keys to add. 34 default_collections: Used if collections is None. 35 """ 36 if collections is None: 37 collections = default_collections 38 for key in collections: 39 ops.add_to_collection(key, val) 40 41 42_INVALID_TAG_CHARACTERS = re.compile(r'[^-/\w\.]') 43 44 45def clean_tag(name): 46 """Cleans a tag. Removes illegal characters for instance. 47 48 Args: 49 name: The original tag name to be processed. 50 51 Returns: 52 The cleaned tag name. 53 """ 54 # In the past, the first argument to summary ops was a tag, which allowed 55 # arbitrary characters. Now we are changing the first argument to be the node 56 # name. This has a number of advantages (users of summary ops now can 57 # take advantage of the tf name scope system) but risks breaking existing 58 # usage, because a much smaller set of characters are allowed in node names. 59 # This function replaces all illegal characters with _s, and logs a warning. 60 # It also strips leading slashes from the name. 61 if name is not None: 62 new_name = _INVALID_TAG_CHARACTERS.sub('_', name) 63 new_name = new_name.lstrip('/') # Remove leading slashes 64 if new_name != name: 65 tf_logging.info('Summary name %s is illegal; using %s instead.' % 66 (name, new_name)) 67 name = new_name 68 return name 69 70 71@contextlib.contextmanager 72def summary_scope(name, family=None, default_name=None, values=None): 73 """Enters a scope used for the summary and yields both the name and tag. 74 75 To ensure that the summary tag name is always unique, we create a name scope 76 based on `name` and use the full scope name in the tag. 77 78 If `family` is set, then the tag name will be '<family>/<scope_name>', where 79 `scope_name` is `<outer_scope>/<family>/<name>`. This ensures that `family` 80 is always the prefix of the tag (and unmodified), while ensuring the scope 81 respects the outer scope from this summary was created. 82 83 Args: 84 name: A name for the generated summary node. 85 family: Optional; if provided, used as the prefix of the summary tag name. 86 default_name: Optional; if provided, used as default name of the summary. 87 values: Optional; passed as `values` parameter to name_scope. 88 89 Yields: 90 A tuple `(tag, scope)`, both of which are unique and should be used for the 91 tag and the scope for the summary to output. 92 """ 93 name = clean_tag(name) 94 family = clean_tag(family) 95 # Use family name in the scope to ensure uniqueness of scope/tag. 96 scope_base_name = name if family is None else '{}/{}'.format(family, name) 97 with ops.name_scope(scope_base_name, default_name, values) as scope: 98 if family is None: 99 tag = scope.rstrip('/') 100 else: 101 # Prefix our scope with family again so it displays in the right tab. 102 tag = '{}/{}'.format(family, scope.rstrip('/')) 103 # Note: tag is not 100% unique if the user explicitly enters a scope with 104 # the same name as family, then later enter it again before summaries. 105 # This is very contrived though, and we opt here to let it be a runtime 106 # exception if tags do indeed collide. 107 yield (tag, scope) 108