Руководство. Создание приложения UWP (C#) для Windows Machine Learning

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

Соответствующие инструкции можно просмотреть в формате видео.


Если вы предпочитаете просмотреть финальную версию кода для этого руководства, см. репозиторий WinML на GitHub. Также доступен аналогичный код на C++/CX.

Необходимые компоненты

1. Открытие проекта в Visual Studio

Скачав проект из GitHub, запустите Visual Studio и откройте файл MNIST_Demo.sln (он должен находиться в папке <путь_к_репозиторию>\Windows-Machine-Learning\Samples\MNIST\Tutorial\cs). Если решение отображается как недоступное, нужно щелкнуть проект правой кнопкой мыши в обозревателе решений и выбрать Перезагрузить проект.

Мы предлагаем шаблон с реализованными событиями и элементами управления XAML, включая следующие:

  • InkCanvas для написания цифр;
  • кнопки для интерпретации цифр и очистки холста;
  • вспомогательные процедуры для преобразования выходных данных InkCanvas в формат VideoFrame.

В обозревателе решений проект содержит три основных файла с кодом:

  • MainPage.xaml — весь XAML-код для создания пользовательского интерфейса для InkCanvas, кнопок и меток.
  • MainPage.xaml.cs — здесь находится код приложения.
  • Helper.cs — вспомогательные процедуры для обрезки и преобразования форматов изображений.

Visual Studio solution explorer with project files

2. Сборка и запуск проекта

Для запуска проекта на локальном компьютере на панели инструментов Visual Studio укажите для параметра Платформа решения значение x64 для 64-битного устройства и значение x86 для 32-битного. (Вы можете проверить тип системы в приложении "Параметры" Windows: Система > О программе > Характеристики устройства > Тип системы.)

Чтобы запустить проект, нажмите кнопку Начать отладку на панели инструментов или клавишу F5. Приложение отобразит элемент InkCanvas, где пользователи могут написать цифру, кнопку Распознать, чтобы интерпретировать эту цифру, поле с пустой меткой, где интерпретированная цифра отобразится в виде текста, и кнопку Удалить цифру для очистки InkCanvas.

Application screenshot

Примечание.

Если проект не компилируется, возможно, нужно изменить целевую версию развертывания для этого проекта. В обозревателе решений щелкните проект правой кнопкой мыши и выберите Свойства. На вкладке Приложение установите значения целевой и минимальной версий в соответствии с используемыми ОС и SDK.

Примечание.

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

3. Скачивание модели

Теперь мы получим модель машинного обучения и добавим ее в приложение. В этом руководстве мы применим предварительно подготовленную модель MNIST, обученную с помощью Microsoft Cognitive Toolkit (CNTK) и экспортированную в формат ONNX.

Модель MNIST уже содержится в папке Assets, и вам нужно добавить ее в приложение в качестве существующего элемента. Также вы можете скачать предварительно обученную модель в репозитории ONNX Model Zoo на GitHub.

4. Добавление модели

Щелкните правой кнопкой мыши папку Assets в обозревателе решений и выберите Добавить>Существующий элемент. В средстве выбора файлов найдите расположение с моделью ONNX и щелкните Добавить.

В проекте должны появиться два новых файла:

  • mnist.onnx содержит обученную модель;
  • mnist.cs содержит сгенерированный код Windows ML.

Solution explorer with new files

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

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

  • mnistModel создает представление модели машинного обучения и сеанс на системном устройстве по умолчанию, привязывает к модели определенные входные и выходные данные, а также асинхронно анализирует модель.
  • mnistInput инициализирует входные типы, которые ожидает модель. В этом случае на входе ожидается значение ImageFeatureValue.
  • mnistOutput инициализирует типы, которые будут выходными данными модели. В нашем примере выходными данными будет список с именем Plus214_Output_0 и типом TensorFloat.

Мы применим эти классы для загрузки, привязки и анализа модели в нашем проекте.

5. Загрузка, привязка и оценка модели

Для приложений Windows ML мы будем придерживаться следующей процедуры: загрузка > привязка > оценка.

  1. загрузка модели машинного обучения;
  2. привязка входных и выходных данных к модели;
  3. оценка модели и просмотр результатов.

Мы применим код интерфейса, созданный в mnist.cs, для загрузки, привязки и анализа модели в нашем приложении.

Во-первых, мы создадим в MainPage.xaml.cs экземпляры модели, а также входных и выходных данных. Добавьте следующие переменные-члены в класс MainPage.

private mnistModel ModelGen;
private mnistInput ModelInput = new mnistInput();
private mnistOutput ModelOutput;

Затем мы загрузим модель в LoadModelAsync. Этот метод нужно вызывать перед использованием любого из методов модели (то есть в событии Loaded объекта MainPage, в переопределении OnNavigatedTo или в любом месте до вызова recognizeButton_Click). Класс mnistModel представляет модель MNIST и создает сеанс на системном устройстве по умолчанию. Чтобы загрузить эту модель, мы вызовем метод CreateFromStreamAsync, передавая в качестве параметра ONNX-файл.

private async Task LoadModelAsync()
{
    // Load a machine learning model
    StorageFile modelFile = await StorageFile.GetFileFromApplicationUriAsync(new Uri($"ms-appx:///Assets/mnist.onnx"));
    ModelGen = await mnistModel.CreateFromStreamAsync(modelFile as IRandomAccessStreamReference);
}

Примечание.

Если слово IRandomAccessStreamReference будет подчеркиваться красной линией, необходимо включить соответствующее пространство имен. Наведите на него курсор и нажмите клавиши CTRL+ ., а затем выберите в раскрывающемся меню вариант С использованием Windows.Storage.Streams.

Теперь нам нужно привязать к модели входные и выходные данные. Созданный код содержит также классы-оболочки mnistInput и mnistOutput. Класс mnistInput представляет ожидаемые входные данные модели, а класс mnistOutput — выходные данные модели.

Чтобы инициализировать входной объект модели, вызовите конструктор класса mnistInput и передайте ему данные приложения. При этом тип входных данных должен соответствовать тому, который ожидает модель. Класс mnistInput ожидает получить ImageFeatureValue, поэтому мы создадим для него ImageFeatureValue с помощью вспомогательного метода.

Используя включенные в helper.cs вспомогательные функции, мы скопируем содержимое InkCanvas, преобразуем его в тип ImageFeatureValue и привяжем к нашей модели.

private async void recognizeButton_Click(object sender, RoutedEventArgs e)
{
    // Bind model input with contents from InkCanvas
    VideoFrame vf = await helper.GetHandWrittenImage(inkGrid);
    ModelInput.Input3 = ImageFeatureValue.CreateFromVideoFrame(vf);
}

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

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

private async void recognizeButton_Click(object sender, RoutedEventArgs e)
{
    // Bind model input with contents from InkCanvas
    VideoFrame vf = await helper.GetHandWrittenImage(inkGrid);
    ModelInput.Input3 = ImageFeatureValue.CreateFromVideoFrame(vf);

    // Evaluate the model
    ModelOutput = await ModelGen.EvaluateAsync(ModelInput);

    // Convert output to datatype
    IReadOnlyList<float> vectorImage = ModelOutput.Plus214_Output_0.GetAsVectorView();
    IList<float> imageList = vectorImage.ToList();

    // Query to check for highest probability digit
    var maxIndex = imageList.IndexOf(imageList.Max());

    // Display the results
    numberLabel.Text = maxIndex.ToString();
}

Наконец, мы очистим InkCanvas, чтобы пользователь мог написать новую цифру.

private void clearButton_Click(object sender, RoutedEventArgs e)
{
    inkCanvas.InkPresenter.StrokeContainer.Clear();
    numberLabel.Text = "";
}

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

Завершив сборку приложения и запустив его (с помощью клавиши F5), мы сможем распознать цифру, написанную на InkCanvas.

complete application

Все готово! Вы создали первое приложение для Windows ML. Дополнительные примеры, демонстрирующие способы использования Windows ML, см. в репозитории Windows-Machine-Learning на GitHub.

Примечание.

Используйте следующие ресурсы для получения справки по машинному обучению в Windows:

  • Чтобы задать технические вопросы о машинном обучении в Windows или ответить на них, используйте тег windows-machine-learning в Stack Overflow.
  • Сообщить об ошибке можно в нашем репозитории GitHub.