Distribuire un modello in un'app di Windows con l'API di Windows ML

Nota

Per una maggiore funzionalità, PyTorch può essere usato anche con DirectML in Windows.

Nella parte precedente di questa esercitazione si è appreso come compilare ed esportare un modello in formato ONNX. Verrà ora illustrato come incorporare il modello esportato in un'applicazione Windows ed eseguirlo in locale in un dispositivo chiamando le API WinML.

Al termine, si avrà un'app di classificazione delle immagini funzionante.

Tip

Questo articolo fa parte del percorso formativo di classificazione delle immagini con PyTorch. Se il modello ONNX proviene da un altro percorso di esercitazione di Windows ML, vedere Distribuire il modello di Custom Vision in un'app Windows o Distribuire il modello di ML.NET in un'app Windows.

Informazioni sull'app di esempio

In questo passaggio dell'esercitazione si creerà un'app in grado di classificare le immagini usando il modello di Machine Learning. L'interfaccia utente di base consente di selezionare un'immagine dal dispositivo locale e usa il modello ONNX di classificazione creato e sottoposto a training nella parte precedente per classificarla. I tag restituiti dal modello vengono quindi visualizzati accanto all'immagine.

Qui, ti guideremo attraverso questo processo.

Nota

Se si sceglie di usare l'esempio di codice predefinito, è possibile clonare il file della soluzione. Clonare il repository, passare a questo esempio e aprire il classifierPyTorch.sln file con Visual Studio.Skip alla parte Avvia applicazione di questa pagina per visualizzarla in uso.

Di seguito verrà illustrato come creare l'app e aggiungere codice windows ML.

Crea Windows ML UWP (C#)

Per creare un'app di Windows ML funzionante, è necessario eseguire le operazioni seguenti:

  • Carica un modello di machine learning.
  • Caricare un'immagine in un formato richiesto.
  • Associa gli input e gli output del modello.
  • Valutare il modello e visualizzare risultati significativi.

Dovrai anche creare un'interfaccia utente di base, perché è difficile creare un'app basata su immagini soddisfacente nella riga di comando.

Apri un nuovo progetto in Visual Studio

  1. Iniziamo. Aprire Visual Studio e scegliere Crea un nuovo progetto.

Creare un nuovo progetto di Visual Studio

  1. Nella barra di ricerca digitare UWP, quindi selezionare Blank APP (Universal Windows). Verrà aperto un progetto C# per un'app UWP a pagina singola con controlli o layout predefiniti. Selezionare questa opzione next per aprire una finestra di configurazione per il progetto.

Creare una nuova app UWP

  1. Nella finestra di configurazione eseguire le operazioni seguenti:
  • Assegna un nome al progetto. Qui, chiamiamo classifierPyTorch.
  • Scegliere la posizione del progetto.
  • Se si usa VS2019, verificare che Create directory for solution sia selezionata.
  • Se si usa VS2017, verificare che Place solution and project in the same directory non sia selezionato.

Nuova configurazione dell'app UWP

Premere create per creare il progetto. È possibile che venga visualizzata la finestra della versione minima di destinazione. Assicurarsi che la versione minima sia impostata su Windows 10, versione 1809 (10.0; build 17763) o successiva.

  1. Dopo aver creato il progetto, passare alla cartella del progetto, aprire la cartella [….\classifierPyTorch \Assets] e copiare il ImageClassifier.onnx file in questo percorso.

Esplora la soluzione del progetto

Esploriamo la soluzione del tuo progetto.

Visual Studio ha creato automaticamente diversi file cs-code all'interno di Esplora soluzioni. MainPage.xaml contiene il codice XAML per l'interfaccia utente grafica e MainPage.xaml.cs contiene il codice dell'applicazione, noto anche come code-behind. Se hai creato un'app UWP in precedenza, questi file dovrebbero essere molto familiari.

Soluzione di app UWP

Creare l'interfaccia utente grafica dell'applicazione

Creare prima di tutto un'interfaccia utente grafica semplice per l'app.

  1. Fare doppio clic sul file di codice MainPage.xaml. Nell'app vuota il modello XAML per l'interfaccia utente grafica dell'app è vuoto, quindi è necessario aggiungere alcune funzionalità dell'interfaccia utente.

  2. Aggiungere il codice seguente a MainPage.xaml, sostituendo i tag <Grid> e </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> 

Aggiungere il modello al progetto usando Il generatore di codice di Windows ML (mlgen)

Generatore di codice di Windows Machine Learning o mlgen è un'estensione di Visual Studio che consente di iniziare a usare le API WinML nelle app UWP. Genera codice modello quando aggiungi un file ONNX sottoposto a training nel progetto UWP.

Il generatore di codice di Windows Machine Learning mlgen crea un'interfaccia (per C#, C++/WinRT e C++/CX) con classi wrapper che chiamano automaticamente l'API di Windows ML. In questo modo è possibile caricare, associare e valutare facilmente un modello nel progetto. Lo useremo in questa esercitazione per gestire molte di queste funzioni.

Il generatore di codice è disponibile per Visual Studio 2017 e versioni successive. È consigliabile usare Visual Studio. Tieni presente che in Windows 10 versione 1903 e successive mlgen non è più incluso in Windows 10 SDK, quindi devi scaricare e installare l'estensione. Se hai seguito questo tutorial dall'introduzione, avrai già gestito questo passaggio, ma in caso contrario, è consigliabile scaricare il pacchetto per VS 2019 o per VS 2017.

Nota

Per altre informazioni su mlgen, vedere la documentazione di mlgen

  1. Se non è già stato fatto, installare mlgen.

  2. Fare clic con il pulsante destro del mouse sulla Assets cartella nel Esplora soluzioni in Visual Studio e scegliere Add > Existing Item.

  3. Passare alla cartella assets all'interno di classifierPyTorch [….\classifierPyTorch \Assets], individuare il modello ONNX copiato in precedenza e selezionare add.

  4. Dopo aver aggiunto un modello ONNX alla cartella assets in Esplora soluzioni in Visual Studio, il progetto dovrebbe ora avere due nuovi file:

  • ImageClassifier.onnx - Questo è il modello in formato ONNX.
  • ImageClassifier.cs : file di codice WinML generato automaticamente.

File ONNX nella soluzione di app UWP

  1. Per assicurarsi che il modello venga generato correttamente durante la compilazione dell'applicazione, selezionare il file ImageClassifier.onnx e scegliere Properties. Per Build Action, selezionare Content.

Codice del file ONNX

Si esaminerà ora un codice appena generato nel ImageClassifier.cs file.

Il codice generato include tre classi:

  • ImageClassifierModel: questa classe include due metodi per la creazione di istanze del modello e la valutazione del modello. Ci aiuterà a creare la rappresentazione del modello di Machine Learning, creare una sessione nel dispositivo predefinito del sistema, associare gli input e gli output specifici al modello e valutare il modello in modo asincrono.
  • ImageClassifierInput: questa classe inizializza i tipi di input previsti dal modello. L'input del modello dipende dai requisiti del modello per i dati di input.
  • ImageClassifierOutput: questa classe inizializza i tipi restituiti dal modello. L'output del modello dipende da come viene definito dal modello.

In questa esercitazione non si vuole gestire la tensorizzazione. Verrà apportata una leggera modifica alla ImageClassifierInput classe per modificare il tipo di dati di input e semplificare la vita.

  1. Apportare le modifiche seguenti nel ImageClassifier.cs file:

Modificare la variabile input da un TensorFloat a un ImageFeatureValue.

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

Caricare il modello

  1. Fare doppio clic sul MainPage.xaml.cs file per aprire il code-behind per l'app.

  2. Sostituire le istruzioni "using" con quanto segue, per ottenere l'accesso a tutte le API necessarie:

// 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. Aggiungere le dichiarazioni di variabili seguenti all'interno della MainPage classe sopra la public MainPage() funzione .
        // 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
        } 

A questo punto, implementerai il LoadModel metodo. Il metodo accederà al modello ONNX e lo memorizzerà in memoria. Si userà quindi il metodo CreateFromStreamAsync per creare un'istanza del modello come oggetto LearningModel. La LearningModel classe rappresenta un modello di Machine Learning sottoposto a training. Una volta istanziato, LearningModel è l'oggetto iniziale usato per interagire con Windows ML.

Per caricare il modello, è possibile usare diversi metodi statici nella LearningModel classe . In questo caso, si userà il CreateFromStreamAsync metodo .

Il CreateFromStreamAsync metodo è stato creato automaticamente con mlgen, quindi non è necessario implementare questo metodo. È possibile esaminare questo metodo facendo doppio clic sul file classifier.cs file generato da mlgen.

Nota

Per altre informazioni sulla LearningModel classe, vedere la documentazione della classe LearningModel. Per altre informazioni sui modi aggiuntivi per caricare il modello, vedere la documentazione caricare un modello

  1. Aggiungere una chiamata a un metodo loadModel al costruttore della classe principale.
        // The main page to initialize and execute the model.
        public MainPage()
        {
            this.InitializeComponent();
            loadModel();
        }
  1. Aggiungere l'implementazione del loadModel metodo all'interno di tale MainPage classe.
        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);
        }

Carica l'immagine

  1. È necessario definire un evento click per avviare la sequenza di quattro chiamate di metodo per l'esecuzione del modello: conversione, associazione e valutazione, estrazione di output e visualizzazione dei risultati. Aggiungere il metodo seguente al MainPage.xaml.cs file di codice all'interno della MainPage classe .
        // 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. A questo punto, implementerai il getImage() metodo. Questo metodo selezionerà un file di immagine di input e lo salverà in memoria. Aggiungere il metodo seguente al MainPage.xaml.cs file di codice all'interno della MainPage classe .
        // 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;
        }

Successivamente, si implementerà un metodo image Bind() per ottenere la rappresentazione del file nel formato bitmap BGRA8. Prima di tutto si creerà una classe helper per ridimensionare l'immagine.

  1. Per creare un file helper, fare clic con il pulsante destro del mouse sul nome della soluzione (ClassifierPyTorch), quindi scegliere Add a new item. Nella finestra aperta selezionare Class e assegnare un nome. Qui lo chiamiamo Helper.

Aggiungere un file helper

  1. Verrà visualizzato un nuovo file di classe all'interno del progetto. Aprire questa classe e aggiungere il codice seguente:
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; 
        } 
    } 
} 

A questo punto, convertire l'immagine nel formato appropriato.

La ImageClassifierInput classe inizializza i tipi di input previsti dal modello. In questo caso, il codice è stato configurato in modo da aspettarsi un ImageFeatureValue.

La ImageFeatureValue classe descrive le proprietà dell'immagine usata come input per un modello. Per creare un oggetto ImageFeatureValue, si usa il CreateFromVideoFrame metodo . Per informazioni più specifiche sui motivi per cui questo è il caso e sul funzionamento di queste classi e metodi, vedere la documentazione della classe ImageFeatureValue

Nota

In questa esercitazione viene usata la ImageFeatureValue classe anziché un tensore. Se Window ML non supporta il formato di colore del modello, questa non sarà un'opzione. Per un esempio di come usare le conversioni di immagini e la tensorizzazione, vedere l'esempio di tensorizzazione personalizzata.

  1. Aggiungere l'implementazione del metodo convert() al MainPage.xaml.cs file di codice all'interno della classe MainPage. Il metodo convert ci otterrà una rappresentazione del file di input in un formato 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) 
    {
    }
} 

Associare e valutare il modello

Si creerà quindi una sessione basata sul modello, si assoceranno l'input e l'output alla sessione e si valuterà il modello.

Creare una sessione per associare il modello:

Per creare una sessione, usare la LearningModelSession classe . Questa classe viene usata per valutare i modelli di Machine Learning e associa il modello a un dispositivo che esegue e valuta il modello. È possibile selezionare un dispositivo quando si crea una sessione per eseguire il modello in un dispositivo specifico del computer. Il dispositivo predefinito è la CPU.

Nota

Per altre informazioni su come scegliere un dispositivo, vedere la documentazione Creare una sessione .

Associare input e output del modello:

Per associare input e output, si usa la LearningModelBinding class. Un modello di Machine Learning include caratteristiche di input e output, che passano informazioni all'interno e all'esterno del modello. Tenere presente che le funzionalità necessarie devono essere supportate dalle API Windows ML. La LearningModelBinding classe viene applicata a un LearningModelSession oggetto per associare valori alle funzionalità di input e output denominate.

L'implementazione del binding viene generata automaticamente da mlgen, quindi non è necessario occuparsene. L'associazione viene implementata chiamando i metodi predefiniti della LearningModelBinding classe . In questo caso, usa il metodo Bind per associare un valore al tipo di feature denominato.

Valuta il modello:

Dopo aver creato una sessione per associare il modello e i valori associati agli input e agli output di un modello, è possibile valutare gli input del modello e ottenere le previsioni del modello. Per eseguire l'esecuzione del modello, è necessario chiamare uno dei metodi evaluate predefiniti sulla LearningModelSession. In questo caso si userà il EvaluateAsync metodo .

Analogamente a CreateFromStreamAsync, il EvaluateAsync metodo è stato generato automaticamente anche dal generatore di codice WinML, quindi non è necessario implementare questo metodo. È possibile esaminare questo metodo nel ImageClassifier.cs file .

Il EvaluateAsync metodo valuterà in modo asincrono il modello di Machine Learning usando i valori delle caratteristiche già associati nei binding. Creerà una sessione con LearningModelSession, assocerà l'input e l'output con LearningModelBinding, eseguirà la valutazione del modello e otterrà le funzionalità di output del modello usando la classe LearningModelEvaluationResult.

Nota

Per informazioni su altri metodi di valutazione per eseguire il modello, verificare quali metodi possono essere implementati in LearningModelSession esaminando la documentazione della classe LearningModelSession.

  1. Aggiungere il metodo seguente al MainPage.xaml.cs file di codice all'interno della classe MainPage per creare una sessione, associare e valutare il modello.
        // A method to evaluate the model
        private async Task evaluate()
        {
            results = await modelGen.EvaluateAsync(image);
        }

Estrarre e visualizzare i risultati

Sarà ora necessario estrarre l'output del modello e visualizzare il risultato corretto, cosa che farai implementando i metodi extractResult e displayResult. È necessario trovare la probabilità più alta per restituire l'etichetta corretta.

  1. Aggiungere il extractResult metodo al MainPage.xaml.cs file di codice all'interno della MainPage classe .
        // 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. Aggiungere il displayResult metodo al MainPage.xaml.cs file di codice all'interno della MainPage classe .
        private async Task displayResult() 
        {
            displayOutput.Text = label; 
        }

Ecco fatto! Hai creato correttamente l'app di Machine Learning di Windows con un'interfaccia utente grafica di base per testare il modello di classificazione. Il passaggio successivo consiste nell'avviare l'applicazione ed eseguirla localmente nel dispositivo Windows.

Avvia l'applicazione

Dopo aver completato l'interfaccia utente dell'app, aggiunto il modello e generato il codice di Windows ML, è possibile testare l'applicazione.

Abilitare la modalità sviluppatore e testare l'applicazione da Visual Studio. Assicurarsi che i menu a discesa nella barra degli strumenti superiore siano impostati su Debug. Impostare Solution Platform su x64 per eseguire il progetto nel computer locale se il dispositivo è a 64 bit o x86 se è a 32 bit.

Il nostro modello è stato sottoposto a training per classificare le immagini seguenti: aereo, auto, uccello, gatto, cervo, cane, rana, cavallo, nave, camion. Per testare la nostra app, userai l'immagine dell'auto LEGO costruita per questo progetto. Vediamo come l'app classifica il contenuto dell'immagine.

Immagine per il test dell'applicazione

  1. Salva questa immagine sul tuo dispositivo locale per testare l'app. Modificare il formato dell'immagine in .jpg se necessario. È anche possibile caricare qualsiasi altra immagine pertinente dal dispositivo locale in un formato .jpg o .png.

  2. Per eseguire il progetto, selezionare il Start Debugging pulsante sulla barra degli strumenti oppure premere F5.

  3. All'avvio dell'applicazione, premere Seleziona immagine e selezionare l'immagine dal dispositivo locale.

Interfaccia dell'applicazione

Il risultato verrà visualizzato immediatamente sullo schermo. Come si può notare, l'app di Windows ML ha classificato correttamente l'immagine come un'auto.

Classificazione riuscita nell'app

Riepilogo

Hai appena creato la tua prima app di Windows Machine Learning, dalla creazione del modello alla corretta esecuzione.

Risorse aggiuntive

Per altre informazioni sugli argomenti presentati in questa esercitazione, vedere le risorse seguenti: