Files
wehub-resource-sync 8a852e4b4e
cffconvert / validate (push) Has been skipped
License Check / license-check (push) Failing after 2s
chore: import upstream snapshot with attribution
2026-07-13 12:14:16 +08:00

153 lines
6.0 KiB
Python

# Copyright 2017 The TensorFlow Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
"""Dependency tracking for trackable objects."""
import warnings
from absl import logging
from tensorflow.python.eager import def_function
from tensorflow.python.eager import function as defun
from tensorflow.python.trackable import base
from tensorflow.python.trackable import data_structures
from tensorflow.python.types import core as core_types
from tensorflow.python.util.tf_export import tf_export
@tf_export("__internal__.tracking.AutoTrackable", v1=[])
class AutoTrackable(base.Trackable):
"""Manages dependencies on other objects.
`Trackable` objects may have dependencies: other `Trackable` objects
which should be saved if the object declaring the dependency is saved. A
correctly saveable program has a dependency graph such that if changing a
global variable affects an object (e.g. changes the behavior of any of its
methods) then there is a chain of dependencies from the influenced object to
the variable.
Dependency edges have names, and are created implicitly when a
`Trackable` object is assigned to an attribute of another
`Trackable` object. For example:
```
obj = Trackable()
obj.v = ResourceVariable(0.)
```
The `Trackable` object `obj` now has a dependency named "v" on a
variable.
`Trackable` objects may specify `Tensor`s to be saved and restored
directly (e.g. a `Variable` indicating how to save itself) rather than through
dependencies on other objects. See
`Trackable._gather_saveables_for_checkpoint` for details.
"""
def __setattr__(self, name, value):
"""Support self.foo = trackable syntax."""
try:
if getattr(self, name) is value:
# Short circuit for `self.$x = self.$x`.
return
except AttributeError:
pass
if getattr(self, "_self_setattr_tracking", True):
value = data_structures.sticky_attribute_assignment(
trackable=self, value=value, name=name)
super(AutoTrackable, self).__setattr__(name, value)
def __delattr__(self, name):
self._delete_tracking(name)
super(AutoTrackable, self).__delattr__(name)
def _no_dependency(self, value):
"""Override to allow TrackableBase to disable dependency tracking."""
return data_structures.NoDependency(value)
def _trackable_children(self, save_type=base.SaveType.CHECKPOINT, **kwargs):
"""Returns all children of a trackable, including functions."""
if save_type != base.SaveType.SAVEDMODEL:
return super(AutoTrackable, self)._trackable_children(
save_type, **kwargs)
functions = {}
try:
# We get the attributes, suppressing warnings and exceptions.
logging_verbosity = logging.get_verbosity()
logging.set_verbosity(logging.FATAL)
for attribute_name in dir(self):
try:
with warnings.catch_warnings():
warnings.simplefilter("ignore")
attribute_value = getattr(self, attribute_name, None)
except Exception: # pylint: disable=broad-except
# NOTE: If we make the exception catching here less broad, we might
# need to revisit `finally` block below.
# We really don't want to throw an exception just because some
# object's attribute accessor is broken.
attribute_value = None
if isinstance(attribute_value, (def_function.Function,
defun.ConcreteFunction)):
functions[attribute_name] = attribute_value
finally:
logging.set_verbosity(logging_verbosity)
# Trace concrete functions to force side-effects:
# 1. populate the cache for functions that have an input_signature
# and have not been called
# 2. force side effects of creation of concrete functions, e.g. create
# variables on first run.
for fn in functions.values():
if isinstance(fn, def_function.Function):
fn._list_all_concrete_functions_for_serialization() # pylint: disable=protected-access
# Additional dependencies may have been generated during function tracing
# (e.g. captured variables). Make sure we return those too.
children = {}
for name, child in self._checkpoint_dependencies:
if isinstance(child, (core_types.PolymorphicFunction,
core_types.ConcreteFunction)):
# Skip "tracked" functions for now since there may be objects that
# automatically track functions that should not be saved.
# TODO(kathywu): remove once `_list_functions_for_serialization` has
# been fully deprecated.
continue
if name in functions and child is not functions[name]:
raise ValueError(
"Can't save object because it has multiple children with the same "
f"name. Object: {self}, attribute name: {name}, child 1: "
f"{child}, child 2: {functions[name]}")
children[name] = child
children.update(functions)
return children
def _delete_tracking(self, name):
"""Removes the tracking of name."""
self._maybe_initialize_trackable()
if name in self._unconditional_dependency_names:
del self._unconditional_dependency_names[name]
for index, (dep_name, _) in enumerate(
self._unconditional_checkpoint_dependencies):
if dep_name == name:
del self._unconditional_checkpoint_dependencies[index]
break
def _add_trackable_child(self, name, value):
self.__setattr__(name, value)