Fix case for upper and lower padding when strides are present. (#11)
* Fix case for upper and lower padding when strides are present. * Address comments. * Code clean-up. * Fix tests.
This commit is contained in:
parent
fe3279e721
commit
e8a0b47e10
|
@ -782,15 +782,15 @@ void ONNXConvNoBiasOp::inferShapes() {
|
||||||
|
|
||||||
auto dataTy = X().getType().cast<RankedTensorType>();
|
auto dataTy = X().getType().cast<RankedTensorType>();
|
||||||
auto weightTy = W().getType().cast<RankedTensorType>();
|
auto weightTy = W().getType().cast<RankedTensorType>();
|
||||||
auto dataShape = dataTy.getShape();
|
auto inDataShape = dataTy.getShape();
|
||||||
auto weightShape = weightTy.getShape();
|
auto weightShape = weightTy.getShape();
|
||||||
|
|
||||||
// Lowest supported convolution is a one dimensional convolution.
|
// Lowest supported convolution is a one dimensional convolution.
|
||||||
if (dataShape.size() < 3)
|
if (inDataShape.size() < 3)
|
||||||
emitError("Data input shape must be at least (NxCxD1)");
|
emitError("Data input shape must be at least (NxCxD1)");
|
||||||
|
|
||||||
// Check that shape of weight and data have same length.
|
// Check that shape of weight and data have same length.
|
||||||
if (dataShape.size() != weightShape.size())
|
if (inDataShape.size() != weightShape.size())
|
||||||
emitError("Weight size not compatible with data size");
|
emitError("Weight size not compatible with data size");
|
||||||
|
|
||||||
// Required attribute auto_pad defaults to NOTSET.
|
// Required attribute auto_pad defaults to NOTSET.
|
||||||
|
@ -799,8 +799,8 @@ void ONNXConvNoBiasOp::inferShapes() {
|
||||||
int64_t group =
|
int64_t group =
|
||||||
ONNXConvNoBiasOp::group().getSExtValue(); //.getLimitedValue();
|
ONNXConvNoBiasOp::group().getSExtValue(); //.getLimitedValue();
|
||||||
// Check that the X.shape[1] == (W.shape[1] * group) == C condition holds.
|
// Check that the X.shape[1] == (W.shape[1] * group) == C condition holds.
|
||||||
if (dataShape[1] != -1 && weightShape[1] != -1 &&
|
if (inDataShape[1] != -1 && weightShape[1] != -1 &&
|
||||||
dataShape[1] != (weightShape[1] * group))
|
inDataShape[1] != (weightShape[1] * group))
|
||||||
emitError("Channel dimension mismatch");
|
emitError("Channel dimension mismatch");
|
||||||
|
|
||||||
// Note: the value of the group attribut only impacts the way the
|
// Note: the value of the group attribut only impacts the way the
|
||||||
|
@ -811,7 +811,7 @@ void ONNXConvNoBiasOp::inferShapes() {
|
||||||
//
|
//
|
||||||
SmallVector<int64_t, 2> dims;
|
SmallVector<int64_t, 2> dims;
|
||||||
// Insert batch size.
|
// Insert batch size.
|
||||||
dims.emplace_back(dataShape[0]);
|
dims.emplace_back(inDataShape[0]);
|
||||||
// Insert number of filters being applied (number of output channels).
|
// Insert number of filters being applied (number of output channels).
|
||||||
dims.emplace_back(weightShape[0]);
|
dims.emplace_back(weightShape[0]);
|
||||||
|
|
||||||
|
@ -821,22 +821,22 @@ void ONNXConvNoBiasOp::inferShapes() {
|
||||||
//
|
//
|
||||||
SmallVector<int64_t, 2> outSpatialDims;
|
SmallVector<int64_t, 2> outSpatialDims;
|
||||||
// Number of spatial dimensions.
|
// Number of spatial dimensions.
|
||||||
int32_t nDims = dataShape.size() - 2;
|
int32_t nSpatialDims = inDataShape.size() - 2;
|
||||||
|
|
||||||
// Initialize dimenions based on the input spatial dimensions.
|
// Initialize dimenions based on the input spatial dimensions.
|
||||||
for (int i = 2; i < dataShape.size(); ++i)
|
for (int i = 2; i < inDataShape.size(); ++i)
|
||||||
outSpatialDims.emplace_back(dataShape[i]);
|
outSpatialDims.emplace_back(inDataShape[i]);
|
||||||
|
|
||||||
// Use kernel_shape attribute if present otherwise use size from weight
|
// Use kernel_shape attribute if present otherwise use size from weight
|
||||||
// argument.
|
// argument.
|
||||||
SmallVector<int64_t, 2> kernelDims;
|
SmallVector<int64_t, 2> kernelDims;
|
||||||
if (auto kernelShape = kernel_shapeAttr()) {
|
if (auto kernelShape = kernel_shapeAttr()) {
|
||||||
if (ArrayAttrSize(kernelShape) != nDims)
|
if (ArrayAttrSize(kernelShape) != nSpatialDims)
|
||||||
emitError("kernel_shape length incompatible with spatial dimensions");
|
emitError("kernel_shape length incompatible with spatial dimensions");
|
||||||
for (int i = 0; i < nDims; ++i)
|
for (int i = 0; i < nSpatialDims; ++i)
|
||||||
kernelDims.emplace_back(ArrayAttrIntVal(kernelShape, i));
|
kernelDims.emplace_back(ArrayAttrIntVal(kernelShape, i));
|
||||||
} else {
|
} else {
|
||||||
for (int i = 0; i < nDims; ++i)
|
for (int i = 0; i < nSpatialDims; ++i)
|
||||||
kernelDims.emplace_back(weightShape[i + 2]);
|
kernelDims.emplace_back(weightShape[i + 2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -852,43 +852,70 @@ void ONNXConvNoBiasOp::inferShapes() {
|
||||||
// From a dimensionality perspective the kernel size becomes the dilated
|
// From a dimensionality perspective the kernel size becomes the dilated
|
||||||
// kernel size.
|
// kernel size.
|
||||||
if (auto dilations = dilationsAttr()) {
|
if (auto dilations = dilationsAttr()) {
|
||||||
if (ArrayAttrSize(dilations) != nDims)
|
if (ArrayAttrSize(dilations) != nSpatialDims)
|
||||||
emitError("dilations length incompatible with spatial dimensions");
|
emitError("dilations length incompatible with spatial dimensions");
|
||||||
for (int i = 0; i < nDims; ++i)
|
for (int i = 0; i < nSpatialDims; ++i)
|
||||||
kernelDims[i] =
|
kernelDims[i] =
|
||||||
(kernelDims[i] + 1) * ArrayAttrIntVal(dilations, i) - 1;
|
(kernelDims[i] + 1) * ArrayAttrIntVal(dilations, i) - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Subtract kernel dimensions from input data dimensions.
|
// Subtract kernel dimensions from input data dimensions.
|
||||||
for (int i = 0; i < nDims; ++i)
|
for (int i = 0; i < nSpatialDims; ++i)
|
||||||
outSpatialDims[i] -= kernelDims[i];
|
outSpatialDims[i] -= kernelDims[i];
|
||||||
|
|
||||||
|
// Array which holds the padding information.
|
||||||
|
SmallVector<int64_t, 2> actualPads(2 * nSpatialDims, 0);
|
||||||
|
auto stridesAttr = ONNXConvNoBiasOp::stridesAttr();
|
||||||
|
|
||||||
// Add padding information.
|
// Add padding information.
|
||||||
if (autoPad == "NOTSET") {
|
if (autoPad == "NOTSET") {
|
||||||
// Use pads to to determine the padding. If attribute is not
|
// Use pads to to determine the padding. If attribute is not
|
||||||
// present then pads is considered to be all zeros (no padding).
|
// present then pads is considered to be all zeros (no padding).
|
||||||
if (auto pads = padsAttr()) {
|
if (auto pads = padsAttr()) {
|
||||||
// pads consists of two entries for each spatial axis.
|
// pads consists of two entries for each spatial axis.
|
||||||
if (ArrayAttrSize(pads) != 2 * nDims)
|
if (ArrayAttrSize(pads) != 2 * nSpatialDims)
|
||||||
emitError("pads size is not twice the spatial size");
|
emitError("pads size is not twice the spatial size");
|
||||||
|
|
||||||
for (int i = 0; i < nDims; ++i) {
|
for (int i = 0; i < nSpatialDims; ++i) {
|
||||||
// Padding for beginning of axis.
|
// Padding for beginning of axis.
|
||||||
outSpatialDims[i] += ArrayAttrIntVal(pads, i);
|
outSpatialDims[i] += ArrayAttrIntVal(pads, i);
|
||||||
// Padding for end of axis.
|
// Padding for end of axis.
|
||||||
outSpatialDims[i] += ArrayAttrIntVal(pads, i + nDims);
|
outSpatialDims[i] += ArrayAttrIntVal(pads, i + nSpatialDims);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (autoPad == "SAME_UPPER" || autoPad == "SAME_LOWER") {
|
} else if (autoPad == "SAME_UPPER" || autoPad == "SAME_LOWER") {
|
||||||
// Pad input so that output size matches input size.
|
// Pad input so that output size matches input size.
|
||||||
// Each spatial dimension needs to be padded by a total of:
|
// Each spatial dimension needs to be padded by a total of:
|
||||||
//
|
//
|
||||||
// K - 1
|
// stride * (InDim - 1) + KerDim - InDim
|
||||||
//
|
//
|
||||||
// where K is a kernel spatial dimension.
|
// where K is a kernel spatial dimension.
|
||||||
// Pad as if stride is 1.
|
for (int i = 0; i < nSpatialDims; ++i) {
|
||||||
for (int i = 0; i < nDims; ++i)
|
// If strides are given use them otherwise stride is 1.
|
||||||
outSpatialDims[i] += kernelDims[i] - 1;
|
int64_t stride = 1;
|
||||||
|
if (stridesAttr)
|
||||||
|
stride = ArrayAttrIntVal(stridesAttr, i);
|
||||||
|
|
||||||
|
// Compute necessary padding. The input dimensions are stored in
|
||||||
|
// inDataShape.
|
||||||
|
int64_t totalPadding = stride * (inDataShape[i + 2] - 1) +
|
||||||
|
kernelDims[i] - inDataShape[i + 2];
|
||||||
|
|
||||||
|
// Adjust current output value with the value of the padding.
|
||||||
|
// When dividing by stride later on, the output dimension should
|
||||||
|
// be equal to the input dimension.
|
||||||
|
outSpatialDims[i] += totalPadding;
|
||||||
|
|
||||||
|
// Record the upper and lower axis padding.
|
||||||
|
actualPads[i] = actualPads[i + nSpatialDims] = totalPadding / 2;
|
||||||
|
if (totalPadding % 2 != 0) {
|
||||||
|
if (autoPad == "SAME_LOWER") {
|
||||||
|
actualPads[i]++;
|
||||||
|
} else {
|
||||||
|
actualPads[i + nSpatialDims]++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
} else if (autoPad == "VALID") {
|
} else if (autoPad == "VALID") {
|
||||||
// No padding
|
// No padding
|
||||||
} else {
|
} else {
|
||||||
|
@ -896,18 +923,34 @@ void ONNXConvNoBiasOp::inferShapes() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Strides
|
// Strides
|
||||||
if (auto strides = ONNXConvNoBiasOp::stridesAttr()) {
|
if (stridesAttr) {
|
||||||
if (ArrayAttrSize(strides) != nDims)
|
if (ArrayAttrSize(stridesAttr) != nSpatialDims)
|
||||||
emitError("strides length incompatible with spatial dimensions");
|
emitError("strides length incompatible with spatial dimensions");
|
||||||
for (int i = 0; i < nDims; ++i) {
|
for (int i = 0; i < nSpatialDims; ++i) {
|
||||||
int64_t stride = ArrayAttrIntVal(strides, i);
|
int64_t stride = ArrayAttrIntVal(stridesAttr, i);
|
||||||
outSpatialDims[i] = floor(outSpatialDims[i] / stride);
|
outSpatialDims[i] = floor(outSpatialDims[i] / stride);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < nDims; ++i)
|
for (int i = 0; i < nSpatialDims; ++i)
|
||||||
outSpatialDims[i] += 1;
|
outSpatialDims[i] += 1;
|
||||||
|
|
||||||
|
// Check input and output sizes match.
|
||||||
|
if (autoPad == "SAME_UPPER" || autoPad == "SAME_LOWER") {
|
||||||
|
for (int i = 0; i < nSpatialDims; ++i)
|
||||||
|
if (outSpatialDims[i] != inDataShape[i + 2])
|
||||||
|
emitError("input and output spatial dimension mismatch");
|
||||||
|
|
||||||
|
// Set pads values in attributes.
|
||||||
|
auto builder = mlir::Builder(this->getContext());
|
||||||
|
ArrayRef<int64_t> defaultRefs(actualPads);
|
||||||
|
padsAttr(builder.getI64ArrayAttr(defaultRefs));
|
||||||
|
|
||||||
|
// Change auto padding attribute to NOTSET since padding values
|
||||||
|
// are now explicitly included in the operation.
|
||||||
|
auto_padAttr(builder.getStringAttr("NOTSET"));
|
||||||
|
}
|
||||||
|
|
||||||
dims.append(outSpatialDims.begin(), outSpatialDims.end());
|
dims.append(outSpatialDims.begin(), outSpatialDims.end());
|
||||||
getResult().setType(RankedTensorType::get(dims, dataTy.getElementType()));
|
getResult().setType(RankedTensorType::get(dims, dataTy.getElementType()));
|
||||||
}
|
}
|
||||||
|
|
|
@ -195,7 +195,7 @@ func @test_conv_no_bias_4(%arg0 : tensor<1x2x32x64xf32>, %arg1 : tensor<5x2x6x10
|
||||||
"std.return"(%0) : (tensor<*xf32>) -> ()
|
"std.return"(%0) : (tensor<*xf32>) -> ()
|
||||||
|
|
||||||
// CHECK-LABEL: test_conv_no_bias_4
|
// CHECK-LABEL: test_conv_no_bias_4
|
||||||
// CHECK: [[RES_ATTR:%.+]] = "onnx.ConvNoBias"(%arg0, %arg1) {auto_pad = "SAME_UPPER", group = 1 : i64} : (tensor<1x2x32x64xf32>, tensor<5x2x6x10xf32>) -> tensor<1x5x32x64xf32>
|
// CHECK: [[RES_ATTR:%.+]] = "onnx.ConvNoBias"(%arg0, %arg1) {auto_pad = "NOTSET", group = 1 : i64, pads = [2, 4, 3, 5]} : (tensor<1x2x32x64xf32>, tensor<5x2x6x10xf32>) -> tensor<1x5x32x64xf32>
|
||||||
// CHECK: return [[RES_ATTR]] : tensor<1x5x32x64xf32>
|
// CHECK: return [[RES_ATTR]] : tensor<1x5x32x64xf32>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -204,7 +204,7 @@ func @test_conv_no_bias_5(%arg0 : tensor<1x2x32x64xf32>, %arg1 : tensor<5x2x6x10
|
||||||
"std.return"(%0) : (tensor<*xf32>) -> ()
|
"std.return"(%0) : (tensor<*xf32>) -> ()
|
||||||
|
|
||||||
// CHECK-LABEL: test_conv_no_bias_5
|
// CHECK-LABEL: test_conv_no_bias_5
|
||||||
// CHECK: [[RES_ATTR:%.+]] = "onnx.ConvNoBias"(%arg0, %arg1) {auto_pad = "SAME_LOWER", group = 1 : i64} : (tensor<1x2x32x64xf32>, tensor<5x2x6x10xf32>) -> tensor<1x5x32x64xf32>
|
// CHECK: [[RES_ATTR:%.+]] = "onnx.ConvNoBias"(%arg0, %arg1) {auto_pad = "NOTSET", group = 1 : i64, pads = [3, 5, 2, 4]} : (tensor<1x2x32x64xf32>, tensor<5x2x6x10xf32>) -> tensor<1x5x32x64xf32>
|
||||||
// CHECK: return [[RES_ATTR]] : tensor<1x5x32x64xf32>
|
// CHECK: return [[RES_ATTR]] : tensor<1x5x32x64xf32>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -238,8 +238,8 @@ func @test_conv_no_bias_8(%arg0 : tensor<1x2x32x64xf32>, %arg1 : tensor<5x2x6x7x
|
||||||
"std.return"(%0) : (tensor<*xf32>) -> ()
|
"std.return"(%0) : (tensor<*xf32>) -> ()
|
||||||
|
|
||||||
// CHECK-LABEL: test_conv_no_bias_8
|
// CHECK-LABEL: test_conv_no_bias_8
|
||||||
// CHECK: [[RES_ATTR:%.+]] = "onnx.ConvNoBias"(%arg0, %arg1) {auto_pad = "SAME_UPPER", group = 1 : i64, strides = [2, 3]} : (tensor<1x2x32x64xf32>, tensor<5x2x6x7xf32>) -> tensor<1x5x16x22xf32>
|
// CHECK: [[RES_ATTR:%.+]] = "onnx.ConvNoBias"(%arg0, %arg1) {auto_pad = "NOTSET", group = 1 : i64, pads = [18, 66, 18, 66], strides = [2, 3]} : (tensor<1x2x32x64xf32>, tensor<5x2x6x7xf32>) -> tensor<1x5x32x64xf32>
|
||||||
// CHECK: return [[RES_ATTR]] : tensor<1x5x16x22xf32>
|
// CHECK: return [[RES_ATTR]] : tensor<1x5x32x64xf32>
|
||||||
}
|
}
|
||||||
|
|
||||||
/// dilations attribute.
|
/// dilations attribute.
|
||||||
|
@ -269,27 +269,30 @@ func @test_conv_no_bias_10(%arg0 : tensor<1x2x32x64xf32>, %arg1 : tensor<5x2x6x7
|
||||||
func @test_conv_no_bias_11(%arg0 : tensor<1x2x32x64xf32>, %arg1 : tensor<5x2x6x7xf32>) -> tensor<*xf32> {
|
func @test_conv_no_bias_11(%arg0 : tensor<1x2x32x64xf32>, %arg1 : tensor<5x2x6x7xf32>) -> tensor<*xf32> {
|
||||||
%0 = "onnx.ConvNoBias"(%arg0, %arg1) {auto_pad = "SAME_UPPER", group = 1 : i64, dilations = [2, 3]} : (tensor<1x2x32x64xf32>, tensor<5x2x6x7xf32>) -> tensor<*xf32>
|
%0 = "onnx.ConvNoBias"(%arg0, %arg1) {auto_pad = "SAME_UPPER", group = 1 : i64, dilations = [2, 3]} : (tensor<1x2x32x64xf32>, tensor<5x2x6x7xf32>) -> tensor<*xf32>
|
||||||
"std.return"(%0) : (tensor<*xf32>) -> ()
|
"std.return"(%0) : (tensor<*xf32>) -> ()
|
||||||
}
|
|
||||||
// CHECK-LABEL: test_conv_no_bias_11
|
// CHECK-LABEL: test_conv_no_bias_11
|
||||||
// CHECK: [[RES_ATTR:%.+]] = "onnx.ConvNoBias"(%arg0, %arg1) {auto_pad = "SAME_UPPER", dilations = [2, 3], group = 1 : i64} : (tensor<1x2x32x64xf32>, tensor<5x2x6x7xf32>) -> tensor<1x5x32x64xf32>
|
// CHECK: [[RES_ATTR:%.+]] = "onnx.ConvNoBias"(%arg0, %arg1) {auto_pad = "NOTSET", dilations = [2, 3], group = 1 : i64, pads = [6, 11, 6, 11]} : (tensor<1x2x32x64xf32>, tensor<5x2x6x7xf32>) -> tensor<1x5x32x64xf32>
|
||||||
// CHECK: return [[RES_ATTR]] : tensor<1x5x32x64xf32>
|
// CHECK: return [[RES_ATTR]] : tensor<1x5x32x64xf32>
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test PadConstantValuePad
|
||||||
|
|
||||||
/// Test PadConstantValuePad_1
|
|
||||||
func @test_PadConstantValuePad_1(%arg0 : tensor<16x13xf32>) -> tensor<*xf32> {
|
func @test_PadConstantValuePad_1(%arg0 : tensor<16x13xf32>) -> tensor<*xf32> {
|
||||||
%0 = "onnx.PadConstantValuePad"(%arg0) {constant_value = 0.000000e+00 : f32, mode = "constant", pads = [0, 2, 0, 0]} : (tensor<16x13xf32>) -> tensor<*xf32>
|
%0 = "onnx.PadConstantValuePad"(%arg0) {constant_value = 0.000000e+00 : f32, mode = "constant", pads = [0, 2, 0, 0]} : (tensor<16x13xf32>) -> tensor<*xf32>
|
||||||
"std.return"(%0) : (tensor<*xf32>) -> ()
|
"std.return"(%0) : (tensor<*xf32>) -> ()
|
||||||
}
|
|
||||||
// CHECK-LABEL: test_PadConstantValuePad_1
|
// CHECK-LABEL: test_PadConstantValuePad_1
|
||||||
// CHECK: [[RES:%.+]] = "onnx.PadConstantValuePad"(%arg0) {constant_value = 0.000000e+00 : f32, mode = "constant", pads = [0, 2, 0, 0]} : (tensor<16x13xf32>) -> tensor<18x13xf32>
|
// CHECK: [[RES:%.+]] = "onnx.PadConstantValuePad"(%arg0) {constant_value = 0.000000e+00 : f32, mode = "constant", pads = [0, 2, 0, 0]} : (tensor<16x13xf32>) -> tensor<18x13xf32>
|
||||||
// CHECK: return [[RES]] : tensor<18x13xf32>
|
// CHECK: return [[RES]] : tensor<18x13xf32>
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test PadConstantPad
|
||||||
|
|
||||||
/// Test PadConstantPad_1
|
|
||||||
func @test_PadConstantPad_1(%arg0 : tensor<16x13xf32>, %arg1 : tensor<*xf32>) -> tensor<*xf32> {
|
func @test_PadConstantPad_1(%arg0 : tensor<16x13xf32>, %arg1 : tensor<*xf32>) -> tensor<*xf32> {
|
||||||
%0 = "onnx.PadConstantPad"(%arg0, %arg1) {mode = "constant", pads = [0, 2, 3, 1]} : (tensor<16x13xf32>, tensor<*xf32>) -> tensor<*xf32>
|
%0 = "onnx.PadConstantPad"(%arg0, %arg1) {mode = "constant", pads = [0, 2, 3, 1]} : (tensor<16x13xf32>, tensor<*xf32>) -> tensor<*xf32>
|
||||||
"std.return"(%0) : (tensor<*xf32>) -> ()
|
"std.return"(%0) : (tensor<*xf32>) -> ()
|
||||||
}
|
|
||||||
// CHECK-LABEL: test_PadConstantPad_1
|
// CHECK-LABEL: test_PadConstantPad_1
|
||||||
// CHECK: [[RES:%.+]] = "onnx.PadConstantPad"(%arg0, %arg1) {mode = "constant", pads = [0, 2, 3, 1]} : (tensor<16x13xf32>, tensor<*xf32>) -> tensor<18x17xf32>
|
// CHECK: [[RES:%.+]] = "onnx.PadConstantPad"(%arg0, %arg1) {mode = "constant", pads = [0, 2, 3, 1]} : (tensor<16x13xf32>, tensor<*xf32>) -> tensor<18x17xf32>
|
||||||
// CHECK: return [[RES]] : tensor<18x17xf32>
|
// CHECK: return [[RES]] : tensor<18x17xf32>
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue