• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2015 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"""Provides templates which allow variable sharing."""
16from __future__ import absolute_import
17from __future__ import division
18from __future__ import print_function
19
20import functools
21import traceback
22
23from tensorflow.python.eager import context
24from tensorflow.python.eager import function
25from tensorflow.python.framework import ops
26from tensorflow.python.ops import variable_scope
27from tensorflow.python.platform import tf_logging as logging
28from tensorflow.python.training.tracking import base as trackable
29from tensorflow.python.training.tracking import util as trackable_util
30from tensorflow.python.util import object_identity
31from tensorflow.python.util import tf_contextlib
32from tensorflow.python.util import tf_decorator
33from tensorflow.python.util.deprecation import deprecated
34from tensorflow.python.util.tf_export import tf_export
35
36__all__ = ["make_template"]
37
38
39@tf_export(v1=["make_template"])
40def make_template(name_,
41                  func_,
42                  create_scope_now_=False,
43                  unique_name_=None,
44                  custom_getter_=None,
45                  **kwargs):
46  """Given an arbitrary function, wrap it so that it does variable sharing.
47
48  This wraps `func_` in a Template and partially evaluates it. Templates are
49  functions that create variables the first time they are called and reuse them
50  thereafter. In order for `func_` to be compatible with a `Template` it must
51  have the following properties:
52
53  * The function should create all trainable variables and any variables that
54     should be reused by calling `tf.compat.v1.get_variable`. If a trainable
55     variable is
56     created using `tf.Variable`, then a ValueError will be thrown. Variables
57     that are intended to be locals can be created by specifying
58     `tf.Variable(..., trainable=false)`.
59  * The function may use variable scopes and other templates internally to
60      create and reuse variables, but it shouldn't use
61      `tf.compat.v1.global_variables` to
62      capture variables that are defined outside of the scope of the function.
63  * Internal scopes and variable names should not depend on any arguments that
64      are not supplied to `make_template`. In general you will get a ValueError
65      telling you that you are trying to reuse a variable that doesn't exist
66      if you make a mistake.
67
68  In the following example, both `z` and `w` will be scaled by the same `y`. It
69  is important to note that if we didn't assign `scalar_name` and used a
70  different name for z and w that a `ValueError` would be thrown because it
71  couldn't reuse the variable.
72
73  ```python
74  def my_op(x, scalar_name):
75    var1 = tf.compat.v1.get_variable(scalar_name,
76                           shape=[],
77                           initializer=tf.compat.v1.constant_initializer(1))
78    return x * var1
79
80  scale_by_y = tf.compat.v1.make_template('scale_by_y', my_op, scalar_name='y')
81
82  z = scale_by_y(input1)
83  w = scale_by_y(input2)
84  ```
85
86  As a safe-guard, the returned function will raise a `ValueError` after the
87  first call if trainable variables are created by calling `tf.Variable`.
88
89  If all of these are true, then 2 properties are enforced by the template:
90
91  1. Calling the same template multiple times will share all non-local
92      variables.
93  2. Two different templates are guaranteed to be unique, unless you reenter the
94      same variable scope as the initial definition of a template and redefine
95      it. An examples of this exception:
96
97  ```python
98  def my_op(x, scalar_name):
99    var1 = tf.compat.v1.get_variable(scalar_name,
100                           shape=[],
101                           initializer=tf.compat.v1.constant_initializer(1))
102    return x * var1
103
104  with tf.compat.v1.variable_scope('scope') as vs:
105    scale_by_y = tf.compat.v1.make_template('scale_by_y', my_op,
106    scalar_name='y')
107    z = scale_by_y(input1)
108    w = scale_by_y(input2)
109
110  # Creates a template that reuses the variables above.
111  with tf.compat.v1.variable_scope(vs, reuse=True):
112    scale_by_y2 = tf.compat.v1.make_template('scale_by_y', my_op,
113    scalar_name='y')
114    z2 = scale_by_y2(input1)
115    w2 = scale_by_y2(input2)
116  ```
117
118  Depending on the value of `create_scope_now_`, the full variable scope may be
119  captured either at the time of first call or at the time of construction. If
120  this option is set to True, then all Tensors created by repeated calls to the
121  template will have an extra trailing _N+1 to their name, as the first time the
122  scope is entered in the Template constructor no Tensors are created.
123
124  Note: `name_`, `func_` and `create_scope_now_` have a trailing underscore to
125  reduce the likelihood of collisions with kwargs.
126
127  Args:
128    name_: A name for the scope created by this template. If necessary, the name
129      will be made unique by appending `_N` to the name.
130    func_: The function to wrap.
131    create_scope_now_: Boolean controlling whether the scope should be created
132      when the template is constructed or when the template is called. Default
133      is False, meaning the scope is created when the template is called.
134    unique_name_: When used, it overrides name_ and is not made unique. If a
135      template of the same scope/unique_name already exists and reuse is false,
136      an error is raised. Defaults to None.
137    custom_getter_: Optional custom getter for variables used in `func_`. See
138      the `tf.compat.v1.get_variable` `custom_getter` documentation for more
139      information.
140    **kwargs: Keyword arguments to apply to `func_`.
141
142  Returns:
143    A function to encapsulate a set of variables which should be created once
144    and reused. An enclosing scope will be created either when `make_template`
145    is called or when the result is called, depending on the value of
146    `create_scope_now_`. Regardless of the value, the first time the template
147    is called it will enter the scope with no reuse, and call `func_` to create
148    variables, which are guaranteed to be unique. All subsequent calls will
149    re-enter the scope and reuse those variables.
150
151  Raises:
152    ValueError: if `name_` is None.
153  """
154  return make_template_internal(
155      name_,
156      func_,
157      create_scope_now_,
158      unique_name_,
159      custom_getter_,
160      create_graph_function_=False,
161      **kwargs)
162
163
164def make_template_internal(name_,
165                           func_,
166                           create_scope_now_=False,
167                           unique_name_=None,
168                           custom_getter_=None,
169                           create_graph_function_=False,
170                           **kwargs):
171  """Make a template, optionally compiling func_ into a graph function.
172
173  See `make_template` for full documentation.
174
175  Args:
176    name_: A name for the scope created by this template. If necessary, the name
177      will be made unique by appending `_N` to the name.
178    func_: The function to wrap.
179    create_scope_now_: Boolean controlling whether the scope should be created
180      when the template is constructed or when the template is called. Default
181      is False, meaning the scope is created when the template is called.
182    unique_name_: When used, it overrides name_ and is not made unique. If a
183      template of the same scope/unique_name already exists and reuse is false,
184      an error is raised. Defaults to None. If executing eagerly, must be None.
185    custom_getter_: Optional custom getter for variables used in `func_`. See
186      the `tf.compat.v1.get_variable` `custom_getter` documentation for more
187      information.
188    create_graph_function_: When True, `func_` will be executed as a graph
189      function. This implies that `func_` must satisfy the properties that
190      `function.defun` requires of functions: See the documentation of
191        `function.defun` for details. When executing eagerly, setting this flag
192        to True can improve performance. Regardless of whether eager execution
193        is enabled, enabling this flag gives the caller access to graph-function
194        semantics, i.e., accesses to variables are totally ordered and
195        side-effecting ops are not pruned.
196    **kwargs: Keyword arguments to apply to `func_`.
197
198  Returns:
199    A function to encapsulate a set of variables which should be created once
200    and reused. An enclosing scope will be created either when `make_template`
201    is called or when the result is called, depending on the value of
202    `create_scope_now_`. Regardless of the value, the first time the template
203    is called it will enter the scope with no reuse, and call `func_` to create
204    variables, which are guaranteed to be unique. All subsequent calls will
205    re-enter the scope and reuse those variables.
206
207  Raises:
208    ValueError: if `name_` is None.
209    ValueError: if `unique_name_` is not None and eager execution is enabled.
210  """
211
212  if kwargs:
213    func_ = tf_decorator.make_decorator(func_,
214                                        functools.partial(func_, **kwargs))
215  if context.executing_eagerly():
216    if unique_name_ is not None:
217      raise ValueError(
218          "unique_name_ cannot be used when eager execution is enabled.")
219    return EagerTemplate(
220        name_,
221        func_,
222        create_scope_now=create_scope_now_,
223        custom_getter=custom_getter_,
224        create_graph_function=create_graph_function_)
225  return Template(
226      name_,
227      func_,
228      create_scope_now=create_scope_now_,
229      unique_name=unique_name_,
230      custom_getter=custom_getter_,
231      create_graph_function=create_graph_function_)
232
233
234def _skip_common_stack_elements(stacktrace, base_case):
235  """Skips items that the target stacktrace shares with the base stacktrace."""
236  for i, (trace, base) in enumerate(zip(stacktrace, base_case)):
237    if trace != base:
238      return stacktrace[i:]
239  return stacktrace[-1:]
240
241
242class Template(trackable.Trackable):
243  """Wrap a function to aid in variable sharing.
244
245  Templates are functions that create variables the first time they are called
246  and reuse them thereafter. See `make_template` for full documentation.
247
248  Note: By default, the full variable scope is captured at the time of first
249  call. If `create_scope_now_` is passed as True to the constructor, the full
250  scope will be captured there, but no variables will created until the first
251  call.
252  """
253
254  def __init__(self,
255               name,
256               func,
257               create_scope_now=False,
258               unique_name=None,
259               custom_getter=None,
260               create_graph_function=False):
261    """Creates a template for the given function.
262
263    Args:
264      name: A name for the scope created by this template. The name will be made
265        unique by appending `_N` to the it (see how
266        `tf.compat.v1.variable_scope` treats the `default_name` for details).
267      func: The function to apply each time.
268      create_scope_now: Whether to create the scope at Template construction
269        time, rather than first call. Defaults to false. Creating the scope at
270        construction time may be more convenient if the template is to passed
271        through much lower level code, and you want to be sure of the scope name
272        without knowing exactly where it will be first called. If set to True,
273        the scope will be created in the constructor, and all subsequent times
274        in `__call__`, leading to a trailing numeral being added to the names of
275        all created Tensors. If set to False, the scope will be created at the
276        first call location.
277      unique_name: When used, it overrides `name` and is not made unique. If a
278        template of the same scope/unique_name already exists and reuse is
279        false, an error is raised. Defaults to None.
280      custom_getter: optional custom getter to pass to `variable_scope()`
281      create_graph_function: When True, `func` will be executed as a graph
282        function. Enabling this flag gives the caller access to graph-function
283        semantics, i.e., accesses to variables are totally ordered and
284        side-effecting ops are not pruned.
285
286    Raises:
287      ValueError: if `name` is None.
288    """
289    if create_graph_function:
290      self._func = function.defun(func)
291    else:
292      self._func = func
293    self._stacktrace = traceback.format_stack()[:-2]
294    self._name = name
295    self._unique_name = unique_name
296    self._custom_getter = custom_getter
297    if name is None:
298      raise ValueError("name cannot be None.")
299    if create_scope_now:
300      with variable_scope._pure_variable_scope(  # pylint:disable=protected-access
301          (self._unique_name or
302           variable_scope._get_unique_variable_scope(self._name)),  # pylint:disable=protected-access
303          custom_getter=self._custom_getter) as vs:
304        self._variable_scope = vs
305    else:
306      self._variable_scope = None
307    # This variable keeps track of whether the template has been called to
308    # completion, which is not the same as whether the scope has been created.
309    self._variables_created = False
310    # `MirroredStrategy` builds the graph with multiple threads. If a
311    # `merge_call` happens within a template, multiple calls may be in progress
312    # simultaneously. This variable keeps track of whether any call of the
313    # template has started.
314    self._first_call = True
315
316  def _call_func(self, args, kwargs):
317    try:
318      if self._variables_created:
319        vars_at_start = len(
320            ops.get_collection_ref(ops.GraphKeys.GLOBAL_VARIABLES))
321        trainable_at_start = len(
322            ops.get_collection_ref(ops.GraphKeys.TRAINABLE_VARIABLES))
323
324        result = self._func(*args, **kwargs)
325
326        # Variables were previously created, implying this is not the first
327        # time the template has been called. Check to make sure that no new
328        # trainable variables were created this time around.
329        trainable_variables = ops.get_collection_ref(
330            ops.GraphKeys.TRAINABLE_VARIABLES)
331
332        # If a variable that we intend to train is created as a side effect
333        # of creating a template, then that is almost certainly an error.
334        if trainable_at_start != len(trainable_variables):
335          raise ValueError("Trainable variable created when calling a template "
336                           "after the first time, perhaps you used tf.Variable "
337                           "when you meant tf.get_variable: %s" %
338                           (trainable_variables[trainable_at_start:],))
339
340        # Non-trainable tracking variables are a legitimate reason why a new
341        # variable would be created, but it is a relatively advanced use-case,
342        # so log it.
343        variables = ops.get_collection_ref(ops.GraphKeys.GLOBAL_VARIABLES)
344        if vars_at_start != len(variables):
345          logging.info(
346              "New variables created when calling a template after "
347              "the first time, perhaps you used tf.Variable when you "
348              "meant tf.get_variable: %s", variables[vars_at_start:])
349      elif self._first_call:
350        self._first_call = False
351        try:
352          # The first time we run, restore variables if necessary (via
353          # Trackable).
354          with trackable_util.capture_dependencies(template=self):
355            result = self._func(*args, **kwargs)
356        except:
357          self._first_call = True
358          raise
359        self._variables_created = True
360      else:  # We are calling the template in parallel from another thread.
361        result = self._func(*args, **kwargs)
362      return result
363    except Exception as exc:
364      # Reraise the exception, but append the original definition to the
365      # trace.
366      args = exc.args
367      if not args:
368        arg0 = ""
369      else:
370        arg0 = args[0]
371      trace = "".join(
372          _skip_common_stack_elements(self._stacktrace,
373                                      traceback.format_stack()))
374      arg0 = "%s\n\noriginally defined at:\n%s" % (arg0, trace)
375      new_args = [arg0]
376      new_args.extend(args[1:])
377      exc.args = tuple(new_args)
378      raise
379
380  def __call__(self, *args, **kwargs):
381    if self._variable_scope:
382      # Only reuse variables if not on first call.
383      with variable_scope.variable_scope(
384          self._variable_scope, reuse=not self._first_call):
385        return self._call_func(args, kwargs)
386    else:
387      # The scope was not created at construction time, so create it here.
388      # Subsequent calls should reuse variables.
389      with variable_scope.variable_scope(
390          self._unique_name, self._name,
391          custom_getter=self._custom_getter) as vs:
392        self._variable_scope = vs
393        return self._call_func(args, kwargs)
394
395  @property
396  def name(self):
397    """Returns the name given to this Template."""
398    return self._name
399
400  @property
401  def func(self):
402    """Returns the func given to this Template."""
403    return self._func
404
405  @property
406  def variable_scope(self):
407    """Returns the variable scope object created by this Template."""
408    return self._variable_scope
409
410  @property
411  def variable_scope_name(self):
412    """Returns the variable scope name created by this Template."""
413    if self._variable_scope:
414      name = self._variable_scope.name
415      if not name or name[-1] == "/":
416        return name
417      else:
418        # To prevent partial matches on the scope_name, we add '/' at the end.
419        return name + "/"
420
421  @property
422  def variables(self):
423    """Returns the list of global and local variables created by the Template."""
424    return self.global_variables + self.local_variables
425
426  @property
427  def trainable_variables(self):
428    """Returns the list of trainable variables created by the Template."""
429    if self._variables_created:
430      return ops.get_collection(ops.GraphKeys.TRAINABLE_VARIABLES,
431                                self.variable_scope_name)
432    else:
433      return []
434
435  @property
436  def non_trainable_variables(self):
437    """Returns the list of non-trainable variables created by the Template."""
438    # TODO(apassos) Make sure it matches Eager when using local variables.
439    global_variables = self.global_variables
440    trainable_variables = set(self.trainable_variables)
441    return [x for x in global_variables if x not in trainable_variables]
442
443  @property
444  def global_variables(self):
445    """Returns the list of global variables created by the Template."""
446    if self._variables_created:
447      return ops.get_collection(ops.GraphKeys.GLOBAL_VARIABLES,
448                                self.variable_scope_name)
449    else:
450      return []
451
452  @property
453  def local_variables(self):
454    """Returns the list of global variables created by the Template."""
455    if self._variables_created:
456      return ops.get_collection(ops.GraphKeys.LOCAL_VARIABLES,
457                                self.variable_scope_name)
458    else:
459      return []
460
461  @property
462  def weights(self):
463    """List of weights/variables created by the Template."""
464    return self.variables
465
466  @property
467  def trainable_weights(self):
468    """List of trainable weights/variables created by the Template."""
469    return self.trainable_variables
470
471  @property
472  def non_trainable_weights(self):
473    """List of non-trainable weights/variables created by the Template."""
474    return self.non_trainable_variables
475
476  @property
477  @deprecated("2017-02-21",
478              "The .var_scope property is deprecated. Please change your "
479              "code to use the .variable_scope property")
480  def var_scope(self):
481    """Returns the variable scope object created by this Template."""
482    return self._variable_scope
483
484
485class _EagerTemplateVariableStore(object):
486  """Wrapper around EagerVariableStore to support nesting EagerTemplates."""
487
488  def __init__(self, variable_scope_name):
489    self._variable_scope_name = variable_scope_name
490    default = variable_scope._get_default_variable_store()  # pylint: disable=protected-access
491    if default._store_eager_variables:  # pylint: disable=protected-access
492      self._eager_variable_store = variable_scope.EagerVariableStore(default)
493    else:
494      self._eager_variable_store = variable_scope.EagerVariableStore()
495
496  def set_variable_scope_name(self, variable_scope_name):
497    self._variable_scope_name = variable_scope_name
498
499  @tf_contextlib.contextmanager
500  def as_default(self):
501    try:
502      with self._eager_variable_store.as_default():
503        yield
504    finally:
505      # Each _EagerTemplateVariableStore object lives underneath a variable
506      # scope (see EagerTemplate.__call__). This variable scope's subscopes are
507      # closed when the EagerTemplate object returns from __call__. For
508      # top-level _EagerTemplateVariableStore objects, the variable store to
509      # which the variable scope is attached is different from the
510      # EagerVariableStore; as such it is necessary to close its subscopes
511      # here as well.
512      if self._variable_scope_name is None:
513        raise RuntimeError("A variable scope must be set before an "
514                           "_EagerTemplateVariableStore object exits.")
515      variable_scope.get_variable_scope_store().close_variable_subscopes(
516          self._variable_scope_name)
517
518  def _variables_in_scope(self, variable_list):
519    if self._variable_scope_name is None:
520      raise RuntimeError(
521          "A variable scope must be set before variables can be accessed.")
522    return [
523        v for v in variable_list
524        if v.name.startswith(self._variable_scope_name + "/")
525    ]
526
527  def variables(self):
528    return self._variables_in_scope(self._eager_variable_store.variables())
529
530  def trainable_variables(self):
531    return self._variables_in_scope(
532        self._eager_variable_store.trainable_variables())
533
534  def non_trainable_variables(self):
535    return self._variables_in_scope(
536        self._eager_variable_store.non_trainable_variables())
537
538
539class EagerTemplate(Template):
540  """Wrap a function to aid in variable sharing in Eager mode.
541
542  Templates are functions that create variables the first time they are called
543  and reuse them thereafter. See `make_template` for full documentation.
544
545  Note: By default, the full variable scope is captured at the time of first
546  call. If `create_scope_now` is passed as True to the constructor, the full
547  scope will be captured there, but no variables will be created until the first
548  call.
549  """
550
551  def __init__(self,
552               name,
553               func,
554               create_scope_now=False,
555               custom_getter=None,
556               create_graph_function=False):
557    """Creates a template for the given function.
558
559    Args:
560      name: A name for the scope created by this template. The name will be made
561        unique by appending `_N` to the it (see how
562        `tf.compat.v1.variable_scope` treats the `default_name` for details).
563      func: The function to apply each time.
564      create_scope_now: Whether to create the scope at Template construction
565        time, rather than first call. Defaults to false. Creating the scope at
566        construction time may be more convenient if the template is passed
567        through much lower level code, and you want to be sure of the scope name
568        without knowing exactly where it will be first called. If set to True,
569        the scope will be created in the constructor, and all subsequent times
570        in `__call__`, leading to a trailing numeral being added to the names of
571        all created Tensors. If set to False, the scope will be created at the
572        first call location.
573      custom_getter: optional custom getter to pass to `variable_scope()`
574      create_graph_function: When True, `func` will be executed as a graph
575        function. Enabling this flag allows the caller to reap the performance
576        benefits associated with executing graphs, at the cost of sacrificing
577        debuggability; however, not all Python functions can be compiled into
578        graph functions. See the documentation for `function.defun` for details.
579
580    Raises:
581      RuntimeError: if eager execution is not enabled.
582    """
583    if not context.executing_eagerly():
584      raise RuntimeError(
585          "{} objects can only be used when eager execution is enabled, use "
586          "tf.Template for graph construction".format(type(self)))
587    super(EagerTemplate, self).__init__(name, func, create_scope_now, None,
588                                        custom_getter, create_graph_function)
589    if self._variable_scope is not None:
590      variable_scope_name = self._variable_scope.name
591    else:
592      # Defer setting the variable scope name until the variable scope
593      # is created in __call__.
594      variable_scope_name = None
595    self._template_store = _EagerTemplateVariableStore(variable_scope_name)
596    self._variable_scope_context_manager = None
597
598  def _call_func(self, args, kwargs):
599    try:
600      vars_at_start = self._template_store.variables()
601      trainable_at_start = self._template_store.trainable_variables()
602      if self._variables_created:
603        result = self._func(*args, **kwargs)
604      else:
605        # The first time we run, restore variables if necessary (via
606        # Trackable).
607        with trackable_util.capture_dependencies(template=self):
608          result = self._func(*args, **kwargs)
609
610      if self._variables_created:
611        # Variables were previously created, implying this is not the first
612        # time the template has been called. Check to make sure that no new
613        # trainable variables were created this time around.
614        trainable_variables = self._template_store.trainable_variables()
615        # If a variable that we intend to train is created as a side effect
616        # of creating a template, then that is almost certainly an error.
617        if len(trainable_at_start) != len(trainable_variables):
618          raise ValueError(
619              "Trainable variable created when calling a template "
620              "after the first time, perhaps you used tf.Variable "
621              "when you meant tf.get_variable: %s" % list(
622                  object_identity.ObjectIdentitySet(trainable_variables) -
623                  object_identity.ObjectIdentitySet(trainable_at_start)))
624
625        # Non-trainable tracking variables are a legitimate reason why a new
626        # variable would be created, but it is a relatively advanced use-case,
627        # so log it.
628        variables = self._template_store.variables()
629        if len(vars_at_start) != len(variables):
630          logging.info(
631              "New variables created when calling a template after "
632              "the first time, perhaps you used tf.Variable when you "
633              "meant tf.get_variable: %s",
634              list(
635                  object_identity.ObjectIdentitySet(variables) -
636                  object_identity.ObjectIdentitySet(vars_at_start)))
637      else:
638        self._variables_created = True
639      return result
640    except Exception as exc:
641      # Reraise the exception, but append the original definition to the
642      # trace.
643      args = exc.args
644      if not args:
645        arg0 = ""
646      else:
647        arg0 = args[0]
648      trace = "".join(
649          _skip_common_stack_elements(self._stacktrace,
650                                      traceback.format_stack()))
651      arg0 = "%s\n\noriginally defined at:\n%s" % (arg0, trace)
652      new_args = [arg0]
653      new_args.extend(args[1:])
654      exc.args = tuple(new_args)
655      raise
656
657  def __call__(self, *args, **kwargs):
658    # In both branches below, the template store is installed as default after
659    # the variable scope is opened in order to ensure that templates nested at
660    # the same level correctly uniquify lower variable scope names.
661    if self._variable_scope:
662      # Create a cache for the variable scope context manager the first time
663      # around so that we don't have to keep recreating it.
664      if not self._variable_scope_context_manager:
665        self._variable_scope_context_manager = variable_scope.variable_scope(
666            self._variable_scope, reuse=variable_scope.AUTO_REUSE)
667      with self._variable_scope_context_manager:
668        with self._template_store.as_default():
669          return self._call_func(args, kwargs)
670    else:
671      # The scope was not created at construction time, so create it here.
672      # Subsequent calls should reuse variables.
673      with variable_scope.variable_scope(
674          self._unique_name, self._name,
675          custom_getter=self._custom_getter) as vs:
676        self._variable_scope = vs
677        # Because the scope was not created at construction time, the template
678        # store's variable scope name is unset; set it here.
679        self._template_store.set_variable_scope_name(vs.name)
680        with self._template_store.as_default():
681          return self._call_func(args, kwargs)
682
683  @property
684  def variables(self):
685    """Returns the list of variables created by the Template."""
686    # Currently there is no local variable in Eager mode.
687    if not self._variables_created:
688      return []
689    return self._template_store.variables()
690
691  @property
692  def trainable_variables(self):
693    """Returns the list of trainable variables created by the Template."""
694    # Currently there is no local variable in Eager mode.
695    if not self._variables_created:
696      return []
697    return self._template_store.trainable_variables()
698
699  @property
700  def non_trainable_variables(self):
701    """Returns the list of non-trainable variables created by the Template."""
702    # Currently there is no local variable in Eager mode.
703    if not self._variables_created:
704      return []
705    return self._template_store.non_trainable_variables()
706
707  @property
708  def global_variables(self):
709    """Returns the list of global variables created by the Template."""
710    # Currently there is no local variable in Eager mode.
711    if not self._variables_created:
712      return []
713    return self.variables
714
715  @property
716  def local_variables(self):
717    """Returns the list of global variables created by the Template."""
718    # Currently there is no local variable in Eager mode.
719    return []
720