次の方法で共有


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

このチュートリアルの前のパートでは、モデルを ONNX 形式で構築し、エクスポートする方法を学習しました。 ここでは、エクスポートしたモデルを Windows アプリケーションに埋め込み、WinML API を呼び出してローカル環境のデバイスで実行する方法について説明します。

完了すると、動作する画像分類アプリができあがります。

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

チュートリアルのこのステップでは、ML モデルを使用して画像を分類できるアプリを作成します。 その基本的な UI を使用すると、ローカル デバイスから画像を選択し、前のパートで構築してトレーニングした分類 ONNX モデルを使用して、それを分類できます。 その後、モデルから返されたタグが、画像の横に表示されます。

ここでは、そのプロセスについて説明します。

Note

定義済みのコード サンプルを使用する場合は、ソリューション ファイルをクローンできます。 リポジトリをクローンし、このサンプルに移動して、classifierPyTorch.sln ファイルを Visual Studio で開きます。このページの「アプリケーションの起動」に進み、それが使用されていることを確認します。

以下では、アプリを作成し、Windows ML コードを追加する方法を説明します。

Windows ML UWP (C#) を作成する

Windows ML アプリを動作させるには、以下のことを行う必要があります。

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

また、コマンド ラインで満足のいく画像ベースのアプリを作成するのは難しいので、基本的な UI を作成する必要があります。

Visual Studio で新しいプロジェクトを開く

  1. では、始めましょう。 Visual Studio を開き、[新しいプロジェクトの作成] を選択します。

新しい Visual Studio プロジェクトを作成する

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

新しい UWP アプリを作成する

  1. 構成ウィンドウで、次のようにします。
  • プロジェクトに名前を付けます。 ここでは、それを classifierPyTorch という名前にします。
  • プロジェクトの場所を選択します。
  • VS 2019 を使用する場合は、[Create directory for solution] がオンになっていることを確認します。
  • VS2017 を使用している場合、[Place solution and project in the same directory] を必ずオフにします。

新しい UWP アプリのセットアップ

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

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

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

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

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

UWP アプリ ソリューション

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

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

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

  2. 次のコードを MainPage.xaml に追加し、<Grid> タグと </Grid> タグを置き換えます。

    <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> 
            <!--Display 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> 

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

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

Note

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

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

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

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

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

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

UWP アプリ ソリューション内の ONNX ファイル

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

ONNX ファイルのコード

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

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

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

このチュートリアルでは、テンソル化は扱いません。 ImageClassifierInput クラスを少し変更し、入力データ型を変更して作業を楽にします。

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

input 変数を、TensorFloat から ImageFeatureValue に変更します。

public sealed class ImageClassifierInput 
    { 
        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 we'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 クラス内の public MainPage() 関数の上に、次の変数宣言を追加します。
        // All the required fields declaration 
        private ImageClassifierModel modelGen; 
        private ImageClassifierInput image = new ImageClassifierInput(); 
        private ImageClassifierOutput results; 
        private StorageFile selectedStorageFile; 
        private string label = ""; 
        private float probability = 0; 
        private Helper helper = new Helper(); 

        public enum Labels 
        {             
            plane,
            car,
            bird,
            cat,
            deer,
            dog,
            frog,
            horse,
            ship,
            truck
        } 

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

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

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

Note

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

  1. メイン クラスのコンストラクターに、loadModel メソッドの呼び出しを追加します。
        // The main page to initialize and execute the model.
        public MainPage()
        {
            this.InitializeComponent();
            loadModel();
        }
  1. その MainPage クラス内に、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/ImageClassifier.onnx"));
            // Instantiate the model. 
            modelGen = await ImageClassifierModel.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 と呼びます。

ヘルパー ファイルを追加する

  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; 
        } 
    } 
} 

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

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

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

Note

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

  1. MainPage クラス内の MainPage.xaml.cs コード ファイルに convert() メソッドの実装を追加します。 convert メソッドにより、BGRA8 形式の入力ファイルの表現が得られます。
// A method to convert and bide 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 32x32  
        inputImage=await helper.CropAndDisplayInputImageAsync(inputImage); 
        // Bind the model input with image 
        ImageFeatureValue imageTensor = ImageFeatureValue.CreateFromVideoFrame(inputImage); 
        image.modelInput = 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 コード ジェネレーターによって自動生成されるので、このメソッドを実装する必要はありません。 このメソッドは ImageClassifier.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 にソリューション プラットフォームを変更します。

ここでのモデルは、飛行機、自動車、鳥、猫、鹿、犬、蛙、馬、船、トラックの画像を分類するためにトレーニングされました。 アプリをテストするには、このプロジェクト用に作成されたレゴの自動車の画像を使用します。 画像の内容がアプリによってどのように分類されるかを見てみましょう。

アプリケーションのテスト用の画像

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

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

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

アプリケーション インターフェイス

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

アプリでの成功した分類

まとめ

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

その他のリソース

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