mirror of
https://github.com/saymrwulf/onnxruntime.git
synced 2026-07-30 20:18:08 +00:00
Brianma/fi (#2470)
* learning model doesn't need lotusEnvironment and CPU shouldn't include dmlEP headers * User/xianz/win ml telemetry (#2410) * add option to enable winml telemetry * add option to enable winml telemetry * clean logs while developping * clean the log of GUID * compile onnxruntime_common with winml telemetry * use option for use_telemetry * rename option winml_use_telemetry to onnxruntime_use_telemetry * little change * Add opset and IR check when loading model (#2413) * Add opset and IR check. * Add test case for future opsets. https://github.com/microsoft/onnxruntime/issues/2371 * WinML CI (#2412) * Pass flags to build/test WinML in CI * Add initial CMake config for unit tests in WinML * Set winml_unittests standard to C++17 * Add WinML API tests and port them to googletest * Install WinML test collateral * Add LearningModelSessionAPITests ported to googletest * Fix WinML test files encoding * Add GPU tests * Add parameterized test, skip GPU tests * Enable precompiled header * Remove unused code and collateral * Remove brand images * Add dllload.cpp * Remove images not used in API tests * Add LICENSE.md to image collaterals * Add models with licenses * Remove FNS Candy tests * Add API test models * Add ModelInSubdirectory * Install collaterals post-build with copy_if_different, split common lib * fix warnings * Link to gtest_main * fix bad merge
This commit is contained in:
parent
1bc2ca6183
commit
3787a071c5
69 changed files with 2833 additions and 13 deletions
|
|
@ -83,7 +83,9 @@ option(tensorflow_C_PACKAGE_PATH "Path to tensorflow C package installation dir"
|
|||
option(onnxruntime_ENABLE_LANGUAGE_INTEROP_OPS "Enable operator implemented in language other than cpp" OFF)
|
||||
option(onnxruntime_DEBUG_NODE_INPUTS_OUTPUTS "Dump node input shapes and output data to standard output when executing the model." OFF)
|
||||
option(onnxruntime_USE_DML "Build with DirectML support" OFF)
|
||||
option(onnxruntime_USE_WINML "Build with WinML support" OFF)
|
||||
option(onnxruntime_USE_ACL "Build with ACL support" OFF)
|
||||
option(onnxruntime_USE_TELEMETRY "Build with Telemetry" OFF)
|
||||
|
||||
set(protobuf_BUILD_TESTS OFF CACHE BOOL "Build protobuf tests" FORCE)
|
||||
#nsync tests failed on Mac Build
|
||||
|
|
@ -747,4 +749,4 @@ if (onnxruntime_BUILD_CSHARP)
|
|||
message(STATUS "CSharp Build is enabled")
|
||||
# set_property(GLOBAL PROPERTY VS_DOTNET_TARGET_FRAMEWORK_VERSION "netstandard2.0")
|
||||
include(onnxruntime_csharp.cmake)
|
||||
endif()
|
||||
endif()
|
||||
|
|
|
|||
|
|
@ -52,6 +52,10 @@ source_group(TREE ${REPO_ROOT} FILES ${onnxruntime_common_src})
|
|||
|
||||
add_library(onnxruntime_common ${onnxruntime_common_src})
|
||||
|
||||
if (onnxruntime_USE_TELEMETRY)
|
||||
set_target_properties(onnxruntime_common PROPERTIES COMPILE_FLAGS "/FI${ONNXRUNTIME_INCLUDE_DIR}/core/platform/windows/TraceLoggingConfigPrivate.h")
|
||||
endif()
|
||||
|
||||
if (onnxruntime_USE_MIMALLOC)
|
||||
if(onnxruntime_USE_CUDA OR onnxruntime_USE_OPENVINO)
|
||||
message(WARNING "Ignoring directive to use mimalloc on unimplemented targets")
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ convert_forward_slashes_to_back(${exclusions} CPPWINRT_COMPONENT_EXCLUSION_LIST)
|
|||
# For winrt idl files:
|
||||
# 1) the file name must match the casing of the file on disk.
|
||||
# 2) for winrt idls the casing must match the namespaces within exactly (Window.AI.MachineLearning).
|
||||
# target_cppwinrt will attempt to create a winmd with the name and same casing as the supplied
|
||||
# target_cppwinrt will attempt to create a winmd with the name and same casing as the supplied
|
||||
# idl file. If the name of the winmd file does not match the contained namespaces, cppwinrt.exe
|
||||
# will generate component template files with fully qualified names, which will not match the existing
|
||||
# generated component files.
|
||||
|
|
@ -90,6 +90,9 @@ add_library(winml_lib_telemetry STATIC
|
|||
# Compiler options
|
||||
target_compile_features(winml_lib_telemetry PRIVATE cxx_std_17)
|
||||
target_compile_options(winml_lib_telemetry PRIVATE /GR- /await /wd4238)
|
||||
if (onnxruntime_USE_TELEMETRY)
|
||||
set_target_properties(winml_lib_telemetry PROPERTIES COMPILE_FLAGS "/FI${ONNXRUNTIME_INCLUDE_DIR}/core/platform/windows/TraceLoggingConfigPrivate.h")
|
||||
endif()
|
||||
|
||||
# Compiler flags
|
||||
target_compile_definitions(winml_lib_telemetry PRIVATE PLATFORM_WINDOWS)
|
||||
|
|
@ -236,7 +239,7 @@ add_library(winml_lib_image STATIC
|
|||
)
|
||||
|
||||
# Compiler options
|
||||
target_compile_features(winml_lib_image PRIVATE cxx_std_17)
|
||||
target_compile_features(winml_lib_image PRIVATE cxx_std_17)
|
||||
target_compile_options(winml_lib_image PRIVATE /GR- /await /wd4238)
|
||||
|
||||
# Compiler flags
|
||||
|
|
@ -323,7 +326,7 @@ add_library(winml_lib_api STATIC
|
|||
)
|
||||
|
||||
# Compiler options
|
||||
target_compile_features(winml_lib_api PRIVATE cxx_std_17)
|
||||
target_compile_features(winml_lib_api PRIVATE cxx_std_17)
|
||||
target_compile_options(winml_lib_api PRIVATE /GR- /await /bigobj /wd4238)
|
||||
|
||||
# Compiler flags
|
||||
|
|
@ -400,7 +403,7 @@ add_library(winml_dll SHARED
|
|||
)
|
||||
|
||||
# Compiler options
|
||||
target_compile_features(winml_dll PRIVATE cxx_std_17)
|
||||
target_compile_features(winml_dll PRIVATE cxx_std_17)
|
||||
target_compile_options(winml_dll PRIVATE /GR- /await /bigobj /wd4238)
|
||||
|
||||
# Compiler definitions
|
||||
|
|
@ -493,4 +496,8 @@ target_link_libraries(winml_dll PRIVATE ${DBGHELP})
|
|||
# rather than waiting for it to fail and retry and resolve incorrectly.
|
||||
if("${CMAKE_BUILD_TYPE}" STREQUAL "Release")
|
||||
set_target_properties(winml_dll PROPERTIES VS_GLOBAL_PreferredToolArchitecture "x64")
|
||||
endif("${CMAKE_BUILD_TYPE}" STREQUAL "Release")
|
||||
endif("${CMAKE_BUILD_TYPE}" STREQUAL "Release")
|
||||
|
||||
if (onnxruntime_BUILD_WINML_TESTS)
|
||||
include(winml_unittests.cmake)
|
||||
endif()
|
||||
|
|
|
|||
90
cmake/winml_unittests.cmake
Normal file
90
cmake/winml_unittests.cmake
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
set(WINML_TEST_SRC_DIR ${REPO_ROOT}/winml/test)
|
||||
set(WINML_TEST_INC_DIR
|
||||
${REPO_ROOT}/winml/test/common
|
||||
${REPO_ROOT}/winml/lib/Api.Image/inc
|
||||
${REPO_ROOT}/winml/lib/Common/inc
|
||||
${REPO_ROOT}/onnxruntime
|
||||
${REPO_ROOT}/onnxruntime/core/providers/dml/DmlExecutionProvider/src/External/D3DX12
|
||||
${REPO_ROOT}/cmake/external/googletest/googletest/include
|
||||
${REPO_ROOT}/cmake/external/protobuf/src
|
||||
${REPO_ROOT}/cmake/external/wil/include
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
${CMAKE_CURRENT_BINARY_DIR}/winml_api
|
||||
${CMAKE_CURRENT_BINARY_DIR}/winml_api/comp_generated
|
||||
${CMAKE_CURRENT_BINARY_DIR}/winml/sdk/cppwinrt/include)
|
||||
|
||||
function(set_winml_target_properties target)
|
||||
set_target_properties(${target} PROPERTIES
|
||||
FOLDER "WinMLTest"
|
||||
CXX_STANDARD 17
|
||||
CXX_STANDARD_REQUIRED YES
|
||||
CXX_EXTENSIONS NO
|
||||
)
|
||||
target_include_directories(${target} PRIVATE ${WINML_TEST_INC_DIR})
|
||||
endfunction()
|
||||
|
||||
function(add_winml_test)
|
||||
# Add a test target and make it discoverable by CTest by calling add_test
|
||||
cmake_parse_arguments(_UT "DYN" "TARGET" "LIBS;SOURCES;DEPENDS" ${ARGN})
|
||||
if(_UT_LIBS)
|
||||
list(REMOVE_DUPLICATES _UT_LIBS)
|
||||
endif()
|
||||
list(REMOVE_DUPLICATES _UT_SOURCES)
|
||||
if (_UT_DEPENDS)
|
||||
list(REMOVE_DUPLICATES _UT_DEPENDS)
|
||||
endif()
|
||||
|
||||
add_executable(${_UT_TARGET} ${_UT_SOURCES})
|
||||
source_group(TREE ${WINML_TEST_SRC_DIR} FILES ${_UT_SOURCES})
|
||||
set_winml_target_properties(${_UT_TARGET})
|
||||
|
||||
if (_UT_DEPENDS)
|
||||
add_dependencies(${_UT_TARGET} ${_UT_DEPENDS})
|
||||
endif()
|
||||
target_link_libraries(${_UT_TARGET} PRIVATE ${_UT_LIBS} gtest_main windowsapp winml_lib_image ${onnxruntime_EXTERNAL_LIBRARIES})
|
||||
|
||||
add_test(NAME ${_UT_TARGET}
|
||||
COMMAND ${_UT_TARGET}
|
||||
WORKING_DIRECTORY $<TARGET_FILE_DIR:${_UT_TARGET}>
|
||||
)
|
||||
endfunction()
|
||||
|
||||
|
||||
file(GLOB winml_test_common_src CONFIGURE_DEPENDS "${WINML_TEST_SRC_DIR}/common/*.cpp")
|
||||
add_library(winml_test_common STATIC ${winml_test_common_src})
|
||||
set_winml_target_properties(winml_test_common)
|
||||
|
||||
file(GLOB winml_test_api_src CONFIGURE_DEPENDS "${WINML_TEST_SRC_DIR}/api/*.cpp")
|
||||
add_winml_test(
|
||||
TARGET winml_test_api
|
||||
SOURCES ${winml_test_api_src}
|
||||
LIBS winml_test_common
|
||||
)
|
||||
target_precompiled_header(winml_test_api testPch.h)
|
||||
|
||||
# During build time, copy any modified collaterals.
|
||||
# configure_file(source destination COPYONLY), which configures CMake to copy the file whenever source is modified,
|
||||
# can't be used here because we don't know the destination during configure time (in multi-configuration generators,
|
||||
# such as VS, one can switch between Debug/Release builds in the same build tree, and the destination depends on the
|
||||
# build mode).
|
||||
function(add_winml_collateral source)
|
||||
get_filename_component(source_directory ${source} DIRECTORY)
|
||||
file(GLOB_RECURSE collaterals RELATIVE ${source_directory} ${source})
|
||||
foreach(collateral ${collaterals})
|
||||
set(collateral_path ${source_directory}/${collateral})
|
||||
if(NOT IS_DIRECTORY ${collateral_path})
|
||||
add_custom_command(TARGET winml_test_common
|
||||
POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${collateral_path} "$<TARGET_FILE_DIR:winml_test_common>/${collateral}")
|
||||
endif()
|
||||
endforeach()
|
||||
endfunction()
|
||||
|
||||
add_winml_collateral("${WINML_TEST_SRC_DIR}/api/models/*.onnx")
|
||||
add_winml_collateral("${WINML_TEST_SRC_DIR}/collateral/images/*.png")
|
||||
add_winml_collateral("${WINML_TEST_SRC_DIR}/collateral/models/*.onnx")
|
||||
add_winml_collateral("${WINML_TEST_SRC_DIR}/common/testdata/squeezenet/*")
|
||||
add_winml_collateral("${WINML_TEST_SRC_DIR}/scenario/cppwinrt/*.onnx")
|
||||
|
|
@ -31,8 +31,11 @@ Environment:
|
|||
|
||||
// Configuration macro for use in TRACELOGGING_DEFINE_PROVIDER. The definition
|
||||
// in this file configures the provider as a normal (non-telemetry) provider.
|
||||
#ifndef TraceLoggingOptionMicrosoftTelemetry
|
||||
#define TraceLoggingOptionMicrosoftTelemetry() \
|
||||
TraceLoggingOptionGroup(0000000000, 00000, 00000, 0000, 0000, 0000, 0000, 0000, 000, 0000, 0000)
|
||||
// Empty definition for TraceLoggingOptionMicrosoftTelemetry
|
||||
#endif
|
||||
|
||||
// Configuration macro for use in TRACELOGGING_DEFINE_PROVIDER. The definition
|
||||
// in this file configures the provider as a normal (non-telemetry) provider.
|
||||
|
|
|
|||
|
|
@ -91,6 +91,10 @@ Model::Model(std::unique_ptr<ModelProto> model_proto, const IOnnxRuntimeOpSchema
|
|||
" specifies which version of the ONNX OperatorSet is being imported.");
|
||||
}
|
||||
|
||||
if (!model_proto->has_ir_version() || model_proto->ir_version() > ONNX_NAMESPACE::Version::IR_VERSION) {
|
||||
throw std::invalid_argument("Unknown model file format version.");
|
||||
}
|
||||
|
||||
model_proto_ = std::move(model_proto);
|
||||
for (auto& prop : model_proto_->metadata_props()) {
|
||||
model_metadata_[prop.key()] = prop.value();
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ using IOnnxRuntimeOpSchemaRegistryList = std::list<std::shared_ptr<IOnnxRuntimeO
|
|||
class Model {
|
||||
public:
|
||||
static constexpr Version kNoVersion = INT64_MAX;
|
||||
|
||||
|
||||
// Construct model from scratch.
|
||||
explicit Model(const std::string& graph_name,
|
||||
bool is_onnx_domain_only = false,
|
||||
|
|
|
|||
|
|
@ -194,6 +194,23 @@ DomainToVersionMap SchemaRegistryManager::GetLatestOpsetVersions(bool is_onnx_on
|
|||
return domain_version_map;
|
||||
}
|
||||
|
||||
static bool IsDomainVersionBeyondSupportedRange(
|
||||
const std::string& domain,
|
||||
const int op_set_version) {
|
||||
// check the ONNX schema registry
|
||||
auto& onnx_domain_version_map =
|
||||
ONNX_NAMESPACE::OpSchemaRegistry::DomainToVersionRange::Instance().Map();
|
||||
|
||||
auto it = onnx_domain_version_map.find(domain);
|
||||
if (it != onnx_domain_version_map.end() && op_set_version > it->second.second) {
|
||||
// The domain is beyond what is registered.
|
||||
return true;
|
||||
}
|
||||
|
||||
// Either all ONNX domains were within range, or the domains were not ONNX.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Return the schema with biggest version, which is not greater than specified
|
||||
// <op_set_version> in specified domain. The value of earliest_opset_where_unchanged
|
||||
// is also set to the earliest version preceding op_set_version where the operator
|
||||
|
|
@ -238,10 +255,14 @@ void SchemaRegistryManager::GetSchemaAndHistory(
|
|||
checked_registry_indices.push_back(index);
|
||||
}
|
||||
|
||||
// if not found in registered custom schema registry, search in ONNX schema registry
|
||||
*latest_schema = ONNX_NAMESPACE::OpSchemaRegistry::Schema(key, version, domain);
|
||||
if (*latest_schema != nullptr) {
|
||||
*earliest_opset_where_unchanged = (*latest_schema)->SinceVersion();
|
||||
// Reject versions greater than what is actually supported.
|
||||
*latest_schema = nullptr;
|
||||
if (!IsDomainVersionBeyondSupportedRange(domain, version)) {
|
||||
// if not found in registered custom schema registry, search in ONNX schema registry
|
||||
*latest_schema = ONNX_NAMESPACE::OpSchemaRegistry::Schema(key, version, domain);
|
||||
if (*latest_schema != nullptr) {
|
||||
*earliest_opset_where_unchanged = (*latest_schema)->SinceVersion();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -72,6 +72,14 @@ TEST(ONNXModelsTest, non_existing_model) {
|
|||
#endif
|
||||
}
|
||||
|
||||
TEST(ONNXModelsTest, future_opset) {
|
||||
// NOTE: this requires the current directory to be where onnxruntime_ir_UT.exe is located
|
||||
std::shared_ptr<Model> model;
|
||||
common::Status st = Model::Load("./testdata/add_opset_314159.onnx", model);
|
||||
ASSERT_FALSE(st.IsOK());
|
||||
ASSERT_EQ(st.Code(), common::INVALID_GRAPH);
|
||||
}
|
||||
|
||||
#ifdef ORT_RUN_EXTERNAL_ONNX_TESTS
|
||||
TEST(ONNXModelsTest1, bvlc_alexnet_1) {
|
||||
using ::google::protobuf::io::CodedInputStream;
|
||||
|
|
|
|||
BIN
onnxruntime/test/testdata/add_opset_314159.onnx
vendored
Normal file
BIN
onnxruntime/test/testdata/add_opset_314159.onnx
vendored
Normal file
Binary file not shown.
|
|
@ -154,6 +154,7 @@ Use the individual flags to only run the specified stages.
|
|||
parser.add_argument("--use_full_protobuf", action='store_true', help="Use the full protobuf library")
|
||||
parser.add_argument("--disable_contrib_ops", action='store_true', help="Disable contrib ops (reduces binary size)")
|
||||
parser.add_argument("--skip_onnx_tests", action='store_true', help="Explicitly disable all onnx related tests")
|
||||
parser.add_argument("--skip_winml_tests", action='store_true', help="Explicitly disable all WinML related tests")
|
||||
parser.add_argument("--enable_msvc_static_runtime", action='store_true', help="Enable static linking of MSVC runtimes.")
|
||||
parser.add_argument("--enable_language_interop_ops", action='store_true', help="Enable operator implemented in language other than cpp")
|
||||
parser.add_argument("--cmake_generator", choices=['Visual Studio 15 2017', 'Visual Studio 16 2019'],
|
||||
|
|
@ -161,6 +162,7 @@ Use the individual flags to only run the specified stages.
|
|||
parser.add_argument("--enable_multi_device_test", action='store_true', help="Test with multi-device. Mostly used for multi-device GPU")
|
||||
parser.add_argument("--use_dml", action='store_true', help="Build with DirectML.")
|
||||
parser.add_argument("--use_winml", action='store_true', help="Build with WinML.")
|
||||
parser.add_argument("--use_telemetry", action='store_true', help="Only official builds can set this flag to enable telemetry.")
|
||||
return parser.parse_args()
|
||||
|
||||
def resolve_executable_path(command_or_path):
|
||||
|
|
@ -326,8 +328,9 @@ def generate_build_tree(cmake_path, source_dir, build_dir, cuda_home, cudnn_home
|
|||
# for now, disable jemalloc if pybind is also enabled.
|
||||
cmake_args = [cmake_path, cmake_dir,
|
||||
"-Donnxruntime_RUN_ONNX_TESTS=" + ("ON" if args.enable_onnx_tests else "OFF"),
|
||||
"-Donnxruntime_BUILD_WINML_TESTS=" + ("OFF" if args.skip_winml_tests else "ON"),
|
||||
"-Donnxruntime_GENERATE_TEST_REPORTS=ON",
|
||||
"-Donnxruntime_DEV_MODE=" + ("OFF" if args.android else "ON"),
|
||||
"-Donnxruntime_DEV_MODE=" + ("OFF" if args.android or args.use_winml and not args.skip_winml_tests else "ON"),
|
||||
"-DPYTHON_EXECUTABLE=" + sys.executable,
|
||||
"-Donnxruntime_USE_CUDA=" + ("ON" if args.use_cuda else "OFF"),
|
||||
"-Donnxruntime_USE_NSYNC=" + ("OFF" if is_windows() or not args.use_nsync else "ON"),
|
||||
|
|
@ -374,6 +377,7 @@ def generate_build_tree(cmake_path, source_dir, build_dir, cuda_home, cudnn_home
|
|||
"-Donnxruntime_ENABLE_LANGUAGE_INTEROP_OPS=" + ("ON" if args.enable_language_interop_ops or (args.config != 'Debug' and bool(os.getenv('NIGHTLY_BUILD') == '1')) else "OFF"),
|
||||
"-Donnxruntime_USE_DML=" + ("ON" if args.use_dml else "OFF"),
|
||||
"-Donnxruntime_USE_WINML=" + ("ON" if args.use_winml else "OFF"),
|
||||
"-Donnxruntime_USE_TELEMETRY=" + ("ON" if args.use_telemetry else "OFF"),
|
||||
]
|
||||
if args.use_brainslice:
|
||||
bs_pkg_name = args.brain_slice_package_name.split('.', 1)
|
||||
|
|
|
|||
|
|
@ -32,6 +32,16 @@ jobs:
|
|||
CUDA_VERSION: ${{ parameters.CudaVersion }}
|
||||
|
||||
steps:
|
||||
- powershell: |
|
||||
if($env:WINMLTELEMETRYGUID)
|
||||
{
|
||||
$length = $env:WINMLTELEMETRYGUID.length
|
||||
$fileContent = "#define TraceLoggingOptionMicrosoftTelemetry() \
|
||||
TraceLoggingOptionGroup("+$env:WINMLTELEMETRYGUID.substring(1, $length-2)+")"
|
||||
New-Item -Path "$(Build.SourcesDirectory)\include\onnxruntime\core\platform\windows\TraceLoggingConfigPrivate.h" -ItemType "file" -Value "$fileContent" -Force
|
||||
}
|
||||
displayName: 'Create TraceLoggingConfigPrivate.h For WinML Telemetry'
|
||||
|
||||
- template: set-test-data-variables-step.yml
|
||||
- template: windows-build-tools-setup-steps.yml
|
||||
parameters:
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ jobs:
|
|||
AgentPool : 'Win-CPU'
|
||||
DoDebugBuild: 'true'
|
||||
DoCompliance: 'false'
|
||||
BuildCommand: '$(Build.SourcesDirectory)\tools\ci_build\build.py --build_dir $(Build.BinariesDirectory) --skip_submodule_sync --cmake_path $(Build.BinariesDirectory)\cmake\bin\cmake.exe --ctest_path $(Build.BinariesDirectory)\cmake\bin\ctest.exe --use_tvm --use_automl --enable_pybind --use_mkldnn --use_openmp --use_dml --build_shared_lib --build_csharp --enable_onnx_tests'
|
||||
BuildCommand: '$(Build.SourcesDirectory)\tools\ci_build\build.py --build_dir $(Build.BinariesDirectory) --skip_submodule_sync --cmake_path $(Build.BinariesDirectory)\cmake\bin\cmake.exe --ctest_path $(Build.BinariesDirectory)\cmake\bin\ctest.exe --use_tvm --use_automl --enable_pybind --use_mkldnn --use_openmp --use_dml --use_winml --build_shared_lib --build_csharp --enable_onnx_tests'
|
||||
JobName: 'Windows_CI_Dev'
|
||||
DoNugetPack: 'false'
|
||||
NuPackScript : ''
|
||||
|
|
|
|||
1
winml/test/.gitignore
vendored
Normal file
1
winml/test/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
!*.onnx
|
||||
41
winml/test/api/APITest.h
Normal file
41
winml/test/api/APITest.h
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
class APITest : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
void LoadModel(const std::wstring& modelPath)
|
||||
{
|
||||
std::wstring fullPath = FileHelpers::GetModulePath() + modelPath;
|
||||
m_model = winrt::Windows::AI::MachineLearning::LearningModel::LoadFromFilePath(fullPath);
|
||||
}
|
||||
|
||||
winrt::Windows::AI::MachineLearning::LearningModel m_model = nullptr;
|
||||
winrt::Windows::AI::MachineLearning::LearningModelDevice m_device = nullptr;
|
||||
winrt::Windows::AI::MachineLearning::LearningModelSession m_session = nullptr;
|
||||
|
||||
uint64_t GetAdapterIdQuadPart()
|
||||
{
|
||||
LARGE_INTEGER id;
|
||||
id.LowPart = m_device.AdapterId().LowPart;
|
||||
id.HighPart = m_device.AdapterId().HighPart;
|
||||
return id.QuadPart;
|
||||
};
|
||||
|
||||
_LUID GetAdapterIdAsLUID()
|
||||
{
|
||||
_LUID id;
|
||||
id.LowPart = m_device.AdapterId().LowPart;
|
||||
id.HighPart = m_device.AdapterId().HighPart;
|
||||
return id;
|
||||
}
|
||||
|
||||
bool m_runGPUTests = true;
|
||||
};
|
||||
261
winml/test/api/LearningModelAPITest.cpp
Normal file
261
winml/test/api/LearningModelAPITest.cpp
Normal file
|
|
@ -0,0 +1,261 @@
|
|||
#include "testPch.h"
|
||||
#include "APITest.h"
|
||||
|
||||
#include <winrt/Windows.Graphics.Imaging.h>
|
||||
#include <winrt/Windows.Media.h>
|
||||
#include <winrt/Windows.Storage.h>
|
||||
#include <winrt/Windows.Storage.Streams.h>
|
||||
|
||||
using namespace winrt;
|
||||
using namespace winrt::Windows::AI::MachineLearning;
|
||||
using namespace winrt::Windows::Foundation::Collections;
|
||||
using namespace winrt::Windows::Graphics::Imaging;
|
||||
using namespace winrt::Windows::Media;
|
||||
using namespace winrt::Windows::Storage;
|
||||
using namespace winrt::Windows::Storage::Streams;
|
||||
|
||||
class LearningModelAPITest : public APITest
|
||||
{
|
||||
protected:
|
||||
LearningModelAPITest() {
|
||||
init_apartment();
|
||||
m_model = nullptr;
|
||||
m_device = nullptr;
|
||||
m_session = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
class LearningModelAPITestGpu : public LearningModelAPITest
|
||||
{};
|
||||
|
||||
TEST_F(LearningModelAPITest, CreateModelFromFilePath)
|
||||
{
|
||||
EXPECT_NO_THROW(LoadModel(L"squeezenet_modifiedforruntimestests.onnx"));
|
||||
}
|
||||
|
||||
TEST_F(LearningModelAPITest, CreateModelFromIStorage)
|
||||
{
|
||||
std::wstring path = FileHelpers::GetModulePath() + L"squeezenet_modifiedforruntimestests.onnx";
|
||||
auto storageFile = winrt::Windows::Storage::StorageFile::GetFileFromPathAsync(path).get();
|
||||
EXPECT_NO_THROW(m_model = LearningModel::LoadFromStorageFileAsync(storageFile).get());
|
||||
EXPECT_TRUE(m_model != nullptr);
|
||||
|
||||
// check the author so we know the model was populated correctly.
|
||||
std::wstring author(m_model.Author());
|
||||
EXPECT_EQ(L"onnx-caffe2", author);
|
||||
}
|
||||
|
||||
TEST_F(LearningModelAPITest, CreateModelFromIStorageOutsideCwd)
|
||||
{
|
||||
std::wstring path = FileHelpers::GetModulePath() + L"ModelSubdirectory\\ModelInSubdirectory.onnx";
|
||||
auto storageFile = winrt::Windows::Storage::StorageFile::GetFileFromPathAsync(path).get();
|
||||
EXPECT_NO_THROW(m_model = LearningModel::LoadFromStorageFileAsync(storageFile).get());
|
||||
EXPECT_TRUE(m_model != nullptr);
|
||||
|
||||
// check the author so we know the model was populated correctly.
|
||||
std::wstring author(m_model.Author());
|
||||
EXPECT_EQ(L"onnx-caffe2", author);
|
||||
}
|
||||
|
||||
TEST_F(LearningModelAPITest, CreateModelFromIStream)
|
||||
{
|
||||
std::wstring path = FileHelpers::GetModulePath() + L"squeezenet_modifiedforruntimestests.onnx";
|
||||
auto storageFile = winrt::Windows::Storage::StorageFile::GetFileFromPathAsync(path).get();
|
||||
winrt::Windows::Storage::Streams::IRandomAccessStreamReference streamref;
|
||||
storageFile.as(streamref);
|
||||
|
||||
EXPECT_NO_THROW(m_model = LearningModel::LoadFromStreamAsync(streamref).get());
|
||||
EXPECT_TRUE(m_model != nullptr);
|
||||
|
||||
// check the author so we know the model was populated correctly.
|
||||
std::wstring author(m_model.Author());
|
||||
EXPECT_EQ(L"onnx-caffe2", author);
|
||||
}
|
||||
|
||||
TEST_F(LearningModelAPITest, GetAuthor)
|
||||
{
|
||||
EXPECT_NO_THROW(LoadModel(L"squeezenet_modifiedforruntimestests.onnx"));
|
||||
std::wstring author(m_model.Author());
|
||||
EXPECT_EQ(L"onnx-caffe2", author);
|
||||
}
|
||||
|
||||
TEST_F(LearningModelAPITest, GetName)
|
||||
{
|
||||
EXPECT_NO_THROW(LoadModel(L"squeezenet_modifiedforruntimestests.onnx"));
|
||||
std::wstring name(m_model.Name());
|
||||
EXPECT_EQ(L"squeezenet_old", name);
|
||||
}
|
||||
|
||||
TEST_F(LearningModelAPITest, GetDomain)
|
||||
{
|
||||
EXPECT_NO_THROW(LoadModel(L"squeezenet_modifiedforruntimestests.onnx"));
|
||||
std::wstring domain(m_model.Domain());
|
||||
EXPECT_EQ(L"test-domain", domain);
|
||||
}
|
||||
|
||||
TEST_F(LearningModelAPITest, GetDescription)
|
||||
{
|
||||
EXPECT_NO_THROW(LoadModel(L"squeezenet_modifiedforruntimestests.onnx"));
|
||||
std::wstring description(m_model.Description());
|
||||
EXPECT_EQ(L"test-doc_string", description);
|
||||
}
|
||||
|
||||
TEST_F(LearningModelAPITest, GetVersion)
|
||||
{
|
||||
EXPECT_NO_THROW(LoadModel(L"squeezenet_modifiedforruntimestests.onnx"));
|
||||
int64_t version(m_model.Version());
|
||||
(void)(version);
|
||||
}
|
||||
|
||||
typedef std::vector<std::pair<std::wstring, std::wstring>> Metadata;
|
||||
|
||||
class MetadataTest : public LearningModelAPITest, public testing::WithParamInterface<std::pair<std::wstring, Metadata>>
|
||||
{};
|
||||
|
||||
TEST_P(MetadataTest, GetMetaData)
|
||||
{
|
||||
std::wstring fileName;
|
||||
std::vector<std::pair<std::wstring, std::wstring>> keyValuePairs;
|
||||
|
||||
tie(fileName, keyValuePairs) = GetParam();
|
||||
EXPECT_NO_THROW(LoadModel(fileName.c_str()));
|
||||
EXPECT_TRUE(m_model.Metadata() != nullptr);
|
||||
EXPECT_EQ(keyValuePairs.size(), m_model.Metadata().Size());
|
||||
|
||||
auto iter = m_model.Metadata().First();
|
||||
for (auto& keyValue : keyValuePairs)
|
||||
{
|
||||
EXPECT_TRUE(iter.HasCurrent());
|
||||
EXPECT_EQ(keyValue.first, std::wstring(iter.Current().Key()));
|
||||
EXPECT_EQ(keyValue.second, std::wstring(iter.Current().Value()));
|
||||
iter.MoveNext();
|
||||
}
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
ModelMetadata,
|
||||
MetadataTest,
|
||||
::testing::Values(
|
||||
std::pair(L"squeezenet_modifiedforruntimestests.onnx", Metadata{}),
|
||||
std::pair(L"modelWithMetaData.onnx", Metadata{{L"thisisalongkey", L"thisisalongvalue"}}),
|
||||
std::pair(L"modelWith2MetaData.onnx", Metadata{{L"thisisalongkey", L"thisisalongvalue"}, {L"key2", L"val2"}})
|
||||
));
|
||||
|
||||
TEST_F(LearningModelAPITest, EnumerateInputs)
|
||||
{
|
||||
EXPECT_NO_THROW(LoadModel(L"squeezenet_modifiedforruntimestests.onnx"));
|
||||
|
||||
// purposely don't cache "InputFeatures" in order to exercise calling it multiple times
|
||||
EXPECT_TRUE(m_model.InputFeatures().First().HasCurrent());
|
||||
|
||||
std::wstring name(m_model.InputFeatures().First().Current().Name());
|
||||
EXPECT_EQ(L"data_0", name);
|
||||
|
||||
// make sure it's either tensor or image
|
||||
TensorFeatureDescriptor tensorDescriptor = nullptr;
|
||||
m_model.InputFeatures().First().Current().try_as(tensorDescriptor);
|
||||
if (tensorDescriptor == nullptr)
|
||||
{
|
||||
ImageFeatureDescriptor imageDescriptor = nullptr;
|
||||
EXPECT_NO_THROW(m_model.InputFeatures().First().Current().as(imageDescriptor));
|
||||
}
|
||||
|
||||
auto modelDataKind = tensorDescriptor.TensorKind();
|
||||
EXPECT_EQ(TensorKind::Float, modelDataKind);
|
||||
|
||||
EXPECT_TRUE(tensorDescriptor.IsRequired());
|
||||
|
||||
std::vector<int64_t> expectedShapes = { 1,3,224,224 };
|
||||
EXPECT_EQ(expectedShapes.size(), tensorDescriptor.Shape().Size());
|
||||
for (uint32_t j = 0; j < tensorDescriptor.Shape().Size(); j++)
|
||||
{
|
||||
EXPECT_EQ(expectedShapes.at(j), tensorDescriptor.Shape().GetAt(j));
|
||||
}
|
||||
|
||||
auto first = m_model.InputFeatures().First();
|
||||
first.MoveNext();
|
||||
EXPECT_FALSE(first.HasCurrent());
|
||||
}
|
||||
|
||||
TEST_F(LearningModelAPITest, EnumerateOutputs)
|
||||
{
|
||||
EXPECT_NO_THROW(LoadModel(L"squeezenet_modifiedforruntimestests.onnx"));
|
||||
|
||||
// purposely don't cache "OutputFeatures" in order to exercise calling it multiple times
|
||||
std::wstring name(m_model.OutputFeatures().First().Current().Name());
|
||||
EXPECT_EQ(L"softmaxout_1", name);
|
||||
|
||||
TensorFeatureDescriptor tensorDescriptor = nullptr;
|
||||
EXPECT_NO_THROW(m_model.OutputFeatures().First().Current().as(tensorDescriptor));
|
||||
EXPECT_TRUE(tensorDescriptor != nullptr);
|
||||
|
||||
auto tensorName = tensorDescriptor.Name();
|
||||
EXPECT_EQ(L"softmaxout_1", tensorName);
|
||||
|
||||
auto modelDataKind = tensorDescriptor.TensorKind();
|
||||
EXPECT_EQ(TensorKind::Float, modelDataKind);
|
||||
|
||||
EXPECT_TRUE(tensorDescriptor.IsRequired());
|
||||
|
||||
std::vector<int64_t> expectedShapes = { 1, 1000, 1, 1 };
|
||||
EXPECT_EQ(expectedShapes.size(), tensorDescriptor.Shape().Size());
|
||||
for (uint32_t j = 0; j < tensorDescriptor.Shape().Size(); j++)
|
||||
{
|
||||
EXPECT_EQ(expectedShapes.at(j), tensorDescriptor.Shape().GetAt(j));
|
||||
}
|
||||
|
||||
auto first = m_model.OutputFeatures().First();
|
||||
first.MoveNext();
|
||||
EXPECT_FALSE(first.HasCurrent());
|
||||
}
|
||||
|
||||
TEST_F(LearningModelAPITest, CloseModelCheckMetadata)
|
||||
{
|
||||
EXPECT_NO_THROW(LoadModel(L"squeezenet_modifiedforruntimestests.onnx"));
|
||||
EXPECT_NO_THROW(m_model.Close());
|
||||
std::wstring author(m_model.Author());
|
||||
EXPECT_EQ(L"onnx-caffe2", author);
|
||||
std::wstring name(m_model.Name());
|
||||
EXPECT_EQ(L"squeezenet_old", name);
|
||||
std::wstring domain(m_model.Domain());
|
||||
EXPECT_EQ(L"test-domain", domain);
|
||||
std::wstring description(m_model.Description());
|
||||
EXPECT_EQ(L"test-doc_string", description);
|
||||
int64_t version(m_model.Version());
|
||||
EXPECT_EQ(123456, version);
|
||||
}
|
||||
|
||||
TEST_F(LearningModelAPITestGpu, CloseModelCheckEval)
|
||||
{
|
||||
EXPECT_NO_THROW(LoadModel(L"model.onnx"));
|
||||
LearningModelSession session = nullptr;
|
||||
EXPECT_NO_THROW(session = LearningModelSession(m_model));
|
||||
EXPECT_NO_THROW(m_model.Close());
|
||||
|
||||
std::wstring fullImagePath = FileHelpers::GetModulePath() + L"kitten_224.png";
|
||||
StorageFile imagefile = StorageFile::GetFileFromPathAsync(fullImagePath).get();
|
||||
IRandomAccessStream stream = imagefile.OpenAsync(FileAccessMode::Read).get();
|
||||
SoftwareBitmap softwareBitmap = (BitmapDecoder::CreateAsync(stream).get()).GetSoftwareBitmapAsync().get();
|
||||
VideoFrame frame = VideoFrame::CreateWithSoftwareBitmap(softwareBitmap);
|
||||
|
||||
LearningModelBinding binding = nullptr;
|
||||
EXPECT_NO_THROW(binding = LearningModelBinding(session));
|
||||
EXPECT_NO_THROW(binding.Bind(m_model.InputFeatures().First().Current().Name(), frame));
|
||||
|
||||
EXPECT_NO_THROW(session.Evaluate(binding, L""));
|
||||
}
|
||||
|
||||
TEST_F(LearningModelAPITest, CloseModelNoNewSessions)
|
||||
{
|
||||
EXPECT_NO_THROW(LoadModel(L"model.onnx"));
|
||||
EXPECT_NO_THROW(m_model.Close());
|
||||
LearningModelSession session = nullptr;
|
||||
EXPECT_THROW(
|
||||
try {
|
||||
session = LearningModelSession(m_model);
|
||||
} catch (const winrt::hresult_error& e) {
|
||||
EXPECT_EQ(E_INVALIDARG, e.code());
|
||||
throw;
|
||||
}
|
||||
, winrt::hresult_error);
|
||||
}
|
||||
615
winml/test/api/LearningModelBindingAPITest.cpp
Normal file
615
winml/test/api/LearningModelBindingAPITest.cpp
Normal file
|
|
@ -0,0 +1,615 @@
|
|||
#include "testPch.h"
|
||||
#include "APITest.h"
|
||||
#include "SqueezeNetValidator.h"
|
||||
|
||||
#include <winrt/Windows.Graphics.Imaging.h>
|
||||
#include <winrt/Windows.Media.h>
|
||||
#include "winrt/Windows.Storage.h"
|
||||
#include "DeviceHelpers.h"
|
||||
|
||||
using namespace winrt;
|
||||
using namespace winrt::Windows::AI::MachineLearning;
|
||||
using namespace winrt::Windows::Foundation::Collections;
|
||||
using namespace winrt::Windows::Graphics::Imaging;
|
||||
using namespace winrt::Windows::Media;
|
||||
using namespace winrt::Windows::Storage;
|
||||
|
||||
class LearningModelBindingAPITest : public APITest
|
||||
{};
|
||||
|
||||
class LearningModelBindingAPITestGpu : public LearningModelBindingAPITest
|
||||
{};
|
||||
|
||||
TEST_F(LearningModelBindingAPITest, CpuSqueezeNet)
|
||||
{
|
||||
std::string cpuInstance("CPU");
|
||||
WinML::Engine::Test::ModelValidator::SqueezeNet(cpuInstance, LearningModelDeviceKind::Cpu, /*dataTolerance*/ 0.00001f, false);
|
||||
}
|
||||
|
||||
TEST_F(LearningModelBindingAPITest, CpuSqueezeNetEmptyOutputs)
|
||||
{
|
||||
std::string cpuInstance("CPU");
|
||||
WinML::Engine::Test::ModelValidator::SqueezeNet(
|
||||
cpuInstance,
|
||||
LearningModelDeviceKind::Cpu,
|
||||
/*dataTolerance*/ 0.00001f,
|
||||
false,
|
||||
OutputBindingStrategy::Empty);
|
||||
}
|
||||
|
||||
TEST_F(LearningModelBindingAPITest, CpuSqueezeNetUnboundOutputs)
|
||||
{
|
||||
std::string cpuInstance("CPU");
|
||||
WinML::Engine::Test::ModelValidator::SqueezeNet(
|
||||
cpuInstance,
|
||||
LearningModelDeviceKind::Cpu,
|
||||
/*dataTolerance*/ 0.00001f,
|
||||
false,
|
||||
OutputBindingStrategy::Unbound);
|
||||
}
|
||||
|
||||
TEST_F(LearningModelBindingAPITest, CpuSqueezeNetBindInputTensorAsInspectable)
|
||||
{
|
||||
std::string cpuInstance("CPU");
|
||||
WinML::Engine::Test::ModelValidator::SqueezeNet(
|
||||
cpuInstance,
|
||||
LearningModelDeviceKind::Cpu,
|
||||
/*dataTolerance*/ 0.00001f,
|
||||
false,
|
||||
OutputBindingStrategy::Bound /* empty outputs */,
|
||||
true /* bind inputs as inspectables */);
|
||||
}
|
||||
|
||||
TEST_F(LearningModelBindingAPITest, CastMapInt64)
|
||||
{
|
||||
EXPECT_NO_THROW(LoadModel(L"castmap-int64.onnx"));
|
||||
// TODO: Check Descriptor
|
||||
}
|
||||
|
||||
TEST_F(LearningModelBindingAPITest, DictionaryVectorizerMapInt64)
|
||||
{
|
||||
EXPECT_NO_THROW(LoadModel(L"dictvectorizer-int64.onnx"));
|
||||
|
||||
auto inputDescriptor = m_model.InputFeatures().First().Current();
|
||||
EXPECT_TRUE(inputDescriptor.Kind() == LearningModelFeatureKind::Map);
|
||||
auto mapDescriptor = inputDescriptor.as<MapFeatureDescriptor>();
|
||||
EXPECT_TRUE(mapDescriptor.KeyKind() == TensorKind::Int64);
|
||||
EXPECT_TRUE(mapDescriptor.ValueDescriptor().Kind() == LearningModelFeatureKind::Tensor);
|
||||
auto tensorDescriptor = mapDescriptor.ValueDescriptor().as<TensorFeatureDescriptor>();
|
||||
// empty size means tensor of scalar value
|
||||
EXPECT_TRUE(tensorDescriptor.Shape().Size() == 0);
|
||||
EXPECT_TRUE(tensorDescriptor.TensorKind() == TensorKind::Float);
|
||||
|
||||
LearningModelSession modelSession(m_model);
|
||||
LearningModelBinding binding(modelSession);
|
||||
std::unordered_map<int64_t, float> map;
|
||||
map[1] = 1.f;
|
||||
map[10] = 10.f;
|
||||
map[3] = 3.f;
|
||||
|
||||
|
||||
auto mapInputName = inputDescriptor.Name();
|
||||
|
||||
// Bind as IMap
|
||||
auto abiMap = winrt::single_threaded_map(std::move(map));
|
||||
binding.Bind(mapInputName, abiMap);
|
||||
auto mapInputInspectable = abiMap.as<winrt::Windows::Foundation::IInspectable>();
|
||||
auto first = binding.First();
|
||||
EXPECT_TRUE(first.Current().Key() == mapInputName);
|
||||
EXPECT_TRUE(first.Current().Value() == mapInputInspectable);
|
||||
EXPECT_TRUE(binding.Lookup(mapInputName) == mapInputInspectable);
|
||||
|
||||
// Bind as IMapView
|
||||
auto mapView = abiMap.GetView();
|
||||
binding.Bind(mapInputName, mapView);
|
||||
mapInputInspectable = mapView.as<winrt::Windows::Foundation::IInspectable>();
|
||||
first = binding.First();
|
||||
EXPECT_TRUE(first.Current().Key() == mapInputName);
|
||||
EXPECT_TRUE(first.Current().Value() == mapView);
|
||||
EXPECT_TRUE(binding.Lookup(mapInputName) == mapView);
|
||||
|
||||
}
|
||||
|
||||
TEST_F(LearningModelBindingAPITest, DictionaryVectorizerMapString)
|
||||
{
|
||||
EXPECT_NO_THROW(LoadModel(L"dictvectorizer-string.onnx"));
|
||||
|
||||
auto inputDescriptor = m_model.InputFeatures().First().Current();
|
||||
EXPECT_TRUE(inputDescriptor.Kind() == LearningModelFeatureKind::Map);
|
||||
|
||||
auto mapDescriptor = inputDescriptor.as<MapFeatureDescriptor>();
|
||||
EXPECT_TRUE(mapDescriptor.KeyKind() == TensorKind::String);
|
||||
EXPECT_TRUE(mapDescriptor.ValueDescriptor().Kind() == LearningModelFeatureKind::Tensor);
|
||||
|
||||
auto tensorDescriptor = mapDescriptor.ValueDescriptor().as<TensorFeatureDescriptor>();
|
||||
// empty size means tensor of scalar value
|
||||
EXPECT_TRUE(tensorDescriptor.Shape().Size() == 0);
|
||||
EXPECT_TRUE(tensorDescriptor.TensorKind() == TensorKind::Float);
|
||||
|
||||
LearningModelSession modelSession(m_model);
|
||||
LearningModelBinding binding(modelSession);
|
||||
std::unordered_map<winrt::hstring, float> map;
|
||||
map[L"1"] = 1.f;
|
||||
map[L"10"] = 10.f;
|
||||
map[L"2"] = 2.f;
|
||||
|
||||
auto mapInputName = inputDescriptor.Name();
|
||||
auto abiMap = winrt::single_threaded_map(std::move(map));
|
||||
binding.Bind(mapInputName, abiMap);
|
||||
|
||||
auto mapInputInspectable = abiMap.as<winrt::Windows::Foundation::IInspectable>();
|
||||
auto first = binding.First();
|
||||
EXPECT_TRUE(first.Current().Key() == mapInputName);
|
||||
EXPECT_TRUE(first.Current().Value() == mapInputInspectable);
|
||||
EXPECT_TRUE(binding.Lookup(mapInputName) == mapInputInspectable);
|
||||
}
|
||||
|
||||
static void RunZipMapInt64(
|
||||
winrt::Windows::AI::MachineLearning::LearningModel model,
|
||||
OutputBindingStrategy bindingStrategy)
|
||||
{
|
||||
auto outputFeatures = model.OutputFeatures();
|
||||
auto outputDescriptor = outputFeatures.First().Current();
|
||||
EXPECT_TRUE(outputDescriptor.Kind() == LearningModelFeatureKind::Sequence);
|
||||
|
||||
auto seqDescriptor = outputDescriptor.as<SequenceFeatureDescriptor>();
|
||||
auto mapDescriptor = seqDescriptor.ElementDescriptor().as<MapFeatureDescriptor>();
|
||||
EXPECT_TRUE(mapDescriptor.KeyKind() == TensorKind::Int64);
|
||||
|
||||
EXPECT_TRUE(mapDescriptor.ValueDescriptor().Kind() == LearningModelFeatureKind::Tensor);
|
||||
auto tensorDescriptor = mapDescriptor.ValueDescriptor().as<TensorFeatureDescriptor>();
|
||||
EXPECT_TRUE(tensorDescriptor.TensorKind() == TensorKind::Float);
|
||||
|
||||
LearningModelSession session(model);
|
||||
LearningModelBinding binding(session);
|
||||
|
||||
std::vector<float> inputs = { 0.5f, 0.25f, 0.125f };
|
||||
std::vector<int64_t> shape = { 1, 3 };
|
||||
|
||||
// Bind inputs
|
||||
auto inputTensor =
|
||||
TensorFloat::CreateFromArray(
|
||||
shape,
|
||||
winrt::array_view<const float>(std::move(inputs)));
|
||||
binding.Bind(winrt::hstring(L"X"), inputTensor);
|
||||
|
||||
typedef IMap<int64_t, float> ABIMap;
|
||||
typedef IVector<ABIMap> ABISequeneceOfMap;
|
||||
|
||||
ABISequeneceOfMap abiOutput = nullptr;
|
||||
// Bind outputs
|
||||
if (bindingStrategy == OutputBindingStrategy::Bound)
|
||||
{
|
||||
abiOutput = winrt::single_threaded_vector<ABIMap>();
|
||||
binding.Bind(winrt::hstring(L"Y"), abiOutput);
|
||||
}
|
||||
|
||||
// Evaluate
|
||||
auto result = session.Evaluate(binding, L"0").Outputs();
|
||||
|
||||
if (bindingStrategy == OutputBindingStrategy::Bound)
|
||||
{
|
||||
// from output binding
|
||||
const auto &out1 = abiOutput.GetAt(0);
|
||||
const auto &out2 = result.Lookup(L"Y").as<IVectorView<ABIMap>>().GetAt(0);
|
||||
SCOPED_TRACE((std::ostringstream() << "size: " << out1.Size()).str());
|
||||
// check outputs
|
||||
auto iter1 = out1.First();
|
||||
auto iter2 = out2.First();
|
||||
for (uint32_t i = 0, size = (uint32_t)inputs.size(); i < size; ++i)
|
||||
{
|
||||
EXPECT_TRUE(iter1.HasCurrent());
|
||||
EXPECT_TRUE(iter2.HasCurrent());
|
||||
const auto &pair1 = iter1.Current();
|
||||
const auto &pair2 = iter2.Current();
|
||||
SCOPED_TRACE((std::ostringstream() << "key: " << pair1.Key() << ", value: " << pair2.Value()).str());
|
||||
EXPECT_TRUE(pair1.Key() == i && pair2.Key() == i);
|
||||
EXPECT_TRUE(pair1.Value() == inputs[i] && pair2.Value() == inputs[i]);
|
||||
iter1.MoveNext();
|
||||
iter2.MoveNext();
|
||||
}
|
||||
EXPECT_TRUE(!iter1.HasCurrent());
|
||||
EXPECT_TRUE(!iter2.HasCurrent());
|
||||
}
|
||||
else
|
||||
{
|
||||
abiOutput = result.Lookup(L"Y").as<ABISequeneceOfMap>();
|
||||
EXPECT_TRUE(abiOutput.Size() == 1);
|
||||
ABIMap map = abiOutput.GetAt(0);
|
||||
EXPECT_TRUE(map.Size() == 3);
|
||||
EXPECT_TRUE(map.Lookup(0) == 0.5);
|
||||
EXPECT_TRUE(map.Lookup(1) == .25);
|
||||
EXPECT_TRUE(map.Lookup(2) == .125);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(LearningModelBindingAPITest, ZipMapInt64)
|
||||
{
|
||||
EXPECT_NO_THROW(LoadModel(L"zipmap-int64.onnx"));
|
||||
RunZipMapInt64(m_model, OutputBindingStrategy::Bound);
|
||||
}
|
||||
|
||||
TEST_F(LearningModelBindingAPITest, ZipMapInt64Unbound)
|
||||
{
|
||||
EXPECT_NO_THROW(LoadModel(L"zipmap-int64.onnx"));
|
||||
RunZipMapInt64(m_model, OutputBindingStrategy::Unbound);
|
||||
}
|
||||
|
||||
TEST_F(LearningModelBindingAPITest, ZipMapString)
|
||||
{
|
||||
// output constraint: "seq(map(string, float))" or "seq(map(int64, float))"
|
||||
EXPECT_NO_THROW(LoadModel(L"zipmap-string.onnx"));
|
||||
auto outputs = m_model.OutputFeatures();
|
||||
auto outputDescriptor = outputs.First().Current();
|
||||
EXPECT_TRUE(outputDescriptor.Kind() == LearningModelFeatureKind::Sequence);
|
||||
auto mapDescriptor = outputDescriptor.as<SequenceFeatureDescriptor>().ElementDescriptor().as<MapFeatureDescriptor>();
|
||||
EXPECT_TRUE(mapDescriptor.KeyKind() == TensorKind::String);
|
||||
EXPECT_TRUE(mapDescriptor.ValueDescriptor().Kind() == LearningModelFeatureKind::Tensor);
|
||||
auto tensorDescriptor = mapDescriptor.ValueDescriptor().as<TensorFeatureDescriptor>();
|
||||
EXPECT_TRUE(tensorDescriptor.TensorKind() == TensorKind::Float);
|
||||
|
||||
LearningModelSession session(m_model);
|
||||
LearningModelBinding binding(session);
|
||||
|
||||
std::vector<float> inputs = { 0.5f, 0.25f, 0.125f };
|
||||
std::vector<int64_t> shape = { 1, 3 };
|
||||
std::vector<winrt::hstring> labels = { L"cat", L"dog", L"lion" };
|
||||
std::map<winrt::hstring, float> mapData = { { L"cat", 0.0f }, { L"dog", 0.0f }, { L"lion", 0.0f } };
|
||||
typedef IMap<winrt::hstring, float> ABIMap;
|
||||
ABIMap abiMap = winrt::single_threaded_map<winrt::hstring, float>(std::move(mapData));
|
||||
std::vector<ABIMap> seqOutput = { abiMap };
|
||||
IVector<ABIMap> ABIOutput = winrt::single_threaded_vector<ABIMap>(std::move(seqOutput));
|
||||
|
||||
TensorFloat inputTensor = TensorFloat::CreateFromArray(shape, winrt::array_view<const float>(std::move(inputs)));
|
||||
binding.Bind(winrt::hstring(L"X"), inputTensor);
|
||||
binding.Bind(winrt::hstring(L"Y"), ABIOutput);
|
||||
auto result = session.Evaluate(binding, L"0").Outputs();
|
||||
// from output binding
|
||||
const auto &out1 = ABIOutput.GetAt(0);
|
||||
const auto &out2 = result.Lookup(L"Y").as<IVectorView<ABIMap>>().GetAt(0);
|
||||
SCOPED_TRACE((std::ostringstream() << "size: " << out1.Size()).str());
|
||||
// single key,value pair for each map
|
||||
auto iter1 = out1.First();
|
||||
auto iter2 = out2.First();
|
||||
for (uint32_t i = 0, size = (uint32_t)inputs.size(); i < size; ++i)
|
||||
{
|
||||
EXPECT_TRUE(iter2.HasCurrent());
|
||||
const auto &pair1 = iter1.Current();
|
||||
const auto &pair2 = iter2.Current();
|
||||
SCOPED_TRACE((std::ostringstream() << "key: " << pair1.Key().c_str() << ", value " << pair2.Value()).str());
|
||||
EXPECT_TRUE(std::wstring(pair1.Key().c_str()).compare(labels[i]) == 0);
|
||||
EXPECT_TRUE(std::wstring(pair2.Key().c_str()).compare(labels[i]) == 0);
|
||||
EXPECT_TRUE(pair1.Value() == inputs[i] && pair2.Value() == inputs[i]);
|
||||
iter1.MoveNext();
|
||||
iter2.MoveNext();
|
||||
}
|
||||
EXPECT_TRUE(!iter1.HasCurrent());
|
||||
EXPECT_TRUE(!iter2.HasCurrent());
|
||||
}
|
||||
|
||||
TEST_F(LearningModelBindingAPITestGpu, GpuSqueezeNet)
|
||||
{
|
||||
|
||||
std::string gpuInstance("GPU");
|
||||
WinML::Engine::Test::ModelValidator::SqueezeNet(
|
||||
gpuInstance,
|
||||
LearningModelDeviceKind::DirectX,
|
||||
/*dataTolerance*/ 0.00001f);
|
||||
}
|
||||
|
||||
TEST_F(LearningModelBindingAPITestGpu, GpuSqueezeNetEmptyOutputs)
|
||||
{
|
||||
|
||||
std::string gpuInstance("GPU");
|
||||
WinML::Engine::Test::ModelValidator::SqueezeNet(
|
||||
gpuInstance,
|
||||
LearningModelDeviceKind::DirectX,
|
||||
/*dataTolerance*/ 0.00001f,
|
||||
false,
|
||||
OutputBindingStrategy::Empty);
|
||||
}
|
||||
|
||||
TEST_F(LearningModelBindingAPITestGpu, GpuSqueezeNetUnboundOutputs)
|
||||
{
|
||||
|
||||
std::string gpuInstance("GPU");
|
||||
WinML::Engine::Test::ModelValidator::SqueezeNet(
|
||||
gpuInstance,
|
||||
LearningModelDeviceKind::DirectX,
|
||||
/*dataTolerance*/ 0.00001f,
|
||||
false,
|
||||
OutputBindingStrategy::Unbound);
|
||||
}
|
||||
|
||||
// Validates that when the input image is the same as the model expects, the binding step is executed correctly.
|
||||
TEST_F(LearningModelBindingAPITestGpu, ImageBindingDimensions)
|
||||
{
|
||||
LearningModelBinding m_binding = nullptr;
|
||||
std::wstring filePath = FileHelpers::GetModulePath() + L"model.onnx";
|
||||
// load a model with expected input size: 224 x 224
|
||||
EXPECT_NO_THROW(m_device = LearningModelDevice(LearningModelDeviceKind::Default));
|
||||
EXPECT_NO_THROW(m_model = LearningModel::LoadFromFilePath(filePath));
|
||||
EXPECT_TRUE(m_model != nullptr);
|
||||
EXPECT_NO_THROW(m_session = LearningModelSession(m_model, m_device));
|
||||
EXPECT_NO_THROW(m_binding = LearningModelBinding(m_session));
|
||||
|
||||
// Create input images and execute bind
|
||||
// Test Case 1: both width and height are larger than model expects
|
||||
VideoFrame inputImage1(BitmapPixelFormat::Rgba8, 1000, 1000);
|
||||
ImageFeatureValue inputTensor = ImageFeatureValue::CreateFromVideoFrame(inputImage1);
|
||||
EXPECT_NO_THROW(m_binding.Bind(L"data_0", inputTensor));
|
||||
|
||||
// Test Case 2: only height is larger, while width is smaller
|
||||
VideoFrame inputImage2(BitmapPixelFormat::Rgba8, 20, 1000);
|
||||
inputTensor = ImageFeatureValue::CreateFromVideoFrame(inputImage2);
|
||||
EXPECT_NO_THROW(m_binding.Bind(L"data_0", inputTensor));
|
||||
|
||||
// Test Case 3: only width is larger, while height is smaller
|
||||
VideoFrame inputImage3(BitmapPixelFormat::Rgba8, 1000, 20);
|
||||
inputTensor = ImageFeatureValue::CreateFromVideoFrame(inputImage3);
|
||||
EXPECT_NO_THROW(m_binding.Bind(L"data_0", inputTensor));
|
||||
|
||||
// Test Case 4: both width and height are smaller than model expects
|
||||
VideoFrame inputImage4(BitmapPixelFormat::Rgba8, 20, 20);
|
||||
inputTensor = ImageFeatureValue::CreateFromVideoFrame(inputImage4);
|
||||
EXPECT_NO_THROW(m_binding.Bind(L"data_0", inputTensor));
|
||||
}
|
||||
|
||||
TEST_F(LearningModelBindingAPITestGpu, VerifyInvalidBindExceptions)
|
||||
{
|
||||
|
||||
EXPECT_NO_THROW(LoadModel(L"zipmap-int64.onnx"));
|
||||
|
||||
LearningModelSession session(m_model);
|
||||
LearningModelBinding binding(session);
|
||||
|
||||
std::vector<float> inputs = { 0.5f, 0.25f, 0.125f };
|
||||
std::vector<int64_t> shape = { 1, 3 };
|
||||
|
||||
auto matchException =
|
||||
[](const winrt::hresult_error& e, HRESULT hr) -> bool
|
||||
{
|
||||
return e.code() == hr;
|
||||
};
|
||||
|
||||
auto ensureWinmlSizeMismatch = std::bind(matchException, std::placeholders::_1, WINML_ERR_SIZE_MISMATCH);
|
||||
auto ensureWinmlInvalidBinding = std::bind(matchException, std::placeholders::_1, WINML_ERR_INVALID_BINDING);
|
||||
|
||||
/*
|
||||
Verify tensor bindings throw correct bind exceptions
|
||||
*/
|
||||
|
||||
// Bind invalid image as tensorfloat input
|
||||
auto image = FileHelpers::LoadImageFeatureValue(L"227x227.png");
|
||||
EXPECT_THROW_SPECIFIC(binding.Bind(L"X", image), winrt::hresult_error, ensureWinmlSizeMismatch);
|
||||
|
||||
// Bind invalid map as tensorfloat input
|
||||
std::unordered_map<float, float> map;
|
||||
auto abiMap = winrt::single_threaded_map(std::move(map));
|
||||
EXPECT_THROW_SPECIFIC(binding.Bind(L"X", abiMap), winrt::hresult_error, ensureWinmlInvalidBinding);
|
||||
|
||||
// Bind invalid sequence as tensorfloat input
|
||||
std::vector<uint32_t> sequence;
|
||||
auto abiSequence = winrt::single_threaded_vector(std::move(sequence));
|
||||
EXPECT_THROW_SPECIFIC(binding.Bind(L"X", abiSequence), winrt::hresult_error, ensureWinmlInvalidBinding);
|
||||
|
||||
// Bind invalid tensor size as tensorfloat input
|
||||
auto tensorBoolean = TensorBoolean::Create();
|
||||
EXPECT_THROW_SPECIFIC(binding.Bind(L"X", tensorBoolean), winrt::hresult_error, ensureWinmlInvalidBinding);
|
||||
|
||||
// Bind invalid tensor shape as tensorfloat input
|
||||
auto tensorInvalidShape = TensorFloat::Create(std::vector<int64_t> { 2, 3, 4 });
|
||||
EXPECT_THROW_SPECIFIC(binding.Bind(L"X", tensorInvalidShape), winrt::hresult_error, ensureWinmlInvalidBinding);
|
||||
|
||||
/*
|
||||
Verify sequence bindings throw correct bind exceptions
|
||||
*/
|
||||
|
||||
// Bind invalid image as sequence<map<int, float> output
|
||||
EXPECT_THROW_SPECIFIC(binding.Bind(L"Y", image), winrt::hresult_error, ensureWinmlInvalidBinding);
|
||||
|
||||
// Bind invalid map as sequence<map<int, float> output
|
||||
EXPECT_THROW_SPECIFIC(binding.Bind(L"Y", abiMap), winrt::hresult_error, ensureWinmlInvalidBinding);
|
||||
|
||||
// Bind invalid sequence<int> as sequence<map<int, float> output
|
||||
EXPECT_THROW_SPECIFIC(binding.Bind(L"Y", abiSequence), winrt::hresult_error, ensureWinmlInvalidBinding);
|
||||
|
||||
// Bind invalid tensor as sequence<map<int, float> output
|
||||
EXPECT_THROW_SPECIFIC(binding.Bind(L"Y", tensorBoolean), winrt::hresult_error, ensureWinmlInvalidBinding);
|
||||
|
||||
/*
|
||||
Verify image bindings throw correct bind exceptions
|
||||
*/
|
||||
|
||||
// EXPECT_NO_THROW(LoadModel(L"fns-candy.onnx"));
|
||||
|
||||
// LearningModelSession imageSession(m_model);
|
||||
// LearningModelBinding imageBinding(imageSession);
|
||||
|
||||
// auto inputName = m_model.InputFeatures().First().Current().Name();
|
||||
|
||||
// // Bind invalid map as image input
|
||||
// EXPECT_THROW_SPECIFIC(imageBinding.Bind(inputName, abiMap), winrt::hresult_error, ensureWinmlInvalidBinding);
|
||||
|
||||
// // Bind invalid sequence as image input
|
||||
// EXPECT_THROW_SPECIFIC(imageBinding.Bind(inputName, abiSequence), winrt::hresult_error, ensureWinmlInvalidBinding);
|
||||
|
||||
// // Bind invalid tensor type as image input
|
||||
// EXPECT_THROW_SPECIFIC(imageBinding.Bind(inputName, tensorBoolean), winrt::hresult_error, ensureWinmlInvalidBinding);
|
||||
|
||||
// // Bind invalid tensor size as image input
|
||||
// auto tensorFloat = TensorFloat::Create(std::vector<int64_t> { 1, 1, 100, 100 });
|
||||
// EXPECT_THROW_SPECIFIC(imageBinding.Bind(inputName, tensorFloat), winrt::hresult_error, ensureWinmlInvalidBinding);
|
||||
|
||||
// // Bind invalid tensor shape as image input
|
||||
// EXPECT_THROW_SPECIFIC(imageBinding.Bind(inputName, tensorInvalidShape), winrt::hresult_error, ensureWinmlInvalidBinding);
|
||||
|
||||
/*
|
||||
Verify map bindings throw correct bind exceptions
|
||||
*/
|
||||
EXPECT_NO_THROW(LoadModel(L"dictvectorizer-int64.onnx"));
|
||||
|
||||
LearningModelSession mapSession(m_model);
|
||||
LearningModelBinding mapBinding(mapSession);
|
||||
|
||||
auto inputName = m_model.InputFeatures().First().Current().Name();
|
||||
|
||||
// Bind invalid image as image input
|
||||
auto smallImage = FileHelpers::LoadImageFeatureValue(L"100x100.png");
|
||||
EXPECT_THROW_SPECIFIC(mapBinding.Bind(inputName, smallImage), winrt::hresult_error, ensureWinmlInvalidBinding);
|
||||
|
||||
// Bind invalid map as image input
|
||||
EXPECT_THROW_SPECIFIC(mapBinding.Bind(inputName, abiMap), winrt::hresult_error, ensureWinmlInvalidBinding);
|
||||
|
||||
// Bind invalid sequence as image input
|
||||
EXPECT_THROW_SPECIFIC(mapBinding.Bind(inputName, abiSequence), winrt::hresult_error, ensureWinmlInvalidBinding);
|
||||
|
||||
// Bind invalid tensor type as image input
|
||||
EXPECT_THROW_SPECIFIC(mapBinding.Bind(inputName, tensorBoolean), winrt::hresult_error, ensureWinmlInvalidBinding);
|
||||
}
|
||||
|
||||
// Verify that it throws an error when binding an invalid name.
|
||||
TEST_F(LearningModelBindingAPITestGpu, BindInvalidInputName)
|
||||
{
|
||||
LearningModelBinding m_binding = nullptr;
|
||||
std::wstring modelPath = FileHelpers::GetModulePath() + L"Add_ImageNet1920.onnx";
|
||||
EXPECT_NO_THROW(m_model = LearningModel::LoadFromFilePath(modelPath));
|
||||
EXPECT_TRUE(m_model != nullptr);
|
||||
EXPECT_NO_THROW(m_device = LearningModelDevice(LearningModelDeviceKind::Default));
|
||||
EXPECT_NO_THROW(m_session = LearningModelSession(m_model, m_device));
|
||||
EXPECT_NO_THROW(m_binding = LearningModelBinding(m_session));
|
||||
|
||||
VideoFrame iuputImage(BitmapPixelFormat::Rgba8, 1920, 1080);
|
||||
ImageFeatureValue inputTensor = ImageFeatureValue::CreateFromVideoFrame(iuputImage);
|
||||
|
||||
auto first = m_model.InputFeatures().First();
|
||||
std::wstring testInvalidName = L"0";
|
||||
|
||||
// Verify that testInvalidName is not in model's InputFeatures
|
||||
while (first.HasCurrent())
|
||||
{
|
||||
EXPECT_NE(testInvalidName, first.Current().Name());
|
||||
first.MoveNext();
|
||||
}
|
||||
|
||||
// Bind inputTensor to a valid input name
|
||||
EXPECT_NO_THROW(m_binding.Bind(L"input_39:0", inputTensor));
|
||||
|
||||
// Bind inputTensor to an invalid input name
|
||||
EXPECT_THROW_SPECIFIC(m_binding.Bind(testInvalidName, inputTensor),
|
||||
winrt::hresult_error,
|
||||
[](const winrt::hresult_error& e) -> bool
|
||||
{
|
||||
return e.code() == WINML_ERR_INVALID_BINDING;
|
||||
});
|
||||
}
|
||||
|
||||
TEST_F(LearningModelBindingAPITest, VerifyOutputAfterEvaluateAsyncCalledTwice)
|
||||
{
|
||||
LearningModelBinding m_binding = nullptr;
|
||||
std::wstring filePath = FileHelpers::GetModulePath() + L"relu.onnx";
|
||||
EXPECT_NO_THROW(m_device = LearningModelDevice(LearningModelDeviceKind::Default));
|
||||
EXPECT_NO_THROW(m_model = LearningModel::LoadFromFilePath(filePath));
|
||||
EXPECT_TRUE(m_model != nullptr);
|
||||
EXPECT_NO_THROW(m_session = LearningModelSession(m_model, m_device));
|
||||
EXPECT_NO_THROW(m_binding = LearningModelBinding(m_session));
|
||||
|
||||
auto inputShape = std::vector<int64_t>{ 5 };
|
||||
auto inputData1 = std::vector<float>{ -50.f, -25.f, 0.f, 25.f, 50.f };
|
||||
auto inputValue1 =
|
||||
TensorFloat::CreateFromIterable(
|
||||
inputShape,
|
||||
single_threaded_vector<float>(std::move(inputData1)).GetView());
|
||||
|
||||
auto inputData2 = std::vector<float>{ 50.f, 25.f, 0.f, -25.f, -50.f };
|
||||
auto inputValue2 =
|
||||
TensorFloat::CreateFromIterable(
|
||||
inputShape,
|
||||
single_threaded_vector<float>(std::move(inputData2)).GetView());
|
||||
|
||||
EXPECT_NO_THROW(m_binding.Bind(L"X", inputValue1));
|
||||
|
||||
auto outputValue = TensorFloat::Create();
|
||||
EXPECT_NO_THROW(m_binding.Bind(L"Y", outputValue));
|
||||
|
||||
EXPECT_NO_THROW(m_session.Evaluate(m_binding, L""));
|
||||
|
||||
auto buffer1 = outputValue.GetAsVectorView();
|
||||
EXPECT_TRUE(buffer1 != nullptr);
|
||||
|
||||
// The second evaluation
|
||||
// If we don't bind output again, the output value will not change
|
||||
EXPECT_NO_THROW(m_binding.Bind(L"X", inputValue2));
|
||||
EXPECT_NO_THROW(m_session.Evaluate(m_binding, L""));
|
||||
auto buffer2 = outputValue.GetAsVectorView();
|
||||
EXPECT_EQ(buffer1.Size(), buffer2.Size());
|
||||
bool isSame = true;
|
||||
for (uint32_t i = 0; i < buffer1.Size(); ++i)
|
||||
{
|
||||
if (buffer1.GetAt(i) != buffer2.GetAt(i))
|
||||
{
|
||||
isSame = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
EXPECT_FALSE(isSame);
|
||||
}
|
||||
|
||||
static VideoFrame CreateVideoFrame(const wchar_t* path)
|
||||
{
|
||||
auto imagefile = StorageFile::GetFileFromPathAsync(path).get();
|
||||
auto stream = imagefile.OpenAsync(FileAccessMode::Read).get();
|
||||
auto decoder = BitmapDecoder::CreateAsync(stream).get();
|
||||
auto softwareBitmap = decoder.GetSoftwareBitmapAsync().get();
|
||||
return VideoFrame::CreateWithSoftwareBitmap(softwareBitmap);
|
||||
}
|
||||
|
||||
TEST_F(LearningModelBindingAPITest, VerifyOutputAfterImageBindCalledTwice)
|
||||
{
|
||||
std::wstring fullModelPath = FileHelpers::GetModulePath() + L"model.onnx";
|
||||
std::wstring fullImagePath1 = FileHelpers::GetModulePath() + L"kitten_224.png";
|
||||
std::wstring fullImagePath2 = FileHelpers::GetModulePath() + L"fish.png";
|
||||
|
||||
// winml model creation
|
||||
LearningModel model = nullptr;
|
||||
EXPECT_NO_THROW(model = LearningModel::LoadFromFilePath(fullModelPath));
|
||||
LearningModelSession modelSession = nullptr;
|
||||
EXPECT_NO_THROW(modelSession = LearningModelSession(model, LearningModelDevice(LearningModelDeviceKind::Default)));
|
||||
LearningModelBinding modelBinding(modelSession);
|
||||
|
||||
// create the tensor for the actual output
|
||||
auto output = TensorFloat::Create();
|
||||
modelBinding.Bind(L"softmaxout_1", output);
|
||||
|
||||
// Bind image 1 and evaluate
|
||||
auto frame = CreateVideoFrame(fullImagePath1.c_str());
|
||||
auto imageTensor = ImageFeatureValue::CreateFromVideoFrame(frame);
|
||||
EXPECT_NO_THROW(modelBinding.Bind(L"data_0", imageTensor));
|
||||
EXPECT_NO_THROW(modelSession.Evaluate(modelBinding, L""));
|
||||
|
||||
// Store 1st result
|
||||
auto outputVectorView1 = output.GetAsVectorView();
|
||||
|
||||
// Bind image 2 and evaluate
|
||||
// In this scenario, the backing videoframe is updated, and the imagefeaturevalue is rebound.
|
||||
// The expected result is that the videoframe will be re-tensorized at bind
|
||||
auto frame2 = CreateVideoFrame(fullImagePath2.c_str());
|
||||
frame2.CopyToAsync(frame).get();
|
||||
EXPECT_NO_THROW(modelBinding.Bind(L"data_0", imageTensor));
|
||||
EXPECT_NO_THROW(modelSession.Evaluate(modelBinding, L""));
|
||||
|
||||
// Store 2nd result
|
||||
auto outputVectorView2 = output.GetAsVectorView();
|
||||
|
||||
EXPECT_EQ(outputVectorView1.Size(), outputVectorView2.Size());
|
||||
bool isSame = true;
|
||||
for (uint32_t i = 0; i < outputVectorView1.Size(); ++i)
|
||||
{
|
||||
if (outputVectorView1.GetAt(i) != outputVectorView2.GetAt(i))
|
||||
{
|
||||
isSame = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
EXPECT_FALSE(isSame);
|
||||
}
|
||||
373
winml/test/api/LearningModelSessionAPITest.cpp
Normal file
373
winml/test/api/LearningModelSessionAPITest.cpp
Normal file
|
|
@ -0,0 +1,373 @@
|
|||
#include "testPch.h"
|
||||
#include "APITest.h"
|
||||
|
||||
#include "winrt/Windows.Storage.h"
|
||||
|
||||
#include "DeviceHelpers.h"
|
||||
#include "protobufHelpers.h"
|
||||
|
||||
#include <D3d11_4.h>
|
||||
#include <dxgi1_6.h>
|
||||
#include "Psapi.h"
|
||||
|
||||
using namespace winrt;
|
||||
using namespace winrt::Windows::AI::MachineLearning;
|
||||
using namespace winrt::Windows::Foundation::Collections;
|
||||
|
||||
using winrt::Windows::Foundation::IPropertyValue;
|
||||
|
||||
class LearningModelSessionAPITests : public APITest
|
||||
{};
|
||||
|
||||
class LearningModelSessionAPITestsGpu : public APITest
|
||||
{}; // TODO create a constructor that calls GTEST_SKIP when GPU tests are disabled
|
||||
|
||||
class LearningModelSessionAPITestsSkipEdgeCore : public LearningModelSessionAPITestsGpu
|
||||
{}; // TODO create a constructor that calls GTEST_SKIP when on EdgeCore
|
||||
|
||||
TEST_F(LearningModelSessionAPITests, CreateSessionDeviceDefault)
|
||||
{
|
||||
EXPECT_NO_THROW(LoadModel(L"model.onnx"));
|
||||
|
||||
EXPECT_NO_THROW(m_device = LearningModelDevice(LearningModelDeviceKind::Default));
|
||||
EXPECT_NO_THROW(m_session = LearningModelSession(m_model, m_device));
|
||||
}
|
||||
|
||||
TEST_F(LearningModelSessionAPITests, CreateSessionDeviceCpu)
|
||||
{
|
||||
EXPECT_NO_THROW(LoadModel(L"model.onnx"));
|
||||
|
||||
EXPECT_NO_THROW(m_device = LearningModelDevice(LearningModelDeviceKind::Cpu));
|
||||
EXPECT_NO_THROW(m_session = LearningModelSession(m_model, m_device));
|
||||
// for the CPU device, make sure that we get back NULL and 0 for any device properties
|
||||
EXPECT_FALSE(m_device.Direct3D11Device());
|
||||
LARGE_INTEGER id;
|
||||
id.QuadPart = GetAdapterIdQuadPart();
|
||||
EXPECT_EQ(id.LowPart, static_cast<DWORD>(0));
|
||||
EXPECT_EQ(id.HighPart, 0);
|
||||
}
|
||||
|
||||
TEST_F(LearningModelSessionAPITests, CreateSessionWithModelLoadedFromStream)
|
||||
{
|
||||
std::wstring path = FileHelpers::GetModulePath() + L"model.onnx";
|
||||
auto storageFile = winrt::Windows::Storage::StorageFile::GetFileFromPathAsync(path).get();
|
||||
|
||||
EXPECT_NO_THROW(m_model = LearningModel::LoadFromStream(storageFile));
|
||||
|
||||
EXPECT_NO_THROW(m_device = LearningModelDevice(LearningModelDeviceKind::Default));
|
||||
EXPECT_NO_THROW(m_session = LearningModelSession(m_model, m_device));
|
||||
}
|
||||
|
||||
TEST_F(LearningModelSessionAPITestsGpu, CreateSessionDeviceDirectX)
|
||||
{
|
||||
|
||||
EXPECT_NO_THROW(LoadModel(L"model.onnx"));
|
||||
|
||||
EXPECT_NO_THROW(m_device = LearningModelDevice(LearningModelDeviceKind::DirectX));
|
||||
EXPECT_NO_THROW(m_session = LearningModelSession(m_model, m_device));
|
||||
}
|
||||
|
||||
TEST_F(LearningModelSessionAPITestsGpu, CreateSessionDeviceDirectXHighPerformance)
|
||||
{
|
||||
|
||||
EXPECT_NO_THROW(LoadModel(L"model.onnx"));
|
||||
|
||||
EXPECT_NO_THROW(m_device = LearningModelDevice(LearningModelDeviceKind::DirectXHighPerformance));
|
||||
EXPECT_NO_THROW(m_session = LearningModelSession(m_model, m_device));
|
||||
}
|
||||
|
||||
TEST_F(LearningModelSessionAPITestsGpu, CreateSessionDeviceDirectXMinimumPower)
|
||||
{
|
||||
|
||||
EXPECT_NO_THROW(LoadModel(L"model.onnx"));
|
||||
|
||||
EXPECT_NO_THROW(m_device = LearningModelDevice(LearningModelDeviceKind::DirectXMinPower));
|
||||
EXPECT_NO_THROW(m_session = LearningModelSession(m_model, m_device));
|
||||
}
|
||||
|
||||
TEST_F(LearningModelSessionAPITestsSkipEdgeCore, AdapterIdAndDevice)
|
||||
{
|
||||
EXPECT_NO_THROW(LoadModel(L"model.onnx"));
|
||||
|
||||
com_ptr<IDXGIFactory6> factory;
|
||||
EXPECT_HRESULT_SUCCEEDED(CreateDXGIFactory1(__uuidof(IDXGIFactory6), factory.put_void()));
|
||||
com_ptr<IDXGIAdapter> adapter;
|
||||
|
||||
m_device = LearningModelDevice(LearningModelDeviceKind::DirectX);
|
||||
EXPECT_HRESULT_SUCCEEDED(factory->EnumAdapters(0, adapter.put()));
|
||||
DXGI_ADAPTER_DESC desc;
|
||||
EXPECT_HRESULT_SUCCEEDED(adapter->GetDesc(&desc));
|
||||
LARGE_INTEGER id;
|
||||
id.QuadPart = GetAdapterIdQuadPart();
|
||||
EXPECT_EQ(desc.AdapterLuid.LowPart, id.LowPart);
|
||||
EXPECT_EQ(desc.AdapterLuid.HighPart, id.HighPart);
|
||||
EXPECT_TRUE(m_device.Direct3D11Device() != nullptr);
|
||||
|
||||
m_device = LearningModelDevice(LearningModelDeviceKind::DirectXHighPerformance);
|
||||
adapter = nullptr;
|
||||
EXPECT_HRESULT_SUCCEEDED(factory->EnumAdapterByGpuPreference(0, DXGI_GPU_PREFERENCE_HIGH_PERFORMANCE, __uuidof(IDXGIAdapter), adapter.put_void()));
|
||||
EXPECT_HRESULT_SUCCEEDED(adapter->GetDesc(&desc));
|
||||
id.QuadPart = GetAdapterIdQuadPart();
|
||||
EXPECT_EQ(desc.AdapterLuid.LowPart, id.LowPart);
|
||||
EXPECT_EQ(desc.AdapterLuid.HighPart, id.HighPart);
|
||||
EXPECT_TRUE(m_device.Direct3D11Device() != nullptr);
|
||||
|
||||
adapter = nullptr;
|
||||
m_device = LearningModelDevice(LearningModelDeviceKind::DirectXMinPower);
|
||||
EXPECT_HRESULT_SUCCEEDED(factory->EnumAdapterByGpuPreference(0, DXGI_GPU_PREFERENCE_MINIMUM_POWER, __uuidof(IDXGIAdapter), adapter.put_void()));
|
||||
EXPECT_HRESULT_SUCCEEDED(adapter->GetDesc(&desc));
|
||||
id.QuadPart = GetAdapterIdQuadPart();
|
||||
EXPECT_EQ(desc.AdapterLuid.LowPart, id.LowPart);
|
||||
EXPECT_EQ(desc.AdapterLuid.HighPart, id.HighPart);
|
||||
EXPECT_TRUE(m_device.Direct3D11Device() != nullptr);
|
||||
|
||||
EXPECT_NO_THROW(m_session = LearningModelSession(m_model, m_device));
|
||||
EXPECT_EQ(m_session.Device().AdapterId(), m_device.AdapterId());
|
||||
}
|
||||
|
||||
TEST_F(LearningModelSessionAPITests, EvaluateFeatures)
|
||||
{
|
||||
std::vector<int64_t> shape = { 4 };
|
||||
std::vector<winrt::hstring> data = { L"one", L"two", L"three", L"four" };
|
||||
|
||||
// create from buffer
|
||||
auto tensor = TensorString::CreateFromArray(shape, data);
|
||||
EXPECT_EQ(tensor.GetAsVectorView().Size(), data.size());
|
||||
EXPECT_TRUE(std::equal(data.cbegin(), data.cend(), begin(tensor.GetAsVectorView())));
|
||||
|
||||
// create from vector view
|
||||
auto dataCopy = data;
|
||||
tensor = TensorString::CreateFromIterable(
|
||||
shape, winrt::single_threaded_vector<winrt::hstring>(std::move(dataCopy)).GetView());
|
||||
EXPECT_EQ(tensor.GetAsVectorView().Size(), data.size());
|
||||
EXPECT_TRUE(std::equal(data.cbegin(), data.cend(), begin(tensor.GetAsVectorView())));
|
||||
|
||||
EXPECT_NO_THROW(LoadModel(L"id-tensor-string.onnx"));
|
||||
LearningModelSession session(m_model);
|
||||
|
||||
auto outputTensor = TensorString::Create();
|
||||
|
||||
std::map<hstring, winrt::Windows::Foundation::IInspectable> featuresstandardmap;
|
||||
featuresstandardmap[L"X"] = tensor;
|
||||
featuresstandardmap[L"Y"] = outputTensor;
|
||||
auto featureswinrtmap = winrt::single_threaded_map(std::move(featuresstandardmap));
|
||||
session.EvaluateFeatures(featureswinrtmap, L"0");
|
||||
|
||||
// verify identity model round-trip works
|
||||
EXPECT_EQ(outputTensor.GetAsVectorView().Size(), data.size());
|
||||
EXPECT_TRUE(std::equal(data.cbegin(), data.cend(), begin(outputTensor.GetAsVectorView())));
|
||||
}
|
||||
|
||||
TEST_F(LearningModelSessionAPITests, EvaluateFeaturesAsync)
|
||||
{
|
||||
std::vector<int64_t> shape = { 4 };
|
||||
std::vector<winrt::hstring> data = { L"one", L"two", L"three", L"four" };
|
||||
|
||||
// create from buffer
|
||||
auto tensor = TensorString::CreateFromArray(shape, data);
|
||||
EXPECT_EQ(tensor.GetAsVectorView().Size(), data.size());
|
||||
EXPECT_TRUE(std::equal(data.cbegin(), data.cend(), begin(tensor.GetAsVectorView())));
|
||||
|
||||
// create from vector view
|
||||
auto dataCopy = data;
|
||||
tensor = TensorString::CreateFromIterable(
|
||||
shape, winrt::single_threaded_vector<winrt::hstring>(std::move(dataCopy)).GetView());
|
||||
EXPECT_EQ(tensor.GetAsVectorView().Size(), data.size());
|
||||
EXPECT_TRUE(std::equal(data.cbegin(), data.cend(), begin(tensor.GetAsVectorView())));
|
||||
|
||||
EXPECT_NO_THROW(LoadModel(L"id-tensor-string.onnx"));
|
||||
LearningModelSession session(m_model);
|
||||
|
||||
auto outputTensor = TensorString::Create(shape);
|
||||
|
||||
std::map<hstring, winrt::Windows::Foundation::IInspectable> featuresstandardmap;
|
||||
featuresstandardmap[L"X"] = tensor;
|
||||
featuresstandardmap[L"Y"] = outputTensor;
|
||||
auto featureswinrtmap = winrt::single_threaded_map(std::move(featuresstandardmap));
|
||||
session.EvaluateFeaturesAsync(featureswinrtmap, L"0").get();
|
||||
|
||||
// verify identity model round-trip works
|
||||
EXPECT_EQ(outputTensor.GetAsVectorView().Size(), data.size());
|
||||
EXPECT_TRUE(std::equal(data.cbegin(), data.cend(), begin(outputTensor.GetAsVectorView())));
|
||||
}
|
||||
|
||||
TEST_F(LearningModelSessionAPITests, EvaluationProperties)
|
||||
{
|
||||
// load a model
|
||||
EXPECT_NO_THROW(LoadModel(L"model.onnx"));
|
||||
// create a session
|
||||
m_session = LearningModelSession(m_model);
|
||||
// set a property
|
||||
auto value = winrt::Windows::Foundation::PropertyValue::CreateBoolean(true);
|
||||
m_session.EvaluationProperties().Insert(L"propName1", value);
|
||||
// get the property and make sure it's there with the right value
|
||||
auto value2 = m_session.EvaluationProperties().Lookup(L"propName1");
|
||||
EXPECT_EQ(value2.as<IPropertyValue>().GetBoolean(), true);
|
||||
}
|
||||
|
||||
static LearningModelSession CreateSession(LearningModel model)
|
||||
{
|
||||
LearningModelDevice device(nullptr);
|
||||
EXPECT_NO_THROW(device = LearningModelDevice(LearningModelDeviceKind::DirectX));
|
||||
|
||||
LearningModelSession session(nullptr);
|
||||
if (DeviceHelpers::IsFloat16Supported(device))
|
||||
{
|
||||
EXPECT_NO_THROW(session = LearningModelSession(model, device));
|
||||
}
|
||||
else
|
||||
{
|
||||
EXPECT_THROW_SPECIFIC(
|
||||
session = LearningModelSession(model, device),
|
||||
winrt::hresult_error,
|
||||
[](const winrt::hresult_error& e) -> bool
|
||||
{
|
||||
return e.code() == DXGI_ERROR_UNSUPPORTED;
|
||||
});
|
||||
}
|
||||
|
||||
return session;
|
||||
}
|
||||
|
||||
TEST_F(LearningModelSessionAPITests, CreateSessionWithCastToFloat16InModel)
|
||||
{
|
||||
// load a model
|
||||
EXPECT_NO_THROW(LoadModel(L"fp16-truncate-with-cast.onnx"));
|
||||
|
||||
CreateSession(m_model);
|
||||
}
|
||||
|
||||
TEST_F(LearningModelSessionAPITests, DISABLED_CreateSessionWithFloat16InitializersInModel)
|
||||
{
|
||||
// Disabled due to https://microsoft.visualstudio.com/DefaultCollection/OS/_workitems/edit/21624720:
|
||||
// Model fails to resolve due to ORT using incorrect IR version within partition
|
||||
|
||||
// load a model
|
||||
EXPECT_NO_THROW(LoadModel(L"fp16-initializer.onnx"));
|
||||
|
||||
CreateSession(m_model);
|
||||
}
|
||||
|
||||
static void EvaluateSessionAndCloseModel(
|
||||
LearningModelDeviceKind kind,
|
||||
bool close_model_on_session_creation)
|
||||
{
|
||||
auto shape = std::vector<int64_t>{ 1, 1000 };
|
||||
|
||||
auto model = ProtobufHelpers::CreateModel(TensorKind::Float, shape, 1000);
|
||||
|
||||
auto device = LearningModelDevice(kind);
|
||||
auto options = LearningModelSessionOptions();
|
||||
|
||||
// close the model on session creation
|
||||
options.CloseModelOnSessionCreation(close_model_on_session_creation);
|
||||
|
||||
// ensure you can create a session from the model
|
||||
LearningModelSession session(nullptr);
|
||||
|
||||
EXPECT_NO_THROW(session = LearningModelSession(model, device, options));
|
||||
|
||||
std::vector<float> input(1000);
|
||||
std::iota(std::begin(input), std::end(input), 0.0f);
|
||||
auto tensor_input = TensorFloat::CreateFromShapeArrayAndDataArray(shape, input);
|
||||
auto binding = LearningModelBinding(session);
|
||||
binding.Bind(L"input", tensor_input);
|
||||
|
||||
LearningModelEvaluationResult result(nullptr);
|
||||
EXPECT_NO_THROW(result = session.Evaluate(binding, L""));
|
||||
|
||||
if (close_model_on_session_creation)
|
||||
{
|
||||
// ensure that the model has been closed
|
||||
EXPECT_THROW_SPECIFIC(
|
||||
LearningModelSession(model, device, options),
|
||||
winrt::hresult_error,
|
||||
[](const winrt::hresult_error& e) -> bool
|
||||
{
|
||||
return e.code() == E_INVALIDARG;
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
EXPECT_NO_THROW(LearningModelSession(model, device, options));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(LearningModelSessionAPITests, EvaluateSessionAndCloseModel)
|
||||
{
|
||||
EXPECT_NO_THROW(::EvaluateSessionAndCloseModel(LearningModelDeviceKind::Cpu, true));
|
||||
EXPECT_NO_THROW(::EvaluateSessionAndCloseModel(LearningModelDeviceKind::Cpu, false));
|
||||
}
|
||||
|
||||
TEST_F(LearningModelSessionAPITests, CloseSession)
|
||||
{
|
||||
EXPECT_NO_THROW(LoadModel(L"model.onnx"));
|
||||
LearningModelSession session = nullptr;
|
||||
|
||||
/*
|
||||
HANDLE currentProcessHandle = NULL;
|
||||
try
|
||||
{
|
||||
currentProcessHandle = GetCurrentProcess();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
VERIFY_FAIL(L"Failed to get current process handle.");
|
||||
}
|
||||
PROCESS_MEMORY_COUNTERS pmc = { 0 };
|
||||
SIZE_T beforeSessionCloseWorkingSetSize = 0;
|
||||
SIZE_T afterSessionCloseWorkingSetSize = 0;
|
||||
bool getProcessMemoryInfoSuccess = false;
|
||||
*/
|
||||
EXPECT_NO_THROW(session = LearningModelSession(m_model));
|
||||
|
||||
/*
|
||||
// Get the current process memory info after session creation.
|
||||
getProcessMemoryInfoSuccess = GetProcessMemoryInfo(currentProcessHandle, &pmc, sizeof(pmc));
|
||||
if (!getProcessMemoryInfoSuccess)
|
||||
{
|
||||
VERIFY_FAIL(L"Failed to get current process memory info.");
|
||||
}
|
||||
beforeSessionCloseWorkingSetSize = pmc.WorkingSetSize;
|
||||
pmc = { 0 };
|
||||
*/
|
||||
EXPECT_NO_THROW(session.Close());
|
||||
|
||||
/*
|
||||
Bug 23659026: Working set difference tolerance is too tight for LearningModelSessionAPITests::CloseSession
|
||||
https://microsoft.visualstudio.com/OS/_workitems/edit/23659026
|
||||
|
||||
// Check that working set size has dropped after session close
|
||||
getProcessMemoryInfoSuccess = GetProcessMemoryInfo(currentProcessHandle, &pmc, sizeof(pmc));
|
||||
if (!getProcessMemoryInfoSuccess)
|
||||
{
|
||||
VERIFY_FAIL(L"Failed to get current process memory info.");
|
||||
}
|
||||
afterSessionCloseWorkingSetSize = pmc.WorkingSetSize;
|
||||
pmc = { 0 };
|
||||
|
||||
// expected working set difference of session close. It is approximately 2x the size of the weights of model.onnx
|
||||
// there needs to be a tolerance because the working set difference varies from run to run.
|
||||
|
||||
// Bug 23739697: Closing Session API in LearningModelSessionAPITests::CloseSession doesn't always result in ~2x working set memory reduction.
|
||||
// https://microsoft.visualstudio.com/OS/_workitems/edit/23739697
|
||||
float tolerance = 0.4f;
|
||||
int64_t expectedWorkingSetDifference = 9662464;
|
||||
VERIFY_IS_LESS_THAN(expectedWorkingSetDifference - (beforeSessionCloseWorkingSetSize - afterSessionCloseWorkingSetSize), expectedWorkingSetDifference * tolerance);
|
||||
*/
|
||||
|
||||
// verify that model still has metadata info after session close
|
||||
std::wstring author(m_model.Author());
|
||||
EXPECT_EQ(author, L"onnx-caffe2");
|
||||
|
||||
// verify that session throws RO_E_CLOSED error
|
||||
std::vector<float> input(1 * 3 * 224 * 224, 0);
|
||||
std::vector<int64_t> shape = { 1, 3, 224, 224 };
|
||||
auto tensor_input = TensorFloat::CreateFromShapeArrayAndDataArray(shape, input);
|
||||
EXPECT_THROW_SPECIFIC(LearningModelBinding binding(session),
|
||||
winrt::hresult_error,
|
||||
[](const winrt::hresult_error &e) -> bool
|
||||
{
|
||||
return e.code() == RO_E_CLOSED;
|
||||
});
|
||||
}
|
||||
12
winml/test/api/models/fp16-initializer.onnx
Normal file
12
winml/test/api/models/fp16-initializer.onnx
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
sheilk:Š
|
||||
6
|
||||
fp16_initializercast_to_float_output"Cast*
|
||||
to
|
||||
!
|
||||
X
|
||||
cast_to_float_outputY"Add*
|
||||
*Bfp16_initializerZ
|
||||
X
|
||||
b
|
||||
Y
|
||||
B
|
||||
12
winml/test/api/models/fp16-truncate-with-cast.onnx
Normal file
12
winml/test/api/models/fp16-truncate-with-cast.onnx
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
sheilk:r
|
||||
,
|
||||
Xcast_to_float16_output"Cast*
|
||||
to
|
||||
|
||||
,
|
||||
cast_to_float16_outputY"Cast*
|
||||
to Z
|
||||
X
|
||||
b
|
||||
Y
|
||||
B
|
||||
11
winml/test/api/models/id-tensor-string.onnx
Normal file
11
winml/test/api/models/id-tensor-string.onnx
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
dwayner:>
|
||||
|
||||
XYIdentity"IdentityZ
|
||||
X
|
||||
|
||||
|
||||
b
|
||||
Y
|
||||
|
||||
|
||||
B
|
||||
BIN
winml/test/collateral/images/100x100.png
Normal file
BIN
winml/test/collateral/images/100x100.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 362 B |
BIN
winml/test/collateral/images/227x227.png
Normal file
BIN
winml/test/collateral/images/227x227.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 791 B |
6
winml/test/collateral/images/LICENSE.md
Normal file
6
winml/test/collateral/images/LICENSE.md
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
# Licenses
|
||||
|
||||
| Image | Source | License |
|
||||
| ----------- | ---------- | ----------- |
|
||||
| [fish_720](fish_720.png), [fish](fish.png) | https://commons.wikimedia.org/wiki/File:Tinca_tinca.jpeg | CC Attribution 3.0 Unported |
|
||||
| [kitten_224](kitten_224.png) | https://clipart.info/42-cat-png-image-download-picture-kitten-8177 | CC BY 4.0 |
|
||||
BIN
winml/test/collateral/images/fish.png
Normal file
BIN
winml/test/collateral/images/fish.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 131 KiB |
BIN
winml/test/collateral/images/fish_720.png
Normal file
BIN
winml/test/collateral/images/fish_720.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.2 MiB |
BIN
winml/test/collateral/images/kitten_224.png
Normal file
BIN
winml/test/collateral/images/kitten_224.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 55 KiB |
27
winml/test/collateral/models/Add_ImageNet1920.onnx
Normal file
27
winml/test/collateral/models/Add_ImageNet1920.onnx
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
OnnxMLTools
|
||||
0.1.0.0000"onnxml:Ä
|
||||
/
|
||||
|
||||
input_39:0
|
||||
|
||||
input_40:0add_3/add:0Add"Addkeras_Add_ImageNet_smallZ&
|
||||
|
||||
input_39:0
|
||||
|
||||
|
||||
|
||||
¸
|
||||
€Z&
|
||||
|
||||
input_40:0
|
||||
|
||||
|
||||
|
||||
¸
|
||||
€b'
|
||||
add_3/add:0
|
||||
|
||||
|
||||
|
||||
¸
|
||||
€B
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
OnnxMLTools
|
||||
0.1.0.0000"onnxml:í
|
||||
%
|
||||
input_39
|
||||
input_40add_3Add"Addkeras_Add_ImageNet_smallZ$
|
||||
input_39
|
||||
|
||||
|
||||
|
||||
¸
|
||||
€Z$
|
||||
input_40
|
||||
|
||||
|
||||
|
||||
¸
|
||||
€b^
|
||||
add_3U
|
||||
LH
|
||||
|
||||
DATA_BATCH
|
||||
DATA_CHANNEL
|
||||
¸DATA_FEATURE
|
||||
€DATA_FEATURE2IMAGEBr
|
||||
Image.BitmapPixelFormatBgr8r
|
||||
Image.ColorSpaceGammaLinearr-
|
||||
Image.NominalPixelRangeNominalRange_0_255
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
OnnxMLTools
|
||||
0.1.0.0000"onnxml:í
|
||||
%
|
||||
input_39
|
||||
input_40add_3Add"Addkeras_Add_ImageNet_smallZ$
|
||||
input_39
|
||||
|
||||
|
||||
|
||||
¸
|
||||
€Z$
|
||||
input_40
|
||||
|
||||
|
||||
|
||||
¸
|
||||
€b^
|
||||
add_3U
|
||||
LH
|
||||
|
||||
DATA_BATCH
|
||||
DATA_CHANNEL
|
||||
¸DATA_FEATURE
|
||||
€DATA_FEATURE2IMAGEBr
|
||||
Image.BitmapPixelFormatBgr8r
|
||||
Image.ColorSpaceGammaSRGBr)
|
||||
Image.NominalPixelRangeNormalized_0_1
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
OnnxMLTools
|
||||
0.1.0.0000"onnxml:·
|
||||
%
|
||||
input_39
|
||||
input_40add_3Add"Addkeras_Add_ImageNet_smallZ$
|
||||
input_39
|
||||
|
||||
|
||||
|
||||
¸
|
||||
€Z$
|
||||
input_40
|
||||
|
||||
|
||||
|
||||
¸
|
||||
€b(
|
||||
add_3
|
||||
|
||||
|
||||
|
||||
¸
|
||||
€2IMAGEBr
|
||||
Image.BitmapPixelFormatBgr8r
|
||||
Image.ColorSpaceGammaSRGBr-
|
||||
Image.NominalPixelRangeNominalRange_0_255
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
OnnxMLTools
|
||||
0.1.0.0000"onnxml:í
|
||||
%
|
||||
input_39
|
||||
input_40add_3Add"Addkeras_Add_ImageNet_smallZ$
|
||||
input_39
|
||||
|
||||
|
||||
|
||||
¸
|
||||
€Z$
|
||||
input_40
|
||||
|
||||
|
||||
|
||||
¸
|
||||
€b^
|
||||
add_3U
|
||||
LH
|
||||
|
||||
DATA_BATCH
|
||||
DATA_CHANNEL
|
||||
¸DATA_FEATURE
|
||||
€DATA_FEATURE2IMAGEBr
|
||||
Image.BitmapPixelFormatBgr8r
|
||||
Image.ColorSpaceGammaSRGBr.
|
||||
Image.NominalPixelRangeNominalRange_16_235
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
OnnxMLTools
|
||||
0.1.0.0000"onnxml:í
|
||||
%
|
||||
input_39
|
||||
input_40add_3Add"Addkeras_Add_ImageNet_smallZ$
|
||||
input_39
|
||||
|
||||
|
||||
|
||||
¸
|
||||
€Z$
|
||||
input_40
|
||||
|
||||
|
||||
|
||||
¸
|
||||
€b^
|
||||
add_3U
|
||||
LH
|
||||
|
||||
DATA_BATCH
|
||||
DATA_CHANNEL
|
||||
¸DATA_FEATURE
|
||||
€DATA_FEATURE2IMAGEBr
|
||||
Image.BitmapPixelFormatBgr8r
|
||||
Image.ColorSpaceGammaSRGBr)
|
||||
Image.NominalPixelRangeNormalized_1_1
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
OnnxMLTools
|
||||
0.1.0.0000"onnxml:·
|
||||
%
|
||||
input_39
|
||||
input_40add_3Add"Addkeras_Add_ImageNet_smallZ$
|
||||
input_39
|
||||
|
||||
|
||||
|
||||
¸
|
||||
€Z$
|
||||
input_40
|
||||
|
||||
|
||||
|
||||
¸
|
||||
€b(
|
||||
add_3
|
||||
|
||||
|
||||
|
||||
¸
|
||||
€2IMAGEBr
|
||||
Image.BitmapPixelFormatBgra8r
|
||||
Image.ColorSpaceGammaSRGBr-
|
||||
Image.NominalPixelRangeNominalRange_0_255
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
OnnxMLTools
|
||||
0.1.0.0000"onnxml:·
|
||||
%
|
||||
input_39
|
||||
input_40add_3Add"Addkeras_Add_ImageNet_smallZ$
|
||||
input_39
|
||||
|
||||
|
||||
|
||||
¸
|
||||
€Z$
|
||||
input_40
|
||||
|
||||
|
||||
|
||||
¸
|
||||
€b(
|
||||
add_3
|
||||
|
||||
|
||||
|
||||
¸
|
||||
€2IMAGEBr
|
||||
Image.BitmapPixelFormatRgb8r
|
||||
Image.ColorSpaceGammaSRGBr-
|
||||
Image.NominalPixelRangeNominalRange_0_255
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
OnnxMLTools
|
||||
0.1.0.0000"onnxml:·
|
||||
%
|
||||
input_39
|
||||
input_40add_3Add"Addkeras_Add_ImageNet_smallZ$
|
||||
input_39
|
||||
|
||||
|
||||
|
||||
¸
|
||||
€Z$
|
||||
input_40
|
||||
|
||||
|
||||
|
||||
¸
|
||||
€b(
|
||||
add_3
|
||||
|
||||
|
||||
|
||||
¸
|
||||
€2IMAGEBr
|
||||
Image.BitmapPixelFormatRgba8r
|
||||
Image.ColorSpaceGammaSRGBr-
|
||||
Image.NominalPixelRangeNominalRange_0_255
|
||||
65
winml/test/collateral/models/LICENSE.md
Normal file
65
winml/test/collateral/models/LICENSE.md
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
# Licenses
|
||||
|
||||
| Model | Source | License |
|
||||
| ----------- | ---------- | ----------- |
|
||||
| SqueezeNet | https://github.com/DeepScale/SqueezeNet | BSD 2-Clause |
|
||||
| Starry Night | https://github.com/pytorch/examples/tree/master/fast_neural_style | BSD 3-Clause |
|
||||
|
||||
SqueezeNet license:
|
||||
|
||||
```
|
||||
BSD LICENSE.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted
|
||||
provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this list of conditions
|
||||
and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions
|
||||
and the following disclaimer in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
|
||||
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
|
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
||||
IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
```
|
||||
|
||||
Starry Night license:
|
||||
|
||||
```
|
||||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2017,
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
```
|
||||
Binary file not shown.
13
winml/test/collateral/models/bad_names.onnx
Normal file
13
winml/test/collateral/models/bad_names.onnx
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
kiyoung:^
|
||||
*
|
||||
|
||||
input/nameoutput:0Identity"IdentityZ
|
||||
|
||||
input/name
|
||||
|
||||
|
||||
b
|
||||
output:0
|
||||
|
||||
|
||||
|
||||
12
winml/test/collateral/models/castmap-int64.onnx
Normal file
12
winml/test/collateral/models/castmap-int64.onnx
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
dwayner:N
|
||||
$
|
||||
XYCastMap"CastMap:
|
||||
ai.onnx.mlZ
|
||||
X*
|
||||
|
||||
|
||||
b
|
||||
Y
|
||||
|
||||
|
||||
B
|
||||
BIN
winml/test/collateral/models/conv-float.onnx
Normal file
BIN
winml/test/collateral/models/conv-float.onnx
Normal file
Binary file not shown.
BIN
winml/test/collateral/models/dictvectorizer-int64.onnx
Normal file
BIN
winml/test/collateral/models/dictvectorizer-int64.onnx
Normal file
Binary file not shown.
BIN
winml/test/collateral/models/dictvectorizer-string.onnx
Normal file
BIN
winml/test/collateral/models/dictvectorizer-string.onnx
Normal file
Binary file not shown.
BIN
winml/test/collateral/models/foo.onnx
Normal file
BIN
winml/test/collateral/models/foo.onnx
Normal file
Binary file not shown.
BIN
winml/test/collateral/models/foo_truncated.onnx
Normal file
BIN
winml/test/collateral/models/foo_truncated.onnx
Normal file
Binary file not shown.
33
winml/test/collateral/models/free_dimensional_imageDes.onnx
Normal file
33
winml/test/collateral/models/free_dimensional_imageDes.onnx
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
OnnxMLTools
|
||||
0.1.0.0000"onnxml:¬
|
||||
/
|
||||
|
||||
input_39:0
|
||||
|
||||
input_40:0add_3/add:0Add"Addkeras_Add_ImageNet_smallZs
|
||||
|
||||
input_39:0e
|
||||
\X
|
||||
|
||||
DATA_BATCH
|
||||
DATA_CHANNEL
|
||||
ÿÿÿÿÿÿÿÿÿDATA_FEATURE
|
||||
ÿÿÿÿÿÿÿÿÿDATA_FEATURE2IMAGEZs
|
||||
|
||||
input_40:0e
|
||||
\X
|
||||
|
||||
DATA_BATCH
|
||||
DATA_CHANNEL
|
||||
ÿÿÿÿÿÿÿÿÿDATA_FEATURE
|
||||
ÿÿÿÿÿÿÿÿÿDATA_FEATURE2IMAGEbu
|
||||
add_3/add:0f
|
||||
\X
|
||||
|
||||
DATA_BATCH
|
||||
DATA_CHANNEL
|
||||
ÿÿÿÿÿÿÿÿÿDATA_FEATURE
|
||||
ÿÿÿÿÿÿÿÿÿDATA_FEATURE2TENSORBr
|
||||
Image.BitmapPixelFormatBgr8r
|
||||
Image.ColorSpaceGammaSRGBr-
|
||||
Image.NominalPixelRangeNominalRange_0_255
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
OnnxMLTools
|
||||
0.1.0.0000"onnxml:ô
|
||||
/
|
||||
|
||||
input_39:0
|
||||
|
||||
input_40:0add_3/add:0Add"Addkeras_Add_ImageNet_smallZ6
|
||||
|
||||
input_39:0(
|
||||
&"
|
||||
|
||||
|
||||
ÿÿÿÿÿÿÿÿÿ
|
||||
ÿÿÿÿÿÿÿÿÿZ6
|
||||
|
||||
input_40:0(
|
||||
&"
|
||||
|
||||
|
||||
ÿÿÿÿÿÿÿÿÿ
|
||||
ÿÿÿÿÿÿÿÿÿb7
|
||||
add_3/add:0(
|
||||
&"
|
||||
|
||||
|
||||
ÿÿÿÿÿÿÿÿÿ
|
||||
ÿÿÿÿÿÿÿÿÿB
|
||||
BIN
winml/test/collateral/models/mnist.onnx
Normal file
BIN
winml/test/collateral/models/mnist.onnx
Normal file
Binary file not shown.
BIN
winml/test/collateral/models/modelWith2MetaData.onnx
Normal file
BIN
winml/test/collateral/models/modelWith2MetaData.onnx
Normal file
Binary file not shown.
BIN
winml/test/collateral/models/modelWithMetaData.onnx
Normal file
BIN
winml/test/collateral/models/modelWithMetaData.onnx
Normal file
Binary file not shown.
BIN
winml/test/collateral/models/mul.onnx
Normal file
BIN
winml/test/collateral/models/mul.onnx
Normal file
Binary file not shown.
11
winml/test/collateral/models/relu.onnx
Normal file
11
winml/test/collateral/models/relu.onnx
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
justoeck:0
|
||||
|
||||
XY"ReluZ
|
||||
X
|
||||
|
||||
|
||||
b
|
||||
Y
|
||||
|
||||
|
||||
B
|
||||
Binary file not shown.
BIN
winml/test/collateral/models/squeezenet_tensor_input.onnx
Normal file
BIN
winml/test/collateral/models/squeezenet_tensor_input.onnx
Normal file
Binary file not shown.
BIN
winml/test/collateral/models/starry-night-fp16.onnx
Normal file
BIN
winml/test/collateral/models/starry-night-fp16.onnx
Normal file
Binary file not shown.
BIN
winml/test/collateral/models/zipmap-int64.onnx
Normal file
BIN
winml/test/collateral/models/zipmap-int64.onnx
Normal file
Binary file not shown.
BIN
winml/test/collateral/models/zipmap-string.onnx
Normal file
BIN
winml/test/collateral/models/zipmap-string.onnx
Normal file
Binary file not shown.
335
winml/test/common/SqueezeNetValidator.cpp
Normal file
335
winml/test/common/SqueezeNetValidator.cpp
Normal file
|
|
@ -0,0 +1,335 @@
|
|||
#include "SqueezeNetValidator.h"
|
||||
#include "protobufHelpers.h"
|
||||
#include "fileHelpers.h"
|
||||
#include <gtest/gtest.h>
|
||||
#include <winrt/Windows.Media.h>
|
||||
#include <winrt/Windows.Graphics.Imaging.h>
|
||||
#include <winrt/Windows.Storage.h>
|
||||
#include <winrt/Windows.Storage.Streams.h>
|
||||
|
||||
#include "WinMLProfiler.h"
|
||||
|
||||
// using namespace winrt::Windows::Foundation;
|
||||
using namespace winrt::Windows::AI::MachineLearning;
|
||||
using namespace winrt::Windows::Foundation::Collections;
|
||||
using namespace winrt::Windows::Graphics::Imaging;
|
||||
using namespace winrt::Windows::Media;
|
||||
using namespace winrt::Windows::Storage;
|
||||
using namespace winrt::Windows::Storage::Streams;
|
||||
|
||||
namespace WinML::Engine::Test{
|
||||
|
||||
enum WINML_RUNTIME_TEST_PERF
|
||||
{
|
||||
PREP_TEST = 0,
|
||||
CREATE_RUNTIME,
|
||||
LOAD_MODEL,
|
||||
CREATE_EVAL_CONTEXT,
|
||||
RUN_TEST,
|
||||
BIND_VALUE,
|
||||
EVAL_MODEL,
|
||||
EVAL_MODEL_FIRST_RUN,
|
||||
kCount
|
||||
};
|
||||
|
||||
static std::vector<std::string> WINML_RUNTIME_TEST_PERF_NAMES =
|
||||
{
|
||||
"PREP TEST ",
|
||||
" CREATE RUNTIME ",
|
||||
" LOAD MODEL ",
|
||||
" CREATE EVAL CONTEXT",
|
||||
"RUN TEST ",
|
||||
" BIND VALUE ",
|
||||
" EVAL MODEL ",
|
||||
" EVAL MODEL 1st Run "
|
||||
};
|
||||
|
||||
#define MAX_PROFILING_LOOP 100
|
||||
Profiler<WINML_RUNTIME_TEST_PERF> g_RuntimeProfiler;
|
||||
|
||||
|
||||
static void BindImage(
|
||||
LearningModelBinding binding,
|
||||
const wchar_t* name,
|
||||
const wchar_t* fullImagePath,
|
||||
bool bindAsInspectable = false)
|
||||
{
|
||||
auto imagefile = StorageFile::GetFileFromPathAsync(fullImagePath).get();
|
||||
auto stream = imagefile.OpenAsync(FileAccessMode::Read).get();
|
||||
auto decoder = BitmapDecoder::CreateAsync(stream).get();
|
||||
auto softwareBitmap = decoder.GetSoftwareBitmapAsync().get();
|
||||
auto frame = VideoFrame::CreateWithSoftwareBitmap(softwareBitmap);
|
||||
|
||||
if (bindAsInspectable)
|
||||
{
|
||||
EXPECT_NO_THROW(binding.Bind(name, frame));
|
||||
}
|
||||
else
|
||||
{
|
||||
auto imagetensor = ImageFeatureValue::CreateFromVideoFrame(frame);
|
||||
EXPECT_NO_THROW(binding.Bind(name, imagetensor));
|
||||
}
|
||||
}
|
||||
|
||||
static void BindTensor(
|
||||
LearningModelBinding binding,
|
||||
const wchar_t* name,
|
||||
ITensor inputTensor,
|
||||
bool bindAsInspectable = false)
|
||||
{
|
||||
EXPECT_TRUE(inputTensor != nullptr);
|
||||
|
||||
if (bindAsInspectable)
|
||||
{
|
||||
EXPECT_NO_THROW(binding.Bind(name, inputTensor.as<TensorFloat>().GetAsVectorView()));
|
||||
}
|
||||
else
|
||||
{
|
||||
EXPECT_NO_THROW(binding.Bind(name, inputTensor));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
ITensor BindOutput(
|
||||
OutputBindingStrategy strategy,
|
||||
LearningModelBinding binding,
|
||||
const wchar_t* name,
|
||||
const IVectorView<int64_t> shape = nullptr
|
||||
)
|
||||
{
|
||||
ITensor outputTensor = nullptr;
|
||||
switch (strategy)
|
||||
{
|
||||
case OutputBindingStrategy::Bound:
|
||||
outputTensor = T::Create(shape);
|
||||
EXPECT_NO_THROW(binding.Bind(name, outputTensor));
|
||||
break;
|
||||
case OutputBindingStrategy::Empty:
|
||||
outputTensor = T::Create();
|
||||
EXPECT_NO_THROW(binding.Bind(name, outputTensor));
|
||||
break;
|
||||
case OutputBindingStrategy::Unbound:
|
||||
__fallthrough;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return outputTensor;
|
||||
}
|
||||
|
||||
ImageFeatureValue BindImageOutput(
|
||||
OutputBindingStrategy strategy,
|
||||
LearningModelBinding binding,
|
||||
const wchar_t* name
|
||||
)
|
||||
{
|
||||
ImageFeatureValue outputTensor = nullptr;
|
||||
switch (strategy)
|
||||
{
|
||||
case OutputBindingStrategy::Bound:
|
||||
{
|
||||
SoftwareBitmap bitmap(BitmapPixelFormat::Bgra8, 720, 720);
|
||||
VideoFrame frame = VideoFrame::CreateWithSoftwareBitmap(bitmap);
|
||||
outputTensor = ImageFeatureValue::CreateFromVideoFrame(frame);
|
||||
EXPECT_NO_THROW(binding.Bind(name, outputTensor));
|
||||
break;
|
||||
}
|
||||
case OutputBindingStrategy::Unbound:
|
||||
__fallthrough;
|
||||
}
|
||||
|
||||
return outputTensor;
|
||||
}
|
||||
|
||||
|
||||
void ModelValidator::FnsCandy16(
|
||||
std::string instance,
|
||||
LearningModelDeviceKind deviceKind,
|
||||
OutputBindingStrategy outputBindingStrategy,
|
||||
bool bindInputsAsIInspectable,
|
||||
float dataTolerance)
|
||||
{
|
||||
WINML_PROFILING_START(g_RuntimeProfiler, WINML_RUNTIME_TEST_PERF::PREP_TEST);
|
||||
// file name strings
|
||||
static wchar_t* modelFileName = L"winmlperf_coreml_FNS-Candy_prerelease_fp16.onnx";
|
||||
static wchar_t* inputDataImageFileName = L"fish_720.png";
|
||||
static wchar_t* outputDataFileName = L"output.png";
|
||||
static wchar_t* inputBindingName = L"inputImage";
|
||||
static const wchar_t* outputDataBindingName = L"outputImage";
|
||||
|
||||
auto modulePath = FileHelpers::GetModulePath();
|
||||
auto fullModelPath = modulePath + modelFileName;
|
||||
auto outputFileName = modulePath + outputDataFileName;
|
||||
WINML_PROFILING_STOP(g_RuntimeProfiler, WINML_RUNTIME_TEST_PERF::PREP_TEST);
|
||||
|
||||
WINML_PROFILING_START(g_RuntimeProfiler, WINML_RUNTIME_TEST_PERF::LOAD_MODEL);
|
||||
// WinML model creation
|
||||
LearningModel model = nullptr;
|
||||
EXPECT_NO_THROW(model = LearningModel::LoadFromFilePath(fullModelPath));
|
||||
WINML_PROFILING_STOP(g_RuntimeProfiler, WINML_RUNTIME_TEST_PERF::LOAD_MODEL);
|
||||
|
||||
WINML_PROFILING_START(g_RuntimeProfiler, WINML_RUNTIME_TEST_PERF::RUN_TEST);
|
||||
WINML_PROFILING_START(g_RuntimeProfiler, WINML_RUNTIME_TEST_PERF::CREATE_EVAL_CONTEXT);
|
||||
LearningModelSession modelSession = nullptr;
|
||||
EXPECT_NO_THROW(modelSession = LearningModelSession(model, LearningModelDevice(deviceKind)));
|
||||
WINML_PROFILING_STOP(g_RuntimeProfiler, WINML_RUNTIME_TEST_PERF::CREATE_EVAL_CONTEXT);
|
||||
|
||||
WINML_PROFILING_START(g_RuntimeProfiler, WINML_RUNTIME_TEST_PERF::BIND_VALUE);
|
||||
LearningModelBinding modelBinding(modelSession);
|
||||
auto fullImagePath = modulePath + inputDataImageFileName;
|
||||
BindImage(modelBinding, inputBindingName, fullImagePath.c_str(), bindInputsAsIInspectable);
|
||||
|
||||
// create the tensor for the actual output
|
||||
auto output = model.OutputFeatures().First().Current();
|
||||
EXPECT_TRUE(output.Kind() == LearningModelFeatureKind::Tensor);
|
||||
|
||||
auto shape = winrt::single_threaded_vector(std::vector<int64_t> {1, 1});
|
||||
auto outputTensor = BindImageOutput(outputBindingStrategy, modelBinding, outputDataBindingName);
|
||||
WINML_PROFILING_STOP(g_RuntimeProfiler, WINML_RUNTIME_TEST_PERF::BIND_VALUE);
|
||||
|
||||
// Evaluate the model
|
||||
WINML_PROFILING_START(g_RuntimeProfiler, WINML_RUNTIME_TEST_PERF::EVAL_MODEL_FIRST_RUN);
|
||||
std::cout << "Calling EvaluateSync on instance" << instance << "\n";
|
||||
LearningModelEvaluationResult result = nullptr;
|
||||
EXPECT_NO_THROW(result = modelSession.Evaluate(modelBinding, {}));
|
||||
WINML_PROFILING_STOP(g_RuntimeProfiler, WINML_RUNTIME_TEST_PERF::EVAL_MODEL_FIRST_RUN);
|
||||
|
||||
// Get results
|
||||
if (outputBindingStrategy == OutputBindingStrategy::Unbound)
|
||||
{
|
||||
// When output binding strategy is unbound, the output tensor was not set on bind.
|
||||
// Therefore, we need to retrieve it from the LearnignModelEvaluationResult
|
||||
// TODO: is this right? outputTensorT is unused...
|
||||
/*auto outputTensorT = */result.Outputs().Lookup(outputDataBindingName).as<TensorFloat16Bit>();
|
||||
}
|
||||
else
|
||||
{
|
||||
EXPECT_EQ(result.Outputs().Lookup(outputDataBindingName), outputTensor);
|
||||
|
||||
auto softwareBitmap = outputTensor.VideoFrame().SoftwareBitmap();
|
||||
|
||||
auto folder = StorageFolder::GetFolderFromPathAsync(modulePath.c_str()).get();
|
||||
auto imagefile = folder.CreateFileAsync(outputDataFileName, CreationCollisionOption::ReplaceExisting).get();
|
||||
auto stream = imagefile.OpenAsync(FileAccessMode::ReadWrite).get();
|
||||
auto encoder = BitmapEncoder::CreateAsync(BitmapEncoder::JpegEncoderId(), stream).get();
|
||||
encoder.SetSoftwareBitmap(softwareBitmap);
|
||||
encoder.FlushAsync();
|
||||
|
||||
}
|
||||
WINML_PROFILING_STOP(g_RuntimeProfiler, WINML_RUNTIME_TEST_PERF::RUN_TEST);
|
||||
}
|
||||
|
||||
void ModelValidator::SqueezeNet(
|
||||
std::string instance,
|
||||
LearningModelDeviceKind deviceKind,
|
||||
float dataTolerance,
|
||||
bool bindAsImage,
|
||||
OutputBindingStrategy outputBindingStrategy,
|
||||
bool bindInputsAsIInspectable)
|
||||
{
|
||||
g_RuntimeProfiler.Enable(ProfilerType::CPU);
|
||||
g_RuntimeProfiler.Enable(ProfilerType::GPU);
|
||||
g_RuntimeProfiler.Reset(ProfilerType::CPU);
|
||||
g_RuntimeProfiler.Reset(ProfilerType::GPU);
|
||||
|
||||
WINML_PROFILING_START(g_RuntimeProfiler, WINML_RUNTIME_TEST_PERF::PREP_TEST);
|
||||
// file name strings
|
||||
static wchar_t* modelFileName = L"model.onnx";
|
||||
static wchar_t* inputDataFileName = L"test_data_0_input.pb";
|
||||
static wchar_t* outputDataFileName = L"test_data_0_output.pb";
|
||||
static wchar_t* inputBindingName = L"data_0";
|
||||
static wchar_t* inputDataImageFileName = L"kitten_224.png";
|
||||
static const wchar_t* outputDataBindingName = L"softmaxout_1";
|
||||
|
||||
auto modulePath = FileHelpers::GetModulePath();
|
||||
auto fullModelPath = modulePath + modelFileName;
|
||||
auto outputFileName = modulePath + outputDataFileName;
|
||||
WINML_PROFILING_STOP(g_RuntimeProfiler, WINML_RUNTIME_TEST_PERF::PREP_TEST);
|
||||
|
||||
WINML_PROFILING_START(g_RuntimeProfiler, WINML_RUNTIME_TEST_PERF::LOAD_MODEL);
|
||||
// WinML model creation
|
||||
LearningModel model = nullptr;
|
||||
EXPECT_NO_THROW(model = LearningModel::LoadFromFilePath(fullModelPath));
|
||||
WINML_PROFILING_STOP(g_RuntimeProfiler, WINML_RUNTIME_TEST_PERF::LOAD_MODEL);
|
||||
|
||||
WINML_PROFILING_START(g_RuntimeProfiler, WINML_RUNTIME_TEST_PERF::RUN_TEST);
|
||||
WINML_PROFILING_START(g_RuntimeProfiler, WINML_RUNTIME_TEST_PERF::CREATE_EVAL_CONTEXT);
|
||||
LearningModelSession modelSession = nullptr;
|
||||
EXPECT_NO_THROW(modelSession = LearningModelSession(model, LearningModelDevice(deviceKind)));
|
||||
WINML_PROFILING_STOP(g_RuntimeProfiler, WINML_RUNTIME_TEST_PERF::CREATE_EVAL_CONTEXT);
|
||||
|
||||
WINML_PROFILING_START(g_RuntimeProfiler, WINML_RUNTIME_TEST_PERF::BIND_VALUE);
|
||||
LearningModelBinding modelBinding(modelSession);
|
||||
|
||||
if (bindAsImage)
|
||||
{
|
||||
std::wstring fullImagePath = modulePath + inputDataImageFileName;
|
||||
BindImage(modelBinding, inputBindingName, fullImagePath.c_str(), bindInputsAsIInspectable);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto inputDataPath = modulePath + inputDataFileName;
|
||||
auto inputTensor = ProtobufHelpers::LoadTensorFromProtobufFile(inputDataPath, false);
|
||||
BindTensor(modelBinding, inputBindingName, inputTensor, bindInputsAsIInspectable);
|
||||
}
|
||||
|
||||
// load up the expected output
|
||||
auto expectedResultsTensor = ProtobufHelpers::LoadTensorFromProtobufFile(outputFileName, false);
|
||||
EXPECT_TRUE(expectedResultsTensor != nullptr);
|
||||
|
||||
// create the tensor for the actual output
|
||||
auto output = model.OutputFeatures().First().Current();
|
||||
EXPECT_TRUE(output.Kind() == LearningModelFeatureKind::Tensor);
|
||||
|
||||
auto outputTensor = BindOutput<TensorFloat>(
|
||||
outputBindingStrategy, modelBinding, outputDataBindingName, expectedResultsTensor.Shape());
|
||||
WINML_PROFILING_STOP(g_RuntimeProfiler, WINML_RUNTIME_TEST_PERF::BIND_VALUE);
|
||||
|
||||
// Evaluate the model
|
||||
WINML_PROFILING_START(g_RuntimeProfiler, WINML_RUNTIME_TEST_PERF::EVAL_MODEL_FIRST_RUN);
|
||||
std::cout << "Calling EvaluateSync on instance" << instance << "\n";
|
||||
LearningModelEvaluationResult result = nullptr;
|
||||
EXPECT_NO_THROW(result = modelSession.Evaluate(modelBinding, {}));
|
||||
WINML_PROFILING_STOP(g_RuntimeProfiler, WINML_RUNTIME_TEST_PERF::EVAL_MODEL_FIRST_RUN);
|
||||
|
||||
// Get results
|
||||
if (outputBindingStrategy == OutputBindingStrategy::Unbound)
|
||||
{
|
||||
// When output binding strategy is unbound, the output tensor was not set on bind.
|
||||
// Therefore, we need to retrieve it from the LearnignModelEvaluationResult
|
||||
outputTensor = result.Outputs().Lookup(outputDataBindingName).as<ITensor>();
|
||||
}
|
||||
else
|
||||
{
|
||||
EXPECT_EQ(result.Outputs().Lookup(outputDataBindingName), outputTensor);
|
||||
}
|
||||
|
||||
auto outDataExpected = expectedResultsTensor.as<TensorFloat>().GetAsVectorView();
|
||||
auto outDataActual = outputTensor.as<TensorFloat>().GetAsVectorView();
|
||||
|
||||
EXPECT_TRUE(outDataActual.Size() == outDataExpected.Size());
|
||||
for (uint32_t i = 0; i < outDataActual.Size(); i++)
|
||||
{
|
||||
float delta = std::abs(outDataActual.GetAt(i) - outDataExpected.GetAt(i));
|
||||
if (delta > dataTolerance)
|
||||
{
|
||||
ADD_FAILURE() << "EXPECTED: " << outDataExpected.GetAt(i) << " , ACTUAL: " << outDataActual.GetAt(i)
|
||||
<< "instance " << instance << ", element " << i;
|
||||
|
||||
}
|
||||
}
|
||||
WINML_PROFILING_STOP(g_RuntimeProfiler, WINML_RUNTIME_TEST_PERF::RUN_TEST);
|
||||
|
||||
std::cout << "Profiling data:\n";
|
||||
for (int i = 0; i < WINML_RUNTIME_TEST_PERF::kCount; ++i)
|
||||
{
|
||||
std::cout << WINML_RUNTIME_TEST_PERF_NAMES[i]
|
||||
<< ": Time=" << g_RuntimeProfiler[i].GetAverage(CounterType::TIMER)
|
||||
<< "\tCPUUse(%%)=" << g_RuntimeProfiler[i].GetAverage(CounterType::CPU_USAGE)
|
||||
<< "\tAvgWorkingSetDelta(MB)=" << g_RuntimeProfiler[i].GetAverage(CounterType::WORKING_SET_USAGE)
|
||||
<< "\tMaxWorkingSetDelta(MB)=" << g_RuntimeProfiler[i].GetMax(CounterType::WORKING_SET_USAGE)
|
||||
<< "\tGPUUse(%%)=" << g_RuntimeProfiler[i].GetAverage(CounterType::GPU_USAGE)
|
||||
<< "\tGPUDedicatedMem(MB)=" << g_RuntimeProfiler[i].GetAverage(CounterType::GPU_DEDICATED_MEM_USAGE);
|
||||
}
|
||||
}
|
||||
}
|
||||
30
winml/test/common/SqueezeNetValidator.h
Normal file
30
winml/test/common/SqueezeNetValidator.h
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <std.h>
|
||||
|
||||
enum OutputBindingStrategy { Bound, Unbound, Empty };
|
||||
|
||||
namespace WinML::Engine::Test::ModelValidator
|
||||
{
|
||||
void FnsCandy16(
|
||||
std::string instance,
|
||||
winrt::Windows::AI::MachineLearning::LearningModelDeviceKind deviceKind,
|
||||
OutputBindingStrategy outputBindingStrategy,
|
||||
bool bindInputsAsIInspectable,
|
||||
float dataTolerance = false);
|
||||
|
||||
void SqueezeNet(
|
||||
std::string instance,
|
||||
winrt::Windows::AI::MachineLearning::LearningModelDeviceKind deviceKind,
|
||||
float dataTolerance,
|
||||
bool bindAsImage = false,
|
||||
OutputBindingStrategy outputBindingStrategy = OutputBindingStrategy::Bound,
|
||||
bool bindInputsAsIInspectable = false
|
||||
);
|
||||
}
|
||||
75
winml/test/common/dllload.cpp
Normal file
75
winml/test/common/dllload.cpp
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
#include "Std.h"
|
||||
#include "fileHelpers.h"
|
||||
#include <winstring.h>
|
||||
|
||||
extern "C"
|
||||
{
|
||||
HRESULT __stdcall OS_RoGetActivationFactory(HSTRING classId, GUID const& iid, void** factory) noexcept;
|
||||
}
|
||||
|
||||
#ifdef _M_IX86
|
||||
#pragma comment(linker, "/alternatename:_OS_RoGetActivationFactory@12=_RoGetActivationFactory@12")
|
||||
#else
|
||||
#pragma comment(linker, "/alternatename:OS_RoGetActivationFactory=RoGetActivationFactory")
|
||||
#endif
|
||||
|
||||
bool starts_with(std::wstring_view value, std::wstring_view match) noexcept
|
||||
{
|
||||
return 0 == value.compare(0, match.size(), match);
|
||||
}
|
||||
|
||||
HRESULT __stdcall WINRT_RoGetActivationFactory(HSTRING classId_hstring, GUID const& iid, void** factory) noexcept
|
||||
{
|
||||
*factory = nullptr;
|
||||
std::wstring_view name{ WindowsGetStringRawBuffer(classId_hstring, nullptr), WindowsGetStringLen(classId_hstring) };
|
||||
HMODULE library{ nullptr };
|
||||
|
||||
std::wstring winmlDllPath = FileHelpers::GetWinMLPath() + L"Windows.AI.MachineLearning.dll";
|
||||
|
||||
if (starts_with(name, L"Windows.AI.MachineLearning."))
|
||||
{
|
||||
const wchar_t* libPath = winmlDllPath.c_str();
|
||||
library = LoadLibraryW(libPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
return OS_RoGetActivationFactory(classId_hstring, iid, factory);
|
||||
}
|
||||
|
||||
if (!library)
|
||||
{
|
||||
return HRESULT_FROM_WIN32(GetLastError());
|
||||
}
|
||||
|
||||
using DllGetActivationFactory = HRESULT __stdcall(HSTRING classId, void** factory);
|
||||
auto call = reinterpret_cast<DllGetActivationFactory*>(GetProcAddress(library, "DllGetActivationFactory"));
|
||||
|
||||
if (!call)
|
||||
{
|
||||
HRESULT const hr = HRESULT_FROM_WIN32(GetLastError());
|
||||
WINRT_VERIFY(FreeLibrary(library));
|
||||
return hr;
|
||||
}
|
||||
|
||||
winrt::com_ptr<winrt::Windows::Foundation::IActivationFactory> activation_factory;
|
||||
HRESULT const hr = call(classId_hstring, activation_factory.put_void());
|
||||
|
||||
if (FAILED(hr))
|
||||
{
|
||||
WINRT_VERIFY(FreeLibrary(library));
|
||||
return hr;
|
||||
}
|
||||
|
||||
if (winrt::guid(iid) != winrt::guid_of<winrt::Windows::Foundation::IActivationFactory>())
|
||||
{
|
||||
return activation_factory->QueryInterface(iid, factory);
|
||||
}
|
||||
|
||||
*factory = activation_factory.detach();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
int32_t __stdcall WINRT_RoGetActivationFactory(void* classId, winrt::guid const& iid, void** factory) noexcept
|
||||
{
|
||||
return WINRT_RoGetActivationFactory((HSTRING)classId, (GUID)iid, factory);
|
||||
}
|
||||
67
winml/test/common/fileHelpers.cpp
Normal file
67
winml/test/common/fileHelpers.cpp
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
#include "std.h"
|
||||
#include "fileHelpers.h"
|
||||
#include "winrt/Windows.Media.h"
|
||||
#include "winrt/Windows.Storage.h"
|
||||
|
||||
EXTERN_C IMAGE_DOS_HEADER __ImageBase;
|
||||
|
||||
using namespace winrt;
|
||||
using namespace winrt::Windows::AI::MachineLearning;
|
||||
using namespace winrt::Windows::Graphics::Imaging;
|
||||
using namespace winrt::Windows::Storage;
|
||||
|
||||
namespace FileHelpers
|
||||
{
|
||||
std::wstring GetModulePath()
|
||||
{
|
||||
std::wstring val;
|
||||
wchar_t modulePath[MAX_PATH] = { 0 };
|
||||
GetModuleFileNameW((HINSTANCE)&__ImageBase, modulePath, _countof(modulePath));
|
||||
wchar_t drive[_MAX_DRIVE];
|
||||
wchar_t dir[_MAX_DIR];
|
||||
wchar_t filename[_MAX_FNAME];
|
||||
wchar_t ext[_MAX_EXT];
|
||||
_wsplitpath_s(modulePath, drive, _MAX_DRIVE, dir, _MAX_DIR, filename, _MAX_FNAME, ext, _MAX_EXT);
|
||||
|
||||
val = drive;
|
||||
val += dir;
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
std::wstring GetWinMLPath()
|
||||
{
|
||||
// bool inboxDll = false;
|
||||
// TODO Add command line parsing
|
||||
// if (SUCCEEDED(WEX::TestExecution::RuntimeParameters::TryGetValue(L"inbox", inboxDll)) && inboxDll)
|
||||
// {
|
||||
// return L"";
|
||||
// }
|
||||
return GetModulePath();
|
||||
}
|
||||
|
||||
|
||||
winrt::Windows::Graphics::Imaging::SoftwareBitmap GetSoftwareBitmapFromFile(const std::wstring& filePath)
|
||||
{
|
||||
auto storageFile = StorageFile::GetFileFromPathAsync(filePath).get();
|
||||
auto stream = storageFile.OpenAsync(FileAccessMode::Read).get();
|
||||
auto decoder = BitmapDecoder::CreateAsync(stream).get();
|
||||
IBitmapFrameWithSoftwareBitmap bitmapFrameWithSoftwareBitmap;
|
||||
decoder.as(bitmapFrameWithSoftwareBitmap);
|
||||
auto softwareBitmap = bitmapFrameWithSoftwareBitmap.GetSoftwareBitmapAsync(
|
||||
BitmapPixelFormat::Bgra8,
|
||||
BitmapAlphaMode::Ignore,
|
||||
BitmapTransform::BitmapTransform(),
|
||||
ExifOrientationMode::IgnoreExifOrientation,
|
||||
ColorManagementMode::DoNotColorManage
|
||||
).get();
|
||||
return softwareBitmap;
|
||||
}
|
||||
|
||||
ImageFeatureValue LoadImageFeatureValue(const std::wstring& imagePath)
|
||||
{
|
||||
auto softwareBitmap = FileHelpers::GetSoftwareBitmapFromFile(FileHelpers::GetModulePath() + imagePath);
|
||||
auto videoFrame = winrt::Windows::Media::VideoFrame::CreateWithSoftwareBitmap(softwareBitmap);
|
||||
return ImageFeatureValue::CreateFromVideoFrame(videoFrame);
|
||||
}
|
||||
}
|
||||
18
winml/test/common/fileHelpers.h
Normal file
18
winml/test/common/fileHelpers.h
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
#include "winrt/Windows.Graphics.Imaging.h"
|
||||
#include "winrt/Windows.AI.MachineLearning.h"
|
||||
|
||||
namespace FileHelpers
|
||||
{
|
||||
std::wstring GetModulePath();
|
||||
std::wstring GetWinMLPath();
|
||||
|
||||
winrt::Windows::Graphics::Imaging::SoftwareBitmap GetSoftwareBitmapFromFile(const std::wstring& filePath);
|
||||
winrt::Windows::AI::MachineLearning::ImageFeatureValue LoadImageFeatureValue(const std::wstring& imagePath);
|
||||
}
|
||||
324
winml/test/common/protobufHelpers.cpp
Normal file
324
winml/test/common/protobufHelpers.cpp
Normal file
|
|
@ -0,0 +1,324 @@
|
|||
// LotusRT
|
||||
#include "core/framework/allocatormgr.h"
|
||||
// #include "core/session/inference_session.h"
|
||||
#include "core/common/logging/logging.h"
|
||||
#include "core/common/logging/sinks/clog_sink.h"
|
||||
|
||||
#include "protobufHelpers.h"
|
||||
#include "onnx/onnx-ml.pb.h"
|
||||
#include <gtest/gtest.h>
|
||||
#include <fstream>
|
||||
|
||||
#include "winrt/Windows.Storage.Streams.h"
|
||||
|
||||
#pragma warning(disable : 4244)
|
||||
|
||||
using namespace winrt::Windows::Storage::Streams;
|
||||
using namespace winrt::Windows::AI::MachineLearning;
|
||||
using namespace winrt::Windows::Foundation::Collections;
|
||||
|
||||
// Copy and pasted from LOTUS as is. temporary code to load tensors from protobufs
|
||||
int FdOpen(const std::string& name)
|
||||
{
|
||||
int fd = -1;
|
||||
#ifdef _WIN32
|
||||
_sopen_s(&fd, name.c_str(), _O_RDONLY | _O_SEQUENTIAL | _O_BINARY, _SH_DENYWR, _S_IREAD | _S_IWRITE);
|
||||
#else
|
||||
fd = open(name.c_str(), O_RDONLY);
|
||||
#endif
|
||||
return fd;
|
||||
};
|
||||
|
||||
// Copy and pasted from LOTUS as is. temporary code to load tensors from protobufs
|
||||
void FdClose(int fd)
|
||||
{
|
||||
if (fd >= 0)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
_close(fd);
|
||||
#else
|
||||
close(fd);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// Copy and pasted from LOTUS as is. temporary code to load tensors from protobufs
|
||||
bool LoadTensorFromPb(onnx::TensorProto& tensor, std::wstring filePath)
|
||||
{
|
||||
// setup a string converter
|
||||
using convert_type = std::codecvt_utf8<wchar_t>;
|
||||
std::wstring_convert<convert_type, wchar_t> converter;
|
||||
|
||||
// use converter (.to_bytes: wstr->str, .from_bytes: str->wstr)
|
||||
std::string file = converter.to_bytes(filePath.c_str());
|
||||
|
||||
std::ifstream stream(file, std::ios::binary | std::ios::ate);
|
||||
std::streamsize size = stream.tellg();
|
||||
stream.seekg(0, std::ios::beg);
|
||||
|
||||
std::vector<char> buffer(size);
|
||||
if (stream.read(buffer.data(), size))
|
||||
{
|
||||
return tensor.ParseFromArray(buffer.data(), static_cast<int>(size));
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template <typename DataType>
|
||||
std::vector<DataType> GetTensorDataFromTensorProto(onnx::TensorProto tensorProto, int elementCount)
|
||||
{
|
||||
if (tensorProto.has_raw_data())
|
||||
{
|
||||
std::vector<DataType> tensorData;
|
||||
auto& values = tensorProto.raw_data();
|
||||
EXPECT_EQ(elementCount, values.size() / sizeof(DataType)) << L"TensorProto elementcount should match raw data buffer size in elements.";
|
||||
|
||||
tensorData = std::vector<DataType>(elementCount);
|
||||
memcpy(tensorData.data(), values.data(), values.size());
|
||||
return tensorData;
|
||||
}
|
||||
else
|
||||
{
|
||||
return std::vector<DataType>(std::begin(tensorProto.float_data()), std::end(tensorProto.float_data()));
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
std::vector<winrt::hstring> GetTensorStringDataFromTensorProto(
|
||||
onnx::TensorProto tensorProto,
|
||||
int elementCount)
|
||||
{
|
||||
EXPECT_EQ(tensorProto.string_data_size(), elementCount);
|
||||
auto& values = tensorProto.string_data();
|
||||
auto returnVector = std::vector<winrt::hstring>(elementCount);
|
||||
std::transform(std::begin(values), std::end(values), std::begin(returnVector),
|
||||
[](auto& value) { return winrt::to_hstring(value); });
|
||||
return returnVector;
|
||||
}
|
||||
|
||||
ITensor ProtobufHelpers::LoadTensorFromProtobufFile(
|
||||
const std::wstring& filePath,
|
||||
bool isFp16)
|
||||
{
|
||||
// load from the file path into the onnx format
|
||||
onnx::TensorProto tensorProto;
|
||||
if (LoadTensorFromPb(tensorProto, filePath))
|
||||
{
|
||||
std::vector<int64_t> tensorShape = std::vector<int64_t>(tensorProto.dims().begin(), tensorProto.dims().end());
|
||||
int64_t initialValue = 1;
|
||||
auto elementCount = std::accumulate(tensorShape.begin(), tensorShape.end(), initialValue, std::multiplies<int64_t>());
|
||||
|
||||
if (!tensorProto.has_data_type())
|
||||
{
|
||||
std::cerr << "WARNING: Loading unknown TensorProto datatype.\n";
|
||||
}
|
||||
if (isFp16)
|
||||
{
|
||||
return TensorFloat16Bit::CreateFromIterable(tensorShape, GetTensorDataFromTensorProto<float>(tensorProto, elementCount));
|
||||
}
|
||||
switch (tensorProto.data_type())
|
||||
{
|
||||
case(onnx::TensorProto::DataType::TensorProto_DataType_FLOAT):
|
||||
return TensorFloat::CreateFromIterable(tensorShape, GetTensorDataFromTensorProto<float>(tensorProto, elementCount));
|
||||
case(onnx::TensorProto::DataType::TensorProto_DataType_INT32):
|
||||
return TensorInt32Bit::CreateFromIterable(tensorShape, GetTensorDataFromTensorProto<int32_t>(tensorProto, elementCount));
|
||||
case(onnx::TensorProto::DataType::TensorProto_DataType_INT64):
|
||||
return TensorInt64Bit::CreateFromIterable(tensorShape, GetTensorDataFromTensorProto<int64_t>(tensorProto, elementCount));
|
||||
case(onnx::TensorProto::DataType::TensorProto_DataType_STRING):
|
||||
return TensorString::CreateFromIterable(tensorShape, GetTensorStringDataFromTensorProto(tensorProto, elementCount));
|
||||
default:
|
||||
ADD_FAILURE() << L"Tensor type for creating tensor from protobuf file not supported.";
|
||||
break;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
TensorFloat16Bit ProtobufHelpers::LoadTensorFloat16FromProtobufFile(
|
||||
const std::wstring& filePath)
|
||||
{
|
||||
// load from the file path into the onnx format
|
||||
onnx::TensorProto tensorProto;
|
||||
if (LoadTensorFromPb(tensorProto, filePath))
|
||||
{
|
||||
if (tensorProto.has_data_type())
|
||||
{
|
||||
EXPECT_EQ(onnx::TensorProto::DataType::TensorProto_DataType_FLOAT16, tensorProto.data_type());
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << "Loading unknown TensorProto datatype as TensorFloat16Bit.\n";
|
||||
}
|
||||
|
||||
auto shape = winrt::single_threaded_vector<int64_t>(std::vector<int64_t>(tensorProto.dims().begin(), tensorProto.dims().end()));
|
||||
TensorFloat16Bit singleTensorValue = TensorFloat16Bit::Create(shape.GetView());
|
||||
|
||||
uint16_t* data;
|
||||
winrt::com_ptr<ITensorNative> spTensorValueNative;
|
||||
singleTensorValue.as(spTensorValueNative);
|
||||
uint32_t sizeInBytes;
|
||||
spTensorValueNative->GetBuffer(reinterpret_cast<BYTE**>(&data), &sizeInBytes);
|
||||
|
||||
EXPECT_TRUE(tensorProto.has_raw_data()) << L"Float16 tensor proto buffers are expected to contain raw data.";
|
||||
|
||||
auto& raw_data = tensorProto.raw_data();
|
||||
auto buff = raw_data.c_str();
|
||||
const size_t type_size = sizeof(uint16_t);
|
||||
|
||||
memcpy((void*)data, (void*)buff, raw_data.size() * sizeof(char));
|
||||
|
||||
return singleTensorValue;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
winrt::Windows::AI::MachineLearning::LearningModel ProtobufHelpers::CreateModel(
|
||||
winrt::Windows::AI::MachineLearning::TensorKind kind,
|
||||
const std::vector<int64_t>& shape,
|
||||
uint32_t num_elements)
|
||||
{
|
||||
onnx::ModelProto model;
|
||||
|
||||
// Set opset import
|
||||
auto opsetimportproto = model.add_opset_import();
|
||||
opsetimportproto->set_version(7);
|
||||
|
||||
onnx::GraphProto& graph = *model.mutable_graph();
|
||||
|
||||
uint32_t begin = 0;
|
||||
uint32_t end = num_elements - 1;
|
||||
for (uint32_t i = begin; i <= end; i++)
|
||||
{
|
||||
onnx::NodeProto& node = *graph.add_node();
|
||||
node.set_op_type("Identity");
|
||||
if (i == begin && i == end)
|
||||
{
|
||||
node.add_input("input");
|
||||
node.add_output("output");
|
||||
}
|
||||
else if (i == begin)
|
||||
{
|
||||
node.add_input("input");
|
||||
node.add_output("output" + std::to_string(i));
|
||||
|
||||
}
|
||||
else if (i == end)
|
||||
{
|
||||
node.add_input("output" + std::to_string(i-1));
|
||||
node.add_output("output");
|
||||
}
|
||||
else
|
||||
{
|
||||
node.add_input("output" + std::to_string(i-1));
|
||||
node.add_output("output" + std::to_string(i));
|
||||
}
|
||||
}
|
||||
|
||||
onnx::TensorProto_DataType dataType;
|
||||
switch (kind)
|
||||
{
|
||||
case TensorKind::Float: dataType = onnx::TensorProto_DataType_FLOAT; break;
|
||||
case TensorKind::UInt8: dataType = onnx::TensorProto_DataType_UINT8; break;
|
||||
case TensorKind::Int8: dataType = onnx::TensorProto_DataType_INT8; break;
|
||||
case TensorKind::UInt16: dataType = onnx::TensorProto_DataType_UINT16; break;
|
||||
case TensorKind::Int16: dataType = onnx::TensorProto_DataType_INT16; break;
|
||||
case TensorKind::Int32: dataType = onnx::TensorProto_DataType_INT32; break;
|
||||
case TensorKind::Int64: dataType = onnx::TensorProto_DataType_INT64; break;
|
||||
case TensorKind::String: dataType = onnx::TensorProto_DataType_STRING; break;
|
||||
case TensorKind::Boolean: dataType = onnx::TensorProto_DataType_BOOL; break;
|
||||
case TensorKind::Float16: dataType = onnx::TensorProto_DataType_FLOAT16; break;
|
||||
case TensorKind::Double: dataType = onnx::TensorProto_DataType_DOUBLE; break;
|
||||
case TensorKind::UInt32: dataType = onnx::TensorProto_DataType_UINT32; break;
|
||||
case TensorKind::UInt64: dataType = onnx::TensorProto_DataType_UINT64; break;
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
char dim_param = 'a';
|
||||
// input
|
||||
{
|
||||
onnx::ValueInfoProto& variable = *graph.add_input();
|
||||
variable.set_name("input");
|
||||
//onnx::TypeProto_Tensor* pTensor = variable.mutable_type()->mutable_tensor_type();
|
||||
variable.mutable_type()->mutable_tensor_type()->set_elem_type(dataType);
|
||||
for (auto dim : shape)
|
||||
{
|
||||
if (dim == -1)
|
||||
{
|
||||
variable.mutable_type()->mutable_tensor_type()->mutable_shape()->add_dim()->set_dim_param(&dim_param, 1);
|
||||
dim_param++;
|
||||
}
|
||||
else
|
||||
{
|
||||
variable.mutable_type()->mutable_tensor_type()->mutable_shape()->add_dim()->set_dim_value(dim);
|
||||
}
|
||||
}
|
||||
|
||||
if (shape.size() > 0)
|
||||
{
|
||||
variable.mutable_type()->mutable_tensor_type()->mutable_shape()->mutable_dim(0)->set_denotation("DATA_BATCH");
|
||||
}
|
||||
}
|
||||
|
||||
// output
|
||||
{
|
||||
onnx::ValueInfoProto& variable = *graph.add_output();
|
||||
variable.set_name("output");
|
||||
//onnx::TypeProto_Tensor* pTensor = variable.mutable_type()->mutable_tensor_type();
|
||||
variable.mutable_type()->mutable_tensor_type()->set_elem_type(dataType);
|
||||
for (auto dim : shape)
|
||||
{
|
||||
if (dim == -1)
|
||||
{
|
||||
variable.mutable_type()->mutable_tensor_type()->mutable_shape()->add_dim()->set_dim_param(&dim_param, 1);
|
||||
dim_param++;
|
||||
}
|
||||
else
|
||||
{
|
||||
variable.mutable_type()->mutable_tensor_type()->mutable_shape()->add_dim()->set_dim_value(dim);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct BufferStreamAdapter : public std::streambuf
|
||||
{
|
||||
RandomAccessStreamReference BufferAsRandomAccessStreamReference()
|
||||
{
|
||||
auto buffer = m_dataWriter.DetachBuffer();
|
||||
m_dataWriter = DataWriter();
|
||||
|
||||
InMemoryRandomAccessStream stream;
|
||||
stream.WriteAsync(buffer).get();
|
||||
return RandomAccessStreamReference::CreateFromStream(stream);
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual int_type overflow(int_type c) {
|
||||
if (c != EOF) {
|
||||
// convert lowercase to uppercase
|
||||
auto temp = static_cast<char>(c);
|
||||
|
||||
m_dataWriter.WriteByte(temp);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
private:
|
||||
DataWriter m_dataWriter;
|
||||
};
|
||||
|
||||
auto size = model.ByteSize();
|
||||
auto raw_array = std::unique_ptr<char[]>(new char[size]);
|
||||
model.SerializeToArray(raw_array.get(), size);
|
||||
|
||||
BufferStreamAdapter buffer;
|
||||
std::ostream os(&buffer);
|
||||
|
||||
os.write(raw_array.get(), size);
|
||||
|
||||
return LearningModel::LoadFromStream(buffer.BufferAsRandomAccessStreamReference());
|
||||
}
|
||||
23
winml/test/common/protobufHelpers.h
Normal file
23
winml/test/common/protobufHelpers.h
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "std.h"
|
||||
|
||||
namespace ProtobufHelpers
|
||||
{
|
||||
// LoadTensorFromProtobufFile take a path to a FP32 data file and loads it into a 32bit array or
|
||||
// 16bit array based on isFp16
|
||||
winrt::Windows::AI::MachineLearning::ITensor LoadTensorFromProtobufFile(const std::wstring& filePath, bool isFp16);
|
||||
// LoadTensorFloat16FromProtobufFile takes a path to a FP16 data file and loads it into a 16bit array
|
||||
winrt::Windows::AI::MachineLearning::TensorFloat16Bit LoadTensorFloat16FromProtobufFile(const std::wstring& filePath);
|
||||
|
||||
winrt::Windows::AI::MachineLearning::LearningModel CreateModel(
|
||||
winrt::Windows::AI::MachineLearning::TensorKind kind,
|
||||
const std::vector<int64_t>& shape,
|
||||
uint32_t num_elements = 1);
|
||||
}
|
||||
51
winml/test/common/std.h
Normal file
51
winml/test/common/std.h
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
// stl
|
||||
#include <algorithm>
|
||||
#include <codecvt>
|
||||
#include <fcntl.h>
|
||||
#include <future>
|
||||
#include <io.h>
|
||||
#include <locale>
|
||||
#include <numeric>
|
||||
#include <random>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
|
||||
// IUnknown must be declared before winrt/base.h is included to light up support for native COM
|
||||
// interfaces with C++/WinRT types (e.g. winrt::com_ptr<ITensorNative>).
|
||||
#include <Unknwn.h>
|
||||
#include "winrt/base.h"
|
||||
#include "winrt/Windows.Foundation.Collections.h"
|
||||
#include "comp_generated/winrt/windows.ai.machinelearning.h"
|
||||
|
||||
// WinML
|
||||
#include "Windows.AI.MachineLearning.Native.h"
|
||||
|
||||
#define EXPECT_THROW_SPECIFIC(statement, exception, condition) \
|
||||
EXPECT_THROW( \
|
||||
try { \
|
||||
statement; \
|
||||
} catch (const exception& e) { \
|
||||
EXPECT_TRUE(condition(e)); \
|
||||
throw; \
|
||||
} \
|
||||
, exception);
|
||||
|
||||
// For old versions of gtest without GTEST_SKIP, stream the message and return success instead
|
||||
#ifndef GTEST_SKIP
|
||||
#define GTEST_SKIP(message) return GTEST_MESSAGE_(message, ::testing::TestPartResult::kSuccess)
|
||||
#endif
|
||||
|
||||
#ifndef INSTANTIATE_TEST_SUITE_P
|
||||
// Use the old name, removed in newer versions of googletest
|
||||
#define INSTANTIATE_TEST_SUITE_P INSTANTIATE_TEST_CASE_P
|
||||
#endif
|
||||
12
winml/test/common/testPch.h
Normal file
12
winml/test/common/testPch.h
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
#define _SILENCE_ALL_CXX17_DEPRECATION_WARNINGS
|
||||
#include "std.h"
|
||||
|
||||
#include <wrl/client.h>
|
||||
#include <wrl/implements.h>
|
||||
|
||||
#include "fileHelpers.h"
|
||||
BIN
winml/test/common/testdata/squeezenet/model.onnx
vendored
Normal file
BIN
winml/test/common/testdata/squeezenet/model.onnx
vendored
Normal file
Binary file not shown.
BIN
winml/test/common/testdata/squeezenet/test_data_0_input.pb
vendored
Normal file
BIN
winml/test/common/testdata/squeezenet/test_data_0_input.pb
vendored
Normal file
Binary file not shown.
BIN
winml/test/common/testdata/squeezenet/test_data_0_output.pb
vendored
Normal file
BIN
winml/test/common/testdata/squeezenet/test_data_0_output.pb
vendored
Normal file
Binary file not shown.
Loading…
Reference in a new issue