S4 で始める強化学習(第6回)エージェントシミュレーションで作ってみる(2/2)

  • HOME
  • S4 で始める強化学習(第6回)エージェントシミュレーションで作ってみる(2/2)

本記事は当社が発行しているシミュレーションメールマガジンVol. 12の記事です。 シミュレーションメールマガジンの詳細・購読申込はこちらのサポートページから

はじめに

S4 で強化学習を使ったシミュレーションモデルを作成してみようという講座の6回目の記事になります。

前回は、崖歩き問題についてモデルの作成方法を途中まで解説しました。具体的には、作成手順を3ステップに分けたうちの、ステップ1「エージェントが格子グラフ上でランダムウォークするシミュレーションモデルの作成」までを行いました。今回はステップ2の「強化学習モデルの追加」から始めて、モデルの完成までを解説します。

ステップ2:強化学習モデルの追加

それでは早速、強化学習モデルを作成してみましょう。S4 をお持ちの方で作成手順を体験したい方は、こちらからプロジェクトファイルをダウンロードして、崖歩き_STEP1.s4というファイルをインポートしてください。

まず、左のブラウザから「エージェント用強化学習モデル」を右のエディタへドラッグ&ドロップします。そして強化学習モデルの中身を設定していきます。第2回で説明した離散イベント用強化学習モデルのときと同様に、次の3点を設定する必要があります。

  • エージェントが何を観測するか
  • エージェントがどのような行動を取るか
  • エージェントに与える即時報酬

今回は「毎秒エージェントは現在位置の座標を観測し、上下左右のいずれかのマスに移動するという行動を取り、(行動前の)現在位置に合わせて問題設定通りの即時報酬を受け取る」と設定します。

まず、観測と行動について設定します。「エージェント用強化学習モデル」をダブルクリックして開き、属性設定タブの「観測値集合の次元」で追加ボタンを2回押して、x、y という名前の変数を定義します。6 $\times$ 3 の格子グラフのサイズに合わせて、それぞれの変数の型を整数、x の最小値と最大値を0と5、y の最小値と最大値を0と2に設定します。次に「行動値集合の次元」で追加ボタンを1回押して、d という名前の変数を定義します。上下左右に動くということに対応して、d の最小値と最大値を0と3に設定します。そして「観測値計算関数」を開いて、次のコードを入力します。

def observation(self, simulator, agent): # 以下のコードを入力
    v = agent.getPosition()
    x, y = agent.agentset.env.layout[v]
    ix, iy = agent.agentset.env.coordIndex(x, y)
    return {"x": ix, "y": iy}

エージェントの現在位置の座標を取得して、先ほど設定した変数名 "x"、"y" をキー、x 座標、y 座標を値とする辞書を戻り値として返しています。ここで環境(env)の coordIndex メソッドは、前回作成した自作メソッドです。これ以降、自作メソッドがよく出てくるので、忘れてしまった方は一度復習しておきましょう。以上で現在位置の座標をエージェントが観測し、4種類の行動を取ると設定できました。

次に即時報酬の設定をします。属性設定タブの「即時報酬値計算関数」を開いて、次のコードを入力します。

def reward(self, simulator, agent, prev_time): # 以下のコードを入力
    v = agent.getPosition()
    type = agent.agentset.env.nodeType(v)
    if type == "ground":
        reward = -1
    elif type == "cliff":
        reward = -100
    elif type == "goal":
        reward = 10
    agent.rewardSum += reward
    return reward

エージェントの現在位置をもとに問題設定通りの即時報酬値を求めて、それを戻り値として返しています。ステップ1でランダムウォークモデルを作成したときには、これらの計算は「同期エージェント」部品の「エージェントのステップ処理」という設定項目で行っていました。強化学習用モデルを追加するにあたって、これらの計算を「即時報酬値計算関数」に移しています。

次に「同期エージェント」部品に「エージェント用強化学習モデル」を組み込みます。「同期エージェント」をダブルクリックして開き、属性設定タブの「エージェントのステップ処理」において、「強化学習を使用する」にチェックを入れます。そして強化学習モデルとして「エージェント用強化学習モデル」を選択し、「編集」を押してコードを以下のとおり変更します。

def step(self):
    id1 = ''
    id2 = self.agentset.name + ' - ' + str(self.agentid)
    act = self.rlmodel.getAction(id1, id2, self)

    # 以下のコードを入力
    v = self.getPosition()
    env = self.agentset.env
    if env.nodeType(v) == "goal": # ゴールした場合はエピソードの終了処理を行う
        self.agentset.episode += 1
        self.agentset.monitor.observe(now(), self.agentset.episode, self.rewardSum)
        self.agentset.remove(self)
        # 次のエージェント(= エピソード)作成
        self.agentset.generateAgents(1, **self.agentset.keys)
    elif env.nodeType(v) == "cliff": # 崖に落ちた場合はスタート地点に戻る
        self.setPosition(env.getStartNode())
    else:
        n = env.getNextNode(v, act['d'])
        if n == None: # 有効な方向ではなかったとき
            d = next(sample(env.getValidDs(v))) # 有効な方向の中から一様な確率で選択
            n = env.getNextNode(v, d)
            act['d'] = d
        self.setPosition(n)
    # 入力ここまで

    self.rlmodel.setActualAction(id1, id2, act)

変更点は以下の通りです。まず先ほど説明した通り、報酬値を計算していたコードを取り除いています。そして else 以下のコードを、強化学習モデルの選択した行動を反映するように変更しています。ここで強化学習モデルの選択した行動は act['d'] により取り出すことができます。ただし強化学習モデルは格子モデルの構造を知りません(知っているのは毎時刻の観測値と行動値、即時報酬値だけです)。そのため現在位置に依らず0から3までの4通りの行動を出力し得るので、このままでは格子グラフの端にいるときに有効でない行動が選択されることがあります。このときは有効な行動の中から一様な確率で行動を選択し直して、それを act['d'] に再設定しています。一番最後の行で self.rlmodel.setActualAction(id1, id2, act) を実行することにより、行動の選択を修正したことを強化学習モデルに知らせています。

最後に学習モデルのパラメータを設定します。「エージェント用強化学習モデル」をダブルクリックして開き、即時報酬値の割引率を1に、$\varepsilon$ を0.1に、学習率を0.1に設定します。ここで即時報酬値の割引率を1としているのは、「(割引なしの)即時報酬の和を最大化するような行動を学習する」という問題設定に合わせるためです。

設定は以上です。メニューからモデルを開始するボタンを押すと、第4回の記事でお見せしたものと同様の結果が得られるかと思います。

ステップ3:細かい機能の追加

ステップ2までで必要最小限の機能を実装することができました。 離散イベントシミュレーションのときと比べてコード量が多くて大変だったかもしれませんが、ここまで来れば安心して細かい機能を追加することができます。完成形のプロジェクトでは、さらに以下の2つの機能を実装しています(完成形のプロジェクトはこちら)。

  • 格子サイズのパラメータ化
  • 可視化用の分析スクリプトの作成

これらについて簡単に解説します。詳しい実装についてはサンプルプロジェクトを参照ください。

S4 においてパラメータを導入するには、メニューバーのPマーク「パラメータを編集する」から「パラメータ」タブを開きます。今回はここに width と height という変数を設定しています。width と height というパラメータを導入したら、これまでの設定において格子サイズに依存していたところを全て width と height を使った形に修正します。「パラメータ」タブで設定した変数はシミュレーターオブジェクトからアクセスできます。例えば、環境オブジェクトからは self.simulator.param.width という形でアクセスします[1]

可視化用の分析スクリプトは、メニューバーのAマーク「分析スクリプトを編集する」から設定できます。「行動価値」という名前のモニタから学習結果の行動価値関数の値を読み取り、Matplotlibを使って描画を行っています。

  • 格子グラフ部品の設定画面で、格子グラフの幅と高さを設定するところだけは例外で、keys["simulator"].params.width などという形で指定しています。これは S4 の実装上の問題で、格子グラフオブジェクトの初期化時に、シミュレーターオブジェクトが初期化されていないためです。

今回までで、崖歩き問題の基本的な性質から S4 で実装するところまでを解説しました。 次回、強化学習の理解を深めるための話題として、Actor-Criticという行動のバラつきを考慮した学習方法を紹介し、Q学習のときと学習結果に差があることを見ます。

監修:株式会社NTTデータ数理システム 機械学習、統計解析、数理計画、シミュレーションなどの数理科学を 背景とした技術を活用し、業種・テーマを問わず幅広く仕事をしています。
http://www.msi.co.jp NTTデータ数理システムができること
「数理科学の基礎知識」e-book無料ダウンロードはこちら

関連記事