ハイパーパラメーターをチューニングする
高次元のハイパーパラメータースペースを探索して最も性能の良いモデルを見つけるのは非常に手間がかかります。ハイパーパラメーター探索は、モデルをバトルロワイヤル形式で対決させ、最も正確なモデルを選び出すための組織化された効率的な方法を提供します。この方法では、ハイパーパラメーターの値(例えば、学習率、バッチサイズ、隠れ層の数、オプティマイザーの種類)の組み合わせを自動的に探索し、最適な値を見つけることができます。
このチュートリアルでは、Weights & Biasesを使って3つの簡単なステップで高度なハイパーパラメーター探索を実行する方法を紹介します。
ビデオチュートリアルと一緒に学ぼう!
🚀 セットアップ
まず、実験管理ライブラリをインストールし、無料のW&Bアカウントを設定します。
!pip install
でインストール- Pythonにライブラリを
import
.login()
してメトリクスをプロジェクトにログ
もしWeights & Biasesを初めて使用する場合、
login
の呼び出しでアカウント作成のリンクが提供されます。
W&Bは個人および学術プロジェクトに無料で使用できます!
!pip install wandb -Uq
import wandb
wandb.login()
Step 1️⃣. Sweepを定義する
本質的に、Sweepは多数のハイパーパラメーターの値を試すための戦略と、それらを評価するコードを組み合わせたものです。 設定 という形で 戦略を定義する必要があります。
ノートブックでSweepを設定する場合、 設定オブジェクトはネストした辞書となります。 コマンドラインでSweepを実行する場合、 設定オブジェクトは YAMLファイル になります。
一緒にSweep設定の定義を進めていきましょう。 各コンポーネントを説明するために、ゆっくり進めます。 一般的なSweepパイプラインでは、 このステップは一度の割り当てで行います。
👈 method
を選択
新しいパラメーター値を選択する method
を定義する最初のステップです。
以下の検索methods
を提供しています:
grid
検索 – ハイパーパラメーター値のすべての組み合わせを繰り返し試行します。 非常に効果的ですが、計算量が多くなることがあります。random
検索 – 提供されたdistribution
に従って新しい組み合わせをランダムに選択します。驚くほど効果的です!bayes
ian 検索 – ハイパーパラメーターに対するメトリクススコアの確率モデルを作成し、メトリクスを改善する確率の高いパラメーターを選択します。少数の連続パラメーターに対しては効果的ですが、スケールしにくいです。
ここでは random
を使用します。
sweep_config = {
'method': 'random'
}
bayes
ian Sweepの場合も、 metric
について少し教えてください。
モデル出力から見つけられるようにその name
を教えてください
また、 goal
が minimize
すること(例えば平方誤差の場合)なのか、
それとも maximize
すること(例えば精度の場合)なのかも教えてください。
metric = {
'name': 'loss',
'goal': 'minimize'
}
sweep_config['metric'] = metric
bayes
ian Sweepを実行しない場合でも、この情報を sweep_config
に含めておくと良いでしょう。後で変更する場合でも役立ちます。また、再現性のためにこのようなことを記録しておくことは良い習慣です。
6か月または6年後に自分や他の人がSweepに戻ってきた際に、 val_G_batch
が高い方が良いのか低い方が良いのかを確認できるようにしておくためです。
📃 ハイパーparameters
の名前を付ける
新しいハイパーパラメーターの値を試す method
を選択したら、その parameters
を定義する必要があります。
ほとんどの場合、このステップは簡単です:
パラメーターに名前を付け、そのパラメーターの合法な values
のリストを指定するだけです。
例えば、ネットワークの optimizer
を選択する際には、選択肢は限られています。
ここでは、最も人気のある2つの選択肢、adam
と sgd
を使用します。
無限の選択肢がある可能性のあるハイパーパラメーターでも、実際にはいくつかの選択された values
を試すのが理にかなっています。ここでは、隠れ層の layer_size
と dropout
で選択しています。
parameters_dict = {
'optimizer': {
'values': ['adam', 'sgd']
},
'fc_layer_size': {
'values': [128, 256, 512]
},
'dropout': {
'values': [0.3, 0.4, 0.5]
},
}
sweep_config['parameters'] = parameters_dict
この探索では変動させたくないハイパーパラメーターがあることが多いですが、それでも sweep_config
に設定しておきたい場合は、その value
を直接設定します。
parameters_dict.update({
'epochs': {
'value': 1}
})
grid
検索の場合、それだけで十分です。
random
検索の場合、パラメーターのすべての value
が同じ確率で選択されます。
もしこの動作が望ましくない場合は、名前付きの distribution
とそのパラメーター、例えば normal
分布の平均 mu
や標準偏差 sigma
を指定することもできます。
ランダム変数の分布の設定方法については、 ここを参照。
parameters_dict.update({
'learning_rate': {
# 0から0.1までの一様分布
'distribution': 'uniform',
'min': 0,
'max': 0.1
},
'batch_size': {
# 32から256までの整数
# 対数が均等に分布している
'distribution': 'q_log_uniform_values',
'q': 8,
'min': 32,
'max': 256,
}
})
設定が完了したとき、 sweep_config
はネストされた辞書であり、どの parameters
に興味があるか、どの method
を使用するかを正確に指定します。
import pprint
pprint.pprint(sweep_config)
しかし、設定オプションはこれだけではありません!
例えば、HyperBand スケジューリングアルゴリズムを使用して early_terminate
オプションも提供しています。詳細は こちら をご覧ください。
すべての設定オプションのリストは こちらに、YAML形式の例の大集合は こちらにあります。
Step 2️⃣. Sweepを初期化する
検索戦略を定義したら、それを実装する何かを設定する時が来ました。
Sweepの中心となるのは Sweep Controller です。 各runが完了するたびに、新しいrunを実行するための指示を発行します。 これらの指示はrunを実際に実行する agents によって受け取られます。
一般的なSweepでは、Controllerは 私たちの マシン上に存在し、runを完了するエージェントは あなたの マシン(例えば次の図)上に存在します。 この労働分担により、エージェントを実行するためのマシンを追加するだけでSweepを簡単にスケールアップできます!
適切な sweep_config
と project
名を使用して wandb.sweep
を呼び出すことで、Sweep Controllerを起動できます。
この関数は sweep_id
を返し、後でこのControllerにエージェントを割り当てる時に使用します。
補足: コマンドラインでは、この関数は次のように置き換えられます
wandb sweep config.yaml
sweep_id = wandb.sweep(sweep_config, project="pytorch-sweeps-demo")
Step 3️⃣. Sweepエージェントを実行する
💻 トレーニング手順を定義する
実際にsweepを実行する前に、それらの値を使用するトレーニング手順を定義する必要があります。
以下の関数では、PyTorchでの単純な完全結合ニューラルネットワークを定義し、次の wandb
ツールを追加してモデルのメトリクスをログし、パフォーマンスと出力を可視化し、実験を追跡します:
wandb.init()
– 新しいW&B Runを初期化します。各Runはトレーニング関数の単一の実行です。wandb.config
– 全てのハイパーパラメーターを設定オブジェクトに保存し、ログされるようにします。wandb.config
の使用方法についてはこちらをご覧ください。wandb.log()
– モデルの振る舞いをW&Bにログします。ここではパフォーマンスのみをログしますが、その他のリッチメディアのログについてはこのColabをご覧ください。
PyTorchとW&Bの統合に関する詳細はこちらのColabをご覧ください。
import torch
import torch.optim as optim
import torch.nn.functional as F
import torch.nn as nn
from torchvision import datasets, transforms
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
def train(config=None):
# 新しいwandb runを初期化
with wandb.init(config=config):
# wandb.agentによって呼び出された場合、以下のように、
# このconfigはSweep Controllerによって設定されます
config = wandb.config
loader = build_dataset(config.batch_size)
network = build_network(config.fc_layer_size, config.dropout)
optimizer = build_optimizer(network, config.optimizer, config.learning_rate)
for epoch in range(config.epochs):
avg_loss = train_epoch(network, loader, optimizer)
wandb.log({"loss": avg_loss, "epoch": epoch})
このセルはトレーニング手順の4つの部分を定義します:
build_dataset
, build_network
, build_optimizer
, そして train_epoch
。
これらはすべて基本的なPyTorchパイプラインの一部であり、 W&Bの使用によってその実装が影響を受けることはありません。 したがって、それについてコメントはしません。
def build_dataset(batch_size):
transform = transforms.Compose(
[transforms.ToTensor(),
transforms.Normalize((0.1307,), (0.3081,))])
# MNISTトレーニングデータセットをダウンロード
dataset = datasets.MNIST(".", train=True, download=True,
transform=transform)
sub_dataset = torch.utils.data.Subset(
dataset, indices=range(0, len(dataset), 5))
loader = torch.utils.data.DataLoader(sub_dataset, batch_size=batch_size)
return loader
def build_network(fc_layer_size, dropout):
network = nn.Sequential( # 完全結合、単一の隠れ層
nn.Flatten(),
nn.Linear(784, fc_layer_size), nn.ReLU(),
nn.Dropout(dropout),
nn.Linear(fc_layer_size, 10),
nn.LogSoftmax(dim=1))
return network.to(device)
def build_optimizer(network, optimizer, learning_rate):
if optimizer == "sgd":
optimizer = optim.SGD(network.parameters(),
lr=learning_rate, momentum=0.9)
elif optimizer == "adam":
optimizer = optim.Adam(network.parameters(),
lr=learning_rate)
return optimizer
def train_epoch(network, loader, optimizer):
cumu_loss = 0
for _, (data, target) in enumerate(loader):
data, target = data.to(device), target.to(device)
optimizer.zero_grad()
# ➡ forward pass
loss = F.nll_loss(network(data), target)
cumu_loss += loss.item()
# ⬅ backward pass + 重みの更新
loss.backward()
optimizer.step()
wandb.log({"batch loss": loss.item()})
return cumu_loss / len(loader)
さあ、sweepを始めましょう! 🧹🧹🧹
Sweep Controllersは、wandb.sweep
を実行することで作成され、
誰かが試すための config
を要求するのを待っています。
その誰かとは agent
であり、 wandb.agent
を使用して作成されます。
始めるために、エージェントは次のことを知る必要があります:
- どのSweepの一部であるか(
sweep_id
) - 実行する関数はどれか(ここでは
train
) - (オプションで)Controllerにどれだけの
config
を要求するか(count
)
FYI、異なる計算リソースで同じ sweep_id
を使用して複数の agent
を開始することができ、
Controllerはそれらが sweep_config
に示された戦略に従って協力するようにします。
これにより、利用可能なノードを最大限に活用してSweepを簡単にスケールアップできます!
補足: コマンドラインでは、この関数は次のように置き換えられます
wandb agent sweep_id
以下のセルは、Sweep Controller から返されたランダムに生成されたハイパーパラメーター値を使用して、train
を5回実行する agent
を起動します。実行時間は5分未満です。
wandb.agent(sweep_id, train, count=5)
👀 Sweep結果を可視化する
🔀 パラレルコーディネートプロット
このプロットはハイパーパラメーターの値をモデルメトリクスにマップします。最も良いモデル性能を引き出したハイパーパラメーターの組み合わせを見つけるのに役立ちます。
📊 ハイパーパラメーターインポータンスプロット
ハイパーパラメーターインポータンスプロットは、メトリクスの最も良い予測因子であったハイパーパラメーターを明らかにします。 特徴重要度(ランダムフォレストモデルから)と相関性(暗黙の線形モデル)を報告します。
これらの可視化は、最も重要で、さらに探る価値のあるパラメーター(および値の範囲)に絞り込むことで、コストの高いハイパーパラメーター最適化を実行する時間とリソースを節約するのに役立ちます。
🧤 sweeps で実践
簡単なトレーニングスクリプトと いくつかのsweep設定の例 を用意しています。ぜひこれらを試してみてください。
このリポジトリには、Bayesian Hyperband、 Hyperopt など、より高度なsweep機能を試すための例も含まれています。