Skip to content

Commit cb2ae79

Browse files
rajeevsraoJohn Yang
andauthored
21.03 release (#653)
* ONNX opset13 importer updates Signed-off-by: Rajeev Rao <rajeevrao@nvidia.com> * Eyelike Operator Support Signed-off-by: Rajeev Rao <rajeevrao@nvidia.com> * GatherElements Operator Support Signed-off-by: Rajeev Rao <rajeevrao@nvidia.com> * Update 21.03 release date Signed-off-by: Rajeev Rao <rajeevrao@nvidia.com> Co-authored-by: John Yang <johny@nvidia.com>
1 parent dc22bb3 commit cb2ae79

File tree

7 files changed

+437
-217
lines changed

7 files changed

+437
-217
lines changed

builtin_op_importers.cpp

Lines changed: 214 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1119,6 +1119,54 @@ DEFINE_BUILTIN_OP_IMPORTER(Expand)
11191119
RETURN_FIRST_OUTPUT(sliceLayer);
11201120
}
11211121

1122+
DEFINE_BUILTIN_OP_IMPORTER(EyeLike)
1123+
{
1124+
// Get input node.
1125+
nvinfer1::ITensor& tensor = convertToTensor(inputs.at(0), ctx);
1126+
OnnxAttrs attrs(node, ctx);
1127+
int k = attrs.get("k", 0);
1128+
1129+
// "Only 2D tensors are supported, i.e. input T1 must be of rank 2..."
1130+
nvinfer1::Dims dims = tensor.getDimensions();
1131+
ASSERT(dims.nbDims == 2 && "Only 2D tensors are supported. Input must be of rank 2.", ErrorCode::kUNSUPPORTED_NODE);
1132+
1133+
// The data type can be specified by the 'dtype' argument
1134+
nvinfer1::DataType dtype = tensor.getType();
1135+
if (attrs.count("dtype"))
1136+
{
1137+
auto onnxType = attrs.get<int32_t>("dtype");
1138+
ASSERT(convertDtype(onnxType, &dtype) && "Unsupported cast!", ErrorCode::kINVALID_NODE);
1139+
LOG_VERBOSE("Casting to type: " << dtype);
1140+
}
1141+
1142+
// Create weights and constant layer
1143+
ASSERT(!isDynamic(dims) && "Eyelike does not work for dynamically shaped tensors.", ErrorCode::kUNSUPPORTED_NODE);
1144+
int totalWeights = dims.d[0]*dims.d[1];
1145+
std::vector<int> values(totalWeights);
1146+
for (int r = 0; r < dims.d[0]; ++r)
1147+
{
1148+
for (int c = 0; c < dims.d[1]; ++c)
1149+
{
1150+
values[r*dims.d[1] + c] = 0;
1151+
if (c - r == k)
1152+
{
1153+
values[r*dims.d[1] + c] = 1;
1154+
}
1155+
}
1156+
}
1157+
1158+
ShapedWeights tempWeights = ctx->createTempWeights(::ONNX_NAMESPACE::TensorProto::INT32, dims);
1159+
std::memcpy(tempWeights.values, values.data(), values.size() * sizeof(int));
1160+
auto* layer = ctx->network()->addConstant(dims, tempWeights);
1161+
layer->setOutputType(0, nvinfer1::DataType::kINT32);
1162+
ctx->registerLayer(layer, node.name());
1163+
1164+
if (dtype != nvinfer1::DataType::kINT32) {
1165+
return {{castHelper(ctx, layer->getOutput(0), dtype)}};
1166+
}
1167+
return {{layer->getOutput(0)}};
1168+
}
1169+
11221170
DEFINE_BUILTIN_OP_IMPORTER(Flatten)
11231171
{
11241172
OnnxAttrs attrs(node, ctx);
@@ -1156,6 +1204,92 @@ DEFINE_BUILTIN_OP_IMPORTER(Gather)
11561204
RETURN_FIRST_OUTPUT(layer);
11571205
}
11581206

1207+
DEFINE_BUILTIN_OP_IMPORTER(GatherElements)
1208+
{
1209+
nvinfer1::ITensor& data = convertToTensor(inputs.at(0), ctx);
1210+
nvinfer1::ITensor& index = convertToTensor(inputs.at(1), ctx);
1211+
1212+
const nvinfer1::Dims& idxDims = index.getDimensions();
1213+
const nvinfer1::Dims& dataDims = data.getDimensions();
1214+
1215+
OnnxAttrs attrs(node, ctx);
1216+
int32_t axis = attrs.get<int32_t>("axis", 0);
1217+
int32_t dataNbDims = dataDims.nbDims;
1218+
1219+
TRT_CHECK(convertAxis(axis, dataNbDims));
1220+
LOG_VERBOSE("Using Gather axis: " << axis);
1221+
1222+
// Calculate how many indices
1223+
int64_t nIndx = volume(idxDims);
1224+
1225+
// Calculate pitches of input tensor
1226+
int32_t nDataElements = volume(dataDims), pitch = 1;
1227+
int32_t pitches[nvinfer1::Dims::MAX_DIMS] = {0};
1228+
pitches[dataDims.nbDims-1] = pitch;
1229+
for (int32_t i = dataDims.nbDims-2; i >= 0 ; i--)
1230+
{
1231+
pitch *= dataDims.d[i];
1232+
pitches[i] = pitch;
1233+
}
1234+
1235+
// Generate constants based on axis
1236+
std::vector<int32_t> sCoeff(nIndx, pitches[axis]);
1237+
std::vector<int32_t> aCoeff;
1238+
1239+
// Transform a 1-d index back to the nDims
1240+
for (int32_t i = 0; i < nIndx; i++)
1241+
{
1242+
std::vector<int32_t> nDimsIdx; //this can be an array
1243+
int32_t currI = i;
1244+
1245+
for (int32_t j = 0; j < dataDims.nbDims; j++)
1246+
{
1247+
int32_t currIdxVal = currI / pitches[j];
1248+
nDimsIdx.push_back(currIdxVal);
1249+
currI = currI % pitches[j];
1250+
}
1251+
1252+
int32_t bias = 0;
1253+
//calculate the aCoeff
1254+
for (size_t j = 0; j < nDimsIdx.size(); j++)
1255+
{
1256+
1257+
if (j == (size_t)axis)
1258+
{
1259+
continue;
1260+
}
1261+
bias += nDimsIdx[j] * pitches[j];
1262+
}
1263+
aCoeff.push_back(bias);
1264+
}
1265+
1266+
auto* sCoeffLayer = addConstant(ctx, sCoeff, ::ONNX_NAMESPACE::TensorProto::INT32, idxDims);
1267+
auto* aCoeffLayer = addConstant(ctx, aCoeff, ::ONNX_NAMESPACE::TensorProto::INT32, idxDims);
1268+
1269+
nvinfer1::ITensor* sCoeffTensor = sCoeffLayer->getOutput(0);
1270+
nvinfer1::ITensor* aCoeffTensor = aCoeffLayer->getOutput(0);
1271+
auto* mul = ctx->network()->addElementWise(index, *sCoeffTensor, nvinfer1::ElementWiseOperation::kPROD);
1272+
1273+
nvinfer1::ITensor* mulTensor = mul->getOutput(0);
1274+
auto* add = ctx->network()->addElementWise(*mulTensor, *aCoeffTensor, nvinfer1::ElementWiseOperation::kSUM);
1275+
1276+
nvinfer1::ITensor* addTensor = add->getOutput(0);
1277+
1278+
nvinfer1::Dims flattenDataDims{1};
1279+
1280+
flattenDataDims.nbDims = 1;
1281+
flattenDataDims.d[0] = nDataElements;
1282+
auto* reshape = ctx->network()->addShuffle(data);
1283+
reshape->setReshapeDimensions(flattenDataDims);
1284+
reshape->setZeroIsPlaceholder(false);
1285+
1286+
nvinfer1::ITensor* flattenData = reshape->getOutput(0);
1287+
auto* layer = ctx->network()->addGather(*flattenData, *addTensor, 0);
1288+
ctx->registerLayer(layer, getNodeName(node));
1289+
RETURN_FIRST_OUTPUT(layer);
1290+
}
1291+
1292+
11591293
DEFINE_BUILTIN_OP_IMPORTER(Gemm)
11601294
{
11611295
OnnxAttrs attrs(node, ctx);
@@ -1859,30 +1993,13 @@ DEFINE_BUILTIN_OP_IMPORTER(Log)
18591993

18601994
DEFINE_BUILTIN_OP_IMPORTER(LogSoftmax)
18611995
{
1996+
auto& input = convertToTensor(inputs.at(0), ctx);
18621997
// Don't use softmax converter since it adds a shuffle layer
18631998
// which prevents the builder to fuse softmax and log operations.
1864-
1865-
OnnxAttrs attrs(node, ctx);
1866-
// "input : T"
1867-
nvinfer1::ITensor& input = convertToTensor(inputs.at(0), ctx);
1868-
const auto dims = shapeOf(input);
1869-
// "axis : int (default is 1)"
1870-
int axis = attrs.get("axis", 1);
1871-
1872-
// "Negative value means counting dimensions from the back.
1873-
// Accepted range is [-r, r-1] where r = rank(input)."
1874-
TRT_CHECK(convertAxis(axis, dims.size()));
1875-
1876-
// "The input does not need to explicitly be a 2D vector; rather, it will be coerced into one."
1877-
auto* flattened = flattenTensor(ctx, node, input, axis);
1878-
auto* softMax = ctx->network()->addSoftMax(*flattened);
1879-
ctx->registerLayer(softMax, node.name());
1880-
// ONNX softmax is always on second dimension.
1881-
softMax->setAxes(1 << 1);
1882-
1999+
auto* softmax = addSoftmax(ctx, node, input);
2000+
nvinfer1::IUnaryLayer* unaryLayer = ctx->network()->addUnary(*softmax, nvinfer1::UnaryOperation::kLOG);
18832001
// Reshape back to original shape
1884-
nvinfer1::IUnaryLayer* unaryLayer = ctx->network()->addUnary(*softMax->getOutput(0), nvinfer1::UnaryOperation::kLOG);
1885-
auto *reshapeLayer = addShuffle(ctx, *unaryLayer->getOutput(0), dims);
2002+
auto* reshapeLayer = addShuffle(ctx, *unaryLayer->getOutput(0), shapeOf(input));
18862003
RETURN_FIRST_OUTPUT(reshapeLayer);
18872004
}
18882005

@@ -3573,27 +3690,10 @@ DEFINE_BUILTIN_OP_IMPORTER(Slice)
35733690

35743691
DEFINE_BUILTIN_OP_IMPORTER(Softmax)
35753692
{
3576-
OnnxAttrs attrs(node, ctx);
3577-
// "input : T"
3578-
nvinfer1::ITensor& input = convertToTensor(inputs.at(0), ctx);
3579-
const auto dims = shapeOf(input);
3580-
3581-
// "axis : int (default is 1)"
3582-
int axis = attrs.get("axis", 1);
3583-
3584-
// "Negative value means counting dimensions from the back.
3585-
// Accepted range is [-r, r-1] where r = rank(input)."
3586-
TRT_CHECK(convertAxis(axis, dims.size()));
3587-
3588-
// "The input does not need to explicitly be a 2D vector; rather, it will be coerced into one."
3589-
auto* flattened = flattenTensor(ctx, node, input, axis);
3590-
auto* softMax = ctx->network()->addSoftMax(*flattened);
3591-
ctx->registerLayer(softMax, node.name());
3592-
// ONNX softmax is always on second dimension.
3593-
softMax->setAxes(1 << 1);
3594-
3693+
auto& input = convertToTensor(inputs.at(0), ctx);
3694+
auto* softmax = addSoftmax(ctx, node, input);
35953695
// Reshape back to original shape
3596-
auto* reshapeLayer = addShuffle(ctx, *softMax->getOutput(0), dims);
3696+
auto* reshapeLayer = addShuffle(ctx, *softmax, shapeOf(input));
35973697
RETURN_FIRST_OUTPUT(reshapeLayer);
35983698
}
35993699

@@ -3684,11 +3784,26 @@ DEFINE_BUILTIN_OP_IMPORTER(Split)
36843784
std::vector<int> splitList;
36853785
ShapeTensor sizes;
36863786
ShapeTensor sizeSliceAxis;
3687-
const bool hasSplitList = attrs.count("split");
3787+
const bool hasSplitList = (ctx->getOpsetVersion() >= 13) ? (inputs.size() == 2) : attrs.count("split");
36883788
if (hasSplitList)
36893789
{
36903790
// "Lengths of the parts can be specified using argument split."
3691-
splitList = attrs.get<std::vector<int>>("split");
3791+
// In opset >= 13, split lengths are an optional input
3792+
if (ctx->getOpsetVersion() >= 13)
3793+
{
3794+
ASSERT(inputs.at(1).is_weights() && "Split input 'split', if specified, must be an initializer!", ErrorCode::kUNSUPPORTED_NODE);
3795+
auto splitWeights = inputs.at(1).weights();
3796+
int32_t* splitValues = static_cast<int32_t*>(splitWeights.values);
3797+
for (size_t i = 0; i < splitWeights.count(); i++)
3798+
{
3799+
splitList.push_back(splitValues[i]);
3800+
}
3801+
}
3802+
// Pre-opset 13 split lengths are provided as an attribute
3803+
else
3804+
{
3805+
splitList = attrs.get<std::vector<int>>("split");
3806+
}
36923807
ASSERT(static_cast<int>(splitList.size()) == numOutputs, ErrorCode::kINVALID_NODE);
36933808
}
36943809
else
@@ -3737,9 +3852,45 @@ DEFINE_BUILTIN_OP_IMPORTER(Squeeze)
37373852
// "data : T
37383853
// Tensor with at least max(dims) dimensions."
37393854
nvinfer1::ITensor& data = convertToTensor(inputs.at(0), ctx);
3855+
std::vector<int> axes;
3856+
// In opset >= 13, axes are an optional input
3857+
if (ctx->getOpsetVersion() >= 13)
3858+
{
3859+
if (inputs.size() == 2)
3860+
{
3861+
ASSERT(inputs.at(1).is_weights() && "Squeeze axes input must an initializer!", ErrorCode::kUNSUPPORTED_NODE);
3862+
// Map weights value to axes
3863+
auto axesWeights = inputs.at(1).weights();
3864+
int32_t* axesValues = static_cast<int32_t*>(axesWeights.values);
3865+
for (size_t i = 0; i < axesWeights.count(); i++)
3866+
{
3867+
axes.push_back(axesValues[i]);
3868+
}
3869+
}
3870+
}
3871+
// Pre-opset 13 axes are provided as an attribute
3872+
else
3873+
{
3874+
OnnxAttrs attrs(node, ctx);
3875+
if (attrs.count("axes"))
3876+
{
3877+
axes = attrs.get<std::vector<int>>("axes");
3878+
}
3879+
}
37403880

3741-
OnnxAttrs attrs(node, ctx);
3742-
auto axes = attrs.get<std::vector<int>>("axes");
3881+
// If axes are ommitted, squeeze all dimensions with values 1
3882+
if (axes.size() == 0)
3883+
{
3884+
const auto shape = data.getDimensions();
3885+
ASSERT(!isDynamic(shape) && "Cannot infer squeeze dimensions from a dynamic shape! Please re-export your model with the Squeeze axes input set.", ErrorCode::kUNSUPPORTED_NODE);
3886+
for (int i = 0; i < shape.nbDims; i++)
3887+
{
3888+
if (shape.d[i] == 1)
3889+
{
3890+
axes.push_back(i);
3891+
}
3892+
}
3893+
}
37433894

37443895
int rank = data.getDimensions().nbDims;
37453896
for (auto& axis : axes)
@@ -3750,7 +3901,6 @@ DEFINE_BUILTIN_OP_IMPORTER(Squeeze)
37503901
// "squeezed : T
37513902
// Reshaped tensor with same data as input."
37523903
auto* squeezed = squeezeTensor(ctx, node, data, axes, true);
3753-
37543904
ASSERT(squeezed && "Failed to squeeze tensor!", ErrorCode::kUNSUPPORTED_NODE);
37553905

37563906
return {{squeezed}};
@@ -3893,11 +4043,25 @@ DEFINE_BUILTIN_OP_IMPORTER(Unsqueeze)
38934043
// "data : T
38944044
// Original tensor"
38954045
nvinfer1::ITensor& data = convertToTensor(inputs.at(0), ctx);
3896-
OnnxAttrs attrs(node, ctx);
4046+
std::vector<int> axes;
38974047

3898-
// "axes : list of ints (required)
3899-
// List of integers indicating the dimensions to be inserted."
3900-
auto axes = attrs.get<std::vector<int>>("axes");
4048+
if (ctx->getOpsetVersion() >= 13)
4049+
{
4050+
const ShapeTensor axesInput{inputs.at(1)};
4051+
ASSERT(axesInput.allValuesKnown() && "Axes input for unsqueeze operation should be a constant tensor.",
4052+
ErrorCode::kUNSUPPORTED_NODE);
4053+
for (auto& a : axesInput)
4054+
{
4055+
axes.push_back(a);
4056+
}
4057+
}
4058+
else
4059+
{
4060+
OnnxAttrs attrs(node, ctx);
4061+
// "axes : list of ints (required)
4062+
// List of integers indicating the dimensions to be inserted."
4063+
axes = attrs.get<std::vector<int>>("axes");
4064+
}
39014065

39024066
// "Negative value means counting dimensions from the back."
39034067
const int newSize = data.getDimensions().nbDims + axes.size();
@@ -3909,7 +4073,6 @@ DEFINE_BUILTIN_OP_IMPORTER(Unsqueeze)
39094073
// "expanded : T
39104074
// Reshaped tensor with same data as input."
39114075
auto* expanded = unsqueezeTensor(ctx, node, data, axes, true);
3912-
39134076
ASSERT(expanded && "Failed to unsqueeze tensor!", ErrorCode::kUNSUPPORTED_NODE);
39144077

39154078
return {{expanded}};

docs/Changelog.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
11
# ONNX-TensorRT Changelog
22

3-
## 21.02 Container Release - 2021-01-22
3+
## 21.03 Container Release - 2021-03-09
4+
### Added
5+
- Added opset13 support for `SoftMax`, `LogSoftmax`, `Squeeze`, and `Unsqueeze`
6+
- Added support for the `EyeLike` operator
7+
- Added support for the `GatherElements` operator
8+
9+
### Fixes
10+
### Removed
11+
12+
## 21.02 Container Release - 2021-01-18
413
### Added
514
- Added support for the `ReverseSequence` operator [#590] - https://github.com/onnx/onnx-tensorrt/pull/590
615
- Updated `parse()` and `supportsModel()` API calls with an optional `model_path` parameter to support models with external weights [#621](https://github.com/onnx/onnx-tensorrt/pull/621)

0 commit comments

Comments
 (0)