機械学習周りのプログラミング中心。 イベント情報
ポケモンバトルAI本電子書籍通販中

ふかうら王でMacのCoreMLを使う(失敗)

ふかうら王(やねうら王のMCTS+DNNバージョン)でMacGPU, Neural Engineを使うことでMac上で高速動作できるか検証しました。しかし、今回のやり方ではうまくいきませんでした。

現象: onnxruntime上でCoreMLを利用する設定をしたが、onnxruntimeデフォルトのCPUプロバイダ利用時と速度が変わらない

失敗原因の仮説

  • (今回実験した)Intel MacではCoreMLがCPUしか使わない
  • dlshogiのモデルで使われているオペレータが、onnxruntimeがCoreMLを使う条件にヒットしない

実験手順メモ

今回は、ふかうら王で既にサポートされているonnxruntimeの内部で、CoreML Execution Providerを使うように改造します。

手順は以下の記事を参考にしましたが、やねうら王のバージョンアップで若干違いが出ました。

qiita.com

まずはCPUのみの動作を試し、その後CoreMLを使うように改造しました。

環境について

Intel Macなので、Neural Engineはついていません。

onnxruntimeのインストール

注意:これで入るものはCoreMLに対応していないようなので、後で削除します。

brew install onnxruntime

バージョン1.11.1が入りました。

やねうら王のclone

やねうら王のmaster (23a7f2a88f98bcbfcaf8e625fe1448ea8d612e2b)をclone

ソースの修正

先述の記事に従って、AVX2を使うように修正しました。他の修正点は最新バージョンのやねうら王では不要でした。

注:これが正しいのかよく理解していません。また、CoreMLとは関係ないのでこの実験においては修正しなくても良いと思います。

diff --git a/source/Makefile b/source/Makefile
index 94795789..734b218c 100644
--- a/source/Makefile
+++ b/source/Makefile
@@ -548,7 +548,7 @@ else ifeq ($(TARGET_CPU),AVX512)
        CPPFLAGS += -DUSE_AVX512 -DUSE_BMI2 -march=skylake-avx512

 else ifeq ($(TARGET_CPU),AVX2)
-       CPPFLAGS += -DUSE_AVX2 -DUSE_BMI2 -mbmi -mbmi2 -mavx2 -march=corei7-avx
+       CPPFLAGS += -DUSE_AVX2 -DUSE_BMI2 -mbmi -mbmi2 -mavx2 -march=core-avx2

 else ifeq ($(TARGET_CPU),SSE42)
        CPPFLAGS += -DUSE_SSE42 -msse4.2 -march=corei7

ビルド

make -C source COMPILER=g++ EXTRA_CPPFLAGS="-I/usr/local/include/onnxruntime/core/session -I/usr/local/include/onnxruntime/core/providers/cpu -fexceptions" EXTRA_LDFLAGS="-L/usr/local/lib -lonnxruntime" YANEURAOU_EDITION=YANEURAOU_ENGINE_DEEP_ORT_CPU

実行ファイルが source/YaneuraOu-by-gcc にできたので、将棋所にエンジン登録します。

モデルの準備

https://tadaoyamaoka.hatenablog.com/entry/2021/08/17/000710 からダウンロードして解凍 source/model-dr2_exhi/model-dr2_exhi.onnx にモデルファイルが存在するようにする。

将棋所での設定

  • EvalDir: model-dr2_exhi
  • DNN_Model1: model-dr2_exhi.onnx

実行

この状態で将棋所でLesserkaiと対局することができました。CPUのみの利用で、40NPS程度でした。

改造する

やねうら王のソースは以下のように修正しました。CPUプロバイダはコメントアウトして、必ずCoreMLが使われるようにしています。

Core ML Execution Providerの説明

diff --git a/source/eval/deep/nn_onnx_runtime.cpp b/source/eval/deep/nn_onnx_runtime.cpp
index 3f11de70..e5e89178 100644
--- a/source/eval/deep/nn_onnx_runtime.cpp
+++ b/source/eval/deep/nn_onnx_runtime.cpp
@@ -14,6 +14,7 @@
 #include <tensorrt_provider_factory.h>
 #else
 #include <cpu_provider_factory.h>
+#include <coreml_provider_factory.h>
 #endif
 #include "../../usi.h"

@@ -28,6 +29,9 @@ namespace Eval::dlshogi
                Ort::SessionOptions session_options;
                session_options.DisableMemPattern();
                session_options.SetExecutionMode(ORT_SEQUENTIAL);
+               Ort::Env env = Ort::Env{ORT_LOGGING_LEVEL_ERROR, "Default"};
+               uint32_t coreml_flags = 0;
+               Ort::ThrowOnError(OrtSessionOptionsAppendExecutionProvider_CoreML(session_options, coreml_flags));
 #if defined(ORT_DML)
                Ort::ThrowOnError(OrtSessionOptionsAppendExecutionProvider_DML(session_options, gpu_id));
 #elif defined(ORT_TRT)
@@ -64,7 +68,7 @@ namespace Eval::dlshogi
                // Ort::ThrowOnError(OrtSessionOptionsAppendExecutionProvider_Tensorrt(session_options, gpu_id));
                Ort::ThrowOnError(OrtSessionOptionsAppendExecutionProvider_CUDA(session_options, gpu_id));
 #else
-           Ort::ThrowOnError(OrtSessionOptionsAppendExecutionProvider_CPU(session_options, true));
+           //Ort::ThrowOnError(OrtSessionOptionsAppendExecutionProvider_CPU(session_options, true));
 #endif
 #if defined(_WIN32)
                // Windows環境ではwstringでファイル名を渡す必要があるようだが?

CoreMLに対応したonnxruntimeの入手

brewで入るonnxruntimeは、ヘッダにcoreml関係が入っておらず非対応のようでした。dylibファイルのサイズも小さいです。

干渉しないように一旦アンインストールしました。

brew uninstall onnxruntime

GithubのReleasesから、ビルド済みバイナリ(onnxruntime-osx-x86_64-1.11.1.tgz)とソース(Source code (tar.gz))両方を入手します。ビルド済みバイナリの方には、coreml関係のヘッダがなかったためです。

https://github.com/microsoft/onnxruntime/releases/tag/v1.11.1

ビルド

make -C source COMPILER=g++ EXTRA_CPPFLAGS="-I/path/to/onnxruntime-1.11.1/include/onnxruntime/core/session -I/path/to/onnxruntime-1.11.1/include/onnxruntime/core/providers/cpu -I/path/to/onnxruntime-1.11.1/include/onnxruntime/core/providers/coreml -fexceptions" EXTRA_LDFLAGS="-L/path/to/onnxruntime-osx-x86_64-1.11.1/lib -lonnxruntime" YANEURAOU_EDITION=YANEURAOU_ENGINE_DEEP_ORT_CPU

強引ですが、インクルードファイルはソースコードを解凍したもの、ライブラリファイルはビルド済みバイナリを解凍したものを指します。

ライブラリを検索パスにコピー

sudo cp -a /path/to/onnxruntime-osx-x86_64-1.11.1/lib/libonnxruntime.1.11.1.dylib /usr/local/lib

セキュリティ許可

ビルドしたバイナリを初回実行するとセキュリティエラーが出るので、「セキュリティとプライバシー」で許可します。

実行結果

エラーは出ずに実行できましたが、速度は40NPS程度のままでした。アクティビティモニタGPU使用率を確認しましたが、対局中にGPU使用率が上がるとは言えませんでした。

onnxruntimeのソースを軽く読んだ感じだと、モデルをCoreMLに丸投げするのではなく、onnxruntime側で解釈した上でCoreMLに処理を移譲出来る部分を個別に移譲するような仕組みのようです。以下のパターンが考えられますが、検証方法がまだわかりません。

  • dlshogiモデルに含まれるオペレータがCoreMLへ変換できない
  • オペレータをCoreMLに移譲しているが、CoreML側の判断でCPU上で実行されており、速度面ではonnxruntimeのオペレータ実装と違いがない
    • Intel Mac上のCoreMLがGPUを使う仕様になっているかどうか、確認できていない

今後の課題

原因を切り分ける必要があります。onnxruntimeを使わずにCoreMLを直接呼び出すプログラムであれば、dlshogiモデルがGPU上で動作するかを検証したいと思います。