Unity Technologies製推論エンジン Barracudaがスゴイという話

# 概要
2020年5月12日のML-Agents正式版公開に伴い、
ML-Agentsで利用されている推論エンジンのBarracudaが正式公開されたのでテストした所
導入および実施がありえない程簡単だったので記事にしました

[Unity Technologies 公式ブログ](https://blogs.unity3d.com/jp/2020/05/12/announcing-ml-agents-unity-package-v1-0/)
# 実施内容
[以前実施した](https://qiita.com/highno_RQ/items/7a33e7eaa6d77c93968f)画像分類モデルに加え、画風変換モデルをテストしました。

### 1.画像分類
空間内に配置した写真をUnityのカメラで撮影し、画像分類モデルで判定を行いました
![sc.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440309/030f3184-370c-6d81-a845-9f0200fedd47.png)

結果は次のようになりました

![malchese.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440309/f71df15a-5f46-f6ec-c451-1475b6bc4c0f.png)
**Maltese dog**

![convertible.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440309/fe86960b-1f59-41b8-dcc2-82fe846a5abb.png)
**convertible(オープンカー)**
見た限り、正しく分類できていそうです。

使用したモデルは次の通りです

|画像分類||
|:--|:--|
|モデル名|[VGG-19](https://github.com/onnx/models/tree/master/vision/classification/vgg)|
|入力|224×224×3|
| 出力 | 1000 |
| データセット | [ImageNet (ILSVRCA2012)](http://www.image-net.org/challenges/LSVRC/2012/)  |

### 2.画風変換
画像分類同様、空間内に配置した写真をUnityのカメラで撮影し、画風変換モデルを適用したものを表示しました

![candy.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440309/04e9a1e2-4642-a570-4949-21f4fdbda3b6.png)
**イラスト風**

![mosaic.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440309/1d604a6d-dc35-ed58-f771-0a0f6956ac7f.png)
**ステンドグラス風**
シェーダーみたいに使えたら面白そうですね

|画風変換||
|:--|:--|
|モデル名|[Fast Neural Style Transfer](https://github.com/onnx/models/tree/master/vision/style_transfer/fast_neural_style)|
|入力|224×224×3|
| 出力 | 224×224×3 |
| データセット |  [COCO 2014 Training images dataset](https://cocodataset.org/#download)  |

**[学習済みモデルの配布元](https://github.com/onnx/models)**

上で示した通り、Unity上で学習済みモデルを推論できることが分かりました。
次から実施方法について解説します。

# 導入方法
###### 1. PackageMangerを開き、Barracudaをインストールします。
![マネージャー.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440309/2f310dfd-9124-3906-bb49-142d01860c22.png)
![barracuda.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440309/8014c909-988f-6115-5aec-825146f4e458.png)

###### 2. スクリプトを作成し、Unity.Barracudaをインポートします。
###### 3. NNModelをPublic変数で定義します

```C#
using UnityEngine;
using UnityEngine.UI;
using Unity.Barracuda;  //　<- 2.

public class Classification : MonoBehaviour
{
    //  Barracuda 推論用
    public NNModel modelAsset;  // <- 3.
    private Model m_RuntimeModel;
    private IWorker m_worker;
```

###### 4. 適当なオブジェクトにスクリプトを追加し、InspectorからONNXファイル(学習済みモデル)をアタッチします。
![barracuda.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440309/cf45531f-e329-1a54-4519-3d2b96bf2dcb.png)

###### 5. モデルをロードし、推論を実行するワーカーを作成します

```C#
    void Start()
    {
        m_RuntimeModel = ModelLoader.Load(modelAsset);
        m_worker = WorkerFactory.CreateWorker(WorkerFactory.Type.Compute, m_RuntimeModel);
    }
```

CPU・GPUの使用はここで決定します。
[Workerのタイプ](https://docs.unity3d.com/Packages/com.unity.barracuda@1.0/manual/Worker.html)

###### 6. ProjectタブからRenderTextureを作成します
![barracuda.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440309/de063821-b098-d759-eec5-ee4c70c78dd9.png)

###### 7. Hierarchyからカメラを作成し、出力先を先ほど作成したRenderTextureにします
![barracuda.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440309/33b0a095-336a-9276-210b-4268fd15a3f0.png)
![barracuda.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440309/0332c158-41e2-ccc0-670e-8f8d686a05eb.png)

###### 8. 作成したRenderTextureをスクリプトにアタッチし、次のようなコードで推論を実行します

```C#
   Tensor input = new Tensor(targetTexture); 

   m_worker.Execute(input);
   Tensor output = m_worker.PeekOutput();
```
　**入力はRenderTextureから直接作成できます！！**
　以前実施した際には前処理が色々面倒だったのでここだけでも使う価値があると思います。

###### 9. 出力は次のような形で取り出せます

```C#
  output.ToReadOnlyArray();  //floatの配列で取得する

  output.ToRenderTexture(outputTexture, 0, 0, 1/255f, 0, null);  //RenderTextureに直接出力
```


  **※Tensor及びworkerは処理が終了したタイミングで破棄する必要があります**

```C#
  private void Update()
  {
      Tensor input = new Tensor(inputTexture); 
      Inference(input);

      input.Dispose(); //処理が終わったタイミングで破棄
  }

  private void OnDestroy()
  {
      m_worker.Dispose(); //終了時に破棄する
  }
```

# まとめ

**今回のポイント**

- 入力が簡単
- モデルをUnity上で管理できる
- RenderTextureに直接出力できる

さすが公式の実装だけあって[以前](https://qiita.com/highno_RQ/items/7a33e7eaa6d77c93968f)と比べてかなり簡単に実施できるようになっていました。
Unityはスマートフォンアプリへの出力にも対応しているので、ARアプリの作成等にも使っていけそうです。

# リンク

[公式ドキュメント](https://docs.unity3d.com/Packages/com.unity.barracuda@1.0/manual/GettingStarted.html)
[今回のコード](https://github.com/highnoRQ/BarracudaTest)