Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Important
Les API Windows ML Runtime sont actuellement expérimentales et ne sont pas prises en charge pour une utilisation dans les environnements de production. Les applications qui testent ces API ne doivent pas être publiées dans le Microsoft Store.
Ce tutoriel montre la séquence d’API Runtime standard sans prescrire la façon dont votre application acquiert ou empaquette son modèle. Le modèle peut être à n’importe quel emplacement auquel votre application peut accéder ; transmettez un chemin absolu ou relatif à LoadModelFromFile.
La fonction utilitaire ci-dessous suppose intentionnellement un modèle avec une entrée FLOAT32 à forme fixe. Cela maintient le code d’allocation de tenseurs axé sur l’API Runtime et n’est pas une limitation du produit. Si votre application utilise des dimensions libres, liez des dimensions de tenseur concrètes pour chaque exécution ; si elle utilise d’autres types de données, créez un stockage d’entrée correspondant.
Avant de commencer
Configurez un projet C++ pour l’API Windows ML Runtime. Vous avez également besoin d’un modèle ONNX dont vous comprenez la spécification d’entrée.
Ajouter un assistant d’exécution de modèle
Ajoutez ces en-têtes et l’utilitaire RunModel à votre projet :
// Copyright (C) Microsoft Corporation. All rights reserved.
#include <Windows.h>
#include <cstdint>
#include <iostream>
#include <string>
#include <vector>
#include <winrt/base.h>
#include <WinMLRuntime.h>
#pragma comment(lib, "windowsapp.lib")
void RunModel(std::wstring const& modelPath)
{
winrt::com_ptr<IWinMLRuntime> runtime;
winrt::check_hresult(WinMLCreateRuntime(
__uuidof(IWinMLRuntime),
runtime.put_void()));
winrt::com_ptr<IWinMLModel> model;
winrt::check_hresult(runtime->LoadModelFromFile(
modelPath.c_str(),
nullptr,
nullptr,
model.put()));
winrt::com_ptr<IWinMLExecutionTarget> target;
winrt::check_hresult(runtime->CreateCpuExecutionTarget(target.put()));
winrt::com_ptr<IWinMLPipelineBuilder> builder;
winrt::check_hresult(runtime->CreatePipelineBuilder(builder.put()));
winrt::com_ptr<IWinMLStage> stage;
winrt::check_hresult(builder->AddModelStage(
model.get(),
target.get(),
L"model",
stage.put()));
winrt::com_ptr<IWinMLPipeline> pipeline;
winrt::check_hresult(builder->Build(pipeline.put()));
winrt::com_ptr<IWinMLModelSchema> schema;
winrt::check_hresult(model->QueryInterface(
__uuidof(IWinMLModelSchema),
schema.put_void()));
WINML_TENSOR_SCHEMA_DESC inputSchema = {};
winrt::check_hresult(schema->GetInputTensorDesc(0, &inputSchema));
if (inputSchema.dataType != WINML_TENSOR_DATA_TYPE_FLOAT32 ||
(inputSchema.dimensionCount > 0 &&
inputSchema.dimensions == nullptr))
{
winrt::throw_hresult(E_INVALIDARG);
}
UINT64 elementCount = 1;
for (UINT32 i = 0; i < inputSchema.dimensionCount; ++i)
{
const UINT64 dimension = inputSchema.dimensions[i];
if (dimension == 0 ||
dimension == UINT64_MAX ||
elementCount > UINT64_MAX / dimension)
{
winrt::throw_hresult(E_INVALIDARG);
}
elementCount *= dimension;
}
if (elementCount > SIZE_MAX)
{
winrt::throw_hresult(E_BOUNDS);
}
std::vector<float> inputData(
static_cast<size_t>(elementCount),
0.5f);
WINML_TENSOR_DESC inputDesc = {};
inputDesc.dataType = inputSchema.dataType;
inputDesc.dimensionCount = inputSchema.dimensionCount;
inputDesc.dimensions = inputSchema.dimensions;
winrt::com_ptr<IWinMLRawTensorFactory> tensorFactory;
winrt::check_hresult(target->QueryInterface(
__uuidof(IWinMLRawTensorFactory),
tensorFactory.put_void()));
winrt::com_ptr<IWinMLTensor> inputTensor;
winrt::check_hresult(tensorFactory->CreateTensor(
&inputDesc,
inputData.data(),
inputData.size() * sizeof(float),
inputTensor.put()));
winrt::check_hresult(stage->BindInput(0, inputTensor.get()));
winrt::check_hresult(pipeline->Run());
winrt::com_ptr<IWinMLTensor> outputTensor;
winrt::check_hresult(stage->GetOutput(0, outputTensor.put()));
winrt::com_ptr<IWinMLTensorDataLock> outputLock;
winrt::check_hresult(outputTensor->Lock(
WINML_TENSOR_LOCK_MODE_READ,
WINML_TENSOR_LOCK_FLAG_NONE,
outputLock.put()));
BYTE* outputBytes = nullptr;
UINT64 outputSizeInBytes = 0;
winrt::check_hresult(outputLock->GetData(
&outputBytes,
&outputSizeInBytes));
(void)outputBytes;
std::wcout << L"Output bytes: " << outputSizeInBytes << L'\n';
}
Transmettez la cible d’exécution à AddModelStage et à la fabrique de tenseurs pour conserver la préparation du modèle et le placement des tenseurs sur la même cible.
Lock établit une fenêtre d’accès processeur pour le tenseur de sortie. Il effectue tout mappage ou synchronisation requis, et le pointeur retourné par GetData reste valide uniquement tant que outputLock est actif.
Appeler l’assistant
Appelez RunModel avec le chemin du modèle. Par exemple, l’élément suivant wmain accepte le chemin d’accès sur la ligne de commande :
int wmain(int argc, wchar_t** argv)
{
if (argc != 2)
{
std::wcerr << L"Usage: RuntimeApp.exe <model-path>\n";
return 1;
}
RunModel(argv[1]);
return 0;
}
Générez et exécutez l’application :
.\x64\Release\RuntimeApp.exe C:\models\model.onnx
Remplacez le chemin d’accès par votre modèle. Interpréter le tenseur retourné en fonction du schéma de sortie de ce modèle ; les étiquettes de classification, l’échantillonnage de jetons, le prétraitement d’image et d’autres comportements spécifiques au modèle appartiennent à votre application.