163 lines
6.1 KiB
Python
163 lines
6.1 KiB
Python
import logging
|
|
import os
|
|
from dataclasses import dataclass
|
|
from typing import Any, Dict, List, Optional, Tuple, Union
|
|
|
|
import pandas as pd
|
|
import pyarrow
|
|
|
|
import ray
|
|
from ray.air.result import Result as ResultV1
|
|
from ray.train import Checkpoint, CheckpointConfig
|
|
from ray.train.v2._internal.constants import CHECKPOINT_MANAGER_SNAPSHOT_FILENAME
|
|
from ray.train.v2._internal.execution.checkpoint.checkpoint_manager import (
|
|
CheckpointManager,
|
|
)
|
|
from ray.train.v2._internal.execution.storage import (
|
|
StorageContext,
|
|
_exists_at_fs_path,
|
|
get_fs_and_path,
|
|
)
|
|
from ray.train.v2.api.exceptions import TrainingFailedError
|
|
from ray.util.annotations import Deprecated, PublicAPI
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
@dataclass
|
|
class Result(ResultV1):
|
|
"""The output of ``trainer.fit()``.
|
|
|
|
Attributes:
|
|
metrics: The latest set of metrics reported by the training function
|
|
via :func:`ray.train.report`.
|
|
checkpoint: The latest checkpoint saved by the training function
|
|
via :func:`ray.train.report`.
|
|
return_value: The value returned by the user-defined training function on the
|
|
rank 0 worker, or ``None`` if no value was returned or if training did
|
|
not complete successfully. The return value must be serializable.
|
|
metrics_dataframe: A DataFrame of metrics from all checkpoints saved
|
|
during the run. Each row corresponds to a checkpoint.
|
|
best_checkpoints: A list of ``(checkpoint, metrics)`` tuples for the
|
|
best checkpoints saved during the run. The checkpoints retained
|
|
are determined by :class:`~ray.train.CheckpointConfig`
|
|
(by default, all checkpoints are kept).
|
|
path: Path pointing to the run output directory on persistent storage.
|
|
This can point to a remote storage location (e.g. S3) or to a
|
|
local location on the head node.
|
|
error: The execution error of the training run, if the run finished
|
|
in error. This is a :class:`~ray.train.v2.api.exceptions.TrainingFailedError`
|
|
wrapping the original exception.
|
|
"""
|
|
|
|
checkpoint: Optional[Checkpoint]
|
|
error: Optional[TrainingFailedError]
|
|
best_checkpoints: Optional[List[Tuple[Checkpoint, Dict[str, Any]]]] = None
|
|
return_value: Optional[Any] = None
|
|
|
|
@PublicAPI(stability="alpha")
|
|
def get_best_checkpoint(
|
|
self, metric: str, mode: str
|
|
) -> Optional["ray.train.Checkpoint"]:
|
|
return super().get_best_checkpoint(metric, mode)
|
|
|
|
@classmethod
|
|
def from_path(
|
|
cls,
|
|
path: Union[str, os.PathLike],
|
|
storage_filesystem: Optional[pyarrow.fs.FileSystem] = None,
|
|
) -> "Result":
|
|
"""Restore a training result from a previously saved training run path.
|
|
|
|
Args:
|
|
path: Path to the run output directory
|
|
storage_filesystem: Optional filesystem to use for accessing the path
|
|
|
|
Returns:
|
|
Result object with restored checkpoints and metrics
|
|
"""
|
|
fs, fs_path = get_fs_and_path(str(path), storage_filesystem)
|
|
|
|
# Validate that the experiment directory exists
|
|
if not _exists_at_fs_path(fs, fs_path):
|
|
raise RuntimeError(f"Experiment folder {fs_path} doesn't exist.")
|
|
|
|
# Remove trailing slashes to handle paths correctly
|
|
# os.path.basename() returns empty string for paths with trailing slashes
|
|
fs_path = fs_path.rstrip("/")
|
|
storage_path, experiment_dir_name = os.path.dirname(fs_path), os.path.basename(
|
|
fs_path
|
|
)
|
|
|
|
storage_context = StorageContext(
|
|
storage_path=storage_path,
|
|
experiment_dir_name=experiment_dir_name,
|
|
storage_filesystem=fs,
|
|
read_only=True,
|
|
)
|
|
|
|
# Validate that the checkpoint manager snapshot file exists
|
|
if not _exists_at_fs_path(
|
|
storage_context.storage_filesystem,
|
|
storage_context.checkpoint_manager_snapshot_path,
|
|
):
|
|
raise RuntimeError(
|
|
f"Failed to restore the Result object: "
|
|
f"{CHECKPOINT_MANAGER_SNAPSHOT_FILENAME} doesn't exist in the "
|
|
f"experiment folder. Make sure that this is an output directory created by a Ray Train run."
|
|
)
|
|
|
|
checkpoint_manager = CheckpointManager(
|
|
storage_context=storage_context,
|
|
checkpoint_config=CheckpointConfig(),
|
|
)
|
|
|
|
# When we build a Result object from checkpoints, the error is not loaded.
|
|
return cls._from_checkpoint_manager(
|
|
checkpoint_manager=checkpoint_manager,
|
|
storage_context=storage_context,
|
|
)
|
|
|
|
@classmethod
|
|
def _from_checkpoint_manager(
|
|
cls,
|
|
checkpoint_manager: CheckpointManager,
|
|
storage_context: StorageContext,
|
|
error: Optional[TrainingFailedError] = None,
|
|
) -> "Result":
|
|
"""Create a Result object from a CheckpointManager."""
|
|
latest_checkpoint_result = checkpoint_manager.latest_checkpoint_result
|
|
if latest_checkpoint_result:
|
|
latest_metrics = latest_checkpoint_result.metrics
|
|
latest_checkpoint = latest_checkpoint_result.checkpoint
|
|
else:
|
|
latest_metrics = None
|
|
latest_checkpoint = None
|
|
best_checkpoints = [
|
|
(r.checkpoint, r.metrics)
|
|
for r in checkpoint_manager.best_checkpoint_results
|
|
]
|
|
|
|
# Provide the history of metrics attached to checkpoints as a dataframe.
|
|
metrics_dataframe = None
|
|
if best_checkpoints:
|
|
metrics_dataframe = pd.DataFrame([m for _, m in best_checkpoints])
|
|
|
|
return Result(
|
|
metrics=latest_metrics,
|
|
checkpoint=latest_checkpoint,
|
|
error=error,
|
|
path=storage_context.experiment_fs_path,
|
|
best_checkpoints=best_checkpoints,
|
|
metrics_dataframe=metrics_dataframe,
|
|
_storage_filesystem=storage_context.storage_filesystem,
|
|
)
|
|
|
|
@property
|
|
@Deprecated
|
|
def config(self) -> Optional[Dict[str, Any]]:
|
|
raise DeprecationWarning(
|
|
"The `config` property for a `ray.train.Result` is deprecated, "
|
|
"since it is only relevant in the context of Ray Tune."
|
|
)
|