Menjalankan model dengan API Runtime ML Windows

Important

API Runtime Windows ML saat ini bersifat eksperimental dan tidak didukung untuk digunakan di lingkungan produksi. Aplikasi yang mencoba API ini tidak boleh diterbitkan ke Microsoft Store.

Tutorial ini menunjukkan urutan API Runtime standar tanpa menetapkan cara aplikasi Anda memperoleh atau mengemas modelnya. Model dapat berada di mana saja aplikasi Anda dapat membaca; berikan jalur absolut atau relatif ke LoadModelFromFile.

Fungsi bantuan di bawah ini sengaja mengasumsikan model dengan satu input FLOAT32 dengan bentuk tetap. Itu menjaga kode alokasi tensor berfokus pada API Runtime dan bukan batasan produk. Jika aplikasi Anda menggunakan dimensi bebas, ikat dimensi tensor tetap untuk setiap proses; jika menggunakan jenis data lain, buat penyimpanan input yang cocok.

Sebelum Anda mulai

Menyelesaikan penyiapan proyek C++ untuk Windows ML Runtime API. Anda juga memerlukan model ONNX yang kontrak inputnya Anda pahami.

Tambahkan helper untuk menjalankan model

Tambahkan header-header ini dan helper RunModel ke proyek Anda:

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

Teruskan target eksekusi ke AddModelStage dan pabrik tensor untuk menjaga persiapan model dan penempatan tensor pada target yang sama.

Lock menetapkan jendela akses CPU untuk tensor output. Ini melakukan pemetaan atau sinkronisasi yang diperlukan, dan penunjuk yang dikembalikan oleh GetData tetap valid hanya saat outputLock masih hidup.

Panggil helper

Jalankan RunModel dengan jalur model. Misalnya, berikut ini wmain menerima argumen path pada baris perintah:

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

Buat dan jalankan aplikasi:

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

Ganti path dengan model Anda. Interpretasikan tensor yang dikembalikan sesuai dengan skema output model tersebut; label klasifikasi, pengambilan sampel token, pra-pemrosesan gambar, dan perilaku khusus model lainnya termasuk dalam aplikasi Anda.

Langkah berikutnya