【Unity】Barracudaを使用したMachineLearningの組み込み

はじめに

こんにちは、SHOJIです。

今回はUnityで機械学習を用いたゲームを作る際にハマったポイントを備忘録的に残していきます。

画像認識だったので画像処理についても記載するので結構雑多です。

Kerasで作成したモデルをONNXに変換する

  1. 事前にJupyter NotebookやGoogle Colaboratoryで学習済みモデルを作成します。

  2. モデルをSaveModelで保存します。SaveModelでの保存は拡張子を指定せずにmodel.saveを呼び出すことで行えます。

    model.save('saved_model/my_model')
    
    www.tensorflow.org

  3. pip install tf2onnxでONNX変換のためのライブラリをインストールします。

  4. python -m tf2onnx.convert --saved-model my_model --output my_model.onnx コマンドでONNXに変換されます。

Barracuda packageの追加

  1. Unity Editorを起動し、PackageManagerを開きます。

  2. 「+」-「Add package from git URL...」からBarracudaのGitリポジトリhttps://github.com/Unity-Technologies/barracuda-release.git)を指定します。 github.com

推論用スクリプトの追加

github.com

  1. MonoBehaviourを継承したクラスを作成し、ONNXモデルを設定するための変数を用意します。

    public class QuickdrawModel : MonoBehaviour
    {
        [SerializeField]
        private NNModel modelAsset;
    }
    
  2. Modelをロードし、Workerを生成します。

    Model model = ModelLoader.Load(modelAsset);
    IWorker worker = WorkerFactory.CreateWorker(WorkerFactory.Type.Compute, model);
    
  3. Tensorを生成し、モデルの入力データを定義します。 僕の場合は、64*64のグレースケールの画像を入力データとするモデルのため、このようなshapeになります。

    Tensor input = new Tensor(new int[] { 1, 64, 64, 1 }, array);
    

    参考までに、Jupyter notebook でモデルのInputLayerを見るとこのような状態です。

    _________________________________________________________________
     Layer (type)                Output Shape              Param #   
    =================================================================
     input_2 (InputLayer)        [(None, 64, 64, 1)]       0         
    

    画像を1枚ずつ渡すモデルのため、第二引数のarrayは64*64のfloat型配列になっています。

    int size = 64;
    float[,] array = new float[size, size];
    
  4. Tensorをworkerに渡して予測を行い、予測結果を取得します。

    Tensor input = new Tensor(new int[] { 1, 64, 64, 1 }, array);
    worker.Execute(input);
    input.Dispose();
       
    var output = worker.PeekOutput("output_layer_name");
    

    worker.PeekOutputのoutput_layer_nameは、推論結果を出力したいレイヤー名に置き換えてください。

  5. これらの処理を良い感じに整理したら、UnityEditorで適当なGameObjectに適用してInspectorからONNXモデルを設定して実行します。

画像データの加工

OpenCVが使えないので、いくつか自前で前処理を入れる必要がありました。

  1. RGBの範囲を1~255から0~1に変換したい場合

    このような変換をしたい場合は「変換前の範囲の中間値」で割った値に「変換後の最小値」を加えます。 ImageNetの処理もこのようにして変換しています。 https://github.com/keras-team/keras-applications/blob/master/keras_applications/imagenet_utils.py

    例:0~255の値を0~1の範囲に変換する場合は、x / 127.5 + 0となります。

      0~255の値を-1~1の範囲に変換する場合は、x / 127.5 + (-1)となります。

  2. 白黒反転させたい場合

    「値の取りうる最大値」から値を引きます。 例:0~255の値を反転させる場合は、255-xとなります。   0~1の値を反転させる場合は、1-xとなります。

  3. グレースケールに変換したい場合

    0.2989 * R + 0.5870 * G + 0.1140 * Bで算出します。

おわりに

ONNXへの変換と、入力データの形式を合わせるところで若干手間取りましたが、やり方さえ分かってしまえばBarracudaは本当に便利ですね。とても簡単にTensorFlowで作成したモデルを転用できるので、個人でもMachineLearningを組み込んだゲームが作れて夢が広がるなと思いました。

とはいえ、どこにMLを適用するかは難しいですね。ゲームでMLというとNPCの動作の制御とかやってくれると嬉しいですが、、個人で学習データ(理想的なNPCの動作)を集めるのが難しいですし、個人でMLを使うならMLで出来ることから逆算して仕様を考えるのが現実的かなと思います。