12
Help us understand the problem. What are the problem?

More than 3 years have passed since last update.

posted at

updated at

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でビルドするのを忘れずに。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
12
Help us understand the problem. What are the problem?