TadaoYamaokaの日記

山岡忠夫Homeで公開しているプログラムの開発ネタを中心に書いていきます。

ハースストーンのAIコンペ

不完全情報ゲームのAIの論文を調べていて、たまたまハースストーンのAIコンペがあることを知ったので、試したいこともあったのでさくっとMCTSのプログラムを作って提出してみた。

ハースストーンは、相手の手札や山札は見ることができない不完全情報ゲームで相手の隠れた情報を予測するなどしないと探索を行うことができないという難しさがある。
また、行動の結果が、確率的な場合がある。

去年のプログラム

AIコンペは、2018年から毎年開催されているようで、昨年の優勝プログラムのソースコードも確認できる。
さぞかしすごいプログラムなのかと思って確認してみたら、拍子抜けするくらい簡単なコードだった。

ハースストーンは自分のターン内でも複数回行動が必要なため、自分のターン内だけでも探索するだけでも有効である。
昨年の優勝プログラムは、自分のターン内だけをMin-Max探索(自分ターン内だけなので実際Maxのみ)して、終端ノードで評価関数で評価するだけという単純なプログラムだった。
確率的状態遷移も考慮していない。
評価関数もヒーローのHP差、ミニオンのHPと攻撃の差をカウントしただけのシンプルなものである。


2位のプログラムは、MCTSを使用して、自分のターン内だけ探索するプログラムである。
ゲームの終端までシミュレーションを行わず、自分のターン内終わりで評価関数で評価した値をバックアップしている。
1ターンの途中で評価を行うと誤差が大きいため、自分のターン内終端まではロールアウトポリシーを使用してロールアウトを行っている。
評価関数とロールアウトポリシーのパラメータ数は、上記の優勝プログラムよりは多く、作りこまれている感じがする。

試したこと

AIコンペに気付いたのが締め切りの2週間前だったので、2位のMCTSのコードをベースにして、評価関数を優勝プログラムのものを使用して、試したかった内容だけ追加した。

試したのは、以下の内容である。

  • 自分のターン内でカードを使用する順番が違うだけで同じノードになることが多いため、ハッシュマップでノードを合流できるようにする
  • 自分のターン内ツリーの再利用する

ハッシュマップを使用することで、確率的な遷移がある場合も複数回シミュレーションされることで、親ノードには期待値がバックアップされることになる。
また、ツリーの再利用も行いやすくなる。

ハッシュコードの生成

将棋や囲碁では、座標と駒の組み合わせにゾブリストハッシュを使用して、XOR演算でハッシュコードを差分を計算していくことができる。
ハースストーンでは、ミニオンにHPやエンチャントや状態異常ステータスなどがある。
ハースストーンで同じようにゾブリストハッシュを使用しようとすると、位置×ミニオン、位置×HPの値、位置×エンチャントの種類などにゾブリストハッシュを割り当てることになる。

しかし、コンペでは、ゲームのシミュレータがエージェントからブラックボックスの扱いになっているので、差分計算のようなことはできない。
また、カードの種類も事前には分かっていない。
そのため、カードIDの文字列、HPの値、位置×エンチャントの種類などから、動的にハッシュコードを生成することにした。

具体的には、C#の文字列のハッシュコードを生成する処理を参考に、

((hash1 << 5) + hash1) ^ 値1
((hash2 << 5) + hash2) ^ 値2
...
return hash1 + hash2 *  * 1566083941

という処理で64bitのハッシュコードを生成するようにした。
1566083941は、線形合同法のランダム生成で使われる値で、この値に適当な数を掛けると上位ビットがランダム性のある値になる。

手札については順番が入れ替わっても同じ状態としたかったため、それぞれのカードのハッシュコードを生成して、それらの和を手札のハッシュコードとした。
(テストしたところ、ハッシュの衝突が起きることがわかったので、HPなどの値には1566083941を掛ける、「((hash2 << 5) + hash1)」としてhash1とhash2を関連させるなどして衝突がおきにくくした。)


ハッシュコードの衝突が起きないように実装できたところで力尽きたので、評価関数はいじることなく提出した。
去年の優勝プログラムには3割くらいしか勝てていないのでたいして強くないが、ハッシュコードについての知識が増えたので良しとしよう。

提出したコード:
HearthstoneAICompetition/MyAgent.cs at master · TadaoYamaoka/HearthstoneAICompetition · GitHub

C#のオブジェクトをダンプする

C#のオブジェクトに格納されている値を調べたいときに、VS Codeなどのデバッガを使えば確認することができるが、オブジェクトの階層が深い場合は、テキストファイルにダンプして確認したくなる。

ObjectDumper.NET

方法がないか調べたところ、「ObjectDumper.NET」というツールが見つかった。
.NET Coreに対応しており、「dotnet add package ObjectDumper.NET」でインストールできる。

dotnet search ObjectDumper」でNuGetのパッケージを検索すると、他にも見つかるが、階層の制限や、除外するプロパティの設定ができるのは、ObjectDumper.NETだけだった。

privateフィールドのダンプ

ObjectDumper.NETは、publicなフィールドしかダンプできないため、privateフィールドもダンプしたい場合は、ソースの修正が必要になる。

GitHubからソースをクローンして、「ObjectDumper/Internal/ObjectDumperConsole.cs」を以下の通り編集する。

var publicFields = element.GetType().GetRuntimeFields().Where(f => !f.IsPrivate);

var publicFields = element.GetType().GetRuntimeFields()/*.Where(f => !f.IsPrivate)*/;

また、除外設定が、フィールドに対しては効かないので、直後に以下の行を追加する。

if (this.DumpOptions.ExcludeProperties != null && this.DumpOptions.ExcludeProperties.Any())
{
    publicFields = publicFields
        .Where(p => !this.DumpOptions.ExcludeProperties.Contains(p.Name))
        .ToList();
}

ソースを修正したら、以下のようにして.csprojにプロジェクトを参照するか

  <ItemGroup>
    <ProjectReference Include="../ObjectDumper/ObjectDumper/ObjectDumper.csproj" />
  </ItemGroup>

dotnet build -c Release」でビルドして、DLLをプロジェクトのディレクトリにコピーして.csprojにDLLの参照を追加する。

  <ItemGroup>
    <Reference Include="ObjectDumper"><HintPath>ObjectDumping.dll</HintPath></Reference>
  </ItemGroup>

使用例

var options = new DumpOptions();
options.MaxLevel = 5; // 5階層に制限
options.ExcludeProperties.add("ignoreField"); // 除外フィールド
var dump = ObjectDumper.Dump(obj, options);

ONNX Runtimeを使ってみる その3(DirectML)

ONNX RuntimeでDirectMLを使ってdlshogiのモデルの推論を行い、処理時間を比較してみた。

DirectMLを使えばAMDGPUでも推論が実行できるようになる。

DirectMLプロバイダの使用

NuGetからビルド済みバイナリが取得できる。
Microsoft.ML.OnnxRuntime.DirectMLをインストールする。

DirectMLプロバイダを使用するにはソースに以下の行を追加する。

#include <dml_provider_factory.h>

	session_options.DisableMemPattern();
	session_options.SetExecutionMode(ORT_SEQUENTIAL);
	OrtSessionOptionsAppendExecutionProvider_DML(session_options, 0);

DisableMemPattern()とSetExecutionMode(ORT_SEQUENTIAL)が必要である点は、
https://github.com/microsoft/onnxruntime/blob/master/docs/execution_providers/DirectML-ExecutionProvider.md
に記載されている。
この記述がないとSessionの初期化でエラーが発生する。

これで、ビルドは通るが実行にOrtSessionOptionsAppendExecutionProvider_DMLでメモリアクセス違反のエラーになった。

issuesを調べたところ同様のエラーの報告があり、対処方法が記載されていた。
https://github.com/microsoft/onnxruntime/issues/3360

ONNX Runtimeが使用するDirectML.dllは、Windows 10のシステムにインストールされているdirectml.dllと互換性がないため発生するようだ。
PATHに設定していても、システムフォルダが優先されるため、DirectML.dllを.exeのあるフォルダにコピーする必要がある。

onnxruntime.dllはちゃんと.exeのフォルダにコピーされるので、NuGetのパッケージの不備ではないかと思うが、「packages\Microsoft.ML.OnnxRuntime.DirectML.1.3.0\runtimes\win-x64\native\DirectML.dll」を手動でコピーした。
自動化するならVisual Studioのプロジェクトのビルド後の処理に設定するのが良いだろう。

処理時間測定

処理時間の測定結果は以下のようになった。
測定条件は、前回の記事と同じ。

比較のために、TensorRTとCPU(MKL-ML、スレッド数8)の結果も記載する。

条件 平均処理時間
DirectML 1.4秒
GPU(TensorRT) 0.3秒
CPU(MKL-ML、スレッド8) 31.3秒

TensorRTと比較すると、4.7倍遅くなっている。
TensorRTはTensorCoreを使いFP16で処理しているので、その違いがでていると思われる。

CPUと比較すると22.6倍速く、DirectMLを使用した方が速い。

まとめ

ONNX RuntimeのDirectMLプロバイダを使うことで、AMDGPUでも推論が動かせるようになる。
処理速度は、TensorRTには劣る(4.7倍遅い)が、CPUよりは断然に速い(22.6倍速い)。

ONNX Runtimeを使ってみる その2(性能測定)

先日、ONNX Runtimeを使って、MNISTの推論を試した。

今回は、dlshogiのResnet 10ブロック、192フィルタのモデルを使って、GPUで実行した場合の速度と、CPUで実行した場合の速度を比較した。

測定条件

GPUでのONNXの推論にはTensorRT(FP16)を使用する。
CPUの測定にはONNX Runtimeを使用し、デフォルトのCPUプロバイダと、MKL-MLを有効にしたCPUプロバイダ、DNNLプロバイダのそれぞれで測定した。

OSはWindows 10 64bit、GPUGeForce 2080Ti、CPUはCore i7-6700K(4コア、8スレッド、4GHz)を使用した。

推論の対象は、floodgateの棋譜からサンプリングした1万局面、バッチサイズは128とした。
それぞれの条件で、3回測定し、その平均時間を求める。

GPUで実行した場合の速度

条件 平均処理時間
GPU(TensorRT) 0.3秒

デフォルトCPUプロバイダ

NuGetでビルド済みバイナリが提供されているので、Microsoft.ML.OnnxRuntimeをインストールして使用した。

条件 平均処理時間
CPU(デフォルト) 32.6秒

GPUよりも、106.7倍遅い。

CPU使用率は、ほぼ100%で張り付いている。
f:id:TadaoYamaoka:20200606183918p:plain

なお、NuGetで取得できるCPUプロバイダはOpenMPが有効になっているので、スレッド数は自動で調整されている。

グラフ最適化のオプションを設定してみたが、変わらなかった。

session_options.SetGraphOptimizationLevel(ORT_ENABLE_ALL);

ORT_DISABLE_ALLにすると2倍くらい遅くなったため、デフォルトでグラフの最適化は有効になっているようだ。

MKL-MLを有効にしたCPUプロバイダ

NuGetから取得できるので、Microsoft.ML.OnnxRuntime.MKLMLをインストールして使用した。

条件 平均処理時間
CPU(MKL-ML) 37.5秒

デフォルトのCPUプロバイダより遅くなっている。

CPU使用率は、余裕が残っている。
f:id:TadaoYamaoka:20200606184158p:plain

CPUを使い切っていないので、スレッド数を明示的に設定してみた。

session_options.SetIntraOpNumThreads(8);

すると、CPU使用率は100%近くになった。
f:id:TadaoYamaoka:20200606184514p:plain

処理時間は、

条件 平均処理時間
CPU(MKL-ML、スレッド8) 31.3秒

となり、デフォルトCPUプロバイダより速くなった。

MKL-MLの場合は、スレッド数を調整した方が速くなる場合があるようだ。

なお、ONNX Runtimeのスレッド数の設定には、SetInterOpNumThreadsとSetIntraOpNumThreadsがあるが、前者はグラフ全体のスレッド数、後者はノード内の実行を並列化する際のスレッド数を設定する。
参考:https://github.com/microsoft/onnxruntime/issues/2177
SetInterOpNumThreadsの方も8に変えてみたが、処理時間に変化はなかった。

DNNLプロバイダ

ビルド済みバイナリが提供されていないため、ソースからビルドした。
スタートメニューから「x64 Native Tools Command Prompt for VS 2019」を起動して、GitHubのレポジトリをcloneして以下のようにビルドする。

git clone --recursive https://github.com/Microsoft/onnxruntime
cd onnxruntime
.\build.bat --config RelWithDebInfo --build_shared_lib --parallel --use_dnnl --cmake_generator "Visual Studio 16 2019"

「build\Windows\RelWithDebInfo\RelWithDebInfo」にビルド済みライブラリができる。

DNNLプロバイダを有効にするには、

#include <dnnl_provider_factory.h>

OrtSessionOptionsAppendExecutionProvider_Dnnl(session_options, 1);

のように、ソースコードでプロバイダを追加する処理が必要になる。

処理時間は、

条件 平均処理時間
CPU(DNNL) 35.0秒

となり、スレッド数を調整したMKL-MLよりも遅い。

CPU使用率は、ほぼ100%になっている。
f:id:TadaoYamaoka:20200606185652p:plain

まとめ

ONNX Runtimeを使用して、CPUでdlshogiのモデルの推論を行った。
GPUと比較した場合、100倍以上遅くdlshogiをCPUで実行しても、とりあえず動かせる程度で強さは期待できなそうだ。

CPUのプロバイダの比較では、MKL-MLを有効にしたプロバイダが一番速かった。
ただし、デフォルトの設定ではCPUを使い切らないため、スレッド数を調整する必要がある。


測定に使用したソースは以下の場所にある。
DeepLearningShogi/onnx_benchmark.cpp at feature/onnx_runtime · TadaoYamaoka/DeepLearningShogi · GitHub

C#からPythonを呼び出す

以前にC#からPythonディープラーニングフレームワークを呼び出すいくつかの方法を記事にした。

最後のlibTorchは、Pythonではないが、速度を出したい場合には選択肢になる。
しかし、モデルの定義をC++で実装する必要があり、メンテナンス性を考慮するとPythonからPyTorchを利用したい。

上記の他の方法は、機能が不足していたり、プロセス間通信のオーバーヘッドが大きすぎて速度がでなかったりで難がある。


最近、ハースストーンのAIの論文を読んでいたら、PythonNet(Python for .NET)で、C#からPythonを呼び出せることを知った。
てっきりPythonから.Netを利用するためだけのライブラリかと思ってスルーしていた。
公式ページの説明の下の方の「Embedding Python」を読めば、.NetからPythonを利用する方法について書かれていた。

メリット

PythonNetは、.NetによるPython実装ではなく、インストールされているPythonそのものが.Netのプロセスで実行される。
そのため、インストールされているPythonで動くものはすべて動くので互換性や機能不足で問題が起きない。

また、プロセス間通信がないため、速度面のボトルネックが少ない。
データのマーシャリングを自動で行ってくれるため、C#の変数をそのままPythonに渡せて、Pythonから結果をC#のオブジェクトとして受け取ることができる。

ということで、とても扱いやすいライブラリであることが分かった。


いろいろ試してみて、WindowsでもLinuxでも.Net Core 2.1から利用することができたので、インストール方法と使い方を記しておく。

インストール方法は、公式のWikiに記載があるが記述が少なくわかりにくかった。

Python環境構築

Python環境はすでに利用しているものが利用できる。
対応しているPythonのバージョンは、3.7なので3.8の場合は新たにPython環境の構築が必要になる。

Anacondaの場合は、アーカイブからAnaconda3-2019.10をインストールする。

PythonNetインストール

Windowsの場合

Windowsの場合、ビルド済みパッケージが利用できる。

.Net CoreのC#プロジェクトのディレクトリで、

dotnet add package Python.Runtime.NETStandard

を実行するだけでよい。

環境変数PATHにPythonをインストールしたディレクトリ(python37.dllのあるディレクトリ)を追加する。
Anaconda3のインストール時にPATHを設定している場合は不要。

Linuxの場合

Linuxの場合、ビルドが必要になる。
GitHubからリポジトリをクローンする。

git clone https://github.com/pythonnet/pythonnet

clangが必要なため、インストールされてない場合はインストールする。

sudo apt install clang

setup.pyでwheelを作成するとビルドされる。

python setup.py bdist_wheel --xplat

※「--xplat」オプションは.Net Coreから利用する場合に必要

Pythonから.Netを利用する場合は、ビルドされたwheelをインストールするが、.NetからPythonを利用する場合は必要ない。

build/lib.linux-x86_64-3.7/netcoreapp2.0
にビルドされたPython.Runtime.dllを使用する。

Python.Runtime.dllをC#プロジェクトのディレクトリにコピーして、.csprojに以下を追記する。

  <ItemGroup>
          <Reference Include="Python.Runtime"><HintPath>Python.Runtime.dll</HintPath></Reference>
  </ItemGroup>

環境変数LD_LIBRARY_PATHを設定する。

export LD_LIBRARY_PATH=/home/xxx/anaconda3/lib:$LD_LIBRARY_PATH

Pythonのインストール場所に合わせる。

サンプルコード

以下は、Python環境にインストールされたPyTorchをインポートしてバージョンを表示するC#コードの例である。
WindowsでもLinuxsでも同じ。

using System;
using System.IO;
using Python.Runtime;

namespace pythonnettest
{
    class Program
    {
        static void Main(string[] args)
        {
            var PYTHON_HOME = Environment.ExpandEnvironmentVariables(@"C:\Anaconda3");
            PythonEngine.PythonHome = PYTHON_HOME;

            using (Py.GIL())
            {
                dynamic torch = Py.Import("torch");
                dynamic version = torch.__version__;
                Console.WriteLine(version);
            }
        }
    }
}

PYTHON_HOMEは、Pythonをインストールしたディレクトリに合わせる。

実行結果:

1.3.1

まとめ

PythonNetを使うと、C#からPython経由でディープラーニングフレームワークが簡単に利用できる。
インストールしているPython環境がそのまま利用でき、プロセス間通信のボトルネックがないので、これ以上よい方法はないのではないかと思う。

今回.Net Coreから利用したが、.Net Frameworkやmonoにも対応しているので、試していないがUnityから利用とかも可能だと思う。

dlshogiの学習則

dlshogiを改造して遊びたい方のために、dlshogiの学習則についてちゃんと書いてなかったので書いておく。

主に、学習部のソースtrain_rl_policy_with_value_using_hcpe_bootstrap.pyの解説になっている。

AlphaZeroの学習則

AlphaZeroの学習則は、
\displaystyle
l(\theta)=(z-v)^2 - \boldsymbol{\pi}^T \log \boldsymbol{p} + c \|\theta\|^2
となっている。
dlshogiではこれとは異なる学習則を使用している。

ルートノードの分布の使用有無

まず、AlphaZeroでは、MCTSのシミュレーション結果のルートの子ノードの訪問回数の分布(上式の\boldsymbol{\pi})を使用しているが、dlshogiでは分布ではなく指し手を使用している。
これは、学習に分布を使用すると学習データのサイズが大幅に増えてしまうので、データサイズを抑えたいという理由による。

Actor-Critic

dlshogiでは、方策をActor-Criticで学習している。

一般的な方策勾配法では、目的関数の勾配に報酬zを使用する。
\displaystyle
\nabla_\theta J(\theta) = \mathbb{E}[\nabla_\theta \log \pi_\theta(a|s)z_t^m]
Actor-Criticでは、分散を低下させる(過剰適合を避ける)ために、ベースラインとして価値関数の値を報酬zから引く。
\displaystyle
\nabla_\theta J(\theta) = \mathbb{E}[\nabla_\theta \log \pi_\theta(a|s)(z_t^m - V^\pi(s))]
ベースラインを追加しても、\nabla_\theta J(\theta)の期待値自体は変わらないが、推定分散を小さくする効果がある。
z_t^m - V^\pi(s)の項はアドバンテージと呼ばれる。


なお、期待値が変わらないというのは、期待値を行動aの積分で表すと、以下の式展開で確かめられる。
\displaystyle
\int \nabla_\theta \log \pi(a) \pi(a) da = \int \nabla_\theta \pi(a) da = \nabla_\theta \int \pi(a) da = 0
式展開では微分の連鎖則を利用している。
 \displaystyle
\begin{eqnarray}
\frac{\partial}{\partial x} \log f(x) &=& \frac{\partial \log f(x)}{\partial f(x)} \frac{\partial f(x)}{\partial x} \\
&=& \frac{1}{f(x)} \frac{\partial f(x)}{\partial x}
\end{eqnarray}


dlshogiでは、この式をそのまま適用してもうまく学習ができなかった。
そこで、ベースラインにさらに0.5を加えている。
\displaystyle
\nabla_\theta J(\theta) = \mathbb{E}[\nabla_\theta \log \pi_\theta(a|s)(z_t^m - V^\pi(s) + 0.5)]
この意味は、負けた対局でも探索した指し手はある程度信用できるので、半分は信用してあげようという気持ちである。
特に理論的な背景があるわけではない。


参考までに、OpenAIのbaselinesのPPOの実装では、アドバンテージの正規化を行っている。
 \displaystyle
\mathcal{A}_{normalized} = \frac{\mathcal{A} - \bar{\mathcal{A}}}{\sigma(\mathcal{A}) + \epsilon}
なぜ、正規化が必要かについて不思議だったので、ネットで検索したらいくつかの議論が見つかった。

引用されていた資料(4.1.2 Advantage Normalization)によると、推定量の分散を小さくすることにより、通常、経験的パフォーマンスを向上させることが知られているトリックだそうだ。
dlshogiの学習でも効果があるかもしれない。

エントロピー正則化

dlshogiの方策は指し手を学習しているため、方策が決定論的に偏りやすい。
そのため、A3Cでも行われているエントロピー正則化を行っている。

方策pエントロピーは以下の式で与えられる。
\displaystyle
H(p) = -\sum_j p_j \log p_j

エントロピーは、方策の分布が均一の場合に最大になり、どれか一つの行動の確率が1(決定論的)になったときに最小となる。
-\beta H(p)正則化項として損失に加えることで、決定論的になる方向にペナルティが加えられる。

価値関数の学習

AlphaZeroでは、価値の損失には平均二乗誤差(MSE)を使用している。
dlshogiでは、方策の損失が交差エントロピーなので、価値の損失もエントロピーの方が自然と考えて、シグモイド交差エントロピーを使用している。
価値関数の出力の範囲は、勝率を表す[0,1]となっている(AlphaZeroでは、勝ちを1、負けを-1として、出力範囲は[-1,1]となっている)。

価値の学習のブートストラップ

強化学習では、報酬そのものを学習する代わりに、Nステップ後の推定収益を学習することで、学習を加速するということが行われる。
この手法は、ブートストラップと呼ばれる。
なお、ブートストラップとは、OSのブートとかでも使われている語であるが、語源は靴のかかとについているタグのことだそうだ。タグをつまむと靴が履きやすくなることのアナロジーである。

コンピュータ将棋では、elmoが報酬のシグモイド交差エントロピーと、深さNの評価関数の出力とルートの評価関数の2つの確率変数の交差エントロピーの加重平均を損失にするという手法を導入して成功している。

dlshogiもこれに倣い、MCTSシミュレーションを行った結果のルートノードの指し手のQ値と、ルートノードの状態価値の2つの確率変数の交差エントロピーを価値の損失に加え、報酬の損失と按分している。
\displaystyle
l_{value}(\theta)= (1 - \lambda) H(v, z) + \lambda H(v, q)

正則化

AlphaZeroでは、損失にL2正則化c \|\theta\|^2を加えている。
dlshogiも同様にL2正則化を行っている(実装上はPyTorchのWeightDecayを使っている)。

SWA

SWA(Stochastic Weight Averaging)は、一定間隔での重みを平均化することで、ニューラルネットワークのテスト精度を改善するテクニックである。
一般的なアンサンブルの手法では予測の結果を平均化するが、SWAでは重みを平均化することで実現する。
Leela Chess ZeroでもSWAを導入している。
dlshogiでも効果があったため導入した。

学習則まとめ

以上の学習則を式で表すと以下のようになる。

\displaystyle
l(\theta)=- \boldsymbol{\pi}^T \log \boldsymbol{p} (z - v + 0.5) - \beta H(p) + (1 - \lambda) H(v, z) + \lambda H(v, q) + c \|\theta\|^2

\boldsymbol{\pi}は指し手のone-hotベクトルを表す。

Agent57: Outperforming the Atari Human Benchmarkを読む その15

付録H. 実験結果

H.1. Atari 10:アブレーションのスコア表

f:id:TadaoYamaoka:20200527091029p:plain

H.2. Backpropウィンドウの長さの比較

図12. 難易度の高い10ゲームのセットでの、Backpropウィンドウの長さが短い場合と長い場合のパフォーマンスの比較

f:id:TadaoYamaoka:20200527091407p:plain

H.3. アイデンティティとh変換の組み合わせの比較

図13. 挑戦的な10ゲームのセットでのアイデンティティとh変換ミックスのパフォーマンス比較

f:id:TadaoYamaoka:20200527092158p:plain

  • 図H.3に示すように、アイデンティティまたはh変換ミックスを選択しても、パフォーマンスの点で違いはないようである。
  • 唯一重要なことは、外発的と内発的の組み合わせが線形かどうかに関係なく発生することである。
  • さらに、βの極値\beta = 0, \beta>>1)の場合、量Q^e_k(x, a) + \beta Q^i_k(x, a)h^{-1}(Q^e_k(x, a)) + \beta h^{-1}(Q^i_k(x, a))は同じargmax_{a\in\mathcal{A}}になることに注意してほしい。 これは、h^{-1}が厳密に増加しているためである。
  • したがって、これは、βの極値で、変換と通常の値の反復スキームが同じ方策に向かって収束することを意味する。
  • βの値の間では、これは当てはまらない。
  • しかし、変換演算子アイデンティティミックスが使用される場合、価値反復スキームは、内発的報酬と外発的報酬r^i, r^e非線形の組み合わせに関してそれぞれ最適な状態行動価値関数を近似すると推測できる。

H.4. Atari 57のスコア表

f:id:TadaoYamaoka:20200527093243p:plain
f:id:TadaoYamaoka:20200527093309p:plain

H.5. Atari 57の学習曲線

図14. Atari57でのAgent57の学習曲線

f:id:TadaoYamaoka:20200527093359p:plain

H.6. ビデオ

  • https://sites.google.com/corp/view/agent57にいくつかの動画を掲載している。
  • 以下を示している。
    • 57のすべてのゲームでのAgent57:Agent57が人間のベースラインを超えるAtari 57スイープの各ゲームのサンプルビデオを提供する。
    • 状態行動価値関数のパラメーター化:価値関数のパラメーター化の重要性を示すために、アイスホッケーとサラウンドの2つのゲームのビデオを示す。 NGUとAgent57の両方の活用的および探索的方策のビデオを示す。 アイスホッケーでは、探索的および活用的な方策は非常に異なるスコアを達成している。 具体的には、探索的な方策はゴールを目指すのではなく、新しい構成を探索しながらコートの周りを移動することを好む。 一方、単一のアーキテクチャのNGUは両方の方策を同時に学習することはできないが、Agent57は非常に多様なパフォーマンスを示す。 サラウンドの場合NGUはまたしても学ぶことができない。 探索的方策は、観察の多様性を新たに増大させるために、新たに開始するためにポイントを失うことを選択したと推測する。 Agent57はこの問題を克服でき、活用的方策と探索的方策の両方で、人間のベースラインを超えるスコアを取得できる。
    • 適応的割引率:ゲームJames BondR2D2(bandit)とR2D2(retrace)のビデオ例を示す。 R2D2(retrace)は、30,000ポイント程度の最終スコアでゲームをクリアすることを学ぶ。 対照的に、R2D2(bandit)は、ゲームの終了を遅らせて、スコアが約140,000ポイントで、大幅に多くの報酬を集めることを学ぶ。 これを実現するために、メタコントローラーの適応メカニズムは、非常に高い割引率を持つ方策を選択する。
    • Backprop Through Timeのウィンドウサイズ:SolarisのゲームでのNGUおよびAgent57のエピソードの例を示すビデオを提供する。 ハイスコアを達成するために、エージェントはグリッド画面内を移動して敵を探すことを学ぶ必要がある。 エージェントはグリッド画面で実行されたアクションを後で多くのタイムステップで達成された報酬にバインドする必要があるため、これは長期的なクレジット割り当ての問題である。