chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 13:17:40 +08:00
commit f1825c8ceb
10096 changed files with 2364182 additions and 0 deletions
@@ -0,0 +1,311 @@
# __validation_fn_simple_start__
import os
import torch
import ray.train
import ray.data
# Define Ray Data validation dataset outside validation function because it is not json serializable
validation_dataset = ...
def validation_fn(checkpoint: ray.train.Checkpoint) -> dict:
# Load the checkpoint
model = ...
with checkpoint.as_directory() as checkpoint_dir:
model_state_dict = torch.load(os.path.join(checkpoint_dir, "model.pt"))
model.load_state_dict(model_state_dict)
model.eval()
# Perform validation on the data
total_accuracy = 0
with torch.no_grad():
for batch in validation_dataset.iter_torch_batches(batch_size=128):
images, labels = batch["image"], batch["label"]
outputs = model(images)
total_accuracy += (outputs.argmax(1) == labels).sum().item()
return {"score": total_accuracy / len(validation_dataset)}
# __validation_fn_simple_end__
# __validation_fn_torch_trainer_start__
import torchmetrics
from torch.nn import CrossEntropyLoss
import ray.train.torch
from ray.data import ExecutionOptions
def eval_only_train_fn(config_dict: dict) -> dict:
# Load the checkpoint
model = ...
with config_dict["checkpoint"].as_directory() as checkpoint_dir:
model_state_dict = torch.load(os.path.join(checkpoint_dir, "model.pt"))
model.load_state_dict(model_state_dict)
model.cuda().eval()
# Set up metrics and data loaders
criterion = CrossEntropyLoss()
mean_valid_loss = torchmetrics.MeanMetric().cuda()
test_data_shard = ray.train.get_dataset_shard("validation")
test_dataloader = test_data_shard.iter_torch_batches(batch_size=128)
# Compute metric and return it directly from the train function
with torch.no_grad():
for batch in test_dataloader:
images, labels = batch["image"], batch["label"]
outputs = model(images)
loss = criterion(outputs, labels)
mean_valid_loss(loss)
return {"score": mean_valid_loss.compute().item()}
def validation_fn(checkpoint: ray.train.Checkpoint, train_run_name: str, epoch: int) -> dict:
trainer = ray.train.torch.TorchTrainer(
eval_only_train_fn,
train_loop_config={"checkpoint": checkpoint},
scaling_config=ray.train.ScalingConfig(
num_workers=2, use_gpu=True, accelerator_type="A10G"
),
# Give unique name to validation run so it does not attempt to load placeholder checkpoint.
# Also allows you to better associate training runs with validation runs.
run_config=ray.train.RunConfig(
name=f"{train_run_name}_validation_epoch_{epoch}"
),
# Use weaker GPUs for validation
datasets={"validation": validation_dataset},
# Pin to the "validation" subcluster so it doesn't compete with
# training. See https://docs.ray.io/en/latest/data/concurrent-dataset-execution.html.
dataset_config=ray.train.DataConfig(
execution_options={
"validation": ExecutionOptions(
label_selector={"ray-subcluster": "validation"}
),
},
),
)
result = trainer.fit()
# return_value holds the value returned by train function of worker 0
return result.return_value
# __validation_fn_torch_trainer_end__
# __validation_fn_map_batches_start__
import ray.data
class Predictor:
def __init__(self, checkpoint: ray.train.Checkpoint):
self.model = ...
with checkpoint.as_directory() as checkpoint_dir:
model_state_dict = torch.load(os.path.join(checkpoint_dir, "model.pt"))
self.model.load_state_dict(model_state_dict)
self.model.cuda().eval()
def __call__(self, batch: dict) -> dict:
image = torch.as_tensor(batch["image"], dtype=torch.float32, device="cuda")
label = torch.as_tensor(batch["label"], dtype=torch.float32, device="cuda")
pred = self.model(image)
return {"res": (pred.argmax(1) == label).cpu().numpy()}
# Construct ``validation_dataset`` under a DataContext copy pinned to the
# "validation" subcluster. ``Dataset.context`` is a deep copy of the
# current context taken at construction, so the selector is baked in and
# every downstream operator (including the ``map_batches`` below) inherits
# it — no in-function mutation needed. See
# https://docs.ray.io/en/latest/data/concurrent-dataset-execution.html.
ctx = ray.data.DataContext.get_current().copy()
ctx.execution_options.label_selector = {"ray-subcluster": "validation"}
with ray.data.DataContext.current(ctx):
validation_dataset = ray.data.read_parquet(...)
def validation_fn(checkpoint: ray.train.Checkpoint) -> dict:
# Set name to avoid confusion; default name is "Dataset"
validation_dataset.set_name("validation")
eval_res = validation_dataset.map_batches(
Predictor,
batch_size=128,
num_gpus=1,
fn_constructor_kwargs={"checkpoint": checkpoint},
concurrency=2,
)
mean = eval_res.mean(["res"])
return {
"score": mean,
}
# __validation_fn_map_batches_end__
# __validation_fn_report_start__
import tempfile
from ray.data import ExecutionOptions
from ray.train import ValidationConfig, ValidationTaskConfig
def train_func(config: dict) -> None:
...
epochs = ...
model = ...
rank = ray.train.get_context().get_world_rank()
for epoch in epochs:
... # training step
if rank == 0:
training_metrics = {"loss": ..., "epoch": epoch}
local_checkpoint_dir = tempfile.mkdtemp()
torch.save(
model.module.state_dict(),
os.path.join(local_checkpoint_dir, "model.pt"),
)
ray.train.report(
training_metrics,
checkpoint=ray.train.Checkpoint.from_directory(local_checkpoint_dir),
checkpoint_upload_mode=ray.train.CheckpointUploadMode.ASYNC,
validation=ValidationTaskConfig(fn_kwargs={
"train_run_name": ray.train.get_context().get_experiment_name(),
"epoch": epoch,
}),
)
else:
ray.train.report({}, None)
def run_trainer() -> ray.train.Result:
# 1) Construction-time tasks (parquet schema inference, file listing)
# read the current DataContext. Pin them to "training" with a copy of
# the DataContext applied via the DataContext.current() context
# manager — scoped to the `with` block so it doesn't leak. See
# https://docs.ray.io/en/latest/data/concurrent-dataset-execution.html.
ctx = ray.data.DataContext.get_current().copy()
ctx.execution_options.label_selector = {"ray-subcluster": "training"}
with ray.data.DataContext.current(ctx):
train_dataset = ray.data.read_parquet(...)
trainer = ray.train.torch.TorchTrainer(
train_func,
validation_config=ValidationConfig(fn=validation_fn),
# Pass training dataset in datasets arg to split it across training workers
datasets={"train": train_dataset},
# 2) DataConfig.execution_options REPLACES ds.context.execution_options
# wholesale at training start, dropping anything not re-specified
# (including label_selector). Restate the selector here so per-worker
# ingest stays pinned to "training".
dataset_config=ray.train.DataConfig(
datasets_to_split=["train"],
execution_options={
"train": ExecutionOptions(
label_selector={"ray-subcluster": "training"}
),
},
),
scaling_config=ray.train.ScalingConfig(
num_workers=2,
use_gpu=True,
# Use powerful GPUs for training
accelerator_type="A100",
),
)
return trainer.fit()
# __validation_fn_report_end__
# __exp_tracking_same_run_wandb_start__
import wandb
import ray.train
from ray.train import ValidationConfig, ValidationTaskConfig
entity = "my_entity"
project = "my_project"
num_epochs = ...
def validation_fn(checkpoint: ray.train.Checkpoint, wandb_run_id: str, val_step: int) -> dict:
wandb.init(
entity=entity,
project=project,
settings=wandb.Settings(mode="shared", x_primary=False),
id=wandb_run_id,
)
score = ...
wandb.log({"validation/loss": score, "val_step": val_step})
wandb.finish() # flush the metrics
return {"validation/loss": score}
def train_func():
if ray.train.get_context().get_world_rank() == 0:
run = wandb.init(
entity=entity,
project=project,
settings=wandb.Settings(mode="shared", x_primary=True,)
)
wandb.define_metric("val_step", hidden=True)
wandb.define_metric("train_step", hidden=True)
wandb.define_metric("validation/loss", step_metric="val_step")
wandb.define_metric("train/loss", step_metric="train_step")
for epoch in range(num_epochs):
loss = ...
if ray.train.get_context().get_world_rank() == 0:
wandb.log({"train/loss": loss, "train_step": epoch})
checkpoint = ...
ray.train.report(
{"train/loss": loss},
checkpoint=checkpoint,
validation=ValidationTaskConfig(
fn_kwargs={"wandb_run_id": run.id, "val_step": epoch}
),
)
else:
ray.train.report({}, None)
if ray.train.get_context().get_world_rank() == 0:
wandb.finish()
# __exp_tracking_same_run_wandb_end__
# __exp_tracking_same_run_mlflow_start__
import mlflow
from mlflow.tracking import MlflowClient
import ray.train
from ray.train import ValidationConfig, ValidationTaskConfig
tracking_uri = "my_uri"
experiment_name = "my_experiment"
num_epochs = ...
def validation_fn(
checkpoint: ray.train.Checkpoint, mlflow_run_id: str, val_step: int
) -> dict:
client = MlflowClient(tracking_uri=tracking_uri)
score = ...
client.log_metric(mlflow_run_id, "val_score", score, step=val_step)
return {"val_score": score}
def train_func():
if ray.train.get_context().get_world_rank() == 0:
client = MlflowClient(tracking_uri=tracking_uri)
experiment = client.get_experiment_by_name(experiment_name)
run = client.create_run(experiment_id=experiment.experiment_id)
for epoch in range(num_epochs):
loss = ...
if ray.train.get_context().get_world_rank() == 0:
client.log_metric(run.info.run_id, "train_loss", loss, step=epoch)
checkpoint = ...
ray.train.report(
{"train_loss": loss},
checkpoint=checkpoint,
validation=ValidationTaskConfig(
fn_kwargs={"mlflow_run_id": run.info.run_id, "val_step": epoch}
),
)
else:
ray.train.report({}, None)
if ray.train.get_context().get_world_rank() == 0:
client.set_terminated(run.info.run_id)
# __exp_tracking_same_run_mlflow_end__
+569
View File
@@ -0,0 +1,569 @@
# flake8: noqa
# isort: skip_file
# __pytorch_save_start__
import os
import tempfile
import numpy as np
import torch
import torch.nn as nn
from torch.optim import Adam
import ray.train.torch
from ray import train
from ray.train import Checkpoint, ScalingConfig
from ray.train.torch import TorchTrainer
def train_func(config):
n = 100
# create a toy dataset
# data : X - dim = (n, 4)
# target : Y - dim = (n, 1)
X = torch.Tensor(np.random.normal(0, 1, size=(n, 4)))
Y = torch.Tensor(np.random.uniform(0, 1, size=(n, 1)))
# toy neural network : 1-layer
# Wrap the model in DDP
model = ray.train.torch.prepare_model(nn.Linear(4, 1))
criterion = nn.MSELoss()
optimizer = Adam(model.parameters(), lr=3e-4)
for epoch in range(config["num_epochs"]):
y = model.forward(X)
loss = criterion(y, Y)
optimizer.zero_grad()
loss.backward()
optimizer.step()
metrics = {"loss": loss.item()}
with tempfile.TemporaryDirectory() as temp_checkpoint_dir:
checkpoint = None
should_checkpoint = epoch % config.get("checkpoint_freq", 1) == 0
# In standard DDP training, where the model is the same across all ranks,
# only the global rank 0 worker needs to save and report the checkpoint
if train.get_context().get_world_rank() == 0 and should_checkpoint:
torch.save(
model.module.state_dict(), # NOTE: Unwrap the model.
os.path.join(temp_checkpoint_dir, "model.pt"),
)
checkpoint = Checkpoint.from_directory(temp_checkpoint_dir)
train.report(metrics, checkpoint=checkpoint)
trainer = TorchTrainer(
train_func,
train_loop_config={"num_epochs": 5},
scaling_config=ScalingConfig(num_workers=2),
)
result = trainer.fit()
# __pytorch_save_end__
# __pytorch_restore_start__
import os
import tempfile
import numpy as np
import torch
import torch.nn as nn
from torch.optim import Adam
import ray.train.torch
from ray import train
from ray.train import Checkpoint, ScalingConfig
from ray.train.torch import TorchTrainer
def train_func(config):
n = 100
# create a toy dataset
# data : X - dim = (n, 4)
# target : Y - dim = (n, 1)
X = torch.Tensor(np.random.normal(0, 1, size=(n, 4)))
Y = torch.Tensor(np.random.uniform(0, 1, size=(n, 1)))
# toy neural network : 1-layer
model = nn.Linear(4, 1)
optimizer = Adam(model.parameters(), lr=3e-4)
criterion = nn.MSELoss()
# Wrap the model in DDP and move it to GPU.
model = ray.train.torch.prepare_model(model)
# ====== Resume training state from the checkpoint. ======
start_epoch = 0
checkpoint = train.get_checkpoint()
if checkpoint:
with checkpoint.as_directory() as checkpoint_dir:
model_state_dict = torch.load(
os.path.join(checkpoint_dir, "model.pt"),
# map_location=..., # Load onto a different device if needed.
)
model.module.load_state_dict(model_state_dict)
optimizer.load_state_dict(
torch.load(os.path.join(checkpoint_dir, "optimizer.pt"))
)
start_epoch = (
torch.load(os.path.join(checkpoint_dir, "extra_state.pt"))["epoch"] + 1
)
# ========================================================
for epoch in range(start_epoch, config["num_epochs"]):
y = model.forward(X)
loss = criterion(y, Y)
optimizer.zero_grad()
loss.backward()
optimizer.step()
metrics = {"loss": loss.item()}
with tempfile.TemporaryDirectory() as temp_checkpoint_dir:
checkpoint = None
should_checkpoint = epoch % config.get("checkpoint_freq", 1) == 0
# In standard DDP training, where the model is the same across all ranks,
# only the global rank 0 worker needs to save and report the checkpoint
if train.get_context().get_world_rank() == 0 and should_checkpoint:
# === Make sure to save all state needed for resuming training ===
torch.save(
model.module.state_dict(), # NOTE: Unwrap the model.
os.path.join(temp_checkpoint_dir, "model.pt"),
)
torch.save(
optimizer.state_dict(),
os.path.join(temp_checkpoint_dir, "optimizer.pt"),
)
torch.save(
{"epoch": epoch},
os.path.join(temp_checkpoint_dir, "extra_state.pt"),
)
# ================================================================
checkpoint = Checkpoint.from_directory(temp_checkpoint_dir)
train.report(metrics, checkpoint=checkpoint)
if epoch == 1:
raise RuntimeError("Intentional error to showcase restoration!")
trainer = TorchTrainer(
train_func,
train_loop_config={"num_epochs": 5},
scaling_config=ScalingConfig(num_workers=2),
run_config=train.RunConfig(failure_config=train.FailureConfig(max_failures=1)),
)
result = trainer.fit()
# __pytorch_restore_end__
# __checkpoint_from_single_worker_start__
import tempfile
from ray import train
def train_fn(config):
...
metrics = {...}
with tempfile.TemporaryDirectory() as temp_checkpoint_dir:
checkpoint = None
# Only the global rank 0 worker saves and reports the checkpoint
if train.get_context().get_world_rank() == 0:
... # Save checkpoint to temp_checkpoint_dir
checkpoint = Checkpoint.from_directory(temp_checkpoint_dir)
train.report(metrics, checkpoint=checkpoint)
# __checkpoint_from_single_worker_end__
# __lightning_save_example_start__
import lightning.pytorch as pl
from ray import train
from ray.train.lightning import RayTrainReportCallback
from ray.train.torch import TorchTrainer
class MyLightningModule(pl.LightningModule):
# ...
def on_validation_epoch_end(self):
...
mean_acc = calculate_accuracy()
self.log("mean_accuracy", mean_acc, sync_dist=True)
def train_func():
...
model = MyLightningModule(...)
datamodule = MyLightningDataModule(...)
trainer = pl.Trainer(
# ...
callbacks=[RayTrainReportCallback()]
)
trainer.fit(model, datamodule=datamodule)
ray_trainer = TorchTrainer(
train_func,
scaling_config=train.ScalingConfig(num_workers=2),
run_config=train.RunConfig(
checkpoint_config=train.CheckpointConfig(
num_to_keep=2,
checkpoint_score_attribute="mean_accuracy",
checkpoint_score_order="max",
),
),
)
# __lightning_save_example_end__
# __lightning_custom_save_example_start__
import os
from tempfile import TemporaryDirectory
from lightning.pytorch.callbacks import Callback
import ray
import ray.train
from ray.train import Checkpoint
class CustomRayTrainReportCallback(Callback):
def on_train_epoch_end(self, trainer, pl_module):
should_checkpoint = trainer.current_epoch % 3 == 0
with TemporaryDirectory() as tmpdir:
# Fetch metrics from `self.log(..)` in the LightningModule
metrics = trainer.callback_metrics
metrics = {k: v.item() for k, v in metrics.items()}
# Add customized metrics
metrics["epoch"] = trainer.current_epoch
metrics["custom_metric"] = 123
checkpoint = None
global_rank = ray.train.get_context().get_world_rank() == 0
if global_rank == 0 and should_checkpoint:
# Save model checkpoint file to tmpdir
ckpt_path = os.path.join(tmpdir, "ckpt.pt")
trainer.save_checkpoint(ckpt_path, weights_only=False)
checkpoint = Checkpoint.from_directory(tmpdir)
# Report to train session
ray.train.report(metrics=metrics, checkpoint=checkpoint)
# __lightning_custom_save_example_end__
# __lightning_restore_example_start__
import os
from ray import train
from ray.train import Checkpoint
from ray.train.torch import TorchTrainer
from ray.train.lightning import RayTrainReportCallback
def train_func():
model = MyLightningModule(...)
datamodule = MyLightningDataModule(...)
trainer = pl.Trainer(..., callbacks=[RayTrainReportCallback()])
checkpoint = train.get_checkpoint()
if checkpoint:
with checkpoint.as_directory() as ckpt_dir:
ckpt_path = os.path.join(ckpt_dir, RayTrainReportCallback.CHECKPOINT_NAME)
trainer.fit(model, datamodule=datamodule, ckpt_path=ckpt_path)
else:
trainer.fit(model, datamodule=datamodule)
ray_trainer = TorchTrainer(
train_func,
scaling_config=train.ScalingConfig(num_workers=2),
run_config=train.RunConfig(
checkpoint_config=train.CheckpointConfig(num_to_keep=2),
),
)
# __lightning_restore_example_end__
# __transformers_save_example_start__
from transformers import TrainingArguments
from ray import train
from ray.train.huggingface.transformers import RayTrainReportCallback, prepare_trainer
from ray.train.torch import TorchTrainer
def train_func(config):
...
# Configure logging, saving, evaluation strategies as usual.
args = TrainingArguments(
...,
eval_strategy="epoch",
save_strategy="epoch",
logging_strategy="step",
)
trainer = transformers.Trainer(args, ...)
# Add a report callback to transformers Trainer
# =============================================
trainer.add_callback(RayTrainReportCallback())
trainer = prepare_trainer(trainer)
trainer.train()
ray_trainer = TorchTrainer(
train_func,
run_config=train.RunConfig(
checkpoint_config=train.CheckpointConfig(
num_to_keep=3,
checkpoint_score_attribute="eval_loss", # The monitoring metric
checkpoint_score_order="min",
)
),
)
# __transformers_save_example_end__
# __transformers_custom_save_example_start__
from ray import train
from transformers.trainer_callback import TrainerCallback
class MyTrainReportCallback(TrainerCallback):
def __init__(self):
super().__init__()
self.metrics = {}
def on_log(self, args, state, control, model=None, logs=None, **kwargs):
"""Log is called on evaluation step and logging step."""
self.metrics.update(logs)
def on_save(self, args, state, control, **kwargs):
"""Event called after a checkpoint save."""
checkpoint = None
if train.get_context().get_world_rank() == 0:
# Build a Ray Train Checkpoint from the latest checkpoint
checkpoint_path = transformers.trainer.get_last_checkpoint(args.output_dir)
checkpoint = Checkpoint.from_directory(checkpoint_path)
# Report to Ray Train with up-to-date metrics
ray.train.report(metrics=self.metrics, checkpoint=checkpoint)
# Clear the metrics buffer
self.metrics = {}
# __transformers_custom_save_example_end__
# __distributed_checkpointing_start__
from ray import train
from ray.train import Checkpoint
from ray.train.torch import TorchTrainer
def train_func(config):
...
with tempfile.TemporaryDirectory() as temp_checkpoint_dir:
rank = train.get_context().get_world_rank()
torch.save(
...,
os.path.join(temp_checkpoint_dir, f"model-rank={rank}.pt"),
)
checkpoint = Checkpoint.from_directory(temp_checkpoint_dir)
train.report(metrics, checkpoint=checkpoint)
trainer = TorchTrainer(
train_func,
scaling_config=train.ScalingConfig(num_workers=2),
run_config=train.RunConfig(storage_path="s3://bucket/"),
)
# The checkpoint in cloud storage will contain: model-rank=0.pt, model-rank=1.pt
# __distributed_checkpointing_end__
# __inspect_checkpoint_example_start__
from pathlib import Path
from ray.train import Checkpoint
# For demonstration, create a locally available directory with a `model.pt` file.
example_checkpoint_dir = Path("/tmp/test-checkpoint")
example_checkpoint_dir.mkdir()
example_checkpoint_dir.joinpath("model.pt").touch()
# Create the checkpoint, which is a reference to the directory.
checkpoint = Checkpoint.from_directory(example_checkpoint_dir)
# Inspect the checkpoint's contents with either `as_directory` or `to_directory`:
with checkpoint.as_directory() as checkpoint_dir:
assert Path(checkpoint_dir).joinpath("model.pt").exists()
checkpoint_dir = checkpoint.to_directory()
assert Path(checkpoint_dir).joinpath("model.pt").exists()
# __inspect_checkpoint_example_end__
# __inspect_transformers_checkpoint_example_start__
# After training finished
checkpoint = result.checkpoint
with checkpoint.as_directory() as checkpoint_dir:
hf_checkpoint_path = f"{checkpoint_dir}/checkpoint/"
# __inspect_transformers_checkpoint_example_end__
# __inspect_lightning_checkpoint_example_start__
# After training finished
checkpoint = result.checkpoint
with checkpoint.as_directory() as checkpoint_dir:
lightning_checkpoint_path = f"{checkpoint_dir}/checkpoint.ckpt"
# __inspect_lightning_checkpoint_example_end__
# __checkpoint_upload_mode_sync_start__
def train_fn(config):
...
metrics = {...}
with tempfile.TemporaryDirectory() as tmpdir:
... # Save checkpoint to tmpdir
checkpoint = Checkpoint.from_directory(tmpdir)
train.report(
metrics,
checkpoint=checkpoint,
checkpoint_upload_mode=train.CheckpointUploadMode.SYNC,
)
# __checkpoint_upload_mode_sync_end__
# __checkpoint_upload_mode_async_start__
def train_fn(config):
...
metrics = {...}
tmpdir = tempfile.mkdtemp()
... # Save checkpoint to tmpdir
checkpoint = Checkpoint.from_directory(tmpdir)
train.report(
metrics,
checkpoint=checkpoint,
checkpoint_upload_mode=train.CheckpointUploadMode.ASYNC,
)
# __checkpoint_upload_mode_async_end__
# __checkpoint_upload_mode_no_upload_start__
from s3torchconnector.dcp import S3StorageWriter
from torch.distributed.checkpoint.state_dict_saver import save
from torch.distributed.checkpoint.state_dict import get_state_dict
def train_fn(config):
...
for epoch in range(config["num_epochs"]):
# Directly upload checkpoint to s3 with Torch
model, optimizer = ...
storage_context = ray.train.get_context().get_storage()
checkpoint_path = (
f"s3://{storage_context.build_checkpoint_path_from_name(str(epoch))}"
)
storage_writer = S3StorageWriter(region="us-west-2", path=checkpoint_path)
model_dict, opt_dict = get_state_dict(model=model, optimizers=optimizer)
save(
{"model": model_dict, "opt": opt_dict},
storage_writer=storage_writer,
)
# Report that checkpoint to Ray Train
metrics = {...}
checkpoint = Checkpoint(checkpoint_path)
train.report(
metrics,
checkpoint=checkpoint,
checkpoint_upload_mode=train.CheckpointUploadMode.NO_UPLOAD,
)
# __checkpoint_upload_mode_no_upload_end__
# __checkpoint_upload_fn_start__
from torch.distributed.checkpoint.state_dict_saver import async_save
from s3torchconnector.dcp import S3StorageWriter
from torch.distributed.checkpoint.state_dict import get_state_dict
from ray import train
from ray.train import Checkpoint
def train_fn(config):
...
for epoch in config["num_epochs"]:
# Start async checkpoint upload to s3 with Torch
model, optimizer = ...
storage_context = train.get_context().get_storage()
checkpoint_path = (
f"s3://{storage_context.build_checkpoint_path_from_name(str(epoch))}"
)
storage_writer = S3StorageWriter(region="us-west-2", path=checkpoint_path)
model_dict, opt_dict = get_state_dict(model=model, optimizers=optimizer)
ckpt_ref = async_save(
{"model": model_dict, "opt": opt_dict},
storage_writer=storage_writer,
)
def wait_async_save(checkpoint, checkpoint_dir_name):
# This function waits for checkpoint to be finalized before returning it as is
ckpt_ref.result()
return checkpoint
# Ray Train kicks off a thread that waits for the async checkpoint upload to complete
# before reporting the checkpoint
metrics = {...}
checkpoint = Checkpoint(checkpoint_path)
train.report(
metrics=metrics,
checkpoint=checkpoint,
checkpoint_upload_mode=train.CheckpointUploadMode.ASYNC,
checkpoint_upload_fn=wait_async_save,
# As uploading into the experiment directory then don't delete the checkpoint after upload is complete
delete_local_checkpoint_after_upload=False,
)
trainer = TorchTrainer(
train_fn,
train_loop_config={"num_epochs": 3},
scaling_config=train.ScalingConfig(num_workers=2, use_gpu=True),
# we need a cpu backend for async_save and a gpu backend for training
torch_config=train.torch.TorchConfig(backend="cpu:gloo,cuda:nccl"),
run_config=train.RunConfig(storage_path="s3://bucket/")
)
# __checkpoint_upload_fn_end__
# __get_all_reported_checkpoints_example_start__
import ray.train
from ray.train import CheckpointConsistencyMode
def train_fn():
for epoch in range(2):
metrics = {"train/loss": 0.1}
checkpoint = ...
ray.train.report(
metrics,
checkpoint=checkpoint,
validation=...,
)
# Get committed checkpoints which may still have ongoing validations.
committed_checkpoints = ray.train.get_all_reported_checkpoints(
consistency_mode=CheckpointConsistencyMode.COMMITTED)
# Wait for all pending validations to finish to access reported checkpoints
# with validation metrics attached.
validated_checkpoints = ray.train.get_all_reported_checkpoints()
...
# __get_all_reported_checkpoints_example_end__
+118
View File
@@ -0,0 +1,118 @@
from dataclasses import dataclass
from typing import Dict, List, Tuple, Union
import torch
from ray import cloudpickle as pickle
import pyarrow as pa
# (dtype, shape, offset)
FEATURE_TYPE = Tuple[torch.dtype, torch.Size, int]
TORCH_BYTE_ELEMENT_TYPE = torch.uint8
def _create_binary_array_from_buffer(buffer: bytes) -> pa.BinaryArray:
"""Zero-copy create a binary array from a buffer."""
data_buffer = pa.py_buffer(buffer)
return pa.Array.from_buffers(
pa.binary(),
1,
[
None,
pa.array([0, data_buffer.size], type=pa.int32()).buffers()[1],
data_buffer,
],
)
@dataclass
class _Metadata:
features: Dict[str, List[FEATURE_TYPE]]
total_buffer_size: int
@dataclass
class _TensorBatch:
"""Internal class for serializing/deserializing tensor batches."""
buffer: torch.Tensor
metadata: _Metadata
@classmethod
def from_batch(cls, batch: Dict[str, Union[List[torch.Tensor], torch.Tensor]]) -> '_TensorBatch':
"""Serialize a batch of tensors into a single buffer."""
features: Dict[str, List[FEATURE_TYPE]] = {}
flattened_binary_tensors = []
total_buffer_size = 0
for name, tensors in batch.items():
features[name] = []
if not isinstance(tensors, list):
tensors = [tensors]
for tensor in tensors:
flattened_tensor = tensor.flatten().contiguous().view(TORCH_BYTE_ELEMENT_TYPE)
flattened_binary_tensors.append(flattened_tensor)
features[name].append((tensor.dtype, tensor.shape, total_buffer_size))
total_buffer_size += flattened_tensor.shape[0]
buffer = torch.empty(total_buffer_size, dtype=TORCH_BYTE_ELEMENT_TYPE)
cur_offset = 0
for flattened_tensor in flattened_binary_tensors:
buffer[cur_offset:cur_offset + flattened_tensor.shape[0]] = flattened_tensor
cur_offset += flattened_tensor.shape[0]
return _TensorBatch(
buffer=buffer,
metadata=_Metadata(
features=features,
total_buffer_size=total_buffer_size,
),
)
def to_table(self) -> pa.Table:
"""Convert to a single-row PyArrow table."""
buffer_array = _create_binary_array_from_buffer(self.buffer.numpy().data)
metadata_array = _create_binary_array_from_buffer(pickle.dumps(self.metadata))
return pa.Table.from_arrays(
arrays=[buffer_array, metadata_array],
names=["_buffer", "_metadata"],
)
@classmethod
def from_table(cls, table: pa.Table) -> '_TensorBatch':
"""Deserialize from a single-row PyArrow table."""
return _TensorBatch(
buffer=torch.frombuffer(
table["_buffer"].chunks[0].buffers()[2],
dtype=TORCH_BYTE_ELEMENT_TYPE
),
metadata=pickle.loads(table["_metadata"].chunks[0].buffers()[2]),
)
def to_batch(self, pin_memory: bool = False) -> Dict[str, List[torch.Tensor]]:
"""Deserialize back to a batch of tensors."""
batch = {}
storage_buffer = self.buffer.untyped_storage()
offsets = []
for name, features in self.metadata.features.items():
for _, _, offset in features:
offsets.append(offset)
offsets.append(self.metadata.total_buffer_size)
offset_id = 0
for name, features in self.metadata.features.items():
batch[name] = []
for dtype, shape, _ in features:
# Create a zero-copy view of the byte slice.
byte_slice = self.buffer[offsets[offset_id]:offsets[offset_id + 1]]
tensor = torch.frombuffer(
byte_slice.numpy().data, dtype=dtype
).view(shape)
if pin_memory:
tensor = tensor.pin_memory()
batch[name].append(tensor)
offset_id += 1
return batch
# Helper functions for use in your code
def serialize_tensors_to_table(batch: Dict[str, Union[List[torch.Tensor], torch.Tensor]]) -> pa.Table:
"""Serialize a batch of tensors to a PyArrow table."""
return _TensorBatch.from_batch(batch).to_table()
def deserialize_table_to_tensors(table: pa.Table, pin_memory: bool = False) -> Dict[str, List[torch.Tensor]]:
"""Deserialize a PyArrow table back to tensors."""
return _TensorBatch.from_table(table).to_batch(pin_memory=pin_memory)
@@ -0,0 +1,147 @@
# flake8: noqa
# isort: skip_file
# __basic__
import ray
from ray import train
from ray.train import ScalingConfig
from ray.train.torch import TorchTrainer
import numpy as np
from typing import Dict
# Load the data.
train_ds = ray.data.read_parquet("s3://anonymous@ray-example-data/iris.parquet")
## Uncomment to randomize the block order each epoch.
# train_ds = train_ds.randomize_block_order()
# Define a preprocessing function.
def normalize_length(batch: Dict[str, np.ndarray]) -> Dict[str, np.ndarray]:
new_col = batch["sepal.length"] / np.max(batch["sepal.length"])
batch["normalized.sepal.length"] = new_col
del batch["sepal.length"]
return batch
# Preprocess your data any way you want. This will be re-run each epoch.
# You can use Ray Data preprocessors here as well,
# e.g., preprocessor.fit_transform(train_ds)
train_ds = train_ds.map_batches(normalize_length)
def train_loop_per_worker():
# Get an iterator to the dataset we passed in below.
it = train.get_dataset_shard("train")
# Train for 10 epochs over the data. We'll use a shuffle buffer size
# of 10k elements, and prefetch up to 10 batches of size 128 each.
for _ in range(10):
for batch in it.iter_batches(
local_shuffle_buffer_size=10000, batch_size=128, prefetch_batches=10
):
print("Do some training on batch", batch)
my_trainer = TorchTrainer(
train_loop_per_worker,
scaling_config=ScalingConfig(num_workers=2),
datasets={"train": train_ds},
)
my_trainer.fit()
# __basic_end__
# __custom_split__
dataset_a = ray.data.read_text(
"s3://anonymous@ray-example-data/sms_spam_collection_subset.txt"
)
dataset_b = ray.data.read_csv("s3://anonymous@ray-example-data/dow_jones.csv")
my_trainer = TorchTrainer(
train_loop_per_worker,
scaling_config=ScalingConfig(num_workers=2),
datasets={"a": dataset_a, "b": dataset_b},
dataset_config=ray.train.DataConfig(
datasets_to_split=["a"],
),
)
# __custom_split_end__
def augment_data(batch):
return batch
# __materialized__
# Load the data.
train_ds = ray.data.read_parquet("s3://anonymous@ray-example-data/iris.parquet")
# Preprocess the data. Transformations that are made to the materialize call below
# will only be run once.
train_ds = train_ds.map_batches(normalize_length)
# Materialize the dataset in object store memory.
train_ds = train_ds.materialize()
# Add per-epoch preprocessing. Transformations that you want to run per-epoch, such
# as data augmentation, should go after the materialize call.
train_ds = train_ds.map_batches(augment_data)
# __materialized_end__
# __options__
from ray.train import DataConfig
options = DataConfig.default_ingest_options()
options.resource_limits = options.resource_limits.copy(object_store_memory=10e9)
my_trainer = TorchTrainer(
train_loop_per_worker,
scaling_config=ScalingConfig(num_workers=2),
dataset_config=ray.train.DataConfig(
execution_options=options,
),
)
# __options_end__
# __custom__
# Note that this example class is doing the same thing as the basic DataConfig
# impl included with Ray Train.
from typing import Optional, Dict, List
from ray.data import Dataset, DataIterator, NodeIdStr
from ray.actor import ActorHandle
class MyCustomDataConfig(DataConfig):
def configure(
self,
datasets: Dict[str, Dataset],
world_size: int,
worker_handles: Optional[List[ActorHandle]],
worker_node_ids: Optional[List[NodeIdStr]],
**kwargs,
) -> List[Dict[str, DataIterator]]:
assert len(datasets) == 1, "This example only handles the simple case"
# Configure Ray Data for ingest.
ctx = ray.data.DataContext.get_current()
ctx.execution_options = DataConfig.default_ingest_options()
# Split the stream into shards.
iterator_shards = datasets["train"].streaming_split(
world_size, equal=True, locality_hints=worker_node_ids
)
# Return the assigned iterators for each worker.
return [{"train": it} for it in iterator_shards]
my_trainer = TorchTrainer(
train_loop_per_worker,
scaling_config=ScalingConfig(num_workers=2),
datasets={"train": train_ds},
dataset_config=MyCustomDataConfig(),
)
my_trainer.fit()
# __custom_end__
+110
View File
@@ -0,0 +1,110 @@
# flake8: noqa
# TODO: [V2] Deprecated doc code to delete.
import os
os.environ["RAY_TRAIN_V2_ENABLED"] = "0"
MOCK = True
# __ft_initial_run_start__
import os
import tempfile
from typing import Dict, Optional
import torch
import ray
from ray import train
from ray.train import Checkpoint
from ray.train.torch import TorchTrainer
def get_datasets() -> Dict[str, ray.data.Dataset]:
return {"train": ray.data.from_items([{"x": i, "y": 2 * i} for i in range(10)])}
def train_loop_per_worker(config: dict):
from torchvision.models import resnet18
model = resnet18()
# Checkpoint loading
checkpoint: Optional[Checkpoint] = train.get_checkpoint()
if checkpoint:
with checkpoint.as_directory() as checkpoint_dir:
model_state_dict = torch.load(os.path.join(checkpoint_dir, "model.pt"))
model.load_state_dict(model_state_dict)
model = train.torch.prepare_model(model)
train_ds = train.get_dataset_shard("train")
for epoch in range(5):
# Do some training...
# Checkpoint saving
with tempfile.TemporaryDirectory() as tmpdir:
torch.save(model.module.state_dict(), os.path.join(tmpdir, "model.pt"))
train.report({"epoch": epoch}, checkpoint=Checkpoint.from_directory(tmpdir))
trainer = TorchTrainer(
train_loop_per_worker=train_loop_per_worker,
datasets=get_datasets(),
scaling_config=train.ScalingConfig(num_workers=2),
run_config=train.RunConfig(
name="dl_trainer_restore", storage_path=os.path.expanduser("~/ray_results")
),
)
result = trainer.fit()
# __ft_initial_run_end__
# __ft_restored_run_start__
from ray.train.torch import TorchTrainer
restored_trainer = TorchTrainer.restore(
path=os.path.expanduser("~/ray_results/dl_trainer_restore"),
datasets=get_datasets(),
)
# __ft_restored_run_end__
if not MOCK:
# __ft_restore_from_cloud_initial_start__
original_trainer = TorchTrainer(
# ...
run_config=train.RunConfig(
# Configure cloud storage
storage_path="s3://results-bucket",
name="dl_trainer_restore",
),
)
result = trainer.fit()
# __ft_restore_from_cloud_initial_end__
# __ft_restore_from_cloud_restored_start__
restored_trainer = TorchTrainer.restore(
"s3://results-bucket/dl_trainer_restore",
datasets=get_datasets(),
)
# __ft_restore_from_cloud_restored_end__
# __ft_autoresume_start__
experiment_path = os.path.expanduser("~/ray_results/dl_restore_autoresume")
if TorchTrainer.can_restore(experiment_path):
trainer = TorchTrainer.restore(experiment_path, datasets=get_datasets())
result = trainer.fit()
else:
trainer = TorchTrainer(
train_loop_per_worker=train_loop_per_worker,
datasets=get_datasets(),
scaling_config=train.ScalingConfig(num_workers=2),
run_config=train.RunConfig(
storage_path=os.path.expanduser("~/ray_results"),
name="dl_restore_autoresume",
),
)
result = trainer.fit()
# __ft_autoresume_end__
@@ -0,0 +1,107 @@
import os
os.environ["RAY_TRAIN_V2_ENABLED"] = "1"
# __failure_config_start__
import ray.train
# Tries to recover a run up to this many times.
failure_config = ray.train.FailureConfig(max_failures=2)
# No limit on the number of retries.
failure_config = ray.train.FailureConfig(max_failures=-1)
# __failure_config_end__
# __worker_fault_tolerance_start__
import tempfile
import uuid
import ray.train
import ray.train.torch
def train_fn_per_worker(train_loop_config: dict):
# [1] Train worker restoration logic.
checkpoint = ray.train.get_checkpoint()
if checkpoint:
with checkpoint.as_directory() as temp_checkpoint_dir:
# model.load_state_dict(torch.load(...))
...
# [2] Checkpoint saving and reporting logic.
with tempfile.TemporaryDirectory() as temp_checkpoint_dir:
# torch.save(...)
ray.train.report(
{"loss": 0.1},
checkpoint=ray.train.Checkpoint.from_directory(temp_checkpoint_dir),
)
trainer = ray.train.torch.TorchTrainer(
train_fn_per_worker,
scaling_config=ray.train.ScalingConfig(num_workers=4),
run_config=ray.train.RunConfig(
# (If multi-node, configure S3 / NFS as the storage path.)
# storage_path="s3://...",
name=f"train_run-{uuid.uuid4().hex}",
# [3] Enable worker-level fault tolerance to gracefully handle
# Train worker failures.
failure_config=ray.train.FailureConfig(max_failures=3),
),
)
trainer.fit()
# __worker_fault_tolerance_end__
# Avoid running the code below so that the argument parser is not used.
__name__ = "__dummy__"
# __job_driver_fault_tolerance_start__
# entrypoint.py
import argparse
import tempfile
import uuid
import ray.train
import ray.train.torch
def train_fn_per_worker(train_loop_config: dict):
# [1] Train worker restoration logic.
checkpoint = ray.train.get_checkpoint()
if checkpoint:
with checkpoint.as_directory() as temp_checkpoint_dir:
# model.load_state_dict(torch.load(...))
...
# [2] Checkpoint saving and reporting logic.
with tempfile.TemporaryDirectory() as temp_checkpoint_dir:
# torch.save(...)
ray.train.report(
{"loss": 0.1},
checkpoint=ray.train.Checkpoint.from_directory(temp_checkpoint_dir),
)
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--storage_path", type=str, required=True)
parser.add_argument("--run_name", type=str, required=True)
args = parser.parse_args()
trainer = ray.train.torch.TorchTrainer(
train_fn_per_worker,
scaling_config=ray.train.ScalingConfig(num_workers=4),
run_config=ray.train.RunConfig(
# [3] Enable worker-level fault tolerance to gracefully handle
# Train worker failures.
failure_config=ray.train.FailureConfig(max_failures=3),
# [4] (Recommendation) The (storage_path, name) pair should be
# determined by the job submitter and passed in as arguments
# to the entrypoint script.
storage_path=args.storage_path,
name=args.run_name,
),
)
trainer.fit()
# __job_driver_fault_tolerance_end__
+80
View File
@@ -0,0 +1,80 @@
# TODO: [V2] Deprecated doc code to delete.
import os
os.environ["RAY_TRAIN_V2_ENABLED"] = "0"
import tempfile
import horovod.torch as hvd
import ray
from ray import train
from ray.train import Checkpoint, ScalingConfig
import ray.train.torch # Need this to use `train.torch.get_device()`
from ray.train.horovod import HorovodTrainer
import torch
import torch.nn as nn
# If using GPUs, set this to True.
use_gpu = False
input_size = 1
layer_size = 15
output_size = 1
num_epochs = 3
class NeuralNetwork(nn.Module):
def __init__(self):
super(NeuralNetwork, self).__init__()
self.layer1 = nn.Linear(input_size, layer_size)
self.relu = nn.ReLU()
self.layer2 = nn.Linear(layer_size, output_size)
def forward(self, input):
return self.layer2(self.relu(self.layer1(input)))
def train_loop_per_worker():
hvd.init()
dataset_shard = train.get_dataset_shard("train")
model = NeuralNetwork()
device = train.torch.get_device()
model.to(device)
loss_fn = nn.MSELoss()
lr_scaler = 1
optimizer = torch.optim.SGD(model.parameters(), lr=0.1 * lr_scaler)
# Horovod: wrap optimizer with DistributedOptimizer.
optimizer = hvd.DistributedOptimizer(
optimizer,
named_parameters=model.named_parameters(),
op=hvd.Average,
)
for epoch in range(num_epochs):
model.train()
for batch in dataset_shard.iter_torch_batches(
batch_size=32, dtypes=torch.float
):
inputs, labels = torch.unsqueeze(batch["x"], 1), batch["y"]
outputs = model(inputs)
loss = loss_fn(outputs, labels)
optimizer.zero_grad()
loss.backward()
optimizer.step()
print(f"epoch: {epoch}, loss: {loss.item()}")
with tempfile.TemporaryDirectory() as tmpdir:
torch.save(model.state_dict(), os.path.join(tmpdir, "model.pt"))
train.report(
{"loss": loss.item()}, checkpoint=Checkpoint.from_directory(tmpdir)
)
train_dataset = ray.data.from_items([{"x": x, "y": x + 1} for x in range(32)])
scaling_config = ScalingConfig(num_workers=3, use_gpu=use_gpu)
trainer = HorovodTrainer(
train_loop_per_worker=train_loop_per_worker,
scaling_config=scaling_config,
datasets={"train": train_dataset},
)
result = trainer.fit()
+136
View File
@@ -0,0 +1,136 @@
# flake8: noqa
# isort: skip_file
from pathlib import Path
import tempfile
import ray.train
from ray.train.v2.api.data_parallel_trainer import DataParallelTrainer
def train_fn(config):
for i in range(3):
with tempfile.TemporaryDirectory() as temp_checkpoint_dir:
Path(temp_checkpoint_dir).joinpath("model.pt").touch()
ray.train.report(
{"loss": i},
checkpoint=ray.train.Checkpoint.from_directory(temp_checkpoint_dir),
)
return {"total loss": 3}
trainer = DataParallelTrainer(
train_fn, scaling_config=ray.train.ScalingConfig(num_workers=2)
)
# __run_config_start__
import os
from ray.train import RunConfig
run_config = RunConfig(
# Name of the training run (directory name).
name="my_train_run",
# The experiment results will be saved to: storage_path/name
storage_path=os.path.expanduser("~/ray_results"),
# storage_path="s3://my_bucket/tune_results",
)
# __run_config_end__
# __checkpoint_config_start__
from ray.train import RunConfig, CheckpointConfig
# Example 1: Only keep the 2 *most recent* checkpoints and delete the others.
run_config = RunConfig(checkpoint_config=CheckpointConfig(num_to_keep=2))
# Example 2: Only keep the 2 *best* checkpoints and delete the others.
run_config = RunConfig(
checkpoint_config=CheckpointConfig(
num_to_keep=2,
# *Best* checkpoints are determined by these params:
checkpoint_score_attribute="mean_accuracy",
checkpoint_score_order="max",
),
# This will store checkpoints on S3.
storage_path="s3://remote-bucket/location",
)
# __checkpoint_config_end__
# __result_metrics_start__
result = trainer.fit()
print("Observed metrics:", result.metrics)
# __result_metrics_end__
# __result_dataframe_start__
df = result.metrics_dataframe
print("Minimum loss", min(df["loss"]))
# __result_dataframe_end__
# __result_return_value_start__
print("Returned data", result.return_value)
# __result_return_value_end__
# __result_checkpoint_start__
print("Last checkpoint:", result.checkpoint)
with result.checkpoint.as_directory() as tmpdir:
# Load model from directory
...
# __result_checkpoint_end__
# __result_best_checkpoint_start__
# Print available checkpoints
for checkpoint, metrics in result.best_checkpoints:
print("Loss", metrics["loss"], "checkpoint", checkpoint)
# Get checkpoint with minimal loss
best_checkpoint = min(
result.best_checkpoints, key=lambda checkpoint: checkpoint[1]["loss"]
)[0]
with best_checkpoint.as_directory() as tmpdir:
# Load model from directory
...
# __result_best_checkpoint_end__
import pyarrow
# __result_path_start__
result_path: str = result.path
result_filesystem: pyarrow.fs.FileSystem = result.filesystem
print(f"Results location (fs, path) = ({result_filesystem}, {result_path})")
# __result_path_end__
# __result_restore_start__
from ray.train import Result
restored_result = Result.from_path(result_path)
print("Restored loss", restored_result.metrics["loss"])
# __result_restore_end__
def error_train_fn(config):
raise RuntimeError("Simulated training error")
trainer = DataParallelTrainer(
error_train_fn, scaling_config=ray.train.ScalingConfig(num_workers=1)
)
# __result_error_start__
try:
result = trainer.fit()
except ray.train.TrainingFailedError as e:
if isinstance(e, ray.train.WorkerGroupError):
print(e.worker_failures)
# __result_error_end__
@@ -0,0 +1,128 @@
# flake8: noqa
# isort: skip_file
# __lightgbm_start__
import pandas as pd
import lightgbm as lgb
# 1. Load your data as a `lightgbm.Dataset`.
train_df = pd.read_csv("s3://ray-example-data/iris/train/1.csv")
eval_df = pd.read_csv("s3://ray-example-data/iris/val/1.csv")
train_X = train_df.drop("target", axis=1)
train_y = train_df["target"]
eval_X = eval_df.drop("target", axis=1)
eval_y = eval_df["target"]
train_set = lgb.Dataset(train_X, label=train_y)
eval_set = lgb.Dataset(eval_X, label=eval_y)
# 2. Define your LightGBM model training parameters.
params = {
"objective": "multiclass",
"num_class": 3,
"metric": ["multi_logloss", "multi_error"],
"verbosity": -1,
"boosting_type": "gbdt",
"num_leaves": 31,
"learning_rate": 0.05,
"feature_fraction": 0.9,
"bagging_fraction": 0.8,
"bagging_freq": 5,
}
# 3. Do non-distributed training.
model = lgb.train(
params,
train_set,
valid_sets=[eval_set],
valid_names=["eval"],
num_boost_round=100,
)
# __lightgbm_end__
# __lightgbm_ray_start__
import lightgbm as lgb
import ray.train
from ray.train.lightgbm import LightGBMTrainer, RayTrainReportCallback
# 1. Load your data as a Ray Data Dataset.
train_dataset = ray.data.read_csv("s3://anonymous@ray-example-data/iris/train")
eval_dataset = ray.data.read_csv("s3://anonymous@ray-example-data/iris/val")
def train_func():
# 2. Load your data shard as a `lightgbm.Dataset`.
# Get dataset shards for this worker
train_shard = ray.train.get_dataset_shard("train")
eval_shard = ray.train.get_dataset_shard("eval")
# Convert shards to PyArrow tables. LightGBM (>=4.2.0) supports PyArrow
# natively, which avoids a round-trip through pandas.
import pyarrow as pa
train_table = pa.concat_tables(
train_shard.iter_batches(batch_format="pyarrow", batch_size=None)
)
eval_table = pa.concat_tables(
eval_shard.iter_batches(batch_format="pyarrow", batch_size=None)
)
train_X = train_table.drop(["target"])
train_y = train_table.column("target")
eval_X = eval_table.drop(["target"])
eval_y = eval_table.column("target")
train_set = lgb.Dataset(train_X, label=train_y)
eval_set = lgb.Dataset(eval_X, label=eval_y)
# 3. Define your LightGBM model training parameters.
params = {
"objective": "multiclass",
"num_class": 3,
"metric": ["multi_logloss", "multi_error"],
"verbosity": -1,
"boosting_type": "gbdt",
"num_leaves": 31,
"learning_rate": 0.05,
"feature_fraction": 0.9,
"bagging_fraction": 0.8,
"bagging_freq": 5,
# Adding the lines below are the only changes needed
# for your `lgb.train` call!
"tree_learner": "data_parallel",
"pre_partition": True,
**ray.train.lightgbm.get_network_params(),
}
# 4. Do distributed data-parallel training.
# Ray Train sets up the necessary coordinator processes and
# environment variables for your workers to communicate with each other.
model = lgb.train(
params,
train_set,
valid_sets=[eval_set],
valid_names=["eval"],
num_boost_round=100,
# Optional: Use the `RayTrainReportCallback` to save and report checkpoints.
callbacks=[RayTrainReportCallback()],
)
# 5. Configure scaling and resource requirements.
scaling_config = ray.train.ScalingConfig(num_workers=2, resources_per_worker={"CPU": 2})
# 6. Launch distributed training job.
trainer = LightGBMTrainer(
train_func,
scaling_config=scaling_config,
datasets={"train": train_dataset, "eval": eval_dataset},
)
result = trainer.fit()
# 7. Load the trained model.
model = RayTrainReportCallback.get_model(result.checkpoint)
# __lightgbm_ray_end__
+138
View File
@@ -0,0 +1,138 @@
# flake8: noqa
# isort: skip_file
import os
os.environ["RAY_TRAIN_V2_ENABLED"] = "1"
# __torchmetrics_start__
# First, pip install torchmetrics
# This code is tested with torchmetrics==0.7.3 and torch==1.12.1
import os
import tempfile
import ray.train.torch
from ray import train
from ray.train import ScalingConfig
from ray.train.torch import TorchTrainer
import torch
import torch.nn as nn
import torchmetrics
from torch.optim import Adam
import numpy as np
def train_func(config):
n = 100
# create a toy dataset
X = torch.Tensor(np.random.normal(0, 1, size=(n, 4)))
X_valid = torch.Tensor(np.random.normal(0, 1, size=(n, 4)))
Y = torch.Tensor(np.random.uniform(0, 1, size=(n, 1)))
Y_valid = torch.Tensor(np.random.uniform(0, 1, size=(n, 1)))
# toy neural network : 1-layer
# wrap the model in DDP
model = ray.train.torch.prepare_model(nn.Linear(4, 1))
criterion = nn.MSELoss()
mape = torchmetrics.MeanAbsolutePercentageError()
# for averaging loss
mean_valid_loss = torchmetrics.MeanMetric()
optimizer = Adam(model.parameters(), lr=3e-4)
for epoch in range(config["num_epochs"]):
model.train()
y = model.forward(X)
# compute loss
loss = criterion(y, Y)
# back-propagate loss
optimizer.zero_grad()
loss.backward()
optimizer.step()
# evaluate
model.eval()
with torch.no_grad():
pred = model(X_valid)
valid_loss = criterion(pred, Y_valid)
# save loss in aggregator
mean_valid_loss(valid_loss)
mape(pred, Y_valid)
# collect all metrics
# use .item() to obtain a value that can be reported
valid_loss = valid_loss.item()
mape_collected = mape.compute().item()
mean_valid_loss_collected = mean_valid_loss.compute().item()
with tempfile.TemporaryDirectory() as temp_checkpoint_dir:
torch.save(
model.state_dict(), os.path.join(temp_checkpoint_dir, "model.pt")
)
train.report(
{
"mape_collected": mape_collected,
"valid_loss": valid_loss,
"mean_valid_loss_collected": mean_valid_loss_collected,
},
checkpoint=train.Checkpoint.from_directory(temp_checkpoint_dir),
)
# reset for next epoch
mape.reset()
mean_valid_loss.reset()
trainer = TorchTrainer(
train_func,
train_loop_config={"num_epochs": 5},
scaling_config=ScalingConfig(num_workers=2),
)
result = trainer.fit()
print(result.metrics["valid_loss"], result.metrics["mean_valid_loss_collected"])
# 0.5109779238700867 0.5512474775314331
# __torchmetrics_end__
# __report_callback_start__
import os
assert os.environ["RAY_TRAIN_V2_ENABLED"] == "1"
from typing import Any, Dict, List, Optional
import ray.train
import ray.train.torch
def train_fn_per_worker(config):
# Free-floating metrics can be accessed from the callback below.
ray.train.report({"rank": ray.train.get_context().get_world_rank()})
class CustomMetricsCallback(ray.train.UserCallback):
def after_report(
self,
run_context,
metrics: List[Dict[str, Any]],
checkpoint: Optional[ray.train.Checkpoint],
):
rank_0_metrics = metrics[0]
print(rank_0_metrics)
# Ex: Write metrics to a file...
trainer = ray.train.torch.TorchTrainer(
train_fn_per_worker,
scaling_config=ray.train.ScalingConfig(num_workers=2),
run_config=ray.train.RunConfig(callbacks=[CustomMetricsCallback()]),
)
trainer.fit()
# __report_callback_end__
@@ -0,0 +1,58 @@
import random
import string
import ray
def random_text(length: int) -> str:
"""Generate random text of specified length."""
if length <= 0:
return ""
if length <= 3:
return "".join(random.choices(string.ascii_lowercase, k=length))
words = []
current_length = 0
while current_length < length:
remaining = length - current_length
if remaining <= 4:
word_length = remaining
word = "".join(random.choices(string.ascii_lowercase, k=word_length))
words.append(word)
break
else:
max_word_length = min(10, remaining - 1)
if max_word_length >= 3:
word_length = random.randint(3, max_word_length)
else:
word_length = remaining
word = "".join(random.choices(string.ascii_lowercase, k=word_length))
words.append(word)
current_length += len(word) + 1
text = " ".join(words)
return text[:length]
def random_label() -> int:
"""Pick a random label."""
labels = [0, 1, 2, 3, 4, 5, 6, 7]
return random.choice(labels)
def create_mock_ray_text_dataset(dataset_size: int = 96, min_len: int = 5, max_len: int = 100):
"""Create a mock Ray dataset with random text and labels."""
numbers = random.choices(range(min_len, max_len + 1), k=dataset_size)
ray_dataset = ray.data.from_items(numbers)
def map_to_text_and_label(item):
length = item['item']
text = random_text(length)
label = random_label()
return {
"length": length,
"text": text,
"label": label
}
text_dataset = ray_dataset.map(map_to_text_and_label)
return text_dataset
+84
View File
@@ -0,0 +1,84 @@
# flake8: noqa
# isort: skip_file
# __tf_train_start__
import os
os.environ["TF_USE_LEGACY_KERAS"] = "1"
import ray
import tensorflow as tf
from ray import train
from ray.train import ScalingConfig
from ray.train.tensorflow import TensorflowTrainer
from ray.train.tensorflow.keras import ReportCheckpointCallback
# If using GPUs, set this to True.
use_gpu = False
a = 5
b = 10
size = 100
def build_model() -> tf.keras.Model:
model = tf.keras.Sequential(
[
tf.keras.layers.InputLayer(input_shape=()),
# Add feature dimension, expanding (batch_size,) to (batch_size, 1).
tf.keras.layers.Flatten(),
tf.keras.layers.Dense(10),
tf.keras.layers.Dense(1),
]
)
return model
def train_func(config: dict):
import os
os.environ["TF_USE_LEGACY_KERAS"] = "1"
import tensorflow as tf
batch_size = config.get("batch_size", 64)
epochs = config.get("epochs", 3)
strategy = tf.distribute.MultiWorkerMirroredStrategy()
with strategy.scope():
# Model building/compiling need to be within `strategy.scope()`.
multi_worker_model = build_model()
multi_worker_model.compile(
optimizer=tf.keras.optimizers.SGD(learning_rate=config.get("lr", 1e-3)),
loss="mean_squared_error",
metrics=["mean_squared_error"],
)
dataset = train.get_dataset_shard("train")
results = []
for _ in range(epochs):
tf_dataset = dataset.to_tf(
feature_columns="x", label_columns="y", batch_size=batch_size
)
history = multi_worker_model.fit(
tf_dataset, callbacks=[ReportCheckpointCallback()]
)
results.append(history.history)
return results
config = {"lr": 1e-3, "batch_size": 32, "epochs": 4}
train_dataset = ray.data.from_items(
[{"x": x / 200, "y": 2 * x / 200} for x in range(200)]
)
scaling_config = ScalingConfig(num_workers=2, use_gpu=use_gpu)
trainer = TensorflowTrainer(
train_loop_per_worker=train_func,
train_loop_config=config,
scaling_config=scaling_config,
datasets={"train": train_dataset},
)
result = trainer.fit()
print(result.metrics)
# __tf_train_end__
@@ -0,0 +1,204 @@
# flake8: noqa
# isort: skip_file
import os
os.environ["RAY_TRAIN_V2_ENABLED"] = "1"
# __quickstart_start__
import random
import tempfile
import uuid
import ray.train
import ray.train.torch
import ray.tune
from ray.tune.integration.ray_train import TuneReportCallback
# [1] Define your Ray Train worker code.
def train_fn_per_worker(train_loop_config: dict):
# Unpack train worker hyperparameters.
# Train feeds in the `train_loop_config` defined below.
lr = train_loop_config["lr"]
# training code here...
print(
ray.train.get_context().get_world_size(),
ray.train.get_context().get_world_rank(),
train_loop_config,
)
# model = ray.train.torch.prepare_model(...) # Wrap model in DDP.
with tempfile.TemporaryDirectory() as temp_checkpoint_dir:
ray.train.report(
{"loss": random.random()},
checkpoint=ray.train.Checkpoint.from_directory(temp_checkpoint_dir),
)
# [2] Define a function that launches the Ray Train run.
def train_driver_fn(config: dict):
# Unpack run-level hyperparameters.
# Tune feeds in hyperparameters defined in the `param_space` below.
num_workers = config["num_workers"]
trainer = ray.train.torch.TorchTrainer(
train_fn_per_worker,
train_loop_config=config["train_loop_config"],
scaling_config=ray.train.ScalingConfig(
num_workers=num_workers,
# Uncomment to use GPUs.
# use_gpu=True,
),
run_config=ray.train.RunConfig(
# [3] Assign unique names to each run.
# Recommendation: use the trial id as part of the run name.
name=f"train-trial_id={ray.tune.get_context().get_trial_id()}",
# [4] (Optional) Pass in a `TuneReportCallback` to propagate
# reported results to the Tuner.
callbacks=[TuneReportCallback()],
# (If multi-node, configure S3 / NFS as the storage path.)
# storage_path="s3://...",
),
)
trainer.fit()
# Launch a single Train run.
# Note that you can only create a TuneReportCallback in a Ray Tune session.
# train_driver_fn({"num_workers": 4, "train_loop_config": {"lr": 1e-3}})
# Launch a sweep of hyperparameters with Ray Tune.
tuner = ray.tune.Tuner(
train_driver_fn,
param_space={
"num_workers": ray.tune.choice([2, 4]),
"train_loop_config": {
"lr": ray.tune.grid_search([1e-3, 3e-4]),
"batch_size": ray.tune.grid_search([32, 64]),
},
},
run_config=ray.tune.RunConfig(
name=f"tune_train_example-{uuid.uuid4().hex[:6]}",
# (If multi-node, configure S3 / NFS as the storage path.)
# storage_path="s3://...",
),
# [5] (Optional) Set the maximum number of concurrent trials
# in order to prevent too many Train driver processes from
# being launched at once.
tune_config=ray.tune.TuneConfig(max_concurrent_trials=2),
)
results = tuner.fit()
print(results.get_best_result(metric="loss", mode="min"))
# __quickstart_end__
# __max_concurrent_trials_start__
# For a fixed size cluster, calculate this based on the limiting resource (ex: GPUs).
total_cluster_gpus = 8
num_gpu_workers_per_trial = 4
max_concurrent_trials = total_cluster_gpus // num_gpu_workers_per_trial
def train_driver_fn(config: dict):
trainer = ray.train.torch.TorchTrainer(
train_fn_per_worker,
scaling_config=ray.train.ScalingConfig(
num_workers=num_gpu_workers_per_trial, use_gpu=True
),
)
trainer.fit()
tuner = ray.tune.Tuner(
train_driver_fn,
tune_config=ray.tune.TuneConfig(max_concurrent_trials=max_concurrent_trials),
)
# __max_concurrent_trials_end__
# __trainable_resources_start__
# Cluster setup:
# head_node:
# resources:
# CPU: 16.0
# worker_node_cpu:
# resources:
# CPU: 32.0
# TRAIN_DRIVER_RESOURCE: 1.0
# worker_node_gpu:
# resources:
# GPU: 4.0
import ray.tune
def train_driver_fn(config):
# trainer = TorchTrainer(...)
...
tuner = ray.tune.Tuner(
ray.tune.with_resources(
train_driver_fn,
# Note: 0.01 is an arbitrary value to schedule the actor
# onto the `worker_node_cpu` node type.
{"TRAIN_DRIVER_RESOURCE": 0.01},
),
)
# __trainable_resources_end__
# __fault_tolerance_start__
import tempfile
import ray.tune
import ray.train
import ray.train.torch
def train_fn_per_worker(train_loop_config: dict):
# [1] Train worker restoration logic.
checkpoint = ray.train.get_checkpoint()
if checkpoint:
with checkpoint.as_directory() as temp_checkpoint_dir:
# model.load_state_dict(torch.load(...))
...
with tempfile.TemporaryDirectory() as temp_checkpoint_dir:
# torch.save(...)
ray.train.report(
{"loss": 0.1},
checkpoint=ray.train.Checkpoint.from_directory(temp_checkpoint_dir),
)
def train_fn_driver(config: dict):
trainer = ray.train.torch.TorchTrainer(
train_fn_per_worker,
run_config=ray.train.RunConfig(
# [2] Train driver restoration is automatic, as long as
# the (storage_path, name) remains the same across trial restarts.
# The easiest way to do this is to attach the trial ID in the name.
# **Do not include any timestamps or random values in the name.**
name=f"train-trial_id={ray.tune.get_context().get_trial_id()}",
# [3] Enable worker-level fault tolerance to gracefully handle
# Train worker failures.
failure_config=ray.train.FailureConfig(max_failures=3),
# (If multi-node, configure S3 / NFS as the storage path.)
# storage_path="s3://...",
),
)
trainer.fit()
tuner = ray.tune.Tuner(
train_fn_driver,
run_config=ray.tune.RunConfig(
# [4] Enable trial-level fault tolerance to gracefully handle
# Train driver process failures.
failure_config=ray.tune.FailureConfig(max_failures=3)
),
)
tuner.fit()
# __fault_tolerance_end__
+245
View File
@@ -0,0 +1,245 @@
# flake8: noqa
# isort: skip_file
# TODO: [V2] Deprecated doc code to delete.
import os
os.environ["RAY_TRAIN_V2_ENABLED"] = "0"
# __basic_start__
import ray
import ray.tune
import ray.train
from ray.tune import Tuner
from ray.train.xgboost import XGBoostTrainer
dataset = ray.data.read_csv("s3://anonymous@air-example-data/breast_cancer.csv")
trainer = XGBoostTrainer(
label_column="target",
params={
"objective": "binary:logistic",
"eval_metric": ["logloss", "error"],
"max_depth": 4,
},
datasets={"train": dataset},
scaling_config=ray.train.ScalingConfig(num_workers=2),
)
# Create Tuner
tuner = Tuner(
trainer,
# Add some parameters to tune
param_space={"params": {"max_depth": ray.tune.choice([4, 5, 6])}},
# Specify tuning behavior
tune_config=ray.tune.TuneConfig(metric="train-logloss", mode="min", num_samples=2),
)
# Run tuning job
tuner.fit()
# __basic_end__
# __xgboost_start__
import ray.data
import ray.train
import ray.tune
from ray.tune import Tuner
from ray.train.xgboost import XGBoostTrainer
dataset = ray.data.read_csv("s3://anonymous@air-example-data/breast_cancer.csv")
# Create an XGBoost trainer
trainer = XGBoostTrainer(
label_column="target",
params={
"objective": "binary:logistic",
"eval_metric": ["logloss", "error"],
"max_depth": 4,
},
num_boost_round=10,
datasets={"train": dataset},
)
param_space = {
# Tune parameters directly passed into the XGBoostTrainer
"num_boost_round": ray.tune.randint(5, 20),
# `params` will be merged with the `params` defined in the above XGBoostTrainer
"params": {
"min_child_weight": ray.tune.uniform(0.8, 1.0),
# Below will overwrite the XGBoostTrainer setting
"max_depth": ray.tune.randint(1, 5),
},
# Tune the number of distributed workers
"scaling_config": ray.train.ScalingConfig(num_workers=ray.tune.grid_search([1, 2])),
}
tuner = Tuner(
trainable=trainer,
run_config=ray.tune.RunConfig(name="test_tuner_xgboost"),
param_space=param_space,
tune_config=ray.tune.TuneConfig(
mode="min", metric="train-logloss", num_samples=2, max_concurrent_trials=2
),
)
result_grid = tuner.fit()
# __xgboost_end__
# __torch_start__
import os
import ray.train
import ray.tune
from ray.tune import Tuner
from ray.train.examples.pytorch.torch_linear_example import (
train_func as linear_train_func,
)
from ray.train.torch import TorchTrainer
trainer = TorchTrainer(
train_loop_per_worker=linear_train_func,
train_loop_config={"lr": 1e-2, "batch_size": 4, "epochs": 10},
scaling_config=ray.train.ScalingConfig(num_workers=1, use_gpu=False),
)
param_space = {
# The params will be merged with the ones defined in the TorchTrainer
"train_loop_config": {
# This is a parameter that hasn't been set in the TorchTrainer
"hidden_size": ray.tune.randint(1, 4),
# This will overwrite whatever was set when TorchTrainer was instantiated
"batch_size": ray.tune.choice([4, 8]),
},
# Tune the number of distributed workers
"scaling_config": ray.train.ScalingConfig(num_workers=ray.tune.grid_search([1, 2])),
}
tuner = Tuner(
trainable=trainer,
run_config=ray.tune.RunConfig(
name="test_tuner", storage_path=os.path.expanduser("~/ray_results")
),
param_space=param_space,
tune_config=ray.tune.TuneConfig(
mode="min", metric="loss", num_samples=2, max_concurrent_trials=2
),
)
result_grid = tuner.fit()
# __torch_end__
# __tune_dataset_start__
import ray.data
import ray.tune
from ray.data.preprocessors import StandardScaler
def get_dataset():
ds1 = ray.data.read_csv("s3://anonymous@air-example-data/breast_cancer.csv")
prep_v1 = StandardScaler(["worst radius", "worst area"])
ds1 = prep_v1.fit_transform(ds1)
return ds1
def get_another_dataset():
ds2 = ray.data.read_csv(
"s3://anonymous@air-example-data/breast_cancer_with_categorical.csv"
)
prep_v2 = StandardScaler(["worst concavity", "worst smoothness"])
ds2 = prep_v2.fit_transform(ds2)
return ds2
dataset_1 = get_dataset()
dataset_2 = get_another_dataset()
tuner = ray.tune.Tuner(
trainer,
param_space={
"datasets": {
"train": ray.tune.grid_search([dataset_1, dataset_2]),
}
# Your other parameters go here
},
)
# __tune_dataset_end__
# __tune_optimization_start__
from ray.tune.search.bayesopt import BayesOptSearch
from ray.tune.schedulers import HyperBandScheduler
from ray.tune import TuneConfig
config = TuneConfig(
# ...
search_alg=BayesOptSearch(),
scheduler=HyperBandScheduler(),
)
# __tune_optimization_end__
# __result_grid_inspection_start__
from ray.tune import Tuner, TuneConfig
tuner = Tuner(
trainable=trainer,
param_space=param_space,
tune_config=TuneConfig(mode="min", metric="loss", num_samples=5),
)
result_grid = tuner.fit()
num_results = len(result_grid)
# Check if there have been errors
if result_grid.errors:
print("At least one trial failed.")
# Get the best result
best_result = result_grid.get_best_result()
# And the best checkpoint
best_checkpoint = best_result.checkpoint
# And the best metrics
best_metric = best_result.metrics
# Or a dataframe for further analysis
results_df = result_grid.get_dataframe()
print("Shortest training time:", results_df["time_total_s"].min())
# Iterate over results
for result in result_grid:
if result.error:
print("The trial had an error:", result.error)
continue
print("The trial finished successfully with the metrics:", result.metrics["loss"])
# __result_grid_inspection_end__
# __run_config_start__
import ray.tune
run_config = ray.tune.RunConfig(
name="MyExperiment",
storage_path="s3://...",
checkpoint_config=ray.tune.CheckpointConfig(checkpoint_frequency=2),
)
# __run_config_end__
# __tune_config_start__
from ray.tune import TuneConfig
from ray.tune.search.bayesopt import BayesOptSearch
tune_config = TuneConfig(
metric="loss",
mode="min",
max_concurrent_trials=10,
num_samples=100,
search_alg=BayesOptSearch(),
)
# __tune_config_end__
# __tune_restore_start__
tuner = Tuner.restore(
path=os.path.expanduser("~/ray_results/test_tuner"),
trainable=trainer,
restart_errored=True,
)
tuner.fit()
# __tune_restore_end__
@@ -0,0 +1,113 @@
# flake8: noqa
# isort: skip_file
# __xgboost_start__
import pandas as pd
import xgboost
# 1. Load your data as an `xgboost.DMatrix`.
train_df = pd.read_csv("s3://ray-example-data/iris/train/1.csv")
eval_df = pd.read_csv("s3://ray-example-data/iris/val/1.csv")
train_X = train_df.drop("target", axis=1)
train_y = train_df["target"]
eval_X = eval_df.drop("target", axis=1)
eval_y = eval_df["target"]
dtrain = xgboost.DMatrix(train_X, label=train_y)
deval = xgboost.DMatrix(eval_X, label=eval_y)
# 2. Define your xgboost model training parameters.
params = {
"tree_method": "approx",
"objective": "reg:squarederror",
"eta": 1e-4,
"subsample": 0.5,
"max_depth": 2,
}
# 3. Do non-distributed training.
bst = xgboost.train(
params,
dtrain=dtrain,
evals=[(deval, "validation")],
num_boost_round=10,
)
# __xgboost_end__
# __xgboost_ray_start__
import xgboost
import ray.train
from ray.train.xgboost import XGBoostTrainer, RayTrainReportCallback
# 1. Load your data as a Ray Data Dataset.
train_dataset = ray.data.read_csv("s3://anonymous@ray-example-data/iris/train")
eval_dataset = ray.data.read_csv("s3://anonymous@ray-example-data/iris/val")
def train_func():
# 2. Load your data shard as an `xgboost.DMatrix`.
# Get dataset shards for this worker
train_shard = ray.train.get_dataset_shard("train")
eval_shard = ray.train.get_dataset_shard("eval")
# Convert shards to pandas DataFrames
train_df = train_shard.materialize().to_pandas()
eval_df = eval_shard.materialize().to_pandas()
train_X = train_df.drop("target", axis=1)
train_y = train_df["target"]
eval_X = eval_df.drop("target", axis=1)
eval_y = eval_df["target"]
dtrain = xgboost.DMatrix(train_X, label=train_y)
deval = xgboost.DMatrix(eval_X, label=eval_y)
# 3. Define your xgboost model training parameters.
params = {
"tree_method": "approx",
"objective": "reg:squarederror",
"eta": 1e-4,
"subsample": 0.5,
"max_depth": 2,
}
# 4. Do distributed data-parallel training.
# Ray Train sets up the necessary coordinator processes and
# environment variables for your workers to communicate with each other.
bst = xgboost.train(
params,
dtrain=dtrain,
evals=[(deval, "validation")],
num_boost_round=10,
# Optional: Use the `RayTrainReportCallback` to save and report checkpoints.
callbacks=[RayTrainReportCallback()],
)
# 5. Configure scaling and resource requirements.
scaling_config = ray.train.ScalingConfig(num_workers=2, resources_per_worker={"CPU": 2})
# 6. Launch distributed training job.
trainer = XGBoostTrainer(
train_func,
scaling_config=scaling_config,
datasets={"train": train_dataset, "eval": eval_dataset},
# If running in a multi-node cluster, this is where you
# should configure the run's persistent storage that is accessible
# across all worker nodes.
# run_config=ray.train.RunConfig(storage_path="s3://..."),
)
result = trainer.fit()
# 7. Load the trained model
import os
with result.checkpoint.as_directory() as checkpoint_dir:
model_path = os.path.join(checkpoint_dir, RayTrainReportCallback.CHECKPOINT_NAME)
model = xgboost.Booster()
model.load_model(model_path)
# __xgboost_ray_end__