Skip to main content

W&Bを使用したKerasチューナー

wandbとkeras-tunerの統合
Created on February 11|Last edited on February 16
このレポートは、Aritra Roy Gosthipatyによる「Keras-Tuner with W&B」の翻訳です。



概要

Kaggle Notebookを確認

人工ニューラルネットワークは、これまでの多くの制約条件、ウエイトおよびバイアスで構成されています。このような制約条件、つまりニューロン数や非線形活性化関数、レイヤー数は、一般的に「ハイパーパラメータ」と呼ばれます。このハイパーパラメータの最適化について広大な研究が行われています。これは人々が、ウエイトとバイアスだけでなく、ハイパーパラメータのノブを回すことにも興味があることを意味します。すでに、この分野には、いくつかの優れたアプローチ(グリッド、ランダム、ベイジアンなど)があります。

深層学習の実験には、適切なハイパーパラメータの選択に多くの時間が費やされています。適切なハイパーパラメータの選択は、実験のゲームチェンジャーになる場合があります。このトピックは広く研究されています。そしてさまざまな検索アルゴリズムの出現によってハイパーパラメータが自動チューニングできるようになっています。ハイパーパラメータのスペースを自動検索でチューニングするという概念は、手動でそれを行っていたDL研究者の時間短縮に役立ちます。

この記事では、ハイパーパラメータ・チューニングの自動化に役立つツールの1つ「keras-tuner」について説明します。ツールの基本を理解するだけでなく、私たちがよく使っているお気に入りの実験トラッカー「wandb」と統合してみます。

以下について説明します。

  • keras-tunerのAPI
  • wandbの統合コード
  • スイープの一部の紹介
  • 終わりに


keras-tunerのAPI

kerasチームは、ツールのAPIデザインに常に多大な労力を費やしています。そのツールは、同様の思考プロセスから逸脱することはありません。

このAPIが提供する基本インターフェースは4つあり、APIの最も重要な部分となっています。

  1. HyperParameters: このクラスはハイパーパラメータのコンテナとして機能します。このクラスのインスタンスには、現在のハイパーパラメータおよび検索スペースの合計に関する情報が含まれています。
  2. Hypermodel: このクラスのインスタンスは、ハイパーパラメータのスペース全体をモデル化するオブジェクトと考えることができます。このインスタンスは、スペースを構築するだけでなく、ハイパーパラメータからサンプリングするDLモデルも構築します。
  3. Oracles: このクラスの各インスタンスは、特定のハイパーパラメータ・チューニングのアルゴリズムを実装しています。
  4. Tuners: ハイパーパラメータのチューニングを行います。Oracleは、Tunerに引数として渡されます。渡されたOracleは、どのハイパーパラメータを次に試行する必要があるかをTunerに通知します。

API設計へのトップダウンアプローチにより、APIを読みやすく、理解しやすくできます。次のようにすべてを繰り返します:

  • HyperParametersオブジェクトを作成する。

  • HyperParametersをHypermodelに渡す。Hypermodelは検索スペースを構築できる。

  • チューニングアルゴリズムを提供するOraclesを構築する。

  • Oraclesに従ってハイパーパラメータをチューニングするTunersを構築する。



keras-tunerを使用したコード

このセクションでは、keras-tunerの基本的な使用法を例で説明します。この例は、こちらのドキュメントから抜粋したものです。

チューナーを実行するために必要なインポートとは別に、最初に検索スペース全体をエミュレートするHypermodelを構築する必要があります。

ハイパーモデルは2つの方法で構築できます。

  1. 関数によるモデル構築
  2. Hypermodelクラスのサブクラス

関数

ここでは、HyperParametersを引数として取る関数を作成します。関数はHyperParametersからサンプリングし、モデルを構築して返します。このようにして、検索スペースからさまざまなモデルが作成されます

# build with function
def build_model(hp):
  model = keras.Sequential()
  model.add(layers.Dense(units=hp.Int('units',
                                      min_value=32,
                                      max_value=512,
                                      step=32),
                         activation='relu'))
  model.add(layers.Dense(10, activation='softmax'))
  model.compile(
      optimizer=keras.optimizers.Adam(
          hp.Choice('learning_rate',
                    values=[1e-2, 1e-3, 1e-4])),
          loss='sparse_categorical_crossentropy',
          metrics=['accuracy'])
  return model

Hypermodelクラスのサブクラス化

このメソッドは、build()メソッドをオーバーライドする必要があります。build()メソッドで、ユーザーはHyperParametersからサンプリングして、適切なモデルを構築できます。

# build with inheritance
class MyHyperModel(HyperModel):

  def __init__(self, num_classes):
    self.num_classes = num_classes

  def build(self, hp):
    model = keras.Sequential()
    model.add(layers.Dense(units=hp.Int('units',
                                        min_value=32,
                                        max_value=512,
                                        step=32),
                           activation='relu'))
    model.add(layers.Dense(self.num_classes, activation='softmax'))
    model.compile(
        optimizer=keras.optimizers.Adam(
            hp.Choice('learning_rate',
                      values=[1e-2, 1e-3, 1e-4])),
            loss='sparse_categorical_crossentropy',
            metrics=['accuracy'])
    return model

どちらの場合も、HypermodelはHyperParametersの提供で作成されます。興味のある方は、ハイパーパラメータがサンプリングされる方法を調べることをお勧めします。このパッケージは、静的オプションだけでなく、条件付きハイパーパラメータも提供します。

Hypermodelの準備ができたら、Tunerを作成します。Tunerはハイパーパラメータスペースを検索し、最適化されたハイパーパラメータのセットを提供します。以下に、両方のHypermodel設定のチューナーを記述しました。

# tuner for function
tuner = RandomSearch(
    build_model,
    objective='val_accuracy',
    max_trials=5,
    executions_per_trial=3,
    directory='my_dir',
    project_name='helloworld')

# tuner for subclass
hypermodel = MyHyperModel(num_classes=10)
tuner = RandomSearch(
    hypermodel,
    objective='val_accuracy',
    max_trials=10,
    directory='my_dir',
    project_name='helloworld')

注:カスタムTunerでは、検索アルゴリズムでチューナーを支援する Oracleをチューナーに渡す必要があります。 すべて設定されたら、検索を実行できます。search 方法は、fit メソッドと同じデザインに従います。search後、チューナーに最適なモデルとハイパーパラメータを照会できます。

tuner.search(x, y,
						 epochs=5,
             validation_data=(val_x, val_y))


keras-tunerをwandbと統合するコード

Kaggle Notebookを確認

keras-tunerと一緒にすべてのモデルを1か所で追跡できれば、どれほど素晴らしいでしょう。ここでは、wandbをkeras-tunerと統合して、作成および検索されたすべてのモデルを追跡できるようにします。これによって、最良のモデルを取得するのに役立つだけでなく、価値あるインサイトも得られます。

このセクションでは、変更したkeras-tinerサブクラス化のコードを実行します。

Hypermodel

ここでは、Hypermodelを構築するにあたって機能的な方法を採用しています。これは、モデルを構築するための非常に簡単な方法として機能します。

この例では、条件付きハイパーパラメータの使用法が実装されていることがわかります。調整可能な数のconv_layersを作成するforループがあり、それ自体が調整可能なfilterskernel_sizeパラメーターを含みます。

def build_model(hp):
    """
    Builds a convolutional model.
    
    Args:
      hp: Hyperparamet object, This is the object that helps
        us sample hyperparameter for a particular trial.
    
    Returns:
      model: Keras model, Returns a keras model.
    """
    inputs = tf.keras.Input(shape=(28, 28, 1))
    x = inputs
    # In this example we also get to look at
    # conditional heyperparameter settings.
    # Here the `kernel_size` is conditioned
    # with the for loop counter. 
    for i in range(hp.Int('conv_layers', 1, 3)):
      x = tf.keras.layers.Conv2D(
          filters=hp.Int('filters_' + str(i), 4, 32, step=4, default=8),
          kernel_size=hp.Int('kernel_size_' + str(i), 3, 5),
          activation='relu',
          padding='same')(x)
      # choosing between max pool and avg pool
      if hp.Choice('pooling' + str(i), ['max', 'avg']) == 'max':
        x = tf.keras.layers.MaxPooling2D()(x)
      else:
        x = tf.keras.layers.AveragePooling2D()(x)
      x = tf.keras.layers.BatchNormalization()(x)
      x = tf.keras.layers.ReLU()(x)

    if hp.Choice('global_pooling', ['max', 'avg']) == 'max':
      x = tf.keras.layers.GlobalMaxPooling2D()(x)
    else:
      x = tf.keras.layers.GlobalAveragePooling2D()(x)
    outputs = tf.keras.layers.Dense(10, activation='softmax')(x)
    
    model = tf.keras.Model(inputs, outputs)
    return model

Tuner

wandbでチューナーを統合してconfig とlossをログに記録するのは、簡単でした。APIは、kt.Tunerクラスのrun_trialメソッドをオーバーライドするユーザーを提供します。run_trialメソッドでは、HyperParametersオブジェクトを利用できます。これは、wandb実行のコンフィギュレーションに現在のハイパーパラメータをクエリするために使用されます。これは、モデルのメトリックをログに記録できるようになっただけでなく、wandbがダッシュボードに提供している優れたウィジェットを使用してハイパーパラメータを比較できることを意味します。

class MyTuner(kt.Tuner):
  """
  Custom Tuner subclassed from `kt.Tuner`
  """
  def run_trial(self, trial, train_ds):
    """
    The overridden `run_trial` function

    Args:
      trial: The trial object that holds information for the
        current trial.
      train_ds: The training data.
    """
    hp = trial.hyperparameters
    # Batching the data
    train_ds = train_ds.batch(
        hp.Int('batch_size', 32, 128, step=32, default=64))
    # The models that are created
    model = self.hypermodel.build(trial.hyperparameters)
    # Learning rate for the optimizer
    lr = hp.Float('learning_rate', 1e-4, 1e-2, sampling='log', default=1e-3)

    if hp.Choice('optimizer', ['adam', 'sgd']) == 'adam':
      optimizer = tf.keras.optimizers.Adam(lr)
    else:
      optimizer = tf.keras.optimizers.SGD(lr)

    epoch_loss_metric = tf.keras.metrics.Mean()

    # build the train_step
    @tf.function
    def run_train_step(data):
      """
      The run step

      Args:
        data: the data that needs to be fit
      
      Returns:
        loss: Returns the loss for the present batch
      """
      images = tf.dtypes.cast(data['image'], 'float32') / 255.
      labels = data['label']
      with tf.GradientTape() as tape:
        logits = model(images)
        loss = tf.keras.losses.sparse_categorical_crossentropy(
            labels, logits)
      gradients = tape.gradient(loss, model.trainable_variables)
      optimizer.apply_gradients(zip(gradients, model.trainable_variables))
      epoch_loss_metric.update_state(loss)
      return loss
    
    # WANDB INITIALIZATION
    # Here we pass the configuration so that
    # the runs are tagged with the hyperparams
    # This also directly means that we can
    # use the different comparison UI widgets in the 
    # wandb dashboard off the shelf.
    run = wandb.init(entity='ariG23498', project='keras-tuner', config=hp.values)
    for epoch in range(10):
      self.on_epoch_begin(trial, model, epoch, logs={})
      for batch, data in enumerate(train_ds):
        self.on_batch_begin(trial, model, batch, logs={})
        batch_loss = run_train_step(data)
        self.on_batch_end(trial, model, batch, logs={'loss': batch_loss})   
        if batch % 100 == 0:
          loss = epoch_loss_metric.result().numpy()
          # Log the batch loss for WANDB
          run.log({f'e{epoch}_batch_loss':loss})
      
      # Epoch loss logic
      epoch_loss = epoch_loss_metric.result().numpy()
      # Log the epoch loss for WANDB
      run.log({'epoch_loss':epoch_loss, 'epoch':epoch})
      
      # `on_epoch_end` has to be called so that 
      # we can send the logs to the `oracle` which handles the
      # tuning.
      self.on_epoch_end(trial, model, epoch, logs={'loss': epoch_loss})
      epoch_loss_metric.reset_states()
    
    # Finish the wandb run
    run.finish()



Run set
10


終わりに

読者のみなさんに、ご自身で素晴らしいツールを今すぐお試しいただくことをお勧めします。今後の参考のためにkeras-tunerのす

ハイパーパラメータ・チューニングの研究は非常に広範囲に行われています。人々は遺伝的アルゴリズムも取り入れ、生物に似たモデルを進化させるコンセプトを使用しました。少し厚かましい事ですが、これに興味を持った読者のみなさんは、私の記事のひとつである遺伝的アルゴリズムを使用したハイパーパラメータ調整の概念の分析を、このリンクからご覧いただければ幸いです。

ご連絡はTwitter @ariG23498にお願いいたします。


Iterate on AI agents and models faster. Try Weights & Biases today.