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