[DML EP] Register pad18 (#15985)

### Description
<!-- Describe your changes. -->
Pad18 adds the `axes` input, which is used to indicate what axes the
padding values should be applied to. Add logic to manipulate paddings
into DML padding operator inputs.


### Motivation and Context
<!-- - Why is this change required? What problem does it solve?
- If it fixes an open issue, please link to the issue here. -->

---------

Co-authored-by: Linnea May <linneamay@microsoft.com>
This commit is contained in:
Linnea May 2023-05-23 18:25:36 -07:00 committed by GitHub
parent bcd8b73343
commit 954ea6604a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 73 additions and 32 deletions

View file

@ -1044,7 +1044,8 @@ Do not modify directly.*
|PRelu|*in* X:**T**<br> *in* slope:**T**<br> *out* Y:**T**|16+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int8)|
|||9+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int8)|
|||7+|**T** = tensor(float), tensor(float16)|
|Pad|*in* data:**T**<br> *in* pads:**tensor(int64)**<br> *in* constant_value:**T**<br> *in* axes:**Tind**<br> *out* output:**T**<br><br>or<br><br>*in* data:**T**<br> *in* pads:**tensor(int64)**<br> *in* constant_value:**T**<br> *out* output:**T**<br><br>or<br><br>*in* data:**T**<br> *out* output:**T**|13+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)|
|Pad|*in* data:**T**<br> *in* pads:**tensor(int64)**<br> *in* constant_value:**T**<br> *in* axes:**Tind**<br> *out* output:**T**<br><br>or<br><br>*in* data:**T**<br> *in* pads:**tensor(int64)**<br> *in* constant_value:**T**<br> *out* output:**T**<br><br>or<br><br>*in* data:**T**<br> *out* output:**T**|18+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)|
|||13+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)|
|||11+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)|
|||2+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)|
|ParametricSoftplus|*in* X:**T**<br> *out* Y:**T**|1+|**T** = tensor(float), tensor(float16)|

View file

@ -15,7 +15,7 @@ public:
{
const uint32_t inputCount = kernelInfo.GetInputCount();
ML_CHECK_VALID_ARGUMENT((opsetVersion >= 2 && opsetVersion < 11 && inputCount == 1)
|| (opsetVersion >= 11 && inputCount >= 2 && inputCount <= 3));
|| (opsetVersion >= 11 && inputCount >= 2 && inputCount <= 4));
ML_CHECK_VALID_ARGUMENT(kernelInfo.GetOutputCount() == 1);
std::vector<std::optional<uint32_t>> kernelInputIndices = { 0 }; // Only bind GPU to first 'data' tensor.
@ -68,12 +68,12 @@ public:
paddingDesc.EndPadding = m_endPadding.data();
// PaddingValueDataType will always be equal to inputDataTensorDataType
// Assigning paddingValueDataType to inputDataTensorDataType because this field
// has to be assigned even if program does not go through below conditional
// has to be assigned even if program does not go through below conditional
// logic for some corner test case (like opsetVersion >= 11, but no validInput at index 2)
// Same applies to paddingValue.
paddingDesc.PaddingValueDataType = this->m_inputTensorDescs[0].GetDmlDataType();
CastToClampedScalarUnion<float>(paddingDesc.PaddingValueDataType, 0.0f, /*out*/&paddingDesc.PaddingValue);
// Read the constant value which can come from an attribute or tensor.
if (opsetVersion >= 11)
{
@ -107,7 +107,7 @@ void CALLBACK QueryPad(IMLOperatorSupportQueryContextPrivate* context, /*out*/ b
*isSupported = true;
MLOperatorAttributes attributes(context);
std::vector<int32_t> padding = attributes.GetOptionalAttributeVectorInt32(AttrName::Pads);
*isSupported = std::none_of(padding.begin(), padding.end(), [](int32_t padCount) {return padCount < 0; });
}
@ -115,5 +115,6 @@ void CALLBACK QueryPad(IMLOperatorSupportQueryContextPrivate* context, /*out*/ b
DML_OP_DEFINE_CREATION_FUNCTION(Pad7, VersionedKernel<DmlOperatorPadding, 7>);
DML_OP_DEFINE_CREATION_FUNCTION(Pad11, VersionedKernel<DmlOperatorPadding, 11>);
DML_OP_DEFINE_CREATION_FUNCTION(Pad13, VersionedKernel<DmlOperatorPadding, 13>);
DML_OP_DEFINE_CREATION_FUNCTION(Pad18, VersionedKernel<DmlOperatorPadding, 18>);
} // namespace Dml

View file

@ -292,6 +292,7 @@ DML_OP_EXTERN_CREATION_FUNCTION(Slice13);
DML_OP_EXTERN_CREATION_FUNCTION(Pad7);
DML_OP_EXTERN_CREATION_FUNCTION(Pad11);
DML_OP_EXTERN_CREATION_FUNCTION(Pad13);
DML_OP_EXTERN_CREATION_FUNCTION(Pad18);
DML_OP_EXTERN_CREATION_FUNCTION(SpaceToDepth);
DML_OP_EXTERN_CREATION_FUNCTION(DepthToSpace);
DML_OP_EXTERN_CREATION_FUNCTION(Sqrt);
@ -650,6 +651,7 @@ constexpr static OperatorRegistrationInformation operatorRegistrationInformation
{REG_INFO_VER( 7, Pad, typeNameListDefault, supportedTypeListAllScalars, DmlGraphSupport::Supported, requiredConstantCpuInputs(), std::nullopt, QueryPad)},
{REG_INFO_VER( 11, Pad, typeNameListDefault, supportedTypeListAllScalars, DmlGraphSupport::Supported, requiredConstantCpuInputs(1, 2) /*pads, value*/)}, // https://microsoft.visualstudio.com/OS/_workitems/edit/26007728
{REG_INFO_VER( 13, Pad, typeNameListDefault, supportedTypeListAllScalars, DmlGraphSupport::Supported, requiredConstantCpuInputs(1, 2) /*pads, value*/)}, // https://microsoft.visualstudio.com/OS/_workitems/edit/26007728
{REG_INFO_VER( 18, Pad, typeNameListDefault, supportedTypeListAllScalars, DmlGraphSupport::Supported, requiredConstantCpuInputs(1, 2, 3) /*pads, value, axes*/)},
{REG_INFO( 7, SpaceToDepth, typeNameListDefault, supportedTypeListAllScalars, DmlGraphSupport::Supported)},
{REG_INFO( 13, SpaceToDepth, typeNameListDefault, supportedTypeListAllScalars, DmlGraphSupport::Supported)},
{REG_INFO( 7, DepthToSpace, typeNameListDefault, supportedTypeListAllScalars, DmlGraphSupport::Supported)},

View file

@ -41,6 +41,21 @@ namespace OperatorHelper
}
}
void 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 empty axes should be treated as a nop, then just leave them as-is.
if (axes.empty() && !treatEmptyAsNop)
{
axes.resize(inputShape.size());
std::iota(axes.begin(), axes.end(), 0);
}
}
float CastFloat16ToFloat32(uint16_t input)
{
// Promote float16m10e5s1 to float32m23e8s1.
@ -1122,12 +1137,36 @@ namespace OperatorHelper
}
ML_CHECK_VALID_ARGUMENT(padding.size() % 2 == 0, "Padding must be even count, including begin/end pairs.");
std::vector<uint32_t> inputShape = shapeInformation.GetInputTensorShape(0);
uint32_t dimCount = gsl::narrow_cast<uint32_t>(inputShape.size());
m_startPadding.resize(dimCount, 0);
m_endPadding.resize(dimCount, 0);
std::vector<int32_t> axes;
uint32_t dimCount = gsl::narrow_cast<uint32_t>(padding.size() / 2);
m_startPadding.resize(dimCount);
m_endPadding.resize(dimCount);
std::copy(padding.begin(), padding.begin() + dimCount, m_startPadding.begin());
std::copy(padding.begin() + dimCount, padding.begin() + dimCount * 2, m_endPadding.begin());
// Handle possible axes input
if (opsetVersion >= 18)
{
if (kernelInformation.IsInputValid(3))
{
ReadCpuLocalTensorIntoInt32(kernelInformation.GetConstantInputTensor(3), /*out*/ axes);
}
HandleEmptyAxes(axes, inputShape, false);
ML_CHECK_VALID_ARGUMENT(axes.size() * 2 == padding.size(), "The number of elements in padding should be 2 times the number of axes.");
HandleNegativeAxes(axes, dimCount);
}
else
{
HandleEmptyAxes(axes, inputShape, false);
}
uint32_t numAxes = gsl::narrow_cast<uint32_t>(axes.size());
for (int32_t i = 0; i < axes.size(); i++)
{
auto xi_begin = padding[i];
auto xi_end = padding[i+axes.size()];
m_startPadding[axes[i]] = xi_begin;
m_endPadding[axes[i]] = xi_end;
}
}
std::vector<EdgeShapes> PaddingHelper::GetOutputShapes(const MLShapeInferenceContext& shapeInfo) const
@ -1360,21 +1399,6 @@ namespace OperatorHelper
}
}
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 empty axes should be treated as a nop, then just leave them as-is.
if (axes.empty() && !treatEmptyAsNop)
{
axes.resize(inputShape.size());
std::iota(axes.begin(), axes.end(), 0);
}
}
void EinSumHelper::Initialize()
{
ParseEquationComponents();

View file

@ -687,13 +687,6 @@ public:
std::vector<EdgeShapes> GetOutputShapes(const MLShapeInferenceContext& shapeInfo) const;
private:
static void HandleEmptyAxes(
/*inout*/std::vector<int32_t>& onnxAxes,
gsl::span<const uint32_t> inputShape,
bool treatEmptyAsNop
);
protected:
std::vector<int32_t> m_axes;
int m_keepDims = 0; // Keep the dimensions rather than removing size 1 dimensions.
@ -1526,6 +1519,7 @@ using ShapeInferenceHelper_Slice13 = VersionedOpsetHelper<SliceHelper, 13>; // N
using ShapeInferenceHelper_Pad7 = VersionedOpsetHelper<PaddingHelper, 7>;
using ShapeInferenceHelper_Pad11 = VersionedOpsetHelper<PaddingHelper, 11>;
using ShapeInferenceHelper_Pad13 = VersionedOpsetHelper<PaddingHelper, 13>;
using ShapeInferenceHelper_Pad18 = VersionedOpsetHelper<PaddingHelper, 18>;
using ShapeInferenceHelper_SpaceToDepth = SpaceToDepthHelper;
using ShapeInferenceHelper_DepthToSpace = DepthToSpaceHelper;

View file

@ -404,6 +404,7 @@ namespace OperatorHelper
static const int sc_sinceVer_BitwiseOr = 18;
static const int sc_sinceVer_BitwiseXor = 18;
static const int sc_sinceVer_BitwiseNot = 18;
static const int sc_sinceVer_Pad = 18;
static const int sc_sinceVer_Split = 18;
}

View file

@ -1011,6 +1011,24 @@ TEST(PadOpTest, ConstantPadAxesTest3) {
test.Run(OpTester::ExpectResult::kExpectSuccess, "", {kTensorrtExecutionProvider, kNnapiExecutionProvider});
}
TEST(PadOpTest, ConstantPadAxesTest4) {
OpTester test("Pad", 18);
test.AddAttribute("mode", "constant");
test.AddInput<float>("data", {1, 2, 2, 2},
{1.0f, 1.0f,
1.0f, 1.0f,
1.0f, 1.0f,
1.0f, 1.0f});
test.AddInput<int64_t>("pads", {8}, {0, 0, 0, 1, 0, 0, 0, 1}, true /* pads_is_initializer */);
test.AddInput<float>("value", {1}, {0.0f}, true /* value_is_initializer */);
test.AddOutput<float>("output", {1, 2, 2, 4},
{0.0f, 1.0f, 1.0f, 0.0f,
0.0f, 1.0f, 1.0f, 0.0f,
0.0f, 1.0f, 1.0f, 0.0f,
0.0f, 1.0f, 1.0f, 0.0f});
test.Run(OpTester::ExpectResult::kExpectSuccess, "", {kTensorrtExecutionProvider, kNnapiExecutionProvider});
}
TEST(PadOpTest, ConstantPadAxesOutOfOrder) {
// Specified out of order axes values
OpTester test("Pad", 18);