Share via


Windows Machine Learning API を使用して ML.NET モデルを Windows アプリに展開する

このチュートリアルの前のパートでは、ML.NET モデルを ONNX 形式で構築し、エクスポートする方法を学習しました。 そのモデルができたので、Windows アプリケーションに組み込み、WinML API を呼び出してデバイス上でローカルに実行できるようになりました。

完了すると、画像分類子 WinML UWP アプリ (C#) を使用できるようになります。

サンプル アプリケーションについて

このモデルを使用して、食べ物の画像を分類できるアプリを作成します。 これを使用すると、ローカル デバイスから画像を選択し、前のパートで構築し、トレーニングしたローカルに格納された分類 ONNX モデルを使用して画像を処理できます。 返されたタグは画像の横に表示され、分類の信頼確率も表示されます。

これまでこのチュートリアルを実行している場合は、アプリ開発に必要な前提条件が既に整っているはずです。 復習が必要な方は、このチュートリアルの最初のパートを参照してください。

Note

完成したサンプル コードをダウンロードしたい場合は、ソリューション ファイルを複製してください。 リポジトリを複製し、このサンプルに移動して、Visual Studio で classifierMLNETModel.sln ファイルを開きます。 これで、[アプリケーションの起動] (#アプリケーションの起動) の手順にスキップすることができます。

WinML UWP を作成する (C#)

ここでは、アプリと WinML のコードを一から作成する方法について説明します。 学習内容は次のとおりです。

  • 機械学習モデルを読み込みます。
  • 必要な形式の画像を読み込む。
  • モデルの入力と出力をバインドします。
  • モデルを評価し、意味のある結果を表示します。

また、画像分類子をテストできるように、基本的な XAML を使用して簡単な GUI を作成する。

アプリを作成する

  1. Visual Studio を開き、create a new project を選択します。

Create a new Visual Studio project

  1. 検索バーに「UWP」と入力し、[Blank APP (Universal Windows)] を選択します。 これで、事前に定義されたコントロールやレイアウトがない単一ページのユニバーサル Windows プラットフォーム (UWP) アプリ用の新しい C# プロジェクトが開きます。 [Next] を選択して、プロジェクトの構成ウィンドウを開きます。

Create a UWP app

  1. 構成ウィンドウで:
  • プロジェクトの名前を選択します。 ここでは、classifierMLNETModel を使用します。
  • プロジェクトの場所を選択します。
  • VS 2019 を使用している場合は、[Place solution and project in the same directory] をオフにします。
  • VS 2017 を使用している場合は、[Create directory for solution] をオンにします。

Configure your app

[create] を選択してプロジェクトを作成します。 最小ターゲット バージョン ウィンドウがポップアップ表示される場合があります。 最小バージョンは必ず Windows 10 ビルド 17763 以降に設定してください。

WinML アプリを使用してアプリを作成し、モデルを展開するには、以下が必要です。

  1. プロジェクトが作成されたら、プロジェクト フォルダーに移動し、assets フォルダー [….\classifierMLNETModel\Assets] を開き、この場所に bestModel.onnx ファイルをコピーします。

プロジェクトのソリューションを調べる

プロジェクトのソリューションを調べてみましょう。

Visual Studio によって、ソリューション エクスプローラー内にいくつかの cs-code ファイルが自動的に作成されました。 MainPage.xaml には GUI の XAML コード、MainPage.xaml.cs にはアプリケーションのコードが含まれています。 UWP アプリを作成したことがあるなら、これらのファイルはとても見慣れたものでしょう。

アプリケーションの GUI を作成する

まず、アプリ用にシンプルな GUI を作成しましょう。

  1. MainPage.xaml ファイルをダブルクリックします。 この空のアプリでは、アプリの GUI 用の XAML テンプレートは空なので、UI 機能を追加する必要があります。

  2. MainPage.xaml のコードを次のように置き換えます。

<Page
    x:Class="classifierMLNETModel.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:classifierMLNETModel"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

       <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

	
        <StackPanel Margin="1,0,-1,0">
            <TextBlock x:Name="Menu" 
                       FontWeight="Bold" 
                       TextWrapping="Wrap"
                       Margin="10,0,0,0"
                       Text="Image Classification"/>
            <TextBlock Name="space" />
            <Button Name="recognizeButton"
                    Content="Pick Image"
                    Click="OpenFileButton_Click" 
                    Width="110"
                    Height="40"
                    IsEnabled="True" 
                    HorizontalAlignment="Left"/>
            <TextBlock Name="space3" />
            <Button Name="Output"
                    Content="Result is:"
                    Width="110"
                    Height="40"
                    IsEnabled="True" 
                    HorizontalAlignment="Left" 
                    VerticalAlignment="Top">
            </Button>
            <!--Dispaly the Result-->
            <TextBlock Name="displayOutput" 
                       FontWeight="Bold" 
                       TextWrapping="Wrap"
                       Margin="25,0,0,0"
                       Text="" Width="1471" />
            <TextBlock Name="space2" />
            <!--Image preview -->
            <Image Name="UIPreviewImage" Stretch="Uniform" MaxWidth="300" MaxHeight="300"/>
        </StackPanel>
    </Grid> 
</Page>

Windows Machine Learning コード ジェネレーターを使用してモデルをプロジェクトに追加する

Windows Machine Learning コード ジェネレーター (mlgen) は、UWP アプリ上で WinML API を使い始めるときに役立つ Visual Studio 拡張機能です。 トレーニング済みの ONNX ファイルを UWP プロジェクトに追加すると、テンプレート コードが生成されます。

Windows Machine Learning のコード ジェネレーター mlgen により、Windows ML API を呼び出すラッパー クラスを備えたインターフェイス (C#、C++/WinRT、C++/CX 用) が自動的に作成されます。 そのため、プロジェクト内のモデルの読み込み、バインド、評価を簡単に行うことができます。 このチュートリアルでは、こうした多数の関数を自動的に処理するために使用します。

コード ジェネレーターは、Visual Studio 2017 以降で使用できます。 Windows 10 バージョン 1903 以降では、mlgen が Windows 10 SDK に含まれなくなったため、拡張機能をダウンロードしてインストールする必要があることに注意してください。 最初からこのチュートリアルを実行している場合は済んでいますが、そうでない場合は VS 2019 用または VS 2017 用にダウンロードしてください。

Note

mlgen の詳細については、mlgen のドキュメントを参照してください。

  1. まだの場合は、mlgen をインストールします。

  2. Visual Studio のソリューション エクスプローラーで Assets フォルダーを右クリックし、[Add > Existing Item] を選択します。

  3. ImageClassifierAppUWP [….\ImageClassifierAppUWP\Assets] 内の assets フォルダーに移動し、そこにコピーした ONNX モデルを探し、[add] を選択します。

  4. VS のソリューション エクスプローラーで assets フォルダーに ONNX モデル (名前: "classifier") を追加した後、プロジェクトには 2 つの新しいファイルができているはずです。

  • bestModel.onnx - これは ONNX 形式のモデルです。
  • bestModel.cs - 自動生成された WinML コード ファイルです。

Project structure with ONNX model added

  1. アプリケーションをコンパイルするときにモデルが確実に構築されるように、bestModel.onnx ファイルを選択し、[Properties] を選択します。 [Build Action] では、[Content] を選択します。

それでは、bestModel.cs ファイルに新しく生成されたコードを見てみましょう。

生成されたコードには、3 つのクラスが含まれています。

  • bestModelModel: このクラスには、モデルのインスタンス化とモデルの評価のための 2 つのメソッドが含まれています。 これを使用すると、機械学習モデルの表現を作成し、システムの既定のデバイスでセッションを作成し、特定の入力と出力をモデルにバインドして、モデルを非同期に評価することができます。
  • bestModelInput: このクラスを使用すると、モデルが想定する入力の型を初期化することができます。 モデルの入力は、入力データに対するモデルの要件によって変わります。
  • bestModelOutput: このクラスを使用すると、モデルによる出力の型を初期化することができます。 モデルの出力は、モデルによってどのように定義されているかによって変わります。

これらのクラスを使って、プロジェクトでモデルを読み込み、バインドし、評価していきます。

テンソルの変換

テンソル化の処理を容易にするために、入力の TensorFloat クラスを ImageFeatureValue に変更します。

  1. bestModel.cs ファイルを次のように変更します。

コード:

public sealed class bestModelInput
    {
        public TensorFloat input; // shape(-1,3,32,32)
    }

次のようになります。

public sealed class bestModelInput
    {
        public ImageFeatureValue input; // shape(-1,3,32,32)
    }

モデルと入力を読み込む

モデルを読み込む

  1. MainPage.xaml.cs コード ファイルをダブルクリックし、アプリケーション コードを開きます。

  2. "using" ステートメントを次のように置き換えて、必要なすべての API にアクセスできるようにします。

// Specify all the using statements which give us the access to all the APIs that you'll need
using System;
using System.Threading.Tasks;
using Windows.AI.MachineLearning;
using Windows.Graphics.Imaging;
using Windows.Media;
using Windows.Storage;
using Windows.Storage.Pickers;
using Windows.Storage.Streams;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media.Imaging;
  1. MainPage クラス内の using ステートメントの後に、名前空間 classifierMLNETModel の下に次の変数宣言を追加します。
		// All the required fields declaration
		private bestModelModel modelGen;
		private bestModelInput image = new bestModelInput();
		private bestModelOutput results;
		private StorageFile selectedStorageFile;
		private string label = "";
		private float probability = 0;
		private Helper helper = new Helper();

		public enum Labels
		{
			desert,
			soup,
			vegetable_fruit,
		}

次は LoadModel メソッドを実装します。 このメソッドを使用すると、ONNX モデルにアクセスし、それをメモリに格納することができます。 次に、CreateFromStreamAsync メソッドを使用して、モデルを LearningModel オブジェクトとしてインスタンス化します。 LearningModel クラスは、トレーニング済みの機械学習モデルを表します。 インスタンス化された LearningModel は、Windows ML との対話に使用する最初のオブジェクトになります。

モデルを読み込むために、LearningModel クラスのいくつかの静的メソッドを使用することができます。 この例では、CreateFromStreamAsync メソッドを使用します。

CreateFromStreamAsync メソッドは mlgen で自動的に作成されているので、このメソッドを実装する必要はありません。 mlgen で生成された bestModel.cs ファイルをダブルクリックすると、このメソッドを確認することができます。

LearningModel クラスの詳細については、LearningModel クラスのドキュメントを参照してください。

モデルを読み込むその他の方法については、モデルの読み込みに関するドキュメントを参照してください。

  1. main メソッドを定義しましょう。
// The main page to initialize and execute the model.
public MainPage()
{
	this.InitializeComponent();
	loadModel();
}
  1. MainPage クラス内の MainPage.xaml.cs コード ファイルに loadModel メソッドの実装を追加します。
private async Task loadModel()
{
// Get an access the ONNX model and save it in memory. 
	StorageFile modelFile = await StorageFile.GetFileFromApplicationUriAsync(new Uri($"ms-appx:///Assets/bestModel.onnx"));
// Instantiate the model. 
	modelGen = await bestModelModel.CreateFromStreamAsync(modelFile);
}

画像を読み込む

  1. モデル実行のための 4 つのメソッド呼び出し (変換、バインドと評価、出力の抽出、結果の表示) のシーケンスを開始するために、クリック イベントを定義する必要があります。 MainPage クラス内の MainPage.xaml.cs コード ファイルに次のメソッドを追加します。
        // Waiting for a click event to select a file 
        private async void OpenFileButton_Click(object sender, RoutedEventArgs e)
        {
            if (!await getImage())
            {
                return;
            }
            // After the click event happened and an input selected, begin the model execution. 
            // Bind the model input
            await imageBind();
            // Model evaluation
            await evaluate();
            // Extract the results
            extractResult();
            // Display the results  
            await displayResult();
        }
  1. 次は getImage() メソッドを実装します。 このメソッドにより、入力画像ファイルが選択され、メモリに保存されます。 MainPage クラス内の MainPage.xaml.cs コード ファイルに次のメソッドを追加します。
        // A method to select an input image file
        private async Task<bool> getImage()
        {
            try
            {
                // Trigger file picker to select an image file
                FileOpenPicker fileOpenPicker = new FileOpenPicker();
                fileOpenPicker.SuggestedStartLocation = PickerLocationId.PicturesLibrary;
                fileOpenPicker.FileTypeFilter.Add(".jpg");
                fileOpenPicker.FileTypeFilter.Add(".png");
                fileOpenPicker.ViewMode = PickerViewMode.Thumbnail;
                selectedStorageFile = await fileOpenPicker.PickSingleFileAsync();
                if (selectedStorageFile == null)
                {
                    return false;
                }
            }
            catch (Exception)
            {
                return false;
            }
            return true;
        }

次に、ファイルの表現をビットマップ BGRA8 形式で取得するために、image Bind() メソッドを実装します。 その前に、画像のサイズを変更するヘルパー クラスを作成します。

  1. ヘルパー ファイルを作成するには、ソリューション名 (ClassifierPyTorch) を右クリックして [Add a new item] を選択します。 開いたウィンドウで Class を選択し、名前を付けます。 ここでは Helper と呼びます。

Add a Helper file

  1. プロジェクト内に新しいクラス ファイルが表示されます。 このクラスを開き、次のコードを追加します。
using System; 
using System.Threading.Tasks; 
using Windows.Graphics.Imaging; 
using Windows.Media; 

namespace classifierPyTorch 
{ 
    public class Helper 
    { 
        private const int SIZE = 32;  
        VideoFrame cropped_vf = null; 
 
        public async Task<VideoFrame> CropAndDisplayInputImageAsync(VideoFrame inputVideoFrame) 
        { 
            bool useDX = inputVideoFrame.SoftwareBitmap == null; 

            BitmapBounds cropBounds = new BitmapBounds(); 
            uint h = SIZE; 
            uint w = SIZE; 
            var frameHeight = useDX ? inputVideoFrame.Direct3DSurface.Description.Height : inputVideoFrame.SoftwareBitmap.PixelHeight; 
            var frameWidth = useDX ? inputVideoFrame.Direct3DSurface.Description.Width : inputVideoFrame.SoftwareBitmap.PixelWidth; 
 
            var requiredAR = ((float)SIZE / SIZE); 
            w = Math.Min((uint)(requiredAR * frameHeight), (uint)frameWidth); 
            h = Math.Min((uint)(frameWidth / requiredAR), (uint)frameHeight); 
            cropBounds.X = (uint)((frameWidth - w) / 2); 
            cropBounds.Y = 0; 
            cropBounds.Width = w; 
            cropBounds.Height = h; 
 
            cropped_vf = new VideoFrame(BitmapPixelFormat.Bgra8, SIZE, SIZE, BitmapAlphaMode.Ignore); 
 
            await inputVideoFrame.CopyToAsync(cropped_vf, cropBounds, null); 
            return cropped_vf; 
        } 
    } 
} 

それでは、画像を適切な形式に変換しましょう。

bestModelInput クラスを使用すると、モデルが想定する入力の型を初期化することができます。 この例では、ImageFeatureValue を想定するようにコードを構成しました。

ImageFeatureValue クラスには、モデルに渡すために使用される画像のプロパティを記述します。 ImageFeatureValue を作成するには、CreateFromVideoFrame メソッドを使用します。 なぜそうなるのか、これらのクラスとメソッドがどのように機能するのか、より具体的な詳細については、ImageFeatureValue クラスのドキュメントを参照してください。

Note

このチュートリアルでは、テンソルではなく ImageFeatureValue クラスを使用します。 モデルの色の形式が Window ML でサポートされていない場合は、この方法を選択できません。 画像の変換とテンソル化の作業方法の例については、カスタム テンソル化サンプルのページを参照してください。

  1. MainPage クラス内の MainPage.xaml.cs コード ファイルに convert() メソッドの実装を追加します。 convert メソッドにより、BGRA8 形式の入力ファイルの表現が得られます。
// A method to convert and bind the input image.  
        private async Task imageBind()
        {
            UIPreviewImage.Source = null;
            try
            {
                SoftwareBitmap softwareBitmap;
                using (IRandomAccessStream stream = await selectedStorageFile.OpenAsync(FileAccessMode.Read))
                {
                    // Create the decoder from the stream 
                    BitmapDecoder decoder = await BitmapDecoder.CreateAsync(stream);
                    // Get the SoftwareBitmap representation of the file in BGRA8 format
                    softwareBitmap = await decoder.GetSoftwareBitmapAsync();
                    softwareBitmap = SoftwareBitmap.Convert(softwareBitmap, BitmapPixelFormat.Bgra8, BitmapAlphaMode.Premultiplied);
                }
                // Display the image
                SoftwareBitmapSource imageSource = new SoftwareBitmapSource();
                await imageSource.SetBitmapAsync(softwareBitmap);
                UIPreviewImage.Source = imageSource;

				// Encapsulate the image within a VideoFrame to be bound and evaluated
            	VideoFrame inputImage = VideoFrame.CreateWithSoftwareBitmap(softwareBitmap);
              	// Resize the image size to 224x224 
              	inputImage=await helper.CropAndDisplayInputImageAsync(inputImage);
              	// Bind the model input with image
              	ImageFeatureValue imageTensor = ImageFeatureValue.CreateFromVideoFrame(inputImage);
				image.input1 = imageTensor;

				// Encapsulate the image within a VideoFrame to be bound and evaluated
				VideoFrame inputImage = VideoFrame.CreateWithSoftwareBitmap(softwareBitmap);
				// Bind the input image
				ImageFeatureValue imageTensor = ImageFeatureValue.CreateFromVideoFrame(inputImage);
				image.modelInput = imageTensor;

            }
            catch (Exception e)
            {
            }
        }

モデルのバインドと評価

次に、モデルに基づいてセッションを作成し、セッションからの入力と出力を結合し、モデルを評価します。

モデルをバインドするセッションを作成します。

セッションの作成には LearningModelSession クラスを使用します。 このクラスは、機械学習モデルを評価するために使用されます。また、モデルの実行と評価を行うデバイスにモデルがバインドされます。 セッションを作成する際にデバイスを選択して、マシンの特定のデバイス上でモデルを実行することができます。 既定のデバイスは CPU です。

Note

デバイスの選択方法の詳細については、セッションの作成に関するドキュメントを参照してください。

モデルの入力と出力をバインドする:

入力と出力をバインドするには、LearningModelBinding クラスを使用します。 機械学習モデルには、モデルとの間で情報をやり取りする入力および出力機能があります。 必要な機能は Window ML API でサポートされている必要があることに注意してください。 LearningModelBinding クラスは LearningModelSession に適用され、名前付きの入力機能と出力機能に値がバインドされます。

バインドの実装は mlgen によって自動的に生成されるので、これに対応する必要ありません。 バインドは LearningModelBinding クラスの定義済みメソッドを呼び出すことで実装されます。 この例では、Bind メソッドを使用して、名前付きの特徴の種類に値をバインドします。

モデルを評価する:

セッションを作成してモデルをバインドし、バインドされた値をモデルの入力と出力に設定すると、モデルの入力を評価して予測値を取得できるようになります。 モデル実行を行うには、LearningModelSession に対して定義済みの evaluate メソッドのいずれかを呼び出す必要があります。 この例では EvaluateAsync メソッドを使用します。

CreateFromStreamAsync と同様に、EvaluateAsync メソッドも WinML コード ジェネレーターによって自動生成されるので、このメソッドを実装する必要はありません。 このメソッドは bestModel.cs ファイルで確認できます。

EvaluateAsync メソッドを使用すると、既にバインドにバインドされている特徴量の値を使用し、機械学習モデルを非同期的に評価することができます。 また、LearningModelSession を使用してセッションを作成し、LearningModelBinding を使用して入力と出力をバインドし、モデルの評価を実行し、LearningModelEvaluationResult クラスを使用してモデルの出力特徴量を取得することができます。

Note

モデルを実行するための他の評価メソッドについては、LearningModelSession クラスのドキュメントを参照して、LearningModelSession にどのようなメソッドを実装できるかを確認してください。

  1. MainPage クラス内の MainPage.xaml.cs コード ファイルに次のメソッドを追加し、セッションの作成、モデルのバインドと評価を行います。
        // A method to evaluate the model
        private async Task evaluate()
        {
            results = await modelGen.EvaluateAsync(image);
        }

結果の抽出と表示

次に、モデルの出力を抽出して正しい結果を表示する必要があります。これを行うには、extractResultdisplayResult のメソッドを実装します。 正しいラベルを返すには、最も高い確率を見つける必要があります。

  1. MainPage クラス内の MainPage.xaml.cs コード ファイルに extractResult メソッドを追加します。
        // A method to extract output from the model 
        private void extractResult()
        {
            // Retrieve the results of evaluation
            var mResult = results.modelOutput as TensorFloat;
            // convert the result to vector format
            var resultVector = mResult.GetAsVectorView();
            
            probability = 0;
            int index = 0;
            // find the maximum probability
            for(int i=0; i<resultVector.Count; i++)
            {
                var elementProbability=resultVector[i];
                if (elementProbability > probability)
                {
                    index = i;
                }
            }
            label = ((Labels)index).ToString();
        }
  1. MainPage クラス内の MainPage.xaml.cs コード ファイルに displayResult メソッドを追加します。
        private async Task displayResult() 
        {
            displayOutput.Text = label; 
        }

完了です! 分類モデルをテストするための基本的な GUI を備えた Windows Machine Learning アプリを作成することができました。 次の手順は、アプリケーションを起動して、Windows デバイス上でローカルに実行することです。

アプリケーションを起動します

アプリケーション インターフェイスを完成させ、モデルを追加し、Windows ML コードを生成したら、アプリケーションをテストすることができます。

開発者モードを有効にして、Visual Studio からアプリケーションをテストします。 上部のツール バーのドロップダウン メニューが Debug に設定されていることを確認します。 ローカル コンピューター上でプロジェクトを実行するように、デバイスが 64 ビットの場合は x64 に、32 ビットの場合は x86 にソリューション プラットフォームを変更します。

アプリをテストするために、次のスープの画像を使用しましょう。 画像の内容がアプリによってどのように分類されるかを見てみましょう。

Image for application testing

  1. アプリをテストするために、この画像をローカル デバイスに保存します。 必要に応じて、画像形式を .jpg に変更します。 また、ローカル デバイスから他の関連する画像 (.jpg または .png 形式) を追加することもできます。

  2. プロジェクトを実行するには、ツール バーの [Start Debugging] ボタンを選択するか、F5 キーを押します。

  3. アプリケーションが起動したら、[Pick Image] を選択し、ローカル デバイスから画像を選択します。

Application interface

結果はすぐに画面に表示されます。 ご覧のように、この Windows ML アプリにより、画像が正しくスープとして分類されました。

Successful classification in your app

まとめ

これで、モデルの作成から実行まで、最初の Windows Machine Learning アプリが完成しました。

その他のリソース

このチュートリアルで説明しているトピックについて詳しくは、次のリソースを参照してください。