ゆるい鳥の教育:強化学習チュートリアル

公開: 2022-03-11

従来のプログラミングでは、ソフトウェア命令はプログラマーによって明示的に作成され、データからは何も学習されません。 対照的に、機械学習は、統計的手法を使用して、コンピューターが明示的にプログラムされていなくてもデータから知識を学習および抽出できるようにするコンピューターサイエンスの分野です。

この強化学習チュートリアルでは、PyTorchを使用して強化学習ニューラルネットワークにFlappyBirdのプレイ方法を教える方法を示します。 ただし、最初に、いくつかのビルディングブロックをカバーする必要があります。

機械学習アルゴリズムは、従来の学習アルゴリズムと深層学習アルゴリズムの2つの部分に大別できます。 従来の学習アルゴリズムは通常、深層学習アルゴリズムよりも学習可能なパラメーターがはるかに少なく、学習能力がはるかに低くなっています。

また、従来の学習アルゴリズムでは特徴抽出を行うことができません。人工知能の専門家は、学習アルゴリズムに送信される適切なデータ表現を把握する必要があります。 従来の機械学習手法の例には、SVM、ランダムフォレスト、決定木、$ k $-meansが含まれますが、深層学習の中心的なアルゴリズムは深層ニューラルネットワークです。

ディープニューラルネットワークへの入力は生の画像である可能性があり、人工知能の専門家はデータ表現を見つける必要はありません。ニューラルネットワークはトレーニングプロセス中に最適な表現を見つけます。

多くの深層学習手法は非常に長い間知られていますが、ハードウェアの最近の進歩により、深層学習の研究開発が急速に促進されました。 Nvidiaは、GPUによって高速な深層学習実験が可能になったため、この分野の拡大に責任を負っています。

学習可能なパラメータとハイパーパラメータ

機械学習アルゴリズムは、トレーニングプロセスで調整される学習可能なパラメーターと、トレーニングプロセスの前に設定される学習不可能なパラメーターで構成されます。 学習前に設定されたパラメータは、ハイパーパラメータと呼ばれます。

グリッド検索は、最適なハイパーパラメータを見つけるための一般的な方法です。 これは力ずくの方法です。つまり、定義された範囲でハイパーパラメータの可能なすべての組み合わせを試し、事前定義されたメトリックを最大化する組み合わせを選択することを意味します。

教師あり、教師なし、強化学習アルゴリズム

学習アルゴリズムを分類する1つの方法は、教師ありアルゴリズムと教師なしアルゴリズムの間に線を引くことです。 (しかし、それは必ずしも簡単ではありません。強化学習は、これら2つのタイプの中間にあります。)

教師あり学習について話すときは、$(x_i、y_i)$ペアを調べます。 $ x_i $はアルゴリズムの入力であり、$y_i$は出力です。 私たちのタスクは、$x_i$から$y_i$への正しいマッピングを行う関数を見つけることです。

$x_i$を$y_i$にマップする関数を定義するように学習可能なパラメーターを調整するには、損失関数とオプティマイザーを定義する必要があります。 オプティマイザは損失関数を最小化します。 損失関数の一例は、平均二乗誤差(MSE)です。

\ [MSE = \ sum_ {i = 1} ^ {n}(y_i-\ widehat {y_i})^ 2 \]

ここで、$ y_i $はグラウンドトゥルースラベルであり、$ \ widehat{y_i}$は予測ラベルです。 深層学習で非常に人気のあるオプティマイザーの1つは、確率的勾配降下法です。 確率的勾配降下法を改善しようとするバリエーションはたくさんあります。Adam、Adadelta、Adagradなどです。

教師なしアルゴリズムは、ラベルを明示的に提供せずにデータ内の構造を見つけようとします。 $ k $ -meansは、データ内で最適なクラスターを見つけようとする教師なしアルゴリズムの例の1つです。 以下は、300個のデータポイントを持つ画像です。 $ k $-アルゴリズムがデータ内の構造を検出し、各データポイントにクラスターラベルを割り当てたことを意味します。 各クラスターには独自の色があります。

データポイントの色分けされたクラスター。クラスターは教師なしアルゴリズムによって検出されました

強化学習は報酬を使用します:まばらで時間遅延のあるラベル。 エージェントは行動を起こし、環境を変化させ、そこから新しい観察と報酬を得ることができます。 観察は、エージェントが環境から知覚する刺激です。 それは、エージェントが見たり、聞いたり、においを嗅いだりするものである可能性があります。

エージェントがアクションを実行すると、報酬が与えられます。 これは、アクションがどれだけ優れているかをエージェントに伝えます。 エージェントは、観察と報酬を認識することにより、環境内で最適に動作する方法を学習します。 これについては、以下で詳しく説明します。

アクティブ、パッシブ、および逆強化学習

この手法にはいくつかの異なるアプローチがあります。 まず、ここで使用しているアクティブな強化学習があります。 対照的に、受動的強化学習があります。この学習では、報酬は単なる別の種類の観察であり、代わりに固定ポリシーに従って決定が行われます。

最後に、逆強化学習は、さまざまな状態でのアクションの履歴とその報酬を考慮して、報酬関数を再構築しようとします。

一般化、過剰適合、および過適合

パラメータとハイパーパラメータの固定インスタンスは、モデルと呼ばれます。 機械学習実験は通常、トレーニングとテストの2つの部分で構成されます。

トレーニングプロセス中に、学習可能なパラメータはトレーニングデータを使用して調整されます。 テストプロセスでは、学習可能なパラメーターが凍結されます。タスクは、モデルが以前に表示されなかったデータをどの程度予測できるかを確認することです。 一般化とは、学習データセットを体験した後、新しい、目に見えない例やタスクを正確に実行する学習マシンの機能です。

モデルがデータに関して単純すぎる場合、モデルはトレーニングデータに適合できず、トレーニングデータセットとテストデータセットの両方でパフォーマンスが低下します。 その場合、モデルは適切ではないと言います。

機械学習モデルがトレーニングデータセットではうまく機能するが、テストデータセットではうまく機能しない場合、それは過剰適合であると言えます。 過剰適合とは、モデルがデータに関して複雑すぎる状況です。 トレーニングデータに完全に適合できますが、トレーニングデータセットに非常に適合しているため、テストデータのパフォーマンスが低下します。つまり、一般化されません。

以下は、全体的なデータと予測関数の間のバランスの取れた状況と比較した、過適合と過適合を示す画像です。

グラフの適合不足、バランス調整、および過剰適合。バランスの取れた関数は、個々のデータポイントに固執することなく、データポイントの一般的な傾向に十分に追従します。

スケーラビリティ

機械学習モデルを構築するには、データが非常に重要です。 通常、従来の学習アルゴリズムはあまり多くのデータを必要としません。 ただし、容量が限られているため、パフォーマンスも制限されます。 以下は、従来の機械学習アルゴリズムと比較して、ディープラーニング手法がどのように拡張できるかを示すプロットです。

ディープラーニングと従来のアルゴリズムのパフォーマンスとデータ量。ニューラルネットワークは、大規模でパフォーマンスが向上します。

ニューラルネットワーク

ニューラルネットワークは複数の層で構成されています。 下の画像は、4層の単純なニューラルネットワークを示しています。 最初のレイヤーは入力レイヤーで、最後のレイヤーは出力レイヤーです。 入力レイヤーと出力レイヤーの間の2つのレイヤーは非表示レイヤーです。

ニューラルネットワークグラフ。入力層のすべてのノードが非表示層1のすべてのノードにマップされ、次に非表示層2のすべてのノードにマップされ、最後に単一ノードで構成される出力層にマップされます。

ニューラルネットワークに複数の隠れ層がある場合、それをディープニューラルネットワークと呼びます。 入力セット$X$がニューラルネットワークに与えられ、出力$y$が取得されます。 学習は、損失関数とオプティマイザーを組み合わせたバックプロパゲーションアルゴリズムを使用して行われます。

バックプロパゲーションは、フォワードパスとバックワードパスの2つの部分で構成されます。 フォワードパスでは、入力データがニューラルネットワークの入力に配置され、出力が取得されます。 グラウンドトゥルースと予測の間の損失が計算され、バックワードパスでは、ニューラルネットワークのパラメーターが損失に関して調整されます。

畳み込みニューラルネットワーク

ニューラルネットワークのバリエーションの1つは、畳み込みニューラルネットワークです。 これは主にコンピュータビジョンタスクに使用されます。

畳み込みニューラルネットワークで最も重要な層は、畳み込み層です(そのため名前が付けられています)。 そのパラメーターは、カーネルとも呼ばれる学習可能なフィルターで構成されています。 畳み込み層は、入力に畳み込み演算を適用し、結果を次の層に渡します。 畳み込み演算は、学習可能なパラメーターの数を減らし、一種のヒューリスティックとして機能し、ニューラルネットワークのトレーニングを容易にします。

以下は、畳み込み層の1つの畳み込みカーネルがどのように機能するかです。 カーネルが画像に適用され、畳み込み特徴が取得されます。

カーネルとその画像としての動作を強調するアニメーションが処理され、対応する畳み込み特徴の出力

ReLU層は、ニューラルネットワークに非線形性を導入するために使用されます。 非線形性は重要です。非線形性を使用すると、線形関数だけでなく、あらゆる種類の関数をモデル化できるため、ニューラルネットワークを普遍関数近似器にすることができます。 これにより、ReLU関数は次のように定義されます。

\ [ReLU = \ max(0、x)\]

ReLUは、ニューラルネットワークに非線形性を導入するために使用されるいわゆる活性化関数の例の1つです。 他の活性化関数の例には、シグモイド関数とハイパータンジェント関数が含まれます。 ReLUは、他の活性化関数と比較してニューラルネットワークをより効率的にトレーニングすることが示されているため、最も人気のある活性化関数です。

以下は、ReLU関数のプロットです。

y = xグラフの単純な対角線に似ていますが、すべての負のx値がゼロにマップされているReLU関数

ご覧のとおり、このReLU関数は、負の値をゼロに変更するだけです。 これは、勾配消失問題を防ぐのに役立ちます。 勾配が消失した場合、ニューラルネットワークの重みの調整に大きな影響はありません。

畳み込みニューラルネットワークは、畳み込み層、ReLU層、および完全に接続された層の複数の層で構成されます。 このセクションの冒頭の画像の2つの隠れた層に見られるように、完全に接続された層は、ある層のすべてのニューロンを別の層のすべてのニューロンに接続します。 最後の完全に接続されたレイヤーは、前のレイヤーからの出力を、この場合はnumber_of_actions値にマップします。

アプリケーション

ディープラーニングは成功しており、コンピュータービジョン、音声認識、強化学習など、いくつかの機械学習サブフィールドで従来の機械学習アルゴリズムよりも優れています。 ディープラーニングのこれらの分野は、金融、医学、娯楽など、さまざまな現実の領域に適用されます。

強化学習

強化学習はエージェントに基づいています。 エージェントは環境内でアクションを実行し、そこから観察と報酬を受け取ります。 累積報酬を最大化するには、エージェントをトレーニングする必要があります。 冒頭で述べたように、古典的な機械学習アルゴリズムでは、機械学習エンジニアは特徴抽出を行う必要があります。つまり、環境を適切に表現し、機械学習アルゴリズムに供給される優れた特徴を作成する必要があります。

ディープラーニングを使用すると、ビデオなどの高次元の入力を受け取るエンドツーエンドのシステムを作成でき、そこから、エージェントが適切なアクションを実行するための最適な戦略を学習します。

2013年、ロンドンのAIスタートアップであるDeepMindは、高次元の感覚入力から直接エージェントを制御する方法を学ぶ上で大きな進歩を遂げました。 彼らは、画面を見るだけでAtariゲームをプレイするための人工ニューラルネットワークをどのように教えたかを示した論文「 PlayingAtari withDeepReinforcementLearning 」を公開しました。 それらはGoogleに買収され、 Natureにいくつかの改善を加えた新しい論文を発表しました。それは、深層強化学習による人間レベルの制御です。

他の機械学習パラダイムとは対照的に、強化学習にはスーパーバイザーがなく、報酬シグナルのみがあります。 フィードバックが遅れる:教師あり学習アルゴリズムのように瞬時ではありません。 データはシーケンシャルであり、エージェントのアクションは、受信する後続のデータに影響を与えます。

現在、エージェントは特定の状態にあるその環境に配置されています。 これをより詳細に説明するために、この強化学習環境をモデル化する正式な方法であるマルコフ決定過程を使用します。 これは、一連の状態、一連の可能なアクション、およびある状態から別の状態に移行するためのルール(確率など)で構成されています。

マルコフ決定過程グラフ:状態(「S」とマークされている)はアクション(「a」とマークされている)を実行します。アクションは、エージェントが次に終了する状態のさまざまな確率を持ちます。いくつかのたどった道も報酬を示しています

エージェントはアクションを実行して、環境を変革することができます。 報酬を$R_t$と呼びます。 これは、エージェントがステップ$t$でどれだけうまく機能しているかを示すスカラーフィードバック信号です。

ニューラルネットワークエージェントは、観察と報酬に基づいて、任意のステップで実行するアクションを決定します。

長期的なパフォーマンスを向上させるには、即時の報酬だけでなく、将来の報酬も考慮に入れる必要があります。 タイムステップ$t$からの1つのエピソードの合計報酬は、$ R_t = r_t + r_ {t + 1} + r_ {t + 2} + \ ldots +r_n$です。 将来は不確実であり、私たちが将来進むほど、より多くの将来の予測が発散する可能性があります。 そのため、割引された将来の報酬が使用されます:$ R_t = r_t + \ gamma r_ {t + 1} + \ gamma ^ 2r_ {t + 2} + \ ldots + \ gamma ^ {nt} r_n = r_t + \ gamma R_ {t +1}$。 エージェントは、割引された将来の報酬を最大化するアクションを選択する必要があります。

深いQ学習

$ Q(s、a)$関数は、アクション$a$が状態$s$で実行された場合の、割引される将来の最大報酬を表します。

\ [Q(s_t、a_t)= \ max R_ {t + 1} \]

将来の報酬の見積もりは、ベルマン方程式によって与えられます。$ Q(s、a)= r + \ gamma \ max_ {a'} Q(s'、a')$。 言い換えると、状態$s$とアクション$a$が与えられた場合の最大の将来の報酬は、即時の報酬に次の状態の最大の将来の報酬を加えたものです。

非線形関数(ニューラルネットワーク)を使用したQ値の近似は、あまり安定していません。 そのため、安定性のためにエクスペリエンスリプレイが使用されます。 トレーニングセッションのエピソード中の経験は、リプレイメモリに保存されます。 最新のトランジションを使用する代わりに、リプレイメモリからのランダムなミニバッチが使用されます。 これにより、ニューラルネットワークを極小値に駆動する後続のトレーニングサンプルの類似性が失われます。

深いQ学習について言及する重要な側面がさらに2つあります。それは、探索と活用です。 悪用により、現在の情報を考慮した最善の決定が行われます。 探索により、より多くの情報が収集されます。

アルゴリズムがニューラルネットワークによって提案されたアクションを実行するとき、それは悪用を行っています。それはニューラルネットワークの学習した知識を悪用します。 対照的に、アルゴリズムはランダムなアクションを取り、新しい可能性を探求し、ニューラルネットワークに潜在的な新しい知識を導入することができます。

DeepMindの論文「PlayingAtariwithDeepReinforcementLearning」の「DeepQ-learningalgorithmwithExperienceReplay」を以下に示します。

擬似コードでのエクスペリエンスリプレイアルゴリズムを使用したディープQ学習

DeepMindは、Deep Q-networks(DQN)としてのアプローチでトレーニングされた畳み込みネットワークを指します。

ゆるい鳥を使った深いQ学習の例

Flappy Birdは、もともとベトナムのビデオゲームアーティストでプログラマーのDongNguyenによって開発された人気のモバイルゲームでした。 その中で、プレイヤーは鳥を操作し、緑色のパイプの間をぶつからずに飛ばそうとします。

以下は、PyGameを使用してコード化されたFlappyBirdクローンのスクリーンショットです。

PyGameを使用してコード化されたFlappyBirdクローンであるFlapPyBirdのスクリーンショット

その後、クローンはフォークされ、変更されました。背景、サウンド、さまざまな鳥とパイプのスタイルが削除され、コードが調整されて、単純な強化学習フレームワークで簡単に使用できるようになりました。 変更されたゲームエンジンは、このTensorFlowプロジェクトから取得されます。

簡略化されたグラフィックを備えたクローンのフォークであるDeepLearningFlappyBirdのスクリーンショット

しかし、TensorFlowを使用する代わりに、PyTorchを使用して深層強化学習フレームワークを構築しました。 PyTorchは、高速で柔軟な実験のためのディープラーニングフレームワークです。 強力なGPUアクセラレーションを備えたPythonのテンソルと動的ニューラルネットワークを提供します。

ニューラルネットワークアーキテクチャは、論文「深層強化学習による人間レベルの制御」で使用されているDeepMindと同じです。

入力フィルターサイズストライドフィルタの数アクティベーション出力
conv1 84x84x4 8x8 4 32 ReLU 20x20x32
conv2 20x20x32 4x4 2 64 ReLU 9x9x64
conv3 9x9x64 3x3 1 64 ReLU 7x7x64
fc4 7x7x64 512 ReLU 512
fc5 512 2 線形2

3つの畳み込み層と2つの完全に接続された層があります。 線形アクティベーションを使用する最後のレイヤーを除いて、各レイヤーはReLUアクティベーションを使用します。 ニューラルネットワークは、プレーヤーの唯一の可能なアクションを表す2つの値を出力します。「フライアップ」と「何もしない」です。

入力は、4つの連続する84x84の白黒画像で構成されます。 以下は、ニューラルネットワークに供給される4つの画像の例です。

画像が回転していることに気付くでしょう。 これは、クローンのゲームエンジンの出力が回転するためです。 しかし、ニューラルネットワークがそのような画像を使用して教えられ、テストされた場合、それはそのパフォーマンスに影響を与えません。

ニューラルネットワークに直接供給される、FlappyBirdクローンの4つの連続した白黒フレーム

また、このタスクとは無関係であるため、画像がトリミングされて床が省略されていることに気付く場合があります。 パイプと鳥を表すすべてのピクセルは白で、背景を表すすべてのピクセルは黒です。

これは、ニューラルネットワークを定義するコードの一部です。 ニューラルネットワークの重みは、一様分布$ \ mathcal {U}(-0.01、0.01)$に従うように初期化されます。 ニューラルネットワークのパラメータのバイアス部分は0.01に設定されています。 いくつかの異なる初期化(Xavierユニフォーム、Xavierノーマル、Kaimingユニフォーム、Kaimingノーマル、ユニフォーム、およびノー​​マル)が試行されましたが、上記の初期化により、ニューラルネットワークが収束して最速でトレーニングされました。 ニューラルネットワークのサイズは6.8MBです。

 class NeuralNetwork(nn.Module): def __init__(self): super(NeuralNetwork, self).__init__() self.number_of_actions = 2 self.gamma = 0.99 self.final_epsilon = 0.0001 self.initial_epsilon = 0.1 self.number_of_iterations = 2000000 self.replay_memory_size = 10000 self.minibatch_size = 32 self.conv1 = nn.Conv2d(4, 32, 8, 4) self.relu1 = nn.ReLU(inplace=True) self.conv2 = nn.Conv2d(32, 64, 4, 2) self.relu2 = nn.ReLU(inplace=True) self.conv3 = nn.Conv2d(64, 64, 3, 1) self.relu3 = nn.ReLU(inplace=True) self.fc4 = nn.Linear(3136, 512) self.relu4 = nn.ReLU(inplace=True) self.fc5 = nn.Linear(512, self.number_of_actions) def forward(self, x): out = self.conv1(x) out = self.relu1(out) out = self.conv2(out) out = self.relu2(out) out = self.conv3(out) out = self.relu3(out) out = out.view(out.size()[0], -1) out = self.fc4(out) out = self.relu4(out) out = self.fc5(out) return out

コンストラクターでは、ハイパーパラメーターが定義されていることに気付くでしょう。 このブログ投稿の目的では、ハイパーパラメータの最適化は行われません。 代わりに、ハイパーパラメータは主にDeepMindの論文から使用されています。 ここでは、Flappy Birdはチューニングに使用したAtariゲームよりも複雑ではないため、一部のハイパーパラメータはDeepMindの論文よりも低くなるようにスケーリングされています。

また、イプシロンはこのゲームにとってはるかに合理的なものに変更されています。 DeepMindは1のイプシロンを使用しますが、ここでは0.1を使用します。 これは、より高いイプシロンが鳥を大きく羽ばたかせ、それが鳥を画面の上部境界に向かって押し、常に最終的に鳥がパイプに衝突する結果になるためです。

強化学習コードには、トレーニングとテストの2つのモードがあります。 テストフェーズでは、強化学習アルゴリズムがゲームをプレイするためにどれだけうまく学習したかを確認できます。 しかし、最初に、ニューラルネットワークをトレーニングする必要があります。 最小化する損失関数と、損失関数を最小化するオプティマイザーを定義する必要があります。 アダム最適化法と損失関数の平均二乗誤差を使用します。

 optimizer = optim.Adam(model.parameters(), lr=1e-6) criterion = nn.MSELoss()

ゲームをインスタンス化する必要があります。

 game_state = GameState()

リプレイメモリはPythonリストとして定義されています。

 replay_memory = []

次に、最初の状態を初期化する必要があります。 アクションは2次元テンソルです。

  • [1、0]は「何もしない」を表します
  • [0、1]は「フライアップ」を表します

frame_stepメソッドは、次の画面、報酬、および次の状態がターミナルであるかどうかに関する情報を提供します。 報酬は、パイプを通過していないときに死なずに鳥が移動するたびに0.1 、鳥がパイプを正常に通過した場合は1 、鳥が墜落した場合は-1です。

resize_and_bgr2gray関数は、床をトリミングし、画面のサイズを84x84の画像に変更し、色空間をBGRから白黒に変更します。 image_to_tensor関数は、画像をPyTorchテンソルに変換し、CUDAが使用可能な場合はGPUメモリに配置します。 最後に、最後の4つの連続した画面が連結され、ニューラルネットワークに送信する準備が整います。

 action = torch.zeros([model.number_of_actions], dtype=torch.float32) action[0] = 1 image_data, reward, terminal = game_state.frame_step(action) image_data = resize_and_bgr2gray(image_data) image_data = image_to_tensor(image_data) state = torch.cat((image_data, image_data, image_data, image_data)).unsqueeze(0)

初期イプシロンは、次のコード行を使用して設定されます。

 epsilon = model.initial_epsilon

主な無限ループが続きます。 コメントはコードに記述されており、コードを上記のエクスペリエンスリプレイアルゴリズムを使用したDeepQラーニングと比較できます。

アルゴリズムは、リプレイメモリからミニバッチをサンプリングし、ニューラルネットワークのパラメータを更新します。 アクションは、イプシロン欲張り探索を使用して実行されます。 イプシロンは時間の経過とともにアニーリングされています。 最小化される損失関数は$L= \ frac {1} {2} \ left [\ max_ {a'} Q(s'、a')-Q(s、a)\ right] ^2$です。 $ Q(s、a)$はベルマン方程式を使用して計算されたグラウンドトゥルース値であり、$ \ max_ {a'} Q(s'、a')$はニューラルネットワークから取得されます。 ニューラルネットワークは、2つの可能なアクションに対して2つのQ値を提供し、アルゴリズムは最も高いQ値でアクションを実行します。

 while iteration < model.number_of_iterations: # get output from the neural network output = model(state)[0] # initialize action action = torch.zeros([model.number_of_actions], dtype=torch.float32) if torch.cuda.is_available(): # put on GPU if CUDA is available action = action.cuda() # epsilon greedy exploration random_action = random.random() <= epsilon if random_action: print("Performed random action!") action_index = [torch.randint(model.number_of_actions, torch.Size([]), dtype=torch.int) if random_action else torch.argmax(output)][0] if torch.cuda.is_available(): # put on GPU if CUDA is available action_index = action_index.cuda() action[action_index] = 1 # get next state and reward image_data_1, reward, terminal = game_state.frame_step(action) image_data_1 = resize_and_bgr2gray(image_data_1) image_data_1 = image_to_tensor(image_data_1) state_1 = torch.cat((state.squeeze(0)[1:, :, :], image_data_1)).unsqueeze(0) action = action.unsqueeze(0) reward = torch.from_numpy(np.array([reward], dtype=np.float32)).unsqueeze(0) # save transition to replay memory replay_memory.append((state, action, reward, state_1, terminal)) # if replay memory is full, remove the oldest transition if len(replay_memory) > model.replay_memory_size: replay_memory.pop(0) # epsilon annealing epsilon = epsilon_decrements[iteration] # sample random minibatch minibatch = random.sample(replay_memory, min(len(replay_memory), model.minibatch_size)) # unpack minibatch state_batch = torch.cat(tuple(d[0] for d in minibatch)) action_batch = torch.cat(tuple(d[1] for d in minibatch)) reward_batch = torch.cat(tuple(d[2] for d in minibatch)) state_1_batch = torch.cat(tuple(d[3] for d in minibatch)) if torch.cuda.is_available(): # put on GPU if CUDA is available state_batch = state_batch.cuda() action_batch = action_batch.cuda() reward_batch = reward_batch.cuda() state_1_batch = state_1_batch.cuda() # get output for the next state output_1_batch = model(state_1_batch) # set y_j to r_j for terminal state, otherwise to r_j + gamma*max(Q) y_batch = torch.cat(tuple(reward_batch[i] if minibatch[i][4] else reward_batch[i] + model.gamma * torch.max(output_1_batch[i]) for i in range(len(minibatch)))) # extract Q-value q_value = torch.sum(model(state_batch) * action_batch, dim=1) # PyTorch accumulates gradients by default, so they need to be reset in each pass optimizer.zero_grad() # returns a new Tensor, detached from the current graph, the result will never require gradient y_batch = y_batch.detach() # calculate loss loss = criterion(q_value, y_batch) # do backward pass loss.backward() optimizer.step() # set state to be state_1 state = state_1

すべての要素が整ったので、ニューラルネットワークを使用したデータフローの概要を次に示します。

ニューラルネットワークを使用したデータフローの最終的な概要:入力状態は、4つの連続した画面で構成されます。それから、ニューラルネットワークは2つの可能なアクション(「何もしない」と「飛ぶ」)に対して2つのQ値を与えます。

これは、訓練されたニューラルネットワークを使用した1つの短いシーケンスです。

結果として得られるトレーニング済みニューラルネットワークによって再生されているFlappyBirdクローンのアニメーション

上に示したニューラルネットワークは、ハイエンドのNvidia GTX1080GPUを使用して数時間トレーニングされました。 代わりにCPUベースのソリューションを使用すると、この特定のタスクには数日かかります。 ゲームエンジンのFPSは、トレーニング中に非常に大きな数値である999〜999に設定されました。つまり、1秒あたりのフレーム数が可能な限り多くなります。 テストフェーズでは、FPSは30に設定されました。

以下は、反復中に最大Q値がどのように変化したかを示すグラフです。 10,000回ごとの反復が表示されます。 下向きのスパイクは、特定のフレーム(1回の反復が1フレーム)について、ニューラルネットワークが鳥が将来非常に低い報酬を受け取ることを予測することを意味します。つまり、鳥はすぐにクラッシュします。

反復中に最大Q値がどのように変化したかを示すグラフ。ゼロから始まり、いくつかの下降スパイクがあり、約100万回の反復後、Q値が最大になる約12または13に向かう全体的な傾向を示しています。

完全なコードと事前トレーニング済みモデルは、こちらから入手できます。

深層強化学習:2D、3D、さらには実生活

このPyTorch強化学習チュートリアルでは、ゲームに初めて遭遇したときに人間が行うように試行錯誤のアプローチのみを使用して、コンピューターがゲームに関する予備知識なしでゆるい鳥のプレイを学習する方法を示しました。

PyTorchフレームワークを使用して、アルゴリズムを数行のコードで実装できるのは興味深いことです。 このブログの方法が基づいている論文は比較的古く、より速い収束を可能にするさまざまな修正を加えた新しい論文がたくさんあります。 それ以来、深層強化学習は3Dゲームのプレイや実世界のロボットシステムで使用されてきました。

DeepMind、Maluuba、Vicariousなどの企業は、深層強化学習に集中的に取り組んでいます。 このような技術は、囲碁で世界最高の選手の1人である李世ドルを打ち負かしたAlphaGoで使用されました。 当時、マシンが囲碁で最高の選手を倒せるようになるまでには、少なくとも10年はかかると考えられていました。

深い強化学習(および一般的な人工知能)への関心と投資の洪水は、潜在的人工知能(AGI)、つまりアルゴリズムとコンピューターでシミュレート。 しかし、AGIは別の記事の主題である必要があります。


参照:

  • 深層強化学習の謎を解き明かす
  • ゆるい鳥のための深層強化学習
  • ウィキペディアの「畳み込みニューラルネットワーク」
  • ウィキペディアの「強化学習」
  • ウィキペディアの「マルコフ決定過程」
  • RLのユニバーシティカレッジロンドンコース
関連:ディープラーニングチュートリアル:パーセプトロンからディープネットワークまで