Een model uitvoeren met de Windows ML Runtime-API

Important

De Windows ML Runtime-API's zijn momenteel experimenteel en worden niet ondersteund voor gebruik in productieomgevingen. Apps die deze API's proberen, mogen niet worden gepubliceerd naar de Microsoft Store.

In deze zelfstudie wordt de standaard-runtime-API-reeks weergegeven zonder voor te schrijven hoe uw app het model verkrijgt of verpakt. Het model kan overal zijn waar uw app kan lezen; geef een absoluut of relatief pad door aan LoadModelFromFile.

In de onderstaande helper wordt bewust uitgegaan van een model met één invoer met vaste vorm FLOAT32. Hierdoor blijft de tensor-toewijzingscode gericht op de Runtime-API en is dit geen productbeperking. Als uw app vrije afmetingen gebruikt, bindt u concrete tensordimensies voor elke uitvoering; als er andere gegevenstypen worden gebruikt, maakt u overeenkomende invoeropslag.

Voordat u begint

Voltooi het instellen van een C++-project voor de Windows ML Runtime-API. U hebt ook een ONNX-model nodig waarvan u het invoercontract begrijpt.

Een helper voor het uitvoeren van een model toevoegen

Voeg deze headers en de RunModel helper toe aan uw project:

// 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';
}

Geef het uitvoeringsdoel door aan zowel AddModelStage als de tensor factory om modelvoorbereiding en tensorplaatsing op hetzelfde doel te houden.

Lock hiermee wordt een CPU-toegangsvenster voor de uitvoertensor tot stand gebracht. Er wordt een vereiste toewijzing of synchronisatie uitgevoerd en de aanwijzer die door GetData wordt geretourneerd, blijft alleen geldig zolang outputLock actief is.

Bel de helper

Roep RunModel aan met het modelpad. Het volgende wmain accepteert bijvoorbeeld het pad op de opdrachtregel:

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

Bouw en voer de app uit:

.\x64\Release\RuntimeApp.exe C:\models\model.onnx

Vervang het pad door het pad naar uw model. Interpreteer de geretourneerde tensor volgens het uitvoerschema van dat model; classificatielabels, tokensampling, voorverwerking van afbeeldingen en ander modelspecifiek gedrag horen bij uw app.

Volgende stappen