Nota
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
Las API de Windows ML se pueden aprovechar para interactuar fácilmente con modelos de aprendizaje automático en aplicaciones de escritorio de C++ (Win32). Con los tres pasos de carga, enlace y evaluación, la aplicación puede beneficiarse de la eficacia del aprendizaje automático.
Vamos a crear una versión algo simplificada del ejemplo de detección de objetos de SqueezeNet, que está disponible en GitHub. Puede descargar el ejemplo completo si desea ver cómo será cuando termine.
Usaremos C++/WinRT para acceder a las API de WinML. Consulta C++/WinRT para obtener más información.
En este tutorial, aprenderá a:
- Carga de un modelo de Machine Learning
- Cargar una imagen como videoframe
- Vincula las entradas y salidas del modelo
- Evaluar el modelo e imprimir resultados significativos
Prerrequisitos
- Visual Studio 2019 (o Visual Studio 2017, versión 15.7.4 o posterior)
- Windows 10, versión 1809 o posterior
- Windows SDK, compilación 17763 o posterior
- Extensión de Visual Studio para C++/WinRT
- En Visual Studio, seleccione Herramientas > Extensiones y actualizaciones.
- Seleccione En línea en el panel izquierdo y busque "WinRT" con el cuadro de búsqueda de la derecha.
- Seleccione C++/WinRT, haga clic en Descargar y cierre Visual Studio.
- Siga las instrucciones de instalación y vuelva a abrir Visual Studio.
- Windows:Machine-Learning repositorio de GitHub (puede descargarlo como un archivo ZIP o clonarlo en la máquina).
Creación del proyecto
En primer lugar, crearemos el proyecto en Visual Studio:
- Seleccione Archivo > Nuevo > Proyecto para abrir la ventana Nuevo proyecto.
- En el panel izquierdo, seleccione Instalado > Visual C++ > Escritorio de Windows, y en el panel central, seleccione Aplicación de consola de Windows (C++/WinRT).
- Asigne un nombre y una ubicación al proyecto y haga clic en Aceptar.
- En la ventana Nuevo proyecto de plataforma universal de Windows, establezca las versiones de destino y mínimas ambas en la compilación 17763 o posterior, y haga clic en Aceptar.
- Asegúrese de que los menús desplegables de la barra de herramientas superior están establecidos en Depurar y x64 o x86 en función de la arquitectura del equipo.
- Presione Ctrl+F5 para ejecutar el programa sin depurar. Un terminal debe abrirse con texto "Hola mundo". Presione cualquier tecla para cerrarla.
Carga del modelo
A continuación, cargaremos el modelo ONNX en nuestro programa mediante LearningModel.LoadFromFilePath:
En pch.h (en la carpeta Archivos de encabezado ), agregue las siguientes
include
instrucciones (que nos proporcionan acceso a todas las API que necesitaremos):#include <winrt/Windows.AI.MachineLearning.h> #include <winrt/Windows.Foundation.Collections.h> #include <winrt/Windows.Graphics.Imaging.h> #include <winrt/Windows.Media.h> #include <winrt/Windows.Storage.h> #include <string> #include <fstream> #include <Windows.h>
En main.cpp (en la carpeta Archivos de origen), agregue las siguientes
using
instrucciones:using namespace Windows::AI::MachineLearning; using namespace Windows::Foundation::Collections; using namespace Windows::Graphics::Imaging; using namespace Windows::Media; using namespace Windows::Storage; using namespace std;
Agregue las siguientes declaraciones de variable después de las
using
sentencias:// Global variables hstring modelPath; string deviceName = "default"; hstring imagePath; LearningModel model = nullptr; LearningModelDeviceKind deviceKind = LearningModelDeviceKind::Default; LearningModelSession session = nullptr; LearningModelBinding binding = nullptr; VideoFrame imageFrame = nullptr; string labelsFilePath; vector<string> labels;
Agregue las siguientes declaraciones de reenvío después de las variables globales:
// Forward declarations void LoadModel(); VideoFrame LoadImageFile(hstring filePath); void BindModel(); void EvaluateModel(); void PrintResults(IVectorView<float> results); void LoadLabels();
En main.cpp, quita el código "Hola mundo" (todo en la función
main
después deinit_apartment
).Busque el archivo SqueezeNet.onnx en el clon local del repositorio Windows-Machine-Learning . Debe encontrarse en \Windows-Machine-Learning\SharedContent\models.
Copie la ruta de acceso del archivo y asígnela a la
modelPath
variable donde la definimos en la parte superior. No olvides prefijar la cadena conL
para convertirla en una cadena de caracteres anchos, de forma que funcione correctamente conhstring
, y para que se escapen las barras diagonales inversas (\
) con una barra diagonal inversa adicional. Por ejemplo:hstring modelPath = L"C:\\Repos\\Windows-Machine-Learning\\SharedContent\\models\\SqueezeNet.onnx";
En primer lugar, implementaremos el
LoadModel
método . Agregue el siguiente método después delmain
método . Este método carga el modelo y genera cuánto tiempo tardó:void LoadModel() { // load the model printf("Loading modelfile '%ws' on the '%s' device\n", modelPath.c_str(), deviceName.c_str()); DWORD ticks = GetTickCount(); model = LearningModel::LoadFromFilePath(modelPath); ticks = GetTickCount() - ticks; printf("model file loaded in %d ticks\n", ticks); }
Por último, llame a este método desde el método
main
.LoadModel();
Ejecuta el programa sin depuración. Deberías ver que el modelo se carga correctamente.
Carga de la imagen
A continuación, cargaremos el archivo de imagen en nuestro programa:
Agregue el método siguiente. Este método cargará la imagen desde la ruta de acceso especificada y creará un VideoFrame a partir de ella:
VideoFrame LoadImageFile(hstring filePath) { printf("Loading the image...\n"); DWORD ticks = GetTickCount(); VideoFrame inputImage = nullptr; try { // open the file StorageFile file = StorageFile::GetFileFromPathAsync(filePath).get(); // get a stream on it auto stream = file.OpenAsync(FileAccessMode::Read).get(); // Create the decoder from the stream BitmapDecoder decoder = BitmapDecoder::CreateAsync(stream).get(); // get the bitmap SoftwareBitmap softwareBitmap = decoder.GetSoftwareBitmapAsync().get(); // load a videoframe from it inputImage = VideoFrame::CreateWithSoftwareBitmap(softwareBitmap); } catch (...) { printf("failed to load the image file, make sure you are using fully qualified paths\r\n"); exit(EXIT_FAILURE); } ticks = GetTickCount() - ticks; printf("image file loaded in %d ticks\n", ticks); // all done return inputImage; }
Agregue una llamada a este método en el
main
método :imageFrame = LoadImageFile(imagePath);
Busque la carpeta multimedia en el clon local del repositorio Windows-Machine-Learning . Debe encontrarse en \Windows-Machine-Learning\SharedContent\media.
Elija una de las imágenes de esa carpeta y asigne su ruta de acceso de archivo a la
imagePath
variable donde la definimos en la parte superior. No olvides prefijarla conL
para convertirla en una cadena de caracteres anchos, y para que se escapen las barras diagonales inversas con otra barra diagonal inversa. Por ejemplo:hstring imagePath = L"C:\\Repos\\Windows-Machine-Learning\\SharedContent\\media\\kitten_224.png";
Ejecuta el programa sin depuración. Debería ver que la imagen se cargó correctamente.
Vincula la entrada y la salida
A continuación, crearemos una sesión basada en el modelo y enlazaremos la entrada y salida de la sesión mediante LearningModelBinding.Bind. Para obtener más información sobre la vinculación, consulte Vincular un modelo.
Implemente el método
BindModel
. Esto crea una sesión basada en el modelo y el dispositivo, y un enlace basado en esa sesión. A continuación, enlazamos las entradas y salidas a las variables que hemos creado con sus nombres. Sabemos con antelación que la característica de entrada se denomina "data_0" y la característica de salida se denomina "softmaxout_1". Puede ver estas propiedades para cualquier modelo si las abre en Netron, una herramienta de visualización de modelos en línea.void BindModel() { printf("Binding the model...\n"); DWORD ticks = GetTickCount(); // now create a session and binding session = LearningModelSession{ model, LearningModelDevice(deviceKind) }; binding = LearningModelBinding{ session }; // bind the intput image binding.Bind(L"data_0", ImageFeatureValue::CreateFromVideoFrame(imageFrame)); // bind the output vector<int64_t> shape({ 1, 1000, 1, 1 }); binding.Bind(L"softmaxout_1", TensorFloat::Create(shape)); ticks = GetTickCount() - ticks; printf("Model bound in %d ticks\n", ticks); }
Agregue una llamada a
BindModel
desde elmain
método :BindModel();
Ejecuta el programa sin depuración. Las entradas y salidas del modelo deben enlazarse correctamente. ¡Casi estamos ahí!
Evaluación del modelo
Ahora estamos en el último paso del diagrama al principio de este tutorial, Evaluar. Evaluaremos el modelo mediante LearningModelSession.Evaluate:
Implemente el método
EvaluateModel
. Este método toma nuestra sesión y lo evalúa mediante nuestro enlace y un identificador de correlación. El identificador de correlación es algo que podríamos usar más adelante para hacer coincidir una llamada de evaluación determinada a los resultados de salida. De nuevo, sabemos con antelación que el nombre de la salida es "softmaxout_1".void EvaluateModel() { // now run the model printf("Running the model...\n"); DWORD ticks = GetTickCount(); auto results = session.Evaluate(binding, L"RunId"); ticks = GetTickCount() - ticks; printf("model run took %d ticks\n", ticks); // get the output auto resultTensor = results.Outputs().Lookup(L"softmaxout_1").as<TensorFloat>(); auto resultVector = resultTensor.GetAsVectorView(); PrintResults(resultVector); }
Ahora vamos a implementar
PrintResults
. Este método obtiene las tres probabilidades principales para qué objeto podría estar en la imagen y los imprime:void PrintResults(IVectorView<float> results) { // load the labels LoadLabels(); // Find the top 3 probabilities vector<float> topProbabilities(3); vector<int> topProbabilityLabelIndexes(3); // SqueezeNet returns a list of 1000 options, with probabilities for each, loop through all for (uint32_t i = 0; i < results.Size(); i++) { // is it one of the top 3? for (int j = 0; j < 3; j++) { if (results.GetAt(i) > topProbabilities[j]) { topProbabilityLabelIndexes[j] = i; topProbabilities[j] = results.GetAt(i); break; } } } // Display the result for (int i = 0; i < 3; i++) { printf("%s with confidence of %f\n", labels[topProbabilityLabelIndexes[i]].c_str(), topProbabilities[i]); } }
También es necesario implementar
LoadLabels
. Este método abre el archivo de etiquetas que contiene todos los distintos objetos que el modelo puede reconocer y lo analiza:void LoadLabels() { // Parse labels from labels file. We know the file's entries are already sorted in order. ifstream labelFile{ labelsFilePath, ifstream::in }; if (labelFile.fail()) { printf("failed to load the %s file. Make sure it exists in the same folder as the app\r\n", labelsFilePath.c_str()); exit(EXIT_FAILURE); } std::string s; while (std::getline(labelFile, s, ',')) { int labelValue = atoi(s.c_str()); if (labelValue >= labels.size()) { labels.resize(labelValue + 1); } std::getline(labelFile, s); labels[labelValue] = s; } }
Busque el archivo Labels.txt en el clon local del repositorio Windows-Machine-Learning . Debe estar en \Windows-Machine-Learning\Samples\SqueezeNetObjectDetection\Desktop\cpp.
Asigne esta ruta de acceso de archivo a la variable
labelsFilePath
, donde la definimos en la parte superior. Asegúrate de que se escapen las barras diagonales inversas con otra barra diagonal inversa. Por ejemplo:string labelsFilePath = "C:\\Repos\\Windows-Machine-Learning\\Samples\\SqueezeNetObjectDetection\\Desktop\\cpp\\Labels.txt";
Agregue una llamada a
EvaluateModel
en elmain
método :EvaluateModel();
Ejecuta el programa sin depuración. Ahora debería reconocer correctamente lo que hay en la imagen. Este es un ejemplo de lo que podría generar:
Loading modelfile 'C:\Repos\Windows-Machine-Learning\SharedContent\models\SqueezeNet.onnx' on the 'default' device model file loaded in 250 ticks Loading the image... image file loaded in 78 ticks Binding the model...Model bound in 15 ticks Running the model... model run took 16 ticks tabby, tabby cat with confidence of 0.931461 Egyptian cat with confidence of 0.065307 Persian cat with confidence of 0.000193
Pasos siguientes
¡Hurra, has conseguido que la detección de objetos funcione en una aplicación de escritorio en C++! A continuación, puede intentar usar argumentos de línea de comandos para introducir los archivos de imagen y modelo en lugar de codificarlos de forma difícil, de forma similar a lo que hace el ejemplo en GitHub. También puede intentar ejecutar la evaluación en un dispositivo diferente, como la GPU, para ver cómo difiere el rendimiento.
Juega con los otros ejemplos en GitHub y ampliarlos como quieras.
Consulte también
Nota:
Use los siguientes recursos para obtener ayuda con Windows ML:
- Para formular o responder preguntas técnicas sobre Windows ML, use la etiqueta windows-machine-learning en Stack Overflow.
- Para notificar un error, envíe un problema en nuestra GitHub.