0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

[PyBullet] [強化学習] [カメラ] 2軸ロボットアームの強化学習 Part5 (DQN)

0
Posted at

はじめに

PyBullet (Python上で動く物理シミュレータ) を使用して,ロボットアームを可視化して,動かしたい.

前記事では,エージェントの学習方法として "Q学習 (off-policy TD学習)" を採用して,PyBullet 内の2軸ロボットアームの強化学習を実装した.

前記事の問題点は,Q学習が "Q-tableで行動価値関数を管理する" ため,状態数が増えるにつれてメモリが線形増加する点である.本記事で実装している2軸ロボットアームでは状態数が $1,369$ ($37 \times 37$) と管理可能な範囲であるが,6軸ロボットアームで同様の離散化を行うと $37^6 \approx 2.57 \times 10^9$ 状態となり,メモリと学習時間が爆発的に増大する.

本記事では,エージェントの学習方法として "DQN (Deep Q-Network)" を採用する.DQNでは,Q-tableの代わりに "ニューラルネットワーク" でQ関数を近似するため,状態空間が大きくなってもパラメータ数はネットワークの重みの数で固定できる.
グリッパー付きの2軸ロボットアームを使用して,干渉物が存在しない環境下にて,複数のカメラから把持物体の位置を取得して,物体を把持する(下図はPyBullet上のグリッパー付き2軸ロボットアームである).赤枠がグリッパー,緑枠が把持したい物体,青枠がカメラ,青色の球が初期位置である.
2DoF_Random_Agent.png

本記事を実施してできる最終的なものは下記動画の通りである.エージェントの学習方法は "DQN" である.
下記は,学習中の強化学習による経路生成の動画である.

DQN_再現フェーズ.gif

下記は,学習後の強化学習による経路生成の動画である.

DQN_推論フェーズ.gif

本記事で実装すること

  • エージェントの学習方法は"DQN"

本記事では実装できないこと (将来実装したい内容)

エージェントの学習手法

  • Actor-Critic
  • PPO (Proximal Policy Optimization)

動作環境

  • macOS Sequoia (バージョン15.5)
  • Python3 (3.13.3)
  • PyBullet (3.25) (物理シミュレータ)
  • Numpy (2.3.0) (数値計算用ライブラリ)
  • PyTorch (2.7.0) (深層学習用ライブラリ)

PyBullet のインストール方法

PyBullet のインストール方法については下記にて,説明したため,割愛する.

PyBulletの使用方法

Pythonの物理シミュレータであるPyBulletの使用方法について説明する.
下記リンクのPyBullet公式で調べながら,PyBulletを使用している.

上記リンクを開くと,下図のようなサイトに飛ぶ.使用方法を調べるときは,下図の赤枠内の「PYBULLET QUICKSTART GUIDE」タグをクリックする.
PyBullet公式.png

「PYBULLET QUICKSTART GUIDE」タグをクリックすると,下図のようなドキュメントを見ることができる.基本的には,ドキュメントに記載されている関数の使用方法を見て,ソースコードを作成している.
PyBulletドキュメント.png
PyBulletの関数の引数や戻り値をもっと知りたいのでしたら,上記ドキュメントを見た方がわかりやすいです.

2軸ロボットアームの定義

2軸ロボットアームの定義については下記にて,説明したため,割愛する.

グリッパーの定義

グリッパーの定義については下記にて,説明したため,割愛する.

グリッパー付き2軸ロボットアームのURDF作成

グリッパー付き2軸ロボットアームのURDF作成については下記にて,説明したため,割愛する.

URDFに関しては,下記にて説明したため,割愛する.

カメラによる把持物体の2次元位置を取得

PyBulletでカメラ情報を取得する方法については下記にて,説明したため,割愛する.

強化学習に関して

強化学習は機械学習の一つの種類である.強化学習とは,自分で試行錯誤をしてデータを集め,詰めたデータから良い行動を学習することである.
強化学習のイメージは下図の通りである.

Env_Agent.drawio.png

1:「環境」から「エージェント」に対して,「状態」を渡す
2:「エージェント」が「状態」より,「行動」を選択する
3:「エージェント」から「環境」に対して,「行動」を実行する
4:「環境」が「行動」より,「状態」を更新する
5:「環境」から「エージェント」に対して,「報酬」を渡す
6:「エージェント」が「報酬」より,「学習」する

ここで,用語の定義をする.

用語 用語の定義
エージェント 行動の主体者
環境 エージェントと相互作用を行う対象
状態 エージェントの行動によって置かれるエージェントの状況
行動 エージェントが起こすアクション
報酬 エージェントの行動に対する環境からのフィードバック

本記事での "環境" は "PuBullet上のロボットと環境" である."エージェント" は "ロボットを動かすための頭脳" である.
以下では,本記事で使用する "環境" と "エージェント" を説明していく.

強化学習の環境に関して

強化学習の環境に関しては下記にて,説明したため本記事では割愛する.

強化学習のエージェントに関して

強化学習のエージェントに関して説明する.強化学習の全体像の中で,下図の赤枠で囲んだ「エージェント」を説明する.

Env_AgentAtract.drawio.png

エージェントは,環境から「状態」を受け取り,「行動」を行い,「報酬」を得て,次の「状態」へ遷移する.下記のように1エピソードが完了するまで,「状態(S)」,「行動(A)」,「報酬(R)」,「状態(S)」のループとなる.
$S_{0}, A_{0}, R_{0}, S_{1}, A_{1}, R_{1}, ...$

行動価値関数に関して

行動価値関数については下記にて,説明したため本記事では割愛する.

DQN (Deep Q-Network) に関して

エージェントの学習方法の一種である "DQN" に関して説明する.DQNは2013年にDeepMindが発表した手法 (Mnih et al., 2015) であり,Q学習のQ-tableをニューラルネットワークで置き換えたものである.

Q学習とDQNの違いを下表にまとめた.

比較項目 Q学習 DQN
Q関数の表現 テーブル (辞書型) ニューラルネットワーク
パラメータ数 状態数 × 行動数 ネットワークの重み数 (固定)
状態空間 離散値のみ 連続値・離散値どちらも対応
経験の再利用 なし (都度学習) 経験再生バッファ
学習安定化 なし ターゲットネットワーク
収束速度 状態数が少なければ速い 状態数が多くても対応可能

ニューラルネットワークによるQ関数の近似

DQNでは,Q関数をパラメータ $\theta$ を持つニューラルネットワーク $Q(s, a; \theta)$ で近似する.ニューラルネットワークの入力は "状態 $s$",出力は "各行動のQ値" である.

\displaylines{
Q(s, a; \theta) \approx Q^*(s, a) \\
\\
\theta ... \text{ニューラルネットワークの重みパラメータ} \\
Q^*(s, a) ... \text{最適行動価値関数}
}

本記事では,入力が2次元(関節1・関節2の状態インデックスを $[0, 1]$ に正規化),隠れ層が2層,出力が9次元(行動数)の全結合ニューラルネットワークを使用する.ネットワークの構造を下表にまとめた.

種類 ユニット数 活性化関数
入力層 全結合 2 (状態次元数) -
隠れ層1 全結合 64 ReLU
隠れ層2 全結合 64 ReLU
出力層 全結合 9 (行動数) -

経験再生 (Experience Replay) に関して

DQNでは,"経験再生バッファ" と呼ばれるメモリに過去の経験 $(s_t, a_t, r_t, s_{t+1}, done_t)$ を蓄積し,学習時にミニバッチをランダムサンプリングする.

経験再生が必要な理由は,Q学習のように "直前の経験で即時更新する" と,連続したステップの経験が相関しているため,ネットワークが不安定に振動するからである.ランダムサンプリングにより相関を壊すことで,学習が安定する.

経験再生バッファの役割を下表にまとめた.

役割 説明
経験の蓄積 $(s_t, a_t, r_t, s_{t+1}, done_t)$ をバッファに保存
相関の除去 ランダムサンプリングで連続経験の相関を断ち切る
経験の再利用 同じ経験を複数回の学習に使い回す (サンプル効率向上)

ターゲットネットワーク (Target Network) に関して

DQNでは,Q値の更新目標 (TD目標値) の計算に "ターゲットネットワーク ($\theta^-$)" を使用する.ターゲットネットワークは一定ステップごとにメインネットワーク ($\theta$) からコピーされ,それ以外の期間は固定される.

ターゲットネットワークが必要な理由は,メインネットワーク ($\theta$) だけで更新すると,更新対象と更新目標が同じネットワークになるため,"移動する目標を追いかける" 問題 (Moving Target Problem) が発生するからである.ターゲットネットワークを固定することで,TD目標値が安定し,学習が収束しやすくなる.

ネットワーク パラメータ 更新タイミング
メインネットワーク $\theta$ 毎ステップ (勾配降下法)
ターゲットネットワーク $\theta^-$ $N$ ステップごとに $\theta^- \leftarrow \theta$

DQNの損失関数と更新式

DQNの損失関数は以下の通りである.

\displaylines{
L(\theta) = \mathbb{E} \left[ \left( \underbrace{r_t + \gamma \max_{a'} Q(s_{t+1}, a'; \theta^-)}_{\text{TD目標値 (ターゲットネットワーク使用)}} - \underbrace{Q(s_t, a_t; \theta)}_{\text{メインネットワークの出力}} \right)^2 \right] \\
\\
\text{ただし,エピソード終了時 (}done = True\text{) は} \\
r_t + \gamma \max_{a'} Q(s_{t+1}, a'; \theta^-) \rightarrow r_t \quad \text{(次状態が存在しないため)}
}

損失 $L(\theta)$ を最小化するように,Adam オプティマイザで $\theta$ を更新する.ターゲット値の計算には $\theta^-$(固定されたターゲットネットワーク)を使い,メインネットワーク $\theta$ は更新しない (勾配を切る torch.no_grad()).

DQNの学習フロー

DQNの学習フローを以下に示す.

1:初期状態 $s_0$ を取得する
2:ε-greedy方策で行動 $a_t$ を選択する
3:行動 $a_t$ を実行して,報酬 $r_t$・次の状態 $s_{t+1}$・終了フラグ $done$ を得る
4:経験 $(s_t, a_t, r_t, s_{t+1}, done)$ を経験再生バッファに追加する
5:バッファに十分な経験が溜まったら,ミニバッチをランダムサンプリングして $L(\theta)$ を最小化するように $\theta$ を更新する
6:$N$ ステップごとに $\theta^- \leftarrow \theta$ (ターゲットネットワーク更新)
7:$s_t \leftarrow s_{t+1}$ として2に戻る

DQNはQ学習と同じ "off-policy" の学習手法であるため,学習フローはQ学習と類似している.主な違いは "経験をバッファに貯めてからミニバッチで更新する" 点と "ターゲットネットワークを使ってTD目標値を計算する" 点である.

行動の選択方法 (ε-greedy法)

行動の選択方法 (ε-greedy法) に関しては下記にて,説明したため本記事では割愛する.

ソースコード一覧

本記事で使用する全ソースコードの一覧を下表にまとめる.フォルダ名が記載されているものは,そのフォルダを作成して,フォルダ内にファイルを保存してください.

URDFファイル

ファイル名 概要
camera_back_to_front.urdf 手前方向(-Y方向)を向いているカメラの定義
camera_front_to_back.urdf 奥行き方向(+Y方向)を向いているカメラの定義
camera_left_to_right.urdf 右方向(+X方向)を向いているカメラの定義
camera_right_to_left.urdf 左方向(-X方向)を向いているカメラの定義
camera_up_to_down.urdf 下方向(-Z方向)を向いているカメラの定義
grasp_object.urdf 把持物体の定義
robot_2dof.urdf グリッパーなし2軸ロボットアームの定義
environment_2dof.urdf 2軸ロボットアームの環境を定義
robot_2dof_hand.urdf グリッパー付き2軸ロボットアームの定義

Pythonファイル

ファイル名 概要
constant.py 定数の定義 (DQN追加)
main.py 全体的なメイン処理
pybullet_grasp.py PyBulletの把持物体
pybullet_gripper.py PyBulletのグリッパー
pybullet_robot.py PyBulletのロボット
pybullet_camera.py PyBulletのカメラ
pybullet_agent_controller.py PyBulletのエージェント制御 (DQN対応・done引数追加)
BaseAgent.py PyBulletのエージェントの抽象クラス
RandomAgent.py PyBulletのランダムに動くエージェント
MonteCarloAgent.py PyBulletのモンテカルロ法で学習するエージェント
SarsaAgent.py PyBulletのSARSAで学習するエージェント
QLearningAgent.py PyBulletのQ学習で学習するエージェント
DQNAgent.py PyBulletのDQNで学習するエージェント
pybullet_environment_controller.py PyBulletの強化学習用の環境制御
BaseEnvironment.py PyBulletの強化学習用の環境の抽象クラス
DiscretizeEnvironment.py PyBulletの強化学習用の離散値環境
pybullet_main.py PyBulletのメイン処理 (DQNループ)

pybullet_main.py の変更点に関して

pybullet_main.py__learning() メソッドをDQN用に変更した点について説明する.

Q学習とDQNの学習ループの違いを下表にまとめた.

比較項目 Q学習 (part4) DQN (part5)
最初の行動選択 whileループ内で選択 whileループ内で選択 (同じ)
add() の呼び出し add(s, a, r, s') add(s, a, r, s', done)
Q値の更新タイミング add() で即時更新 add() 内で経験バッファに追加 → ミニバッチ学習
update() の役割 ε更新のみ ε更新のみ (同じ)

DQNで done フラグを渡す理由は,エピソード終了時 ($done = True$) にTD目標値の計算から次状態の項を除外するためである.$done = True$ のとき,次状態は存在しないため $r + \gamma \max_{a'} Q(s', a'; \theta^-)$ の代わりに $r$ を使う.

DQNの __learning() の主要部分を抜粋すると以下の通りとなる (Q学習との差分は done フラグのみ).

while not done:
    # 行動の選択 (ε-greedy方策)
    action = self.__agent.get_action(state)

    # 1ステップ実行: (s, a) → (s', r, done)
    next_state, reward, done = self.__env.step(action)

    # 経験バッファに追加 + ミニバッチ学習 (done フラグを渡す点がQ学習と異なる)
    self.__agent.add(state, action, reward, next_state, done)

    # 状態の更新 (s ← s')
    state = next_state

# エピソード終了: 探索確率 ε の更新
self.__agent.update()

DQNAgent.py の実装に関して

DQNAgent.py では,Q学習 (QLearningAgent.py) と比較して,以下の点が異なる.

ニューラルネットワーク (_QNetwork)
Q-tableの代わりに,PyTorchで実装した2層MLPでQ関数を近似する.入力として渡す状態インデックスは $[0, 1]$ に正規化する (インデックス / (状態数 - 1)).

class _QNetwork(nn.Module):
    def __init__(self, state_dim, n_action, hidden_dim=64):
        super().__init__()
        self.layers = nn.Sequential(
            nn.Linear(state_dim, hidden_dim),
            nn.ReLU(),
            nn.Linear(hidden_dim, hidden_dim),
            nn.ReLU(),
            nn.Linear(hidden_dim, n_action)
        )

    def forward(self, x):
        return self.layers(x)

add() メソッドの役割変更
Q学習では add() で即時Q値更新を行っていたが,DQNでは以下の2つの処理を行う.

  1. 経験 $(s, a, r, s', done)$ を経験再生バッファに追加
  2. バッファに _MIN_REPLAY_SIZE 件以上溜まったら,ミニバッチをサンプリングしてネットワークを更新

ターゲットネットワークの更新
_TARGET_UPDATE_INTERVAL ステップごとにターゲットネットワークの重みをメインネットワークからコピーする.

パラメータファイル
DQNは dqn_param.pth (PyTorchのモデルファイル形式) にメインネットワークの重みを保存する.

パラメータ
割引率 γ 0.9
オプティマイザ Adam
学習率 (Adam) 0.001
隠れ層のユニット数 64
経験バッファサイズ 10,000
ミニバッチサイズ 64
学習開始最小経験数 500
ターゲット更新間隔 100ステップ
探索確率の最小値 ε_min 0.1
探索確率の最大値 ε_max 0.9
探索確率の減衰率 0.9999

PyBulletのメイン処理 (pybullet_main.py)

p.connect() 関数の引数である p.DIRECTp.GUI に関しては下記にて,説明したため本記事では割愛する.

PyBulletでロボットを動かす

上記にて,ソースコードを説明した.
main.py ファイルを実施することによって,PyBullet上のロボットを動かしていく.

経路生成の動画

DQNに動くエージェントによる経路生成の動画を載せる.

動画1:学習時の経路生成の動画

DQN_再現フェーズ.gif

動画2:学習完了後の経路生成の動画

DQN_推論フェーズ.gif

DQNはQ学習と比べてニューラルネットワークで状態間の類似性を汎化できるため,より効率的に良い方策を学習することが期待される.また,状態空間が大きくなっても対応できるため,将来的に6軸ロボットアームへの適用も見据えた手法となっている.

おわりに

本記事では,Pythonを使用して,下記内容を実装しました

  • エージェントの学習方法としてDQNを採用して,PyBullet 内の2軸ロボットアームの強化学習

次記事では,下記内容を実装していきます.

  • エージェントの学習方法としてActor-Critic (方策勾配法) を採用して,PyBullet 内の2軸ロボットアームの強化学習

参考文献

本記事を作成する上で参考にした内容を記載する.

参考記事

参考論文

  • Mnih, V. et al. (2015). Human-level control through deep reinforcement learning. Nature, 518, 529–533.

参考書籍

  • ゼロから作るDeep Learning ❹ ―強化学習編
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?