「Space Invaders」のような Atari ゲームでは、ゲームフレームを状態と入力として使用し、単一のフレーム画像は 210x160 ピクセルで構成されています。画像はカラー(RGB)であるため、3 つのチャネルを含みます。したがって、観測空間の形状は (210, 160, 3) です。各ピクセルの値は 0 から 255 の範囲であるため、合計 通りの可能な観測結果があります。
この場合、Q テーブルを生成および更新する効率は非常に低くなります。したがって、Q-Learning のようなタブラー法ではなく、Deep Q-Learning を使用し、Q 関数の近似器としてニューラルネットワークを選択します。このニューラルネットワークは、与えられた状態に基づいて、その状態での各可能なアクションの Q 値を近似的に推定します。
DQN#
入力前処理と時系列の制約#
私たちは状態の複雑さを減らして、訓練に必要な計算時間を短縮したいと考えています。
グレースケール化#
色は重要な情報を提供しないため、3 つの色チャネル(RGB)を 1 つに減らすことができます。
スクリーンクロップ#
重要な情報を含まない領域は切り取ることができます。
時系列情報のキャプチャ#
単一のフレームではピクセルの運動情報(方向、速度)を知ることができないため、時系列情報を得るために 4 フレームをスタックします。
CNN#
スタックされたフレームは 3 つの畳み込み層を通じて処理され、画像内の空間関係をキャプチャし利用することを目的としています。さらに、フレームがスタックされているため、フレーム間の時間情報も得ることができます。
MLP#
最後に、全結合層が出力として、各可能なアクションに対して Q 値を出力します。
class QNetwork(nn.Module):
def __init__(self, env):
super().__init__()
self.network = nn.Sequential(
nn.Conv2d(4, 32, 8, stride=4),
nn.ReLU(),
nn.Conv2d(32, 64, 4, stride=2),
nn.ReLU(),
nn.Conv2d(64, 64, 3, stride=1),
nn.ReLU(),
nn.Flatten(), # 多次元入力を一次元に平坦化
nn.Linear(3136, 512), # 全結合層、3136次元の入力を512次元にマッピング
nn.ReLU(),
nn.Linear(512, env.single_action_space.n), # 出力層、アクション空間の次元に対応
)
def forward(self, x):
return self.network(x / 255.0) # 入力を[0,1]の範囲に正規化
ネットワークの入力:ネットワークを通過する4 フレームのスタックを状態として使用
出力:その状態での各可能なアクションのQ 値ベクトル。
その後、Q-Learning と同様に、epsilon-greedy ポリシーを使用してどのアクションを取るかを選択します。
訓練段階では、Q 学習のように状態 - アクションペアの Q 値を直接更新することはありません:損失関数と勾配降下法を設計して DQN の重みを最適化します。
訓練プロセス#
深層 Q 学習訓練アルゴリズムには 2 つの段階があります:
- サンプリング:操作を実行し、観測された経験タプルをリプレイバッファに保存します。
- 訓練:ランダムに小さなバッチのタプルを選択し、勾配降下法の更新ステップを使用してそのバッチから学習します。
深層 Q 学習(オフポリシー)では、非線形の Q 値関数(ニューラルネットワーク)とブートストラップ法(すなわち、実際の完全な報酬ではなく、既存の推定を使用してターゲットを更新すること)を組み合わせているため、訓練プロセスに不安定性が生じる可能性があります。サットンとバルトが提唱した **「致命的な三重奏」** はまさにこの状況を指しています。
安定した訓練#
訓練を安定させるために、3 つの異なる解決策を実施しました:
- 経験再生で経験をより効率的に利用します。
- 固定 Q ターゲットで訓練を安定させます。
- ダブル DQNを使用してQ 値の過大評価の問題を解決します。
経験再生#
深層 Q 学習における経験再生には 2 つの機能があります:
- 訓練プロセス中の経験をより効率的に利用します。通常のオンライン強化学習では、エージェントが環境と相互作用して経験(状態、アクション、報酬、次の状態)を取得し、それから学習(ニューラルネットワークを更新)し、その経験を破棄する方法は非常に非効率的です。経験再生は訓練経験をより効率的に利用するのに役立ちます。私たちはリプレイバッファを使用して経験サンプルを保存し、訓練中に再利用します。
エージェントは同じ経験から何度も学習できます。
- 以前の経験を忘れない(すなわち、破滅的干渉または破滅的忘却)ようにし、経験間の関連性を減少させます。リプレイバッファの設定により、環境と相互作用する際に経験タプルを保存し、そこから小さなバッチのタプルを抽出できます。これにより、ネットワークが最近実行した操作のみを学習することを防ぎます。経験をランダムにサンプリングすることで、接触する経験を多様化し、短期的な状態に過剰適合するのを防ぎ、アクション値の急激な変動や破滅的な発散を回避します。
経験をサンプリングし、損失を計算します:
rb = ReplayBuffer(
args.buffer_size, # リプレイバッファのサイズ、どれだけの経験を保存するかを決定します。
envs.single_observation_space,
envs.single_action_space,
device,
optimize_memory_usage=True,
handle_timeout_termination=False,
)
if global_step > args.learning_starts:
if global_step % args.train_frequency == 0:
data = rb.sample(args.batch_size) # ランダムにバッチをサンプリング
with torch.no_grad():
target_max, _ = target_network(data.next_observations).max(dim=1)
td_target = data.rewards.flatten() + args.gamma * target_max * (1 - data.dones.flatten())
old_val = q_network(data.observations).gather(1, data.actions).squeeze()
loss = F.mse_loss(td_target, old_val)
固定 Q ターゲット#
Q-Learning には重要な問題があります。TD ターゲット(すなわち Q ターゲット)と現在の Q 値(すなわち Q 推定)のパラメータが共有されていることです。これにより、Q ターゲットと Q 推定が同時に変化します。まるで常に動いているターゲットを追いかけているかのようです。素晴らしい比喩は、カウボーイ(Q 推定)が動いている牛(Q ターゲット)を追いかけようとしている状況です。カウボーイが牛に徐々に近づいている(誤差が減少している)にもかかわらず、ターゲットは依然として動いており、訓練中に顕著な振動を引き起こします。
この表現が大好き🥹
この問題を解決するために、固定の Q ターゲットを導入しました。その核心的な考え方は、独立したネットワークを導入することです。このネットワークは各タイムステップで更新されるのではなく、C ステップごとにメインネットワークのパラメータをこのターゲットネットワークにコピーします。これにより、複数のタイムステップにわたってターゲット(Q ターゲット)が固定され、古い推定に基づいてネットワークを更新することができます。これにより、ターゲットと推定の間の振動問題を大幅に減少させることができます。
上記の擬似コードのように、重要なのは 2 つの異なるネットワークを使用することです:1 つはアクションを選択して更新するためのメインネットワーク、もう 1 つは Q ターゲットを計算するためのターゲットネットワークです。そして、C ステップごとにメインネットワークの重みをターゲットネットワークにコピーします。これにより、訓練プロセスを安定させ、「カウボーイが牛をより効果的に追いかける」ことができ、振動を減少させ、収束速度を加速します。
q_network = QNetwork(envs).to(device) # 現在のポリシーネットワーク、アクションを選択しQ値を予測する
optimizer = optim.Adam(q_network.parameters(), lr=args.learning_rate)
target_network = QNetwork(envs).to(device) # ターゲットネットワーク、TDターゲットを計算し、安定した学習目標を提供する
target_network.load_state_dict(q_network.state_dict()) # 初期化:ターゲットネットワークのパラメータは現在のポリシーネットワークと同じ
args.target_network_frequency
ステップごとにメインネットワークのパラメータをターゲットネットワークに完全にコピーします。これにより、複数のタイムステップにわたって Q ターゲットが固定され、古い推定に基づいてネットワークを更新することで、ターゲットと推定の間の振動問題を大幅に減少させます。
tau = 1.0
if global_step % args.target_network_frequency == 0:
for target_param, param in zip(target_network.parameters(), q_network.parameters()):
target_param.data.copy_(args.tau * param.data + (1.0 - args.tau) * target_param.data)
ダブル DQN#
ダブル DQN は ハド・バン・ハッセルトによって提案され、Q 値の過大評価の問題を解決するために特化されています。
Q-Learning の TD - ターゲット計算において、一般的な問題は「次の状態の最適なアクションは Q 値が最も高いアクションである」をどのように決定するかです。私たちは、Q 値の正確性が試みたアクションと探索した近隣状態に依存することを知っています。したがって、訓練の初期段階では、最適なアクションに関する情報は不十分です。もし最高の Q 値に基づいてアクションを選択するだけであれば、誤った判断を引き起こす可能性があります。
例えば、非最適なアクションに最適なアクションよりも高い Q 値が与えられた場合、学習プロセスは複雑になり、収束が困難になります。この問題を解決するために、ダブル DQN はアクション選択と Q 値ターゲットの生成を解耦するために 2 つのネットワークを導入します:
- メインネットワーク(DQN ネットワーク) は次の状態の最適なアクション(すなわち Q 値が最も高いアクション)を選択します。
- ターゲットネットワーク(ターゲットネットワーク) はそのアクションを実行した後に生成されるターゲット Q 値を計算します。
with torch.no_grad():
# メインネットワークを使用して次の状態の最適なアクションを選択
next_q_values = q_network(data.next_observations)
next_actions = torch.argmax(next_q_values, dim=1, keepdim=True)
# ターゲットネットワークを使用してこれらのアクションの Q 値を評価
target_q_values = target_network(data.next_observations)
target_max = target_q_values.gather(1, next_actions).squeeze()
# TD-ターゲットを計算
td_target = data.rewards.flatten() + args.gamma * target_max * (1 - data.dones.flatten())
# 現在の Q 値を計算
old_val = q_network(data.observations).gather(1, data.actions).squeeze()
loss = F.mse_loss(td_target, old_val)
現代の深層強化学習には、優先経験再生やデュエリングネットワーク(Dueling Networks)などのさらなる改良技術もありますが、ここでは触れません。
Optuna#
深層強化学習における最も重要なタスクの 1 つは、良好な訓練ハイパーパラメータのセットを見つけることです。Optuna は、最適なハイパーパラメータの組み合わせを自動的に検索するのを助けるライブラリです。
ポリシー勾配#
前述の Q-Learning と DQN はすべて Value-based 方法に属し、価値関数を推定することによって間接的に最適ポリシーを探します。ポリシー()の存在はアクション価値の推定に完全に依存しています。ポリシーは価値関数から生成されるものであり、例えば与えられた状態で最も高い価値のアクションを選択する貪欲ポリシーです。
ポリシーベースの方法を使用することで、私たちはポリシーを直接最適化し、価値関数を学習する中間ステップを回避したいと考えています。次に、ポリシー勾配(Policy Gradient)のサブセットを深く学びます。
ポリシーベースの方法では、最適化はほとんどの場合オンポリシーであり、各更新時に最新バージョンの から収集したデータ(行動軌跡)のみを使用します。
パラメータ化された確率ポリシー#
例えば、ニューラルネットワーク がアクションの確率分布(確率ポリシー) を出力するようにします:
目的関数 、最適化パラメータ 、パラメータ化されたポリシーの性能を最大化するために勾配上昇を行います。
利点#
統合が容易#
- 追加データ(アクション価値)を保存することなくポリシーを直接推定でき、エンドツーエンドであると理解できます。
確率ポリシーを学習できる#
出力がアクションの確率分布であるため、エージェントは状態空間を探索し、常に同じ軌跡に従う必要がありません。探索 / 利用のトレードオフを手動で実装する必要はありません。DNQ は決定論的ポリシー(deterministic policy)を学習しますが、いくつかのテクニック(例えば ε- 貪欲ポリシー)を通じてランダム性を導入しましたが、これは価値関数方法の内在的な特性ではありません。また、状態の不確実性を自然に処理し、知覚の混乱問題を解決します。
例えば、以下のシナリオでは、エージェントの掃除機がほこりを吸い取り、ハムスターを傷つけないようにする必要があります。掃除機は壁の位置しか感知できません。図中のこれら 2 つの赤い状態は「混乱状態」(aliased states)と呼ばれます。なぜなら、これらの状態では、エージェントが感知するのは壁の位置であり、上部と下部の両方に壁があるからです。これにより、状態の曖昧さが生じ、具体的な赤い状態がどれであるかを区別できなくなります。
決定論的ポリシーを使用する場合、掃除機は赤い状態で常に右または左に移動し、誤った方向を選択するとループに陥ります。ε- 貪欲ポリシーを使用しても、掃除機は主に最適なポリシーに従いますが、誤った状態では誤った方向を繰り返し探索する可能性があり、効率が低下します。
高次元、連続アクション空間での有効性#
高次元または連続アクション空間では、ポリシー勾配法が特に効果的です。
自動運転車は、各状態で無限のアクション選択肢を持つ可能性があります —— 例えば、ハンドルを 15°、17.2°、19.4° 回転させることができるか、他のアクション(クラクションを鳴らすなど)を行うことができます。Deep Q-Learning は、各可能なアクションの Q 値を計算する必要がありますが、連続アクション空間で最大の Q 値を選択すること自体が最適化問題です。
対照的に、ポリシー勾配法はアクションの確率分布を直接出力し、各アクションの Q 値を計算および保存する必要がないため、複雑な連続アクションシーンでより効率的です。
収束性が良好#
値方法では、 を使用して最大 Q 値を取得してポリシーを更新します。この場合、Q 値がわずかに変化しても、アクションの選択が劇的に変わる可能性があります。例えば、訓練中に左折の Q 値が 0.22 で、次に右折の Q 値が 0.23 に変わると、ポリシーは大きく変化し、左よりも右を選択することが増えます。
一方、ポリシー勾配法では、アクションの確率が時間とともに滑らかに変化し、ポリシーがより安定します。
欠点#
局所最適#
常に局所最適に収束し、全体最適には至らないことが多いです。
訓練効率が低い#
訓練プロセスが遅く、効率が低いです。
高い分散#
分散が大きく、これは後のアクター - クリティックで原因と解決策を探ります。
具体的な分析#
ポリシー勾配は、エージェントが環境と相互作用するたびにパラメータ(ポリシー)を調整し、アクションの確率分布が報酬を最大化する良好なアクションをより多くサンプリングできるようにします。
目的関数#
私たちの目標は、期待報酬を最大化するパラメータ を見つけることです:
これは凹関数(値を最大化したい)であるため、勾配上昇法を使用します: .
しかし、目的関数の真の勾配は計算できません。なぜなら、それにはすべての可能な軌跡の確率を計算する必要があり、計算上非常に高価だからです。したがって、サンプルベースの推定(いくつかの軌跡を収集)を通じて勾配推定を計算したいと考えています。
さらに、環境の状態遷移確率(または状態分布)はしばしば未知であり、たとえ知られていても、複雑で非線形であり、その導関数を直接計算することはできません。つまり、この状態遷移の力学(マルコフ決定過程によって制御される)を微分してポリシーを最適化することはできません。
ポリシー勾配定理#
完全な導出は Andrej Karpathy のブログで見ることができます。ここでも以前に学習の要約を行いました:
ポリシー勾配入門 6. PG 導出
強化アルゴリズム(モンテカルロ強化)#
全体の エピソード の推定報酬を使用してポリシーパラメータ を更新します。
ポリシー を使用して 1 つのセグメント を収集し、そのエピソードを利用して勾配 を推定します。
最適化:
-
:
この部分は、特定の状態 とアクション に対して、具体的なアクション値(Q 値)ではなく、アクション確率の対数の勾配を計算します。 -
:
ここで は全体の軌跡 上の累積報酬であり、ポリシー を実行した後の総報酬を測定します。報酬率が高いほど、(状態、アクション)の組み合わせの確率を高め、逆に低ければ減少させます。
また、複数のセグメント(軌跡)を収集して勾配を推定することもできます: