Развертывание модели 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, чтобы протестировать классификатор изображений.
Создание приложения
- Откройте Visual Studio и выберите
create a new project
.
- В строке поиска введите
UWP
и выберитеBlank APP (Universal Windows
. Это действие открывает новый проект C# для одностраничного приложения универсальной платформы Windows (UWP), не имеющего предопределенных элементов управления или макета. ВыберитеNext
, чтобы открыть окно конфигурации для проекта.
- В окне настроек сделайте следующее:
- Выберите имя для проекта. В нашем примере это classifierMLNETModel.
- Выберите расположение проекта.
- Если вы используете VS 2019, убедитесь, что флажок рядом с
Place solution and project in the same directory
снят. - Если вы используете VS 2017, убедитесь, что флажок рядом с
Create directory for solution
установлен.
Щелкните create
, чтобы создать проект. Может появиться окно для ввода минимальной целевой версией. Убедитесь, что в качестве минимальной версии указана версияWindows 10, сборка 17763 или более поздняя.
Чтобы создать приложение и развернуть модель с помощью приложения WinML, вам потребуется следующее:
- После создания проекта перейдите в папку проекта, откройте папку ресурсов [….\ImageClassifierAppUWP\Assets] и скопируйте в это расположение файл
bestModel.onnx
.
Обзор решения проекта
Давайте изучим решение проекта.
Visual Studio автоматически создает несколько файлов cs-code в Обозревателе решений. Файл MainPage.xaml
содержит код XAML для графического пользовательского интерфейса, а файл MainPage.xaml.cs
— код приложения. Если вы уже создавали приложения UWP, эти файлы должны быть вам хорошо знакомы.
Создание графического пользовательского интерфейса приложения
Сначала мы создадим простой графический пользовательский интерфейс для приложения.
Дважды щелкните файл
MainPage.xaml
. В пустом приложении шаблон XAML для графического пользовательского интерфейса приложения пуст, поэтому нам нужно добавить некоторые возможности пользовательского интерфейса.Замените код
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.
Установите mlgen, если вы еще этого не сделали.
Щелкните правой кнопкой мыши папку
Assets
в Обозревателе решений в Visual Studio и выберитеAdd > Existing Item
.Перейдите в папку ресурсов внутри
ImageClassifierAppUWP [….\ImageClassifierAppUWP\Assets]
, найдите модель ONNX, которую вы ранее скопировали туда, и выберитеadd
.Когда вы добавите модель ONNX (с именем classifier) в папку ресурсов в Обозревателе решений в VS, проект будет иметь два новых файла:
bestModel.onnx
— это модель в формате ONNX;bestModel.cs
— автоматически созданный файл кода WinML.
- Чтобы убедиться, что модель успешно компилируется вместе с приложением, выберите файл
bestModel.onnx
и щелкнитеProperties
. ВBuild Action
выберитеContent
.
Теперь давайте изучим только созданный код в файле bestModel.cs
.
Созданный код включает в себя три класса:
- Класс
bestModelModel
содержит два метода: для создания экземпляра модели и оценки модели. Этот класс поможет создать представление модели машинного обучения и сеанс на системном устройстве по умолчанию, привязать к модели определенные входные и выходные данные, а также асинхронно анализировать модель. - Класс
bestModelInput
инициализирует типы входных данных, которые ожидает получить модель. Входные данные модели зависят от требований модели к входным данным. - Класс
bestModelOutput
инициализирует типы, которые модель выводит. Выходные данные модели зависят от того, как они определяются моделью.
Вы примените эти классы для загрузки, привязки и оценки модели в нашем проекте.
Преобразование в тензор
Чтобы преобразование в тензор стало проще, измените входной класс TensorFloat
на ImageFeatureValue
.
- Внесите следующие изменения в файл
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)
}
Загрузка модели и входных данных
Загрузка модели
Дважды щелкните файл кода
MainPage.xaml.cs
, чтобы открыть код приложения.Замените операторы 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;
- Добавьте следующие объявления переменных после операторов 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.
Дополнительные сведения о способах загрузки модели см. в документации по загрузке модели.
- Давайте определим метод main.
// The main page to initialize and execute the model.
public MainPage()
{
this.InitializeComponent();
loadModel();
}
- Добавьте реализацию метода
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);
}
Загрузка изображения
- Необходимо определить событие щелчка, которое инициирует последовательность из вызовов четырех методов для выполнения модели — преобразование, привязку и оценку, извлечение выходных данных и отображение результатов. Добавьте следующий метод в файл кода
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();
}
- Теперь вы реализуете метод
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. Но сначала нужно создать вспомогательный класс для изменения размера изображения.
- Чтобы создать вспомогательный файл, щелкните имя решения (
ClassifierPyTorch
) правой кнопкой мыши, а затем выберитеAdd a new item
. В открывшемся окне выберитеClass
и присвойте ему имя. В нашем примере этоHelper
.
- В вашем проекте появится новый файл класса. Откройте этот класс и добавьте в него следующий код:
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 не поддерживает цветовой формат вашей модели, такой вариант вам не подходит. Пример работы с преобразованием изображений и преобразованием в тензор см. здесь.
- Добавьте реализацию метода
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.
- Добавьте следующий метод в файл кода
MainPage.xaml.cs
внутри класса MainPage, чтобы создать сеанс, привязать и оценить модель.
// A method to evaluate the model
private async Task evaluate()
{
results = await modelGen.EvaluateAsync(image);
}
Извлечение и отображение результатов
Теперь вам нужно извлечь выходные данные модели и отобразить правильный результат, что можно выполнить с помощью реализации методов extractResult
и displayResult
. Чтобы вернуть правильную метку, нужно найти наибольшую вероятность.
- Добавьте метод
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();
}
- Добавьте метод
displayResult
в файл кодаMainPage.xaml.cs
внутри классаMainPage
.
private async Task displayResult()
{
displayOutput.Text = label;
}
Вот и все! Вы успешно создали приложение Windows Machine Learning с простым графическим интерфейсом для тестирования модели классификации. Следующий шаг — запуск приложения на локальном устройстве Windows.
Запуск приложения
После того, как вы создали интерфейс приложения, добавили модель и сгенерировали код WinML, вы можете протестировать приложение.
Включите режим разработчика и протестируйте приложение в Visual Studio. В раскрывающихся меню на верхней панели инструментов должен быть указан режим Debug
. Для запуска проекта на локальном компьютере укажите для параметра "Платформа решения" значение x64 для 64-разрядного устройства или x86 для 32-разрядного.
Чтобы протестировать приложение, используйте следующее изображение супа. Давайте посмотрим, как наше приложение классифицирует содержимое этого изображения.
Сохраните это изображение на локальном устройстве, чтобы протестировать приложение. При необходимости измените формат изображения на
.jpg
. Вы также можете использовать любое другое релевантное изображение, сохраненное на локальном устройстве в формате.jpg
или.png
.Чтобы запустить проект, нажмите кнопку
Start Debugging
на панели инструментов или нажмите клавишуF5
.При запуске приложения щелкните
Pick Image
и выберите изображение на локальном устройстве.
Результат сразу отобразится на экране. Как видите, приложение Windows ML успешно распознало суп на этом изображении.
Итоги
Вы только что создали свое первое приложение для Windows Machine Learning — от этапа создания модели до этапа успешного выполнения.
Дополнительные ресурсы
Дополнительные сведения см. в следующих статьях:
- Инструменты Windows ML. Ознакомьтесь с дополнительными инструментами, такими как информационная панель Windows ML, WinMLRunner и генератор кода mglen из Windows ML.
- Модель ONNX. Дополнительные сведения о формате ONNX.
- Использование вычислительной мощности и памяти для Windows ML. Узнайте, как управлять производительностью приложений в Windows ML.нк
- Справочник по API Windows Machine Learning. Узнайте больше о трех областях API-интерфейсов Windows ML.