# 強化学習とQ学習の入門 ![機械学習における強化学習の概要をスケッチノートで表現](../../../../sketchnotes/ml-reinforcement.png) > スケッチノート: [Tomomi Imura](https://www.twitter.com/girlie_mac) 強化学習には、エージェント、状態、そして各状態における一連の行動という3つの重要な概念が含まれます。指定された状態で行動を実行することで、エージェントは報酬を得ます。例えば、コンピュータゲーム「スーパーマリオ」を想像してみてください。あなたはマリオであり、ゲームのレベルにいて、崖の端に立っています。上にはコインがあります。マリオとして、ゲームレベルの特定の位置にいる状態が「状態」です。右に一歩進む(行動)と崖から落ちてしまい、低い数値スコアが与えられます。しかし、ジャンプボタンを押すとポイントを獲得し、生き残ることができます。それはポジティブな結果であり、ポジティブな数値スコアが与えられるべきです。 強化学習とシミュレーター(ゲーム)を使用することで、生き残りながらできるだけ多くのポイントを獲得するためのゲームプレイ方法を学ぶことができます。 [![強化学習の紹介](https://img.youtube.com/vi/lDq_en8RNOo/0.jpg)](https://www.youtube.com/watch?v=lDq_en8RNOo) > 🎥 上の画像をクリックして、Dmitryが強化学習について話す様子を聞いてみましょう ## [講義前のクイズ](https://ff-quizzes.netlify.app/en/ml/) ## 前提条件とセットアップ このレッスンでは、Pythonでいくつかのコードを試してみます。このレッスンのJupyter Notebookコードを、コンピュータ上またはクラウド上で実行できる必要があります。 [レッスンノートブック](https://github.com/microsoft/ML-For-Beginners/blob/main/8-Reinforcement/1-QLearning/notebook.ipynb)を開き、このレッスンを進めながら構築してください。 > **Note:** クラウドからこのコードを開く場合、ノートブックコードで使用される[`rlboard.py`](https://github.com/microsoft/ML-For-Beginners/blob/main/8-Reinforcement/1-QLearning/rlboard.py)ファイルも取得する必要があります。このファイルをノートブックと同じディレクトリに追加してください。 ## はじめに このレッスンでは、ロシアの作曲家[セルゲイ・プロコフィエフ](https://en.wikipedia.org/wiki/Sergei_Prokofiev)による音楽童話「[ピーターと狼](https://en.wikipedia.org/wiki/Peter_and_the_Wolf)」にインスパイアされた世界を探求します。**強化学習**を使用して、ピーターが環境を探索し、美味しいリンゴを集め、狼に遭遇しないようにします。 **強化学習**(RL)は、エージェントがある環境で最適な行動を学ぶために多くの実験を行う学習技術です。この環境内のエージェントには、**報酬関数**によって定義された**目標**が必要です。 ## 環境 簡単のために、ピーターの世界を`幅` x `高さ`のサイズの正方形のボードと考えます。以下のようなものです: ![ピーターの環境](../../../../8-Reinforcement/1-QLearning/images/environment.png) このボードの各セルは以下のいずれかになります: * **地面**: ピーターや他の生物が歩ける場所。 * **水**: 明らかに歩けない場所。 * **木**または**草**: 休むことができる場所。 * **リンゴ**: ピーターが見つけて喜ぶ食べ物。 * **狼**: 危険で避けるべき存在。 この環境で作業するためのコードを含むPythonモジュール[`rlboard.py`](https://github.com/microsoft/ML-For-Beginners/blob/main/8-Reinforcement/1-QLearning/rlboard.py)があります。このコードは概念を理解する上で重要ではないため、モジュールをインポートしてサンプルボードを作成します(コードブロック1): ```python from rlboard import * width, height = 8,8 m = Board(width,height) m.randomize(seed=13) m.plot() ``` このコードは上記のような環境の画像を出力するはずです。 ## 行動とポリシー この例では、ピーターの目標はリンゴを見つけることであり、狼やその他の障害物を避けることです。そのため、リンゴを見つけるまで歩き回ることができます。 したがって、任意の位置で以下の行動のいずれかを選択できます: 上、下、左、右。 これらの行動を辞書として定義し、対応する座標変化のペアにマッピングします。例えば、右に移動する(`R`)はペア`(1,0)`に対応します。(コードブロック2): ```python actions = { "U" : (0,-1), "D" : (0,1), "L" : (-1,0), "R" : (1,0) } action_idx = { a : i for i,a in enumerate(actions.keys()) } ``` このシナリオの戦略と目標をまとめると以下の通りです: - **戦略**: エージェント(ピーター)の戦略は、**ポリシー**と呼ばれる関数によって定義されます。ポリシーは任意の状態で行動を返します。この場合、問題の状態はプレイヤーの現在位置を含むボードによって表されます。 - **目標**: 強化学習の目標は、問題を効率的に解決するための良いポリシーを最終的に学ぶことです。ただし、基準として最も簡単なポリシーである**ランダムウォーク**を考えます。 ## ランダムウォーク まず、ランダムウォーク戦略を実装して問題を解決してみましょう。ランダムウォークでは、許可された行動から次の行動をランダムに選択し、リンゴに到達するまで繰り返します(コードブロック3)。 1. 以下のコードでランダムウォークを実装します: ```python def random_policy(m): return random.choice(list(actions)) def walk(m,policy,start_position=None): n = 0 # number of steps # set initial position if start_position: m.human = start_position else: m.random_start() while True: if m.at() == Board.Cell.apple: return n # success! if m.at() in [Board.Cell.wolf, Board.Cell.water]: return -1 # eaten by wolf or drowned while True: a = actions[policy(m)] new_pos = m.move_pos(m.human,a) if m.is_valid(new_pos) and m.at(new_pos)!=Board.Cell.water: m.move(a) # do the actual move break n+=1 walk(m,random_policy) ``` `walk`の呼び出しは、対応する経路の長さを返すはずです。これは実行ごとに異なる場合があります。 1. ウォーク実験を複数回(例えば100回)実行し、結果の統計を出力します(コードブロック4): ```python def print_statistics(policy): s,w,n = 0,0,0 for _ in range(100): z = walk(m,policy) if z<0: w+=1 else: s += z n += 1 print(f"Average path length = {s/n}, eaten by wolf: {w} times") print_statistics(random_policy) ``` 経路の平均長さは約30〜40ステップであることに注意してください。これは、最寄りのリンゴまでの平均距離が約5〜6ステップであることを考えるとかなり多いです。 また、ランダムウォーク中のピーターの動きを確認することもできます: ![ピーターのランダムウォーク](../../../../8-Reinforcement/1-QLearning/images/random_walk.gif) ## 報酬関数 ポリシーをより賢くするためには、どの動きが他より「良い」のかを理解する必要があります。そのためには、目標を定義する必要があります。 目標は、各状態に対してスコア値を返す**報酬関数**によって定義できます。数値が高いほど報酬関数が良いことを意味します。(コードブロック5) ```python move_reward = -0.1 goal_reward = 10 end_reward = -10 def reward(m,pos=None): pos = pos or m.human if not m.is_valid(pos): return end_reward x = m.at(pos) if x==Board.Cell.water or x == Board.Cell.wolf: return end_reward if x==Board.Cell.apple: return goal_reward return move_reward ``` 報酬関数の興味深い点は、ほとんどの場合、*ゲーム終了時にのみ実質的な報酬が与えられる*ことです。つまり、アルゴリズムは最終的なポジティブな報酬につながる「良い」ステップを覚えてその重要性を高める必要があります。同様に、悪い結果につながるすべての動きは抑制されるべきです。 ## Q学習 ここで説明するアルゴリズムは**Q学習**と呼ばれます。このアルゴリズムでは、ポリシーは**Qテーブル**と呼ばれる関数(またはデータ構造)によって定義されます。これは、特定の状態での各行動の「良さ」を記録します。 Qテーブルと呼ばれる理由は、テーブルや多次元配列として表現するのが便利だからです。ボードの寸法が`幅` x `高さ`であるため、Qテーブルを形状`幅` x `高さ` x `len(actions)`のnumpy配列として表現できます。(コードブロック6) ```python Q = np.ones((width,height,len(actions)),dtype=np.float)*1.0/len(actions) ``` Qテーブルのすべての値を等しい値(この場合は0.25)で初期化することに注意してください。これは「ランダムウォーク」ポリシーに対応します。すべての状態でのすべての動きが同じくらい良いということです。Qテーブルを`plot`関数に渡してボード上でテーブルを視覚化できます: `m.plot(Q)`。 ![ピーターの環境](../../../../8-Reinforcement/1-QLearning/images/env_init.png) 各セルの中央には、移動の推奨方向を示す「矢印」があります。すべての方向が等しいため、点が表示されます。 次にシミュレーションを実行し、環境を探索し、Qテーブル値のより良い分布を学習します。これにより、リンゴへの道をはるかに速く見つけることができます。 ## Q学習の本質: ベルマン方程式 移動を開始すると、各行動には対応する報酬があります。つまり、理論的には最も高い即時報酬に基づいて次の行動を選択できます。しかし、ほとんどの状態では、移動がリンゴに到達するという目標を達成するわけではないため、どの方向が良いかをすぐに決定することはできません。 > 即時の結果ではなく、シミュレーションの最後に得られる最終結果が重要であることを忘れないでください。 この遅延報酬を考慮するために、**[動的計画法](https://en.wikipedia.org/wiki/Dynamic_programming)**の原則を使用する必要があります。これにより、問題を再帰的に考えることができます。 現在の状態を*s*とし、次の状態*s'*に移動したいとします。このようにして、報酬関数によって定義された即時報酬*r(s,a)*を受け取り、さらに将来の報酬を得ることができます。Qテーブルが各行動の「魅力」を正確に反映していると仮定すると、状態*s'*では*Q(s',a')*の最大値に対応する行動*a'*を選択します。したがって、状態*s*で得られる可能性のある最良の将来の報酬は`max` ## ポリシーの確認 Q-Tableは各状態における各アクションの「魅力」を示しているため、これを使って効率的なナビゲーションを定義するのは非常に簡単です。最も単純な場合、Q-Tableの値が最も高いアクションを選択します。(コードブロック9) ```python def qpolicy_strict(m): x,y = m.human v = probs(Q[x,y]) a = list(actions)[np.argmax(v)] return a walk(m,qpolicy_strict) ``` > 上記のコードを何度か試してみると、時々「停止」してしまい、ノートブックのSTOPボタンを押して中断する必要があることに気付くかもしれません。これは、最適なQ-Valueの観点で2つの状態が互いに「指し示す」状況が発生する可能性があるためで、その場合、エージェントはその状態間を無限に移動し続けてしまいます。 ## 🚀チャレンジ > **タスク1:** `walk`関数を修正して、パスの最大長を特定のステップ数(例えば100)で制限し、上記のコードが時々この値を返す様子を観察してください。 > **タスク2:** `walk`関数を修正して、以前に訪れた場所に戻らないようにしてください。これにより`walk`がループするのを防ぐことができますが、エージェントが脱出できない場所に「閉じ込められる」可能性は依然としてあります。 ## ナビゲーション より良いナビゲーションポリシーは、トレーニング中に使用したものです。これは、利用と探索を組み合わせたものです。このポリシーでは、Q-Tableの値に比例した確率で各アクションを選択します。この戦略では、エージェントがすでに探索した位置に戻る可能性はありますが、以下のコードからわかるように、目的地までの平均パスが非常に短くなります(`print_statistics`はシミュレーションを100回実行します):(コードブロック10) ```python def qpolicy(m): x,y = m.human v = probs(Q[x,y]) a = random.choices(list(actions),weights=v)[0] return a print_statistics(qpolicy) ``` このコードを実行すると、以前よりもはるかに短い平均パス長が得られるはずです。3〜6の範囲内です。 ## 学習プロセスの調査 前述の通り、学習プロセスは探索と問題空間の構造に関する知識の活用のバランスです。学習の結果(エージェントが目標への短いパスを見つける能力)が改善されたことがわかりますが、学習プロセス中の平均パス長の挙動を観察するのも興味深いです: 学習内容を以下のようにまとめることができます: - **平均パス長の増加**。最初は平均パス長が増加することが見られます。これは、環境について何も知らない場合、悪い状態(水や狼)に閉じ込められる可能性が高いためです。より多くを学び、この知識を使い始めると、環境をより長く探索できますが、まだリンゴの場所をよく知らない状態です。 - **学習が進むにつれてパス長が減少**。十分に学習すると、エージェントが目標を達成するのが容易になり、パス長が減少し始めます。ただし、まだ探索を続けているため、最適なパスから外れて新しい選択肢を探索することがあり、パスが最適よりも長くなることがあります。 - **突然のパス長の増加**。グラフで観察されるもう一つの特徴は、ある時点でパス長が突然増加することです。これはプロセスの確率的な性質を示しており、Q-Tableの係数が新しい値で上書きされることで「損なわれる」可能性があることを意味します。これを最小化するには、学習率を減少させる(例えば、トレーニングの終盤ではQ-Tableの値を小さな値でのみ調整する)ことが理想的です。 全体として、学習プロセスの成功と質は、学習率、学習率の減衰、割引率などのパラメータに大きく依存することを覚えておくことが重要です。これらは**ハイパーパラメータ**と呼ばれ、トレーニング中に最適化する**パラメータ**(例えば、Q-Tableの係数)とは区別されます。最適なハイパーパラメータ値を見つけるプロセスは**ハイパーパラメータ最適化**と呼ばれ、別のトピックとして扱う価値があります。 ## [講義後のクイズ](https://ff-quizzes.netlify.app/en/ml/) ## 課題 [より現実的な世界](assignment.md) --- **免責事項**: この文書はAI翻訳サービス[Co-op Translator](https://github.com/Azure/co-op-translator)を使用して翻訳されています。正確性を追求しておりますが、自動翻訳には誤りや不正確な部分が含まれる可能性があります。元の言語で記載された文書が正式な情報源とみなされるべきです。重要な情報については、専門の人間による翻訳を推奨します。この翻訳の使用に起因する誤解や誤解釈について、当社は責任を負いません。