Inference maxpool (#48)
* first steps for shape inference of maxpool * setps forward * ongoing * working version * first steps for shape inference of maxpool * setps forward * ongoing * working version * fix errors introduced by github merge * changes suggested by Doru * updates * requested fixes * reqested changes Co-authored-by: Gheorghe-Teodor Bercea <gt.bercea@gmail.com>
This commit is contained in:
parent
9fb826ae7e
commit
0d77840969
|
@ -128,7 +128,7 @@ def ONNXConvNoBiasOp:ONNX_Op<"ConvNoBias",
|
||||||
}
|
}
|
||||||
|
|
||||||
def ONNXMaxPoolSingleOutOp: ONNX_Op<"MaxPoolSingleOut",
|
def ONNXMaxPoolSingleOutOp: ONNX_Op<"MaxPoolSingleOut",
|
||||||
[NoSideEffect]> {
|
[NoSideEffect, DeclareOpInterfaceMethods<ShapeInferenceOpInterface>]> {
|
||||||
let summary = "ONNX MaxPool operation with a single output.";
|
let summary = "ONNX MaxPool operation with a single output.";
|
||||||
let description = [{
|
let description = [{
|
||||||
"ONNX MaxPool operation with a single output."
|
"ONNX MaxPool operation with a single output."
|
||||||
|
|
|
@ -746,6 +746,161 @@ void ONNXConvNoBiasOp::inferShapes() {
|
||||||
}
|
}
|
||||||
|
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
// MaxPoolSingleOut
|
||||||
|
|
||||||
|
void ONNXMaxPoolSingleOutOp::inferShapes() {
|
||||||
|
// Cannot infer shape if no shape exists.
|
||||||
|
if (!X().getType().isa<RankedTensorType>())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// 1) get shape of input
|
||||||
|
auto xTy = X().getType().cast<RankedTensorType>();
|
||||||
|
auto xShape = xTy.getShape();
|
||||||
|
auto xRank = xShape.size();
|
||||||
|
|
||||||
|
// 2) analyse parameters
|
||||||
|
// get kernel sizes from kernel_shape attribute
|
||||||
|
auto kernelShape = kernel_shape();
|
||||||
|
if (!kernelShape)
|
||||||
|
emitError("kernel_shape is a mandatory attribute for which there is no default.");
|
||||||
|
auto kernelShapeArray = kernelShape.getValue();
|
||||||
|
auto kernelRank = kernelShape.size();
|
||||||
|
if (kernelRank > xRank)
|
||||||
|
emitError("kernel_shape spatial dimension is too large.");
|
||||||
|
auto kernelOffset = xRank - kernelRank;
|
||||||
|
|
||||||
|
// ceil mode
|
||||||
|
auto ceilMode = ceil_mode().getSExtValue();
|
||||||
|
|
||||||
|
// dilatation
|
||||||
|
SmallVector<int64_t, 4> actualDilations;
|
||||||
|
auto dilationsOpt = dilations();
|
||||||
|
if (dilationsOpt.hasValue()) {
|
||||||
|
auto dilationsArray = dilationsOpt.getValue().getValue(); // opt -> attr -> array
|
||||||
|
if (dilationsArray.size() != kernelRank)
|
||||||
|
emitError("dialation rank is not the same as the spatial rank.");
|
||||||
|
// fill in the actual values
|
||||||
|
for (int i = 0; i < kernelRank; ++i) {
|
||||||
|
int64_t d = (dilationsArray[i]).cast<IntegerAttr>().getInt();
|
||||||
|
if (d < 1)
|
||||||
|
emitError("dialation value must be nonzero positive.");
|
||||||
|
actualDilations.emplace_back(d);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for(int i=0; i < kernelRank; ++i) {
|
||||||
|
actualDilations.emplace_back(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// storage order
|
||||||
|
|
||||||
|
// strides
|
||||||
|
SmallVector<int64_t, 4> actualStrides;
|
||||||
|
auto stridesOpt = strides();
|
||||||
|
if (stridesOpt.hasValue()) {
|
||||||
|
auto stridesArray = stridesOpt.getValue().getValue();
|
||||||
|
if (stridesArray.size() != kernelRank)
|
||||||
|
emitError("strides rank is not the same as the spatial rank.");
|
||||||
|
// fill in the actual values
|
||||||
|
for (int i = 0; i < kernelRank; ++i) {
|
||||||
|
int64_t s = (stridesArray[i]).cast<IntegerAttr>().getInt();
|
||||||
|
if (s < 1)
|
||||||
|
emitError("strides value must be nonzero positive.");
|
||||||
|
actualStrides.emplace_back(s);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for(int i=0; i < kernelRank; ++i) {
|
||||||
|
actualStrides.emplace_back(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// now try to find padding, getting auto_pad attribute first
|
||||||
|
auto autoPad = auto_pad();
|
||||||
|
// and then investigate the various different cases
|
||||||
|
SmallVector<int64_t, 4> actualPads;
|
||||||
|
auto defaultPads = false;
|
||||||
|
if (autoPad == "NOTSET") {
|
||||||
|
auto padsOpt = pads();
|
||||||
|
if (padsOpt.hasValue()) {
|
||||||
|
auto padsArray = padsOpt.getValue().getValue();
|
||||||
|
// pads consists of two entries for each spatial axis.
|
||||||
|
if (padsArray.size() != 2 * kernelRank)
|
||||||
|
emitError("pads rank is not twice the spatial rank.");
|
||||||
|
// fill in the actual values
|
||||||
|
for (int i = 0; i < 2*kernelRank; ++i) {
|
||||||
|
int64_t p = (padsArray[i]).cast<IntegerAttr>().getInt();
|
||||||
|
if (p < 0)
|
||||||
|
emitError("pads value must be nonnegative.");
|
||||||
|
actualPads.emplace_back(p);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// pads are not defined, default to value 0
|
||||||
|
defaultPads = true;
|
||||||
|
}
|
||||||
|
} else if (autoPad == "VALID") {
|
||||||
|
defaultPads = true;
|
||||||
|
} else if (autoPad == "SAME_UPPER" || autoPad == "SAME_LOWER") {
|
||||||
|
// init pad with zero
|
||||||
|
for(int i=0; i<2*kernelRank; ++i) {
|
||||||
|
actualPads.emplace_back(0);
|
||||||
|
}
|
||||||
|
for(int i=0; i<kernelRank; ++i) {
|
||||||
|
auto inputSpatialShape = xShape[kernelOffset + i];
|
||||||
|
auto kernelSpatialShape = (kernelShapeArray[i]).cast<IntegerAttr>().getInt();
|
||||||
|
auto dilations = actualDilations[i];
|
||||||
|
auto strideSpatialShape = actualStrides[i];
|
||||||
|
int64_t outputSpatialShape = ceil((1.0 * inputSpatialShape) /
|
||||||
|
(1.0 * strideSpatialShape));
|
||||||
|
auto sumOfPad = (outputSpatialShape - 1) * strideSpatialShape +
|
||||||
|
((kernelSpatialShape - 1) * dilations + 1) - inputSpatialShape;
|
||||||
|
actualPads[i] = actualPads[kernelRank + i] = sumOfPad / 2;
|
||||||
|
if (sumOfPad % 2 != 0) {
|
||||||
|
if (autoPad == "SAME_UPPER") {
|
||||||
|
actualPads[kernelRank + i] += 1;
|
||||||
|
} else {
|
||||||
|
actualPads[i] += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
emitError("auto_pad of unknown / unsupported value.");
|
||||||
|
}
|
||||||
|
// handle case where default pad values must be used
|
||||||
|
if (defaultPads) {
|
||||||
|
for(int i=0; i<2*kernelRank; ++i) {
|
||||||
|
actualPads.emplace_back(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// initialize output shape
|
||||||
|
SmallVector<int64_t, 4> yShape(xShape.begin(), xShape.end());
|
||||||
|
// for all kernel dimensions
|
||||||
|
for(int i=0; i<kernelRank; ++i) {
|
||||||
|
auto inputSpatialShape = xShape[kernelOffset + i];
|
||||||
|
auto padShape = actualPads[i] + actualPads[kernelRank+i];
|
||||||
|
auto kernelSpatialShape = (kernelShapeArray[i]).cast<IntegerAttr>().getInt();
|
||||||
|
auto dilations = actualDilations[i];
|
||||||
|
auto strideSpatialShape = actualStrides[i];
|
||||||
|
///output_spatial_shape[i] = ceil( (input_spatial_shape[i] + pad_shape[i] -
|
||||||
|
// ((kernel_spatial_shape[i] - 1) * dilations[i] + 1)) / strides_spatial_shape[i] + 1)
|
||||||
|
double numerator = inputSpatialShape + padShape -
|
||||||
|
((kernelSpatialShape - 1) * dilations + 1);
|
||||||
|
double denominator = strideSpatialShape;
|
||||||
|
int64_t res;
|
||||||
|
if (ceilMode) {
|
||||||
|
res = ceil(numerator / denominator) + 1;
|
||||||
|
} else {
|
||||||
|
res = floor(numerator / denominator) + 1;
|
||||||
|
}
|
||||||
|
yShape[kernelOffset + i] = res;
|
||||||
|
}
|
||||||
|
auto arrayTy = getOperand().getType().cast<RankedTensorType>();
|
||||||
|
getResult().setType(RankedTensorType::get(yShape, arrayTy.getElementType()));
|
||||||
|
}
|
||||||
|
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
// Unsqueeze
|
// Unsqueeze
|
||||||
|
|
||||||
void ONNXUnsqueezeOp::inferShapes() {
|
void ONNXUnsqueezeOp::inferShapes() {
|
||||||
|
|
|
@ -112,6 +112,7 @@ public:
|
||||||
op->getName().getStringRef() != "onnx.Xor" &&
|
op->getName().getStringRef() != "onnx.Xor" &&
|
||||||
op->getName().getStringRef() != "onnx.Sum" &&
|
op->getName().getStringRef() != "onnx.Sum" &&
|
||||||
op->getName().getStringRef() != "onnx.Max" &&
|
op->getName().getStringRef() != "onnx.Max" &&
|
||||||
|
op->getName().getStringRef() != "onnx.MaxPoolSingleOut" &&
|
||||||
op->getName().getStringRef() != "onnx.Min" &&
|
op->getName().getStringRef() != "onnx.Min" &&
|
||||||
op->getName().getStringRef() != "onnx.Identity" &&
|
op->getName().getStringRef() != "onnx.Identity" &&
|
||||||
op->getName().getStringRef() != "onnx.MatMul" &&
|
op->getName().getStringRef() != "onnx.MatMul" &&
|
||||||
|
|
|
@ -0,0 +1,100 @@
|
||||||
|
// RUN: onnf-opt --shape-inference %s -split-input-file | FileCheck %s
|
||||||
|
|
||||||
|
/// Test the default behavior of Max Pool with no padding (pad are set but shoudl be ignored)
|
||||||
|
func @test_default_maxpoolsingleout(%arg0 : tensor<5x5x32x32xf32>) -> tensor<*xf32> {
|
||||||
|
%0 = "onnx.MaxPoolSingleOut"(%arg0) {auto_pad = "VALID", ceil_mode = 0, kernel_shape = [3,3], pads = [1, 1, 1, 1] } : (tensor<5x5x32x32xf32>) -> tensor<*xf32>
|
||||||
|
"std.return"(%0) : (tensor<*xf32>) -> ()
|
||||||
|
}
|
||||||
|
// CHECK-LABEL: test_default_maxpoolsingleout
|
||||||
|
// CHECK: [[RES:%.+]] = "onnx.MaxPoolSingleOut"(%arg0) {auto_pad = "VALID", ceil_mode = 0 : i64, kernel_shape = [3, 3], pads = [1, 1, 1, 1]} : (tensor<5x5x32x32xf32>) -> tensor<5x5x30x30xf32>
|
||||||
|
// CHECK: return [[RES]] : tensor<5x5x30x30xf32>
|
||||||
|
|
||||||
|
|
||||||
|
/// Test the default behavior of Max Pool with no padding (pad are not set, default to zero)
|
||||||
|
func @test_default_maxpoolsingleout_defpad(%arg0 : tensor<5x5x32x32xf32>) -> tensor<*xf32> {
|
||||||
|
%0 = "onnx.MaxPoolSingleOut"(%arg0) {auto_pad = "NOTSET", ceil_mode = 0, kernel_shape = [3,3]} : (tensor<5x5x32x32xf32>) -> tensor<*xf32>
|
||||||
|
"std.return"(%0) : (tensor<*xf32>) -> ()
|
||||||
|
}
|
||||||
|
// CHECK-LABEL: test_default_maxpoolsingleout_defpad
|
||||||
|
// CHECK: [[RES:%.+]] = "onnx.MaxPoolSingleOut"(%arg0) {auto_pad = "NOTSET", ceil_mode = 0 : i64, kernel_shape = [3, 3]} : (tensor<5x5x32x32xf32>) -> tensor<5x5x30x30xf32>
|
||||||
|
// CHECK: return [[RES]] : tensor<5x5x30x30xf32>
|
||||||
|
|
||||||
|
|
||||||
|
/// Test the default behavior of Max Pool with uniform padding
|
||||||
|
func @test_default_maxpoolsingleout_pad(%arg0 : tensor<5x5x32x32xf32>) -> tensor<*xf32> {
|
||||||
|
%0 = "onnx.MaxPoolSingleOut"(%arg0) {auto_pad = "NOTSET", ceil_mode = 0, kernel_shape = [3,3], pads = [1, 1, 1, 1] } : (tensor<5x5x32x32xf32>) -> tensor<*xf32>
|
||||||
|
"std.return"(%0) : (tensor<*xf32>) -> ()
|
||||||
|
}
|
||||||
|
// CHECK-LABEL: test_default_maxpoolsingleout_pad
|
||||||
|
// CHECK: [[RES:%.+]] = "onnx.MaxPoolSingleOut"(%arg0) {auto_pad = "NOTSET", ceil_mode = 0 : i64, kernel_shape = [3, 3], pads = [1, 1, 1, 1]} : (tensor<5x5x32x32xf32>) -> tensor<5x5x32x32xf32>
|
||||||
|
// CHECK: return [[RES]] : tensor<5x5x32x32xf32>
|
||||||
|
|
||||||
|
|
||||||
|
/// Test the default behavior of Max Pool with non uniform padding
|
||||||
|
func @test_default_maxpoolsingleout_pad_nonunif(%arg0 : tensor<5x5x32x32xf32>) -> tensor<*xf32> {
|
||||||
|
%0 = "onnx.MaxPoolSingleOut"(%arg0) {auto_pad = "NOTSET", ceil_mode = 0, kernel_shape = [5,3], pads = [2, 1, 1, 0] } : (tensor<5x5x32x32xf32>) -> tensor<*xf32>
|
||||||
|
"std.return"(%0) : (tensor<*xf32>) -> ()
|
||||||
|
}
|
||||||
|
// CHECK-LABEL: test_default_maxpoolsingleout_pad_nonunif
|
||||||
|
// CHECK: [[RES:%.+]] = "onnx.MaxPoolSingleOut"(%arg0) {auto_pad = "NOTSET", ceil_mode = 0 : i64, kernel_shape = [5, 3], pads = [2, 1, 1, 0]} : (tensor<5x5x32x32xf32>) -> tensor<5x5x31x31xf32>
|
||||||
|
// CHECK: return [[RES]] : tensor<5x5x31x31xf32>
|
||||||
|
|
||||||
|
|
||||||
|
/// Test the default behavior of Max Pool with non uniform padding
|
||||||
|
func @test_default_maxpoolsingleout_strides(%arg0 : tensor<5x5x32x32xf32>) -> tensor<*xf32> {
|
||||||
|
%0 = "onnx.MaxPoolSingleOut"(%arg0) {auto_pad = "NOTSET", ceil_mode = 0, kernel_shape = [3,3], pads = [1, 1, 1, 1], strides = [2, 2] } : (tensor<5x5x32x32xf32>) -> tensor<*xf32>
|
||||||
|
"std.return"(%0) : (tensor<*xf32>) -> ()
|
||||||
|
}
|
||||||
|
// CHECK-LABEL: test_default_maxpoolsingleout_strides
|
||||||
|
// CHECK: [[RES:%.+]] = "onnx.MaxPoolSingleOut"(%arg0) {auto_pad = "NOTSET", ceil_mode = 0 : i64, kernel_shape = [3, 3], pads = [1, 1, 1, 1], strides = [2, 2]} : (tensor<5x5x32x32xf32>) -> tensor<5x5x16x16xf32>
|
||||||
|
// CHECK: return [[RES]] : tensor<5x5x16x16xf32>
|
||||||
|
|
||||||
|
|
||||||
|
/// Test the default behavior of Max Pool with non uniform padding
|
||||||
|
func @test_default_maxpoolsingleout_strides_nonunifpad(%arg0 : tensor<5x5x30x32xf32>) -> tensor<*xf32> {
|
||||||
|
%0 = "onnx.MaxPoolSingleOut"(%arg0) {auto_pad = "NOTSET", ceil_mode = 0, kernel_shape = [2,2], pads = [1, 0, 0, 0], strides = [2, 2] } : (tensor<5x5x30x32xf32>) -> tensor<*xf32>
|
||||||
|
"std.return"(%0) : (tensor<*xf32>) -> ()
|
||||||
|
}
|
||||||
|
// CHECK-LABEL: test_default_maxpoolsingleout_strides_nonunifpad
|
||||||
|
// CHECK: [[RES:%.+]] = "onnx.MaxPoolSingleOut"(%arg0) {auto_pad = "NOTSET", ceil_mode = 0 : i64, kernel_shape = [2, 2], pads = [1, 0, 0, 0], strides = [2, 2]} : (tensor<5x5x30x32xf32>) -> tensor<5x5x15x16xf32>
|
||||||
|
// CHECK: return [[RES]] : tensor<5x5x15x16xf32>
|
||||||
|
|
||||||
|
|
||||||
|
/// Test the default behavior of Max Pool with non uniform padding
|
||||||
|
func @test_default_maxpoolsingleout_strides_nonunifpad_ceil(%arg0 : tensor<5x5x30x32xf32>) -> tensor<*xf32> {
|
||||||
|
%0 = "onnx.MaxPoolSingleOut"(%arg0) {auto_pad = "NOTSET", ceil_mode = 1, kernel_shape = [2,2], pads = [1, 0, 0, 0], strides = [2, 2] } : (tensor<5x5x30x32xf32>) -> tensor<*xf32>
|
||||||
|
"std.return"(%0) : (tensor<*xf32>) -> ()
|
||||||
|
}
|
||||||
|
// CHECK-LABEL: test_default_maxpoolsingleout_strides_nonunifpad_ceil
|
||||||
|
// CHECK: [[RES:%.+]] = "onnx.MaxPoolSingleOut"(%arg0) {auto_pad = "NOTSET", ceil_mode = 1 : i64, kernel_shape = [2, 2], pads = [1, 0, 0, 0], strides = [2, 2]} : (tensor<5x5x30x32xf32>) -> tensor<5x5x16x16xf32>
|
||||||
|
// CHECK: return [[RES]] : tensor<5x5x16x16xf32>
|
||||||
|
|
||||||
|
|
||||||
|
/// Test the default behavior of Max Pool with dilatation
|
||||||
|
func @test_default_maxpoolsingleout_strides_dilatation(%arg0 : tensor<5x5x8x8xf32>) -> tensor<*xf32> {
|
||||||
|
%0 = "onnx.MaxPoolSingleOut"(%arg0) {auto_pad = "NOTSET", ceil_mode = 0, kernel_shape = [2,2], dilations = [2, 2], strides = [3, 3] } : (tensor<5x5x8x8xf32>) -> tensor<*xf32>
|
||||||
|
"std.return"(%0) : (tensor<*xf32>) -> ()
|
||||||
|
}
|
||||||
|
// CHECK-LABEL: test_default_maxpoolsingleout_strides_dilatation
|
||||||
|
// CHECK: [[RES:%.+]] = "onnx.MaxPoolSingleOut"(%arg0) {auto_pad = "NOTSET", ceil_mode = 0 : i64, dilations = [2, 2], kernel_shape = [2, 2], strides = [3, 3]} : (tensor<5x5x8x8xf32>) -> tensor<5x5x2x2xf32>
|
||||||
|
// CHECK: return [[RES]] : tensor<5x5x2x2xf32>
|
||||||
|
|
||||||
|
/// Test the default behavior of Max Pool with dilatation
|
||||||
|
func @test_default_maxpoolsingleout_upper(%arg0 : tensor<5x5x16x13xf32>) -> tensor<*xf32> {
|
||||||
|
%0 = "onnx.MaxPoolSingleOut"(%arg0) {auto_pad = "SAME_UPPER", ceil_mode = 0, kernel_shape = [4,4], strides = [4, 4] } : (tensor<5x5x16x13xf32>) -> tensor<*xf32>
|
||||||
|
"std.return"(%0) : (tensor<*xf32>) -> ()
|
||||||
|
}
|
||||||
|
// CHECK-LABEL: test_default_maxpoolsingleout_upper
|
||||||
|
// CHECK: [[RES:%.+]] = "onnx.MaxPoolSingleOut"(%arg0) {auto_pad = "SAME_UPPER", ceil_mode = 0 : i64, kernel_shape = [4, 4], strides = [4, 4]} : (tensor<5x5x16x13xf32>) -> tensor<5x5x4x4xf32>
|
||||||
|
// CHECK: return [[RES]] : tensor<5x5x4x4xf32>
|
||||||
|
|
||||||
|
|
||||||
|
/// Test the default behavior of Max Pool with dilatation
|
||||||
|
func @test_default_maxpoolsingleout_lower(%arg0 : tensor<5x5x16x13xf32>) -> tensor<*xf32> {
|
||||||
|
%0 = "onnx.MaxPoolSingleOut"(%arg0) {auto_pad = "SAME_LOWER", ceil_mode = 0, kernel_shape = [4,4], strides = [4, 4] } : (tensor<5x5x16x13xf32>) -> tensor<*xf32>
|
||||||
|
"std.return"(%0) : (tensor<*xf32>) -> ()
|
||||||
|
}
|
||||||
|
// CHECK-LABEL: test_default_maxpoolsingleout_lower
|
||||||
|
// CHECK: [[RES:%.+]] = "onnx.MaxPoolSingleOut"(%arg0) {auto_pad = "SAME_LOWER", ceil_mode = 0 : i64, kernel_shape = [4, 4], strides = [4, 4]} : (tensor<5x5x16x13xf32>) -> tensor<5x5x4x4xf32>
|
||||||
|
// CHECK: return [[RES]] : tensor<5x5x4x4xf32>
|
||||||
|
|
Loading…
Reference in New Issue