Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
API машинного обучения Windows можно использовать для легкого взаимодействия с моделями машинного обучения в классических приложениях C++ (Win32). Используя три этапа загрузки, привязки и оценки, приложение может воспользоваться преимуществами машинного обучения.
Мы создадим несколько упрощенную версию примера обнаружения объектов SqueezeNet, которая доступна на сайте GitHub. Вы можете скачать полный пример, если вы хотите увидеть, как это будет по завершении.
Для доступа к API WinML мы будем использовать C++/WinRT. Дополнительные сведения см. в статье C++/WinRT .
В этом руководстве описано, как:
- Загрузка модели машинного обучения
- Загрузка изображения в виде кадра видео
- Привязка входных и выходных данных модели
- Оценка модели и печать значимых результатов
Предпосылки
- Visual Studio 2019 (или Visual Studio 2017, версия 15.7.4 или более поздней версии)
- Windows 10 версии 1809 или более поздней
- Пакет SDK для Windows, сборка 17763 или более поздняя версия
- Расширение Visual Studio для C++/WinRT
- В Visual Studio выберите "Расширения инструментов > " и "Обновления".
- Выберите "Online " в левой области и найдите "WinRT" с помощью поля поиска справа.
- Выберите C++/WinRT, нажмите кнопку "Скачать" и закройте Visual Studio.
- Следуйте инструкциям по установке, а затем снова откройте Visual Studio.
- Репозиторий Windows-Machine-Learning Github (его можно скачать в виде ZIP-файла или клонировать на компьютер)
Создание проекта
Сначала мы создадим проект в Visual Studio:
- Выберите файл > нового проекта, чтобы открыть окно "Новый > проект".
- В левой области выберите Установленный Visual C++ для рабочего стола Windows>>, и в середине выберите консольное приложение Windows (C++/WinRT).
- Присвойте проекту имя и расположение, а затем нажмите кнопку "ОК".
- В окне "Новый проект универсальной платформы Windows " задайте целевые и минимальные версии для сборки 17763 или более поздней версии и нажмите кнопку "ОК".
- Убедитесь, что раскрывающееся меню на верхней панели инструментов задано значение Debug и x64 или x86 в зависимости от архитектуры компьютера.
- Нажмите клавиши CTRL+F5 , чтобы запустить программу без отладки. Терминал должен открыться с текстом Hello world. Нажмите любую клавишу, чтобы закрыть ее.
Загрузка модели
Затем мы загрузим модель ONNX в нашу программу с помощью LearningModel.LoadFromFilePath:
В pch.h (в папке "Файлы заголовков ") добавьте следующие
include
инструкции (они предоставляют нам доступ ко всем api, которые нам потребуются):#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>
В main.cpp (в папке исходных файлов ) добавьте следующие
using
инструкции: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;
Добавьте следующие объявления переменных после
using
инструкций.// 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;
Добавьте следующие объявления пересылки после глобальных переменных:
// Forward declarations void LoadModel(); VideoFrame LoadImageFile(hstring filePath); void BindModel(); void EvaluateModel(); void PrintResults(IVectorView<float> results); void LoadLabels();
В main.cpp удалите код Hello world (все в
main
функции послеinit_apartment
).Найдите файл SqueezeNet.onnx в локальном клоне репозитория Windows-Machine-Learning . Он должен находиться в \Windows-Machine-Learning\SharedContent\models.
Скопируйте путь к файлу и назначьте его переменной
modelPath
, которую мы определили вверху. Не забудьте добавить к строке префиксL
, чтобы она стала строкой символов широкого формата и правильно работала сhstring
, а также экранировать все обратные косые черты (\
) добавлением дополнительной обратной косой черты. Рассмотрим пример.hstring modelPath = L"C:\\Repos\\Windows-Machine-Learning\\SharedContent\\models\\SqueezeNet.onnx";
Во-первых, мы реализуем
LoadModel
метод. Добавьте следующий метод послеmain
метода. Этот метод загружает модель и выводит, сколько времени потребовалось.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); }
Наконец, вызовите этот метод из
main
метода:LoadModel();
Запустите программу без отладки. Вы увидите, что модель успешно загружается!
Загрузка образа
Затем мы загрузим файл образа в нашу программу:
Добавьте следующий метод. Этот метод загружает изображение из заданного пути и создает видеофрейм из него:
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; }
Добавьте вызов к этому методу в методе
main
:imageFrame = LoadImageFile(imagePath);
Найдите папку мультимедиа в локальном клоне репозитория Windows-Machine-Learning . Он должен находиться в \Windows-Machine-Learning\SharedContent\media.
Выберите одно из изображений в этой папке и назначьте путь к файлу переменной
imagePath
, которую мы определили в верхней части. Не забудьте добавить префиксL
, чтобы сделать его строкой широких символов, и экранировать все обратные косые черты с помощью второй обратной косой черты. Рассмотрим пример.hstring imagePath = L"C:\\Repos\\Windows-Machine-Learning\\SharedContent\\media\\kitten_224.png";
Запустите программу без отладки. Изображение должно быть загружено успешно!
Привязка входных и выходных данных
Затем мы создадим сеанс на основе модели и привязываем входные и выходные данные сеанса с помощью LearningModelBinding.Bind. Дополнительные сведения о привязке см. в разделе "Привязка модели".
Реализуйте метод
BindModel
. Это создает сеанс на основе модели и устройства, а также связь, основанную на этом сеансе. Затем мы привязываем входные и выходные данные к переменным, созданным с помощью их имен. Мы знаем заранее, что входная функция называется "data_0", а выходной компонент называется "softmaxout_1". Эти свойства можно просмотреть для любой модели, открыв их в Netron, инструмент визуализации онлайн-модели.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); }
Добавьте вызов
BindModel
изmain
метода:BindModel();
Запустите программу без отладки. Входные и выходные данные модели должны быть привязаны успешно. Мы почти там!
Оценка модели
Теперь мы на последнем шаге на схеме в начале этого руководства, оценка. Мы рассмотрим модель с помощью LearningModelSession.Evaluate:
Реализуйте метод
EvaluateModel
. Этот метод принимает сеанс и оценивает его с помощью нашей привязки и идентификатора корреляции. Идентификатор корреляции — это то, что мы могли бы использовать позже для сопоставления определенного вызова оценки с выходными результатами. Опять же, мы знаем заранее, что имя выходных данных — "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); }
Теперь давайте реализуем
PrintResults
. Этот метод получает три лучших вероятности для того, какой объект может находиться на изображении, и выводит их: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]); } }
Нам также нужно реализовать
LoadLabels
. Этот метод открывает файл меток, содержащий все различные объекты, которые модель может распознать, и анализирует его: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; } }
Найдите файлLabels.txt в локальном клоне репозитория Windows-Machine-Learning . Он должен находиться в \Windows-Machine-Learning\Samples\SqueezeNetObjectDetection\Desktop\cpp.
Назначьте этот путь к файлу переменной
labelsFilePath
, которую мы определили в начале. Не забудьте избежать любых обратных косых черт с другой обратной косой чертой. Рассмотрим пример.string labelsFilePath = "C:\\Repos\\Windows-Machine-Learning\\Samples\\SqueezeNetObjectDetection\\Desktop\\cpp\\Labels.txt";
Добавьте вызов
EvaluateModel
вmain
метод:EvaluateModel();
Запустите программу без отладки. Теперь он должен правильно распознавать, что находится на изображении! Ниже приведен пример выходных данных:
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
Дальнейшие шаги
Ура, у вас получилось настроить обнаружение объектов в настольном приложении на C++! Затем можно попробовать использовать аргументы командной строки для ввода файлов модели и изображений, а не жесткого кода, аналогично тому, что делает пример на GitHub. Вы также можете попробовать запустить оценку на другом устройстве, например GPU, чтобы узнать, как производительность отличается.
Поэкспериментируйте с другими примерами на сайте GitHub и расширяйте их, как вам нравится!
См. также
Замечание
Используйте следующие ресурсы, чтобы получить помощь по Windows ML.
- Чтобы задать или ответить на технические вопросы о Windows ML, используйте тег windows-machine-learning в Stack Overflow.
- Чтобы сообщить об ошибке, отправьте сообщение о проблеме на сайте GitHub.