機械学習による図形推定

機械学習ライブラリML.netを使用し,描かれたストロークの図形を推定して,整形された図形に置き換える機能を実装する.

今回は,楕円と四角どちらかを推定する,二項分類を行う.

shapepredict.gif

ML.netのインストール

ML.netは.NET standard 2.0に依存している.UWPで.NET standard 2.0を使用可能な状態にするには最小ターゲットバージョンを1809にする必要がある.

ソリューションエクスプローラーから,Kisozemiプロジェクトのプロパティを開き,[アプリケーション]-[ターゲットバージョン]と[最小バージョン]を共に,1809に変更し,保存,いったんVisual Studioを再起動する.

target.png

再起動後,[プロジェクト]-[NuGetパッケージの管理]を開き,[参照]からML.netを検索し,プロジェクトにインストールする.

nuget.png

学習モデルの読み込み

本来,今回のような分類の機械学習を行う場合,学習データの準備と学習モデルの作成が必要となる.

しかし,時間も限られているため,今回はすでに作成されている学習モデルを読み込み,図形の分類推定に使用することとする.

以下のmodel.zipは,楕円と四角の1ストロークで描いた図形を約60個学習させたモデルとなる.model.zipを置く場所は後述する.

適当なButtonを用意し,Clickイベントでmodel.zipを読み込む.

ML.netを使用するため,usingに以下を追加:

using Microsoft.ML;
using Microsoft.ML.Data;

クラスのメンバに以下を追加:

MLContext mlContext = null; 
ITransformer strokePredictModel = null;

クリックイベント内で実際にmodel.zipを読み込む,非同期処理(await, async)を使用しているため,イベントメソッドの先頭にasyncを追加すること,System.IOのクラスを使用しているのでusing System.IOを追加する必要があるが,System.IO.PathとWindows.UI.Xaml.Shapes.Pathがあいまいになるので,Windows.UI.Xaml.Shapes.Pathクラスを使用する場合,明示的にすべて書く必要がある

           Windows.Storage.StorageFolder storageFolder = Windows.Storage.ApplicationData.Current.LocalFolder;
           Windows.Storage.StorageFile modelFile = await storageFolder.GetFileAsync("model.zip");

           mlContext = new MLContext();

           //Define DataViewSchema for data preparation pipeline and trained model
           DataViewSchema modelSchema;

           // Load trained model
           using (System.IO.Stream stream = await modelFile.OpenStreamForReadAsync()) //using System.IO;が必要
           {
               strokePredictModel = mlContext.Model.Load(stream, out modelSchema);
               stream.Flush();
           }

注意

UWPで作成されるアプリはiOSやAndroidのアプリ同様に,端末内のストレージに自由にアクセスすることはできない.

そのため,

            Windows.Storage.StorageFolder storageFolder = Windows.Storage.ApplicationData.Current.LocalFolder;

によって,アプリが自由に使えるフォルダを取得している.このフォルダにmodel.zipを置いておくことになるが,このフォルダの場所は環境によりことなるため,各自確認すること.

ブレークポイントなどを使用することで,確認,パスのコピーができる.

path.png

[Windows]+[R]で「ファイル名を指定して実行」を開き,コピーしたパスを入れて開けばすぐにフォルダを開ける.

図形の推定

以下の.csファイルをプロジェクトに追加する.中には機械学習用の入力データ(StrokeData),出力データ(StrokePredict)クラスが定義されている.

学習モデルを読み込んだ状態であれば,ストロークを描き終えた後,そのストロークを特徴ベクトルに変換し,それを学習モデルに入力,図形の推定を行い,推定結果を基に該当する図形をストロークの大きさ,位置に生成する.

以下のdrawingStrokeは描画中のWindows.UI.Xaml.Shapes.Pathである.

                   StrokeData input; 
                   //input に ストロークを特徴ベクトル化したデータを生成(後述)

                   StrokeData[] inputdata = new[] { input };
                   IDataView batchinput = mlContext.Data.LoadFromEnumerable(inputdata);
                   IDataView predictions = strokePredictModel.Transform(batchinput);
                   IEnumerable<StrokePredict> strokePredict = mlContext.Data.CreateEnumerable<StrokePredict>(predictions, reuseRowObject: false);
                   foreach(StrokePredict sp in strokePredict)
                   {
                       AddMessage($"Prediction: {(Convert.ToBoolean(sp.Prediction) ? "Ellipse" : "Rect")} | Probability: {sp.Probability} ");
                       var pathgeom = (PathGeometry)drawingStroke.Data;
                       var rect = pathgeom.Bounds;
                       if (sp.Prediction == true) //trueであれば楕円として推定されたのでEllipseをrectのサイズ,位置で生成する falseであればRectangleを同様に生成
                       {

Shapeクラスの位置指定はCanvas.Set〜メソッドで行う.

Canvas.SetLeft(ellipse, rect.X);
Canvas.SetTop(ellipse, rect.Y);

ストロークから特徴ベクトルの生成

ストロークの点群そのままでは,機械学習に使用できないため,特徴ベクトルへ変換する.

今回は,ストロークを構成する点群の差分ベクトル(Pi+1 - Pi)を算出し,その角度を整数値0〜359とし,各角度の出現回数をカウントすることで,360次元のベクトルを特徴ベクトルとする(少なくとも楕円と四角ではその分布は大きく異なるはず,という仮定)

vector.png

ヒント:

StrokeData input; 
input = new StrokeData();
input.DirCount = new float[360];
//点群全体に以下の処理
//差分ベクトルの角度をMath.Atan2で算出し0〜359整数値に変換->deg
input.DirCount[deg] += 1.0f;
//一通り終わったら全体から最大値を求める->max
//すべてをmaxで割り正規化
input.DirCount[i] /= max;

その他ヒント

Windows.UI.Xaml.Shapes.Pathから点群,PolyLineSegmentを取り出すメソッド(エラー処理なし)

       private PolyLineSegment GetPolyLineSegment(Windows.UI.Xaml.Shapes.Path src)
       {
           var pathgeom = (PathGeometry)src.Data;
           var pathfig = pathgeom.Figures[0];
           return (PolyLineSegment)pathfig.Segments[0];
       }

添付ファイル: filevector.png 119件 [詳細] fileStrokeData.cs 137件 [詳細] filepath.png 118件 [詳細] filelearning.gif 120件 [詳細] filemodel.zip 139件 [詳細] filetarget.png 105件 [詳細] filenuget.png 120件 [詳細] fileshapepredict.gif 119件 [詳細]

トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2019-11-05 (火) 12:27:26