Important
Windows ML 執行時 API 目前仍處於實驗階段,尚未支援於生產環境中使用。 嘗試這些 API 的應用程式不應該發佈至 Microsoft 市集。
這個教學展示了標準的執行時 API 序列,但沒有規定你的應用程式如何取得或打包其模型。 模型可以位於你的應用程式能讀取的任何地方;將絕對或相對路徑傳遞到 LoadModelFromFile。
以下的輔助工具刻意假設模型只有一個固定形狀 FLOAT32 輸入。 這樣張量配置程式碼就能聚焦於 Runtime API,這不是產品上的限制。 如果你的應用程式使用自由維度,請為每次執行綁定具體的張量維度;如果使用其他資料型態,則建立相符的輸入儲存空間。
在你開始之前
完成設定一個 Windows ML 執行時 API 的 C++ 專案。 你還需要一個你能理解其輸入規格的 ONNX 模型。
新增模型執行輔助工具
把這些標頭和 RunModel 助手加入你的專案:
// 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';
}
將執行目標傳遞給兩者 AddModelStage 及張量工廠,以保持模型準備與張量放置在同一目標上。
Lock 建立輸出張量的 CPU 存取視窗。 它執行任何必要的映射或同步,且 GetData 傳回的指標僅在 outputLock 存活時有效。
呼叫幫手
用模型路徑呼叫RunModel。 例如,以下 wmain 接受命令列上的路徑參數:
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;
}
建立並執行應用程式:
.\x64\Release\RuntimeApp.exe C:\models\model.onnx
以你的模型取代該路徑。 根據該模型的輸出結構描述來解釋回傳的張量;分類標籤、權杖取樣、影像預處理及其他模型專屬行為都應該包含在你的應用程式中。