Eseguire un modello con l'API runtime di Machine Learning Windows

Importante

Le API Windows Ml Runtime sono attualmente sperimentali e non sono supportate per l'uso in ambienti di produzione. Le app che provano queste API non devono essere pubblicate in Microsoft Store.

Questa esercitazione illustra la sequenza di API runtime standard senza prescrivere come l'app acquisisce o impacchetta il modello. Il modello può trovarsi ovunque l'app possa leggere; passare un percorso assoluto o relativo a LoadModelFromFile.

L'helper seguente presuppone intenzionalmente un modello con un input FLOAT32 con forma fissa. In questo modo il codice di allocazione tensor è incentrato sull'API di runtime e non è una limitazione del prodotto. Se la tua app usa dimensioni libere, associa dimensioni tensor concrete per ogni esecuzione; se usa altri tipi di dati, crea l'archiviazione di input corrispondente.

Prima di iniziare

Completa Configurare un progetto C++ per l'API Windows ML Runtime. È anche necessario un modello ONNX di cui comprendi il contratto di input.

Aggiungere un helper che esegue il modello

Aggiungere queste intestazioni e l'helper RunModel al progetto:

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

Passare la destinazione di esecuzione sia a AddModelStage sia alla factory tensor per mantenere la preparazione del modello e il posizionamento del tensore sulla stessa destinazione.

Lock stabilisce una finestra di accesso alla CPU per il tensore di output. Esegue il mapping o la sincronizzazione necessari e il puntatore restituito da GetData rimane valido solo mentre outputLock è in vita.

Chiama l'helper

Chiamare RunModel con il percorso del modello. Ad esempio, il seguente wmain accetta il percorso dalla riga di comando:

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

Compilare ed eseguire l'app:

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

Sostituire il percorso con il tuo modello. Interpreta il tensore restituito in base allo schema di output del modello; le etichette di classificazione, il campionamento dei token, la pre-elaborazione delle immagini e altri comportamenti specifici del modello appartengono all'app.

Passaggi successivi