Files
tensorflow--tensorflow/tensorflow/python/data/ops/load_op.py
T
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

280 lines
9.3 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.
# ==============================================================================
"""Implementation of LoadDataset in Python."""
import multiprocessing
import os
import time
from typing import Any, Callable, Optional, Union
from absl import logging
from google.protobuf import message
from google.protobuf import text_format
from tensorflow.core.protobuf import snapshot_pb2
from tensorflow.python.data.experimental.service import _pywrap_snapshot_utils
from tensorflow.python.data.ops import dataset_ops
from tensorflow.python.data.ops import structured_function
from tensorflow.python.framework import dtypes
from tensorflow.python.framework import errors
from tensorflow.python.framework import tensor_spec
from tensorflow.python.ops import gen_experimental_dataset_ops as ged_ops
from tensorflow.python.platform import gfile
# TODO(b/238903802): Use TypeSpec serialization methods directly.
from tensorflow.python.saved_model import nested_structure_coder
# For distributed snapshot load V2, retries loading after this time, if the
# snapshot is not ready yet.
_RETRY_INTERVAL_SEC = 5
def _load( # pylint: disable=unused-private-name
path: str,
element_spec: Any,
compression: Optional[str],
reader_func: Optional[Callable[[dataset_ops.Dataset], dataset_ops.Dataset]],
wait: bool,
) -> dataset_ops.Dataset:
"""Loads dataset from tf.data snapshot."""
if wait:
return _load_with_retry(path, element_spec, compression, reader_func)
if reader_func is None:
reader_func = lambda datasets: datasets.interleave( # pylint:disable=g-long-lambda
lambda x: x,
cycle_length=multiprocessing.cpu_count(),
num_parallel_calls=dataset_ops.AUTOTUNE)
distributed_snapshot_metadata = _load_distributed_snapshot_metadata(path)
if distributed_snapshot_metadata:
_validate_snapshot(
path, distributed_snapshot_metadata, element_spec, compression)
return _load_distributed_snapshot(
path, distributed_snapshot_metadata, reader_func)
if element_spec is None:
element_spec = _load_element_spec(path)
return _LoadDataset(path, element_spec, compression, reader_func)
def _load_with_retry( # pylint: disable=unused-private-name
path: str,
element_spec: Any = None,
compression: Optional[str] = None,
reader_func: Optional[
Callable[[dataset_ops.Dataset], dataset_ops.Dataset]] = None,
) -> dataset_ops.Dataset:
"""Tries loading the snapshot. Retries if not found."""
while True:
try:
dataset = dataset_ops.Dataset.load(
path=path,
element_spec=element_spec,
compression=compression,
reader_func=reader_func,
wait=False)
logging.info("Load tf.data snapshot at %s.", path)
return dataset
except (errors.NotFoundError, FileNotFoundError):
logging.info(
"Could not find tf.data snapshot at %s. Will wait and retry.", path)
time.sleep(_RETRY_INTERVAL_SEC)
def _load_distributed_snapshot_metadata(
path: str,
) -> Optional[snapshot_pb2.DistributedSnapshotMetadata]:
"""Reads the distributed snapshot metadata.
Args:
path: Base path of the snapshot.
Returns:
DistributedSnapshotMetadata if the snapshot is a distributed snapshot.
Returns None if it is a non-distributed snapshot.
"""
metadata_file = _pywrap_snapshot_utils.TF_DATA_SnapshotMetadataFilePath(path)
if not gfile.Exists(metadata_file):
return None
try:
with gfile.GFile(metadata_file, "r") as f:
return text_format.ParseLines(
f, snapshot_pb2.DistributedSnapshotMetadata())
except (
errors.NotFoundError,
text_format.ParseError,
message.DecodeError,
UnicodeDecodeError):
return None
def _load_distributed_snapshot(
path: str,
metadata: snapshot_pb2.DistributedSnapshotMetadata,
reader_func: Callable[[dataset_ops.Dataset], dataset_ops.Dataset],
) -> dataset_ops.Dataset:
"""Loads a distributed snapshot."""
dataset = _ListSnapshotChunksDataset(path)
dataset = dataset.map(
lambda chunk_file: _SnapshotChunkDataset( # pylint:disable=g-long-lambda
chunk_file,
element_spec=_parse_element_spec(metadata.element_spec),
compression=metadata.compression))
return reader_func(dataset)
def _load_element_spec(path: str) -> Any:
"""Loads the dataset element spec.
Args:
path: Base path of the snapshot.
Returns:
Dataset element_spec.
Raises:
NotFoundError if the element spec file does not exist or cannot be decoded.
"""
dataset_spec_filename = os.path.join(path, dataset_ops.DATASET_SPEC_FILENAME)
if not gfile.Exists(dataset_spec_filename):
raise errors.NotFoundError(
node_def=None, op=None,
message="tf.data snapshot element_spec file not found: "
f"{dataset_spec_filename}.")
with gfile.GFile(dataset_spec_filename, "rb") as f:
encoded_spec = f.read()
try:
return _parse_element_spec(encoded_spec)
except nested_structure_coder.NotEncodableError as e:
raise errors.NotFoundError(
node_def=None, op=None,
message="tf.data snapshot element_spec file not found or invalid: "
f"{dataset_spec_filename}.") from e
def _parse_element_spec(encoded_element_spec: Union[bytes, str]) -> Any:
struct_pb = nested_structure_coder.struct_pb2.StructuredValue()
struct_pb.ParseFromString(encoded_element_spec)
return nested_structure_coder.decode_proto(struct_pb)
class _LoadDataset(dataset_ops.DatasetSource):
"""A dataset that loads previously saved dataset."""
def __init__(
self,
path: str,
element_spec: Any,
compression: str,
reader_func: Callable[[dataset_ops.Dataset], dataset_ops.Dataset]):
self._path = path
self._element_spec = element_spec
self._compression = compression
self._reader_func = structured_function.StructuredFunctionWrapper(
reader_func,
"load()",
# Dataset of datasets of input elements
input_structure=dataset_ops.DatasetSpec(
dataset_ops.DatasetSpec(self._element_spec)))
variant_tensor = ged_ops.load_dataset(
path,
reader_func_other_args=self._reader_func.function.captured_inputs,
compression=compression,
reader_func=self._reader_func.function,
**self._flat_structure)
super().__init__(variant_tensor)
@property
def element_spec(self) -> Any:
return self._element_spec
class _SnapshotChunkDataset(dataset_ops.DatasetSource):
"""A dataset for one chunk file from a tf.data distributed snapshot."""
def __init__(self, chunk_file: str, element_spec: Any, compression: str):
self._chunk_file = chunk_file
self._element_spec = element_spec
variant_tensor = ged_ops.snapshot_chunk_dataset(
chunk_file,
compression=compression,
**self._flat_structure)
super().__init__(variant_tensor)
@property
def element_spec(self) -> Any:
return self._element_spec
class _ListSnapshotChunksDataset(dataset_ops.DatasetSource):
"""A dataset for listing snapshot chunk files.
It supports listing partially written snapshots. When a snapshot is being
written, it returns the currently available chunk files.
"""
def __init__(self, snapshot_path: str):
self._snapshot_path = snapshot_path
variant_tensor = ged_ops.list_snapshot_chunks_dataset(
snapshot_path, **self._flat_structure)
super().__init__(variant_tensor)
@property
def element_spec(self) -> tensor_spec.TensorSpec:
return tensor_spec.TensorSpec([], dtypes.string)
def _validate_snapshot(
path: str,
metadata: snapshot_pb2.DistributedSnapshotMetadata,
element_spec: Any,
compression: str) -> None:
"""Validates a tf.data distributed snapshot.
Args:
path: Root path of the distributed snapshot.
metadata: The DistributedSnapshotMetadata of the snapshot.
element_spec: Dataset element_spec.
compression: Compression method used for saving.
Raises:
ValueError if the snapshot is invalid.
"""
error_file = _pywrap_snapshot_utils.TF_DATA_SnapshotErrorFilePath(path)
if gfile.Exists(error_file):
with gfile.GFile(error_file, "r") as f:
raise ValueError(
f"Failed to load tf.data snapshot at {path}. The save job failed to "
f"write it. Status: {f.read()}")
snapshot_element_spec = _parse_element_spec(metadata.element_spec)
if element_spec and element_spec != snapshot_element_spec:
raise ValueError(
f"Failed to load tf.data snapshot at {path}. User specified "
f"element_spec {element_spec}, but the actual element_spec is "
f"{snapshot_element_spec}.")
if compression and compression != metadata.compression:
raise ValueError(
f"Failed to load tf.data snapshot at {path}. User specified "
f"compression {compression}, but the actual compression is "
f"{metadata.compression}.")