メインコンテンツまでスキップ
バージョン: v2506

チューニングTips

ZenithTuneによる自動チューニングを強化する便利機能について説明します。

最大化問題に変更する

Tunerの初期化時にmaximize引数にTrueを設定することで最小化問題から最大化問題に変更できます。 例えば、学習スループットを最大化する場合などに役立ちます。

tuner = GeneralTuner(maximize=True)
tuner.optimize(objective, n_trials=10)

チューニングから試行を除外する

すべてのTunerに共通して、目的関数内でエラーや例外が生じた試行は自動的に除外されます。 例えば、コマンド実行時にOut of Memoryなどによってプログラムが異常終了してしまった場合についてもユーザが考慮する必要はありません。 一方で、コスト値の算出に失敗した場合や、何らかの制約を満たさなかった場合など試行を意図的に除外したい場合には、目的関数内でNoneを返すことで該当の試行を除外することができます。

def objective(trial, **kwargs):
x = trial.suggest_int("x", low=2, high=10)
y = trial.suggest_int("y", low=1, high=10)

if x % y != 0:
return None
return x**2 + y**2

既存のチューニング結果からの再開・分析

既存のチューニング結果(Optunaが出力するsqliteデータベース)のパスをTunerのdb_pathに指定することで、最後の試行からチューニングを再開します。

tuner = GeneralTuner(db_path="path/to/study.db")
tuner.optimize(objective, n_trials=10)

既存のチューニング結果の分析のみをしたい場合にも同様にdb_pathを指定することで実現できます。

tuner = GeneralTuner(db_path="path/to/study.db")
tuner.analyze()

チューニングパラメータに初期値を設定する

Tuner optimize関数のdefault_params引数に、目的関数で定義したハイパーパラメータの値をdict形式で与えることで初期値を設定できます。 良い初期値を与えることで、悪いパラメータを試行する回数が減り、効率的にチューニングできる可能性が高まります。 また、ベースラインとなる初期値を設定することで、チューニングによる改善度合いの分析が容易になります。

def objective(trial, **kwargs):
x = trial.suggest_int("x", low=-10, high=10)
y = trial.suggest_int("y", low=-10, high=10)
return x**2 + y**2

tuner = GeneralTuner()
tuner.optimize(
objective,
n_trials=10,
default_params={
"x": 1,
"y": 2,
},
)

目的関数からメタデータを受け取る

目的関数のキーワード引数には、メタデータとして以下の値が提供されます。 必要に応じて参照してください。

  • trial_id: 試行のインデックス(0-indexed)
  • dist_info: 分散実行情報を表す辞書データ。以下のキーバリューが含まれます。
    • rank: プロセスのランク
    • local_rank: プロセスのローカルランク
    • world_size: プロセス数
    • launcher: 分散実行方法。"mpi","torch", Noneのいずれか
  • study_dir: チューニングログ・結果が出力されるディレクトリ。作業ファイルを生成したい場合にも活用できます。

環境変数をチューニングする

目的関数で定義したハイパーパラメータを、os.environを介して環境変数に直接設定することで、環境変数をチューニングできます。

def command_generator(trial, **kwargs):
num_workers = trial.suggest_int("num_workers", low=1, high=10)
omp_num_threads = trial.suggest_categorical("omp_num_threads", [1, 2, 4, 8, 16, 32])
os.environ["OMP_NUM_THREADS"] = str(omp_num_threads)

command = f"python train.py --num-workers {num_workers}"
return command

CommandOutputTunerを使用している場合は、実行コマンドログファイルの冒頭に現在のコンテキストにおける環境変数が列挙されるため、適切に環境変数が設定されているか確認できます。

スクリプト・設定ファイルをチューニングする

ZenithTuneが提供するreplace_params_to_file関数を使用することでスクリプトや設定ファイル内に記載されている値もチューニングすることができます。

事前準備として、チューニングしたいファイルのコピーを作成し、チューニングパラメータとして変更したい値に対してパラメータ名とその両端を{{ }}で囲むことでプレースホルダ化します。

train:
batch_size: 4
learning_rate: 1e-5

train:
batch_size: {{batch_size}}
learning_rate: 1e-5

そして、目的関数で対応するチューニングパラメータを定義し、replace_params_to_file関数を使用してプレースホルダ化された値を上書きします。 replace_params_to_file関数の引数には、プレースホルダが含まれる入力ファイルパス、出力ファイルパス、そして設定するパラメータとその値をdict形式で指定します。

def replace_params_to_file(
input_filepath: str,
output_filepath: str,
params: Dict[str, Any],
) -> None:

まとめると次のようになります。 目的関数からメタデータを受け取ることで、試行ごとのyamlファイルを個別に書き出しています。

from zenith_tune.utils import replace_params_to_file

def command_generator(trial, trial_id, study_dir, **kwargs):
num_workers = trial.suggest_int("num_workers", low=1, high=10)
batch_size = trial.suggest_int("batch_size", low=2, high=8)
tuning_yaml = os.path.join(study_dir, f"trial_{trial_id}.yaml")
replace_params_to_file(
"edited.yaml",
tuning_yaml,
{"batch_size": batch_size},
)

command = f"python train.py --num-workers {num_workers} --config {tuning_yaml}"
return command

このようにすることで、output_filepathに出力したファイルをチューニング後もそのまま使用することができます。