このガイドでは、ハイパーパラメーター探索の最適化のために、W&BをPythonトレーニングスクリプトやノートブックに統合する方法についての推奨事項を説明します。
元のトレーニングスクリプト
モデルをトレーニングするPythonスクリプト(以下を参照)があると仮定します。ゴールは、検証精度(val_acc)を最大化するハイパーパラメーターを見つけることです。
Pythonスクリプトでは、train_one_epoch と evaluate_one_epoch の2つの関数を定義します。train_one_epoch 関数は1エポック分のトレーニングをシミュレートし、トレーニングの精度と損失を返します。evaluate_one_epoch 関数は、検証データセットでのモデル評価をシミュレートし、検証の精度と損失を返します。
学習率(lr)、バッチサイズ(batch_size)、エポック数(epochs)などのハイパーパラメーター値を含む設定辞書(config)を定義します。設定辞書の値がトレーニングプロセスを制御します。
次に、典型的なトレーニングループを模した main という関数を定義します。各エポックで、トレーニングデータセットと検証データセットに対して精度と損失が計算されます。
このコードは模擬トレーニングスクリプトです。モデルのトレーニングは行わず、ランダムな精度と損失の値を生成することでトレーニングプロセスをシミュレートしています。このコードの目的は、W&Bをトレーニングスクリプトに統合する方法を示すことです。
import random
import numpy as np
def train_one_epoch(epoch, lr, batch_size):
acc = 0.25 + ((epoch / 30) + (random.random() / 10))
loss = 0.2 + (1 - ((epoch - 1) / 10 + random.random() / 5))
return acc, loss
def evaluate_one_epoch(epoch):
acc = 0.1 + ((epoch / 20) + (random.random() / 10))
loss = 0.25 + (1 - ((epoch - 1) / 10 + random.random() / 6))
return acc, loss
# ハイパーパラメーターの値を持つconfig変数
config = {"lr": 0.0001, "batch_size": 16, "epochs": 5}
def main():
lr = config["lr"]
batch_size = config["batch_size"]
epochs = config["epochs"]
for epoch in np.arange(1, epochs):
train_acc, train_loss = train_one_epoch(epoch, lr, batch_size)
val_acc, val_loss = evaluate_one_epoch(epoch)
print("epoch: ", epoch)
print("training accuracy:", train_acc, "training loss:", train_loss)
print("validation accuracy:", val_acc, "validation loss:", val_loss)
if __name__ == "__main__":
main()
次のセクションでは、トレーニング中のハイパーパラメーターとメトリクスを追跡するために、PythonスクリプトにW&Bを追加します。W&Bを使用して、検証精度(val_acc)を最大化する最適なハイパーパラメーターを見つけます。
トレーニングスクリプトにW&Bを追加する
トレーニングスクリプトを更新してW&Bを含めます。PythonスクリプトまたはノートブックにW&Bを統合する方法は、Sweeps をどのように管理するかによって異なります。
W&B Python SDKを使用して Sweeps の開始、停止、管理を行う場合は、Python script or notebook タブの手順に従ってください。代わりにW&B CLIを使用する場合は、CLI タブの手順に従ってください。
sweep configuration を含むYAML設定ファイルを作成します。この設定ファイルには、sweep に探索させたいハイパーパラメーターが含まれます。次の例では、各 sweep 中にバッチサイズ(batch_size)、エポック数(epochs)、および学習率(lr)のハイパーパラメーターが変化します。# config.yaml
program: train.py
method: random
name: sweep
metric:
goal: maximize
name: val_acc
parameters:
batch_size:
values: [16, 32, 64]
lr:
min: 0.0001
max: 0.1
epochs:
values: [5, 10, 15]
W&B Sweep の設定作成方法の詳細については、Define sweep configuration を参照してください。YAMLファイルの program キーには、Pythonスクリプトの名前を指定する必要があります。次に、コード例に以下を追加します。
- W&B Python SDK (
wandb) と PyYAML (yaml) をインポートします。PyYAMLはYAML設定ファイルを読み込むために使用されます。
- 設定ファイルを読み込みます。
wandb.init() を使用して、データを W&B Run として同期およびログ記録するためのバックグラウンドプロセスを開始します。config オブジェクトを config パラメータに渡します。
- 値をハードコードする代わりに、
wandb.Run.config からハイパーパラメーター値を定義します。
- 最適化したいメトリクスを
wandb.Run.log() でログ記録します。設定で定義したメトリクスをログに記録する必要があります。設定辞書(この例では sweep_configuration)内で、val_acc の値を最大化するように sweep を定義しています。
import wandb
import yaml
import random
import numpy as np
def train_one_epoch(epoch, lr, batch_size):
acc = 0.25 + ((epoch / 30) + (random.random() / 10))
loss = 0.2 + (1 - ((epoch - 1) / 10 + random.random() / 5))
return acc, loss
def evaluate_one_epoch(epoch):
acc = 0.1 + ((epoch / 20) + (random.random() / 10))
loss = 0.25 + (1 - ((epoch - 1) / 10 + random.random() / 6))
return acc, loss
def main():
# デフォルトのハイパーパラメーターを設定
with open("./config.yaml") as file:
config = yaml.load(file, Loader=yaml.FullLoader)
with wandb.init(config=config) as run:
for epoch in np.arange(1, run.config['epochs']):
train_acc, train_loss = train_one_epoch(epoch, run.config['lr'], run.config['batch_size'])
val_acc, val_loss = evaluate_one_epoch(epoch)
run.log(
{
"epoch": epoch,
"train_acc": train_acc,
"train_loss": train_loss,
"val_acc": val_acc,
"val_loss": val_loss,
}
)
# main関数を呼び出し
main()
CLIで、sweep agent が試行する最大 run 数を設定します。これはオプションです。この例では最大数を5に設定します。次に、wandb sweep コマンドで sweep を初期化します。YAMLファイルの名前を指定します。オプションで、プロジェクトフラグ (--project) に Projects 名を指定します。wandb sweep --project sweep-demo-cli config.yaml
これにより sweep ID が返されます。sweep の初期化方法の詳細については、Initialize sweeps を参照してください。sweep ID をコピーし、次のコードスニペットの sweepID を置き換えて、wandb agent コマンドで sweep ジョブを開始します。wandb agent --count $NUM your-entity/sweep-demo-cli/sweepID
詳細については、Start sweep jobs を参照してください。 以下の手順に従って、PythonスクリプトにW&Bを追加します。
- キーと値のペアで sweep configuration を定義する辞書オブジェクトを作成します。sweep configuration では、W&Bに探索させたいハイパーパラメーターと、最適化したいメトリクスを定義します。前の例に引き続き、各 sweep 中に変化させるハイパーパラメーターは、バッチサイズ(
batch_size)、エポック数(epochs)、および学習率(lr)です。検証スコアの精度を最大化したいので、"goal": "maximize" と設定し、最適化したい変数の名前(この場合は val_acc)を "name": "val_acc" と設定します。
- sweep configuration 辞書を
wandb.sweep() に渡します。これにより sweep が初期化され、sweep ID (sweep_id) が返されます。詳細については、Initialize sweeps を参照してください。
- スクリプトの冒頭で W&B Python SDK (
wandb) をインポートします。
main 関数内で、wandb.init() を使用して、データを W&B Run として同期およびログ記録するためのバックグラウンドプロセスを生成します。wandb.init() メソッドに引数としてプロジェクト名を渡します。プロジェクト名を渡さない場合、W&Bはデフォルトのプロジェクト名を使用します。
wandb.Run.config オブジェクトからハイパーパラメーター値を取得します。これにより、ハードコードされた値の代わりに sweep configuration 辞書で定義されたハイパーパラメーター値を使用できるようになります。
wandb.Run.log() を使用して、最適化しているメトリクスをW&Bにログ記録します。設定で定義したメトリクスをログに記録する必要があります。例えば、最適化するメトリクスを val_acc と定義した場合は、val_acc をログに記録しなければなりません。メトリクスをログに記録しないと、W&Bは何を最適化すればよいか判断できません。設定辞書(この例では sweep_configuration)内で、val_acc の値を最大化するように sweep を定義しています。
wandb.agent() で sweep を開始します。sweep ID と sweep が実行する関数の名前(function=main)を指定し、試行する最大 run 数を4回(count=4)に指定します。
これらをすべてまとめると、スクリプトは以下のようになります。import wandb # W&B Python SDKをインポート
import numpy as np
import random
import argparse
def train_one_epoch(epoch, lr, batch_size):
acc = 0.25 + ((epoch / 30) + (random.random() / 10))
loss = 0.2 + (1 - ((epoch - 1) / 10 + random.random() / 5))
return acc, loss
def evaluate_one_epoch(epoch):
acc = 0.1 + ((epoch / 20) + (random.random() / 10))
loss = 0.25 + (1 - ((epoch - 1) / 10 + random.random() / 6))
return acc, loss
def main(args=None):
# sweep agentによって呼び出される場合、argsはNoneになるため、
# sweep configからprojectを使用します
project = args.project if args else None
with wandb.init(project=project) as run:
# `wandb.Run.config` オブジェクトからハイパーパラメーター値を取得
lr = run.config["lr"]
batch_size = run.config["batch_size"]
epochs = run.config["epochs"]
# トレーニングループを実行し、パフォーマンス値をW&Bにログ記録
for epoch in np.arange(1, epochs):
train_acc, train_loss = train_one_epoch(epoch, lr, batch_size)
val_acc, val_loss = evaluate_one_epoch(epoch)
run.log(
{
"epoch": epoch,
"train_acc": train_acc,
"train_loss": train_loss,
"val_acc": val_acc, # 最適化されるメトリクス
"val_loss": val_loss,
}
)
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--project", type=str, default="sweep-example", help="W&B project name")
args = parser.parse_args()
# sweep config辞書を定義
sweep_configuration = {
"method": "random",
"name": "sweep",
# 最適化したいメトリクス
# 例えば、検証精度を最大化したい場合は
# "goal": "maximize" と設定し、最適化したい
# 変数の名前(この場合は "val_acc")を指定します
"metric": {
"goal": "maximize",
"name": "val_acc"
},
"parameters": {
"batch_size": {"values": [16, 32, 64]},
"epochs": {"values": [5, 10, 15]},
"lr": {"max": 0.1, "min": 0.0001},
},
}
# 設定辞書を渡してsweepを初期化
sweep_id = wandb.sweep(sweep=sweep_configuration, project=args.project)
# sweepジョブを開始
wandb.agent(sweep_id, function=main, count=4)
sweep におけるW&Bへのメトリクスのログ記録sweep configuration と wandb.Run.log() の両方で、定義した最適化対象のメトリクスをログに記録する必要があります。例えば、sweep configuration 内で最適化するメトリクスを val_acc と定義した場合、val_acc もW&Bにログ記録しなければなりません。メトリクスをログに記録しないと、W&Bは何を最適化すればよいか判断できません。with wandb.init() as run:
val_loss, val_acc = train()
run.log(
{
"val_loss": val_loss,
"val_acc": val_acc
}
)
以下は、W&Bへのメトリクスログ記録の誤った例です。sweep configuration で最適化されるメトリクスは val_acc ですが、コードでは validation というキーの下のネストされた辞書内に val_acc をログ記録しています。メトリクスは、ネストされた辞書内ではなく、直接ログに記録する必要があります。with wandb.init() as run:
val_loss, val_acc = train()
run.log(
{
"validation": {
"val_loss": val_loss,
"val_acc": val_acc
}
}
)