I'm KUITARIDER.

がりゅうさんのサイキックミラクルブログ

【ポケモンAI】Python製ポケモン強化学習ライブラリ「poke_env」のインストール

対象読者:

github.com

目次


前回記事でPokemon Showdownのライブラリ機能について紹介しましたが、今回はさらにポケモンAI開発に特化した最新のPythonライブラリ(インターフェイス)を紹介します。以下のようなことができます。

  • 場の状態に応じて行動を決定する(※)"Player"を定義し、Player同士を自動対戦させて結果を収集
  • 強化学習用のOpenAI Gym 形式でのPlayerの定義、および対戦実行・学習
  • 上記のPlayerを実際のshowdownサーバーで自動的に戦わせる

※ これがいわゆるルールベースAI

僕は強化学習ではなくこの記事にあるようなCFRを実行するためにこのpoke_envを使用しているのですが、共通するインストール方法だけ紹介しておこうかと思います。(ので、強化学習機能の中身についてはまだ触れていません…)

shingaryu.hatenablog.com

今回も初心者向け解説です。

Pythonのインストール

poke-env はpython 3.6 以上が必要です。

既にインストールされているか調べる

既にPythonがインストールされている場合もあると思います。一例ですが、以下の場合は既にインストール済みです。

  • powershell(※1) 上で python -V と入力してバージョン番号が表示される

    f:id:shingaryu:20210710200921p:plain

  • (AnacondaでPythonをインストールした場合) Anaconda Prompt(※2) 上でpython -Vと入力してバージョン番号が表示される

    f:id:shingaryu:20210710202232p:plain

バージョン番号が3.6 以上の場合
-> Pythonに関してはインストールの必要はありません。

バージョン番号が3.6 未満の場合
-> アップデートする必要があります。(当記事では説明しませんが、別途Pythonのインストール関係の情報を参考ください)

※1 PowershellWindows標準のシェル(コマンドプロンプトの新しい版のようなもの)です。以降よく使います。 f:id:shingaryu:20210710200437p:plain

※2 Anaconda は科学計算用のモジュールをセットにしたPythonの仮想環境で、Anaconda Promptはその方式でインストールしたPythonを使うために用います。Anacondaと検索して以下のように色々インストールされていたら、過去にAnacondaでPythonを導入しているはずです。 f:id:shingaryu:20210710200249p:plain

Pythonの新規インストール

新規でインストールする場合は公式サイトのインストーラーからインストールできます。

www.python.org

Downloads > Download for Windows をクリックするとインストーラーがダウンロードできると思います。 f:id:shingaryu:20210710202746p:plain

あとはインストーラーを起動してそのままOKボタンを押して進めていくのですが、"Add Python 3.9 to PATH" にチェックを入れておくことをオススメします。 f:id:shingaryu:20210710203023p:plain

f:id:shingaryu:20210710203105p:plain

最終的にpython 3.6以上のインストールが確認できればOKです。 なおpoke-envモジュールのインストールにpipというパッケージマネージャも必要になりますが、python 3.4 以降であれば標準装備のはずなので、基本的は問題ないはずです。 f:id:shingaryu:20210710204204p:plain

Showdown サーバーのローカル起動

poke-envはshowdownサーバーに接続して評価用の対戦を実行します。

Githubで公開されているソースコードを、ローカルPC内にダウンロード or Cloneしてください。 方法は問いません。

  • git clone (powershellから) でClone
  • SourceTreeなどGUIからClone
  • GithubからZipファイルを直接DL

github.com

f:id:shingaryu:20210710204519p:plain

ソースコードがローカルで開けるようになったら、ディレクトリ直下でPowershellを開き、次のコマンドを実行します。
※Node.js のインストールが必要です。前回記事参考のこと。

node pokemon-showdown start --no-security

f:id:shingaryu:20210710205041p:plain

f:id:shingaryu:20210710205057p:plain

初回は依存関係の解決が行われて追加モジュールが色々DLされると思います。最終的に"Test your server at http://localhost:8000"と表示されれば成功です。 これでローカルPC内の他のプログラムから、ポート8000を通してshowdownサーバーにアクセスできるようになりました。 以降poke-env を使う際には、毎回このようにshowdownサーバーを別途立ち上げておく必要があります。

Visual Studio Code でのPython環境設定

Python の実行環境についてはなんでもいいのですが、説明の都合上VSCodeで行うことにします。

Python 用エクステンションをインストールしておいてください。

code.visualstudio.com

marketplace.visualstudio.com

Python用エクステンションをインストールしたVSCodeで、適当なフォルダを開きます。このフォルダ内にpoke-envを利用するソースコードを書いていきます。 f:id:shingaryu:20210710222030p:plain

少し省いた説明になりますが、以下の手順でサンプルコードを実行できることを確認してください。

  • hello.pyというファイルを作成
  • 下記のソースコードをhello.py に貼り付け
  • 画面左下にPythonインタプリタの選択画面が出てくるので、冒頭でインストールしたPythonのバージョンと一致しているか確認(※)
  • 合っていなかったら、クリックして正しいものに変更する
  • 右上の三角ボタンを押してhello.pyを実行
  • コンソールにHello worldと表示されれば成功!
msg = "Hello World"
print(msg)

f:id:shingaryu:20210710222449p:plain

※Anacondaなど色々Pythonをインストールしている人は、正確にインタプリタを設定する必要があります。 f:id:shingaryu:20210710223806p:plain

f:id:shingaryu:20210710222808p:plain

f:id:shingaryu:20210710222821p:plain

poke-env モジュールのインストール

Powershellで(※)以下のコマンドを実行します。

pip install poke-env
pip install tabulate

VSCodeのコンソールからだとエラーになります

このうちtabulateについてはpoke_envの実行には直接必要ではありませんが、サンプルコードを動かすのに必要となります。ただ、この記事に沿ってPython 3.6 以上の環境で進めている場合は、基本的にインストール済みと表示されると思います。

f:id:shingaryu:20210710210311p:plain

最後にこのコード実行してみて、特にエラーが表示されなければ環境設定は完了です。 このコードはasyncioという非同期処理ライブラリの基本機能を試しているだけのコードになりますが、特定のIDEの特定の環境設定(※)だとイベントループの生成に失敗して下記のようなエラーが出ることがあります。

"RuntimeError: This event loop is already running"

# -*- coding: utf-8 -*-
import asyncio

async def main():
    pass

if __name__ == "__main__":
    asyncio.get_event_loop().run_until_complete(main())

f:id:shingaryu:20210710210644p:plain

※ 筆者はSpyderでこのエラー表示を確認しました

サンプルコードの実行確認 - ランダムプレイヤー

参考: Cross evaluating random players — Poke-env documentation

まずは、とりあえずこのコードを実行してみましょう。

# -*- coding: utf-8 -*-
import asyncio

from poke_env.player.random_player import RandomPlayer
from poke_env.player.utils import cross_evaluate
from tabulate import tabulate


async def main():
    # ランダム行動をするPlayerオブジェクトを3個作成します
    players = [RandomPlayer(max_concurrent_battles=10) for _ in range(3)]

    # 相互に対戦させ評価を行います。Player同士の一つの組合わせごとに、20回の対戦を行います。
    cross_evaluation = await cross_evaluate(players, n_challenges=20)

    # 表示用のヘッダー行を作成します
    table = [["-"] + [p.username for p in players]]

    # 対戦結果を抽出して、それぞれのPlayerごとに1行ずつtableオブジェクトに追加していきます
    for p_1, results in cross_evaluation.items():
        table.append([p_1] + [cross_evaluation[p_1][p_2] for p_2 in results])

    # 見やすい表形式で結果を表示します
    print(tabulate(table))


if __name__ == "__main__":
    asyncio.get_event_loop().run_until_complete(main())

WARNINGが大量に出ていますが気にしないでください。正常です。

別途立ち上げておいたshowdownサーバー上で行われた、ランダムプレイヤー3人の総当り戦の結果が表示されました! ランダム行動をするプレイヤー同士(ポケモンもランダム)なので、当然どの組み合わせも勝率が0.5付近になっているはずです。

f:id:shingaryu:20210710212208p:plain

poke-envの仕組み

ここでpoke-envの仕組みについて少し解説します。 poke-envはライブラリを使用するユーザー(例としてAI開発者)が技選択や戦略の学習のロジックを埋め込むインターフェイスを、"Player"というクラスで抽象化しています。
Playerクラスで宣言されているメソッドの中でもっとも代表的なものが、choose_move(self, battle)です。Playerを実際にテスト対戦に用いるときには、技選択がリクエストされる度にこのメソッドが呼ばれます。


そこで、AI開発者は、自分だけの対戦AIの定義として、Playerクラスを継承したサブクラスを作ります。そして「その対戦AIは対戦の局面(battle)が与えられたときに、どのような行動を取るか?」というロジックをchoose_move(self, battle)の実装として表現します(単純なルールベースAIの場合)。実装が終わったら、poke-envに元々組み込まれている対戦実行や結果集計の便利なメソッドから性能評価を行います。

対戦実行メソッドの内部では、showdownサーバーへのwebsocket通信や、並列で対戦を実行するためのスレッド管理などが行われていますが、AI開発者はその内容を気にせずPlayerのロジックの記述だけに集中することができます。

ということで、上のコードで用いられている組み込みのRandomPlayerの定義は以下のようなものになっています。

class RandomPlayer(Player):
    def choose_move(self, battle) -> BattleOrder:
        return self.choose_random_move(battle)

強化学習用機能(EnvPlayer)

上記での説明は単純なルールベースAIに限ったもので、実際には、強化学習用の機能が存在します(というか、これが目的でみんな使っています)。具体的にいうと、EnvPlayer という強化学習用のPlayerクラスが存在します。

  • Player
  • EnvPlayer
  • TrainablePlayer

EnvPlayerにはstepというメソッドの宣言があります。これはOpenAI Gym の形式に則ったもので、このEnvPlayerオブジェクトを強化学習におけるEnvironmentとして外部から使用することを想定しています。

step(self, action: int) -> Tuple

筆者は残念ながら強化学習の知識に乏しいのでまだこの機能を試せていませんが、公式マニュアルにはkeras-rlと連携して戦略の学習を行う例があります。参考にしてみてください。

サンプルコードの実行確認 - 最大ダメージプレイヤー

参考: Creating a simple max damage player — Poke-env documentation

以下はサンプルコードの2つ目ですが、いわば「AI開発者が実装したルールベースAIの例」です。

# -*- coding: utf-8 -*-
import asyncio
import time

from poke_env.player.player import Player
from poke_env.player.random_player import RandomPlayer


class MaxDamagePlayer(Player):
    def choose_move(self, battle):
        # playerが一つでも攻撃技を使用可能なときは、攻撃する
        if battle.available_moves:
            # 使用可能な技の中で、技威力が一番高いものを探す
            best_move = max(battle.available_moves, key=lambda move: move.base_power)
            return self.create_order(best_move)

        # 攻撃技を使えない場合は、ランダムに控えポケモンと交代する
        else:
            return self.choose_random_move(battle)


async def main():
    start = time.time()

    # Playerオブジェクトを2つ作成
    random_player = RandomPlayer(battle_format="gen8randombattle")
    max_damage_player = MaxDamagePlayer(battle_format="gen8randombattle")

    # Playerを評価 (max_damage_player vs random_player の対戦を100回実行)
    await max_damage_player.battle_against(random_player, n_battles=100)

    print(
        "Max damage player won %d / 100 battles [this took %f seconds]"
        % (max_damage_player.n_won_battles, time.time() - start)
    )


if __name__ == "__main__":
    asyncio.get_event_loop().run_until_complete(main())

f:id:shingaryu:20210710221325p:plain

Max damage player won 88 / 100 battles [this took 31.858377 seconds]

ちゃんとランダムプレイヤーには88%の確率で勝利していますね。

まとめ

今回はインストール方法とサンプルコードの実行確認だけですが、ここから独自のPlayerサブクラスを定義して色々なことができます。 メインの強化学習機能もいつか試します。

正式名称がpoke_env なのかpoke-envなのか謎