Поделиться через


Развертывание модели ML.NET в приложении для Windows с помощью API Windows Machine Learning

В предыдущей части этого руководства описано, как создать модель ML.NET и экспортировать ее в формат ONNX. Теперь, когда вы настроили эту модель, ее можно внедрить в приложение для Windows и запускать локально на устройстве путем вызова API WinML.

Завершив этот процесс, вы получите работающее приложение UWP (C#) для классификации изображений с использованием WinML.

Сведения о примере приложения

Используя модель, мы создадим приложение для классификации изображений еды. Такое приложение позволяет выбрать изображение на локальном устройстве и обработать его с помощью локально хранимой модели классификации ONNX, которая была создана и обучена в предыдущей части. Возвращенные теги, как и доверительная вероятность классификации, будут отображаться рядом с изображением.

Если вы уже работали с этим руководством, у вас должны быть все необходимые компоненты для разработки приложений. Если вам нужно освежить полученные знания, вернитесь к первой части этого руководства.

Примечание.

Если вы предпочитаете скачать готовый пример кода, можно клонировать файл решения. Клонируйте репозиторий, перейдите к этому примеру и откройте файл classifierMLNETModel.sln в Visual Studio. Затем можно сразу перейти к шагу [Запуск приложения](#Launch the application).

Создание приложения UWP (C#) для WinML

Далее вы узнаете, как создать с нуля приложение и код для работы с WinML. Вы изучите следующие темы:

  • Загрузите модель машинного обучения.
  • загрузить изображение в нужном формате;
  • Создайте привязки для входных и выходных данных модели.
  • Оцените модели и отобразите полезные результаты.

Вы также создадите простой графический пользовательский интерфейс на основе базового языка XAML, чтобы протестировать классификатор изображений.

Создание приложения

  1. Откройте Visual Studio и выберите create a new project.

Create a new Visual Studio project

  1. В строке поиска введите UWP и выберите Blank APP (Universal Windows. Это действие открывает новый проект C# для одностраничного приложения универсальной платформы Windows (UWP), не имеющего предопределенных элементов управления или макета. Выберите 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. После создания проекта перейдите в папку проекта, откройте папку ресурсов [….\ImageClassifierAppUWP\Assets] и скопируйте в это расположение файл bestModel.onnx.

Обзор решения проекта

Давайте изучим решение проекта.

Visual Studio автоматически создает несколько файлов cs-code в Обозревателе решений. Файл MainPage.xaml содержит код XAML для графического пользовательского интерфейса, а файл MainPage.xaml.cs — код приложения. Если вы уже создавали приложения UWP, эти файлы должны быть вам хорошо знакомы.

Создание графического пользовательского интерфейса приложения

Сначала мы создадим простой графический пользовательский интерфейс для приложения.

  1. Дважды щелкните файл MainPage.xaml. В пустом приложении шаблон XAML для графического пользовательского интерфейса приложения пуст, поэтому нам нужно добавить некоторые возможности пользовательского интерфейса.

  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) — это расширение Visual Studio, которое поможет вам приступить к использованию API-интерфейсов WinML в приложениях UWP. Он создает код шаблона при добавлении обученного файла ONNX в проект UWP.

Генератор кода Windows Machine Learning mlgen создает интерфейс (для C#, C++/WinRT и C++/CX) с классами-оболочками, которые вызывают API Windows ML. Это позволяет легко загружать, привязывать и оценивать модель в проекте. Мы будем использовать его в этом руководстве для решения многих задач такого типа.

Генератор кода доступен для Visual Studio 2017 и более поздних версий. Имейте в виду, что в Windows 10 версии 1903 и более поздних версиях mlgen больше не входит в Windows 10 SDK, поэтому вы должны скачать и установить расширение. Если вы выполняли все инструкции из этого руководства с самого начала, то у вас уже есть это средство. Если нет, то вам следует скачать версию для Visual Studio 2019 или для Visual Studio 2017.

Примечание.

Дополнительные сведения см. в документации по mlgen.

  1. Установите mlgen, если вы еще этого не сделали.

  2. Щелкните правой кнопкой мыши папку Assets в Обозревателе решений в Visual Studio и выберите Add > Existing Item.

  3. Перейдите в папку ресурсов внутри ImageClassifierAppUWP [….\ImageClassifierAppUWP\Assets], найдите модель ONNX, которую вы ранее скопировали туда, и выберите add.

  4. Когда вы добавите модель ONNX (с именем classifier) в папку ресурсов в Обозревателе решений в VS, проект будет иметь два новых файла:

  • bestModel.onnx — это модель в формате ONNX;
  • bestModel.cs — автоматически созданный файл кода WinML.

Project structure with ONNX model added

  1. Чтобы убедиться, что модель успешно компилируется вместе с приложением, выберите файл bestModel.onnx и щелкните Properties. В Build Action выберите Content.

Теперь давайте изучим только созданный код в файле bestModel.cs.

Созданный код включает в себя три класса:

  • Класс bestModelModel содержит два метода: для создания экземпляра модели и оценки модели. Этот класс поможет создать представление модели машинного обучения и сеанс на системном устройстве по умолчанию, привязать к модели определенные входные и выходные данные, а также асинхронно анализировать модель.
  • Класс 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. Добавьте следующие объявления переменных после операторов using внутри класса MainPage в пространстве имен 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, поэтому вам не нужно реализовывать его. Чтобы проверить этот метод, дважды щелкните файл bestModel.cs, созданный с помощью mlgen.

Дополнительные сведения о классе LearningModel см. в документации по классу LearningModel.

Дополнительные сведения о способах загрузки модели см. в документации по загрузке модели.

  1. Давайте определим метод main.
// The main page to initialize and execute the model.
public MainPage()
{
	this.InitializeComponent();
	loadModel();
}
  1. Добавьте реализацию метода loadModel в файл кода MainPage.xaml.cs внутри класса MainPage.
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. Необходимо определить событие щелчка, которое инициирует последовательность из вызовов четырех методов для выполнения модели — преобразование, привязку и оценку, извлечение выходных данных и отображение результатов. Добавьте следующий метод в файл кода MainPage.xaml.cs внутри класса MainPage.
        // 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.xaml.cs внутри класса MainPage.
        // 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;
        }

Теперь вы реализуете метод изображения Bind(), чтобы получить представление файла в формате растрового изображения BGRA8. Но сначала нужно создать вспомогательный класс для изменения размера изображения.

  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.

Примечание.

В этом руководстве мы вместо тензора используем класс ImageFeatureValue. Если Window ML не поддерживает цветовой формат вашей модели, такой вариант вам не подходит. Пример работы с преобразованием изображений и преобразованием в тензор см. здесь.

  1. Добавьте реализацию метода convert() в файл кода MainPage.xaml.cs внутри класса MainPage. Метод 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. Этот класс используется для оценки моделей машинного обучения и привязки модели к устройству, которое затем запускается и оценивает модель. Вы можете выбрать устройство при создании сеанса, чтобы иметь возможность выполнять модель на определенном устройстве компьютера. В качестве устройства по умолчанию используется ЦП.

Примечание.

Дополнительные сведения о том, как выбрать устройство, см. в документации по созданию сеанса.

Привязка входных и выходных данных:

Для привязки входных и выходных данных нужно использовать класс LearningModelBinding. Модель машинного обучения имеет выходные и выходные признаки для передачи информации в модель и из нее. Имейте в виду, что требуемые признаки должны поддерживаться API Window ML. Класс LearningModelBinding применяется к LearningModelSession для привязки значений к именованным входным и выходным признакам.

Реализация привязки автоматически создается с помощью mlgen, поэтому вам не нужно об этом беспокоиться. Привязка реализуется путем вызова предопределенных методов класса LearningModelBinding. В нашем случае используется метод Bind для привязки значения к именованному типу признака.

Оценка модели:

Создав сеанс для привязки модели и привязанные значения для входных и выходных данных модели, вы можете приступить к оценке входных данных модели и получить ее прогнозы. Чтобы запустить выполнение модели, следует вызвать любой из предопределенных методов оценки в LearningModelSession. В нашем примере мы будем использовать метод EvaluateAsync.

Как и CreateFromStreamAsync, метод EvaluateAsync был автоматически создан генератором кода WinML, поэтому вам не нужно реализовывать этот метод. Этот метод можно просмотреть в файле bestModel.cs.

Метод EvaluateAsync будет асинхронно оценивать модель машинного обучения, используя значения признаков, уже привязанные в привязках. Он создает сеанс с помощью метода LearningModelSession, привязывает входные и выходные данные с помощью метода LearningModelBinding, выполняет оценку модели и получает выходные признаки модели с помощью класса LearningModelEvaluationResult.

Примечание.

Чтобы узнать о других методах оценки для запуска модели, проверьте, какие методы можно реализовать в LearningModelSession, просмотрев документацию по классу LearningModelSession.

  1. Добавьте следующий метод в файл кода MainPage.xaml.cs внутри класса MainPage, чтобы создать сеанс, привязать и оценить модель.
        // A method to evaluate the model
        private async Task evaluate()
        {
            results = await modelGen.EvaluateAsync(image);
        }

Извлечение и отображение результатов

Теперь вам нужно извлечь выходные данные модели и отобразить правильный результат, что можно выполнить с помощью реализации методов extractResult и displayResult. Чтобы вернуть правильную метку, нужно найти наибольшую вероятность.

  1. Добавьте метод extractResult в файл кода MainPage.xaml.cs внутри класса MainPage.
        // 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. Добавьте метод displayResult в файл кода MainPage.xaml.cs внутри класса MainPage.
        private async Task displayResult() 
        {
            displayOutput.Text = label; 
        }

Вот и все! Вы успешно создали приложение Windows Machine Learning с простым графическим интерфейсом для тестирования модели классификации. Следующий шаг — запуск приложения на локальном устройстве Windows.

Запуск приложения

После того, как вы создали интерфейс приложения, добавили модель и сгенерировали код WinML, вы можете протестировать приложение.

Включите режим разработчика и протестируйте приложение в Visual Studio. В раскрывающихся меню на верхней панели инструментов должен быть указан режим Debug. Для запуска проекта на локальном компьютере укажите для параметра "Платформа решения" значение x64 для 64-разрядного устройства или x86 для 32-разрядного.

Чтобы протестировать приложение, используйте следующее изображение супа. Давайте посмотрим, как наше приложение классифицирует содержимое этого изображения.

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 — от этапа создания модели до этапа успешного выполнения.

Дополнительные ресурсы

Дополнительные сведения см. в следующих статьях: