はじめに
こんにちは、ihpolypheです。
夏休みの自由研究として「Pokemon ShowDownを使った強化学習AI」を試してみました。
この記事では 強化学習アルゴリズムそのものの解説は省略 し、
状態設計・報酬設計・学習結果 を中心に紹介します。
対象読者:
- 強化学習の基礎をある程度理解している方
- ポケモン環境でRLを実装してみたい方
- Pokemon ShowDown の情報収集をしている方
Pokemon ShowDownとは
Pokemon ShowDownはブラウザ上で動作する無料のポケモンバトルシミュレーターです。
強化学習を行うには「ポケモンの状態を逐次取得する」必要がありますが、本家ポケモンでは正攻法でアクセスできません。そのため、今回は ShowDown のローカルサーバを立ち上げて学習環境としました。
セットアップ手順
$ git clone https://github.com/smogon/pokemon-showdown.git
$ cd pokemon-showdown
$ npm install
$ node pokemon-showdown start --no-security
学習アルゴリズム
アルゴリズムは AI vs AI 形式で進行します。ターンごとに報酬を計算し、リプレイバッファを用いて Q 関数を更新しました。
Q 関数には Dueling DQN を採用しています。これは以下の理由によります。
-
離散的な状態空間に適している
-
「勝敗がほぼ決まった状況」でも安定的に収束しやすい
-
行動価値(Advantage)と状態価値(Value)を分離できる
(※このあたりの理論的背景は正直まだ理解が浅いので、間違いがあればコメントいただけると嬉しいです 🙏)
class DuelingDQN(Model):
# Q関数は価値観数とアドバンテージ関数から算出
Q = V + (A - mean(A))
def combine_va(tensors):
v_, a_ = tensors
a_mean = K.mean(a_, axis=1, keepdims=True)
return v_ + (a_ - a_mean)
q = Lambda(combine_va, name="q_values")([v, a])
super(DuelingDQN, self).__init__(inp, q, name="DuelingDQN")
状態設計
モデルに入力する特徴量を以下のように設計しました。
記事作成時に「相手ポケモンの情報が未考慮」であることに気づいたので、今後の改善点です。
(25/8/25追記)以下に加えて相手のポケモンの状態、タイプ、HPなども追加しました。
カテゴリ | 項目 | 内容 / 形式 | 備考 |
---|---|---|---|
自分のアクティブポケモン | HP割合 | 0〜1 | しきい値フラグ付き (75%, 50%, 25%) |
状態異常 | one-hot | 眠り・毒・火傷・麻痺・凍り・猛毒 | |
能力変化 | -6~+6 → -1~1 に正規化 | 攻撃・防御・特攻・特防・素早さ | |
技情報 (最大4つ) | PP割合、効果抜群/半減フラグ、技カテゴリフラグ | 0埋めパディング | |
相手のアクティブポケモン | HP割合 | 0〜1 | しきい値フラグ付き (75%, 50%, 25%) |
状態異常 | one-hot | 眠り・毒・火傷・麻痺・凍り・猛毒 | |
能力変化 | -6~+6 → -1~1 に正規化 | 攻撃・防御・特攻・特防・素早さ | |
技情報 (最大4つ) | PP割合、効果抜群/半減フラグ、技カテゴリフラグ | 0埋めパディング | |
チーム全体(最大6体) | HP割合 | 0〜1 | |
状態異常 | one-hot | ||
技のPP割合 | 最大4つ | ||
生存数 / 平均HP | scalar | ||
バトル全体 | ターン数 | 0〜1 (100でclip) | |
残存ポケモン割合 | 0〜1 | ||
効果抜群・半減フラグ | string検索結果 |
報酬設計
学習の精度に大きく影響するため、ここは試行錯誤中です。
✅ 正の報酬
条件 | 報酬値 | 意図 |
---|---|---|
HP回復 | HP_diff * 2.0 |
回復行動を評価 |
相手が眠り/凍り | +2.0 | 状態異常付与を評価 |
相手が麻痺/火傷/毒 | +1.2〜1.5 | 継続ダメージ・行動制限 |
攻撃/特攻/素早さ上昇 | +1.5 | バフ行動を評価 |
防御/特防上昇 | +1.0 | 耐久強化 |
不利対面から有利対面へ交換 | +2.0〜3.0 | 適切な交代を奨励 |
ひるみ発生 | +0.3 | 小さい報酬 |
❌ 負の報酬
条件 | 報酬値 | 意図 |
---|---|---|
HP減少 | HP_diff * 3.0 |
被ダメを強くペナルティ |
自分のポケモン戦闘不能 | -4.0 | 大きな損失 |
効果いまひとつ | -1.5 | ミスマッチ行動を抑制 |
テラスタルで不利技 | -0.5 | リスクを低く設定 |
交換した場合 | -2.0 | 無駄な交代の抑制 |
バフ中に交換 | -3.0 | 有利状態を手放す行動を抑制 |
学習結果
学習環境:AI vs AI、50バトル(1バトル ≈ 50ターン → 約2500ステップ)
結果:
- lossが収束せず(ポケモンがバトルごとに変わるため汎化が難しい可能性)
- 勝率は上がらず頭打ち
- 自作AIと対戦したところ、まだ容易に勝ててしまう
例:TensorBoard 出力
勝率推移:
学習中の様子:
(25/8/27追記)
優先度付きリプレイバッファを追加して学習させることで学習が進むにつれlossを小さくすることができるようになりました。ただしAIとの対戦では特に改善点はなく、簡単に勝利できてしまいます。
DQN手法の限界を感じました。
考察と今後の展開
現状の課題:
-
相手ポケモンの情報を状態に含めていない
↑改善済み。あまりバトルの行動に変化なし。 -
報酬設計が交換行動に偏っている
-
サンプル数が少なく、汎化できていない
-
loss未収束 → ネットワーク設計やアルゴリズム選択に課題
今後の改善案:
-
状態空間の拡張(相手情報の導入)
↑対応済み。 -
PPOなど方策勾配法の検討
-
Self-Playや対人データによる学習
-
報酬スケーリングの調整
まとめ
-
Pokemon ShowDown を使って強化学習環境を構築
-
状態設計・報酬設計の工夫を試したが、十分な成果は得られず
-
今後の改善ポイントが明確になった
まだまだ改良の余地がありますが、今後の進捗も Qiita にまとめていきたいと思います。
最後までお読みいただきありがとうございました!
関連記事