Visual StudioにONNXファイルを登録したのにクラスを自動生成してくれない場合の対処方法
Windows MLのチュートリアルをやってみたのですが、まさかのVisual Studioがチュートリアル通り動いてくれなかったので、対処した手順を記録しておきます。
目次
- Windows10でディープラーニングを使ったアプリを作る
- どういうチュートリアルなのか
- 準備
- チュートリアルのダウンロード
- プロジェクトをVisual Studioで開く
- モデルをプロジェクトに組み込む
- モデル用csファイルを生成する
- 生成されたコードを覗いてみる
- メインのコードを修正する
- 実行してみる
Windows10でディープラーニングを使ったアプリを作る
Windows10には、アップデートによってWindows MLという機械学習用の機能が載るようになりました。これによってONNXというフォーマットのディープラーニングのモデルデータをWindowsのアプリに組み込むことができるようになりました。
その方法としてマイクロソフトが チュートリアル を公開しています。
本投稿ではこのチュートリアルを再現してみるのと、途中でうまくいかないところがあったのでその対処を記録しておきます。
どういうチュートリアルなのか
学習済みのMNISTのモデルを使って、手書き入力した数字が何なのか判定するUWPアプリを作ります。モデルはONNX形式に変換済みのものを使います。
ONNXというのは、ディープラーニングの学習済みモデルの交換用のフォーマットです。ChainerやKerasで学習したモデルをWindows MLで使うといった場合に使われるものです。
準備
まずは環境を準備します。
Windows10 version 1809以降
スタートメニューのWindowsマークを右クリックして「システム」を選択すると「バージョン情報」のウィンドウが開きます。そのウィンドウの下の方にWindows10のバージョンが記載されています。
Visual Studio 2019
Visual Studio 2017 version 15.7.4以降でも大丈夫らしいのですが、私はいきなり2019をインストールしてしまったので試せていません。
Windows10 SDK build 17763以降
Visual Studio 2019を起動するときに「コード無しで続行」を選択します。そうすると何もプロジェクトがない状態でVisual Studioが起動します。
そうしたら、メニューの「ツール」「ツールと機能を取得」を選択します。
インストールオプションのウィンドウが開くので「個別のコンポーネント」を選択します。
その中に「SDK、ライブラリ、およびフレームワーク」の欄があって、インストールされているWindows SDKのビルド番号がわかるようになっています。
Windows Machine Learning Code Generator extention for Visual Studio 2019 or 2017
ONNXファイルからコード(クラス)を自動生成してくれるツールです。Visual Studioのバージョンによってインストールするツールが異なります。
本稿では 2019用 をインストールしました。
チュートリアルのダウンロード
GitHubのリポジトリ から、チュートリアルをダウンロードします。この中にはソースコードのほかにONNX形式に変換済みのモデルが含まれています。
GitHubにアカウントがある人であれば、GitHubのsvnの機能を使ってサブディレクトリをダウンロードできるらしいです。私は持っていないので、左上タブの「Code」というところをクリックしてプロジェクトのルートに移動して、プロジェクトごとzipファイルにしてダウンロードしました。
プロジェクトをVisual Studioで開く
それではチュートリアルのプロジェクトを開いてみます。
「MNIST_Demo.sln」というファイルがありますので、それをVisual Studioで開きます。
ここで、初めてUWPアプリを作る人には、開発者モードへの変更を促すウィンドウが開きます。「開発者モード」へ変更してウィンドウを閉じます。Visual Studioの再起動が必要な場合があります。
ソリューションエクスプローラーを見てみましょう。
MainPage.xamlはアプリのビューを定義するもので、ここにInkCanvasやボタンやラベルの指定が書かれます。 MainPage.xaml.csには実際にアプリの動作を記述します。 Helper.csは入出力の画像をやりとりするときの変換をします。
とりあえずこのまま動かしてみましょう。
メニューのターゲットをx64(32bit Windowsの場合はx86)に変更します。
その横の緑色の三角マークをクリックすると、デバッグモードでビルドして実行されます。
最初のビルドではかなり時間がかかりますので、気長に待ちましょう。
こんなアプリが起動したと思います。
黒いところにマウスで適当に文字が書けます。ただ、「Recognize」ボタンを押しても何も反応しません。まだ学習結果を組み込んでませんからね。
モデルをプロジェクトに組み込む
それでは学習済みのモデルをアプリに組み込んでみます。
MNIST_Demo.sinというファイルと同じフォルダにAssetsというフォルダがありますので、それを開いてみてください。フォルダの中にmnist.onnxという名前のファイルがあります。これが学習済みのモデルです。
「ソリューションエクスプローラー」の「Assets」を右クリックして、「追加」「既存の項目」と選択していきます。するとファイルを選択するウィンドウが開きますので、Assetsフォルダ内にあるmnist.onnxを選択します。
ソリューションエクスプローラーのAssetsのところにmnist.onnxが追加されましたね。
そうしたらmnist.onnxをクリックして、プロパティの「ビルドアクション」を「コンテンツ」に変更します。
本来はここで自動的にONNX読み込み用のクラス(本家のチュートリアルではMNIST.cs)が自動的に生成されるということなのですが、私の環境では生成されませんでした。
モデル用csファイルを生成する
自動で生成してくれないので、半自動で生成します。
mlgen.exeがインストールされていると思いますので、探してください。
私の場合は下記にありました。
c:\Program Files (x86)\Windows Kits\10\bin\10.0.17763.0\x64\mlgen.exe
モデル(mnist.onnx)が保存されているフォルダでコマンドプロンプトを開きます。
まずは、引数無しでmlgen.exeを実行してみます。
Assets>"c:\Program Files (x86)\Windows Kits\10\bin\10.0.17763.0\x64\mlgen.exe"
usage: mlgen.exe -i <INPUT-FILE> -l <LANGUAGE> -n <NAMESPACE> -p <PREFIX> [-o OUTPUT-FILE]
<INPUT-FILE> : onnx model file
<LANGUAGE> : cppwinrt or cppcx or cs
<NAMESPACE> : code namespace
<PREFIX> : generated class prefix
<OUTPUT-FILE>: generated code output file. If not specified
the code will be written to std output.
INPUT-FILEにONNXファイルを、LANGUAGEに言語(cs)を、NAMESPACEに名前空間を、OUTPUT-FILEに出力先のファイル名を指定すればよいわけですね。
というわけでコード生成を実行します。
>"c:\Program Files (x86)\Windows Kits\10\bin\10.0.17763.0\x64\mlgen.exe" -i mnist.onnx -l cs -n MNIST -o Mnist.cs
フォルダを見てみると、Mnist.csというファイルができているはずです。
ソリューションエクスプローラーのプロジェクトを右クリックして、「追加」「既存の項目」を選択します。
するとファイルを選択するウィンドウが開きますので、Mnist.csを選択します。
Mnist.csの中に定義されたクラスを使用するためにMainPage.xaml.csのusing句にMNISTを追加します。
using MNIST;
生成されたコードを覗いてみる
mlgen.exeが生成したコードを覗いてみると、クラスが3つ定義されています。
mnistInputクラスは、モデルへの入力画像を定義するクラスです。
mnistOutputクラスは、モデルからの出力を定義するクラスです。
mnistModelクラスが本体ですね。画像から出力を計算して戻します。
メインのコードを修正する
MainPage.xaml.csを修正してモデルで推論するようにしてみます。
まず、Mnist.csで定義されているクラスのインスタンスを作ります。
MainPage.xaml.csのprivate Helperとpublic MainPage()の間に下記を追加します。
private mnistModel ModelGen;
private mnistInput ModelInput = new mnistInput();
private mnistOutput ModelOutput;
MainPage()の中にLoadModelAsync()というメソッドがありますね。アプリを起動するとMainPage()が実行されます。その中でLoadModelAsync()も実行されます。そこで、このメソッドの中でONNXモデルのロードをします。
LoadModelAsync()メソッドを下記のように記述します。
private async Task LoadModelAsync()
{
StorageFile modelFile = await StorageFile.GetFileFromApplicationUriAsync(new Uri($"ms-appx:///Assets/mnist.onnx"));
ModelGen = await mnistModel.CreateFromStreamAsync(modelFile as IRandomAccessStreamReference);
}
はいここでIRandomAccessStreamReferenceに赤線がつきました。「型または名前空間が見つかりませんでした。」というエラーメッセージがでます。usingが足りないのでusingディレクティブのところに下記を追加します。
using Windows.Storage.Streams;
次に「Recognize」ボタンを押したらInkCanvasから画像を取得してモデルに渡すようにします。
recognizeButton_Click()メソッドを下記のようにします。
private async void recognizeButton_Click(object sender, RoutedEventArgs e)
{
VideoFrame vf = await helper.GetHandWrittenImage(inkGrid);
ModelInput.Input3 = ImageFeatureValue.CreateFromVideoFrame(vf);
}
モデルに画像を渡したら、推論して結果をラベルに出力します。この一連の操作もボタンのクリックで実行しますので、regognizeButton_Click()メソッドにコードを記述します。上記と合わせるとこんな感じになります。
private async void recognizeButton_Click(object sender, RoutedEventArgs e)
{
// 画像をモデルへ渡す
VideoFrame vf = await helper.GetHandWrittenImage(inkGrid);
ModelInput.Input3 = ImageFeatureValue.CreateFromVideoFrame(vf);
// 推論を実行
ModelOutput = await ModelGen.EvaluateAsync(ModelInput);
// 結果をリストに変換する
IReadOnlyList<float> vectorImage = ModelOutput.Plus214_Output_0.GetAsVectorView();
IList<float> imageList = vectorImage.ToList();
// リストの最大値(確率が最も高い回答)を求める
var maxIndex = imageList.IndexOf(imageList.Max());
// ラベルに結果を表示する
numberLabel.Text = maxIndex.ToString();
}
実行してみる
では実行してみましょう。メニューの緑色の三角マークをクリックするとビルドして実行されます。
適当に数字を書いて、「Recognize」ボタンを押してみます。
かなり崩して書いても認識してくれます。
公開日
広告