onnxruntime
687 строк · 29.3 Кб
1// Copyright (c) Microsoft Corporation. All rights reserved.
2// Licensed under the MIT License.
3
4#include "testPch.h"
5
6#include "APITest.h"
7#include "LearningModelBindingAPITest.h"
8#include "SqueezeNetValidator.h"
9
10#include <sstream>
11
12using namespace winrt;
13using namespace winml;
14using namespace wfc;
15using namespace wgi;
16using namespace wm;
17using namespace ws;
18
19static void LearningModelBindingAPITestsClassSetup() {
20init_apartment();
21#ifdef BUILD_INBOX
22winrt_activation_handler = WINRT_RoGetActivationFactory;
23#endif
24}
25
26static void CpuSqueezeNet() {
27std::string cpuInstance("CPU");
28WINML_EXPECT_NO_THROW(WinML::Engine::Test::ModelValidator::SqueezeNet(
29cpuInstance,
30LearningModelDeviceKind::Cpu,
31/*dataTolerance*/ 0.00001f,
32false
33));
34}
35
36static void CpuSqueezeNetEmptyOutputs() {
37std::string cpuInstance("CPU");
38WINML_EXPECT_NO_THROW(WinML::Engine::Test::ModelValidator::SqueezeNet(
39cpuInstance,
40LearningModelDeviceKind::Cpu,
41/*dataTolerance*/ 0.00001f,
42false,
43OutputBindingStrategy::Empty
44););
45}
46
47static void CpuSqueezeNetUnboundOutputs() {
48std::string cpuInstance("CPU");
49WINML_EXPECT_NO_THROW(WinML::Engine::Test::ModelValidator::SqueezeNet(
50cpuInstance,
51LearningModelDeviceKind::Cpu,
52/*dataTolerance*/ 0.00001f,
53false,
54OutputBindingStrategy::Unbound
55););
56}
57
58static void CpuSqueezeNetBindInputTensorAsInspectable() {
59std::string cpuInstance("CPU");
60WINML_EXPECT_NO_THROW(WinML::Engine::Test::ModelValidator::SqueezeNet(
61cpuInstance,
62LearningModelDeviceKind::Cpu,
63/*dataTolerance*/ 0.00001f,
64false,
65OutputBindingStrategy::Bound /* empty outputs */,
66true /* bind inputs as inspectables */
67););
68}
69
70static void CastMapInt64() {
71WINML_EXPECT_NO_THROW(LearningModel::LoadFromFilePath(FileHelpers::GetModulePath() + L"castmap-int64.onnx"));
72// TODO: Check Descriptor
73}
74
75static void DictionaryVectorizerMapInt64() {
76LearningModel learningModel = nullptr;
77WINML_EXPECT_NO_THROW(APITest::LoadModel(L"dictvectorizer-int64.onnx", learningModel));
78
79auto inputDescriptor = learningModel.InputFeatures().First().Current();
80WINML_EXPECT_TRUE(inputDescriptor.Kind() == LearningModelFeatureKind::Map);
81auto mapDescriptor = inputDescriptor.as<MapFeatureDescriptor>();
82WINML_EXPECT_TRUE(mapDescriptor.KeyKind() == TensorKind::Int64);
83WINML_EXPECT_TRUE(mapDescriptor.ValueDescriptor().Kind() == LearningModelFeatureKind::Tensor);
84auto tensorDescriptor = mapDescriptor.ValueDescriptor().as<TensorFeatureDescriptor>();
85// empty size means tensor of scalar value
86WINML_EXPECT_TRUE(tensorDescriptor.Shape().Size() == 0);
87WINML_EXPECT_TRUE(tensorDescriptor.TensorKind() == TensorKind::Float);
88
89LearningModelSession modelSession(learningModel);
90LearningModelBinding binding(modelSession);
91std::unordered_map<int64_t, float> map;
92map[1] = 1.f;
93map[10] = 10.f;
94map[3] = 3.f;
95
96auto mapInputName = inputDescriptor.Name();
97
98// Bind as IMap
99auto abiMap = winrt::single_threaded_map(std::move(map));
100binding.Bind(mapInputName, abiMap);
101auto mapInputInspectable = abiMap.as<wf::IInspectable>();
102auto first = binding.First();
103WINML_EXPECT_TRUE(first.Current().Key() == mapInputName);
104WINML_EXPECT_TRUE(first.Current().Value() == mapInputInspectable);
105WINML_EXPECT_TRUE(binding.Lookup(mapInputName) == mapInputInspectable);
106
107// Bind as IMapView
108auto mapView = abiMap.GetView();
109binding.Bind(mapInputName, mapView);
110mapInputInspectable = mapView.as<wf::IInspectable>();
111first = binding.First();
112WINML_EXPECT_TRUE(first.Current().Key() == mapInputName);
113WINML_EXPECT_TRUE(first.Current().Value() == mapView);
114WINML_EXPECT_TRUE(binding.Lookup(mapInputName) == mapView);
115}
116
117static void DictionaryVectorizerMapString() {
118LearningModel learningModel = nullptr;
119WINML_EXPECT_NO_THROW(APITest::LoadModel(L"dictvectorizer-string.onnx", learningModel));
120
121auto inputDescriptor = learningModel.InputFeatures().First().Current();
122WINML_EXPECT_TRUE(inputDescriptor.Kind() == LearningModelFeatureKind::Map);
123
124auto mapDescriptor = inputDescriptor.as<MapFeatureDescriptor>();
125WINML_EXPECT_TRUE(mapDescriptor.KeyKind() == TensorKind::String);
126WINML_EXPECT_TRUE(mapDescriptor.ValueDescriptor().Kind() == LearningModelFeatureKind::Tensor);
127
128auto tensorDescriptor = mapDescriptor.ValueDescriptor().as<TensorFeatureDescriptor>();
129// empty size means tensor of scalar value
130WINML_EXPECT_TRUE(tensorDescriptor.Shape().Size() == 0);
131WINML_EXPECT_TRUE(tensorDescriptor.TensorKind() == TensorKind::Float);
132
133LearningModelSession modelSession(learningModel);
134LearningModelBinding binding(modelSession);
135std::unordered_map<winrt::hstring, float> map;
136map[L"1"] = 1.f;
137map[L"10"] = 10.f;
138map[L"2"] = 2.f;
139
140auto mapInputName = inputDescriptor.Name();
141auto abiMap = winrt::single_threaded_map(std::move(map));
142binding.Bind(mapInputName, abiMap);
143
144auto mapInputInspectable = abiMap.as<wf::IInspectable>();
145auto first = binding.First();
146WINML_EXPECT_TRUE(first.Current().Key() == mapInputName);
147WINML_EXPECT_TRUE(first.Current().Value() == mapInputInspectable);
148WINML_EXPECT_TRUE(binding.Lookup(mapInputName) == mapInputInspectable);
149
150modelSession.Evaluate(binding, L"");
151}
152
153static void RunZipMapInt64(winml::LearningModel model, OutputBindingStrategy bindingStrategy) {
154auto outputFeatures = model.OutputFeatures();
155auto outputDescriptor = outputFeatures.First().Current();
156WINML_EXPECT_TRUE(outputDescriptor.Kind() == LearningModelFeatureKind::Sequence);
157
158auto seqDescriptor = outputDescriptor.as<SequenceFeatureDescriptor>();
159auto mapDescriptor = seqDescriptor.ElementDescriptor().as<MapFeatureDescriptor>();
160WINML_EXPECT_TRUE(mapDescriptor.KeyKind() == TensorKind::Int64);
161
162WINML_EXPECT_TRUE(mapDescriptor.ValueDescriptor().Kind() == LearningModelFeatureKind::Tensor);
163auto tensorDescriptor = mapDescriptor.ValueDescriptor().as<TensorFeatureDescriptor>();
164WINML_EXPECT_TRUE(tensorDescriptor.TensorKind() == TensorKind::Float);
165
166LearningModelSession session(model);
167LearningModelBinding binding(session);
168
169std::vector<float> inputs = {0.5f, 0.25f, 0.125f};
170std::vector<int64_t> shape = {1, 3};
171
172// Bind inputs
173auto inputTensor = TensorFloat::CreateFromArray(shape, winrt::array_view<const float>(std::move(inputs)));
174binding.Bind(winrt::hstring(L"X"), inputTensor);
175
176typedef IMap<int64_t, float> ABIMap;
177typedef IVector<ABIMap> ABISequeneceOfMap;
178
179ABISequeneceOfMap abiOutput = nullptr;
180// Bind outputs
181if (bindingStrategy == OutputBindingStrategy::Bound) {
182abiOutput = winrt::single_threaded_vector<ABIMap>();
183binding.Bind(winrt::hstring(L"Y"), abiOutput);
184}
185
186// Evaluate
187auto result = session.Evaluate(binding, L"0").Outputs();
188
189if (bindingStrategy == OutputBindingStrategy::Bound) {
190// from output binding
191const auto& out1 = abiOutput.GetAt(0);
192const auto& out2 = result.Lookup(L"Y").as<IVectorView<ABIMap>>().GetAt(0);
193WINML_LOG_COMMENT((std::ostringstream() << "size: " << out1.Size()).str());
194// check outputs
195auto iter1 = out1.First();
196auto iter2 = out2.First();
197for (uint32_t i = 0, size = (uint32_t)inputs.size(); i < size; ++i) {
198WINML_EXPECT_TRUE(iter1.HasCurrent());
199WINML_EXPECT_TRUE(iter2.HasCurrent());
200const auto& pair1 = iter1.Current();
201const auto& pair2 = iter2.Current();
202WINML_LOG_COMMENT((std::ostringstream() << "key: " << pair1.Key() << ", value: " << pair2.Value()).str());
203WINML_EXPECT_TRUE(pair1.Key() == i && pair2.Key() == i);
204WINML_EXPECT_TRUE(pair1.Value() == inputs[i] && pair2.Value() == inputs[i]);
205iter1.MoveNext();
206iter2.MoveNext();
207}
208WINML_EXPECT_TRUE(!iter1.HasCurrent());
209WINML_EXPECT_TRUE(!iter2.HasCurrent());
210} else {
211abiOutput = result.Lookup(L"Y").as<ABISequeneceOfMap>();
212WINML_EXPECT_TRUE(abiOutput.Size() == 1);
213ABIMap map = abiOutput.GetAt(0);
214WINML_EXPECT_TRUE(map.Size() == 3);
215WINML_EXPECT_TRUE(map.Lookup(0) == 0.5);
216WINML_EXPECT_TRUE(map.Lookup(1) == .25);
217WINML_EXPECT_TRUE(map.Lookup(2) == .125);
218}
219}
220
221static void ZipMapInt64() {
222LearningModel learningModel = nullptr;
223WINML_EXPECT_NO_THROW(APITest::LoadModel(L"zipmap-int64.onnx", learningModel));
224RunZipMapInt64(learningModel, OutputBindingStrategy::Bound);
225}
226
227static void ZipMapInt64Unbound() {
228LearningModel learningModel = nullptr;
229WINML_EXPECT_NO_THROW(APITest::LoadModel(L"zipmap-int64.onnx", learningModel));
230RunZipMapInt64(learningModel, OutputBindingStrategy::Unbound);
231}
232
233static void ZipMapString() {
234// output constraint: "seq(map(string, float))" or "seq(map(int64, float))"
235LearningModel learningModel = nullptr;
236WINML_EXPECT_NO_THROW(APITest::LoadModel(L"zipmap-string.onnx", learningModel));
237auto outputs = learningModel.OutputFeatures();
238auto outputDescriptor = outputs.First().Current();
239WINML_EXPECT_TRUE(outputDescriptor.Kind() == LearningModelFeatureKind::Sequence);
240auto mapDescriptor = outputDescriptor.as<SequenceFeatureDescriptor>().ElementDescriptor().as<MapFeatureDescriptor>();
241WINML_EXPECT_TRUE(mapDescriptor.KeyKind() == TensorKind::String);
242WINML_EXPECT_TRUE(mapDescriptor.ValueDescriptor().Kind() == LearningModelFeatureKind::Tensor);
243auto tensorDescriptor = mapDescriptor.ValueDescriptor().as<TensorFeatureDescriptor>();
244WINML_EXPECT_TRUE(tensorDescriptor.TensorKind() == TensorKind::Float);
245
246LearningModelSession session(learningModel);
247LearningModelBinding binding(session);
248
249std::vector<float> inputs = {0.5f, 0.25f, 0.125f};
250std::vector<int64_t> shape = {1, 3};
251std::vector<winrt::hstring> labels = {L"cat", L"dog", L"lion"};
252std::map<winrt::hstring, float> mapData = {
253{ L"cat", 0.0f},
254{ L"dog", 0.0f},
255{L"lion", 0.0f}
256};
257typedef IMap<winrt::hstring, float> ABIMap;
258ABIMap abiMap = winrt::single_threaded_map<winrt::hstring, float>(std::move(mapData));
259std::vector<ABIMap> seqOutput = {abiMap};
260IVector<ABIMap> ABIOutput = winrt::single_threaded_vector<ABIMap>(std::move(seqOutput));
261
262TensorFloat inputTensor = TensorFloat::CreateFromArray(shape, winrt::array_view<const float>(std::move(inputs)));
263binding.Bind(winrt::hstring(L"X"), inputTensor);
264binding.Bind(winrt::hstring(L"Y"), ABIOutput);
265auto result = session.Evaluate(binding, L"0").Outputs();
266// from output binding
267const auto& out1 = ABIOutput.GetAt(0);
268const auto& out2 = result.Lookup(L"Y").as<IVectorView<ABIMap>>().GetAt(0);
269WINML_LOG_COMMENT((std::ostringstream() << "size: " << out1.Size()).str());
270// single key,value pair for each map
271auto iter1 = out1.First();
272auto iter2 = out2.First();
273for (uint32_t i = 0, size = (uint32_t)inputs.size(); i < size; ++i) {
274WINML_EXPECT_TRUE(iter2.HasCurrent());
275const auto& pair1 = iter1.Current();
276const auto& pair2 = iter2.Current();
277WINML_LOG_COMMENT((std::ostringstream() << "key: " << pair1.Key().c_str() << ", value " << pair2.Value()).str());
278WINML_EXPECT_TRUE(std::wstring(pair1.Key().c_str()).compare(labels[i]) == 0);
279WINML_EXPECT_TRUE(std::wstring(pair2.Key().c_str()).compare(labels[i]) == 0);
280WINML_EXPECT_TRUE(pair1.Value() == inputs[i] && pair2.Value() == inputs[i]);
281iter1.MoveNext();
282iter2.MoveNext();
283}
284WINML_EXPECT_TRUE(!iter1.HasCurrent());
285WINML_EXPECT_TRUE(!iter2.HasCurrent());
286}
287
288static void GpuSqueezeNet() {
289std::string gpuInstance("GPU");
290WINML_EXPECT_NO_THROW(WinML::Engine::Test::ModelValidator::SqueezeNet(
291gpuInstance,
292LearningModelDeviceKind::DirectX,
293/*dataTolerance*/ 0.00001f
294););
295}
296
297static void GpuSqueezeNetEmptyOutputs() {
298std::string gpuInstance("GPU");
299WINML_EXPECT_NO_THROW(WinML::Engine::Test::ModelValidator::SqueezeNet(
300gpuInstance,
301LearningModelDeviceKind::DirectX,
302/*dataTolerance*/ 0.00001f,
303false,
304OutputBindingStrategy::Empty
305););
306}
307
308static void GpuSqueezeNetUnboundOutputs() {
309std::string gpuInstance("GPU");
310WINML_EXPECT_NO_THROW(WinML::Engine::Test::ModelValidator::SqueezeNet(
311gpuInstance,
312LearningModelDeviceKind::DirectX,
313/*dataTolerance*/ 0.00001f,
314false,
315OutputBindingStrategy::Unbound
316););
317}
318
319// Validates that when the input image is the same as the model expects, the binding step is executed correctly.
320static void ImageBindingDimensions() {
321LearningModelBinding learningModelBinding = nullptr;
322LearningModel learningModel = nullptr;
323LearningModelSession learningModelSession = nullptr;
324LearningModelDevice leraningModelDevice = nullptr;
325std::wstring filePath = FileHelpers::GetModulePath() + L"model.onnx";
326// load a model with expected input size: 224 x 224
327WINML_EXPECT_NO_THROW(leraningModelDevice = LearningModelDevice(LearningModelDeviceKind::Default));
328WINML_EXPECT_NO_THROW(learningModel = LearningModel::LoadFromFilePath(filePath));
329WINML_EXPECT_TRUE(learningModel != nullptr);
330WINML_EXPECT_NO_THROW(learningModelSession = LearningModelSession(learningModel, leraningModelDevice));
331WINML_EXPECT_NO_THROW(learningModelBinding = LearningModelBinding(learningModelSession));
332
333// Create input images and execute bind
334// Test Case 1: both width and height are larger than model expects
335VideoFrame inputImage1(BitmapPixelFormat::Rgba8, 1000, 1000);
336ImageFeatureValue inputTensor = ImageFeatureValue::CreateFromVideoFrame(inputImage1);
337WINML_EXPECT_NO_THROW(learningModelBinding.Bind(L"data_0", inputTensor));
338
339// Test Case 2: only height is larger, while width is smaller
340VideoFrame inputImage2(BitmapPixelFormat::Rgba8, 20, 1000);
341inputTensor = ImageFeatureValue::CreateFromVideoFrame(inputImage2);
342WINML_EXPECT_NO_THROW(learningModelBinding.Bind(L"data_0", inputTensor));
343
344// Test Case 3: only width is larger, while height is smaller
345VideoFrame inputImage3(BitmapPixelFormat::Rgba8, 1000, 20);
346inputTensor = ImageFeatureValue::CreateFromVideoFrame(inputImage3);
347WINML_EXPECT_NO_THROW(learningModelBinding.Bind(L"data_0", inputTensor));
348
349// Test Case 4: both width and height are smaller than model expects
350VideoFrame inputImage4(BitmapPixelFormat::Rgba8, 20, 20);
351inputTensor = ImageFeatureValue::CreateFromVideoFrame(inputImage4);
352WINML_EXPECT_NO_THROW(learningModelBinding.Bind(L"data_0", inputTensor));
353}
354
355static void VerifyInvalidBindExceptions() {
356LearningModel learningModel = nullptr;
357WINML_EXPECT_NO_THROW(APITest::LoadModel(L"zipmap-int64.onnx", learningModel));
358
359LearningModelSession session(learningModel);
360LearningModelBinding binding(session);
361
362std::vector<float> inputs = {0.5f, 0.25f, 0.125f};
363std::vector<int64_t> shape = {1, 3};
364
365auto matchException = [](const winrt::hresult_error& e, HRESULT hr) -> bool { return e.code() == hr; };
366
367auto ensureWinmlSizeMismatch = std::bind(matchException, std::placeholders::_1, WINML_ERR_SIZE_MISMATCH);
368auto ensureWinmlInvalidBinding = std::bind(matchException, std::placeholders::_1, WINML_ERR_INVALID_BINDING);
369
370/*
371Verify tensor bindings throw correct bind exceptions
372*/
373
374// Bind invalid image as tensorfloat input
375auto image = FileHelpers::LoadImageFeatureValue(L"227x227.png");
376WINML_EXPECT_THROW_SPECIFIC(binding.Bind(L"X", image), winrt::hresult_error, ensureWinmlSizeMismatch);
377
378// Bind invalid map as tensorfloat input
379std::unordered_map<float, float> map;
380auto abiMap = winrt::single_threaded_map(std::move(map));
381WINML_EXPECT_THROW_SPECIFIC(binding.Bind(L"X", abiMap), winrt::hresult_error, ensureWinmlInvalidBinding);
382
383// Bind invalid sequence as tensorfloat input
384std::vector<uint32_t> sequence;
385auto abiSequence = winrt::single_threaded_vector(std::move(sequence));
386WINML_EXPECT_THROW_SPECIFIC(binding.Bind(L"X", abiSequence), winrt::hresult_error, ensureWinmlInvalidBinding);
387
388// Bind invalid tensor size as tensorfloat input
389auto tensorBoolean = TensorBoolean::Create();
390WINML_EXPECT_THROW_SPECIFIC(binding.Bind(L"X", tensorBoolean), winrt::hresult_error, ensureWinmlInvalidBinding);
391
392// Bind invalid tensor shape as tensorfloat input
393auto tensorInvalidShape = TensorFloat::Create(std::vector<int64_t>{2, 3, 4});
394WINML_EXPECT_THROW_SPECIFIC(binding.Bind(L"X", tensorInvalidShape), winrt::hresult_error, ensureWinmlInvalidBinding);
395
396/*
397Verify sequence bindings throw correct bind exceptions
398*/
399
400// Bind invalid image as sequence<map<int, float> output
401WINML_EXPECT_THROW_SPECIFIC(binding.Bind(L"Y", image), winrt::hresult_error, ensureWinmlInvalidBinding);
402
403// Bind invalid map as sequence<map<int, float> output
404WINML_EXPECT_THROW_SPECIFIC(binding.Bind(L"Y", abiMap), winrt::hresult_error, ensureWinmlInvalidBinding);
405
406// Bind invalid sequence<int> as sequence<map<int, float> output
407WINML_EXPECT_THROW_SPECIFIC(binding.Bind(L"Y", abiSequence), winrt::hresult_error, ensureWinmlInvalidBinding);
408
409// Bind invalid tensor as sequence<map<int, float> output
410WINML_EXPECT_THROW_SPECIFIC(binding.Bind(L"Y", tensorBoolean), winrt::hresult_error, ensureWinmlInvalidBinding);
411
412/*
413Verify map bindings throw correct bind exceptions
414*/
415WINML_EXPECT_NO_THROW(APITest::LoadModel(L"dictvectorizer-int64.onnx", learningModel));
416
417LearningModelSession mapSession(learningModel);
418LearningModelBinding mapBinding(mapSession);
419
420auto inputName = learningModel.InputFeatures().First().Current().Name();
421
422// Bind invalid image as image input
423auto smallImage = FileHelpers::LoadImageFeatureValue(L"100x100.png");
424WINML_EXPECT_THROW_SPECIFIC(mapBinding.Bind(inputName, smallImage), winrt::hresult_error, ensureWinmlInvalidBinding);
425
426// Bind invalid map as image input
427WINML_EXPECT_THROW_SPECIFIC(mapBinding.Bind(inputName, abiMap), winrt::hresult_error, ensureWinmlInvalidBinding);
428
429// Bind invalid sequence as image input
430WINML_EXPECT_THROW_SPECIFIC(mapBinding.Bind(inputName, abiSequence), winrt::hresult_error, ensureWinmlInvalidBinding);
431
432// Bind invalid tensor type as image input
433WINML_EXPECT_THROW_SPECIFIC(
434mapBinding.Bind(inputName, tensorBoolean), winrt::hresult_error, ensureWinmlInvalidBinding
435);
436}
437
438// Verify that it throws an error when binding an invalid name.
439static void BindInvalidInputName() {
440LearningModel learningModel = nullptr;
441LearningModelBinding learningModelBinding = nullptr;
442LearningModelDevice learningModelDevice = nullptr;
443LearningModelSession learningModelSession = nullptr;
444std::wstring modelPath = FileHelpers::GetModulePath() + L"Add_ImageNet1920.onnx";
445WINML_EXPECT_NO_THROW(learningModel = LearningModel::LoadFromFilePath(modelPath));
446WINML_EXPECT_TRUE(learningModel != nullptr);
447WINML_EXPECT_NO_THROW(learningModelDevice = LearningModelDevice(LearningModelDeviceKind::Default));
448WINML_EXPECT_NO_THROW(learningModelSession = LearningModelSession(learningModel, learningModelDevice));
449WINML_EXPECT_NO_THROW(learningModelBinding = LearningModelBinding(learningModelSession));
450
451VideoFrame iuputImage(BitmapPixelFormat::Rgba8, 1920, 1080);
452ImageFeatureValue inputTensor = ImageFeatureValue::CreateFromVideoFrame(iuputImage);
453
454auto first = learningModel.InputFeatures().First();
455std::wstring testInvalidName = L"0";
456
457// Verify that testInvalidName is not in model's InputFeatures
458while (first.HasCurrent()) {
459WINML_EXPECT_NOT_EQUAL(testInvalidName, first.Current().Name());
460first.MoveNext();
461}
462
463// Bind inputTensor to a valid input name
464WINML_EXPECT_NO_THROW(learningModelBinding.Bind(L"input_39:0", inputTensor));
465
466// Bind inputTensor to an invalid input name
467WINML_EXPECT_THROW_SPECIFIC(
468learningModelBinding.Bind(testInvalidName, inputTensor),
469winrt::hresult_error,
470[](const winrt::hresult_error& e) -> bool { return e.code() == WINML_ERR_INVALID_BINDING; }
471);
472}
473
474static void VerifyOutputAfterEvaluateAsyncCalledTwice() {
475LearningModel learningModel = nullptr;
476LearningModelBinding learningModelBinding = nullptr;
477LearningModelDevice learningModelDevice = nullptr;
478LearningModelSession learningModelSession = nullptr;
479std::wstring filePath = FileHelpers::GetModulePath() + L"relu.onnx";
480WINML_EXPECT_NO_THROW(learningModelDevice = LearningModelDevice(LearningModelDeviceKind::Default));
481WINML_EXPECT_NO_THROW(learningModel = LearningModel::LoadFromFilePath(filePath));
482WINML_EXPECT_TRUE(learningModel != nullptr);
483WINML_EXPECT_NO_THROW(learningModelSession = LearningModelSession(learningModel, learningModelDevice));
484WINML_EXPECT_NO_THROW(learningModelBinding = LearningModelBinding(learningModelSession));
485
486auto inputShape = std::vector<int64_t>{5};
487auto inputData1 = std::vector<float>{-50.f, -25.f, 0.f, 25.f, 50.f};
488auto inputValue1 =
489TensorFloat::CreateFromIterable(inputShape, single_threaded_vector<float>(std::move(inputData1)).GetView());
490
491auto inputData2 = std::vector<float>{50.f, 25.f, 0.f, -25.f, -50.f};
492auto inputValue2 =
493TensorFloat::CreateFromIterable(inputShape, single_threaded_vector<float>(std::move(inputData2)).GetView());
494
495WINML_EXPECT_NO_THROW(learningModelBinding.Bind(L"X", inputValue1));
496
497auto outputValue = TensorFloat::Create();
498WINML_EXPECT_NO_THROW(learningModelBinding.Bind(L"Y", outputValue));
499
500WINML_EXPECT_NO_THROW(learningModelSession.Evaluate(learningModelBinding, L""));
501
502auto buffer1 = outputValue.GetAsVectorView();
503WINML_EXPECT_TRUE(buffer1 != nullptr);
504
505// The second evaluation
506// If we don't bind output again, the output value will not change
507WINML_EXPECT_NO_THROW(learningModelBinding.Bind(L"X", inputValue2));
508WINML_EXPECT_NO_THROW(learningModelSession.Evaluate(learningModelBinding, L""));
509auto buffer2 = outputValue.GetAsVectorView();
510WINML_EXPECT_EQUAL(buffer1.Size(), buffer2.Size());
511bool isSame = true;
512for (uint32_t i = 0; i < buffer1.Size(); ++i) {
513if (buffer1.GetAt(i) != buffer2.GetAt(i)) {
514isSame = false;
515break;
516}
517}
518WINML_EXPECT_FALSE(isSame);
519}
520
521static VideoFrame CreateVideoFrame(const wchar_t* path) {
522auto imagefile = StorageFile::GetFileFromPathAsync(path).get();
523auto stream = imagefile.OpenAsync(FileAccessMode::Read).get();
524auto decoder = BitmapDecoder::CreateAsync(stream).get();
525auto softwareBitmap = decoder.GetSoftwareBitmapAsync().get();
526return VideoFrame::CreateWithSoftwareBitmap(softwareBitmap);
527}
528
529static void VerifyOutputAfterImageBindCalledTwice() {
530std::wstring fullModelPath = FileHelpers::GetModulePath() + L"model.onnx";
531std::wstring fullImagePath1 = FileHelpers::GetModulePath() + L"kitten_224.png";
532std::wstring fullImagePath2 = FileHelpers::GetModulePath() + L"fish.png";
533
534// winml model creation
535LearningModel model = nullptr;
536WINML_EXPECT_NO_THROW(model = LearningModel::LoadFromFilePath(fullModelPath));
537LearningModelSession modelSession = nullptr;
538WINML_EXPECT_NO_THROW(
539modelSession = LearningModelSession(model, LearningModelDevice(LearningModelDeviceKind::Default))
540);
541LearningModelBinding modelBinding(modelSession);
542
543// create the tensor for the actual output
544auto output = TensorFloat::Create();
545modelBinding.Bind(L"softmaxout_1", output);
546
547// Bind image 1 and evaluate
548auto frame = CreateVideoFrame(fullImagePath1.c_str());
549auto imageTensor = ImageFeatureValue::CreateFromVideoFrame(frame);
550WINML_EXPECT_NO_THROW(modelBinding.Bind(L"data_0", imageTensor));
551WINML_EXPECT_NO_THROW(modelSession.Evaluate(modelBinding, L""));
552
553// Store 1st result
554auto outputVectorView1 = output.GetAsVectorView();
555
556// Bind image 2 and evaluate
557// In this scenario, the backing videoframe is updated, and the imagefeaturevalue is rebound.
558// The expected result is that the videoframe will be re-tensorized at bind
559auto frame2 = CreateVideoFrame(fullImagePath2.c_str());
560frame2.CopyToAsync(frame).get();
561WINML_EXPECT_NO_THROW(modelBinding.Bind(L"data_0", imageTensor));
562WINML_EXPECT_NO_THROW(modelSession.Evaluate(modelBinding, L""));
563
564// Store 2nd result
565auto outputVectorView2 = output.GetAsVectorView();
566
567WINML_EXPECT_EQUAL(outputVectorView1.Size(), outputVectorView2.Size());
568bool isSame = true;
569for (uint32_t i = 0; i < outputVectorView1.Size(); ++i) {
570if (outputVectorView1.GetAt(i) != outputVectorView2.GetAt(i)) {
571isSame = false;
572break;
573}
574}
575WINML_EXPECT_FALSE(isSame);
576}
577
578static void SequenceLengthTensorFloat() {
579// Tests sequence of tensor float as an input
580LearningModel learningModel = nullptr;
581LearningModelBinding learningModelBinding = nullptr;
582LearningModelDevice learningModelDevice = nullptr;
583LearningModelSession learningModelSession = nullptr;
584std::wstring filePath = FileHelpers::GetModulePath() + L"sequence_length.onnx";
585WINML_EXPECT_NO_THROW(learningModelDevice = LearningModelDevice(LearningModelDeviceKind::Default));
586WINML_EXPECT_NO_THROW(learningModel = LearningModel::LoadFromFilePath(filePath));
587WINML_EXPECT_TRUE(learningModel != nullptr);
588WINML_EXPECT_NO_THROW(learningModelSession = LearningModelSession(learningModel, learningModelDevice));
589WINML_EXPECT_NO_THROW(learningModelBinding = LearningModelBinding(learningModelSession));
590
591auto input = winrt::single_threaded_vector<TensorFloat>();
592for (int i = 0; i < 3; i++) {
593std::vector<int64_t> shape = {5, 3 * i + 1};
594std::vector<float> data(
595static_cast<size_t>(std::accumulate(shape.begin(), shape.end(), static_cast<int64_t>(1), std::multiplies()))
596);
597input.Append(TensorFloat::CreateFromShapeArrayAndDataArray(shape, data));
598}
599
600WINML_EXPECT_NO_THROW(learningModelBinding.Bind(L"X", input));
601auto results = learningModelSession.Evaluate(learningModelBinding, L"");
602
603WINML_EXPECT_EQUAL(3, results.Outputs().Lookup(L"Y").as<TensorInt64Bit>().GetAsVectorView().GetAt(0));
604}
605
606static void SequenceConstructTensorString() {
607LearningModel learningModel = nullptr;
608LearningModelBinding learningModelBinding = nullptr;
609LearningModelDevice learningModelDevice = nullptr;
610LearningModelSession learningModelSession = nullptr;
611std::wstring filePath = FileHelpers::GetModulePath() + L"sequence_construct.onnx";
612WINML_EXPECT_NO_THROW(learningModelDevice = LearningModelDevice(LearningModelDeviceKind::Default));
613WINML_EXPECT_NO_THROW(learningModel = LearningModel::LoadFromFilePath(filePath));
614WINML_EXPECT_TRUE(learningModel != nullptr);
615WINML_EXPECT_NO_THROW(learningModelSession = LearningModelSession(learningModel, learningModelDevice));
616WINML_EXPECT_NO_THROW(learningModelBinding = LearningModelBinding(learningModelSession));
617
618std::vector<int64_t> shape1 = {2, 3};
619std::vector<int64_t> data1(
620static_cast<size_t>(std::accumulate(shape1.begin(), shape1.end(), static_cast<int64_t>(1), std::multiplies()))
621);
622auto input1 = TensorInt64Bit::CreateFromShapeArrayAndDataArray(shape1, data1);
623std::vector<int64_t> shape2 = {2, 3};
624std::vector<int64_t> data2(
625static_cast<size_t>(std::accumulate(shape2.begin(), shape2.end(), static_cast<int64_t>(1), std::multiplies()))
626);
627auto input2 = TensorInt64Bit::CreateFromShapeArrayAndDataArray(shape2, data2);
628
629WINML_EXPECT_NO_THROW(learningModelBinding.Bind(L"tensor1", input1));
630WINML_EXPECT_NO_THROW(learningModelBinding.Bind(L"tensor2", input2));
631auto results = learningModelSession.Evaluate(learningModelBinding, L"");
632
633auto output_sequence = results.Outputs().Lookup(L"output_sequence").as<wfc::IVectorView<TensorInt64Bit>>();
634WINML_EXPECT_EQUAL(static_cast<uint32_t>(2), output_sequence.Size());
635WINML_EXPECT_EQUAL(2, output_sequence.GetAt(0).Shape().GetAt(0));
636WINML_EXPECT_EQUAL(3, output_sequence.GetAt(0).Shape().GetAt(1));
637WINML_EXPECT_EQUAL(2, output_sequence.GetAt(1).Shape().GetAt(0));
638WINML_EXPECT_EQUAL(3, output_sequence.GetAt(1).Shape().GetAt(1));
639
640auto bound_output_sequence = winrt::single_threaded_vector<TensorInt64Bit>();
641WINML_EXPECT_NO_THROW(learningModelBinding.Bind(L"output_sequence", bound_output_sequence));
642WINML_EXPECT_NO_THROW(learningModelSession.Evaluate(learningModelBinding, L""));
643WINML_EXPECT_EQUAL(static_cast<uint32_t>(2), bound_output_sequence.Size());
644WINML_EXPECT_EQUAL(2, bound_output_sequence.GetAt(0).Shape().GetAt(0));
645WINML_EXPECT_EQUAL(3, bound_output_sequence.GetAt(0).Shape().GetAt(1));
646WINML_EXPECT_EQUAL(2, bound_output_sequence.GetAt(1).Shape().GetAt(0));
647WINML_EXPECT_EQUAL(3, bound_output_sequence.GetAt(1).Shape().GetAt(1));
648}
649
650const LearningModelBindingAPITestsApi& getapi() {
651static LearningModelBindingAPITestsApi api = {
652LearningModelBindingAPITestsClassSetup,
653CpuSqueezeNet,
654CpuSqueezeNetEmptyOutputs,
655CpuSqueezeNetUnboundOutputs,
656CpuSqueezeNetBindInputTensorAsInspectable,
657CastMapInt64,
658DictionaryVectorizerMapInt64,
659DictionaryVectorizerMapString,
660ZipMapInt64,
661ZipMapInt64Unbound,
662ZipMapString,
663GpuSqueezeNet,
664GpuSqueezeNetEmptyOutputs,
665GpuSqueezeNetUnboundOutputs,
666ImageBindingDimensions,
667VerifyInvalidBindExceptions,
668BindInvalidInputName,
669VerifyOutputAfterEvaluateAsyncCalledTwice,
670VerifyOutputAfterImageBindCalledTwice,
671SequenceLengthTensorFloat,
672SequenceConstructTensorString
673};
674
675if (SkipGpuTests()) {
676api.GpuSqueezeNet = SkipTest;
677api.GpuSqueezeNetEmptyOutputs = SkipTest;
678api.GpuSqueezeNetUnboundOutputs = SkipTest;
679}
680if (RuntimeParameterExists(L"noVideoFrameTests")) {
681api.ImageBindingDimensions = SkipTest;
682api.BindInvalidInputName = SkipTest;
683api.VerifyOutputAfterImageBindCalledTwice = SkipTest;
684api.VerifyInvalidBindExceptions = SkipTest;
685}
686return api;
687}
688