unique_name.py 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. # Copyright (c) 2018 PaddlePaddle 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. import collections
  15. from copy import deepcopy
  16. from .wrapped_decorator import signature_safe_contextmanager
  17. __all__ = []
  18. class UniqueNameGenerator:
  19. """
  20. Generate unique name with prefix.
  21. Args:
  22. prefix(str): The generated name prefix. All generated name will be
  23. started with this prefix.
  24. """
  25. def __init__(self, prefix=None):
  26. self.ids = collections.defaultdict(int)
  27. if prefix is None:
  28. prefix = ""
  29. self.prefix = prefix
  30. def __call__(self, key):
  31. return self.generate(key)
  32. def generate(self, key):
  33. """
  34. Generate unique names with prefix
  35. Args:
  36. key(str): The key of return string.
  37. Returns(str): A unique string with the prefix
  38. """
  39. tmp = self.ids[key]
  40. self.ids[key] += 1
  41. return self.prefix + "_".join([key, str(tmp)])
  42. def generate_with_ignorable_key(self, key):
  43. from .framework import _dygraph_tracer, in_dygraph_mode
  44. if in_dygraph_mode():
  45. return _dygraph_tracer()._generate_unique_name()
  46. return self.generate(key)
  47. def clone(self):
  48. ret = UniqueNameGenerator(self.prefix)
  49. ret.ids = deepcopy(self.ids)
  50. return ret
  51. class DygraphParameterNameChecker:
  52. """
  53. Check whether the name of parameter is used.
  54. """
  55. def __init__(self):
  56. self._name_set = set()
  57. def __call__(self, name):
  58. '''
  59. Check whether the name is used. If not used, insert into the _name_set.
  60. Args:
  61. name(str): The name of parameter to check.
  62. Returns(bool): If the name is in name_set, return True; Otherwise, return False.
  63. '''
  64. if name in self._name_set:
  65. return True
  66. else:
  67. self._name_set.add(name)
  68. return False
  69. dygraph_parameter_name_checker = DygraphParameterNameChecker()
  70. generator = UniqueNameGenerator()
  71. def generate(key):
  72. """
  73. Generate unique name with prefix key. Currently, Paddle distinguishes the
  74. names of the same key by numbering it from zero. For example, when key=fc,
  75. it continuously generates fc_0, fc_1, fc_2, etc.
  76. Args:
  77. key(str): The prefix of generated name.
  78. Returns:
  79. str: A unique string with the prefix key.
  80. Examples:
  81. .. code-block:: python
  82. >>> import paddle
  83. >>> name1 = paddle.utils.unique_name.generate('fc')
  84. >>> name2 = paddle.utils.unique_name.generate('fc')
  85. >>> print(name1, name2)
  86. fc_0 fc_1
  87. """
  88. return generator(key)
  89. # FIXME(zjl): The previous naming rule in static graph would
  90. # cause memory leak in dygraph mode. It is because the previous
  91. # naming rule would use `conv_0.tmp` as the key, and in dygraph
  92. # mode, `conv_i` increases as batch increases. Thus, keys would
  93. # increase in a way like `conv_0.tmp`, `conv_1.tmp`, ....
  94. # Not find a better way to fix this bug in dygraph mode. In TF,
  95. # variable name is meaningless in eager execution mode, and in
  96. # PyTorch, there is no variable name at all. Maybe we should
  97. # discard variable name in dygraph mode.
  98. #
  99. # Another concern is that save/load interfaces. Usually, user
  100. # would save model in static graph mode, and load it in dygraph
  101. # mode. Therefore, we keep the variable name of Parameter currently.
  102. #
  103. # Please fix me if a better method is found.
  104. #
  105. # NOTE(zhiqiu): use c++ unique_name_generator in dygraph mode,
  106. # in order to keep name consistency.
  107. def generate_with_ignorable_key(key):
  108. from .framework import _dygraph_tracer, in_dygraph_mode
  109. if in_dygraph_mode():
  110. return _dygraph_tracer()._generate_unique_name()
  111. return generator(key)
  112. def switch(new_generator=None, new_para_name_checker=None):
  113. """
  114. Switch the namespace of in current context to a new namespace. Though
  115. :code:`switch()` and :code:`guard()` can both change namespace,
  116. :code:`guard()` is recommended since it can manage the context better
  117. together with :code:`with` statement.
  118. Args:
  119. new_generator(UniqueNameGenerator, optional): A new UniqueNameGenerator, not
  120. required normally. Default is None, which means switch to a new anonymous
  121. namespace.
  122. new_para_name_checker(DygraphParameterNameChecker, optional): A new DygraphParameterNameChecker,
  123. not required normally. Default is None, which means switch to a new parameter name
  124. checker.
  125. Returns:
  126. UniqueNameGenerator: The previous UniqueNameGenerator.
  127. DygraphParameterNameChecker: The previous DygraphParameterNameChecker
  128. Examples:
  129. .. code-block:: python
  130. >>> import paddle
  131. >>> name1 = paddle.utils.unique_name.generate('fc')
  132. >>> name2 = paddle.utils.unique_name.generate('fc')
  133. >>> print(name1, name2)
  134. fc_0 fc_1
  135. >>> pre_generator, pre_dygraph_name_checker = paddle.utils.unique_name.switch() # switch to a new anonymous namespace.
  136. >>> name2 = paddle.utils.unique_name.generate('fc')
  137. >>> print(name2)
  138. fc_0
  139. >>> paddle.utils.unique_name.switch(pre_generator, pre_dygraph_name_checker) # switch back to pre_generator.
  140. >>> name3 = paddle.utils.unique_name.generate('fc')
  141. >>> print(name3)
  142. fc_2
  143. """
  144. global generator
  145. old_generator = generator
  146. global dygraph_parameter_name_checker
  147. old_para_name_checker = dygraph_parameter_name_checker
  148. if new_generator is None:
  149. generator = UniqueNameGenerator()
  150. else:
  151. generator = new_generator
  152. if new_para_name_checker is None:
  153. dygraph_parameter_name_checker = DygraphParameterNameChecker()
  154. else:
  155. dygraph_parameter_name_checker = new_para_name_checker
  156. return old_generator, old_para_name_checker
  157. @signature_safe_contextmanager
  158. def guard(new_generator=None):
  159. """
  160. Change the namespace of unique name with :code:`with` statement. After calling it,
  161. a new namespace in the context of :code:`with` will be created, and it will number
  162. names from zero again when calling :code:`generate()` with same key.
  163. Args:
  164. new_generator(str|bytes, optional): New name of global namespace. Note that str
  165. in Python2 was splitted into str and bytes in Python3, so here are two
  166. types. Default is None. If not None, new_generator will be added into
  167. the prefix of unique name generated by :code:`generate()`.
  168. Returns:
  169. None.
  170. Examples:
  171. .. code-block:: python
  172. >>> import paddle
  173. >>> with paddle.utils.unique_name.guard():
  174. ... name_1 = paddle.utils.unique_name.generate('fc')
  175. >>> with paddle.utils.unique_name.guard():
  176. ... name_2 = paddle.utils.unique_name.generate('fc')
  177. >>> print(name_1, name_2)
  178. fc_0 fc_0
  179. >>> with paddle.utils.unique_name.guard('A'):
  180. ... name_1 = paddle.utils.unique_name.generate('fc')
  181. >>> with paddle.utils.unique_name.guard('B'):
  182. ... name_2 = paddle.utils.unique_name.generate('fc')
  183. >>> print(name_1, name_2)
  184. Afc_0 Bfc_0
  185. """
  186. if isinstance(new_generator, str):
  187. new_generator = UniqueNameGenerator(new_generator)
  188. elif isinstance(new_generator, bytes):
  189. new_generator = UniqueNameGenerator(new_generator.decode())
  190. old_generator, old_para_name_checker = switch(new_generator)
  191. try:
  192. yield
  193. finally:
  194. switch(old_generator, old_para_name_checker)