C#の深層学習ライブラリKelpNetをUnityで使う


はじめに

KelpNetは春条さんが開発されたC#で深層学習を行うためのライブラリです。文法的にはChainerに近いとのことです(私はChainer使ってないのでよくわかりませんが)。

これを、今回はUnityで使ってみます。UnityにはML-Agentsという公式の機械学習のライブラリがありますが、あちらはおもに強化学習を目的としているので、それ以外の機械学習(画像認識など)にKelpNetを使おうかと考えているところです。


KelpNetを入手する

KelpNetはこちらのGitHubレポジトリからダウンロードしてください。Zip形式でダウンロードすればよいです。ダウンロードしたら、Zipを解凍してください。KelpNet-masterというフォルダが作られるはずです。


プロジェクトのセットアップ

Unityの方で空のプロジェクトを作ってください。Unityエディターが起動したら、以下の通りエディターからC#プロジェクトを立ち上げてください。UnityエディターとVisual Studioが連携されていれば、Visua Studioが立ち上がるはずです。

image.png

Visua Studioのソリューションエクスプローラーを開き、一番上の「ソリューション、、、」というアイコンを右クリックして、「追加」→「既存のプロジェクト」を選んでください。

image.png

ファイルを選択するダイアログボックスが開くので、先ほど解凍したKelpNet-masterフォルダの中のKelpNetというフォルダにあるKelpNet.csprojを選んでください。KelpNetがソリューションに追加されます。

同じようにKelpNet-master\KelpNet-master\Cloo にあるCloo.csprojも追加してください。ソリューションエクスプローラーはこういう感じになっているはずです。

image.png

次に、ソリューションエクスプローラーでKelpNetのアイコンを右クリックしてメニュー一番下のプロパティを選択してください。設定画面が出てきます。

image.png

設定画面の中で「ビルド」のタブを選び、下の方にある「出力」の項目の「出力パス」のところにある「参照、、」ボタンを押してください。ここで、上で作ったUnityプロジェクト内のAssetsフォルダの下(下ならどこでもよい)を選んでください。

image.png

これにより、ビルドした時の出力ファイルがUnityプロジェクト内に生成されて、Unityで使えるようになります。

次に、再びソリューションエクスプローラーでKelpNetのアイコンの左にある矢印アイコンをクリックしてサブメニューを開き、その中の「参照」を右クリックしてください。メニューが出てくるので、その中の「参照の追加」を選んでください。

image.png

ここで、自分が使っているUnityのUnityEngine.dllを選択します。場所はUnityのインストール先をどこにしたかによりますが、 C:\Program Files\Unity\Hub\Editor\2018.3.8f1\Editor\Data\Managed のような感じです。うまくいったら、ソリューションエクスプローラーでKelpNetの参照に「UnityEngine」が加わっていることを確認してください。

image.png

次に、その「UnityEngine」をクリックし、下に出てくる「プロパティ」を見てください。その中に「ローカルにコピー」という項目があるので、それをtrueからfalseに変更してください。

image.png

すべて設定したら、Ctrl+Sで保存しておくと良いでしょう。


スクリプトを書いてみる

それでは、機械学習用のスクリプトを書いてみましょう。ソリューションエクスプローラーでKelpNetのアイコンを右クリックして、追加→新しい項目を選んでください。

image.png

ウィンドウが開いたら、項目の中から「クラス」を選び、ファイル名(ウィンドウの一番下で設定)をSample01.csにしてください。

image.png

次に、この中にKelpNetについてくるサンプルプログラムSample01.csの内容をそのままコピーしてください。次に、ソースコードの7行目の

class Sample01

public class Sample01

に変更してください。次に、メニューの「ビルド」→「ソリューションのビルド」を選んでビルドしてください。

終わったら、Unityプロジェクトの方を開きましょう。すると、新しいファイルが作られていることがわかります。

image.png

これで、Unity側でKelpNetのソースコードが使える状態になっています。


UnityのスクリプトからKelpNetのスクリプトを使う。

上記の手段で作ったKelpNetのスクリプトは、Unity側から呼び出せます。Unityのエディタ内でTest.csというファイルを作り、適当なゲームオブジェクトにアタッチしてください。次に、Test.csを開いて、以下の内容を入力してください。


Test.cs

using UnityEngine;

public class Test : MonoBehaviour
{
void Start()
{
KelpNet.Sample.Sample01.Run();
}
}


終わったら、Unityエディタに戻ってプロジェクトを実行してください。エディタがフリーズしたような感じになりますが、KelpNetの方で学習が行われています。終わると、XmlExceptionが出てしまいます。作者の春条さんによりますと、エラーの元はModelIO.csのシリアライズ・デシリアライズの部分とのことなので、そこの部分を以下のように修正しました。


ModelIO.cs


using System.IO;
using System.Runtime.Serialization;
using System;
using System.Collections;
using System.Runtime.Serialization.Formatters.Binary;

namespace KelpNet
{
public class ModelIO
{
public static void Save(FunctionStack functionStack, string fileName)
{

FileStream fs = new FileStream(fileName, FileMode.Create);
BinaryFormatter formatter = new BinaryFormatter();
try
{
formatter.Serialize(fs, functionStack);
}
catch (SerializationException e)
{
Console.WriteLine("Failed to serialize. Reason: " + e.Message);
throw;
}
finally
{
fs.Close();
}

}

public static FunctionStack Load(string fileName)
{

FunctionStack result;
FileStream fs = new FileStream(fileName, FileMode.Open);

try
{
BinaryFormatter formatter = new BinaryFormatter();
result = (FunctionStack)formatter.Deserialize(fs);
}
catch (SerializationException e)
{
Console.WriteLine("Failed to deserialize. Reason: " + e.Message);
throw;
}
finally
{
fs.Close();
}

foreach (Function function in result.Functions)
{
function.ResetState();

for (int i = 0; i < function.Optimizers.Length; i++)
{
function.Optimizers[i].ResetParams();
}

if (function is IParallelizable)
{
((IParallelizable)function).CreateKernel();
}
}

return result;
}

}
}


BinaryFormatterの部分はマイクロソフトのサイトを参考にしています(https://docs.microsoft.com/ja-jp/dotnet/api/system.runtime.serialization.formatters.binary.binaryformatter?view=netframework-4.7.2)

これでエラーが出なくなるはずです。


KelpNetのスクリプトをUnityのスクリプトと同じように使いたい。

KelpNetのスクリプトにMonoBehaviourを継承させることで、Unityのスクリプトと同じようにゲームオブジェクトにアタッチできるようになります。Sample01.csの二行目のusing System.Linq;の下に

Using UnityEngine;

を追加し、

public class Sample01

public class Sample01:MonoBehaviour

に変更してください。変更したら、Unityエディタに戻ってください。同じようにファイルが作られますが、KelpNet.dllのところに三角のアイコンが追加されています。

image.png

これをクリックすると、Sample01ファイルが出てくるので、これを適当なゲームオブジェクトにドラッグアンドドロップすると、Sample01をアタッチすることができます。

image.png

このように、KelpNetのスクリプトをUnityのスクリプトと同じように扱うことができます。ただし、KelpNetの方のスクリプトは自動ビルドされない(自動にする方法あったと思うけど忘れた)ので、スクリプトを変更したらVisual Studioでビルドするのを忘れずに。