Tutorial: Creación de una aplicación para UWP de Windows Machine Learning (C#)

En este tutorial, compilaremos una aplicación para la Plataforma universal de Windows simple que use un modelo de aprendizaje automático entrenado para reconocer un dígito numérico dibujado por el usuario. Este tutorial se centra principalmente en cómo cargar y usar Windows ML en tu aplicación para UWP.

El siguiente vídeo te guía por el ejemplo en el que se basa este tutorial.


Si prefieres examinar el código del tutorial finalizado, puedes encontrarlo en el repositorio de WinML en GitHub. También está disponible en C++/CX.

Requisitos previos

1. Abrir el proyecto en Visual Studio

Después de descargar el proyecto desde GitHub, inicia Visual Studio y abre el archivo MNIST_Demo.sln (debe encontrarse en la <ruta de acceso al repositorio>\Windows-Machine-Learning\Samples\MNIST\Tutorial\cs). Si la solución se muestra como no disponible, tendrás que hacer clic con el botón derecho en el proyecto en el Explorador de soluciones y seleccionar Volver a cargar el proyecto.

Hemos proporcionado una plantilla con controles y eventos XAML implementados, que incluye:

  • Un control InkCanvas para dibujar el dígito.
  • Botones para interpretar el dígito y borrar el lienzo.
  • Rutinas auxiliares para convertir la salida de InkCanvas en un VideoFrame.

En el Explorador de soluciones, el proyecto tiene tres archivos de código principales:

  • MainPage.xaml: todo nuestro código XAML para crear la interfaz de usuario para el control InkCanvas, los botones y las etiquetas.
  • MainPage.xaml.cs: lugar en el que reside el código de la aplicación.
  • Helper.cs: rutinas auxiliares para recortar y convertir formatos de imagen.

Visual Studio solution explorer with project files

2. Compilar y ejecutar el proyecto

En la barra de herramientas de Visual Studio, cambia la plataforma de soluciones de a x64 para ejecutar el proyecto en la máquina local si el dispositivo es de 64 bits o a x86 si el dispositivo es de 32 bits. (Puede consultar la aplicación Configuración de Windows: Sistema > Acerca de > Especificaciones del dispositivo > Tipo de sistema).

Para ejecutar el proyecto, haz clic en el botón Iniciar depuración en la barra de herramientas o pulsa F5. La aplicación debe mostrar un control InkCanvas donde los usuarios podrán escribir un dígito, un botón Reconocer para interpretar el número, un campo de etiqueta vacío donde se mostrará el dígito interpretado como texto y un botón Borrar dígito para borrar el control InkCanvas.

Application screenshot

Nota:

Si el proyecto no se va a compilar, puede que tengas que cambiar la versión de destino de implementación del proyecto. Haz clic con el botón derecho en el Explorador de soluciones y selecciona Propiedades. En la pestaña Aplicación, establece Versión de destino y Versión mínima para que coincidan con el sistema operativo y el SDK.

Nota:

Si recibes una advertencia que indica que la aplicación ya está instalada, selecciona para continuar con la implementación. Es posible que tengas que cerrar Visual Studio y volver a abrirlo si sigue sin funcionar.

3. Descargar un modelo

A continuación, vamos a obtener un modelo de aprendizaje automático para agregarlo a nuestra aplicación. Para este tutorial, usaremos un modelo MNIST previamente entrenado con Microsoft Cognitive Toolkit (CNTK) y exportado al formato ONNX.

El modelo MNIST ya se ha incluido en la carpeta Assets y tendrás que agregarlo a la aplicación como un elemento existente. También puedes descargar el modelo previamente entrenado desde ONNX Model Zoo en GitHub.

4. Agregar el modelo

Haz clic con el botón derecho en la carpeta Assets en el Explorador de soluciones y selecciona Agregar>elemento existente. Apunta con el selector de archivos a la ubicación del modelo ONNX y haz clic en Agregar.

El proyecto ahora debería tener dos archivos nuevos:

  • mnist. onnx: modelo entrenado.
  • mnist.cs: código generado por Windows ML.

Solution explorer with new files

Para asegurarte de que el modelo se genere cuando compilemos la aplicación, haz clic con el botón derecho en el archivo mnist.onnx y selecciona Propiedades. En Acción de compilación, selecciona Contenido.

Ahora, veamos el código recién generado en el archivo mnist.cs. Tenemos tres clases:

  • mnistModel crea la representación del modelo de aprendizaje automático, crea una sesión en el dispositivo predeterminado del sistema, enlaza las entradas y salidas específicas al modelo y evalúa el modelo de forma asincrónica.
  • mnistInput inicializa los tipos de entrada que el modelo espera. En este caso, la entrada espera un elemento ImageFeatureValue.
  • mnistOutput inicializa los tipos que el modelo generará. En este caso, la salida será una lista denominada Plus214_Output_0 de tipo TensorFloat.

Ahora usaremos estas clases para cargar, enlazar y evaluar el modelo en nuestro proyecto.

5. Cargar, enlazar y evaluar el modelo

Para las aplicaciones de Windows ML, el patrón que queremos seguir es: cargar > enlazar > evaluar.

  1. Cargue el modelo de Machine Learning.
  2. Enlace entradas y salidas al modelo.
  3. Evalúe el modelo y vea los resultados.

Vamos a usar el código de interfaz generado en mnist.cs para cargar, enlazar y evaluar el modelo en nuestra aplicación.

Primero, en MainPage.xaml.cs, vamos a crear una instancia del modelo, las entradas y las salidas. Agrega las siguientes variables de miembro a la clase MainPage:

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

A continuación, en LoadModelAsync, cargaremos el modelo. Se debe llamar a este método antes de usar cualquiera de los métodos del modelo (es decir, en el evento Loaded en MainPage, en una invalidación OnNavigatedTo, o en cualquier lugar antes de llamar a recognizeButton_Click). La clase mnistModel representa el modelo MNIST y crea la sesión en el dispositivo predeterminado del sistema. Para cargar el modelo, llamamos al método CreateFromStreamAsync y pasamos el archivo ONNX como parámetro.

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);
}

Nota:

Si aparece un subrayado rojo en IRandomAccessStreamReference, debes incluir su espacio de nombres. Coloque el cursor encima, presione Ctrl + . y seleccione using Windows.Storage.Streams (usar Windows.Storage.Streams) en el menú desplegable.

A continuación, queremos enlazar nuestras entradas y salidas al modelo. El código generado también incluye las clases de contenedor mnistInput y mnistOutput. La clase mnistInput representa las entradas esperadas del modelo y la clase mnistOutput representa las salidas esperadas del modelo.

Para inicializar el objeto de entrada del modelo, llama al constructor de clase mnistInput, pasa los datos de la aplicación y asegúrate de que los datos de entrada coinciden con el tipo de entrada que el modelo espera. La clase mnistInput espera un tipo ImageFeatureValue, por lo que usamos un método auxiliar para obtener un tipo ImageFeatureValue para la entrada.

Utilizando nuestras funciones auxiliares incluidas en helper.cs, copiaremos el contenido de InkCanvas, lo convertiremos al tipo ImageFeatureValue y lo enlazaremos a nuestro modelo.

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);
}

Para la salida, simplemente llamaremos a EvaluateAsync con la entrada especificada. Una vez que se hayan inicializado tus entradas, llama al método EvaluateAsync del modelo para evaluar el modelo en los datos de entrada. EvaluateAsync enlaza tus entradas y salidas al objeto del modelo y evalúa el modelo en las entradas.

Dado que el modelo devuelve un tensor de salida, primero lo convertiremos en un tipo de datos descriptivo y, a continuación, analizaremos la lista devuelta para determinar qué dígito tenía la probabilidad más alta y mostrarlo.

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();
}

Por último, borraremos el control InkCanvas para permitir a los usuarios dibujar otro número.

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

6. Iniciar la aplicación

Una vez que compilemos e iniciemos la aplicación (presiona F5), podremos reconocer un número dibujado en InkCanvas.

complete application

Eso es todo, has creado tu primera aplicación de Windows ML. Para obtener más ejemplos sobre cómo usar Windows ML, consulta nuestro repositorio Windows-Machine-Learning en GitHub.

Nota:

Use los siguientes recursos para obtener ayuda con Windows ML:

  • Para formular o responder a preguntas técnicas sobre Windows Machine Learning, utilice la etiqueta windows-machine-learning en Stack Overflow.
  • Para notificar un error, registre un problema en GitHub.