Tune 中的日志记录和输出#

默认情况下,Tune 会记录 TensorBoard、CSV 和 JSON 格式的结果。如果您需要记录较低级别的内容,如模型权重或梯度,请参阅 可训练日志记录。您可以在此处了解更多关于日志记录和自定义的信息:日志记录器文档字符串

如何在 Tune 中配置日志记录?#

Tune 会将每次试验的结果记录到指定本地目录下的一个子文件夹中,默认目录为 ~/ray_results

# This logs to two different trial folders:
# ~/ray_results/trainable_name/trial_name_1 and ~/ray_results/trainable_name/trial_name_2
# trainable_name and trial_name are autogenerated.
tuner = tune.Tuner(trainable, run_config=RunConfig(num_samples=2))
results = tuner.fit()

你可以指定 storage_pathtrainable_name

# This logs to 2 different trial folders:
# ./results/test_experiment/trial_name_1 and ./results/test_experiment/trial_name_2
# Only trial_name is autogenerated.
tuner = tune.Tuner(trainable,
    tune_config=tune.TuneConfig(num_samples=2),
    run_config=RunConfig(storage_path="./results", name="test_experiment"))
results = tuner.fit()

要了解更多关于 Trials 的信息,请参阅其详细的 API 文档:试验

如何将您的 Tune 运行记录到 TensorBoard?#

Tune 在 Tuner.fit() 期间自动输出 TensorBoard 文件。要使用 tensorboard 可视化学习过程,请安装 tensorboardX:

$ pip install tensorboardX

然后,在你运行实验之后,你可以通过指定结果的输出目录,使用 TensorBoard 来可视化你的实验。

$ tensorboard --logdir=~/ray_results/my_experiment

如果你在一个没有sudo权限的远程多用户集群上运行Ray,你可以运行以下命令以确保tensorboard能够写入tmp目录:

$ export TMPDIR=/tmp/$USER; mkdir -p $TMPDIR; tensorboard --logdir=~/ray_results
../../_images/ray-tune-tensorboard.png

如果使用 TensorFlow 2.x,Tune 还会自动生成 TensorBoard HParams 输出,如下所示:

tuner = tune.Tuner(
    ...,
    param_space={
        "lr": tune.grid_search([1e-5, 1e-4]),
        "momentum": tune.grid_search([0, 0.9])
    }
)
results = tuner.fit()
../../_images/tune-hparams.png

如何使用 Tune 控制控制台输出?#

用户提供的字段将基于最大努力原则自动输出。你可以使用 Reporter 对象来自定义控制台输出。

== Status ==
Memory usage on this node: 11.4/16.0 GiB
Using FIFO scheduling algorithm.
Resources requested: 4/12 CPUs, 0/0 GPUs, 0.0/3.17 GiB heap, 0.0/1.07 GiB objects
Result logdir: /Users/foo/ray_results/myexp
Number of trials: 4 (4 RUNNING)
+----------------------+----------+---------------------+-----------+--------+--------+----------------+-------+
| Trial name           | status   | loc                 |    param1 | param2 |    acc | total time (s) |  iter |
|----------------------+----------+---------------------+-----------+--------+--------+----------------+-------|
| MyTrainable_a826033a | RUNNING  | 10.234.98.164:31115 | 0.303706  | 0.0761 | 0.1289 |        7.54952 |    15 |
| MyTrainable_a8263fc6 | RUNNING  | 10.234.98.164:31117 | 0.929276  | 0.158  | 0.4865 |        7.0501  |    14 |
| MyTrainable_a8267914 | RUNNING  | 10.234.98.164:31111 | 0.068426  | 0.0319 | 0.9585 |        7.0477  |    14 |
| MyTrainable_a826b7bc | RUNNING  | 10.234.98.164:31112 | 0.729127  | 0.0748 | 0.1797 |        7.05715 |    14 |
+----------------------+----------+---------------------+-----------+--------+--------+----------------+-------+

如何在 Tune 运行中将可训练日志重定向到文件?#

在 Tune 中,Trainables 作为远程执行者运行。默认情况下,Ray 会收集执行者的 stdout 和 stderr 并将其打印到主进程(更多信息请参见 ray worker logs)。在 Tune Trainables 中发生的日志记录默认遵循这种处理方式。但是,如果您希望将 Trainable 日志收集到文件中进行分析,Tune 提供了 log_to_file 选项来实现这一点。这适用于打印语句、warnings.warnlogger.info 等。

通过将 log_to_file=True 传递给 RunConfig,该配置由 Tuner 接收,stdout 和 stderr 将分别记录到 trial_logdir/stdouttrial_logdir/stderr 中:

tuner = tune.Tuner(
    trainable,
    run_config=RunConfig(log_to_file=True)
)
results = tuner.fit()

如果你想指定输出文件,你可以传递一个文件名,其中将存储合并的输出,或者传递两个文件名,分别用于标准输出和标准错误:

tuner = tune.Tuner(
    trainable,
    run_config=RunConfig(log_to_file="std_combined.log")
)
tuner.fit()

tuner = tune.Tuner(
    trainable,
    run_config=RunConfig(log_to_file=("my_stdout.log", "my_stderr.log")))
results = tuner.fit()

文件名相对于试验的日志目录。你也可以传递绝对路径。

注意事项#

在分布式训练工作器中发生的日志记录(如果您碰巧将 Ray Tune 与 Ray Train 一起使用)不属于此 log_to_file 配置。

log_to_file 文件在哪里可以找到?#

如果你的 Tune 工作负载配置为同步到主节点,那么相应的 log_to_file 输出可以在每个试验文件夹下找到。如果你的 Tune 工作负载配置为同步到云端,那么相应的 log_to_file 输出*不会*同步到云端,只能在相应的试验发生的 worker 节点上找到。

备注

当可训练对象在其生命周期内被移动到不同的节点时,这可能会导致问题。这种情况可能发生在某些调度器或节点故障时。如果有足够的用户请求,我们可能会优先启用此功能。如果这影响了您的工作流程,请考虑在 [此工单](ray-project/ray#32142) 上发表评论。

请对此功能留下反馈#

我们知道,日志记录和可观测性可以极大地提升您的工作流程性能。请告诉我们您更喜欢以何种方式与训练中的日志记录进行交互。请在[此工单](ray-project/ray#32142)中留下您的评论。

如何从 Tune Trainable 中记录任意文件?#

默认情况下,Tune 仅记录 训练结果字典检查点 来自您的可训练对象。然而,您可能希望保存一个可视化模型权重或模型图的文件,或者使用需要多进程日志记录的自定义日志库。例如,如果您尝试将图像记录到 TensorBoard,您可能希望这样做。我们将这些保存的文件称为 试验工件

备注

如果 SyncConfig(sync_artifacts=True),试验工件会定期从每个试验(或从Ray Train的每个远程训练工作者)上传到 RunConfig(storage_path)

请参阅 SyncConfig API 参考以获取工件同步配置选项。

你可以在可训练对象中直接保存试验工件,如下所示:

小技巧

确保任何日志调用或对象保持在 Trainable 的作用域内。否则,您可能会看到序列化错误或不一致的日志。

import logging_library  # ex: mlflow, wandb
from ray import train

def trainable(config):
    logging_library.init(
        name=trial_id,
        id=trial_id,
        resume=trial_id,
        reinit=True,
        allow_val_change=True)
    logging_library.set_log_path(os.getcwd())

    for step in range(100):
        logging_library.log_model(...)
        logging_library.log(results, step=step)

        # You can also just write to a file directly.
        # The working directory is set to the trial directory, so
        # you don't need to worry about multiple workers saving
        # to the same location.
        with open(f"./artifact_{step}.txt", "w") as f:
            f.write("Artifact Data")

        train.report(results)
import logging_library  # ex: mlflow, wandb
from ray import tune

class CustomLogging(tune.Trainable)
    def setup(self, config):
        trial_id = self.trial_id
        logging_library.init(
            name=trial_id,
            id=trial_id,
            resume=trial_id,
            reinit=True,
            allow_val_change=True
        )
        logging_library.set_log_path(os.getcwd())

    def step(self):
        logging_library.log_model(...)

        # You can also write to a file directly.
        # The working directory is set to the trial directory, so
        # you don't need to worry about multiple workers saving
        # to the same location.
        with open(f"./artifact_{self.iteration}.txt", "w") as f:
            f.write("Artifact Data")

    def log_result(self, result):
        res_dict = {
            str(k): v
            for k, v in result.items()
            if (v and "config" not in k and not isinstance(v, str))
        }
        step = result["training_iteration"]
        logging_library.log(res_dict, step=step)

在上面的代码片段中,logging_library 指的是你正在使用的任何第三方日志库。注意,logging_library.set_log_path(os.getcwd()) 是我们为了演示目的而使用的虚构API,它强调了第三方库应配置为将日志记录到Trainable的*工作目录*。默认情况下,一旦作为远程Ray actor启动,功能性和类Trainable的当前工作目录都会被设置为相应的试验目录。

如何构建自定义调优记录器?#

你可以通过继承 LoggerCallback 接口(LoggerCallback 接口 (tune.logger.LoggerCallback))来创建自定义日志记录器:

from typing import Dict, List

import json
import os

from ray.tune.logger import LoggerCallback


class CustomLoggerCallback(LoggerCallback):
    """Custom logger interface"""

    def __init__(self, filename: str = "log.txt"):
        self._trial_files = {}
        self._filename = filename

    def log_trial_start(self, trial: "Trial"):
        trial_logfile = os.path.join(trial.logdir, self._filename)
        self._trial_files[trial] = open(trial_logfile, "at")

    def log_trial_result(self, iteration: int, trial: "Trial", result: Dict):
        if trial in self._trial_files:
            self._trial_files[trial].write(json.dumps(result))

    def on_trial_complete(self, iteration: int, trials: List["Trial"],
                          trial: "Trial", **info):
        if trial in self._trial_files:
            self._trial_files[trial].close()
            del self._trial_files[trial]

然后,您可以按如下方式传入自己的记录器:

from ray import tune
from ray.train import RunConfig

tuner = tune.Tuner(
    MyTrainableClass,
    run_config=RunConfig(name="experiment_name", callbacks=[CustomLoggerCallback("log_test.txt")])
)
results = tuner.fit()

默认情况下,如果您不自行传递日志记录回调,Ray Tune 会创建 JSON、CSV 和 TensorBoardX 日志记录回调。您可以通过将 TUNE_DISABLE_AUTO_CALLBACK_LOGGERS 环境变量设置为 "1" 来禁用此行为。

创建自定义记录器的示例可以在 日志记录示例 中找到。