Partilhar via


Implantar modelo no aplicativo do Windows com a API do Windows ML

Observação

Para maior funcionalidade, o PyTorch também pode ser usado com DirectML no Windows.

Na parte anterior deste tutorial, você aprendeu como criar e exportar um modelo no formato ONNX. Agora, mostraremos como incorporar seu modelo exportado em um aplicativo do Windows e executá-lo localmente em um dispositivo chamando APIs do WinML.

Quando terminarmos, você terá um aplicativo de classificação de imagens funcionando.

Sobre o aplicativo de exemplo

Nesta etapa do tutorial, você criará um aplicativo que pode classificar imagens usando seu modelo de ML. Sua interface do usuário básica permite que você selecione uma imagem do seu dispositivo local e usa o modelo ONNX de classificação que você criou e treinou na parte anterior para classificá-la. As tags retornadas pelo modelo são exibidas ao lado da imagem.

Aqui, vamos orientá-lo através desse processo.

Observação

Se você optar por usar o exemplo de código predefinido, poderá clonar o arquivo de solução. Clone o repositório, navegue até este exemplo e abra o classifierPyTorch.sln arquivo com o Visual Studio. Pule para a parte Iniciar a Aplicação desta página para vê-lo em uso.

Abaixo, vamos orientá-lo sobre como criar seu aplicativo e adicionar o código de ML do Windows.

Criar um Windows ML UWP (C#)

Para criar um aplicativo de ML do Windows em funcionamento, você precisará fazer o seguinte:

  • Carregue um modelo de aprendizado de máquina.
  • Carregue uma imagem no formato necessário.
  • Vincule as entradas e saídas do modelo.
  • Avalie o modelo e exiba resultados significativos.

Você também precisará criar uma interface do usuário básica, pois é difícil criar um aplicativo baseado em imagem satisfatório na linha de comando.

Abrir um novo projeto no Visual Studio

  1. Vamos começar. Abra o Visual Studio e escolha criar um novo projeto.

Criar novo projeto do Visual Studio

  1. Na barra de pesquisa, escreva UWPe, em seguida, selecione Blank APP (Universal Windows). Isso abre um projeto C# para um aplicativo da Plataforma Universal do Windows (UWP) de página única com controles ou layout predefinidos. Selecione next para abrir uma janela de configuração para o projeto.

Criar novo aplicativo UWP

  1. Na janela de configuração, faça o seguinte:
  • Atribua um nome ao seu projeto. Aqui, chamamos-lhe classificadorPyTorch.
  • Escolha a localização do seu projeto.
  • Se você estiver usando o VS2019, verifique se Create directory for solution está marcado.
  • Se você estiver usando o VS2017, verifique se Place solution and project in the same directory está desmarcado.

Nova configuração do aplicativo UWP

Pressione create para criar seu projeto. A janela da versão de destino mínima pode aparecer. Certifique-se de que a sua versão mínima está definida para Windows 10, versão 1809 (10.0; build 17763) ou superior.

  1. Depois que o projeto for criado, navegue até a pasta do projeto, abra a pasta [….\classifierPyTorch \Assets] e copie o ImageClassifier.onnx arquivo para esse local.

Explore a solução do projeto

Vamos explorar a solução do seu projeto.

O Visual Studio criou automaticamente vários arquivos de código cs, dentro do Gerenciador de Soluções. MainPage.xaml contém o código XAML para sua GUI e MainPage.xaml.cs contém o código do aplicativo, também conhecido como code-behind. Se você já criou um aplicativo UWP antes, esses arquivos devem ser muito familiares para você.

Solução de aplicativo UWP

Criar a GUI do aplicativo

Primeiro, vamos criar uma GUI simples para seu aplicativo.

  1. Faça duplo clique no ficheiro MainPage.xaml de código. Em seu aplicativo em branco, o modelo XAML para a GUI do seu aplicativo está vazio, portanto, precisaremos adicionar alguns recursos da interface do usuário.

  2. Adicione o código abaixo ao MainPage.xaml, substituindo as <Grid> tags 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> 

Adicione o modelo ao projeto usando o Gerador de Código de ML do Windows (mlgen)

O Gerador de Código do Windows Machine Learning, ou mlgen, é uma extensão do Visual Studio para ajudá-lo a começar a usar APIs WinML em aplicativos UWP. Ele gera código de modelo quando você adiciona um arquivo ONNX treinado ao projeto UWP.

O gerador de código mlgen do Windows Machine Learning cria uma interface (para C#, C++/WinRT e C++/CX) com classes wrapper que chamam a API do Windows ML para você. Isso permite que você carregue, vincule e avalie facilmente um modelo em seu projeto. Vamos usá-lo neste tutorial para lidar com muitas dessas funções para nós.

O gerador de código está disponível para o Visual Studio 2017 e posterior. Recomendamos o uso do Visual Studio. Por favor, esteja ciente de que no Windows 10, versão 1903 e posterior, mlgen não está mais incluído no SDK do Windows 10, então você deve baixar e instalar a extensão. Se você tem acompanhado este tutorial desde a introdução, você já terá lidado com isso, mas se não, você deve baixar para VS 2019 ou VS 2017.

Observação

Para saber mais sobre mlgen, consulte a documentação mlgen

  1. Se ainda não o fez, instale o mlgen.

  2. Clique com o botão direito do mouse na pasta Assets no Gerenciador de Soluções no Visual Studio e selecione Add > Existing Item.

  3. Navegue até a pasta assets dentro classifierPyTorch [….\classifierPyTorch \Assets], encontre o modelo ONNX que você já copiou lá e selecione add.

  4. Depois de adicionar um modelo ONNX à pasta assets no gerenciador de soluções no VS, o projeto agora deve ter dois novos arquivos:

  • ImageClassifier.onnx - este é o seu modelo em formato ONNX.
  • ImageClassifier.cs – arquivo de código WinML gerado automaticamente.

Arquivos ONNX em sua solução de aplicativo UWP

  1. Para se certificar de que o modelo é construído ao compilar a nossa aplicação, selecione o ficheiro ImageClassifier.onnx e escolha Properties. Para Build Action, selecione Content.

Código do arquivo ONNX

Agora, vamos explorar um código recém-gerado no ImageClassifier.cs arquivo.

O código gerado inclui três classes:

  • ImageClassifierModel: Esta classe inclui dois métodos para instanciação de modelo e avaliação de modelo. Isso nos ajudará a criar a representação do modelo de aprendizado de máquina, criar uma sessão no dispositivo padrão do sistema, vincular as entradas e saídas específicas ao modelo e avaliar o modelo de forma assíncrona.
  • ImageClassifierInput: Esta classe inicializa os tipos de entrada esperados pelo modelo. A entrada do modelo depende dos requisitos do modelo para os dados de entrada.
  • ImageClassifierOutput: Esta classe inicializa os tipos que o modelo irá produzir. A saída do modelo depende de como ela é definida pelo modelo.

Neste tutorial, não queremos lidar com tensorização. Faremos uma pequena alteração na ImageClassifierInput classe, para alterar o tipo de dados de entrada e facilitar a nossa vida.

  1. Faça as seguintes alterações no ImageClassifier.cs arquivo:

Altere a input variável de a TensorFloat para um ImageFeatureValue.

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

Carregue o modelo

  1. Dê um duplo clique no arquivo MainPage.xaml.cs para abrir o código subjacente do aplicativo.

  2. Substitua as instruções "usando" seguindo para obter acesso a todas as APIs necessárias:

// 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. Adicione as seguintes declarações de variáveis dentro da sua MainPage classe, acima da public MainPage() função.
        // 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
        } 

Agora, você implementará o LoadModel método. O método irá acessar o modelo ONNX e armazená-lo na memória. Em seguida, você usará o CreateFromStreamAsync método para instanciar o modelo como um LearningModel objeto. A LearningModel classe representa um modelo de aprendizagem de máquina treinado. Uma vez instanciado, o LearningModel é o objeto inicial utilizado para interagir com o Windows ML.

Para carregar o modelo, você pode usar vários métodos estáticos na LearningModel classe. Nesse caso, você usará o CreateFromStreamAsync método.

O CreateFromStreamAsync método foi criado automaticamente com mlgen, então você não precisa implementar esse método. Você pode revisar este método clicando duas vezes no classifier.cs arquivo gerado pelo mlgen.

Observação

Para saber mais sobre a LearningModel classe, consulte a documentação da classe LearningModel. Para saber mais sobre formas adicionais de carregar o modelo, consulte a documentação Carregar um modelo

  1. Adicione uma chamada ao método loadModel ao construtor da classe principal.
        // The main page to initialize and execute the model.
        public MainPage()
        {
            this.InitializeComponent();
            loadModel();
        }
  1. Adicione a implementação do loadModel método, dentro dessa 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);
        }

Carregue a imagem

  1. Precisamos definir um evento de clique para iniciar a sequência de quatro chamadas de método para a execução do modelo – conversão, vinculação e avaliação, extração de saída e exibição dos resultados. Adicione o seguinte método ao seu MainPage.xaml.cs arquivo de código dentro da 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. Agora, você implementará o getImage() método. Este método irá selecionar um arquivo de imagem de entrada e salvá-lo na memória. Adicione o seguinte método ao seu MainPage.xaml.cs arquivo de código dentro da 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;
        }

Em seguida, você implementará um método de imagem Bind() para obter a representação do arquivo no formato bitmap BGRA8. Mas primeiro, você criará uma classe auxiliar para redimensionar a imagem.

  1. Para criar um ficheiro auxiliar, clique com o botão direito do rato no nome da solução (ClassifierPyTorch) e, em seguida, selecione Add a new item. Na janela aberta, selecione Class e atribua-lhe um nome. Aqui, estamos chamando de Helper.

Adicionar um arquivo auxiliar

  1. Um novo arquivo de classe aparecerá em seu projeto. Abra esta classe e adicione o seguinte código:
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; 
        } 
    } 
} 

Agora, vamos converter a imagem para o formato apropriado.

A ImageClassifierInput classe inicializa os tipos de entrada esperados pelo modelo. No nosso caso, configurámos o nosso código para esperar um ImageFeatureValue.

A ImageFeatureValue classe descreve as propriedades da imagem usada para passar para um modelo. Para criar um ImageFeatureValue, use o método CreateFromVideoFrame. Para obter detalhes mais específicos sobre por que esse é o caso e como essas classes e métodos funcionam, consulte a documentação da classe ImageFeatureValue

Observação

Neste tutorial, usamos a ImageFeatureValue classe em vez de um tensor. Se o Window ML não suportar o formato de cores do seu modelo, esta não será uma opção. Para obter um exemplo de como trabalhar com conversões de imagem e tensorização, consulte o Exemplo de tensorização personalizada.

  1. Adicione a implementação do método convert() ao seu ficheiro de código MainPage.xaml.cs dentro da classe MainPage. O método convert nos dará uma representação do arquivo de entrada em um 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) 
    {
    }
} 

Vincular e avaliar o modelo

Em seguida, você criará uma sessão com base no modelo, vinculará a entrada e a saída da sessão e avaliará o modelo.

Crie uma sessão para vincular o modelo:

Para criar uma sessão, use a LearningModelSession classe. Essa classe é usada para avaliar modelos de aprendizado de máquina e vincula o modelo a um dispositivo que, em seguida, executa e avalia o modelo. Você pode selecionar um dispositivo ao criar uma sessão para executar seu modelo em um dispositivo específico da sua máquina. O dispositivo padrão é a CPU.

Observação

Para saber mais sobre como escolher um dispositivo, consulte a documentação Criar uma sessão .

Vincular entradas e saídas do modelo:

Para vincular entrada e saída, use a LearningModelBinding classe. Um modelo de aprendizado de máquina tem recursos de entrada e saída, que passam informações para dentro e para fora do modelo. Lembre-se de que os recursos necessários devem ser suportados pelas APIs do Window ML. A LearningModelBinding classe é aplicada em um LearningModelSession para vincular valores a recursos de entrada e saída nomeados.

A implementação da ligação é gerada automaticamente pelo mlgen, para que você não precise cuidar dela. A associação é implementada chamando os métodos predefinidos da LearningModelBinding classe. No nosso caso, ele usa o Bind método para vincular um valor ao tipo de recurso nomeado.

Avalie o modelo:

Depois de criar uma sessão para vincular o modelo e os valores limitados às entradas e saídas de um modelo, você pode avaliar as entradas do modelo e obter suas previsões. Para executar a execução do modelo, você deve chamar qualquer um dos métodos de avaliação predefinidos no LearningModelSession. No nosso caso, usaremos o EvaluateAsync método.

Semelhante ao CreateFromStreamAsync, o EvaluateAsync método também foi gerado automaticamente pelo WinML Code Generator, então você não precisa implementar esse método. Você pode revisar esse método no ImageClassifier.cs arquivo.

O EvaluateAsync método avaliará de forma assíncrona o modelo de aprendizado de máquina usando os valores de recurso já vinculados em associações. Ele criará uma sessão com LearningModelSession, vinculará a entrada e a saída com LearningModelBinding, executará a avaliação do modelo e obterá os recursos de saída do modelo usando a LearningModelEvaluationResult classe.

Observação

Para saber mais sobre outros métodos de avaliação para executar o modelo, verifique quais métodos podem ser implementados no LearningModelSession examinando a documentação da classe LearningModelSession.

  1. Adicione o seguinte método ao seu MainPage.xaml.cs arquivo de código dentro da classe MainPage para criar uma sessão, vincular e avaliar o modelo.
        // A method to evaluate the model
        private async Task evaluate()
        {
            results = await modelGen.EvaluateAsync(image);
        }

Extrair e exibir os resultados

Agora você precisará extrair a saída do modelo e exibir o resultado certo, o que será feito implementando os extractResult métodos e displayResult . Você precisará encontrar a maior probabilidade de retornar o rótulo correto.

  1. Adicione o extractResult método ao seu MainPage.xaml.cs arquivo de código dentro da 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. Adicione o displayResult método ao seu MainPage.xaml.cs arquivo de código dentro da MainPage classe.
        private async Task displayResult() 
        {
            displayOutput.Text = label; 
        }

Está feito! Você criou com sucesso o aplicativo de aprendizado de máquina do Windows com uma GUI básica para testar nosso modelo de classificação. O próximo passo é iniciar o aplicativo e executá-lo localmente em seu dispositivo Windows.

Inicie o aplicativo

Depois de concluir a interface do aplicativo, adicionar o modelo e gerar o código de ML do Windows, você pode testar o aplicativo!

Habilite o modo de desenvolvedor e teste seu aplicativo do Visual Studio. Verifique se os menus suspensos na barra de ferramentas superior estão definidos como Debug. Altere a plataforma de solução para x64 para executar o projeto em sua máquina local se o dispositivo for de 64 bits ou x86 se for de 32 bits.

Nosso modelo foi treinado para classificar as seguintes imagens: avião, carro, pássaro, gato, veado, cão, sapo, cavalo, navio, caminhão. Para testar nosso aplicativo, você usará a imagem do carro Lego construído para este projeto. Vamos ver como nosso aplicativo classifica o conteúdo da imagem.

Imagem para teste de aplicação

  1. Guarde esta imagem no seu dispositivo local para testar a aplicação. Altere o formato da imagem para .jpg se necessário. Você também pode usar qualquer outra imagem relevante do seu dispositivo local nos formatos .jpg ou .png.

  2. Para executar o projeto, selecione o Start Debugging botão na barra de ferramentas ou pressione F5.

  3. Quando o aplicativo for iniciado, pressione Pick Image e selecione a imagem do seu dispositivo local.

Interface de aplicação

O resultado aparecerá na tela imediatamente. Como você pode ver, nosso aplicativo Windows ML classificou com sucesso a imagem como um carro.

Classificação bem-sucedida em seu aplicativo

Resumo

Você acabou de criar seu primeiro aplicativo de Aprendizado de Máquina do Windows, desde a criação do modelo até a execução bem-sucedida.

Recursos adicionais

Para saber mais sobre os tópicos mencionados neste tutorial, visite os seguintes recursos: