Merge pull request #10619 from microsoft/user/dwayner/DmlDev20220221

Update DirectML EP for ORT 1.11
This commit is contained in:
Dwayne Robinson 2022-02-23 01:09:26 -08:00 committed by GitHub
commit ea7f773a6e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 950 additions and 576 deletions

View file

@ -124,8 +124,12 @@ namespace Dml::GraphDescBuilder
// Check whether this specific node requested support for constant CPU inputs
if (std::find(requiredConstantCpuInputs.begin(), requiredConstantCpuInputs.end(), inputIndex) != requiredConstantCpuInputs.end())
{
const onnxruntime::NodeArg* arg = node.InputDefs()[inputIndex];
tensor = constantCpuGraphInputGetter(arg->Name());
auto inputDefs = node.InputDefs();
if (inputIndex < inputDefs.size())
{
const onnxruntime::NodeArg* arg = inputDefs[inputIndex];
tensor = constantCpuGraphInputGetter(arg->Name());
}
}
return tensor;

View file

@ -9,7 +9,7 @@
#include "GraphPartitioner.h"
#include "core/providers/dml/OperatorAuthorHelper/Attributes.h"
#include "core/providers/dml/OperatorAuthorHelper/OperatorHelper.h"
#include "core/providers/dml/OperatorAuthorHelper/OperatorRegistration.h"
#include "core/providers/dml/OperatorAuthorHelper/OperatorVersions.h"
#include "core/framework/kernel_registry.h"
#include "core/graph/graph_utils.h"

View file

@ -505,41 +505,53 @@ public:
std::vector<uint32_t> outputShape = kernelInfo.GetTensorShapeDescription().GetOutputTensorShape(0);
const uint32_t outputShapeDimCount = gsl::narrow_cast<uint32_t>(outputShape.size());
Initialize(kernelInfo, std::nullopt, std::nullopt, outputShape);
Initialize(kernelInfo, std::nullopt, std::nullopt);
// If the axis attribute is explicitly provided, then broadcasting must be performed along that axis.
// So massage the actual shapes of the scale and zero-point tensors (1D with length equal to the input
// axis being broadcast to) into broadcastable shapes.
uint32_t axis = 0;
uint32_t broadcastAxisLength = 0;
// If an axis was given explicitly passed (or the default value 1 is set from the schema),
// then other inputs are broadcasting to the shape of the input data tensor.
if (kernelInfo.HasAttribute(AttrName::Axis, MLOperatorAttributeType::Int))
{
const int32_t signedAxis = gsl::narrow_cast<int32_t>(kernelInfo.GetAttribute<int64_t>(AttrName::Axis));
const uint32_t axis = Dml::HandleNegativeAxis(signedAxis, outputShapeDimCount);
const uint32_t broadcastAxisLength = outputShape[axis];
axis = Dml::HandleNegativeAxis(signedAxis, outputShapeDimCount);
broadcastAxisLength = outputShape[axis];
}
// Explicitly reshape each of the inputs after the first input (scale and zero point tensors).
for (uint32_t index = 1, inputCount = gsl::narrow_cast<uint32_t>(m_inputTensorDescs.size()); index < inputCount; ++index)
// Explicitly reshape each of the inputs after the first input (scale and zero point tensors).
for (uint32_t index = 1, inputCount = gsl::narrow_cast<uint32_t>(m_inputTensorDescs.size()); index < inputCount; ++index)
{
auto edgeDesc = kernelInfo.GetInputEdgeDescription(index);
assert(edgeDesc.edgeType == MLOperatorEdgeType::Tensor);
// Fix up the the tensor shape by filling with trailing ones. So input[2,3] with axis=0 and scale[2]
// becomes scale[2,1], so that broadcasting works correctly.
std::vector<uint32_t> inputTensorShape = kernelInfo.GetTensorShapeDescription().GetInputTensorShape(index);
// If the input tensor is a 1D vector, then extra massaging is needed to project their
// 1D vectors back to the full shape for broadcasting along the given axis.
// The 1D vector should have a length equal to the output tensor's dimension on that axis.
if (inputTensorShape.size() == 1 && inputTensorShape != outputShape)
{
auto edgeDesc = kernelInfo.GetInputEdgeDescription(index);
assert(edgeDesc.edgeType == MLOperatorEdgeType::Tensor);
// Fix up the the tensor shape by filling with trailing ones. So input[2,3] with axis=0 and scale[2]
// becomes scale[2,1], so that broadcasting works correctly.
std::vector<uint32_t> adjustedInputTensorShape = kernelInfo.GetTensorShapeDescription().GetInputTensorShape(index);
ML_CHECK_VALID_ARGUMENT(adjustedInputTensorShape.size() == 1);
ML_CHECK_VALID_ARGUMENT(adjustedInputTensorShape[0] == broadcastAxisLength);
adjustedInputTensorShape.insert(adjustedInputTensorShape.end(), outputShapeDimCount - 1 - axis, 1);
m_inputTensorDescs[index] = TensorDesc(
edgeDesc.tensorDataType,
gsl::make_span(outputShape),
gsl::make_span(adjustedInputTensorShape),
TensorAxis::DoNotCoerce,
TensorAxis::W,
TensorAxis::RightAligned,
NchwDimensionCount, // minDimensionCount
0 // guaranteedBaseOffsetAlignment
);
ML_CHECK_VALID_ARGUMENT(inputTensorShape[0] == broadcastAxisLength);
inputTensorShape.insert(inputTensorShape.begin(), axis, 1);
inputTensorShape.insert(inputTensorShape.end(), outputShapeDimCount - 1 - axis, 1);
}
// For any other shape (scalar/ND), leave it alone, and the TensorDesc constructor
// will apply broadcasting with standard elementwise alignment.
m_inputTensorDescs[index] = TensorDesc(
edgeDesc.tensorDataType,
gsl::make_span(outputShape),
gsl::make_span(inputTensorShape),
TensorAxis::DoNotCoerce,
TensorAxis::W,
TensorAxis::RightAligned,
NchwDimensionCount, // minDimensionCount
0 // guaranteedBaseOffsetAlignment
);
}
std::vector<DML_TENSOR_DESC> inputDescs = GetDmlInputDescs();

View file

@ -14,44 +14,51 @@ public:
DML_REDUCE_FUNCTION function
)
: DmlOperator(kernelInfo),
ReduceHelperBase(kernelInfo,
kernelInfo.GetTensorShapeDescription(),
(function != DML_REDUCE_FUNCTION_ARGMAX && function != DML_REDUCE_FUNCTION_ARGMIN))
ReduceHelperBase(
kernelInfo,
kernelInfo.GetTensorShapeDescription(),
(function != DML_REDUCE_FUNCTION_ARGMAX && function != DML_REDUCE_FUNCTION_ARGMIN)
)
{
ML_CHECK_VALID_ARGUMENT(kernelInfo.GetInputCount() == 1);
ML_CHECK_VALID_ARGUMENT(kernelInfo.GetInputCount() >= 1);
ML_CHECK_VALID_ARGUMENT(kernelInfo.GetOutputCount() == 1);
DmlOperator::Initialize(kernelInfo);
std::vector<std::optional<uint32_t>> inputIndices = { 0 };
std::vector<std::optional<uint32_t>> outputIndices = { 0 };
DmlOperator::Initialize(kernelInfo, inputIndices, outputIndices, std::nullopt, std::nullopt, 1u);
std::vector<uint32_t> dmlAxes;
std::vector<DimensionType> reducedDims = kernelInfo.GetTensorShapeDescription().GetInputTensorShape(0);
int dimOffset = gsl::narrow_cast<int>(m_inputTensorDescs[0].GetDimensionCount() - reducedDims.size());
for (auto& dim : m_axes)
{
// Replace all reduced axes with 1 for their size.
assert(dim < static_cast<int32_t>(reducedDims.size())); // ReduceHelperBase already validated this.
reducedDims[dim] = 1;
dmlAxes.push_back(static_cast<uint32_t>(dim + dimOffset));
dmlAxes.push_back(static_cast<uint32_t>(dim)); // Signed to unsigned which DML expects.
}
if (!m_keepDims)
{
// DML doesn't know about keepDim and always assume the dim is preserved after reduce.
// DML expects the input and output tensors to have identical counts and doesn't know about
// ONNX's 'keepdims' attribute, keeping all dimensions anyway rather removing those of size 1.
// So if m_keepDims is false, the ONNX output dim is different than DML tensor desc dim.
// ReduceSum example:
// input dims: {3, 2, 2}
// axes: 1
// keepDims: 0
//
// the ONNX output expect to be of dim {3, 2}, while DML expect the output tensor desc
// dim to be {3, 1, 2}.
//
// ReduceSum example:
// input dims: {3, 2, 2}
// axes: 1
// keepDims: 0
//
// The ONNX output expects output dims of {3, 2},
// while DML expect the output tensor desc of {3, 1, 2}.
m_outputTensorDescs[0] = CreateTensorDescFromOutput(
kernelInfo,
0,
TensorAxis::DoNotCoerce,
TensorAxis::W,
kernelInfo,
0,
TensorAxis::DoNotCoerce,
TensorAxis::W,
TensorAxis::RightAligned,
reducedDims);
reducedDims,
1 // minimumDimensionCount
);
}
std::vector<DML_TENSOR_DESC> inputDescs = GetDmlInputDescs();

View file

@ -19,9 +19,10 @@ constexpr NameAndIndex coordinateTransformationModes[] =
constexpr NameAndIndex nearestNeighborRoundingModes[] =
{
{"", 0},
{"round_prefer_floor", 0},
{"round_prefer_ceil", 1},
{"floor", 2},
{"round_prefer_floor", 0}, // round halves down
{"round_prefer_ceil", 1}, // round halves up
{"floor", 2}, // round always down
// {"ceil", 3}, // round always up (requires a DirectML API addition)
};
void ComputePixelOffsetsAndScales(
@ -338,9 +339,9 @@ void CALLBACK QueryResize(IMLOperatorSupportQueryContextPrivate* context, bool*
DML_OP_DEFINE_CREATION_FUNCTION(Resize10, VersionedKernel<DmlOperatorResize, 10>);
DML_OP_DEFINE_CREATION_FUNCTION(Resize11, VersionedKernel<DmlOperatorResize, 11>);
DML_OP_DEFINE_CREATION_FUNCTION(Resize13, VersionedKernel<DmlOperatorResize, 13>);
DML_OP_DEFINE_CREATION_FUNCTION(Upsample7, VersionedKernel<DmlOperatorResize, 7>);
DML_OP_DEFINE_CREATION_FUNCTION(Upsample9, VersionedKernel<DmlOperatorResize, 9>);
DML_OP_DEFINE_CREATION_FUNCTION(Upsample10, VersionedKernel<DmlOperatorResize, 10>);
DML_OP_DEFINE_CREATION_FUNCTION(Upsample13, VersionedKernel<DmlOperatorResize, 13>);
} // namespace Dml

View file

@ -11,13 +11,18 @@ class DmlOperatorSplit : public DmlOperator, public SplitHelper
public:
using Self = DmlOperatorSplit;
DmlOperatorSplit(const MLOperatorKernelCreationContext& kernelInfo)
DmlOperatorSplit(const MLOperatorKernelCreationContext& kernelInfo, uint32_t opsetVersion)
: DmlOperator(kernelInfo),
SplitHelper(kernelInfo, kernelInfo.GetTensorShapeDescription())
SplitHelper(kernelInfo, kernelInfo.GetTensorShapeDescription(), opsetVersion)
{
ML_CHECK_VALID_ARGUMENT(kernelInfo.GetInputCount() == 1, "DML only supports split on a single input tensor.");
ML_CHECK_VALID_ARGUMENT(kernelInfo.GetOutputCount() > 0, "Runtime error no output stream specified.");
DmlOperator::Initialize(kernelInfo);
ML_CHECK_VALID_ARGUMENT(kernelInfo.GetInputCount() > 0, "Splits needs an input tensor.");
ML_CHECK_VALID_ARGUMENT(kernelInfo.GetOutputCount() > 0, "Splits needs an output tensor.");
// Use only the first input tensor. Later opset versions may pass parameters
// like splits as dynamic parameters via tensors rather than constants,
// and that second parameter is CPU based.
std::vector<std::optional<uint32_t>> inputIndices = {0};
DmlOperator::Initialize(kernelInfo, inputIndices, std::nullopt);
uint32_t dmlAxis = GetDmlAdjustedAxis(m_axis, kernelInfo, m_inputTensorDescs.front().GetDimensionCount());
@ -36,6 +41,8 @@ public:
}
};
DML_OP_DEFINE_CREATION_FUNCTION(Split, DmlOperatorSplit);
DML_OP_DEFINE_CREATION_FUNCTION(Split7, VersionedKernel<DmlOperatorSplit, 7>);
DML_OP_DEFINE_CREATION_FUNCTION(Split11, VersionedKernel<DmlOperatorSplit, 11>);
DML_OP_DEFINE_CREATION_FUNCTION(Split13, VersionedKernel<DmlOperatorSplit, 13>);
} // namespace Dml

View file

@ -4,7 +4,7 @@
#include "precomp.h"
#include "OperatorRegistration.h"
#include "core/providers/dml/OperatorAuthorHelper/MLOperatorAuthorHelper.h"
#include "core/providers/dml/OperatorAuthorHelper/OperatorRegistration.h"
#include "core/providers/dml/OperatorAuthorHelper/OperatorVersions.h"
#include "core/providers/dml/DmlExecutionProvider/inc/IWinmlExecutionProvider.h"
#include "core/framework/customregistry.h"
#include "onnx/defs/operator_sets.h"
@ -103,7 +103,9 @@ DML_OP_EXTERN_CREATION_FUNCTION(GRU);
DML_OP_EXTERN_CREATION_FUNCTION(LSTM);
DML_OP_EXTERN_CREATION_FUNCTION(Gather);
DML_OP_EXTERN_CREATION_FUNCTION(Flatten);
DML_OP_EXTERN_CREATION_FUNCTION(Split);
DML_OP_EXTERN_CREATION_FUNCTION(Split7);
DML_OP_EXTERN_CREATION_FUNCTION(Split11);
DML_OP_EXTERN_CREATION_FUNCTION(Split13);
DML_OP_EXTERN_CREATION_FUNCTION(Transpose);
DML_OP_EXTERN_CREATION_FUNCTION(Tile);
DML_OP_EXTERN_CREATION_FUNCTION(Concat);
@ -165,7 +167,6 @@ DML_OP_EXTERN_CREATION_FUNCTION(ImageScaler);
DML_OP_EXTERN_CREATION_FUNCTION(Upsample7);
DML_OP_EXTERN_CREATION_FUNCTION(Upsample9);
DML_OP_EXTERN_CREATION_FUNCTION(Upsample10);
DML_OP_EXTERN_CREATION_FUNCTION(Upsample13);
DML_OP_EXTERN_CREATION_FUNCTION(Sigmoid);
DML_OP_EXTERN_CREATION_FUNCTION(HardSigmoid);
DML_OP_EXTERN_CREATION_FUNCTION(Tanh);
@ -229,6 +230,7 @@ DML_OP_EXTERN_CREATION_FUNCTION(Scatter11);
DML_OP_EXTERN_CREATION_FUNCTION(Scatter13);
DML_OP_EXTERN_CREATION_FUNCTION(Resize10);
DML_OP_EXTERN_CREATION_FUNCTION(Resize11);
DML_OP_EXTERN_CREATION_FUNCTION(Resize13);
DML_OP_EXTERN_CREATION_FUNCTION(ConstantOfShape);
DML_OP_EXTERN_CREATION_FUNCTION(IsInf);
DML_OP_EXTERN_CREATION_FUNCTION(Mod);
@ -303,6 +305,7 @@ constexpr static std::array<SupportedTensorDataTypes, 2> supportedTypeListLogica
constexpr static std::array<SupportedTensorDataTypes, 1> supportedTypeListSigned = { SupportedTensorDataTypes::Float16to32 | SupportedTensorDataTypes::Int64 | SupportedTensorDataTypes::Int32 | SupportedTensorDataTypes::Int16 | SupportedTensorDataTypes::Int8 };
constexpr static std::array<SupportedTensorDataTypes, 1> supportedTypeListRange = {SupportedTensorDataTypes::Int16|SupportedTensorDataTypes::Int32|SupportedTensorDataTypes::Int64|SupportedTensorDataTypes::Float32};
constexpr static std::array<SupportedTensorDataTypes, 2> supportedTypeListResize11 = {SupportedTensorDataTypes::Float16to32, SupportedTensorDataTypes::Float16to32 /* ROI read by CPU */};
constexpr static std::array<SupportedTensorDataTypes, 2> supportedTypeListResize13 = supportedTypeListResize11;
constexpr static std::array<SupportedTensorDataTypes, 3> supportedTypeListInteger = {SupportedTensorDataTypes::Int8|SupportedTensorDataTypes::UInt8, SupportedTensorDataTypes::Int8|SupportedTensorDataTypes::UInt8, SupportedTensorDataTypes::Int32 };
constexpr static std::array<SupportedTensorDataTypes, 1> supportedTypeListInteger8 = {SupportedTensorDataTypes::Int8|SupportedTensorDataTypes::UInt8 };
constexpr static std::array<SupportedTensorDataTypes, 2> supportedTypeListRoiAlign = {SupportedTensorDataTypes::Float16to32, SupportedTensorDataTypes::Int32|SupportedTensorDataTypes::Int64 };
@ -333,7 +336,7 @@ constexpr auto requiredConstantCpuInputs(Args... args)
return std::make_pair(inputs, static_cast<int>(sizeof...(args)));
}
// Define a single row of registration information.
// Define a single row of OperatorRegistrationInformation.
#define REG_INFO(version, operatorName, ...) \
#operatorName, OnnxOperatorSet##version::sc_sinceVer_##operatorName, onnxruntime::kOnnxDomain, Create##operatorName, ShapeInferenceFunction<ShapeInferenceHelper_##operatorName>, false, ##__VA_ARGS__,
@ -341,12 +344,10 @@ constexpr auto requiredConstantCpuInputs(Args... args)
#define REG_INFO_VER(version, operatorName, ...) \
#operatorName, OnnxOperatorSet##version::sc_sinceVer_##operatorName, onnxruntime::kOnnxDomain, Create##operatorName##version, ShapeInferenceFunction<ShapeInferenceHelper_##operatorName##version>, false, ##__VA_ARGS__,
// Identity operators use Copy, alias their first input, and require floating point formats
// for usage in the graph, besides constant inputs. This is because they currently use
// element-wise identity operators in the graph for striding support, but issue actual copies
// outside the graph. Element-wise identity currently only supports floating point types.
// Identity operators use Copy, alias their first input, and use elementwise identity operators
// when needed for striding support, but issue actual copies outside the graph.
#define REG_INFO_ID(version, operatorName, ...) \
#operatorName, OnnxOperatorSet##version::sc_sinceVer_##operatorName, onnxruntime::kOnnxDomain, CreateCopy, ShapeInferenceFunction<ShapeInferenceHelper_##operatorName>, true, ##__VA_ARGS__,
#operatorName, OnnxOperatorSet##version::sc_sinceVer_##operatorName, onnxruntime::kOnnxDomain, CreateCopy, ShapeInferenceFunction<ShapeInferenceHelper_##operatorName##version>, true, ##__VA_ARGS__,
// MS-domain operators
#define REG_INFO_MS(version, operatorName, ...) \
@ -398,8 +399,9 @@ constexpr static OperatorRegistrationInformation operatorRegistrationInformation
{REG_INFO_MS( 1, ConvTransposeWithDynamicPads, typeNameListDefault, supportedTypeListFloat16to32, DmlGraphSupport::Supported, requiredConstantCpuInputs(2))},
// Data Reorganization Layers
{REG_INFO( 7, Split, typeNameListDefault, supportedTypeListAllScalars, DmlGraphSupport::Supported)},
{REG_INFO( 11, Split, typeNameListDefault, supportedTypeListAllScalars, DmlGraphSupport::Supported)}, // Adds negative axis.
{REG_INFO_VER( 7, Split, typeNameListDefault, supportedTypeListAllScalars, DmlGraphSupport::Supported)},
{REG_INFO_VER( 11, Split, typeNameListDefault, supportedTypeListAllScalars, DmlGraphSupport::Supported)}, // Adds negative axis.
{REG_INFO_VER( 13, Split, typeNameListDefault, supportedTypeListAllScalars, DmlGraphSupport::Supported, requiredConstantCpuInputs(1))}, // Moves splits from constant parameter to dynamic input.
{REG_INFO( 7, Transpose, typeNameListDefault, supportedTypeListAllScalars, DmlGraphSupport::Supported)},
{REG_INFO( 13, Transpose, typeNameListDefault, supportedTypeListAllScalars, DmlGraphSupport::Supported)},
{REG_INFO( 7, Concat, typeNameListDefault, supportedTypeListAllScalars, DmlGraphSupport::Supported)},
@ -448,10 +450,12 @@ constexpr static OperatorRegistrationInformation operatorRegistrationInformation
{REG_INFO_ID( 13, Flatten, typeNameListDefault, supportedTypeListAllScalars, DmlGraphSupport::Supported)},
{REG_INFO_ID( 7, Squeeze, typeNameListDefault, supportedTypeListAllScalars, DmlGraphSupport::Supported)},
{REG_INFO_ID( 11, Squeeze, typeNameListDefault, supportedTypeListAllScalars, DmlGraphSupport::Supported)},
{REG_INFO_ID( 13, Squeeze, typeNameListDefault, supportedTypeListAllScalars, DmlGraphSupport::Supported, requiredConstantCpuInputs(1))},
{REG_INFO_ID( 7, Unsqueeze, typeNameListDefault, supportedTypeListAllScalars, DmlGraphSupport::Supported)},
{REG_INFO_ID( 11, Unsqueeze, typeNameListDefault, supportedTypeListAllScalars, DmlGraphSupport::Supported)},
{REG_INFO_ID( 7, Reshape, typeNameListDefault, supportedTypeListAllScalars, DmlGraphSupport::Supported, requiredConstantCpuInputs(1))},
{REG_INFO_ID( 13, Reshape, typeNameListDefault, supportedTypeListAllScalars, DmlGraphSupport::Supported, requiredConstantCpuInputs(1))},
{REG_INFO_ID( 13, Unsqueeze, typeNameListDefault, supportedTypeListAllScalars, DmlGraphSupport::Supported, requiredConstantCpuInputs(1))},
{REG_INFO_ID( 7, Reshape, typeNameListDefault, supportedTypeListAllScalars, DmlGraphSupport::Supported, requiredConstantCpuInputs(1))},
{REG_INFO_ID( 13, Reshape, typeNameListDefault, supportedTypeListAllScalars, DmlGraphSupport::Supported, requiredConstantCpuInputs(1))},
// Elementwise
{REG_INFO( 7, Sqrt, typeNameListDefault, supportedTypeListFloat16to32, DmlGraphSupport::Supported)},
@ -505,7 +509,9 @@ constexpr static OperatorRegistrationInformation operatorRegistrationInformation
{REG_INFO( 7, Atan, typeNameListDefault, supportedTypeListFloat16to32, DmlGraphSupport::Supported)},
{REG_INFO( 7, Affine, typeNameListDefault, supportedTypeListFloat16to32, DmlGraphSupport::Supported)},
{REG_INFO( 10, QuantizeLinear, typeNameListTwo, supportedTypeListQuantizeLinear, DmlGraphSupport::Supported)},
{REG_INFO( 13, QuantizeLinear, typeNameListTwo, supportedTypeListQuantizeLinear, DmlGraphSupport::Supported)},
{REG_INFO( 10, DequantizeLinear, typeNameListTwo, supportedTypeListDequantizeLinear, DmlGraphSupport::Supported)},
{REG_INFO( 13, DequantizeLinear, typeNameListTwo, supportedTypeListDequantizeLinear, DmlGraphSupport::Supported)},
{REG_INFO_MS( 1, QuantizeLinear, typeNameListTwo, supportedTypeListQuantize, DmlGraphSupport::Supported)},
{REG_INFO_MS( 1, DequantizeLinear, typeNameListTwo, supportedTypeListQuantize, DmlGraphSupport::Supported)},
{REG_INFO( 9, Sign, typeNameListDefault, supportedTypeListNumericDefault, DmlGraphSupport::Supported)},
@ -522,6 +528,7 @@ constexpr static OperatorRegistrationInformation operatorRegistrationInformation
{REG_INFO( 9, Where, typeNameListWhere, supportedTypeListWhere, DmlGraphSupport::Supported)},
{REG_INFO( 7, ReduceSum, typeNameListDefault, supportedTypeListFloat16to32Ints32to64, DmlGraphSupport::Supported)},
{REG_INFO( 11, ReduceSum, typeNameListDefault, supportedTypeListFloat16to32Ints32to64, DmlGraphSupport::Supported)},
{REG_INFO( 13, ReduceSum, typeNameListDefault, supportedTypeListFloat16to32Ints32to64, DmlGraphSupport::Supported, requiredConstantCpuInputs(1))},
{REG_INFO_VER( 12, Einsum, typeNameListDefault, supportedTypeListFloat16to32, DmlGraphSupport::Supported, requiredConstantCpuInputs(), std::nullopt, QueryEinSum )},
{REG_INFO( 7, ReduceMean, typeNameListDefault, supportedTypeListFloat16to32, DmlGraphSupport::Supported)},
{REG_INFO( 11, ReduceMean, typeNameListDefault, supportedTypeListFloat16to32, DmlGraphSupport::Supported)},
@ -588,9 +595,9 @@ constexpr static OperatorRegistrationInformation operatorRegistrationInformation
{REG_INFO_VER( 7, Upsample, typeNameListDefault, supportedTypeListFloat16to32, DmlGraphSupport::Supported)},
{REG_INFO_VER( 9, Upsample, typeNameListDefault, supportedTypeListFloat16to32, DmlGraphSupport::Supported, requiredConstantCpuInputs(1) /*scales*/)},
{REG_INFO_VER( 10, Upsample, typeNameListDefault, supportedTypeListFloat16to32, DmlGraphSupport::Supported, requiredConstantCpuInputs(1) /*scales*/)},
{REG_INFO_VER( 13, Upsample, typeNameListDefault, supportedTypeListFloat16to32, DmlGraphSupport::Supported, requiredConstantCpuInputs(1) /*scales*/)},
{REG_INFO_VER( 10, Resize, typeNameListDefault, supportedTypeListFloat16to32, DmlGraphSupport::Supported, requiredConstantCpuInputs(1) /*scales*/)},
{REG_INFO_VER( 11, Resize, typeNameListTwo, supportedTypeListResize11, DmlGraphSupport::Supported, requiredConstantCpuInputs(1, 2, 3) /*roi, scales, sizes*/, std::nullopt, QueryResize)},
{REG_INFO_VER( 13, Resize, typeNameListTwo, supportedTypeListResize13, DmlGraphSupport::Supported, requiredConstantCpuInputs(1, 2, 3) /*roi, scales, sizes*/, std::nullopt, QueryResize)},
// Activation Functions
{REG_INFO( 7, Sigmoid, typeNameListDefault, supportedTypeListFloat16to32, DmlGraphSupport::Supported)},

View file

@ -3,7 +3,7 @@
#include "precomp.h"
#include "OperatorUtility.h"
#include "core/providers/dml/OperatorAuthorHelper/OperatorRegistration.h"
#include "core/providers/dml/OperatorAuthorHelper/OperatorVersions.h"
namespace Dml
{

View file

@ -7,4 +7,4 @@
#include "core/providers/dml/OperatorAuthorHelper/Attributes.h"
#include "core/providers/dml/OperatorAuthorHelper/OperatorHelper.h"
#include "DmlOperator.h"
#include "OperatorRegistration.h"
#include "OperatorRegistration.h"

View file

@ -47,7 +47,6 @@ namespace AttrName
static constexpr const char* InputForget = "input_forget";
static constexpr const char* K = "k";
static constexpr const char* KeepDims = "keepdims";
static constexpr const char* SelectLastIndex = "select_last_index";
static constexpr const char* KernelShape = "kernel_shape";
static constexpr const char* LinearBeforeReset = "linear_before_reset";
static constexpr const char* Lambda = "lambd"; // Deliberate typo to match ONNX spec.
@ -58,6 +57,7 @@ namespace AttrName
static constexpr const char* Min = "min";
static constexpr const char* Mode = "mode";
static constexpr const char* NearestMode = "nearest_mode";
static constexpr const char* NoopWithEmptyAxes = "noop_with_empty_axes";
static constexpr const char* NormalizeVariance = "normalize_variance";
static constexpr const char* P = "p";
static constexpr const char* OutputHeight = "output_height";
@ -72,6 +72,7 @@ namespace AttrName
static constexpr const char* Scale = "scale";
static constexpr const char* Scales = "scales";
static constexpr const char* Seed = "seed";
static constexpr const char* SelectLastIndex = "select_last_index";
static constexpr const char* Shape = "shape";
static constexpr const char* Size = "size";
static constexpr const char* Sorted = "sorted";

View file

@ -190,10 +190,10 @@ class MLOperatorTensorShapeDescription
return ret;
}
Microsoft::WRL::ComPtr<IMLOperatorTensorShapeDescription> GetInterface() const { return m_impl; }
Microsoft::WRL::ComPtr<IMLOperatorTensorShapeDescription> GetInterface() const noexcept { return m_impl; }
protected:
Microsoft::WRL::ComPtr<IMLOperatorTensorShapeDescription> m_impl ;
Microsoft::WRL::ComPtr<IMLOperatorTensorShapeDescription> m_impl;
};
class MLOperatorAttributes
@ -203,6 +203,12 @@ class MLOperatorAttributes
{
}
// For cases of interop where the caller needs to pass the unwrapped class across a boundary.
Microsoft::WRL::ComPtr<IMLOperatorAttributes> GetInterface() const noexcept
{
return m_impl;
}
uint32_t GetAttributeElementCount(
_In_z_ MLConstStringParam name,
MLOperatorAttributeType type) const
@ -638,7 +644,7 @@ public:
MLOperatorTypeInferenceContext(IMLOperatorTypeInferenceContext* impl) : MLOperatorAttributes(impl), m_impl(impl) {}
// For cases of interop where the caller needs to pass the unwrapped class across a boundary.
Microsoft::WRL::ComPtr<IMLOperatorTypeInferenceContext> GetInterface() const noexcept
Microsoft::WRL::ComPtr<IMLOperatorTypeInferenceContext> GetInterface() const noexcept
{
return m_impl;
}

View file

@ -617,6 +617,122 @@ namespace OperatorHelper
return outputShape;
}
void ConvolutionHelperBase::InitializeKernelAndShapes(const IShapeInformationAdapter& shapeInformation)
{
const std::vector<DimensionType> inputDimensions = shapeInformation.GetInputTensorShape(m_inputTensorIndex);
const std::vector<DimensionType> filterDims = shapeInformation.GetInputTensorShape(m_filterTensorIndex);
ML_CHECK_VALID_ARGUMENT(
inputDimensions.size() >= 3 && inputDimensions.size() <= 5,
"Input dimensions must be: 3, 4, 5."
);
ResolvingPadding(inputDimensions);
m_outputShapes.resize(1);
m_outputShapes[0] = InitializeKernelOutputDimensions(inputDimensions, m_kernel);
m_outputShapes[0].GetShape()[C] = filterDims[K];
}
void ConvolutionHelperBase::InitializeKernelAndShapesTransposed(
const IKernelInformationAdapter& kernelInformation,
const IShapeInformationAdapter& shapeInformation,
bool hasDynamicPads
)
{
auto& attributes = kernelInformation.GetAttributes();
std::vector<int> outputShape = attributes.GetOptionalAttributeVectorInt32(AttrName::OutputShape);
if (!outputShape.empty())
{
ML_CHECK_VALID_ARGUMENT(
outputShape.size() >= m_kernel.spatialDimensionCount,
"The output shape must equal the number of spatial dimensions"
);
}
const std::vector<DimensionType> inputDimensions = shapeInformation.GetInputTensorShape(m_inputTensorIndex);
const std::vector<DimensionType> filterDims = shapeInformation.GetInputTensorShape(m_filterTensorIndex);
ML_CHECK_VALID_ARGUMENT(inputDimensions.size() > NonspatialDimensionCount, "Input dimensions must be >= 3");
if (hasDynamicPads)
{
MLOperatorTensor padsTensor = kernelInformation.GetConstantInputTensor(2);
const std::vector<uint32_t>& padsTensorDimensions = padsTensor.GetShape();
ML_CHECK_VALID_ARGUMENT(padsTensorDimensions.size() == 1, "Pads dimensions must equal 1");
const size_t dimCount = padsTensorDimensions[0];
ML_CHECK_VALID_ARGUMENT(dimCount == 2 * NchwSpatialDimensionCount, "Pads count must equal 4");
const int64_t* padsData = padsTensor.GetData<int64_t>();
for (size_t i = 0; i < dimCount; ++i)
{
ML_CHECK_VALID_ARGUMENT(padsData[i] >= 0, "Padding values must be greater than or equal to 0");
if (i < dimCount / 2)
{
m_kernel.startPadding[i] = gsl::narrow_cast<uint32_t>(padsData[i]);
}
else
{
m_kernel.endPadding[i - dimCount/2] = gsl::narrow_cast<uint32_t>(padsData[i]);
}
}
}
else
{
ResolvingPadding(inputDimensions);
}
m_outputShapes.resize(1);
m_outputShapes[0] = InitializeKernelOutputDimsTranspose(inputDimensions, m_kernel);
static_assert(C < NonspatialDimensionCount);
assert(m_outputShapes[0].GetShape().size() > C);
m_outputShapes[0].GetShape()[C] = filterDims[C] * m_groupCount;
if (!outputShape.empty())
{
// Start padding, end padding, and output padding are all ignored if output shape is set.
std::fill(m_kernel.outputPadding, m_kernel.outputPadding + m_kernel.spatialDimensionCount, 0);
if (outputShape.size() > 2)
{
ML_CHECK_VALID_ARGUMENT(outputShape[outputShape.size() - 3] == gsl::narrow_cast<int>(m_outputShapes[0].GetShape()[C]), "Output channel must be equivalent to filter channel.");
}
for (size_t i = 0; i < m_kernel.spatialDimensionCount; ++i)
{
size_t outputIndex = outputShape.size() - m_kernel.spatialDimensionCount + i;
ML_CHECK_VALID_ARGUMENT(outputShape[outputIndex] >= gsl::narrow_cast<int>(inputDimensions[H + i]), "Output dimension cannot be smaller than input dimension.");
m_outputShapes[0].GetShape()[H + i] = outputShape[outputIndex];
}
const int dimOffset = gsl::narrow_cast<int>(inputDimensions.size() - m_kernel.spatialDimensionCount);
for (size_t i = 0; i < m_kernel.spatialDimensionCount; ++i)
{
int stride = m_kernel.strides[i];
int windowSize = m_kernel.windowSize[i];
// Compute padding such that in reverse order, the logical input (m_outputShapes below) is fully defined
// for a convolution over the logical output region (inputDimensions below).
//
// The padding required is the first windowSize element (for the first logical output element),
// plus (logicalOutput - 1) steps of stride (the distance between each windowed set of logical
// input elements), minus the actual logical input size.
int paddings = gsl::narrow_cast<int>((inputDimensions[i + dimOffset] - 1) * stride + windowSize - m_outputShapes[0].GetShape()[i + dimOffset]);
paddings = std::max<int>(0, paddings);
m_kernel.startPadding[i] = m_kernel.autoPadSameUpper ? (paddings + 1) / 2 : paddings / 2;
m_kernel.endPadding[i] = paddings - m_kernel.startPadding[i];
}
}
}
std::vector<EdgeShapes> ConvolutionHelperBase::GetOutputShapes(const MLShapeInferenceContext& shapeInformation) const
{
ORT_UNUSED_PARAMETER(shapeInformation);
return m_outputShapes;
}
void ConvolutionHelperBase::ResolvingPadding(gsl::span<const DimensionType> inputDimensions)
{
ResolveAutoPadding(m_kernel, inputDimensions);
@ -638,13 +754,30 @@ namespace OperatorHelper
}
void SplitHelper::Initialize(
const MLOperatorAttributes& operatorAttributes,
gsl::span<const DimensionType> inputDimensions
IKernelInformationAdapter const& kernelInformation,
IShapeInformationAdapter const& shapeInformation,
uint32_t opsetVersion
)
{
auto& operatorAttributes = kernelInformation.GetAttributes();
if (opsetVersion >= 13) // Axes are a dynamic input parameter.
{
// The tensor is optional, which if empty, means to default to equal splits.
if (kernelInformation.IsInputValid(1))
{
ReadCpuLocalTensorIntoInt32(kernelInformation.GetConstantInputTensor(1), /*out*/ m_split);
}
}
else // Axes were a constant attribute parameter.
{
m_split = operatorAttributes.GetOptionalAttributeVectorInt32(AttrName::Split);
}
const std::vector<DimensionType> inputDimensions = shapeInformation.GetInputTensorShape(0);
const uint32_t inputDimCount = gsl::narrow_cast<int32_t>(inputDimensions.size());
m_axis = static_cast<int>(HandleNegativeAxis(operatorAttributes.GetOptionalAttribute<int32_t>(AttrName::Axis, 0), inputDimCount));
m_split = operatorAttributes.GetOptionalAttributeVectorInt32(AttrName::Split);
const uint32_t axis = operatorAttributes.GetOptionalAttribute<int32_t>(AttrName::Axis, 0);
m_axis = static_cast<int>(HandleNegativeAxis(axis, inputDimCount));
}
std::vector<EdgeShapes> SplitHelper::GetOutputShapes(const MLShapeInferenceContext& shapeInfo) const
@ -699,13 +832,124 @@ namespace OperatorHelper
return edgeShapes;
}
void SliceHelper::Initialize(
const IKernelInformationAdapter& kernelInformation,
const IShapeInformationAdapter& shapeInformation,
uint32_t opsetVersion
)
{
auto& attributes = kernelInformation.GetAttributes();
std::vector<DimensionType> inputDimensions = shapeInformation.GetInputTensorShape(0);
std::vector<int32_t> starts;
std::vector<int32_t> ends;
std::vector<int32_t> axes;
std::vector<int32_t> steps;
if (opsetVersion >= 10)
{
// Get starts, ends, optional axes, and optional steps from constant tensor inputs.
ReadCpuLocalTensorIntoInt32(kernelInformation.GetConstantInputTensor(1), /*out*/ starts);
ReadCpuLocalTensorIntoInt32(kernelInformation.GetConstantInputTensor(2), /*out*/ ends);
if (kernelInformation.IsInputValid(3))
{
ReadCpuLocalTensorIntoInt32(kernelInformation.GetConstantInputTensor(3), /*out*/ axes);
}
if (kernelInformation.IsInputValid(4))
{
ReadCpuLocalTensorIntoInt32(kernelInformation.GetConstantInputTensor(4), /*out*/ steps);
}
}
else if (opsetVersion >= 7)
{
// Read starts, ends, and axes from attributes.
starts = attributes.GetOptionalAttributeVectorInt32(AttrName::Starts);
ends = attributes.GetOptionalAttributeVectorInt32(AttrName::Ends);
axes = attributes.GetOptionalAttributeVectorInt32(AttrName::Axes);
}
const uint32_t inputDimensionCount = gsl::narrow_cast<int32_t>(inputDimensions.size());
HandleNegativeAxes(/*inout*/ axes, inputDimensionCount);
ML_CHECK_VALID_ARGUMENT(starts.size() == ends.size(), "'starts' must equal 'ends' in size.");
ML_CHECK_VALID_ARGUMENT(steps.empty() || steps.size() == axes.size(), "'steps' must equal 'axes' in size, or 'steps' must be empty.");
ML_CHECK_VALID_ARGUMENT(axes.empty() || starts.size() == axes.size(), "'axes' must equal 'starts' in size, or 'axes' must be empty.");
m_outputDimensions.assign(inputDimensions.begin(), inputDimensions.end());
m_offsets.resize(m_outputDimensions.size());
m_sizes.resize(m_outputDimensions.size());
m_strides.resize(m_outputDimensions.size(), 1); // Default initialize to all steps to 1's.
// Set initial defaults lest 'starts' and 'ends' arrays are shorter than the dimension count.
std::copy(inputDimensions.begin(), inputDimensions.begin() + m_outputDimensions.size(), m_sizes.begin());
// Clamp selected dimensions to given 'starts' and 'ends'.
for (int i = 0, ci = gsl::narrow_cast<int>(starts.size()); i < ci; ++i)
{
int dimIndex = axes.empty() ? i : axes[i];
int stride = steps.empty() ? 1 : steps[i];
ML_CHECK_VALID_ARGUMENT(static_cast<size_t>(dimIndex) < static_cast<size_t>(inputDimensions.size()), "'axes' must be valid with within actual input dimensions.");
ML_CHECK_VALID_ARGUMENT(stride != 0, "'steps' must not be 0.");
// Positive values are offsets from 0.
// Negative values are offsets from back of the dimension's size.
// INT_MIN is a special value in ONNX which means to treat it as the smallest
// possible value, rather than the usual reversed from-the-back semantics.
int dim = gsl::narrow_cast<int>(inputDimensions[dimIndex]);
int start = (starts[i] < 0 && starts[i] > INT_MIN) ? (starts[i] + dim) : starts[i];
int end = (ends[i] < 0 && starts[i] > INT_MIN) ? (ends[i] + dim) : ends[i];
// For negative strides, the ONNX start and end values are off-by-one.
// So fix them such that the start value remains the minimum extent
// of the slice window, and end remains the maximum exclusive extent.
if (stride < 0)
{
std::swap(start, end);
start += (start < INT_MAX) ? 1 : 0; // Avoid overflow wrap.
end += (end < INT_MAX) ? 1 : 0;
}
// Clamp the dimensions to the slice extents.
// Clamp negative numbers to 0, per case test_slice_start_out_of_bounds.
start = std::max(start, 0);
end = std::min(end, dim);
int size = std::max(end - start, 0);
// Set the input window offsets/sizes, and compute output size based on input
// window size (rounding up).
// e.g. a window size 13 and step 3 yields 5 output elements.
int absoluteStride = abs(stride);
m_outputDimensions[dimIndex] = (size / absoluteStride) + (size % absoluteStride != 0);
m_offsets[dimIndex] = start;
m_strides[dimIndex] = stride;
m_sizes[dimIndex] = gsl::narrow_cast<uint32_t>(size);
}
}
std::vector<EdgeShapes> SliceHelper::GetOutputShapes(const MLShapeInferenceContext& shapeInfo) const
{
return { m_outputDimensions };
}
void PaddingHelper::Initialize(const MLOperatorAttributes& operatorAttributes, gsl::span<int32_t> padding, uint32_t opsetVersion)
void PaddingHelper::Initialize(
const IKernelInformationAdapter& kernelInformation,
const IShapeInformationAdapter& shapeInformation,
uint32_t opsetVersion
)
{
auto& attributes = kernelInformation.GetAttributes();
std::vector<int32_t> padding;
if (opsetVersion >= 11)
{
MLOperatorTensor padsTensor = kernelInformation.GetConstantInputTensor(1);
ReadCpuLocalTensorIntoInt32(padsTensor, /*out*/ padding);
}
else
{
padding = attributes.GetOptionalAttributeVectorInt32(AttrName::Pads);
}
ML_CHECK_VALID_ARGUMENT(padding.size() % 2 == 0, "Padding must be even count, including begin/end pairs.");
uint32_t dimCount = gsl::narrow_cast<uint32_t>(padding.size() / 2);
@ -746,10 +990,13 @@ namespace OperatorHelper
}
void GatherHelper::Initialize(
const MLOperatorAttributes& operatorAttributes,
gsl::span<const DimensionType> inputDimensions
const IKernelInformationAdapter& kernelInformation,
const IShapeInformationAdapter& shapeInformation
)
{
auto& operatorAttributes = kernelInformation.GetAttributes();
std::vector<DimensionType> inputDimensions = shapeInformation.GetInputTensorShape(0);
int32_t signedOnnxAxis = operatorAttributes.GetOptionalAttribute<int>(AttrName::Axis, 0);
uint32_t inputRank = gsl::narrow_cast<int>(inputDimensions.size());
m_axis = HandleNegativeAxis(signedOnnxAxis, inputRank);
@ -865,6 +1112,39 @@ namespace OperatorHelper
return { EdgeShapes(std::move(outputDimensions)) };
}
void ReduceHelperBase::Initialize(
IKernelInformationAdapter const& kernelInformation,
IShapeInformationAdapter const& shapeInformation,
bool usingMultipleAxes
)
{
auto& attributes = kernelInformation.GetAttributes();
m_keepDims = attributes.GetOptionalAttribute<int32_t>(AttrName::KeepDims, 1);
m_selectLastIndex = attributes.GetOptionalAttribute<int32_t>(AttrName::SelectLastIndex, 0);
m_noopWithEmptyAxes = attributes.GetOptionalAttribute<int32_t>(AttrName::NoopWithEmptyAxes, 0);
if (usingMultipleAxes) // Read full axis list. e.g. ReduceSum.
{
if (kernelInformation.IsInputValid(1)) // Axes are from a dynamic input parameter.
{
ReadCpuLocalTensorIntoInt32(kernelInformation.GetConstantInputTensor(1), /*out*/ m_axes);
}
else // Axes were a constant attribute parameter.
{
m_axes = attributes.GetOptionalAttributeVectorInt32(AttrName::Axes);
}
}
else // Only read a single axis. e.g. ArgMin/ArgMax.
{
int axis = attributes.GetOptionalAttribute<int32_t>(AttrName::Axis, 0);
m_axes.push_back(axis);
}
std::vector<uint32_t> inputShape = shapeInformation.GetInputTensorShape(0);
HandleNegativeAxes(/*inout*/ m_axes, gsl::narrow_cast<uint32_t>(inputShape.size()));
HandleEmptyAxes(/*inout*/ m_axes, inputShape, bool(m_noopWithEmptyAxes));
}
std::vector<EdgeShapes> ReduceHelperBase::GetOutputShapes(const MLShapeInferenceContext& shapeInfo) const
{
// Example:
@ -909,13 +1189,18 @@ namespace OperatorHelper
}
}
void ReduceHelperBase::AdjustAxesAndOutputShape(const std::vector<uint32_t>& inputShape)
void ReduceHelperBase::HandleEmptyAxes(
/*inout*/std::vector<int32_t>& axes,
gsl::span<const uint32_t> inputShape,
bool treatEmptyAsNop
)
{
// If axes is not specified, reduce over all the dimensions
if (m_axes.empty())
// If axes is not specified, reduce over all the dimensions.
// If empty axes should be treated as a nop, then just leave them as-is.
if (axes.empty() && !treatEmptyAsNop)
{
m_axes.resize(inputShape.size());
std::iota(m_axes.begin(), m_axes.end(), 0);
axes.resize(inputShape.size());
std::iota(axes.begin(), axes.end(), 0);
}
}
@ -1236,7 +1521,32 @@ namespace OperatorHelper
return {std::move(outputDims)};
}
void TopKHelper::Initialize(
const IKernelInformationAdapter& kernelInformation,
const IShapeInformationAdapter& shapeInformation,
uint32_t opsetVersion
)
{
auto& attributes = kernelInformation.GetAttributes();
int32_t k;
if (opsetVersion >= 10)
{
MLOperatorTensor kTensor = kernelInformation.GetConstantInputTensor(1);
k = gsl::narrow_cast<int32_t>(ReadScalarTensorCastToInt64(kTensor));
}
else
{
k = attributes.template GetOptionalAttribute<int32_t>(AttrName::K, -1);
}
ML_CHECK_VALID_ARGUMENT(k >= 0, "Attribute k is missing or negative.");
m_k = k;
auto inputShape = shapeInformation.GetInputTensorShape(0);
int32_t axis = attributes.template GetOptionalAttribute<int32_t>(AttrName::Axis, -1);
m_axis = HandleNegativeAxis(axis, gsl::narrow_cast<uint32_t>(inputShape.size()));
}
std::vector<EdgeShapes> TopKHelper::GetOutputShapes(const MLShapeInferenceContext& shapeInfo) const
{
assert(m_axis >= 0);
@ -1528,11 +1838,20 @@ namespace OperatorHelper
}
void SqueezeHelper::Initialize(
gsl::span<const int32_t> axes,
gsl::span<const DimensionType> inputDimensions
IKernelInformationAdapter const& kernelInformation,
IShapeInformationAdapter const& shapeInformation,
uint32_t opsetVersion
)
{
m_axes.assign(axes.begin(), axes.end());
if (opsetVersion >= 13) // Axes are a dynamic input parameter.
{
ReadCpuLocalTensorIntoInt32(kernelInformation.GetConstantInputTensor(1), /*out*/ m_axes);
}
else // Axes were a constant attribute parameter.
{
m_axes = kernelInformation.GetAttributes().GetOptionalAttributeVectorInt32(AttrName::Axes);
}
std::vector<DimensionType> inputDimensions = shapeInformation.GetInputTensorShape(0);
HandleNegativeAxes(/*inout*/ m_axes, gsl::narrow_cast<uint32_t>(inputDimensions.size()));
std::sort(m_axes.begin(), m_axes.end());
}
@ -1571,12 +1890,22 @@ namespace OperatorHelper
}
void UnsqueezeHelper::Initialize(
gsl::span<const int32_t> axes,
gsl::span<const DimensionType> inputDimensions
IKernelInformationAdapter const& kernelInformation,
IShapeInformationAdapter const& shapeInformation,
uint32_t opsetVersion
)
{
m_axes.assign(axes.begin(), axes.end());
const uint32_t outputDimensionCount = gsl::narrow_cast<uint32_t>(inputDimensions.size() + axes.size());
if (opsetVersion >= 13) // Axes are a dynamic input parameter.
{
ReadCpuLocalTensorIntoInt32(kernelInformation.GetConstantInputTensor(1), /*out*/ m_axes);
}
else // Axes were a constant attribute parameter.
{
m_axes = kernelInformation.GetAttributes().GetOptionalAttributeVectorInt32(AttrName::Axes);
}
std::vector<DimensionType> inputDimensions = shapeInformation.GetInputTensorShape(0);
const uint32_t outputDimensionCount = gsl::narrow_cast<uint32_t>(inputDimensions.size() + m_axes.size());
HandleNegativeAxes(/*inout*/ m_axes, outputDimensionCount);
std::sort(m_axes.begin(), m_axes.end());
}
@ -1615,7 +1944,19 @@ namespace OperatorHelper
return { EdgeShapes(outputDimensions) };
}
void ReshapeHelper::Initialize(IKernelInformationAdapter const& kernelInformation)
{
ML_CHECK_VALID_ARGUMENT(kernelInformation.GetInputCount() >= 2);
ML_CHECK_VALID_ARGUMENT(kernelInformation.GetOutputCount() >= 1);
// The 'shape' tensor is a 1D tensor holding the new shape to reshape to,
// and the first element of its own shape holds how many dimensions there
// will be for the output.
MLOperatorTensor shapeTensor = kernelInformation.GetConstantInputTensor(1);
ReadCpuLocalTensorIntoInt32(shapeTensor, /*out*/ m_shapeDims);
}
std::vector<EdgeShapes> ReshapeHelper::GetOutputShapes(const MLShapeInferenceContext& shapeInfo) const
{
// Fill in the output dimensions. The shape may have -1 in a single dimension,
@ -1709,15 +2050,84 @@ namespace OperatorHelper
return { EdgeShapes(desiredTensorShape) };
}
void TileHelper::Initialize(
const IKernelInformationAdapter& kernelInformation,
const IShapeInformationAdapter& shapeInformation
)
{
m_inputDimensions = shapeInformation.GetInputTensorShape(0);
// Read the repeats tensor.
const std::vector<uint32_t> repeatsTensorDimensions = shapeInformation.GetInputTensorShape(1);
ML_CHECK_VALID_ARGUMENT(repeatsTensorDimensions.size() == 1, "Tile's repeats tensor must be 1D.");
const size_t dimCount = repeatsTensorDimensions[0];
MLOperatorTensor repeatsTensor = kernelInformation.GetConstantInputTensor(1);
const int64_t* repeatsData = repeatsTensor.GetData<int64_t>();
ML_CHECK_VALID_ARGUMENT(m_inputDimensions.size() == dimCount, "Tile's repeats tensor must be the same dimension count as the input tensor.");
ML_CHECK_VALID_ARGUMENT(repeatsTensor.IsCpuData(), "Tile's repeats tensor must be CPU Tensor.");
for (size_t i = 0; i < dimCount; ++i)
{
ML_CHECK_VALID_ARGUMENT(repeatsData[i] >= 0, "Repeat values should be >= 0.");
m_repeatsData.push_back(gsl::narrow_cast<uint32_t>(repeatsData[i]));
}
// Update the computed output shape accordingly, repeat every axis's length by the repeat count.
m_outputDimensions.assign(m_inputDimensions.begin(), m_inputDimensions.end());
for (size_t dimIndex = 0; dimIndex < dimCount; ++dimIndex)
{
m_outputDimensions[dimIndex] *= m_repeatsData[dimIndex];
}
}
std::vector<EdgeShapes> TileHelper::GetOutputShapes(const MLShapeInferenceContext& shapeInfo) const
{
return { EdgeShapes(m_outputDimensions) };
}
void ResizeHelper::Initialize(
gsl::span<const int32_t> outputSizes
IKernelInformationAdapter const& kernelInformation,
IShapeInformationAdapter const& shapeInformation,
uint32_t opsetVersion
)
{
auto& attributes = kernelInformation.GetAttributes();
m_inputDimensions = shapeInformation.GetInputTensorShape(0);
std::vector<int32_t> outputSizes;
if (opsetVersion >= 11)
{
if (kernelInformation.IsInputValid(1))
{
MLOperatorTensor regionOfInterestTensor = kernelInformation.GetConstantInputTensor(1);
ReadCpuLocalTensorIntoFloat32(regionOfInterestTensor, /*out*/ m_regionOfInterest);
}
if (kernelInformation.IsInputValid(2))
{
MLOperatorTensor scalesTensor = kernelInformation.GetConstantInputTensor(2);
ReadCpuLocalTensorIntoFloat32(scalesTensor, /*out*/ m_scales);
}
if (kernelInformation.IsInputValid(3))
{
MLOperatorTensor outputSizesTensor = kernelInformation.GetConstantInputTensor(3);
ReadCpuLocalTensorIntoInt32(outputSizesTensor, /*out*/ outputSizes);
}
}
else if (opsetVersion >= 9)
{
// Read the scales from the 2nd tensor.
// Compatible with Upsample-9/Upsample-10 and Resize-10.
MLOperatorTensor scalesTensor = kernelInformation.GetConstantInputTensor(1);
ReadCpuLocalTensorIntoFloat32(scalesTensor, /*out*/ m_scales);
}
else
{
// From attribute, compatible with Upsample-7.
m_scales = attributes.template GetOptionalAttribute<std::vector<float>>(AttrName::Scales, std::vector<float>());
}
assert(m_outputDimensions.empty());
ML_CHECK_VALID_ARGUMENT(m_scales.empty() || outputSizes.empty(), "scales and roi cannot both be present.");
@ -1797,6 +2207,35 @@ namespace OperatorHelper
return { m_outputDimensions };
}
void OneHotHelper::Initialize(
const IKernelInformationAdapter& kernelInformation,
const IShapeInformationAdapter& shapeInformation
)
{
ML_CHECK_VALID_ARGUMENT(kernelInformation.GetInputCount() == 3);
ML_CHECK_VALID_ARGUMENT(kernelInformation.GetOutputCount() == 1);
auto& attributes = kernelInformation.GetAttributes();
const std::vector<DimensionType> inputDimensions = shapeInformation.GetInputTensorShape(0);
std::vector<uint32_t> outputDimensions;
m_onnxAxis = attributes.template GetOptionalAttribute<int32_t>(AttrName::Axis, -1);
// Get 'depth' tensor, which is really a scalar for the output size along the given axis.
MLOperatorTensor shapeTensor = kernelInformation.GetConstantInputTensor(1);
auto indicesShape = shapeInformation.GetInputTensorShape(0);
m_absoluteAxis = HandleNegativeAxis(m_onnxAxis, gsl::narrow_cast<uint32_t>(indicesShape.size() + 1));
// The shape tensor ('depth') is a 0D tensor holding the size for the output tensor along the specified axis.
// It must be registered as OrtMemType::OrtMemTypeCPUInput for CPU read access.
const int64_t depth64 = ReadScalarTensorCastToInt64(shapeTensor);
ML_CHECK_VALID_ARGUMENT(depth64 > 0, "Negative or zero 'depth' values for OneHot are illegal.");
const uint32_t depth = gsl::narrow_cast<uint32_t>(depth64);
m_outputDimensions.assign(indicesShape.begin(), indicesShape.end());
m_outputDimensions.insert(m_outputDimensions.begin() + m_absoluteAxis, depth);
}
std::vector<EdgeShapes> OneHotHelper::GetOutputShapes(const MLShapeInferenceContext& shapeInfo) const
{
return { EdgeShapes(m_outputDimensions) };

View file

@ -276,6 +276,7 @@ namespace OperatorHelper
static const int sc_sinceVer_Concat = 13;
static const int sc_sinceVer_Constant = 13;
static const int sc_sinceVer_DepthToSpace = 13;
static const int sc_sinceVer_DequantizeLinear = 13;
static const int sc_sinceVer_Div = 13;
static const int sc_sinceVer_Equal = 13;
static const int sc_sinceVer_Erf = 13;
@ -303,6 +304,7 @@ namespace OperatorHelper
static const int sc_sinceVer_Neg = 13;
static const int sc_sinceVer_Pad = 13;
static const int sc_sinceVer_Pow = 13;
static const int sc_sinceVer_QuantizeLinear = 13;
static const int sc_sinceVer_Reciprocal = 13;
static const int sc_sinceVer_ReduceL1 = 13;
static const int sc_sinceVer_ReduceL2 = 13;
@ -312,23 +314,28 @@ namespace OperatorHelper
static const int sc_sinceVer_ReduceMean = 13;
static const int sc_sinceVer_ReduceMin = 13;
static const int sc_sinceVer_ReduceProd = 13;
static const int sc_sinceVer_ReduceSum = 13;
static const int sc_sinceVer_ReduceSumSquare = 13;
static const int sc_sinceVer_Relu = 13;
static const int sc_sinceVer_Reshape = 13;
static const int sc_sinceVer_Resize = 13;
static const int sc_sinceVer_Scatter = 13;
static const int sc_sinceVer_ScatterElements = 13;
static const int sc_sinceVer_ScatterND = 13;
static const int sc_sinceVer_Sigmoid = 13;
static const int sc_sinceVer_Sign = 13;
static const int sc_sinceVer_Slice = 13;
static const int sc_sinceVer_Split = 13;
static const int sc_sinceVer_SpaceToDepth = 13;
static const int sc_sinceVer_Sqrt = 13;
static const int sc_sinceVer_Squeeze = 13;
static const int sc_sinceVer_Sub = 13;
static const int sc_sinceVer_Sum = 13;
static const int sc_sinceVer_Tanh = 13;
static const int sc_sinceVer_Tile = 13;
static const int sc_sinceVer_Transpose = 13;
static const int sc_sinceVer_Upsample = 13;
static const int sc_sinceVer_Unsqueeze = 13;
static const int sc_sinceVer_ReduseSum = 13;
} // namespace OnnxOperatorSet13
namespace MsftOperatorSet1

View file

@ -4,7 +4,7 @@
#pragma once
#include "OperatorHelper.h"
#include "OperatorRegistration.h"
#include "OperatorVersions.h"
namespace SchemaInferenceOverrider
{
@ -61,7 +61,7 @@ namespace SchemaInferenceOverrider
}
}
#pragma push_macro("REGISTER_FUSED_OP_SCHEMA")
#pragma push_macro("OVERRIDE_SCHEMA")
#define OVERRIDE_SCHEMA(version, isLatest, opName) \
OverrideSchemaInferenceFunction<OperatorHelper::ShapeInferenceHelper_##opName>( \
#opName, OperatorHelper::OnnxOperatorSet##version##::sc_sinceVer_##opName, isLatest, gsl::span<uint32_t>());
@ -82,7 +82,7 @@ OverrideSchemaInferenceFunction<OperatorHelper::ShapeInferenceHelper_##shapeInfe
OVERRIDE_SCHEMA_EX( 7, false, Upsample, Upsample7);
OVERRIDE_SCHEMA_EX( 9, true, Upsample, Upsample9, 1);
OVERRIDE_SCHEMA_EX( 7, true, Slice, Slice7);
OVERRIDE_SCHEMA( 7, true, Split);
OVERRIDE_SCHEMA_EX( 7, true, Split, Split7);
OVERRIDE_SCHEMA_EX( 7, true, Tile, Tile, 1);
OVERRIDE_SCHEMA_EX( 8, true, Expand, Expand, 1);
OVERRIDE_SCHEMA( 8, true, MaxPool);
@ -91,6 +91,6 @@ OverrideSchemaInferenceFunction<OperatorHelper::ShapeInferenceHelper_##shapeInfe
}
#pragma pop_macro("OVERRIDE_SCHEMA_EX")
#pragma pop_macro("REGISTER_FUSED_OP_SCHEMA")
#pragma pop_macro("OVERRIDE_SCHEMA")
}

View file

@ -17,7 +17,7 @@
#include "core/providers/dml/DmlExecutionProvider/src/ErrorHandling.h"
#include "core/providers/dml/OperatorAuthorHelper/MLOperatorAuthorHelper.h"
#include "core/providers/dml/OperatorAuthorHelper/OperatorHelper.h"
#include "core/providers/dml/OperatorAuthorHelper/OperatorRegistration.h"
#include "core/providers/dml/OperatorAuthorHelper/OperatorVersions.h"
#include "core/graph/constants.h"
#include "CustomNullOp.h"
#include <wil/wrl.h>