はじめに
KelpNetは春条さんが開発されたC#で深層学習を行うためのライブラリです。文法的にはChainerに近いとのことです(私はChainer使ってないのでよくわかりませんが)。
これを、今回はUnityで使ってみます。UnityにはML-Agentsという公式の機械学習のライブラリがありますが、あちらはおもに強化学習を目的としているので、それ以外の機械学習(画像認識など)にKelpNetを使おうかと考えているところです。
KelpNetを入手する
KelpNetはこちらのGitHubレポジトリからダウンロードしてください。Zip形式でダウンロードすればよいです。ダウンロードしたら、Zipを解凍してください。KelpNet-masterというフォルダが作られるはずです。
プロジェクトのセットアップ
Unityの方で空のプロジェクトを作ってください。Unityエディターが起動したら、以下の通りエディターからC#プロジェクトを立ち上げてください。UnityエディターとVisual Studioが連携されていれば、Visua Studioが立ち上がるはずです。
Visua Studioのソリューションエクスプローラーを開き、一番上の「ソリューション、、、」というアイコンを右クリックして、「追加」→「既存のプロジェクト」を選んでください。
ファイルを選択するダイアログボックスが開くので、先ほど解凍したKelpNet-masterフォルダの中のKelpNetというフォルダにあるKelpNet.csprojを選んでください。KelpNetがソリューションに追加されます。
同じようにKelpNet-master\KelpNet-master\Cloo にあるCloo.csprojも追加してください。ソリューションエクスプローラーはこういう感じになっているはずです。
次に、ソリューションエクスプローラーでKelpNetのアイコンを右クリックしてメニュー一番下のプロパティを選択してください。設定画面が出てきます。
設定画面の中で「ビルド」のタブを選び、下の方にある「出力」の項目の「出力パス」のところにある「参照、、」ボタンを押してください。ここで、上で作ったUnityプロジェクト内のAssetsフォルダの下(下ならどこでもよい)を選んでください。
これにより、ビルドした時の出力ファイルがUnityプロジェクト内に生成されて、Unityで使えるようになります。
次に、再びソリューションエクスプローラーでKelpNetのアイコンの左にある矢印アイコンをクリックしてサブメニューを開き、その中の「参照」を右クリックしてください。メニューが出てくるので、その中の「参照の追加」を選んでください。
ここで、自分が使っているUnityのUnityEngine.dllを選択します。場所はUnityのインストール先をどこにしたかによりますが、 C:\Program Files\Unity\Hub\Editor\2018.3.8f1\Editor\Data\Managed のような感じです。うまくいったら、ソリューションエクスプローラーでKelpNetの参照に「UnityEngine」が加わっていることを確認してください。
次に、その「UnityEngine」をクリックし、下に出てくる「プロパティ」を見てください。その中に「ローカルにコピー」という項目があるので、それをtrueからfalseに変更してください。
すべて設定したら、Ctrl+Sで保存しておくと良いでしょう。
#スクリプトを書いてみる
それでは、機械学習用のスクリプトを書いてみましょう。ソリューションエクスプローラーでKelpNetのアイコンを右クリックして、追加→新しい項目を選んでください。
ウィンドウが開いたら、項目の中から「クラス」を選び、ファイル名(ウィンドウの一番下で設定)をSample01.csにしてください。
次に、この中にKelpNetについてくるサンプルプログラムSample01.csの内容をそのままコピーしてください。次に、ソースコードの7行目の
class Sample01
を
public class Sample01
に変更してください。次に、メニューの「ビルド」→「ソリューションのビルド」を選んでビルドしてください。
終わったら、Unityプロジェクトの方を開きましょう。すると、新しいファイルが作られていることがわかります。
これで、Unity側でKelpNetのソースコードが使える状態になっています。
UnityのスクリプトからKelpNetのスクリプトを使う。
上記の手段で作ったKelpNetのスクリプトは、Unity側から呼び出せます。Unityのエディタ内でTest.csというファイルを作り、適当なゲームオブジェクトにアタッチしてください。次に、Test.csを開いて、以下の内容を入力してください。
using UnityEngine;
public class Test : MonoBehaviour
{
void Start()
{
KelpNet.Sample.Sample01.Run();
}
}
終わったら、Unityエディタに戻ってプロジェクトを実行してください。エディタがフリーズしたような感じになりますが、KelpNetの方で学習が行われています。終わると、XmlExceptionが出てしまいます。作者の春条さんによりますと、エラーの元は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のところに三角のアイコンが追加されています。
これをクリックすると、Sample01ファイルが出てくるので、これを適当なゲームオブジェクトにドラッグアンドドロップすると、Sample01をアタッチすることができます。
このように、KelpNetのスクリプトをUnityのスクリプトと同じように扱うことができます。ただし、KelpNetの方のスクリプトは自動ビルドされない(自動にする方法あったと思うけど忘れた)ので、スクリプトを変更したらVisual Studioでビルドするのを忘れずに。