Implementación del modelo en una aplicación de Windows con la API de Windows ML
Nota:
Para obtener una mayor funcionalidad, PyTorch también se puede usar con DirectML en Windows.
En la parte anterior de este tutorial, aprendió a compilar y exportar un modelo en formato ONNX. Ahora, le mostraremos cómo insertar el modelo exportado en una aplicación de Windows y ejecutarlo localmente en un dispositivo mediante una llamada a las API de WinML.
Cuando hayamos terminado, tendrá una aplicación de clasificación de imágenes que funcionará.
Acerca de la aplicación de ejemplo
En este paso del tutorial, creará una aplicación que puede clasificar imágenes mediante el modelo de ML. Su interfaz de usuario básica le permite seleccionar una imagen del dispositivo local y usa el modelo de ONNX de clasificación que ha creado y entrenado en la parte anterior. Las etiquetas devueltas por el modelo se muestran junto a la imagen.
Aquí, le acompañaremos en ese proceso.
Nota:
Si decide usar el ejemplo de código predefinido, puede clonar el archivo de solución. Clone el repositorio, navegue hasta el ejemplo y abra el archivo classifierPyTorch.sln
con Visual Studio. Vaya a la parte Iniciar la aplicación de esta página para verlo en uso.
A continuación, le guiaremos en la creación de la aplicación y la adición de código Windows ML.
Creación de una aplicación para UWP de Windows ML (C#)
Para crear una aplicación de Windows ML que funcione, deberá hacer lo siguiente:
- Cargar un modelo de aprendizaje automático.
- Cargar una imagen en un formato necesario.
- Enlazar las entradas y salidas del modelo.
- Evaluar el modelo y mostrar resultados significativos.
También deberá crear una interfaz de usuario básica, ya que es difícil crear una aplicación satisfactoria basada en imágenes en la línea de comandos.
Apertura de un nuevo proyecto en Visual Studio
- Empecemos. Abra Visual Studio y elija Crear un proyecto.
- En la barra de búsqueda, escriba
UWP
y, a continuación, seleccioneBlank APP (Universal Windows)
. Se abre un proyecto de C# para una aplicación para Plataforma universal de Windows (UWP) de una sola página con diseño y controles predefinidos. Seleccionenext
para abrir una ventana de configuración para el proyecto.
- En la ventana de configuración, haga lo siguiente:
- Dé un nombre al proyecto. Aquí, lo llamamos clasificadorPyTorch.
- Elija la ubicación del proyecto.
- Si usa VS2019, asegúrese de que la opción
Create directory for solution
esté activada. - Si usa VS2017, asegúrese de que la opción
Place solution and project in the same directory
esté desactivada.
Presione create
para crear el proyecto. Puede que se abra la ventana de versión mínima de destino. Asegúrese de que la versión mínima esté establecida en Windows 10, versión 1809 (10.0; compilación 17763) o versión posterior.
- Después de crear el proyecto, vaya a la carpeta del proyecto, abra la carpeta assets
[….\classifierPyTorch \Assets]
y copie el archivoImageClassifier.onnx
en esta ubicación.
Exploración de la solución de proyecto
Vamos a explorar la solución de proyecto.
Visual Studio creó automáticamente varios archivos de código CS dentro del Explorador de soluciones. MainPage.xaml
contiene el código XAML de la GUI y MainPage.xaml.cs
contiene el código de la aplicación, también conocido como código subyacente. Si ha creado una aplicación para UWP antes, debería estar muy familiarizado con estos archivos.
Creación de la GUI de aplicación
En primer lugar, vamos a crear una GUI sencilla para la aplicación.
Haga doble clic en el archivo de código
MainPage.xaml
. En la aplicación en blanco, la plantilla XAML de la GUI de la aplicación está vacía, por lo que tendremos que agregar algunas características de la interfaz de usuario.Agregue el código siguiente a
MainPage.xaml
, reemplazando las etiquetas<Grid>
y</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>
Adición del modelo al proyecto mediante el generador de código de Windows ML (mlgen)
El generador de código de Windows Machine Learning, o mlgen, es una extensión de Visual Studio que le ayudará a empezar a usar las API de WinML en aplicaciones para UWP. Genera código de plantilla al agregar un archivo ONNX entrenado al proyecto de UWP.
El generador de códigos de Windows Machine Learning mlgen crea una interfaz (para C#, C++/WinRT y C++/CX) con clases contenedoras que llaman automáticamente a la API de Windows ML. Esto le permite cargar, enlazar y evaluar fácilmente un modelo en el proyecto. Lo usaremos en este tutorial para controlar muchas de esas funciones.
El generador de código está disponible para Visual Studio 2017 y versiones posteriores. Se recomienda usar Visual Studio. Tenga en cuenta que en Windows 10, versión 1903 y posteriores, mlgen ya no se incluye en el SDK de Windows 10, de modo que debe descargar e instalar la extensión. Si ha seguido este tutorial desde la introducción, ya se habrá encargado de esto, pero si no es así, debe descargarlo para VS 2019 o para VS 2017.
Nota:
Para más información sobre mlgen, consulte la documentación de mlgen.
Si todavía no lo ha hecho, instale mlgen.
Haga clic con el botón derecho en la carpeta
Assets
, en el Explorador de soluciones de Visual Studio, y seleccioneAdd > Existing Item
.Vaya a la carpeta assets dentro de
classifierPyTorch [….\classifierPyTorch \Assets]
, busque el modelo de ONNX que copió anteriormente y seleccioneadd
.Después de agregar un modelo de ONNX a la carpeta assets en el Explorador de soluciones de VS, el proyecto debe tener dos archivos nuevos:
ImageClassifier.onnx
: el modelo en formato ONNX.ImageClassifier.cs
: archivo de código WinML generado automáticamente.
- Para asegurarse de que se crea el modelo al compilar la aplicación, seleccione el archivo
ImageClassifier.onnx
y elijaProperties
. EnBuild Action
, seleccioneContent
.
Código de archivo ONNX
Ahora, vamos a explorar el código recién generado en el archivo ImageClassifier.cs
.
El código generado incluye tres clases:
ImageClassifierModel
: esta clase incluye dos métodos para la creación de instancias del modelo y la evaluación del modelo. Eso nos ayudará a crear la representación del modelo de aprendizaje automático, crear una sesión en el dispositivo predeterminado del sistema, enlazar las entradas y salidas específicas al modelo y evaluar el modelo de forma asincrónica.ImageClassifierInput
: esta clase inicializa los tipos de entrada que el modelo espera. La entrada del modelo depende de los requisitos del modelo para los datos de entrada.ImageClassifierOutput
: esta clase inicializa los tipos que el modelo generará. La salida del modelo depende de cómo la defina el modelo.
En este tutorial, no queremos abarcar la tensorización. Realizaremos un pequeño cambio en la clase ImageClassifierInput
para cambiar el tipo de datos de entrada y facilitar la vida.
- Realice los siguientes cambios en el archivo
ImageClassifier.cs
:
Cambie la variable input
de TensorFloat
a ImageFeatureValue
.
public sealed class ImageClassifierInput
{
public ImageFeatureValue input; // shape(-1,3,32,32)
}
Carga del modelo
Haga doble clic en el archivo
MainPage.xaml.cs
para abrir el código subyacente de la aplicación.Reemplace las instrucciones "using" por las siguientes para obtener acceso a todas las API que necesitará:
// 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;
- Agregue las siguientes declaraciones de variable dentro de la clase
MainPage
, encima de la funciónpublic MainPage()
.
// 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
}
Ahora, implementará el método LoadModel
. El método accederá al modelo de ONNX y lo almacenará en memoria. A continuación, usará el método CreateFromStreamAsync
para crear una instancia del modelo como un objeto LearningModel
. La clase LearningModel
representa un modelo de aprendizaje automático entrenado. Una vez creada una instancia, LearningModel
es el objeto inicial que se usa para interactuar con Windows ML.
Para cargar el modelo, puede usar varios métodos estáticos en la clase LearningModel
. En este caso, usará el método CreateFromStreamAsync
.
El método CreateFromStreamAsync
se creó automáticamente con mlgen, por lo que no es necesario implementar este método. Puede revisar este método haciendo doble clic en el archivo classifier.cs
generado por mlgen.
Nota:
Para obtener más información sobre la clase LearningModel
, revise la documentación de la clase LearningModel. Para más información sobre otras formas de cargar el modelo, consulte la documentación de la carga de un modelo.
- Agregue una llamada a un método
loadModel
en el constructor de la clase principal.
// The main page to initialize and execute the model.
public MainPage()
{
this.InitializeComponent();
loadModel();
}
- Agregue la implementación del método
loadModel
, dentro de la claseMainPage
.
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);
}
Carga de la imagen
- Es necesario definir un evento de clic para iniciar la secuencia de cuatro llamadas del método para la ejecución del modelo: conversión, enlace y evaluación, extracción de salida y visualización de los resultados. Agregue el método siguiente al archivo de código
MainPage.xaml.cs
dentro de la claseMainPage
.
// 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();
}
- Ahora, implementará el método
getImage()
. Este método seleccionará un archivo de imagen de entrada y lo guardará en memoria. Agregue el método siguiente al archivo de códigoMainPage.xaml.cs
dentro de la claseMainPage
.
// 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;
}
A continuación, implementará un método Bind()
de imagen para obtener la representación del archivo en el formato BGRA8 del mapa de bits. Pero, en primer lugar, creará una clase auxiliar para cambiar el tamaño de la imagen.
- Para crear un archivo auxiliar, haga clic con el botón derecho en el nombre de la solución (
ClassifierPyTorch
) y elijaAdd a new item
. En la ventana abierta, seleccioneClass
y asígnele un nombre. Aquí, lo llamamosHelper
.
- Aparecerá un nuevo archivo de clase dentro del proyecto. Abra esta clase y agregue el código siguiente:
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;
}
}
}
Ahora, vamos a convertir la imagen al formato adecuado.
La clase ImageClassifierInput
inicializa los tipos de entrada que el modelo espera. En nuestro caso, hemos configurado el código para esperar un tipo ImageFeatureValue
.
La clase ImageFeatureValue
describe las propiedades de la imagen usada para pasar a un modelo. Para crear un tipo ImageFeatureValue
, se usa el método CreateFromVideoFrame
. Para obtener detalles más específicos del porqué de este caso y cómo funcionan estas clases y métodos, consulte la documentación de la clase ImageFeatureValue.
Nota:
En este tutorial, se usa la clase ImageFeatureValue
en lugar de un tensor. Si Windows ML no admite el formato de color del modelo, esta no será una opción. Para un ejemplo de cómo trabajar con conversiones de imágenes y tensorización, vea Ejemplo de tensorización personalizada.
- Agregue la implementación del método
convert()
al archivo de códigoMainPage.xaml.cs
dentro de la clase MainPage. El método de conversión nos dará una representación del archivo de entrada en un 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)
{
}
}
Enlace y evaluación del modelo
A continuación, creará una sesión basada en el modelo, enlazará la entrada y la salida de la sesión y evaluará el modelo.
Cree una sesión para enlazar el modelo:
Para crear una sesión, se usa la clase LearningModelSession
. Esta clase se usa para evaluar los modelos de aprendizaje automático y enlaza el modelo a un dispositivo que, a continuación, ejecuta y evalúa el modelo. Puede seleccionar un dispositivo al crear una sesión para ejecutar el modelo en un dispositivo específico de la máquina. El dispositivo predeterminado es la CPU.
Nota:
Para más información sobre cómo elegir un dispositivo, consulte la documentación de Creación de una sesión.
Enlace de las entradas y salidas del modelo:
Para enlazar la entrada y la salida, use la clase LearningModelBinding
. Un modelo de aprendizaje automático tiene características de entrada y salida, que pasan información dentro y fuera del modelo. Tenga en cuenta que las características necesarias deben ser compatibles con a API de Windows ML. La clase LearningModelBinding
se aplica a una clase LearningModelSession
para enlazar valores a características de entrada y salida con nombre.
mlgen genera automáticamente la implementación del enlace, por lo que no tiene que ocuparse de él. El enlace se implementa llamando a los métodos predefinidos de la clase LearningModelBinding
. En nuestro caso, usa el método Bind
para enlazar un valor al tipo de característica con nombre.
Evaluación del modelo:
Después de crear una sesión para enlazar el modelo y los valores enlazados a las entradas y salidas de un modelo, puede evaluar las entradas del modelo y obtener sus predicciones. Para ejecutar el modelo, debe llamar a cualquiera de los métodos de evaluación predefinidos en LearningModelSession. En nuestro caso, usaremos el método EvaluateAsync
.
De forma similar a CreateFromStreamAsync
, el generador de código WinML también genera automáticamente el método EvaluateAsync
, por lo que no es necesario implementar este método. Puede revisar este método en el archivo ImageClassifier.cs
.
El método EvaluateAsync
evaluará de forma asincrónica el modelo de Machine Learning con los valores de característica ya enlazados en los enlaces. Creará una sesión con LearningModelSession
, enlazará la entrada y la salida con LearningModelBinding
, ejecutará la evaluación del modelo y obtendrá las características de salida del modelo mediante la clase LearningModelEvaluationResult
.
Nota:
Para información sobre otros métodos de evaluación para ejecutar el modelo, compruebe qué métodos se pueden implementar en LearningModelSession revisando la documentación de la clase LearningModelSession.
- Agregue el método siguiente al archivo de código
MainPage.xaml.cs
dentro de la clase MainPage para crear una sesión, y enlazar y evaluar el modelo.
// A method to evaluate the model
private async Task evaluate()
{
results = await modelGen.EvaluateAsync(image);
}
Extracción y visualización de los resultados
Ahora deberá extraer la salida del modelo y mostrar el resultado correcto, lo que hará mediante la implementación de los métodos extractResult
y displayResult
. Deberá encontrar la probabilidad mayor para devolver la etiqueta correcta.
- Agregue el método
extractResult
al archivo de códigoMainPage.xaml.cs
dentro de la claseMainPage
.
// 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();
}
- Agregue el método
displayResult
al archivo de códigoMainPage.xaml.cs
dentro de la claseMainPage
.
private async Task displayResult()
{
displayOutput.Text = label;
}
Ya está Ha creado correctamente la aplicación de aprendizaje automático de Windows con una GUI básica para probar nuestro modelo de clasificación. El siguiente paso consiste en iniciar la aplicación y ejecutarla localmente en el dispositivo de Windows.
Iniciar la aplicación
Una vez completada la interfaz de aplicación, agregado el modelo y generado el código de Windows ML, puede probar la aplicación.
Habilite el modo de desarrollador y pruebe la aplicación desde Visual Studio. Asegúrese de que los menús desplegables de la barra de herramientas superior están establecidos en Debug
. Cambie la plataforma de soluciones 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.
Nuestro modelo se entrenó para clasificar las siguientes imágenes: aeronave, automóvil, ave, gato, ciervo, perro, rana, caballo, embarcación, camión. Para probar nuestra aplicación, usará la imagen del automóvil de Lego creado para este proyecto. Veamos cómo clasifica la aplicación el contenido de la imagen.
Guarde esta imagen en el dispositivo local para probar la aplicación. Cambie el formato de imagen a
.jpg
si es necesario. También puede agregar cualquier otra imagen relevante desde el dispositivo local en un formato .jpg o .png.Para ejecutar el proyecto, seleccione el botón
Start Debugging
de la barra de herramientas o presioneF5
.Cuando se inicie la aplicación, presione Pick Image (Seleccionar imagen) y seleccione la imagen del dispositivo local.
El resultado aparecerá en la pantalla de inmediato. Como puede ver, nuestra aplicación de Windows ML ha clasificado correctamente la imagen como un auto.
Resumen
Acaba de crear la primera aplicación de Windows Machine Learning, desde la creación del modelo hasta la ejecución correcta.
Recursos adicionales
Para más información sobre los temas mencionados en este tutorial, visite estos recursos:
- Herramientas de Windows ML: obtenga más herramientas, como el panel de Windows ML, WinMLRunner y el generador de código de Windows ML mglen.
- Modelo de ONNX: más información sobre el formato ONNX.
- Rendimiento y memoria de Windows ML: más información sobre cómo administrar el rendimiento de las aplicaciones con Windows ML.
- Referencia de API de Windows Machine Learning: más información sobre tres áreas de la API de Windows ML.